@ian-pascoe/pi-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ian Pascoe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,193 @@
1
+ # @ian-pascoe/pi-mcp
2
+
3
+ Pi MCP makes [Pi](https://github.com/earendil-works/pi) an MCP Host for named local and remote MCP Servers. It supports the core protocol with automatic current/legacy negotiation; it does **not** support MCP Standard Extensions. In particular, Tasks are deferred indefinitely.
4
+
5
+ ## Install
6
+
7
+ Pi loads the extension directly from TypeScript. From this repository, install the collection and select its source entrypoint:
8
+
9
+ ```bash
10
+ pi install git:github.com/ian-pascoe/pi-extensions
11
+ ```
12
+
13
+ For a filtered Git installation, include this path in the package's `extensions` list:
14
+
15
+ ```json
16
+ "packages/pi-mcp/src/index.ts"
17
+ ```
18
+
19
+ The `pi-mcp` executable is compiled into the npm tarball, not into a Git checkout. After `@ian-pascoe/pi-mcp` is published, install the package with Pi and use its supplied binary:
20
+
21
+ ```bash
22
+ pi install npm:@ian-pascoe/pi-mcp
23
+ pi-mcp --help
24
+ ```
25
+
26
+ Until publication, do not add `npm:@ian-pascoe/pi-mcp` to `.pi/settings.json`. Workspace and Git installs use the source extension; build the local CLI explicitly when needed:
27
+
28
+ ```bash
29
+ pnpm --filter @ian-pascoe/pi-mcp build:cli
30
+ node packages/pi-mcp/dist/pi-mcp-cli.js --help
31
+ ```
32
+
33
+ Requires Node.js 22.19 or newer and a compatible Pi installation.
34
+
35
+ ## Configure servers
36
+
37
+ Put only an `mcp` object in Pi's normal global or trusted project `settings.json`. There is no separate MCP configuration file.
38
+
39
+ ```json
40
+ {
41
+ "mcp": {
42
+ "connectTimeoutMs": 10000,
43
+ "requestTimeoutMs": 60000,
44
+ "retry": {
45
+ "maxRetries": 2,
46
+ "initialDelayMs": 1000,
47
+ "maxDelayMs": 30000,
48
+ "backoffFactor": 1.5
49
+ },
50
+ "servers": {
51
+ "local-docs": {
52
+ "command": "node",
53
+ "args": ["./tools/docs-mcp.mjs"],
54
+ "cwd": ".",
55
+ "environment": { "DOCS_TOKEN": "${DOCS_TOKEN}" }
56
+ },
57
+ "remote-docs": {
58
+ "url": "https://mcp.example.com/mcp",
59
+ "headers": { "X-Workspace": "${WORKSPACE_ID}" },
60
+ "auth": { "type": "oauth", "scopes": ["tools.read"] }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ A **Server Definition** is named by its `servers` key. It is either:
68
+
69
+ - a local `stdio` definition: `command`, optional `args`, `cwd`, and `environment`; or
70
+ - a remote `http` or explicit legacy `sse` definition: `url`, optional `headers`, and optional `auth`.
71
+
72
+ Exactly one of `command` and `url` is required. `stdio` is the default for `command`; Streamable HTTP is the default for `url`. Remote URLs must be absolute HTTP(S) URLs. Remote definitions cannot contain process fields, and local definitions cannot contain headers or authentication fields. `enabled` defaults to `true`.
73
+
74
+ Authentication is omitted for anonymous access, or is one of:
75
+
76
+ ```json
77
+ { "type": "none" }
78
+ { "type": "bearer", "token": "${MCP_TOKEN}" }
79
+ {
80
+ "type": "oauth",
81
+ "clientId": "optional-client-id",
82
+ "clientSecret": "${MCP_CLIENT_SECRET}",
83
+ "redirectUri": "http://127.0.0.1:19876/mcp/oauth/callback",
84
+ "scopes": ["tools.read", "resources.read"]
85
+ }
86
+ ```
87
+
88
+ `none` disables OAuth discovery. Bearer authentication supplies the Authorization header and cannot be combined with a configured `Authorization` header. OAuth supports discovery, Client ID Metadata Documents when configured, Dynamic Client Registration where necessary, refresh tokens, and persisted PKCE/state data. The default callback is the loopback URL above; custom redirects must also be HTTP loopback URLs.
89
+
90
+ ### Defaults, merge, masks, and environment values
91
+
92
+ The host-wide defaults are:
93
+
94
+ | Setting | Default |
95
+ | ---------------------- | --------: |
96
+ | `connectTimeoutMs` | 10,000 ms |
97
+ | `requestTimeoutMs` | 60,000 ms |
98
+ | `retry.maxRetries` | 2 |
99
+ | `retry.initialDelayMs` | 1,000 ms |
100
+ | `retry.maxDelayMs` | 30,000 ms |
101
+ | `retry.backoffFactor` | 1.5 |
102
+ | shutdown budget | 5,000 ms |
103
+
104
+ Global and project `mcp` objects merge top-level timeout and retry fields. A project Server Definition replaces the whole global definition with the same name; it does not field-merge it. A project definition of `null`, or an inherited definition written as `{ "enabled": false }`, masks the global definition. Removing or enabling the project entry reveals the global definition again.
105
+
106
+ `${NAME}` expands in every string **value** in a Server Definition, using Pi's process environment. Keys are never expanded, values are expanded once only, and a missing variable makes the merged MCP configuration invalid without exposing the secret value. Unknown fields and invalid settings are rejected with path-qualified errors; Pi continues to start with MCP disabled.
107
+
108
+ ## Commands
109
+
110
+ `/mcp` accepts every command below. The standalone `pi-mcp` binary accepts the first eight only.
111
+
112
+ | Command | Surface | Purpose |
113
+ | ------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
114
+ | `list [--json]` | both | List effective definitions, provenance, enabled/masked state, auth type, and stored-auth presence; `--json` is standalone-only and list never connects. |
115
+ | `add [-l] <name> <url> …` | both | Add or replace a remote definition. |
116
+ | `add [-l] <name> … -- <command> [args…]` | both | Add or replace a local stdio definition. |
117
+ | `remove [-l] [--logout] <server>` | both | Remove a definition, optionally removing its stored credentials. |
118
+ | `enable [-l] <server>` / `disable [-l] <server>` | both | Change enabled state. |
119
+ | `auth <server> [--no-open] [--callback URL \| --code CODE --state STATE]` | both | Run an explicit OAuth authorization flow. |
120
+ | `logout <server>` / `logout --all --force` | both | Remove one server's credentials, or explicitly reset corrupt auth storage. |
121
+ | `test <server> \| --all [--json]` | both | Connect temporary clients and close them without disturbing live connections; `--json` is standalone-only. |
122
+ | `status` | `/mcp` | Show live connection state. |
123
+ | `reconnect <server>` | `/mcp` | Reconnect one live server. |
124
+ | `prompt <server> <prompt> [--arg NAME=VALUE]…` | `/mcp` | Run an MCP Prompt. |
125
+ | `subscribe <server> <uri>` / `unsubscribe <server> <uri>` | `/mcp` | Manage Resource subscriptions. |
126
+ | `logs [server] [--level LEVEL]` | `/mcp` | Read retained server logs. |
127
+
128
+ Mutations default to global scope. `-l` or `--local` selects project scope and is allowed only when Pi has saved trust for that project. The standalone CLI also accepts `--approve`/`-a` or `--no-approve`/`-na` to override project trust for one invocation. In a running Pi session, add/enable persists first and then connects in the background; disable/remove persists first and then disconnects. A failed connection never rolls back the setting.
129
+
130
+ For a remote server, `add` accepts repeated `--header NAME=VALUE`, `--transport http|sse`, and the OAuth/bearer flags `--auth`, `--token`, `--client-id`, `--client-secret`, `--redirect-uri`, and repeated `--scope`. For a local server, use repeated `--environment NAME=VALUE` (or `--env`) and optional `--cwd` before `--`.
131
+
132
+ OAuth is always explicit: the authorization URL is printed before a best-effort browser launch. Use `--no-open` for remote/headless use, then provide a full callback URL with `--callback`, or a verified `--code` and `--state` pair. The host permits one active authorization flow per process and validates callback state, issuer, resource, and loopback redirect values.
133
+
134
+ ## What the model can use
135
+
136
+ Every advertised MCP **Server Tool** becomes an individual Pi tool named:
137
+
138
+ ```text
139
+ mcp__<server>__<tool>
140
+ ```
141
+
142
+ Names are sanitized for Pi and get a deterministic hash suffix only when they collide. Annotations are untrusted metadata; they do not grant permission or alter execution.
143
+
144
+ Pi also registers these fixed tools, only when a connected server supports Resources:
145
+
146
+ ```text
147
+ list_mcp_resources
148
+ list_mcp_resource_templates
149
+ read_mcp_resource
150
+ ```
151
+
152
+ There is no generic raw-MCP request tool or protocol gateway. Prompts, authentication, subscriptions, status, reconnect, and logs remain `/mcp` operations rather than model tools.
153
+
154
+ Input and output schemas are retained as the Server advertised them. Pi structurally validates Server Tool schemas and validates mutated tool input before calling the server. On Pi 0.84.2 there is no public provider-schema compatibility preflight: a model provider can reject an otherwise valid exact MCP JSON Schema, which can fail that model turn. Pi MCP deliberately does not maintain a provider matrix, rewrite schemas, inspect Pi internals, or rewrite provider payloads to hide that limitation.
155
+
156
+ ## Host behavior
157
+
158
+ Enabled servers connect in the background at session start, so Pi startup and TUI rendering do not wait for a process or network connection. Each session owns its clients, transports, children, listeners, timers, retries, subscriptions, logs, and private files. Current MCP peers are negotiated automatically and legacy 2025-era peers remain supported. Streamable HTTP never silently falls back to SSE.
159
+
160
+ The host maps the core protocol surface:
161
+
162
+ - tools, Resources, Resource Templates, Prompts, completion, and Server Instructions;
163
+ - sampling with Pi's active model and credentials, returning server-executable tool-use blocks;
164
+ - roots (the current Pi working directory), elicitation, logging, progress, and cancellation; and
165
+ - current multi-round input requests (bounded to ten rounds) plus legacy server-initiated callbacks.
166
+
167
+ Interactive callbacks are request-scoped. Headless environments decline interaction rather than hanging; background work never opens a dialog or browser and never starts a model turn. Resource changes queue a provenance-labelled notice for the next turn; the host does not fetch or inject resource content automatically.
168
+
169
+ Before the first model request, Pi waits only for the bounded initial connection deadline, then freezes one deterministic **Instruction Snapshot**. Its Server Instructions bytes stay unchanged for the session; instructions from later connections appear only after reload or a new session.
170
+
171
+ Failures stay isolated to the affected Server Definition. Status is one of `disabled`, `connecting`, `connected`, `needs_auth`, `needs_client_registration`, `retrying`, or `failed`. Retryable startup failures and unexpected closes use the shared capped exponential policy. Authentication, invalid configuration, unsupported protocol, disable, and shutdown do not retry. After retries are exhausted, use reconnect, reload, or a new session.
172
+
173
+ Catalog lists are cached for the session, aggregate at most 1,000 pages, reject repeated cursors, and invalidate on their matching MCP notifications. Server Tool additions/replacements take effect immediately. Pi has no public deregistration API, so removed tools are deactivated until reload.
174
+
175
+ ## Output, persistence, and reload
176
+
177
+ Text and images map to Pi-native content. Embedded text Resources and Resource Links become provenance-labelled text. Structured content is visible as labelled JSON and retained in tool details. Unsupported audio and binary Resources are saved as private, mode-safe session files rather than discarded.
178
+
179
+ All model-facing text uses Pi's 2,000-line / 50-KB limit. Oversized complete output is retained in a private Result Spill and the returned content includes its path. Per-server stderr and MCP logging retain only the newest 256 KB. Stdio stdout is protocol framing only; stderr and MCP logs do not write directly to TUI, JSON, or RPC output.
180
+
181
+ Desired Resource subscriptions and expanded Prompt messages are persisted as versioned Pi custom entries and replay only on the active session branch. Connections and logs are ephemeral. Reload closes the old session generation and its dormant tool definitions, then creates a clean generation; shutdown awaits owned cleanup.
182
+
183
+ OAuth data lives in a strict, URL-and-client-identity-bound `mcp-auth.json` under Pi's agent directory, protected with mode `0600`. Settings and auth writes use a bounded lock plus atomic replacement. Malformed auth storage is not overwritten by ordinary operations; use the explicit reset command.
184
+
185
+ ## Security boundaries
186
+
187
+ A project Server Definition can launch an arbitrary local executable with Pi's permissions. Treat project MCP settings as executable configuration: review the command, arguments, working directory, and environment before trusting a project or approving a project-local mutation.
188
+
189
+ Pi MCP owns no permission or approval policy for server tools, Resources, Prompts, or sampling. Another Pi extension may govern the surrounding tool call. Elicitation and OAuth are protocol interactions, not permission grants. Resolved environment values, bearer tokens, OAuth credentials, callback values, and persisted auth bytes are redacted from settings/auth errors and are not intentionally emitted to logs or results.
190
+
191
+ ## License
192
+
193
+ MIT