> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qlaud.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# /v1/mcp-servers

> Connect any MCP server URL — every tool it exposes is auto-registered. Compose tools without writing webhooks.

[MCP](https://modelcontextprotocol.io) (Model Context Protocol) is the
open standard Anthropic introduced for AI tool servers. Linear, GitHub,
Stripe, Atlassian, Sentry, and dozens of other vendors publish official
MCP servers. qlaud lets you connect any of them with one POST — we open
a connection, list every tool the server exposes, and add them to your
account, prefixed with the server name you pick.

This is the third tool kind alongside [webhooks](/api-reference/tools)
and [built-ins](/api-reference/builtins). All three flow through the
same `/v1/threads/:id/messages` dispatch path; the model never sees a
difference.

<Warning>
  **All `/v1/mcp-servers` endpoints are master-key only.** Same posture as
  `/v1/tools` — registering an arbitrary MCP server URL is control plane,
  and a leaked per-user key shouldn't be able to point a discovery probe
  at attacker.com or shove tools into another tenant's roster.
</Warning>

## How it works

```
   POST /v1/mcp-servers              MCP server (Linear, Stripe, …)
        │                                      ▲
        │ name: "linear"                      │ tools/list
        │ server_url: "https://mcp.linear..."  │
        │ auth_headers: { Authorization: ... } │
        ▼                                      │
   ┌───────────────────────────────────────────┴──┐
   │  qlaud edge                                  │
   │  1. Connect (Streamable HTTP)                │
   │  2. tools/list → discover what's available   │
   │  3. Encrypt auth_headers (AES-GCM, write-only)│
   │  4. Insert mcp_servers row                    │
   │  5. Insert one `tools` row per discovered    │
   │     tool, prefixed: "linear/create_issue"     │
   └───────────────────────────────────────────────┘
```

When the model later invokes `linear/create_issue`, qlaud opens a fresh
MCP session, calls `tools/call` with the unprefixed name, and returns
the result — same shape the model expects from any tool.

## POST /v1/mcp-servers — Connect

```bash theme={null}
curl https://api.qlaud.ai/v1/mcp-servers \
  -H "x-api-key: $QLAUD_MASTER_KEY" \
  -H "content-type: application/json" \
  -d '{
    "name": "linear",
    "server_url": "https://mcp.linear.app/sse",
    "auth_headers": {
      "Authorization": "Bearer lin_oauth_…"
    }
  }'
```

We connect to the server BEFORE persisting. If the URL is bad, the auth
is wrong, or the server doesn't speak MCP, you get a clean 400 at
registration time — not a mid-chat dispatch failure.

### Body

| Field          | Type   | Required | Description                                                                                                                                                                                                                                               |
| -------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`         | string | yes      | Lowercase alphanumeric (with `-` or `_`), 1-31 chars. Becomes the prefix on every tool from this server. Per-account unique among non-revoked servers. Cannot start with `qlaud-builtin/`.                                                                |
| `server_url`   | string | yes      | The MCP server's Streamable HTTP endpoint. `https://` only.                                                                                                                                                                                               |
| `auth_mode`    | string | no       | `tenant` (default — `auth_headers` set here are used for every dispatch by every end-user) or `per_user` (each end-user supplies their own headers via `qlaud_manage_connections.connect` at runtime).                                                    |
| `auth_headers` | object | no       | Flat string→string map of headers sent on every call (e.g. `Authorization`, `X-API-Key`). Encrypted at rest. **Must be omitted when `auth_mode='per_user'`** — per-user mode means headers come from each end-user inline in chat, not from registration. |

### Response (201)

```json theme={null}
{
  "id": "mcp_a1b2c3d4...",
  "object": "mcp_server",
  "name": "linear",
  "server_url": "https://mcp.linear.app/sse",
  "tools_discovered": 12,
  "tools_registered": 12,
  "tools_skipped": [],
  "tools": [
    { "id": "tool_xxx", "name": "linear/create_issue" },
    { "id": "tool_yyy", "name": "linear/list_issues" },
    { "id": "tool_zzz", "name": "linear/update_issue" }
  ],
  "created_at": 1777262997717
}
```

`tools_skipped` lists any tools whose prefixed name conflicted with an
existing tool (registered as a webhook or built-in earlier). Rename
those first or pick a different MCP server prefix.

## GET /v1/mcp-servers — List

```bash theme={null}
curl https://api.qlaud.ai/v1/mcp-servers -H "x-api-key: $QLAUD_MASTER_KEY"
```

Returns every non-revoked MCP server you've connected. `auth_headers`
is never returned — only a `has_auth_headers: boolean` indicator.

## DELETE /v1/mcp-servers/:id — Disconnect

```bash theme={null}
curl -X DELETE https://api.qlaud.ai/v1/mcp-servers/$ID \
  -H "x-api-key: $QLAUD_MASTER_KEY"
```

Soft delete — the row stays for audit, hard-delete cron sweeps later.
**Cascades to tools**: every `tools` row backed by this server is
revoked at the same time. Existing thread audits referencing those
tools resolve cleanly; new thread messages can no longer use them.

## POST /v1/mcp-servers/:id/refresh — Re-discover

```bash theme={null}
curl -X POST https://api.qlaud.ai/v1/mcp-servers/$ID/refresh \
  -H "x-api-key: $QLAUD_MASTER_KEY"
```

Vendors add and remove tools. This re-calls `tools/list` and reconciles:
new tools get inserted, removed tools get revoked, matching tools stay
intact. Returns the diff:

```json theme={null}
{
  "id": "mcp_a1b2c3d4...",
  "refreshed": true,
  "tools_discovered": 13,
  "added": ["linear/create_comment"],
  "removed": []
}
```

## Tool naming convention

Every discovered tool is registered as `<server_name>/<original_name>`.
This means:

* The model sees `linear/create_issue` and knows it's a Linear tool.
* Two MCP servers can expose tools with the same bare name without
  colliding (e.g. `linear/create_issue` vs `github/create_issue`).
* The `<server_name>` half is a path in your namespace — tool names
  starting with `qlaud-builtin/` are reserved for the built-in catalog.

When dispatch happens, qlaud sends only the bare name (`create_issue`)
to the MCP server since that's what the server registered.

## Auth header encryption

`auth_headers` is encrypted with AES-GCM using the gateway's
`TOOL_CONFIG_ENC_KEY` secret before being persisted to D1. The headers
are only decrypted at dispatch time inside the worker. There is no read
path that returns the plaintext — to rotate a token, revoke the server
and re-register with the new one.

## Errors

| Status | Meaning                                                                                                                                                                                                                                                           |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400    | Body malformed (bad name format, non-https URL, malformed `auth_headers`), OR the MCP server returned an error during the discovery probe (bad auth, unreachable, no tools, protocol error). The error message names the failing stage (`connect`, `list_tools`). |
| 401    | Bad / revoked qlk key.                                                                                                                                                                                                                                            |
| 403    | Caller used a per-user (standard-scope) key. All `/v1/mcp-servers` endpoints require a master (admin-scope) key.                                                                                                                                                  |
| 409    | An MCP server with the same `name` already exists. Pick a different name or revoke the existing one.                                                                                                                                                              |
| 502    | Refresh failed because the server is unreachable. Server stays connected; tools cache stays as-is.                                                                                                                                                                |
| 503    | Gateway is missing the `TOOL_CONFIG_ENC_KEY` operator secret — MCP servers requiring `auth_headers` can't be registered until it's set. Servers with no auth still work.                                                                                          |

## Where MCP fits vs. the other tool kinds

| Kind                                    | When to use                                                                                                                 | Code you write            |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
| **[Built-in](/api-reference/builtins)** | The popular cases qlaud already curates (web search, Slack post, Linear issue, …) — paste an API key, done                  | None                      |
| **[Webhook](/api-reference/tools)**     | Custom business logic only your backend knows (read your DB, query your warehouse, post-process your data)                  | One HTTP handler per tool |
| **MCP**                                 | Any vendor that publishes an MCP server — you get the FULL surface (often 30+ tools per vendor) without us writing wrappers | None                      |

The general rule: try built-in first (curated UX, sensible defaults).
If you want broader vendor coverage than the curated builtins offer,
connect their MCP server. Use webhooks for the truly custom stuff that
no public tool can express.
