MCP (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
and built-ins. All three flow through the
same /v1/threads/:id/messages dispatch path; the model never sees a
difference.
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.
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
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)
{
"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
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
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
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:
{
"id": "mcp_a1b2c3d4...",
"refreshed": true,
"tools_discovered": 13,
"added": ["linear/create_comment"],
"removed": []
}
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_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. |
| Kind | When to use | Code you write |
|---|
| Built-in | The popular cases qlaud already curates (web search, Slack post, Linear issue, …) — paste an API key, done | None |
| Webhook | 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.