pi-mcp-extension
Health Pass
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 10 GitHub stars
Code Warn
- process.env — Environment variable access in src/config.ts
- process.env — Environment variable access in src/server-manager.ts
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Full-featured, lightweight MCP extension for the Pi coding agent: stdio, SSE & Streamable HTTP transports, OAuth 2.1 (PKCE + DCR), lazy/eager lifecycles — minimal dependencies, zero bloat.
pi-mcp-extension
Connect Pi to any MCP server. Supabase, DeepSource, Playwright, Context7, filesystem, databases — if it speaks MCP, Pi can use it.
pi-mcp-extension is a production-ready Model Context Protocol client extension for the Pi coding agent. It manages server connections, discovers tools, and bridges them into Pi so the LLM can call them directly.
Features
- Multi-transport —
stdiosubprocesses,streamable-http(including"http"and URL-only shorthand), and legacysseout of the box - Auto-discovery — Paginated
tools/listwith cursor following (MCP 2025-03-26 spec) - Live tool refresh —
notifications/tools/list_changedtriggers re-discovery without restart - Clean cancellation —
AbortSignalpropagation vianotifications/cancelled - Smart reconnection — Fixed delay retry schedule (1s → 3s → 5s → 10s → 30s), configurable max retries, and automatic recovery when a server transport closes unexpectedly
- Health checks — Opt-in periodic pings to detect stale connections
- Safe process lifecycle — Child PID tracking (
StdioClientTransport.pid) with safety-netSIGKILLfor orphaned subprocesses - Stable tool names — Sanitized, collision-avoiding names (max 64 chars) that stay constant across reconnects
- No tool churn — Activate/deactivate pattern ensures tools don't flicker during reconnection
- Layered config & trust boundary — Global
<agent dir>/mcp.json, project.pi/mcp.json(loaded only in trusted projects viactx.isProjectTrusted()), and Pi 1.x extension-registered servers (pi.registerMcpServer()) - Rich tool results & annotations — MCP
imagecontent passthrough (ImageContent), plusreadOnlyHint,destructiveHint,idempotentHint,openWorldHintsurfaced in tool descriptions - On-demand agent tools & commands —
mcp_statusandmcp_connectagent tools plus/mcp,/mcp:start,/mcp:stop, and/mcp:authslash commands with server-name autocomplete - Structured error handling — Distinct error codes for config, connection, protocol, and tool-level failures
- OAuth 2.1 — Browser and headless/SSH authorization with PKCE, protected-resource discovery, Dynamic Client Registration, startup prompts for eager servers, cross-process lock recovery, token refresh, and secure local persistence (see OAUTH.md)
Installation
pi install npm:pi-mcp-extension
Or try without installing:
pi -e npm:pi-mcp-extension
Quick Start
Install the extension:
pi install npm:pi-mcp-extension(Pi 1.x) Disable Pi's built-in
mcpextension so Pi does not warn that both extensions are enabled at the same time — either runpi configand uncheckmcpunder Built-in extensions, or add"extensions": ["-builtin:mcp"]to~/.pi/agent/settings.json:{ "extensions": [ "-builtin:mcp" ] }Create a config file at
~/.pi/agent/mcp.json(global) or.pi/mcp.json(project-level):{ "mcpServers": { "supabase": { "transport": "streamable-http", "url": "https://mcp.supabase.com/mcp", "lifecycle": "eager" } } }Start Pi — your MCP tools are ready. Use
/mcpto check server status.
Configuration
Servers can be configured via JSON files or registered programmatically by other Pi 1.x extensions:
| Source | Precedence & Scope |
|---|---|
.pi/mcp.json |
Highest — Project-level overrides per server and per explicitly stated setting. Loaded on session_start only when the project is trusted (ctx.isProjectTrusted()); never read at bootstrap or in untrusted projects. |
~/.pi/agent/mcp.json |
Global — Applies to all projects (honors $PI_CODING_AGENT_DIR with ~ expansion when set: <agent dir>/mcp.json). |
pi.registerMcpServer() |
Extension-registered — Discovered via pi.getMcpServers() and updated live on mcp_servers_change events when replacing builtin:mcp. Any server with the same name in mcp.json takes precedence. |
Full Example
{
"settings": {
"toolPrefix": "mcp", // Tools appear as mcp_<server>_<tool> (default: "mcp")
"requestTimeoutMs": 30000, // Per-request timeout in ms (default: 30000)
"maxRetries": 5 // Max reconnection attempts, 0-10 (default: 5)
},
"mcpServers": {
// ── HTTP-based servers ───────────────────────────────────────────────────
"supabase": {
"transport": "streamable-http",
"url": "https://mcp.supabase.com/mcp",
"auth": {
"type": "oauth"
},
"lifecycle": "eager"
},
"deepsource": {
"url": "https://mcp.deepsource.io/mcp", // "transport" defaults to "streamable-http" when "url" is set
"lifecycle": "eager"
},
// ── Legacy SSE servers ───────────────────────────────────────────────────
"legacy-server": {
"transport": "sse",
"url": "https://example.com/sse",
"lifecycle": "lazy"
},
// ── stdio subprocess servers ─────────────────────────────────────────────
"pathfinder": {
"command": "/path/to/pathfinder-mcp",
"transport": "stdio",
"lifecycle": "eager"
},
"context7": {
"command": "npx",
"args": ["-y", "@context7/mcp"],
"transport": "stdio",
"lifecycle": "lazy"
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"],
"transport": "stdio",
"lifecycle": "lazy",
"env": {
"NODE_ENV": "production"
}
}
}
}
Global Settings
In project-level .pi/mcp.json, settings keys carry no defaults — only the keys explicitly stated in the project file override global settings.
| Field | Type | Default | Description |
|---|---|---|---|
toolPrefix |
string |
"mcp" |
Prefix for registered tools: <prefix>_<server>_<tool>. Must match ^[a-zA-Z][a-zA-Z0-9_]*$. |
requestTimeoutMs |
number |
30000 |
Default per-request timeout in milliseconds |
maxRetries |
number |
5 |
Max reconnection attempts (0–10) before giving up |
Per-Server Config
For Pi 1.x compatibility, server entries also accept type as an alias for transport, "http" as an alias for "streamable-http", oauth as an alias for auth, and timeout (in seconds) as an alias for requestTimeoutMs.
| Field | Type | Default | Description |
|---|---|---|---|
transport (or type) |
"stdio" | "streamable-http" | "http" | "sse" |
"stdio" ("streamable-http" when url is set without command) |
Transport protocol ("http" is normalized to "streamable-http") |
command |
string |
— | Executable to spawn (required for stdio) |
args |
string[] |
[] |
Arguments for the command |
env |
Record<string, string> |
— | Extra environment variables for the child process (literal values, no ${VAR} interpolation) |
url |
string |
— | Server URL (required for streamable-http and sse) |
headers |
Record<string, string> |
— | Static headers sent with streamable-http and sse requests |
auth (or oauth) |
object |
— | OAuth 2.1 configuration described below and in OAUTH.md (streamable-http / sse only) |
lifecycle |
"eager" | "lazy" |
"lazy" ("eager" for pi.registerMcpServer() unless enabled: false) |
eager = auto-start on session_start, lazy = connect on request via mcp_connect, /mcp:start, or /mcp:auth |
requestTimeoutMs (or timeout) |
number |
global setting | Per-server timeout override in ms (timeout is specified in seconds and multiplied by 1000) |
healthCheckIntervalMs |
number |
disabled | Opt-in ping interval in ms for proactive connection health monitoring |
OAuth Config
Configured under auth (or oauth). Token and client state are persisted per server in <agent dir>/mcp-auth/<hash>.json (0700 directory, 0600 files on POSIX) with atomic writes and automatic backup of corrupt state files. See OAUTH.md for architecture and troubleshooting details.
| Field | Type | Default | Description |
|---|---|---|---|
type |
"oauth" |
"oauth" |
Authentication type |
scope |
string |
discovered | Requested scope when the server does not advertise one in WWW-Authenticate |
clientId |
string |
dynamic registration | Pre-registered OAuth client ID (skips Dynamic Client Registration) |
clientSecret |
string |
— | Secret for a pre-registered confidential client |
redirectUrl (or oauth.callbackUrl / oauth.callbackPort) |
string |
http://127.0.0.1:19876/callback |
Local HTTP loopback callback (localhost, 127.0.0.1, or ::1 with path /callback and an explicit non-zero port; callbackPort maps to http://127.0.0.1:<port>/callback). Scans forward from 19876 when omitted; binds the exact host and port when configured. |
Commands
Server-name arguments provide autocomplete suggestions from the configured MCP servers. /mcp:auth suggests only servers configured for OAuth.
| Command | Description |
|---|---|
/mcp |
Show status summary of all configured servers |
/mcp <name> |
Show detailed status and stderr log for a specific server |
/mcp:start <name> |
Connect a server and wait for tool discovery |
/mcp:stop <name> |
Cancel a pending connection, startup prompt, or authorization, or stop a running server and deactivate its tools |
/mcp:auth <name> |
Connect with stored OAuth credentials; request browser authorization only if needed |
/mcp:auth <name> --reset |
Discard stored OAuth credentials and request fresh authorization |
Agent tools mcp_status and mcp_connect let Pi inspect configured servers and connect one named server without routine slash commands. mcp_status reports lifecycle, connection state, discovered and active tool counts, credential presence, and safe action hints without returning tokens, headers, or authorization URLs. mcp_connect activates discovered tools before returning success. Connection failures return fixed actionable categories instead of raw server response text.
OAuth startup, headless/SSH, and concurrency behavior
- Interactive sessions required for browser auth: Print and RPC sessions fail promptly when interactive authorization is required.
- Startup prompt for eager OAuth servers: Silent startup and background reconnects never open a browser unprompted or trigger Dynamic Client Registration when no client credentials are stored. When an
eagerOAuth server needs interactive authorization at startup in an interactive Pi session, Pi presents a sequential<name> needs authorizationprompt (Authorize now/Not now). ChoosingAuthorize nowruns the interactive OAuth flow; choosingNot nowleaves the server stopped, shows arun /mcp:auth <name>footer hint, and remembers the decline for the rest of the session. - Headless and SSH support: While authorization is pending, Pi displays the authorization URL and offers
Retry: open browser again,Paste redirect URL, andCancel authentication. On remote/SSH or headless machines, open the URL locally and either forward the callback port (ssh -L <port>:127.0.0.1:<port>) or paste the browser's finalhttp://127.0.0.1:<port>/callback?code=...&state=...redirect URL intoPaste redirect URL. Onlyhttps:(or loopbackhttp:) authorization URLs are passed to the system browser opener. - Coordinated connects and cross-process locks: Concurrent connect requests join an in-flight attempt, while
--resetsupersedes a normal connect and shares one pending reset. Cross-process interactive flows are serialized per server via<agent dir>/mcp-auth/<hash>.json.lockwith a 30s heartbeat and automatic reclamation of locks from dead local PIDs or stale heartbeats (>90s), while allowing silent token refreshes to persist rotated tokens.
Connection readiness and active tools
mcp_status reports tools.discovered and tools.active for each server. Connection state ready means the MCP handshake and discovery completed. It does not mean that another Pi extension has kept every discovered tool active.
mcp_connect checks current tool activation before reporting success, including when the server is already connected. If discovered tools are inactive, it reports that condition instead of overriding another extension's tool restrictions. Check the active tool policy or conflicting extension. A reconnect is not a substitute for fixing an extension that removes tools at each user turn.
How It Works
┌─────────────────────────────────────────────────────────┐
│ Pi Agent │
│ │
│ ┌──────────┐ ┌────────────────┐ ┌─────────────┐ │
│ │ index.ts │───▶│ server-manager │───▶│ tool-bridge │ │
│ │ (entry) │ │ (lifecycle) │ │ (MCP → Pi) │ │
│ └──────────┘ └───────┬────────┘ └──────┬──────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ config │ │ MCP SDK │ │ TypeBox │ │
│ │ (Zod) │ │ (transport) │ │ (schema) │ │
│ └──────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│ │
▼ ▼
Global/Project ┌──────────────┐
mcp.json files │ MCP Servers │
+ Extensions │ (stdio/http) │
└──────────────┘
- Config is loaded from global
<agent dir>/mcp.json, trusted project.pi/mcp.json(checked viactx.isProjectTrusted()onsession_start), and extension-registered servers (pi.getMcpServers()/mcp_servers_change), withmcp.jsontaking precedence over extension-registered servers and project entries overriding global ones - Eager servers connect at
session_start(offering a sequentialAuthorize now/Not nowprompt when an eager OAuth server needs authorization in an interactive session); lazy servers wait formcp_connect,/mcp:start, or/mcp:auth - Tools are discovered via paginated
tools/listcalls (cursor-based, up to 100 pages) before a server entersready - JSON Schema → TypeBox conversion registers tools with Pi-compatible parameter schemas
- Pi tools are registered as
<prefix>_<server>_<tool>(sanitized, max 64 chars with hash suffix) and pass through text andimagecontent blocks (ImageContent) - Tools activate/deactivate automatically as servers connect/disconnect
- AbortSignal is wired through to
notifications/cancelledfor clean cancellation - Reconnection & recovery uses a fixed delay schedule with configurable max retries on initial failure, health check failure, or unexpected transport close (
client.onclose) list_changednotifications trigger re-discovery, deactivating stale tools automatically- Session shutdown cancels pending auth/connection flows, stops the callback server, deactivates all tools, and cleanly stops all servers (with a
SIGKILLsafety net forstdiochild processes)
MCP Spec Compliance
Implements the MCP 2025-03-26 specification for tool clients:
| Feature | Status |
|---|---|
Protocol version 2025-03-26 (+ 2024-11-05 fallback) |
✅ |
stdio transport |
✅ |
streamable-http transport (including "http" / URL-only shorthand) |
✅ |
sse transport (legacy backwards compat) |
✅ |
| OAuth 2.1 authorization (PKCE S256, RFC 9728 Protected Resource Metadata, RFC 7591 DCR) | ✅ |
Cursor-based tools/list pagination |
✅ |
tools/call with parameters & image content (ImageContent) passthrough |
✅ |
roots/list capability (workspace root) |
✅ |
notifications/tools/list_changed (live refresh) |
✅ |
notifications/cancelled (AbortSignal) |
✅ |
notifications/message (structured logging) |
✅ |
isError: true distinction (protocol vs tool errors, non-text filtered on error) |
✅ |
Tool annotations (readOnlyHint, destructiveHint, etc.) |
✅ |
| Resources bridge | ⏳ v2 |
| Prompts bridge | ⏳ v2 |
| Sampling | ⏳ v2 |
Development
# Install dependencies
npm install
# Type check (strict mode)
npm run typecheck
# Run all tests
npm test
# Run integration tests only (real stdio server)
npm run test:integration
Testing
The test suite includes:
- Config & Pi 1.x compatibility — Zod schema,
$PI_CODING_AGENT_DIRresolution, global/project merge, project trust boundary (ctx.isProjectTrusted()), Pi 1.x schema aliases (type: "http", URL-only HTTP,oauth,timeout), and dynamicpi.getMcpServers()/mcp_servers_change - Server lifecycle & coordination — Connect, retry, unexpected close recovery, shutdown with PID
SIGKILLsafety net, health check, concurrent connect/reset coordination, and cancellation - OAuth 2.1 & auth locks — Challenge discovery, PKCE, Dynamic Client Registration, startup authorization prompts, headless/SSH redirect URL paste-back, URL scheme validation, cross-process lock heartbeat and stale lock reclamation
- Tool bridge & management tools — JSON Schema → TypeBox conversion, name sanitization, image content passthrough,
isErrorfiltering, activate/deactivate,mcp_status/mcp_connect, and slash command completions - End-to-end integration — Real mock MCP and OAuth servers over stdio and HTTP with tool discovery and execution
License
MIT
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found