Vaults & MCP
Connect agents to external tools — raw MCP servers, curated connectors, and credentials held in encrypted vaults.
Agents can call tools served by remote MCP (Model Context Protocol) servers. When a server needs a secret bearer token, you store it in an encrypted vault and attach the vault to a session — the agent definition itself never carries a credential.
MCP servers
MCP servers are configured on the agent, in mcp_servers. Streamable HTTP
servers are supported in v1.
In the agent form, add servers under MCP servers — each entry has a name, a URL, and optional auth and tool settings.
Set mcp_servers when creating or patching an agent:
{
"mcp_servers": [
{
"name": "gateway",
"url": "https://mcp.example.com",
"allowed_tools": ["search", "lookup"],
"tool_timeout_secs": 60
}
]
}| Field | Notes |
|---|---|
name | Unique within the agent; becomes the mcp__<name>__<tool> namespace. |
url | The server's Streamable HTTP endpoint. |
auth | none (default). See below. |
allowed_tools | An enable-list of tool names. Omit to allow every tool the server advertises (must be non-empty when present). |
tool_timeout_secs | Per-call timeout, 1–600. Defaults to 120. |
Auth
Agent-level MCP auth is secret-free — an agent definition never stores a token:
none— for public, unauthenticated servers.
For third-party servers that require a static bearer token, store the token in a vault (next section) rather than on the agent.
token_source is retired
The token_source auth type (a platform-minted service bearer) has been
retired. New or updated agent definitions that set auth.type: token_source
are rejected with a 400; use a vault credential or an OAuth connector
instead. Existing records that predate this change remain readable but
can no longer mint that credential.
Discover tools
Probe an agent's declared MCP servers and list the tools they expose:
curl https://agents.clusterbase.dev/v1/agents/agt_3f9c2a/mcp-tools \
-H "Authorization: Bearer $CLUSTER_TOKEN"Connectors
Connectors are a curated layer on top of raw MCP: a catalog of ready-made
integrations — hosted MCP services plus native ones such as Gmail, Google
Calendar, Google Drive, Slack and Outlook — that you
connect once with OAuth and then enable on agents by id. Unlike mcp_servers
(you supply the URL) or vault tokens (you supply the credential), a connector
needs neither: the platform hosts the integration, and each user authorizes
their own account.
List the catalog:
curl https://agents.clusterbase.dev/v1/mcp-services \
-H "Authorization: Bearer $CLUSTER_TOKEN"Each entry reports its id, display_name, backend (hosted_mcp or
hosted_slack, native_gmail, native_google_drive,
native_google_calendar, or native_outlook), auth_mode, and whether
you are already connected.
Connect
Connecting is a standard OAuth authorization-code flow. Start it, send the user
to the returned authorization_url, and complete it with the code the provider
returns:
# 1. Start — returns { "authorization_url": "...", "expires_at": "..." }
curl https://agents.clusterbase.dev/v1/mcp-services/gmail/connect/start \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-H "Origin: https://console.clusterbase.ai" \
-d '{ "return_url": "https://console.clusterbase.ai/" }'
# 2. The user authorizes in the browser; the provider redirects to Agents'
# own server-owned callback, which then redirects to `return_url` with
# an opaque `state` ticket (never a `code`).
# 3. Complete — the `state` identifies the flow, so there is no {id} in the
# path and no `code`: Agents already holds the provider's authorization
# code server-side.
curl https://agents.clusterbase.dev/v1/mcp-services/connect/complete \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "state": "..." }'The Console does all of this for you — its callback lives at
console.clusterbase.ai/mcp/callback. Disconnect with
DELETE /v1/mcp-services/{id}/connection.
Providers redirect to a single Agents-owned callback,
/v1/mcp-services/connect/callback, configured by the deployment's
MCP_OAUTH_PROVIDER_CALLBACK_URL — register that URL with the OAuth provider,
not an app origin. Agents keeps the provider's authorization code server-side
and redirects the browser to return_url with only the opaque state (and
error/error_description on failure). Web and Console both use the public
path /mcp/callback on their respective origins as return_url; Console's
host routing rewrites this path to its internal /console/mcp/callback page.
The return_url is the destination after completion, not the provider callback;
its origin must match the request's Origin header and an Agents-approved origin.
Use on an agent
Enable a connector by adding its id to the agent's
mcp_services list on create or patch. The
ids are catalog-validated, and the definition stores nothing secret — at run
time the connector's tools resolve with the session owner's connection.
Connector selection lives on the agent definition only; a per-message connector
field on ordinary session events is rejected.
Tune the tools
List a connector's tools and disable the ones you don't want:
curl https://agents.clusterbase.dev/v1/mcp-services/gmail/tools \
-H "Authorization: Bearer $CLUSTER_TOKEN"
curl -X PUT https://agents.clusterbase.dev/v1/mcp-services/gmail/tool-selection \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "disabled_tools": ["delete_email"] }'Use from an external MCP client
POST /v1/mcp-services/{id}/mcp bridges a connected service as stateless MCP
JSON-RPC (initialize, tools/list, tools/call) for clients other than an
agent — for example the Build CLI reusing your Cluster login instead of
connecting the service again. It requires a signed-in (non-guest) bearer and
resolves your current connection and disabled-tool selection on every call, so
a disconnect or tool change takes effect immediately. Provider credentials
never leave the server. Requests are capped at 1 MiB, tool calls at two
minutes, and the whole request at 125 seconds; closing the HTTP connection
cancels any in-flight discovery or call.
Vaults
A vault holds encrypted credentials. Every secret is encrypted at rest and never returned by any response. Three credential types are supported, in two categories — MCP credentials keyed to a specific MCP server URL, and an environment-variable credential keyed to a VM env-var name:
static_bearer— a fixed bearer token (API key / PAT). No refresh.mcp_oauth— an OAuth 2.0 access token with an optionalrefreshblock. When the access token expires, the runtime re-issues it automatically at thetoken_endpoint(using the stored refresh token) at connect time.environment_variable— a plaintext secret injected as a VM environment variable at provision time, for third-party keys the agent's in-VM tools read directly (e.g.NOTION_API_KEY). Unlike the MCP types — whose tokens are used only by the gateway — the agent process can read this value (the same way it readsGH_TOKEN), so use it only for keys the agent is meant to use. It's keyed onsecret_name(not a URL) and is applied only on the/v1/runslane (see Connecting them).
Console support
Vaults have no dedicated Console screen yet, but the agent builder can create a vault for you when you ask it to (e.g. "set up an MCP server with a vault token"). Full vault management is available over the API.
Create a vault, then add a credential to it:
# 1. Create a vault
curl https://agents.clusterbase.dev/v1/vaults \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "display_name": "Third-party APIs" }'
# → { "id": "vlt_1a2b3c", "display_name": "Third-party APIs", ... }
# 2. Add a static-bearer credential
curl https://agents.clusterbase.dev/v1/vaults/vlt_1a2b3c/credentials \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Example API token",
"auth": {
"type": "static_bearer",
"mcp_server_url": "https://mcp.example.com",
"token": "sk-the-secret-token"
}
}'The token is plaintext on the request only — it's encrypted before it
touches storage and is never echoed back. The credential response carries only
non-secret fields — its type, the mcp_server_url or secret_name key, and
display fields.
For an OAuth server, use mcp_oauth and supply a refresh block so tokens
renew themselves:
curl https://agents.clusterbase.dev/v1/vaults/vlt_1a2b3c/credentials \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Slack (OAuth)",
"auth": {
"type": "mcp_oauth",
"mcp_server_url": "https://mcp.slack.com/mcp",
"access_token": "xoxp-...",
"expires_at": "2026-01-01T00:00:00Z",
"refresh": {
"token_endpoint": "https://slack.com/api/oauth.v2.access",
"client_id": "1234.5678",
"scope": "chat:write",
"refresh_token": "xoxe-...",
"token_endpoint_auth": { "type": "client_secret_post", "client_secret": "..." }
}
}
}'token_endpoint_auth.type is none (public client), client_secret_basic, or
client_secret_post. If a refresh ever fails, the credential is dropped for that
connection (the agent proceeds unauthenticated) rather than failing the turn — to
diagnose why, call
POST /v1/vaults/{id}/credentials/{cid}/mcp_oauth_validate, which returns a
status of valid (works), invalid (re-authorize), or unknown (transient,
retry). Diagnosing may itself rotate and persist the refresh token.
For a secret your agent's in-VM tools read as an environment variable, use
environment_variable — there's no mcp_server_url, just the secret_name to
inject and its value:
curl https://agents.clusterbase.dev/v1/vaults/vlt_1a2b3c/credentials \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Notion API key",
"auth": {
"type": "environment_variable",
"secret_name": "NOTION_API_KEY",
"value": "ntn-the-secret-value"
}
}'secret_name must be a valid POSIX env-var name ([A-Za-z_][A-Za-z0-9_]*, up to
64 chars) and may not use a runtime-reserved name or prefix (PATH, IFS,
LD_*, or the GH_ / GIT_ / CCP_ / CLUSTA_ system prefixes) — a name that
breaks these rules is a 400. The value is write-only (encrypted at rest,
never echoed).
Connecting them
Attach vaults to a session with vault_ids when you create it:
curl https://agents.clusterbase.dev/v1/sessions \
-H "Authorization: Bearer $CLUSTER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "agent": "agt_3f9c2a", "vault_ids": ["vlt_1a2b3c"] }'At MCP connect time, the runtime resolves each server's credential by matching
the server url against credentials' mcp_server_url. The first attached
vault holding a credential whose mcp_server_url exactly matches the server
wins. You must own every vault you attach (a non-owned id returns 404).
environment_variable credentials are handled differently: they're injected as
VM environment variables at provision time, so they apply only when you
attach the vault to a run via vault_ids on POST /v1/runs
— not on POST /v1/sessions, where a VM may be warm-reused (env-var creds on a
session bind are ignored, with a server-side warning). On a name collision across
attached vaults, the first attached vault wins, mirroring the MCP rule.
Because these secrets are load-bearing, delivery is all-or-nothing: if the
run's env-var secrets can't be rendered onto the VM (a rare transient
provisioning fault, with no later reconcile on an ephemeral run VM), the run
fails with a 502 and the VM is torn down — rather than starting an agent that's
silently missing them. Retry the run.
Manage vaults and credentials with the standard endpoints. PUT /v1/vaults/{id}
updates a vault's display_name and/or metadata; archived vaults return
409. PUT /v1/vaults/{id}/credentials/{cid} rotates a static_bearer secret
and/or renames it. Structural keys (mcp_server_url / secret_name) are
immutable; changing mcp_server_url returns 400. Archive and recreate for
mcp_oauth, environment_variable, or structural-key changes. Remove a vault
or credential with DELETE (hard) or /archive (soft) on both
/v1/vaults/{id} and /v1/vaults/{id}/credentials/{cid}. List endpoints are
paginated (limit, has_more), return newest-first records, and exclude
archived records unless you pass include_archived=true. See the
API reference.