Files
dotfiles/pi/.pi/agent/extensions/pi-ask-mcp/README.md
2026-04-24 14:22:59 +02:00

57 lines
1.9 KiB
Markdown

# pi-ask-mcp
A minimal MCP stdio server that gives Claude **one** tool — `ask` — which routes
structured questions back to pi's native ask UI instead of using Claude's
built-in `AskUserQuestion`.
This is **not** a regular pi extension. It is a subprocess of `claude`, which is
itself a subprocess of the `chat-claude` extension. The pi-side counterpart is
[`shared/pi-ask-bridge.ts`](../../shared/pi-ask-bridge.ts), which:
1. Opens a Unix-domain socket per chat session.
2. Generates an `--mcp-config` JSON pointing here, with `PI_ASK_SOCKET=<sock>`.
3. Translates `ask` requests off the socket into
`askSingleQuestionWithInlineNote` / `askQuestionsWithTabs` calls and writes
the result back.
## Architecture
```
pi
└── chat-claude
├── pi-ask-bridge (UDS server, owns ui.custom)
└── claude -p ... --mcp-config <generated.json> --disallowed-tools AskUserQuestion
└── pi-ask-mcp/server.js (this file)
↳ on tools/call ask → connect $PI_ASK_SOCKET → ask → reply
```
## Why a hand-written MCP server
No `@modelcontextprotocol/sdk` dependency, no transpile step, no
`node_modules`. The MCP stdio protocol is small enough (~6 method handlers)
that writing it directly keeps the file self-contained and trivially
portable. Claude CLI spawns it via `node server.js`.
## Wire format
Stdio (with Claude): JSON-RPC 2.0 over newline-delimited JSON.
Socket (with pi-ask-bridge): NDJSON, one request → one response, then close.
```jsonc
// → pi
{ "id": "uuid", "type": "ask",
"questions": [
{ "id": "auth", "question": "Auth method?",
"options": [{"label": "OAuth"}, {"label": "API key"}],
"multi": false, "recommended": 0 }
] }
// ← pi (success)
{ "id": "uuid", "type": "result",
"results": [{ "id": "auth", "selectedOptions": ["OAuth"] }] }
// ← pi (cancel / error)
{ "id": "uuid", "type": "error", "message": "cancelled" }
```