> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.ocpplab.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.ocpplab.io/_mcp/server.

# Connect a client

## Sign in with your browser

Clients that support MCP OAuth only need the URL. Your browser opens on OCPPLab: sign in, choose the organization the agent may use, and select **Allow**. No token to copy, and the client refreshes its access on its own.

```bash
claude mcp add --transport http ocpplab https://api.ocpplab.io/mcp
```

This works the same way in Cursor, VS Code, claude.ai connectors and ChatGPT: add `https://api.ocpplab.io/mcp` with no credentials.

* Reconnecting the same app skips the consent screen and keeps the organization you chose.
* Disconnect an app in Studio under **Settings → Connected apps**. It loses access within an hour.

## Use an SDK token

For CI, scripts and headless agents, where no browser can open, send an [SDK token](/get-started/authentication) as a header. The examples use the `cpo` server; swap the name and URL for any other domain.

#### Claude Code

```bash
claude mcp add --transport http ocpplab-cpo https://api.ocpplab.io/mcp/cpo \
  --header 'Authorization: Bearer ${OCPPLAB_TOKEN}'
```

Add `--scope project` to share it through `.mcp.json`. Claude Code reads `${OCPPLAB_TOKEN}` from each developer's environment, so the file holds no secret:

**`.mcp.json`**

```json .mcp.json
{
  "mcpServers": {
    "ocpplab-cpo": {
      "type": "http",
      "url": "https://api.ocpplab.io/mcp/cpo",
      "headers": { "Authorization": "Bearer ${OCPPLAB_TOKEN}" }
    }
  }
}
```

Check it with `claude mcp list`. Tools appear as `mcp__ocpplab-cpo__cpo_charge_points_list`.

#### Codex

```bash
codex mcp add ocpplab-cpo --url https://api.ocpplab.io/mcp/cpo \
  --bearer-token-env-var OCPPLAB_TOKEN
```

That writes to `~/.codex/config.toml`:

```toml
[mcp_servers.ocpplab-cpo]
url = "https://api.ocpplab.io/mcp/cpo"
bearer_token_env_var = "OCPPLAB_TOKEN"
```

#### Cursor

**`~/.cursor/mcp.json`**

```json ~/.cursor/mcp.json
{
  "mcpServers": {
    "ocpplab-cpo": {
      "url": "https://api.ocpplab.io/mcp/cpo",
      "headers": { "Authorization": "Bearer ${env:OCPPLAB_TOKEN}" }
    }
  }
}
```

#### VS Code

VS Code asks for the token once and stores it securely:

**`.vscode/mcp.json`**

```json .vscode/mcp.json
{
  "inputs": [
    { "type": "promptString", "id": "ocpplab-token", "description": "OCPPLab SDK token", "password": true }
  ],
  "servers": {
    "ocpplab-cpo": {
      "type": "http",
      "url": "https://api.ocpplab.io/mcp/cpo",
      "headers": { "Authorization": "Bearer ${input:ocpplab-token}" }
    }
  }
}
```

#### Windsurf

**`~/.codeium/windsurf/mcp_config.json`**

```json ~/.codeium/windsurf/mcp_config.json
{
  "mcpServers": {
    "ocpplab-cpo": {
      "serverUrl": "https://api.ocpplab.io/mcp/cpo",
      "headers": { "Authorization": "Bearer ${env:OCPPLAB_TOKEN}" }
    }
  }
}
```

#### Claude Desktop

Claude Desktop starts local servers only, so bridge with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote):

**`claude_desktop_config.json`**

```json claude_desktop_config.json
{
  "mcpServers": {
    "ocpplab-cpo": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://api.ocpplab.io/mcp/cpo",
               "--header", "Authorization:${OCPPLAB_AUTH}"],
      "env": { "OCPPLAB_AUTH": "Bearer <token>" }
    }
  }
}
```

Write the header without a space after the colon: some Desktop builds split arguments on spaces.

#### Python

```python
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport

transport = StreamableHttpTransport(
    "https://api.ocpplab.io/mcp/cpo",
    headers={"Authorization": f"Bearer {token}"},
)
async with Client(transport) as client:
    tools = await client.list_tools()
    result = await client.call_tool("cpo_charge_points_list", {"limit": 10})
```

In TypeScript, use `StreamableHTTPClientTransport` from `@modelcontextprotocol/sdk` with `requestInit: { headers: { Authorization: "Bearer …" } }`.

## Test the connection

```bash
curl -sS -X POST https://api.ocpplab.io/mcp/cpo \
  -H "Authorization: Bearer $OCPPLAB_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
```

A working setup answers with `"name": "ocpplab-cpo"` in `serverInfo`. To browse tools interactively, run `npx @modelcontextprotocol/inspector`, choose **Streamable HTTP**, and enter the URL.

## Troubleshooting

| Symptom                                                     | Fix                                                                                                         |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `401`, "failed to connect", or an unexpected sign-in prompt | The token is missing or deleted, or `OCPPLAB_TOKEN` is not exported in the shell that started the client.   |
| "This app has no OCPPLab organization access"               | You left the organization you chose. Disconnect the app in **Settings → Connected apps** and connect again. |
| The client keeps asking you to sign in                      | Its access was revoked or expired. Run `/mcp` in Claude Code and sign in again.                             |
| `403 admin role required`                                   | The tool needs an organization admin.                                                                       |
| `404` for an id you know exists                             | The id belongs to another organization.                                                                     |
| `429 rate limit exceeded`                                   | Over 120 calls per minute for your user in this organization. Wait for `Retry-After` seconds.               |
| The agent picks the wrong tool                              | Connect fewer servers: only the domains the task needs.                                                     |