Using MCP Servers
Overview
There are two contexts for MCP servers: admin (changing what exists) and
chat (using what exists). Agents in chat discover and use servers directly
from the platform catalog — no workspace enable step required. Workspace
enable/disable only controls workspace.yml overrides, not chat visibility.
When to Use
- User asks to add, remove, enable, disable, or configure an MCP server
- Agent must choose between
search_mcp_servers, install_mcp_server,
enable_mcp_server, disable_mcp_server, or delete_mcp_server
- Agent needs MCP tools inside a
delegate sub-agent
- MCP connection fails or tool names collide across servers
Two contexts
| Context |
Who asks |
What changes |
Key rule |
| Admin |
User talking to settings / UI |
Catalog or workspace config |
install/delete mutate the catalog; enable/disable mutate workspace YAML |
| Chat |
Agent during a conversation |
Nothing — uses existing servers |
list_mcp_servers(scope?) for the MCP-only inventory; list_mcp_tools({serverId}) for a server's tool catalog; describe_mcp_server({id}) for back-references (which agents/jobs use it). enable is irrelevant. |
Critical: Chat agents do not need enable_mcp_server to see or use a
catalog server. enable only adds workspace-level overrides or custom
servers to workspace.yml.
Discovery tool selection: prefer the per-domain list_mcp_servers for
"what MCP servers can I use?" — it returns enabled + catalog entries
without bundled-agent noise. list_capabilities is the cross-domain router
("I don't know whether this is an agent or an MCP server"); reach for it
only when the question genuinely spans both surfaces.
Admin path: choosing the right tool
| User says |
Tool |
Why |
| "Install X from the registry" / "Search for X" |
search_mcp_servers → install_mcp_server |
Adds to the platform catalog |
| "Add X to this workspace" |
If X is in catalog: enable_mcp_server(X) |
Copies into workspace.yml for non-chat uses (FSM jobs, bundled agents) |
| "Remove X from this workspace" |
disable_mcp_server(X) |
Removes from workspace.yml. Catalog entry stays. |
| "Delete X entirely" / "Uninstall X" |
delete_mcp_server(X) |
Destructive — removes from catalog. Only for custom/user-created servers. |
- "Add" is ambiguous. Ask: add to catalog (
install) or add to workspace
YAML (enable)? Chat agents don't need either.
- "Remove" vs "Delete" is ambiguous. Ask: remove from workspace (
disable)
or delete from catalog (delete)?
- Disable is safe by default. Returns 409 +
willUnlinkFrom if agents/jobs
reference it. Surface the list, ask the user, retry with force: true.
Chat path: delegation with MCP tools
- Discover:
list_mcp_servers(scope?) — scope=workspace (default)
returns enabled + available; scope=catalog returns the global catalog
only; scope=all returns both. Each entry's requiresConfig empty
means credentialed. For cross-domain "what can I do here?" questions
that may resolve to a bundled agent or an MCP server, use
list_capabilities instead.
- Connect if needed: If
requiresConfig is non-empty and provider is
present (on mcp_available), call connect_service(provider). The UI will
fire data-credential-linked when the user finishes.
- Auto-continue: On
data-credential-linked, re-call list_mcp_servers
to confirm requiresConfig is empty, then delegate with
mcpServers: [id] and the original goal.
- Delegate:
delegate validates IDs fail-fast. Unknown or unconfigured
IDs return ok: false before any connection attempt.
- Tool naming: Child gets prefixed tools (
strava_getActivity) when 2+
servers are selected; unprefixed (getActivity) when 1.
- Graceful degradation: One failed server does not kill the child.
serverFailures surfaces reasons to the parent.
Setting an MCP server's env vars
A server often needs config beyond a credential — a workspace slug, a region,
a log path, a base URL. Two distinct stores, picked by what the value is:
- Non-secret config (slug, path, region, URL) →
env_set. It raises a
confirmation card and returns pending_confirmation — nothing is written
until the user confirms. Verify with env_get on a later turn. This is the
only supported way to touch a workspace's .env from chat.
- Credentials (API keys, tokens, OAuth) →
connect_service. Never put a
real secret through env_set — the workspace .env is the non-secret store,
and env_get masks secret-looking keys.
Never hand-edit .env files through run_code (curl/python/sed). It has
corrupted the daemon .env in practice. If a delegate or list_mcp_tools
call fails because a required env var is unset, the fix is env_set.
Common Mistakes
| Mistake |
Why it happens |
Fix |
Hand-editing .env via run_code to set a server's env var |
Didn't know env_set exists |
env_set for non-secret config, connect_service for credentials. Never curl/python the .env. |
Calling enable so chat can use a server |
Confusing workspace YAML with chat discovery |
Catalog servers are visible to list_capabilities (as kind: "mcp_available") without enable. |
Calling delete when user said "remove from workspace" |
Confusing catalog deletion with workspace disable |
"Remove from workspace" = disable. "Delete from catalog" = delete. |
Calling install when user said "add to workspace" |
Catalog installation does not wire into workspace YAML |
Check catalog first. If present, ask: install (catalog) or enable (workspace YAML)? |
Delegating without calling list_capabilities |
LLM assumes server IDs from stale prompt |
Always list first. Validate IDs server-side. |
Ignoring non-empty requiresConfig |
Async credential check is accurate |
Prompt user to connect_service. Do not attempt connection. |
| Re-enabling a custom server after disable |
Custom servers have no catalog backing |
Re-enable requires manual YAML editing. Say this explicitly. |
Quick diagnostic
- An agent asks for a tool that fails as unknown → do not create a tool-access elicitation for the guessed name. Call
list_mcp_servers to see what's wired, then list_mcp_tools({ serverId }) for the chosen server, and use the exact runtime-visible tool name.
- User says "I don't see X" →
list_mcp_servers(scope=all) to see both enabled and catalog. If it's there but requiresConfig is non-empty → connect_service.
- User says "Add X to workspace" → Check if X is in catalog. If yes, ask:
chat already sees it — do you mean enable for workspace YAML?
- Disable fails → Surface
willUnlinkFrom, confirm, retry with force: true.
1---2name: using-mcp-servers3description: Use when choosing between install, enable, disable, or delete for an MCP server; when an agent needs to discover or delegate to MCP servers; or when MCP tools fail with credential, connection, or prefixing issues.4---56# Using MCP Servers78## Overview910There are two contexts for MCP servers: **admin** (changing what exists) and11**chat** (using what exists). Agents in chat discover and use servers directly12from the platform catalog — no workspace enable step required. Workspace13enable/disable only controls `workspace.yml` overrides, not chat visibility.1415## When to Use1617- User asks to add, remove, enable, disable, or configure an MCP server18- Agent must choose between `search_mcp_servers`, `install_mcp_server`,19 `enable_mcp_server`, `disable_mcp_server`, or `delete_mcp_server`20- Agent needs MCP tools inside a `delegate` sub-agent21- MCP connection fails or tool names collide across servers2223## Two contexts2425| Context | Who asks | What changes | Key rule |26|---------|----------|--------------|----------|27| Admin | User talking to settings / UI | Catalog or workspace config | `install`/`delete` mutate the catalog; `enable`/`disable` mutate workspace YAML |28| Chat | Agent during a conversation | Nothing — uses existing servers | `list_mcp_servers(scope?)` for the MCP-only inventory; `list_mcp_tools({serverId})` for a server's tool catalog; `describe_mcp_server({id})` for back-references (which agents/jobs use it). `enable` is irrelevant. |2930**Critical:** Chat agents do not need `enable_mcp_server` to see or use a31catalog server. `enable` only adds workspace-level overrides or custom32servers to `workspace.yml`.3334**Discovery tool selection:** prefer the per-domain `list_mcp_servers` for35"what MCP servers can I use?" — it returns enabled + catalog entries36without bundled-agent noise. `list_capabilities` is the cross-domain router37("I don't know whether this is an agent or an MCP server"); reach for it38only when the question genuinely spans both surfaces.3940## Admin path: choosing the right tool4142| User says | Tool | Why |43|-----------|------|-----|44| "Install X from the registry" / "Search for X" | `search_mcp_servers` → `install_mcp_server` | Adds to the platform catalog |45| "Add X to this workspace" | If X is in catalog: `enable_mcp_server(X)` | Copies into `workspace.yml` for non-chat uses (FSM jobs, bundled agents) |46| "Remove X from this workspace" | `disable_mcp_server(X)` | Removes from `workspace.yml`. Catalog entry stays. |47| "Delete X entirely" / "Uninstall X" | `delete_mcp_server(X)` | Destructive — removes from catalog. Only for custom/user-created servers. |4849- **"Add" is ambiguous.** Ask: add to catalog (`install`) or add to workspace50 YAML (`enable`)? Chat agents don't need either.51- **"Remove" vs "Delete" is ambiguous.** Ask: remove from workspace (`disable`)52 or delete from catalog (`delete`)?53- **Disable is safe by default.** Returns 409 + `willUnlinkFrom` if agents/jobs54 reference it. Surface the list, ask the user, retry with `force: true`.5556## Chat path: delegation with MCP tools57581. **Discover:** `list_mcp_servers(scope?)` — `scope=workspace` (default)59 returns enabled + available; `scope=catalog` returns the global catalog60 only; `scope=all` returns both. Each entry's `requiresConfig` empty61 means credentialed. For cross-domain "what can I do here?" questions62 that may resolve to a bundled agent or an MCP server, use63 `list_capabilities` instead.642. **Connect if needed:** If `requiresConfig` is non-empty and `provider` is65 present (on `mcp_available`), call `connect_service(provider)`. The UI will66 fire `data-credential-linked` when the user finishes.673. **Auto-continue:** On `data-credential-linked`, re-call `list_mcp_servers`68 to confirm `requiresConfig` is empty, then `delegate` with69 `mcpServers: [id]` and the original goal.704. **Delegate:** `delegate` validates IDs fail-fast. Unknown or unconfigured71 IDs return `ok: false` before any connection attempt.725. **Tool naming:** Child gets prefixed tools (`strava_getActivity`) when 2+73 servers are selected; unprefixed (`getActivity`) when 1.746. **Graceful degradation:** One failed server does not kill the child.75 `serverFailures` surfaces reasons to the parent.7677## Setting an MCP server's env vars7879A server often needs config beyond a credential — a workspace slug, a region,80a log path, a base URL. Two distinct stores, picked by what the value *is*:8182- **Non-secret config** (slug, path, region, URL) → `env_set`. It raises a83 confirmation card and returns `pending_confirmation` — nothing is written84 until the user confirms. Verify with `env_get` on a *later* turn. This is the85 only supported way to touch a workspace's `.env` from chat.86- **Credentials** (API keys, tokens, OAuth) → `connect_service`. Never put a87 real secret through `env_set` — the workspace `.env` is the non-secret store,88 and `env_get` masks secret-looking keys.8990Never hand-edit `.env` files through `run_code` (curl/python/sed). It has91corrupted the daemon `.env` in practice. If a `delegate` or `list_mcp_tools`92call fails because a required env var is unset, the fix is `env_set`.9394## Common Mistakes9596| Mistake | Why it happens | Fix |97|---------|---------------|-----|98| Hand-editing `.env` via `run_code` to set a server's env var | Didn't know `env_set` exists | `env_set` for non-secret config, `connect_service` for credentials. Never curl/python the `.env`. |99| Calling `enable` so chat can use a server | Confusing workspace YAML with chat discovery | Catalog servers are visible to `list_capabilities` (as `kind: "mcp_available"`) without enable. |100| Calling `delete` when user said "remove from workspace" | Confusing catalog deletion with workspace disable | "Remove from workspace" = `disable`. "Delete from catalog" = `delete`. |101| Calling `install` when user said "add to workspace" | Catalog installation does not wire into workspace YAML | Check catalog first. If present, ask: install (catalog) or enable (workspace YAML)? |102| Delegating without calling `list_capabilities` | LLM assumes server IDs from stale prompt | Always list first. Validate IDs server-side. |103| Ignoring non-empty `requiresConfig` | Async credential check is accurate | Prompt user to `connect_service`. Do not attempt connection. |104| Re-enabling a custom server after disable | Custom servers have no catalog backing | Re-enable requires manual YAML editing. Say this explicitly. |105106## Quick diagnostic1071081. An agent asks for a tool that fails as unknown → do **not** create a tool-access elicitation for the guessed name. Call `list_mcp_servers` to see what's wired, then `list_mcp_tools({ serverId })` for the chosen server, and use the exact runtime-visible tool name.1092. User says "I don't see X" → `list_mcp_servers(scope=all)` to see both enabled and catalog. If it's there but `requiresConfig` is non-empty → `connect_service`.1103. User says "Add X to workspace" → Check if X is in catalog. If yes, ask:111 chat already sees it — do you mean enable for workspace YAML?1124. Disable fails → Surface `willUnlinkFrom`, confirm, retry with `force: true`.