Connect MCP servers
Register a remote Model Context Protocol (MCP) server, call its tools over the API, and add it to an agent.
Central Hub stores the server's credentials encrypted and calls the server itself, so a client calls tools without holding the server's secret.
Endpoints
Every route selects the workspace with X-Space-Slug.
| Method | URL | Does | Requires | Auth |
|---|---|---|---|---|
GET | /api/v1/mcp-connections/ | List the MCP connections you can see | Read | Key or bearer |
GET | /api/v1/mcp-connections/catalog/ | List the ready-made servers the Connectors page offers | Read | Key or bearer |
POST | /api/v1/mcp-connections/ | Register a server; lists its tools | Create | Key or bearer |
POST | /api/v1/mcp-connections/test/ | List an unsaved config's tools; saves nothing | Create | Key or bearer |
GET | /api/v1/mcp-connections/{connection_id}/ | Read one, with discovered_tools; secrets are [REDACTED] unless ?mask_credentials=false | Read | Key or bearer |
PATCH | /api/v1/mcp-connections/{connection_id}/ | Edit or rotate credentials | Edit | Key or bearer |
DELETE | /api/v1/mcp-connections/{connection_id}/ | Delete it; 204 | Delete | Key or bearer |
POST | /api/v1/mcp-connections/{connection_id}/refresh-tools/ | List the server's tools again | Edit | Key or bearer |
POST | /api/v1/mcp-connections/{connection_id}/call/ | Call one tool | Read | Key or bearer |
GET | /api/v1/mcp-connections/{connection_id}/auth-status/ | Whether you can use it now | Read | Key or bearer |
POST | /api/v1/mcp-connections/{connection_id}/authenticate/init/ · …/callback/ | Connect your own account to an oauth2 server | Read | Key or bearer |
DELETE | /api/v1/mcp-connections/{connection_id}/authenticate/ | Disconnect your own account; 204 | Read | Key or bearer |
Requires names the MCP connections permission (mcp_connection:read, create, edit, delete).
Request fields
| Field | Type | Notes |
|---|---|---|
name | string, required | |
url | string, required | The server's MCP endpoint; outside local deployments it must be https and not a private host |
transport | streamable_http or sse, default streamable_http | |
auth_type | none, bearer_token, custom_header, basic_auth or oauth2, default none | |
credentials | object, default {} | Keys per auth_type, below |
scope_visibility | private or space, default private | space needs a second workspace member |
connector_key | string, optional | A catalog entry's key; the web app shows that vendor's logo for the connection |
auth_type | credentials |
|---|---|
none | {} |
bearer_token | token; sent as Authorization: Bearer <token> |
custom_header | header_name, header_value |
basic_auth | username, password |
oauth2 | Optional client_id, client_secret for servers without dynamic client registration; each user signs in |
Register an MCP server
-
Optional: check the config first with
POST /api/v1/mcp-connections/test/and the same body. The response is{"ok": true, "tools": [...], "server_instructions": null}. -
Register it. Central Hub connects, lists the tools and stores them in
discovered_tools:curl -X POST "$CENTRAL_HUB_URL/api/v1/mcp-connections/" \ -H "X-API-Key: $ALO_API_KEY" \ -H "X-Space-Slug: $WORKSPACE_SLUG" \ -H "Content-Type: application/json" \ -d '{ "name": "Issues MCP", "url": "https://mcp.example.com/mcp", "auth_type": "bearer_token", "credentials": {"token": "…"} }' -
Keep the
idfrom the201response (abridged):{ "id": 62, "name": "Issues MCP", "url": "https://mcp.example.com/mcp", "transport": "streamable_http", "auth_type": "bearer_token", "credentials": {"token": "[REDACTED]"}, "scope_visibility": "private", "discovered_tools": [ { "name": "search_issues", "title": null, "description": "Search issues by text.", "input_schema": { "type": "object", "required": ["query"], "properties": {"query": {"type": "string"}, "limit": {"type": "integer", "default": 5}} } } ], "last_error": null, "user_authenticated": true }
If the server cannot be reached or refuses the credentials, nothing is saved and the response is 422 with the reason.
Connect an OAuth server
An oauth2 server is saved without tools; each user then signs in with their own account.
-
Register it with
"auth_type": "oauth2".discovered_toolsis[]anduser_authenticatedisfalse. -
Start your sign-in:
curl -X POST "$CENTRAL_HUB_URL/api/v1/mcp-connections/63/authenticate/init/" \ -H "X-API-Key: $ALO_API_KEY" \ -H "X-Space-Slug: $WORKSPACE_SLUG" \ -H "Content-Type: application/json" \ -d '{"redirect_uri": "https://your-app.example.com/oauth/callback"}' -
Send the user to the returned
auth_url. When the server redirects back, send itscodeandstatetoPOST …/mcp-connections/63/authenticate/callback/; the response is the auth status withauthenticated: true. -
Fill
discovered_toolswithPOST …/mcp-connections/63/refresh-tools/.
To sign in before the connection exists, send {url, redirect_uri} to POST /api/v1/mcp-connections/authenticate/init/, then {code, state} to POST /api/v1/mcp-connections/authenticate/callback/; pass the returned handle as oauth_handle when you register the server.
Connect a server from the catalog
GET /api/v1/mcp-connections/catalog/ lists the ready-made servers the Connectors page offers:
[
{
"key": "intercom_eu",
"name": "Intercom (EU)",
"description": "Search conversations, contacts and Help Center articles.",
"url": "https://mcp.eu.intercom.com/mcp",
"auth_type": "bearer_token",
"logo_url": "/images/mcp-catalog/intercom_eu.png",
"docs_url": "https://developers.intercom.com/docs/guides/mcp"
}
]| Field | Notes |
|---|---|
key | Never changes; send it as connector_key |
url, auth_type | What to register; auth_type is oauth2, none, bearer_token or basic_auth |
logo_url | A path on the web app |
docs_url | Where the vendor issues the key; null when there is none |
To connect one, register it with its name, url and auth_type, connector_key set to its key, and the credentials that auth_type needs. For oauth2, sign in first and pass oauth_handle.
Call a tool
-
Read the connection and pick a tool:
curl "$CENTRAL_HUB_URL/api/v1/mcp-connections/62/" \ -H "X-API-Key: $ALO_API_KEY" \ -H "X-Space-Slug: $WORKSPACE_SLUG"Each
discovered_tools[].input_schemais whatargumentsmust satisfy. If the list is empty or stale, sendPOST …/mcp-connections/62/refresh-tools/. -
Call it.
argumentsis optional and defaults to{}:curl -X POST "$CENTRAL_HUB_URL/api/v1/mcp-connections/62/call/" \ -H "X-API-Key: $ALO_API_KEY" \ -H "X-Space-Slug: $WORKSPACE_SLUG" \ -H "Content-Type: application/json" \ -d '{"tool_name": "search_issues", "arguments": {"query": "renewal"}}' -
Read
result, the tool's output:{"result": {"issues": [{"id": "ISS-1", "title": "Renewal overdue (renewal)"}]}}
Rotate credentials
-
Send the new credentials to
PATCH /api/v1/mcp-connections/{connection_id}/. They merge into the stored ones:curl -X PATCH "$CENTRAL_HUB_URL/api/v1/mcp-connections/62/" \ -H "X-API-Key: $ALO_API_KEY" \ -H "X-Space-Slug: $WORKSPACE_SLUG" \ -H "Content-Type: application/json" \ -d '{"credentials": {"token": "…"}}' -
A change to
url,transport,auth_typeorcredentialsreconnects and refreshesdiscovered_tools; if that fails, the response is422and nothing changes.
Add an MCP server to an agent
- On every run: add the connection ID to the agent's
enabled_mcp_connection_ids, withPATCH /api/v1/agents-v2/agents/{agent_id}/or in a bundle'sagent.yaml, as in Create and configure an agent. - For one run: pass it in
forwardedProps.runtimeConfig.mcp_connection_ids; see Run an agent and stream its events.
Troubleshooting
| Response | Cause | Fix |
|---|---|---|
404 MCP connection not found | The ID does not exist, or it is another user's private connection | Check the ID with GET /api/v1/mcp-connections/ |
502 MCP tool 'nope' call failed: Unknown tool: nope | tool_name is not one of the server's tools | Use a name from discovered_tools; refresh them if stale |
502 MCP tool 'search_issues' call failed: Error executing tool search_issues: 1 validation error … | arguments do not satisfy the tool's input_schema | Fix arguments |
422 tool_name is required | Empty tool_name | Send a tool name |
428 {"code": "PER_USER_AUTH_REQUIRED", "reason": "not_connected", "connector_key": "mcp", "connection_id": 63, …} | An oauth2 server you have not connected; reason is needs_reauth when your token expired | Connect your account |
422 Couldn't reach 127.0.0.1:1. Check the server is running and the address is right. | Create, test or edit could not list the server's tools | Fix the URL or credentials |
422 Missing MCP credential field: token | A required credential for the auth_type is empty | Add it |
422 This MCP connection does not use OAuth. | authenticate/ on a server whose auth_type is not oauth2 | Rotate its credentials with PATCH instead |
409 Workspace sharing is available after a second member joins | scope_visibility: "space" in a one-member workspace | Keep private or invite a member |