# Connect a client

> Connect Claude Code, Claude Desktop, claude.ai, Cursor, Codex or any MCP client with OAuth and just the URL, or with an API token.

There are two ways to authenticate. **OAuth 2.1** is the default: give the client the URL, sign in to the dashboard when it asks and choose what the assistant may do. An **API token** is the fallback for clients that cannot do OAuth, or for automation.

## Before you start

- An OpenWAF Cloud account with access to an organization. For OAuth, you sign in with it in the browser.
- For the token variant, a token of its own for the assistant, created in the dashboard (**API tokens**) or with `POST /v1/orgs/{org}/tokens`, with the narrowest role and an expiry. See [OAuth and tokens](https://developers.openwaf.dev/mcp/oauth-and-tokens/#api-tokens).

> **Caution: A token is a credential**
>
> It goes in the MCP client's configuration or environment, never in the conversation.


## Claude Code

**OAuth**

```sh
claude mcp add --transport http openwaf https://mcp.openwaf.dev/mcp
```

Without a header, Claude Code uses OAuth: run `/mcp`, pick `openwaf` and *Authenticate* opens the browser.
**API token**

```sh
claude mcp add --transport http openwaf https://mcp.openwaf.dev/mcp \
  --header "Authorization: Bearer $OPENWAF_TOKEN"
```


`claude mcp list` should show it connected, and `/mcp` inside Claude Code lists its tools.

## Claude Desktop and claude.ai

**OAuth**

Settings → Connectors → *Add custom connector* → URL `https://mcp.openwaf.dev/mcp` → *Connect*. The dashboard opens so you sign in and grant access. You do not need `mcp-remote` or Node.js.
**API token**

To use an API token instead of OAuth in Claude Desktop, the [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) bridge still works in `claude_desktop_config.json` (Settings → Developer → Edit config):

```json
{
  "mcpServers": {
    "openwaf": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.openwaf.dev/mcp", "--header", "Authorization:${OPENWAF_AUTH}"],
      "env": { "OPENWAF_AUTH": "Bearer owf_…" }
    }
  }
}
```

The header goes in a variable so the arguments contain no spaces. Restart Claude Desktop afterwards.


## Cursor

In `~/.cursor/mcp.json` (or the project's `.cursor/mcp.json`, kept out of the repository):

**OAuth**

```json
{
  "mcpServers": {
    "openwaf": {
      "url": "https://mcp.openwaf.dev/mcp"
    }
  }
}
```

Without `headers`, Cursor uses OAuth and only needs the `url`.
**API token**

```json
{
  "mcpServers": {
    "openwaf": {
      "url": "https://mcp.openwaf.dev/mcp",
      "headers": { "Authorization": "Bearer owf_…" }
    }
  }
}
```


## Codex and other clients

Any MCP client with the Streamable HTTP transport works: add `https://mcp.openwaf.dev/mcp` as a remote server. If the client supports MCP authorization, the URL is enough; otherwise set the header `Authorization: Bearer owf_…`.

By hand, to check a token:

```sh
curl -sS https://mcp.openwaf.dev/mcp \
  -H "Authorization: Bearer $OPENWAF_TOKEN" -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

### Transport rules

Every client must meet these rules:

- `Accept` must include both `application/json` and `text/event-stream`.
- `GET` and `DELETE` answer 405, because there are no sessions.
- Each request carries **one** JSON-RPC message: batches (an array) answer 400 `bad_request`.
- The body is at most 1 MiB (413 `too_large`).
- Each token has at most **4 MCP requests in flight**; the next answers HTTP 429 `rate_limited` with `Retry-After: 1`.

## Check the connection

Ask the assistant to list your zones. It calls `list_zones` and answers with the zones the credential can see. If a zone is missing, the token is probably limited to a client or a zone; if the write tools are missing, its role is `readonly`.

To keep the assistant's context small, connect with the [lite profile](https://developers.openwaf.dev/mcp/oauth-and-tokens/#lite-profile) instead.
