MCP Servers

Connecting external Model Context Protocol servers.

pi-forge connects to MCP servers and surfaces their tools to the agent. Configure from Settings → MCP or by editing config files.

Pi has no native MCP support. The integration lives in packages/server/src/mcp/ — see manager.ts for the contract.

Scope

Where servers live

Two layers, merged at session create time:

Scope File Editable from UI?
Global ${FORGE_DATA_DIR}/mcp.json Yes (Settings → MCP)
Project <projectPath>/.mcp.json No — edit in your repo

Project entries override global entries when names collide (per-server, not per-tool — add a project entry with the same name to swap a global server for a project-specific one).

File format

mcp.json (pi-forge-native shape, written by the UI):

{
  "disabled": false,
  "servers": {
    "my-server": {
      "url": "https://mcp.example.com/sse",
      "transport": "auto",
      "enabled": true,
      "headers": {
        "Authorization": "Bearer sk-..."
      }
    }
  }
}

Project .mcp.json also accepts the Claude Desktop / pi-mcp-adapter shape, so existing files don't need rewriting:

{
  "mcpServers": {
    "chrome-devtools": {
      "url": "https://mcp.example.com/sse"
    }
  }
}

Field reference

Field Type Default Notes
url string (required) The MCP endpoint URL.
transport "auto" | "streamable-http" | "sse" "auto" Connection probe order. auto tries StreamableHTTP first.
enabled boolean true Disabled servers don't connect or contribute tools.
headers Record<string, string> (none) Forwarded on every MCP RPC. Treated as secret on read — GET /mcp/servers returns ***REDACTED*** for every value.
disabled boolean (top-level) false Master kill-switch. When true, NO MCP tools reach the agent regardless of per-server enabled. Surfaced as the toggle at the top of Settings → MCP.

How the agent sees the tools

Each MCP tool from a connected, enabled server becomes a pi ToolDefinition namespaced as <server>__<tool> — the prefix keeps two servers' search tools from colliding.

CallToolResult.content is mapped into pi's content shape:

isError: true prefixes the first text block with [error].

Lifecycle

Header status badge

The badge next to Settings shows a colored dot + MCP X/Y:

Hidden when no servers are configured and in MINIMAL_UI mode.

Troubleshooting

Status stuck in error — Settings → MCP, expand the row, read lastError. Common causes: wrong URL, missing Authorization header, server returning 4xx on tools/list. Probe forces a reconnect + tool re-list.

Both transports fail — pin transport explicitly. The auto probe round-trip wastes ~100 ms per reconnect when only one transport actually works.

Headers show ***REDACTED*** — read-path sentinel, not real data. The on-disk file still has the real value. On save, a sentinel value preserves the prior secret; a new value overwrites it (same pattern as models.json).

Tools don't appear after editing project .mcp.json — the file is read once per project per server lifetime. Probe the row or restart pi-forge.

API surface

All routes under /api/v1/mcp/:

Method Path Purpose
GET /mcp/settings Master enable + connected/total count
PUT /mcp/settings Toggle the master flag
GET /mcp/servers[?projectId=…] Global config (redacted) + status
PUT /mcp/servers/:name Upsert a global server
DELETE /mcp/servers/:name Remove a global server
POST /mcp/servers/:name/probe[?projectId=…] Force reconnect + re-list
GET /mcp/tools?projectId=… Flat tool list available to the project's sessions

Request/response schemas in the Swagger UI at /api/docs.

See also