Files
mcpctl/docs/pi-extension.md
Michal 28f1a411fd feat(pi): add native pi integration — extension, config pi, skills --agent pi
- src/pi-ext/: self-contained pi extension (mcpctl-pi.ts + vendored
  mcp-http client) that talks JSON-RPC directly to mcplocal and registers
  project MCP tools as native pi tools. No MCP client, no ~/.claude.
- Persistent per-project session so gated projects ungate on begin_session.
- /mcpctl command: status, switch project (GUI), refresh tools, sync skills.
- mcpctl config pi: installs extension, wires pi settings, persists active
  project, syncs skills into ~/.pi/agent/skills.
- skills sync: add --agent pi (target install root).
- docs + tests.
2026-08-08 16:23:02 +01:00

3.9 KiB
Raw Blame History

mcpctl × pi — native integration (no MCP, no Claude)

Motivation

With Claude Code, mcpctl config claude --project X wires a project's MCP servers into the agent two ways:

  1. a .mcp.json entry running mcpctl mcp -p X (an MCP stdio bridge), and
  2. a SessionStart hook + mcpctl skills sync that materialises server-side skills under ~/.claude/skills/.

Claude Code supports MCP natively, so tools travel over MCP. pi does not support MCP. But pi has two native mechanisms that cover the same ground:

  • Extensions — TypeScript modules that can register tools, commands, and custom UI, and can talk HTTP directly.
  • Skills — pi implements the Agent Skills standard and loads SKILL.md trees (the exact format mcpctl already syncs).

This addon exploits both so mcpctl works with pi only, with no MCP and no dependency on ~/.claude/ (so it keeps working if you drop Claude).

How it works

The MCP layer is not magic. mcpctl mcp is just a JSON-RPC ↔ stdio bridge that forwards to mcplocal's Streamable-HTTP endpoint:

POST {mcplocalUrl}/projects/<project>/mcp
Accepts: application/json, text/event-stream
Authorization: Bearer <token>

The protocol is JSON-RPC over HTTP:

  • initialize → returns an mcp-session-id header
  • notifications/initialized
  • tools/list{ tools: [{ name, description, inputSchema }] }
  • tools/call{ name, arguments }{ content, isError }

The pi extension is a direct JSON-RPC client to that same endpoint. It replaces the MCP transport with a plain function call, so pi never needs an MCP client library.

Gating

Some projects are gated: until begin_session is called, tools/list returns only begin_session. After it is called the full tool set appears.

Critical subtlety: mcplocal's gate state is bound to a mcp-session-id. A fresh session is gated again even after another session was ungated — so the extension keeps one persistent McpHttpSession per active project and routes every tools/call through it. After each call it re-runs tools/list and reconciles the active pi tool set, so calling begin_session automatically opens the gate and reveals the rest — no push listener required (mcplocal's HTTP responses are request/response, not a persistent push stream).

Deliverables

Artifact Purpose
src/pi-ext/mcpctl-pi.ts Self-contained pi extension (load via pi -e or settings extensions)
mcpctl config pi CLI wiring: registers the extension with pi + initial skill sync
docs/pi-extension.md This document

Project switching

The active project is persisted in ~/.mcpctl/pi-state.json (settable, immune to your shell's cwd) and is also inferred from a .mcpctl-project marker walk-up. /mcpctl opens an interactive menu; switch project lists projects via ctx.ui.select() and reloads the tool set for the new project, namespaced so projects never collide.

Tool naming

To allow multiple projects and avoid schema collisions, registered pi tools are namespaced: mc_<project-slug>_<mcp-tool-name>. Only the current project's tools are in the active set at any time.

Skills

Skills already sync in the Agent Skills SKILL.md format. mcpctl config pi (when given a project) writes them under pi's own directory ~/.pi/agent/skills/ and registers that directory in ~/.pi/agent/settings.json — no Claude involvement. mcpctl pi sync-skills (a thin wrapper) re-runs the existing skills code path against the pi target directory.

Layout

src/pi-ext/
  mcpctl-pi.ts          # the extension (self-contained)
  mcp-http.ts           # vendored Streamable-HTTP JSON-RPC client (no deps)

The extension imports only from pi-bundled packages (@earendil-works/pi-coding-agent, @earendil-works/pi-ai, typebox), so it loads standalone.