
# Connecting a Client

Once [`MCP_ENABLED=true`](/en/docs/mcp-integration) the app serves one JSON-RPC endpoint at `MCP_MOUNT_PATH` — `https://your-app.example.com/mcp` unless you moved it. Any MCP-capable client can speak to it.

## The Generic Configuration

Claude Desktop, Claude Code, Cursor and ChatGPT Dev Mode all read some form of an `mcpServers` block:

```json
{
  "mcpServers": {
    "sovrium": {
      "url": "https://your-app.example.com/mcp",
      "transport": "http"
    }
  }
}
```

Where that block lives differs per client — a settings file, a project config, a UI panel — but the two values it needs are always the same: the URL and the transport.

## Authenticating

Which credential you send depends on `MCP_AUTH_STRATEGY`. See [Auth, RBAC & Rate Limiting](/en/docs/mcp-security) for what each grants.

**Token mode.** Send the role's bearer token on every request. The token _is_ the role — a `viewer` token only ever sees read and list tools.

```text
Authorization: Bearer <MCP_TOKEN_ADMIN>
```

**OAuth mode.** When `app.auth` is configured, register a client once by dynamic client registration and then run the ordinary authorization-code flow:

```bash
curl -X POST 'https://your-app.example.com/api/auth/oauth2/register' \
  --header 'Content-Type: application/json' \
  --data '{
    "client_name": "Sovrium MCP — Claude",
    "redirect_uris": ["https://your-app.example.com/oauth/callback"],
    "grant_types": ["authorization_code", "refresh_token"],
    "token_endpoint_auth_method": "client_secret_post"
  }'
```

An unauthenticated request answers `401` with a `WWW-Authenticate: Bearer` discovery header, so a compliant client can start the flow without being told how.

## Local IDE over stdio

For a local integration there is no HTTP at all. Set `MCP_TRANSPORT=stdio` and the client launches the Sovrium binary as a child process, speaking MCP over stdin and stdout.

```json
{
  "mcpServers": {
    "sovrium": {
      "command": "sovrium",
      "args": ["start", "./app.ts"],
      "env": { "MCP_ENABLED": "true", "MCP_TRANSPORT": "stdio" }
    }
  }
}
```

The `/mcp` HTTP route is **not mounted** in this mode. They are alternatives, not layers — pick one per process.

## Verifying the Connection

Do not trust the client's green dot. The smallest useful check is `tools/list`:

```bash
curl -X POST 'https://your-app.example.com/mcp' \
  --header 'Authorization: Bearer <MCP_TOKEN_ADMIN>' \
  --header 'Content-Type: application/json' \
  --data '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }'
```

The response lists exactly the tools your credential can call — the tables, actions and automations you marked with [`aiAccess`](/en/docs/mcp-server), filtered by the role behind the credential.

That last clause is what makes this worth running. Run it with an admin token and a viewer token in turn: two different lists is the proof that role filtering is live. One identical list means something is wrong with your credentials, not with your schema.

:::callout
**An empty list is usually `aiAccess`, not auth.** A `200` with no tools means you authenticated fine and nothing is eligible for exposure. A `401` means the credential is wrong. Check which one you got before editing the schema.
:::

## Related Pages

- [MCP Overview](/en/docs/mcp-integration) — enabling the server.
- [Server Mode](/en/docs/mcp-server) — what appears in `tools/list` and why.
- [Auth, RBAC & Rate Limiting](/en/docs/mcp-security) — tokens, OAuth, and limits.
- [Connect Claude via MCP](/en/docs/connect-claude-mcp) — a worked end-to-end setup.
- [OAuth Server](/en/docs/auth-oauth-server) — the OAuth plugin behind `oauth2` mode.
