pi-mcp-extension
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.
Package details
Install pi-mcp-extension from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-mcp-extension- Package
pi-mcp-extension- Version
1.11.10- Published
- Oct 29, 2026
- Downloads
- 779.8K/mo · 473.3K/wk
- Author
- irahardianto
- License
- MIT
- Types
- extension
- Size
- 401.9 KB
- Dependencies
- 2 dependencies · 3 peers
Pi manifest JSON
{
"image": "https://raw.githubusercontent.com/irahardianto/pi-mcp-extension/main/docs/icon-preview.png",
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
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.
Quick Start · Configuration · OAuth 2.1 · Commands & Agent Tools · Debug Log · How It Works · MCP Spec Compliance
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, automatic recovery when a server transport closes or errors unexpectedly, and mid-session HTTP
401/403 insufficient_scopedetection that invalidates stale tokens and prompts for re-authorization - Health checks — Opt-in periodic pings to detect stale connections
- Safe process lifecycle — Child PID tracking with tree-aware cleanup: descendants of wrapper commands (
npx,npm exec,uvx,sh -c) are terminated too, and inherited pipes are released so teardown never stalls - Stable tool names — Sanitized, collision-avoiding names (
<prefix>_<server>_<tool>, 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, including Pi 1.3.0 partial overrides), and Pi 1.x extension-registered servers (pi.registerMcpServer()) - Rich tool results & annotations — MCP
text,image(ImageContent),structuredContent, embeddedresourcetext/blobs, andaudiofallback descriptions, plusreadOnlyHint,destructiveHint,idempotentHint, andopenWorldHintsurfaced in tool descriptions - On-demand agent tools & commands —
mcp_statusandmcp_connectagent tools plus/mcp,/mcp <name>(with config provenance and failure hints),/mcp:start,/mcp:stop, and/mcp:authslash commands with server-name autocomplete - Structured error handling — Distinct error codes (
config,connection,auth,timeout,protocol,tool) and actionable failure hints - Redacted debug logging — Opt-in
PI_MCP_DEBUG=1JSON Lines log (<agent dir>/logs/pi-mcp.log,0600, ~2 MB rotation) with automatic secret masking and zero TUIstderrnoise - 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 origin/issuer-bound local persistence (see OAUTH.md)
Quick Start
Prerequisite: Node.js 22+ and the Pi coding agent.
Install the extension (or run once without installing via
pi -e npm:pi-mcp-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(trusted project). Both files support JSONC (//line and/* ... */block comments):{ "mcpServers": { // Remote HTTP server with OAuth 2.1 (prompts "Authorize now" on startup) "supabase": { "transport": "streamable-http", "url": "https://mcp.supabase.com/mcp", "auth": { "type": "oauth" }, "lifecycle": "eager" }, // Local stdio subprocess server (connects on demand via /mcp:start or mcp_connect) "context7": { "command": "npx", "args": ["-y", "@context7/mcp"], "lifecycle": "lazy" } } }Start Pi — your MCP tools are ready. Use
/mcpto check all servers or/mcp <name>to inspect one in detail.
Configuration
Servers can be configured via JSON/JSONC files (// and /* */ comments are supported) 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
See also examples/mcp.json.example for a copy-ready template.
{
"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 servers with OAuth 2.1 ──────────────────────────────────────────
"supabase": {
"transport": "streamable-http",
"url": "https://mcp.supabase.com/mcp",
"auth": {
"type": "oauth"
},
"lifecycle": "eager"
},
"deepsource": {
"url": "https://mcp.deepsource.com/mcp", // "transport" defaults to "streamable-http" when "url" is set
"auth": {
"type": "oauth"
},
"lifecycle": "eager"
},
// ── HTTP servers with static API key / Bearer headers ────────────────────
"zai-web-search": {
"transport": "streamable-http",
"url": "https://api.z.ai/api/mcp/web_search_prime/mcp",
"headers": {
"Authorization": "Bearer YOUR_ZAI_API_KEY"
},
"lifecycle": "lazy"
},
// ── 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"],
"cwd": "./",
"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 / 1.3.0 compatibility, server entries also accept:
typeas an alias fortransport, and"http"as an alias for"streamable-http"oauthas an alias forauthtimeout(in seconds) as an alias forrequestTimeoutMsenabled: falseto keep the server configured but out of the eager set (normalized tolifecycle: "lazy", even iflifecycle: "eager"is also set)
[!NOTE] Pi's
auth.provider(/loginprovider token) is rejected at load time with aconfigerror instead of being silently downgraded to OAuth 2.1 — useauth(OAuth 2.1) or staticheaders.
| 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 |
cwd |
string |
session working directory | Working directory for a stdio server (rejected on streamable-http / sse). A relative path resolves against the session working directory and a leading ~ expands to your home directory |
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. When auth is set, OAuth owns Authorization, so an Authorization entry here is ignored (other headers still apply) |
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 |
enabled |
boolean |
true |
Pi 1.3.0 switch: false keeps the entry configured but out of the eager set (normalized to lifecycle: "lazy", even when lifecycle: "eager" is also set) |
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 |
Project Override Entries
A trusted project's .pi/mcp.json may carry a Pi 1.3.0 partial override for a global server: an entry with no command, url, type, or transport changes only per-project settings and keeps the rest of the global entry, including credentials the project cannot set itself.
// ~/.pi/agent/mcp.json
{ "mcpServers": { "internal-tools": { "command": "internal-mcp", "lifecycle": "eager" } } }
// <project>/.pi/mcp.json — turn it off for this project only
{ "mcpServers": { "internal-tools": { "enabled": false } } }
An override may set enabled, lifecycle, and Pi's exposure / toolExposure (the two Pi-native exposure keys are accepted for Pi compatibility and ignored, because this extension decides tool activation itself). Any other key, or an override for a name no global entry defines, is reported as a config error naming the file and the server.
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, origin (serverUrl) and authorization-server (issuer) binding, 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 (used in memory; never written to disk) |
clientName (or oauth.clientName) |
string |
pi-mcp/<server name> |
client_name sent with Dynamic Client Registration, for servers that only accept known clients |
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.
| Slash Command | Description |
|---|---|
/mcp |
Show status summary of all configured servers |
/mcp <name> |
Show detailed status for one server: config source, transport/lifecycle, tool activation, OAuth credential status, failure hint, and stderr log |
/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 (or force a fresh reconnect if already ready) using stored OAuth credentials and client registration; request browser authorization only if needed |
/mcp:auth <name> --reset |
Discard stored tokens and client registration (re-running Dynamic Client Registration) and request fresh authorization (--reset may appear before or after <name>) |
In addition to user slash commands, two built-in agent tools let the LLM inspect and connect configured servers directly:
| Agent Tool | Parameters | Description |
|---|---|---|
mcp_status |
{} |
List configured servers with lifecycle, state, tools (discovered / active), auth credential summary, source ("global" / "project" / "extension" / "unknown"), and safe error action hints (never returns tokens, headers, or authorization URLs) |
mcp_connect |
{ name: string } |
Connect one named server, run interactive OAuth if needed, and verify that discovered tools are active before returning success (returns fixed actionable categories on failure instead of raw server response text) |
mcp_status returns a JSON array of ServerStatusRow objects:
[
{
"name": "supabase",
"lifecycle": "eager",
"state": "ready",
"tools": { "discovered": 12, "active": 12 },
"auth": "credentials stored",
"source": "global"
}
]
Inspecting one server (/mcp <name>)
/mcp <name> answers "why is this server in this state?" without leaving Pi:
Server: supabase
State: stopped
Retries: 5
Transport: streamable-http
Lifecycle: lazy
Source: global
Tools: 0/0 active
OAuth: credentials stored
Last error: Connection timed out after 30000ms
Hint: run with PI_MCP_DEBUG=1 pi to log redacted diagnostics, or report server issues at https://github.com/irahardianto/pi-mcp-extension/issues
Recent output:
(no stderr output from supabase)
Sourceis the entry that won:global(<agent dir>/mcp.json),project(<cwd>/.pi/mcp.json), orextension(pi.registerMcpServer()).mcp_statusreports the same value assourceon each row (or"unknown"if unrecorded).- When the project is untrusted,
Sourcenever saysprojectand aProject config: not loaded (<path>)line explains whymcp.jsonin the workspace is not in effect. Toolscompares discovered tools with the ones Pi has active; a server can bereadywith0/N activewhen another extension restricts tools.OAuthappears only for servers with anauthconfig:credentials stored,authorization may be required, orcorrupt state file backed up (re-auth required)(the corrupt file is backed up and the view still renders;mcp_statusreports"not configured"whenauthis unset).- A failed server adds one
Hint:line:/mcp:auth <name>(plus--resetwhen stored credentials expired) for a missing/expired authorization, waiting for another Pi process holding the auth lock, orPI_MCP_DEBUG=1 piplus the issue tracker for a connection failure.
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. - Resetting stale client registrations (
--reset):/mcp:auth <name>reuses the cached Dynamic Client Registration (clientInfo.client_id) stored in<agent dir>/mcp-auth/<hash>.json. If an OAuth provider expires or purges a previously registered client—for example, when the browser displays{"message":"Unrecognized client_id"}orinvalid_clienton the authorization page without redirecting back to the local callback—run/mcp:auth <name> --resetto discard the cachedclient_id, register a new client via Dynamic Client Registration, and start a fresh authorization flow.
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.
Debug log
Set PI_MCP_DEBUG=1 before starting Pi to record what the extension itself is doing — server state transitions, retries and backoff, OAuth refresh and cross-process lock contention, 401/403 classification, and session reloads:
PI_MCP_DEBUG=1 pi
The log is written to <agent dir>/logs/pi-mcp.log (JSON Lines, one event per line):
{"ts":"2026-01-01T00:00:00.000Z","level":"info","event":"server.state","server":"github","message":"Server github state starting -> ready","from":"starting","to":"ready","ms":412}
- Off by default. Without
PI_MCP_DEBUG=1no log file is created. - Private. The directory is
0o700and the file is0o600, like the OAuth credential files. - Bounded. The file is rotated to
pi-mcp.log.1at ~2 MB; a single backup is kept. - Redacted. Only allow-listed field names are recorded, and a delimiter-aware pass masks anything that looks like a credential:
- OAuth tokens, authorization codes, and PKCE verifiers
- API keys and other JSON or key/value secrets (
api_key,x-api-key,private_key,password,secret,token, and prefixed forms such asdb_passwordoraws_secret_access_key) - Credential-bearing query parameters on a server URL (
?api_key=…,?token=…,?code=…) Authorizationheaders in any scheme or quoting, and URL userinfo passwords (https://user:password@host)- The same redactor protects the per-server
Recent output:(stderr) buffer, theLast error:line in/mcpand/mcp <name>, and user-facing start/auth/recovery failure notifications.
- Best-effort. A failed log write never breaks a connection or a tool call.
Attach the file to a bug report. It contains no credentials.
While a Pi UI is active, the extension never writes diagnostics to Pi's stderr (that stream belongs to the TUI): events go to the log file instead, and user-actionable ones are surfaced as Pi notifications. Headless runs without a UI keep the stderr output they have always had — warnings, errors, and child-server output; informational events such as state transitions are recorded only in the debug log.
How It Works
flowchart TD
subgraph Pi["Pi Coding Agent (TUI / RPC, ExtensionAPI)"]
Entry["index.ts (entry & mcp_* agent tools)"]
Cmds["register-commands (/mcp, :start, :stop, :auth)"]
Config["config (Zod + JSONC)"]
Coord["connection-coordinator (in-flight connects, authQueue, teardown)"]
Mgr["server-manager (stopped -> starting -> ready lifecycle)"]
Bridge["tool-bridge (MCP -> Pi tools)"]
Schema["schema-convert (JSON Schema -> TypeBox)"]
Transport["transport-factory & oauth-provider (MCP SDK)"]
Entry --> Cmds
Entry --> Config
Entry --> Coord
Cmds --> Coord
Coord --> Mgr
Mgr --> Bridge
Bridge --> Schema
Mgr --> Transport
end
Sources[("Global & Project mcp.json + Registered Extensions")]
Servers["MCP Servers (stdio / streamable-http / sse)"]
Config --> Sources
Transport <--> Servers
- 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_startviaConnectionCoordinator(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 (preserving nullable union ordering, resolving
$refs cycle-safely, and inferring/flatteningallOfobject schemas) - Pi tools are registered as
<prefix>_<server>_<tool>(sanitized[a-zA-Z0-9_], max 64 chars with an 8-char SHA-256 suffix on truncation or collision) and convert MCPtext,image(ImageContent),structuredContent,resource, andaudioresults - 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/error (
client.onclose/client.onerror), while mid-session HTTP401/403 insufficient_scopeinvalidates stale tokens and surfaces a re-authorization hint list_changednotifications trigger re-discovery, deactivating stale tools automatically- Session shutdown cancels pending auth/connection flows, stops the callback server, deactivates all tools, flushes the debug log, and cleanly stops all servers (with process-tree
SIGTERM→SIGKILLand pipe cleanup 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 (ImageContent), structuredContent, and resource conversion |
✅ |
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 |
Contributing / Architecture
See CONTRIBUTING.md for setup, workflow, and the pre-merge smoke-test checklist, and docs/ARCHITECTURE.md for a behavioral overview of how the runtime fits together (lifecycle, coordination, config trust, and credential storage).
Development
Requires Node.js 22+.
# Install dependencies (clean lockfile install)
npm ci
# Lint (type-aware ESLint + structural/complexity checks)
npm run lint
# Type check (strict mode)
npm run typecheck
# Run all test suites
npm test
# Run all test suites with coverage floor (95% lines / 85% branches / 83% functions)
npm run test:coverage
# Run a single test suite (e.g. server lifecycle & stdio integration)
npm run test:integration
# Verify npm tarball contents
npm pack --dry-run
Testing
The test suite includes:
- Config & Pi 1.x / 1.3.0 compatibility (
tests/config.test.ts,tests/pi-1x-compat.test.ts) — Zod schema, JSONC comments,$PI_CODING_AGENT_DIRresolution, global/project merge, project trust boundary (ctx.isProjectTrusted()), Pi 1.3.0enabled: falseand partial project overrides,stdiocwd,oauth.clientName, Pi 1.x schema aliases (type: "http", URL-only HTTP,oauth,timeout), and dynamicpi.getMcpServers()/mcp_servers_change - Server lifecycle & coordination (
tests/server-manager.test.ts,tests/stdio-process.test.ts,tests/transport-factory.test.ts,tests/connection-coordinator.test.ts,tests/connection-coordination.test.ts) — Connect, retry, unexpected close recovery, shutdown with PIDSIGKILLsafety net, health check, concurrent connect/reset coordination, and cancellation - OAuth 2.1 & auth locks (
tests/oauth-fixes.test.ts,tests/authorize-flow.test.ts,tests/auth-lock.test.ts,tests/auth-commands.test.ts,tests/auth-arguments.test.ts,tests/fake-oauth-server.test.ts) — Challenge discovery, PKCE, Dynamic Client Registration, origin and issuer binding, startup authorization prompts, headless/SSH redirect URL paste-back, URL scheme validation, cross-process lock heartbeat, and stale lock reclamation - Tool bridge, status diagnostics & debug log (
tests/tool-bridge.test.ts,tests/management-tools.test.ts,tests/server-status.test.ts,tests/status-commands.test.ts,tests/completions.test.ts,tests/debug-log.test.ts) — JSON Schema → TypeBox conversion (allOfobject inference,$refcycle protection, unionnullordering), name sanitization, image content passthrough,isErrorfiltering, activate/deactivate,mcp_status/mcp_connect,/mcp <name>config provenance and failure hints, slash command completions, and redactedPI_MCP_DEBUG=1JSONL logging - Property-based fuzzing (
tests/fuzz.test.ts) —fast-checkproperties covering JSONC comment stripping, callback/redirect URL validation,WWW-Authenticatechallenge parsing, JSON Schema → TypeBox conversion, tool naming, terminal/log secret sanitization, and custom header merging - Release flow & repository policy (
tests/release.test.ts,tests/ruleset-audit.test.ts,tests/repo-policy.test.ts) — Release PR orchestration and npm/git self-healing (scripts/release.ts), live GitHub ruleset drift auditing (scripts/ruleset-audit.ts), and CI/ESLint/Qlty policy invariants - End-to-end integration — Real mock MCP and OAuth servers over
stdioand HTTP with tool discovery and execution
Security
Please report vulnerabilities privately, not in public issues. See SECURITY.md for scope, supported versions, and how to report.
License
MIT