@bytetrue/pi-mcp

extensionmaintained

Pi extension: minimal MCP adapter — one proxy tool, disk-cached tool metadata, lazy stdio/HTTP connections, output guard. Context management over feature breadth.

by — · v0.5.1 · published 1w ago

$ pi install npm:@bytetrue/pi-mcp
downloads/mo
0
stars
7
last push
3d ago
open issues
1

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

299 downloads · last 12 weeks (weekly)

README

@bytetrue/pi-mcp

Minimal MCP (Model Context Protocol) adapter for the pi coding agent. One proxy tool instead of hundreds of tool definitions; tool metadata is cached to disk so discovery works without live server connections; servers connect lazily and disconnect when idle.

Context management over feature breadth: the core design (proxy tool, metadata cache, lazy lifecycle, output guard) is ported from pi-mcp-adapter (MIT — see NOTICE), which itself depends only on registry-published @modelcontextprotocol/client/core 2.0.0. What's deliberately absent: OAuth flows, MCP UI, elicitation/sampling, scripting tools, plugin loaders, setup panels.

Install

pi install npm:@bytetrue/pi-mcp

Configuration

Same layered layout as pi-mcp-adapter / Claude Code / Cursor — an existing setup migrates by doing nothing:

FilePurpose
~/.config/mcp/mcp.jsonUser-global shared MCP config
~/.agents/mcp.jsonUser-global tool-agnostic MCP config
~/.agents/mcp/mcp.jsonUser-global tool-agnostic MCP config (nested)
~/.pi/agent/mcp.jsonPi global override ($PI_CODING_AGENT_DIR/mcp.json)
.mcp.jsonProject-local shared MCP config
.pi/mcp.jsonPi project override

Same-name servers in higher layers override lower ones; disabled: true in a higher layer disables an inherited server.

{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "directTools": ["search_repositories", "get_file_contents"],
      "searchKeywords": { "*": ["gh"] }
    },
    "docs": {
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  },
  "settings": {
    "idleTimeout": 10,
    "outputGuard": { "maxBytes": 51200, "maxLines": 2000, "detailsMaxBytes": 16384 }
  }
}

Server fields: command/args/env/cwd (stdio, npx commands are resolved to direct binaries — cached, with fallback to plain npx), url + headers (HTTP; Streamable HTTP with automatic SSE fallback on 404/405/406/415), type, disabled, lifecycle (lazy default | eager), idleTimeout (minutes; default 10, 0 disables — eager servers never idle-disconnect), directTools (true | ["tool_a"]), includeTools/excludeTools (glob-capable), searchKeywords, toolPrefix.

Usage

The extension registers one mcp tool (~200 tokens of schema):

OperationCall
Statusmcp({})
List server toolsmcp({ server: "name" })
Search toolsmcp({ search: "words", limit?: 12, offset?: 0 })
Regex searchmcp({ search: "pattern", regex: true })
Describe a toolmcp({ describe: "server_tool" })
Call a toolmcp({ tool: "server_tool", args: { ... } })
Server instructionsmcp({ instructions: "name" })
Connect/reconnectmcp({ connect: "name" })

mcp({}) reports five honest states per server — ✓ connected / ✗ failed (Ns ago, reason) / ○ cached; not connected / ○ not connected / ⊘ disabled — instead of one blurred "offline" line.

Managing servers

/mcp command (Tab-completion for subcommands and server names):

CommandEffect
/mcp (or /mcp status)Five-state server listing
/mcp reconnect [server]Reconnect one server or all enabled servers
/mcp toolsAll tools across enabled servers (cache-backed)
/mcp promptsAll discovered prompts, grouped by server
/mcp disable <server>Persist a disabled: true project override (.pi/mcp.json), then /reload
/mcp enable <server>Remove the marker (or write disabled: false when a lower layer disables), then /reload

Interactive menu: /mcp with no arguments in the TUI opens a native Pi dialog menu (select, editor, input, notify) — browse servers with live status and direct tool counts, toggle direct tools per server with [●]/[○], enable/disable servers, reconnect individual or all servers, search tools, and browse prompts. Zero fragile custom terminal overlay diffing. All changes persist to .pi/mcp.json overrides.

Footer status bar: the TUI footer shows a live mcp slot, e.g. 2 servers enabled (1 connected) (1 disabled); "compact" renders mcp:1/2. Configure with settings.mcpFooterStatus: "full" (default) | "compact" | "off". Disabled servers stay visible in status instead of vanishing from config, and are skipped by connect/search/direct-tools.

Protocol tracing (opt-in): set settings.trace: { enabled, file?, maxBytes?, maxEvents? } (or trace: true per server) to record metadata-only JSONL protocol traces — direction, method, bytes, duration, secrets redacted — under .pi/mcp-traces/ by default. Writers self-disable on any failure so tracing never changes MCP behavior.

Tool names match fuzzily across -/_; a miss returns ranked suggestions. Search ranks across name, server, description, and configured keywords, paginated via offset/nextOffset.

Direct tools: configure directTools per server (or "directTools": true in settings) to register chosen tools as real Pi tools with real schemas, skipping the search→describe hop. Each costs ~150–300 tokens in the system prompt, so it fits targeted sets of 5–20 tools; use include/exclude filters to trim noisy servers. Direct tools register statically from the disk metadata cache at load; on the first session after adding them, connect the server once (mcp({ connect: "name" })) to populate the cache, then /reload.

Prompts: servers advertising prompt templates become slash commands — /mcp__<server>__<prompt> — with positional and key=value arguments (bash-style quoting supported). Command definitions come from the metadata cache and refresh on reconnect.

How it works

  • One proxy tool in context instead of hundreds of definitions — the agent discovers tools on demand.
  • Disk metadata cache (~/.pi/agent/mcp-cache.json, keyed by a config hash): search/list/describe work offline; a changed server config invalidates only that server's entry.
  • Lazy connections: servers spawn on first tool call, disconnect after the idle timeout, and reconnect automatically on next use.
  • Output guard (port of upstream's): text results capped at 50 KiB / 2,000 lines; oversize output spills to a private 0600 temp file whose path is returned for read/grep. Image blocks pass through untouched. details.mcpResult keeps raw JSON up to 16 KiB, then summarizes and spills. Disable with "outputGuard": false or MCP_OUTPUT_GUARD=0.
  • Failure backoff: a server that just failed a connection is excluded from search for 60s instead of polluting results.

Limitations (by design)

  • No OAuth: HTTP servers authenticate with static headers only.
  • No elicitation, sampling, MCP UI, or resource subscriptions — servers requiring them degrade gracefully.
  • Direct tools don't hot-load; a /reload applies config or catalog changes.
  • No config-writing UI: manage mcp.json files directly (or via your dotfiles).

Development

npm --workspace @bytetrue/pi-mcp test       # vitest (includes real-stdio regression via npx)
npm --workspace @bytetrue/pi-mcp run typecheck

MIT. See NOTICE for upstream attribution.