009 · MCP
MCP servers (spec_mcp_009)
| Status | Implemented. Reverse-engineered from 53f8c53 (2026-09-24) and kept with the code since; its paths and names checked 2026-09-27 (//tools/specs). |
| Source | pkg/engine/tools/mcp.go; pkg/engine/breaker |
| Tests | pkg/engine/tools/mcp_test.go, mcp_resilience_test.go; pkg/engine/runtime/mcp_agents_test.go |
| Depends on | spec_shell_007 (exec env, sandbox), spec_approvals_005, spec_models_015 (breaker) |
Model Context Protocol servers extend agents with external tools, over stdio (a child process) or streamable HTTP. Blitz decides which agents see which servers, prevents name clashes with built-in tools, gates calls with approval, bounds calls with timeouts, and pauses unhealthy servers.
- MCP-01 Each server needs a unique
nameand exactly one ofcommand(stdio, withargs,env) orurl(streamable HTTP); otherwise startup fails. - MCP-02 Optional:
tools(allow-list),auto_approve(skip approval),sandbox(stdio servers run in the OS sandbox unlessfalse),prefix(exposecreate_issueas<prefix>__create_issue),agents(empty = the active primary agent only;"*"= every agent includinginvoke_agentsub-agents),timeout_seconds(per call; default 300),headers(sent to an HTTP server),disabled(configured but not started). - MCP-03
blitz mcp add <name> <command> [args…] | <url> [--env K=V] [--header K:V] [--prefix p] [--agents a,b],list(with “disabled” and “signed in”),get(values masked),remove,enable,disableedit the global settings’[[mcp.servers]]entries in place (config.AddMCPServer,RemoveMCPServer,SetMCPServerDisabled: other text and comments kept, a server’s sub-tables removed with it; a name in use is refused) (PAR-MCP-01). - MCP-04 A project’s
.mcp.jsonand.agents/mcp_config.json({"mcpServers": {name: {command, args, env} | {url | httpUrl | serverUrl, headers}}}) are the project’s MCP servers, needing trust like those of.blitz/settings.tomland part of its hash (spec_project_config_031; PAR-MCP-06).
- MCP-10 Servers connect lazily when their tools are first listed (before a model call).
- MCP-11 Stdio servers run through the exec environment: process guard, scrubbed environment plus the server’s
env, and the OS sandbox unless disabled. A new process is started on every (re)connect and the previous one is killed, so a crashed server is restarted on the next call. 2026-10-03: Blitz owns each process (its pipes to the SDK’sIOTransport) and waits for it from the start, so a killed or crashed one is reaped at once, its connection closed or not; closing a connection closes the server’s input, then sends SIGTERM, then kills its process group, waiting 5 s after each. - MCP-12 All sessions (HTTP included) are tracked and closed when the workspace closes; stdio processes are stopped.
- MCP-20 Listing a server’s tools times out after 30 s. Tools not in
toolsare dropped. A tool whose (prefixed) name would shadow a built-in tool is skipped with a warning; a tool that can’t be renamed under a prefix is skipped. If two servers serve the same name, the first wins with a warning. - MCP-21 Ownership (tool → server) is recorded for approvals; prefixed tools call the server under the original name.
- MCP-22 In plan and read-only modes every MCP tool is refused (the resource tools excepted, MCP-35) (spec_engine_016 ENG-30).
- MCP-30 Unless the server is
auto_approve, each call needs approval: kindmcp_tool, detail<tool> (MCP server "<server>")plus sortedkey=valueargs, keymcp:<server>:<tool>, targetserver:tool. - MCP-31 Each call is bounded by the server’s timeout (“MCP tool X timed out after d”).
- MCP-32 Circuit breaker per server, threshold 2 consecutive transport failures (timeouts or “failed to call MCP tool” errors; tool errors reported by the server count as healthy). An open server’s tools disappear from the model’s list and calls fail “MCP server X is unavailable after repeated failures; retrying in d”. Cooldown 15 s doubling to 5 min; then one trial call.
- MCP-33 The user hears once when a server first fails, once when it is paused, and once when it recovers; every failure is logged. (2026-10-14: the service sends a workspace’s warnings to its log, so these are also the turn’s notices when they happen in a turn (
tools.WithNotices, set byruntime.WithTurnNotices), shown in the conversation in the terminal and the desktop app.)
- MCP-34 Each server gets its own MCP client (
MCPManager.mcpClient): a middleware notes the session every request uses, so resources and prompts go over the connection the tools use (connecting first if need be). - MCP-35 Resources (PAR-MCP-02): the tools
list_mcp_resources(server?)andread_mcp_resource(server, uri)— allowed in plan mode, approved like the server’s tools (kindmcp_tool, action named after the tool), text up to 256 KB, binary content described;@server:uriin a prompt attaches the resource’s text (<resource name=…>), as@pathattaches a file. - MCP-36 Prompts (PAR-MCP-03): every server’s prompts are custom commands
/mcp__<server>__<prompt>(ListCommands, each server waited on at most 5 s; the argument hint listsname=), run with arguments asname=valueor in the prompt’s order; a missing required argument is refused. The prompt’s text messages become the turn’s prompt. - MCP-37 Elicitation (PAR-MCP-05): a server’s request for input during a tool call is put to whoever that call’s run asks (the user, or a background task’s session:
Hooks.askUser), one question per requested field (booleans and enums as choices; integers and numbers parsed), or, for a URL, whether to continue; nobody to ask, no call in progress, or no answer declines. - MCP-38 OAuth (PAR-MCP-04,
pkg/mcpauth):blitz mcp login <name>runs the authorization-code flow with PKCE and dynamic client registration (go-sdk’sAuthorizationCodeHandler), the redirect to a local127.0.0.1callback or, over SSH, the address the browser ends on pasted; the client, endpoints and token are kept in the secret store asmcp-oauth-<name>;logoutforgets them. A signed-in HTTP server’s requests carry the token, refreshed and kept as it expires; a server that asks for authorization without one is told to runblitz mcp login.
- MCP-40
doctorchecks each stdio command is on PATH, and with--onlineconnects and counts tools./mcplists servers (spec_workspace_018 WS-72);/toolsshows which servers are offered to the active agent. - MCP-41 2026-10-14.
ListMCPServerslists every configured server, a disabled one marked (MCPServer.disabled;/mcpin the terminal and the desktop app says “disabled: configured, not started”), even when none could start; a disabled server’s tools are offered to no agent (/tools). - MCP-42 2026-10-14. An MCP server’s
headersvalues, and aBearer <token>’s token alone, are secrets to the redactor (logs, telemetry, the audit log) as itsenvvalues are, andblitz config showmasks them. - MCP-43 2026-10-14.
auto_approveskips the question for a server’s resources too, never a rule:read_mcp_resourceandlist_mcp_resourceswith a server followdenyandaskrules as its tools do (approveMCPServer). - MCP-44 2026-10-14. Blitz speaks stdio and streamable HTTP, not SSE. A project’s
.mcp.jsonor.agents/mcp_config.jsonentry with"type": "sse", or with neither or both of a command and a URL, is skipped, and the project’s problems say why. - MCP-45 Since v0.7.1 (the owner’s request). A settings change made through the service (
ConfigService: the desktop app’s forms, its settings file) applies to open workspaces’ MCP servers at once (Workspace.ReloadMCP, from the service’schanged, as permissions and providers reload):MCPManager.Updatekeeps a server whose settings are unchanged running, restarts a changed one, stops a removed or disabled one and adds a new one (started when its tools are first listed), and the agents are built again only when something changed. Wrong settings (no name, a name twice, neither or both of a command and a URL) change nothing.blitz mcpedits the file directly, so the service still picks those up when it restarts, as it says. - MCP-46 Since v0.7.1. Settings › MCP servers (advanced, after Skills;
McpSettings.tsx,mcpForm.ts): global, like all of Settings: the global settings’ servers in their order, each with its command line or address, a switch (on or off:disabled), Edit and Remove (asked first; a sign-in is kept); the file they’re kept in. Add server and Edit open a form: name (letters, digits,.,-,_; not another’s), a command (its arguments a line each,NAME=valueenvironment lines, Run it in the OS sandbox, on by default and written only when off) or a web address (Name: valueheader lines, and how to sign in:blitz mcp login <name>), Run its tools without asking, the timeout for one call and the agents offered its tools; problems are listed under it and nothing is saved until they’re fixed. Saving tells the window (configChanged). How a server does in a workspace (MCP-47’s state, Restart) and a project’s own servers belong to the workspace, not here (next). - MCP-47 Since v0.7.1. The APIs:
ConfigService.ListMCPServerConfigs(the global servers, asMCPServerConfig, and the file),SaveMCPServer(add, or replace the one named, renaming it if asked:config.ReplaceMCPServerwrites the entry afresh in its place, its comments lost, the rest of the file kept),RemoveMCPServer,SetMCPServerEnabled(an unknown name isNOT_FOUND,MCP_SERVER_NOT_FOUND; each answers aConfigChangeafter applying it);WorkspaceService.ListMCPServersgives each server’sstate(disabled,idle,connected,failed),errorandtools(MCPManager.States, from the last listing of its tools), andRestartMCPServerstops one and starts it afresh at the next listing (MCPManager.Restart;NOT_FOUNDwhen it isn’t running). - MCP-48 Since v0.7.1. The tray’s MCP servers submenu (
apps/tray:tray.MCPEntries, drawn bysubmenu): each global server as a check item, checked while it’s on; a click turns it off or on (SetMCPServerEnabled, so open workspaces follow, MCP-45). With none, a greyed note says to add them in Settings. The list is asked with the tray’s status every 2 s (FetchExtras) and the submenu redrawn only when it changes; restarting a server is per workspace, so it’s in Settings only.