# OpenHands

> Set up Spicrawl in OpenHands (web UI, CLI or SDK): add the hosted MCP server with your API key, install the agent skill, and add AGENTS.md rules so the agent fetches web pages cheaply and handles errors correctly.

Source: https://docs.spicrawl.com/agents/openhands

Three steps give OpenHands Spicrawl's tools, the knowledge to write Spicrawl code, and rules for using both well. You need an API key from [app.spicrawl.com](https://app.spicrawl.com) (`spicrawl_live_...`). The server uses a bearer key, not OAuth.

## 1. Add the MCP server

**Web UI**

Go to **Customize → MCP Servers**, select **Add custom server**, and choose the SHTTP transport (the recommended one for remote servers):

| Field          | Value                          |
| -------------- | ------------------------------ |
| Server name    | `spicrawl`                     |
| URL            | `https://mcp.spicrawl.com/mcp` |
| Authentication | Bearer token                   |
| API Key        | your key (`spicrawl_live_...`) |

OpenHands sends the token as `Authorization: Bearer <key>`. Select **Test connection**, then save. The server applies to new conversations.

**CLI**

```bash
openhands mcp add spicrawl --transport http \
  --header "Authorization: Bearer $SPICRAWL_API_KEY" \
  https://mcp.spicrawl.com/mcp
```

The shell expands `$SPICRAWL_API_KEY` when you run the command, so `~/.openhands/mcp.json` stores the key itself. Keep that file out of version control. Check it with `openhands mcp list`.

**SDK**

`mcp_config` on `Agent` maps a server name to its settings. A bearer key goes in `auth`:

```python title="agent.py"
import os

mcp_config = {
    "spicrawl": {
        "url": "https://mcp.spicrawl.com/mcp",
        "auth": {"strategy": "bearer", "value": os.environ["SPICRAWL_API_KEY"]},
    }
}

# agent = Agent(llm=llm, tools=tools, mcp_config=mcp_config)
```

Pass `mcp_config` to `Agent` next to your `llm` and `tools`, as in the SDK's MCP example.

The server lists 25 `spicrawl_*` tools. See [MCP server](https://docs.spicrawl.com/agents/mcp.md) for each tool.

> **Warning:** Current OpenHands releases do not read MCP servers from the `[mcp]` section of `config.toml`. That format is from legacy OpenHands (V0). Add the server through the UI, the CLI or the SDK instead.

## 2. Install the skill

OpenHands loads skills from `.agents/skills/` in the repository (preferred) or the older `.openhands/skills/`, and from `~/.agents/skills/` or `~/.openhands/skills/` for every project. A project skill overrides a user skill with the same name.

```bash
mkdir -p .agents/skills/spicrawl
curl -fsSL https://app.spicrawl.com/skill.md -o .agents/skills/spicrawl/SKILL.md
```

The skill's `name` must match its directory, `spicrawl`. The older `.openhands/microagents/` directory still works but `.agents/skills/` replaces it. See [Agent skill](https://docs.spicrawl.com/agents/skill.md).

## 3. Add rules to AGENTS.md

OpenHands puts the full `AGENTS.md` at the repository root into the initial system prompt, so keep it short. Paste this section into it:

```markdown title="AGENTS.md"
## Web data (Spicrawl)

Use Spicrawl to read web pages: the spicrawl_* MCP tools in chat, or
POST https://api.spicrawl.com/v1/scrape with `Authorization: Bearer $SPICRAWL_API_KEY` in code.
Docs: https://docs.spicrawl.com/llms.txt (append .md to any page URL for Markdown).

- Ask for markdown: `response_format: "markdown"` (API default is html); on spicrawl_scrape, `format: "markdown"`.
- Check the site's status, not only the HTTP status: `X-Target-Status` header, or `status`
  in the JSON envelope (spicrawl_scrape with `format: "json"`). 200 is the page; 404/410
  mean it does not exist; 403/429/503 mean the site refused, so escalate.
- On an error, switch on `code`. Retry only when `retryable` is true, after
  `retry_after_seconds`. Read `diagnostics.hint` and change what it names first.
  Never retry ERR::REQUEST::*, ERR::AUTH::* or ERR::LIMIT::QUOTA_EXCEEDED.
- Escalate one step at a time and stop at the first that works:
  plain fetch (1 credit) -> `js_render: true` (3) -> add the user's own `proxy`
  if they have one. Empty or skeleton content means render; ERR::UPSTREAM::CHALLENGE
  or a 403 target status means retry once, then the user's own proxy.
- Set `max_cost` on every request to the price of the step you intend.
- Keep the cache on (default). Set `cache: false` only for prices, stock or other live data.
- Trim tokens with `main_content_only` (on by default for markdown), `include_tags`, `exclude_tags`.
- For more than 20 URLs, use a batch job (spicrawl_batch_submit / POST /v1/batch).
  Batch items return raw HTML and apply only render, proxy and block_resources settings.
- Never print or commit SPICRAWL_API_KEY. Log `X-Request-Id` for failures.
```

## A first task to try

Start a conversation and write:

```text
Use Spicrawl to read https://example.com/pricing as markdown. List each plan with its monthly price.
If the page comes back empty, retry with rendering.
```

The agent should call `spicrawl_scrape` with `url` and `format: "markdown"`, and add `render: true` only if the first result is empty. For a coding task:

```text
Add a fetch_page(url) function to this project that calls the Spicrawl API for markdown,
following the Web data rules in AGENTS.md. Read the key from SPICRAWL_API_KEY.
```

Compare the result with the reference implementation in [Best practices](https://docs.spicrawl.com/agents/best-practices.md).

## Troubleshooting

| Symptom                                                                       | Fix                                                                                                                                                          |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Test connection** fails or the server returns 401                           | The API Key field must hold only the key (`spicrawl_live_...`), with Bearer token selected. For a custom header, the value is `Bearer ` followed by the key. |
| The server is missing after upgrading from an older OpenHands                 | Servers in `config.toml` `[mcp]` are ignored. Add it again through the UI, CLI or SDK.                                                                       |
| A new server has no tools in a running conversation                           | MCP changes apply to new conversations. Start a new one.                                                                                                     |
| The agent writes `js_render` on `spicrawl_scrape` and gets "unknown argument" | The tool's argument is `render`. The API field is `js_render`.                                                                                               |
| Usage tools return `ERR::AUTH::INSUFFICIENT_SCOPE`                            | Grant the `read` scope to the key in the dashboard.                                                                                                          |
