pi-session-mcp 0.6.3 → 0.7.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/.pi-session-mcp-cli/config.js +11 -1
- package/.pi-session-mcp-cli/doctor-cli.js +5 -1
- package/.pi-session-mcp-cli/doctor.js +22 -1
- package/.pi-session-mcp-cli/execution-profile.js +1 -0
- package/.pi-session-mcp-cli/extension-spec.js +21 -0
- package/.pi-session-mcp-cli/pi-extensions.js +67 -0
- package/CHANGELOG.md +54 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +2 -2
- package/SECURITY.md +1 -1
- package/dist/capabilities.d.ts +1 -0
- package/dist/capabilities.js +2 -1
- package/dist/client-adapters/mcp-verifier.js +5 -2
- package/dist/config.d.ts +1 -0
- package/dist/config.js +11 -1
- package/dist/doctor-cli.js +5 -1
- package/dist/doctor.d.ts +4 -1
- package/dist/doctor.js +22 -1
- package/dist/execution-profile.d.ts +3 -0
- package/dist/execution-profile.js +1 -0
- package/dist/extension-spec.d.ts +15 -0
- package/dist/extension-spec.js +21 -0
- package/dist/main.js +6 -2
- package/dist/pi-adapter.d.ts +1 -1
- package/dist/pi-adapter.js +4 -0
- package/dist/pi-extensions.d.ts +17 -0
- package/dist/pi-extensions.js +67 -0
- package/dist/sdk-pi-adapter.d.ts +24 -6
- package/dist/sdk-pi-adapter.js +253 -86
- package/dist/server.js +1 -0
- package/dist/session-tools.d.ts +9 -0
- package/dist/session-tools.js +25 -0
- package/dist/setup-command-guardian.js +29 -73
- package/dist/setup-main.js +2 -1
- package/dist/setup-ownership.js +18 -28
- package/dist/setup-platform.d.ts +57 -0
- package/dist/setup-platform.js +195 -0
- package/dist/setup-process.d.ts +6 -3
- package/dist/setup-process.js +85 -93
- package/dist/setup.js +2 -1
- package/dist/stdout-guard-install.d.ts +5 -0
- package/dist/stdout-guard-install.js +6 -0
- package/dist/stdout-guard.d.ts +20 -0
- package/dist/stdout-guard.js +72 -0
- package/docs/acceptance.md +245 -0
- package/docs/adr/0005-macos-setup-platform.md +84 -0
- package/docs/adr/0006-pi-extension-allowlist.md +58 -0
- package/docs/architecture.md +1 -1
- package/docs/client-setup-design.md +21 -9
- package/docs/client-setup.md +56 -10
- package/docs/doctor-design.md +9 -2
- package/docs/installation.md +129 -4
- package/docs/macos-setup-design.md +386 -0
- package/docs/pi-extensions-design.md +421 -0
- package/docs/releasing.md +1 -1
- package/docs/superpowers/plans/2026-10-10-macos-setup.md +1725 -0
- package/docs/superpowers/plans/2026-10-11-pi-extensions.md +2450 -0
- package/docs/threat-model.md +54 -18
- package/docs/tool-contracts.md +8 -3
- package/package.json +1 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# ADR 0005: setup supports macOS with the same fence as on Linux
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted (2026-10-10). macOS counts as accepted only after
|
|
4
|
+
Level-2 setup acceptance on real hardware; see [Acceptance](../acceptance.md).
|
|
5
|
+
That acceptance is recorded for Claude Code (2026-10-11); Codex is still pending.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
`setup` refused every operation on macOS with `platform_unsupported`. #28
|
|
10
|
+
reported this from a Mac: Doctor and a manual registration worked, but
|
|
11
|
+
`--verify` and the ownership lifecycle were unavailable. ADR 0003 names
|
|
12
|
+
repeated setup friction beyond version drift as a trigger to revisit this
|
|
13
|
+
area.
|
|
14
|
+
|
|
15
|
+
The setup fence relied on three Linux facts:
|
|
16
|
+
|
|
17
|
+
- util-linux `flock` locks the open file description of the ownership lock
|
|
18
|
+
file, and the transaction guardian inherits it;
|
|
19
|
+
- a `/proc` scan proves that no non-zombie member of a client command's
|
|
20
|
+
process group remains before the fence is released;
|
|
21
|
+
- `statfs` magic numbers identify the local filesystems on which lock and
|
|
22
|
+
fsync semantics are accepted.
|
|
23
|
+
|
|
24
|
+
None of them carries over: macOS ships no `flock(1)` and no `/proc`, and its
|
|
25
|
+
`statfs` `f_type` is a dynamically assigned VFS index rather than a stable
|
|
26
|
+
magic number.
|
|
27
|
+
|
|
28
|
+
## Decision
|
|
29
|
+
|
|
30
|
+
`setup` supports Linux and macOS. Every other platform still fails closed with
|
|
31
|
+
`platform_unsupported` before any client command is spawned.
|
|
32
|
+
|
|
33
|
+
`src/setup-platform.ts` owns the operating-system facts behind the fence. Its
|
|
34
|
+
macOS primitives are:
|
|
35
|
+
|
|
36
|
+
- **Lock.** The lock file is opened with `O_EXLOCK | O_NONBLOCK`. XNU takes a
|
|
37
|
+
flock(2)-semantics lock on the open file description at open time. A busy
|
|
38
|
+
lock is retried every 50 ms within the existing acquisition bound, and the
|
|
39
|
+
guardian inherits the locked description as on Linux.
|
|
40
|
+
- **Process-group proof.** The trusted system `/bin/ps -A -o pid=,pgid=,stat=`
|
|
41
|
+
is parsed strictly. A scan that fails, times out, has an unexpected line or
|
|
42
|
+
does not list the caller is `unknown`, and `unknown` retains the fence.
|
|
43
|
+
- **Filesystem.** The ownership directory must have the same `statfs` type
|
|
44
|
+
as `/`, which is the APFS system volume on every macOS that Node.js 22
|
|
45
|
+
supports. Any other type fails closed.
|
|
46
|
+
|
|
47
|
+
Public result codes, statuses, exit codes and the result schema do not
|
|
48
|
+
change. CI runs on Linux, macOS 15 and macOS 26. A `verify` job aggregates
|
|
49
|
+
the matrix, so the required status check keeps its name.
|
|
50
|
+
|
|
51
|
+
## Consequences
|
|
52
|
+
|
|
53
|
+
- Operators on macOS get the same lifecycle, ownership records and
|
|
54
|
+
verification as on Linux, including `--verify` for a registration that
|
|
55
|
+
`setup` made.
|
|
56
|
+
- On macOS, the proof that no live group member remains rests on `/bin/ps`,
|
|
57
|
+
which sits on the sealed system volume, plus the uid and mode check that
|
|
58
|
+
already guarded `flock`. As on Linux, it does not cover descendants that
|
|
59
|
+
escape the process group, and it does not promise PID reaping.
|
|
60
|
+
- An ownership directory on HFS+, exFAT, a network volume or any other
|
|
61
|
+
non-APFS filesystem fails closed on macOS.
|
|
62
|
+
- Intel Macs run the same code path but are not in the CI matrix.
|
|
63
|
+
- The guardian and the process runner load the platform module with a
|
|
64
|
+
dynamic import, because source-mode tests run them through Node type
|
|
65
|
+
stripping. `setup-platform.js` is therefore a mandatory release artifact
|
|
66
|
+
next to `main.js` and `setup-command-guardian.js`.
|
|
67
|
+
|
|
68
|
+
## Alternatives considered
|
|
69
|
+
|
|
70
|
+
- **A `lockf(1)` helper and `/sbin/mount` parsing.** This would have mirrored
|
|
71
|
+
the Linux shape. Rejected: the descriptor mode of `lockf` on macOS 15 and
|
|
72
|
+
the `mount` output format are unverified.
|
|
73
|
+
- **A weaker macOS profile without the kernel fence or the process-group
|
|
74
|
+
proof.** Rejected: it would break the guarantee that a later setup
|
|
75
|
+
transaction cannot race an orphaned client command.
|
|
76
|
+
- **`kill(-pgid, 0)` as the process-group proof.** Rejected: the XNU source
|
|
77
|
+
handles groups that contain only zombies inconsistently.
|
|
78
|
+
|
|
79
|
+
## Revisit triggers
|
|
80
|
+
|
|
81
|
+
- The `ps` output format or its `stat` letters change.
|
|
82
|
+
- The `O_EXLOCK` semantics of open(2) change.
|
|
83
|
+
- The minimum macOS version that Node.js supports changes.
|
|
84
|
+
- A request to add Intel Macs to CI, or to support another platform.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# ADR 0006: Execution profiles may load pinned Pi extensions
|
|
2
|
+
|
|
3
|
+
**Status:** Accepted (2026-10-11). Issue #34. Design:
|
|
4
|
+
[Pi extensions design](../pi-extensions-design.md).
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Embedded sessions loaded no Pi extensions. Providers that exist only as Pi
|
|
9
|
+
extensions, such as `cursor` from `pi-cursor-sdk`, were unusable through Pi
|
|
10
|
+
Session MCP although Pi had them installed and authenticated.
|
|
11
|
+
|
|
12
|
+
ADR 0003 chose maintenance without a new feature phase. Unlike ADR 0005,
|
|
13
|
+
which followed a fired ADR 0003 revisit trigger, this is a scope expansion
|
|
14
|
+
the owner explicitly approved (2026-10-11, issue #34).
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
An execution profile may name Pi extensions as
|
|
19
|
+
`npm:<name>@<exact version>`.
|
|
20
|
+
- The server resolves each one from Pi's own user install, normally
|
|
21
|
+
`~/.pi/agent/npm/node_modules/<name>` from `pi install npm:<name>`, and never
|
|
22
|
+
installs anything. A package installed anywhere else, for example with
|
|
23
|
+
`npm i -g`, counts as not installed.
|
|
24
|
+
- A missing package or another installed version fails closed before a session
|
|
25
|
+
exists.
|
|
26
|
+
- An extension loads with its full capabilities: providers, tools, hooks and
|
|
27
|
+
system-prompt changes.
|
|
28
|
+
- In `read-only` profiles the model's tools stay `read`, `grep`, `find` and
|
|
29
|
+
`ls`.
|
|
30
|
+
- In `coding` profiles every extension tool may be active.
|
|
31
|
+
- An extension tool name that collides with another tool fails closed in both
|
|
32
|
+
profiles.
|
|
33
|
+
|
|
34
|
+
A profile with extensions uses the Pi SDK session-lifecycle API with its own
|
|
35
|
+
model runtime per session:
|
|
36
|
+
- `createAgentSessionServices`, then the existing verification, then
|
|
37
|
+
`createAgentSessionFromServices` and `bindExtensions`.
|
|
38
|
+
- Close goes through `AgentSessionRuntime.dispose()`, which emits
|
|
39
|
+
`session_shutdown`.
|
|
40
|
+
|
|
41
|
+
Profiles without extensions keep the previous path. Client prompts are plain
|
|
42
|
+
text for every profile: they never dispatch extension commands, skills or
|
|
43
|
+
templates. An always-on stdout guard gives the MCP transport the only real
|
|
44
|
+
stdout writer.
|
|
45
|
+
|
|
46
|
+
## Consequences
|
|
47
|
+
|
|
48
|
+
- An allowlisted extension is trusted operator code with the user's full
|
|
49
|
+
rights inside the server process. Version pinning prevents silent updates,
|
|
50
|
+
not tampering with the installed package.
|
|
51
|
+
- Extensions may persist their own state; Pi Session MCP itself stays
|
|
52
|
+
in-memory.
|
|
53
|
+
- A child process that an extension starts with inherited stdio can still
|
|
54
|
+
write to the MCP stream directly. This is a documented residual risk.
|
|
55
|
+
- Extension tool names are checked for conflicts after loading and again after
|
|
56
|
+
binding. A tool that an extension registers later, during a turn, is not
|
|
57
|
+
re-checked, because allowlisted extensions are trusted code.
|
|
58
|
+
- Doctor checks configured extensions offline by reading their manifests.
|
package/docs/architecture.md
CHANGED
|
@@ -79,4 +79,4 @@ Public errors are stable and sanitized. They omit caller values, complete prompt
|
|
|
79
79
|
|
|
80
80
|
## Scope
|
|
81
81
|
|
|
82
|
-
The product boundary is local stdio and in-memory-only operation for one trusted local MCP peer. A controller keeps one immutable configuration/profile snapshot for its lifetime; file edits require a genuinely new controller process and do not hot-reload, mark stale, or mutate running sessions. Each new session performs external catalog discovery within the loaded grants, so catalog changes do not require a controller restart, but a running session's active tools remain fixed. Restarting the controller loses all sessions; reconnecting a client reloads configuration only when it actually starts a new controller process. Capability discovery reports captured metadata only and does not prove current-file freshness, server reachability, provider availability, or catalog state. It excludes HTTP, a web UI, persistence/resume, remote access, multi-user behavior, transcript export, streaming, live provider/model discovery, availability probes during capability discovery, and provider credential management. A coding execution profile is an application-level tool selection, not an OS sandbox.
|
|
82
|
+
The product boundary is local stdio and in-memory-only operation for one trusted local MCP peer. Allowlisted Pi extensions may persist state of their own outside this boundary; Pi Session MCP itself keeps no state beyond process memory. A controller keeps one immutable configuration/profile snapshot for its lifetime; file edits require a genuinely new controller process and do not hot-reload, mark stale, or mutate running sessions. Each new session performs external catalog discovery within the loaded grants, so catalog changes do not require a controller restart, but a running session's active tools remain fixed. Restarting the controller loses all sessions; reconnecting a client reloads configuration only when it actually starts a new controller process. Capability discovery reports captured metadata only and does not prove current-file freshness, server reachability, provider availability, or catalog state. It excludes HTTP, a web UI, persistence/resume, remote access, multi-user behavior, transcript export, streaming, live provider/model discovery, availability probes during capability discovery, and provider credential management. A coding execution profile is an application-level tool selection, not an OS sandbox.
|
|
@@ -22,22 +22,34 @@ Codex supports only `codex:user:pi-session-mcp`. Claude Code supports `user`,
|
|
|
22
22
|
registration output, or scope outside the recorded contracts is `unsupported`,
|
|
23
23
|
and the client version is reported for information only. The adapters do not
|
|
24
24
|
parse undocumented client config files.
|
|
25
|
-
Setup is
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
Setup is supported on Linux and macOS
|
|
26
|
+
([ADR 0005](adr/0005-macos-setup-platform.md)); `src/setup-platform.ts` owns
|
|
27
|
+
the operating-system facts behind the fence.
|
|
28
|
+
|
|
29
|
+
- **Linux:**
|
|
30
|
+
- a root-owned, non-writable util-linux `flock` at `/usr/bin/flock` or
|
|
31
|
+
`/bin/flock`;
|
|
32
|
+
- readable `/proc`;
|
|
33
|
+
- an ownership directory on ext2/3/4, XFS, Btrfs, tmpfs, overlayfs, ZFS,
|
|
34
|
+
F2FS, UBIFS, or bcachefs.
|
|
35
|
+
- **macOS:**
|
|
36
|
+
- the root-owned, non-writable system `/bin/ps`;
|
|
37
|
+
- an ownership directory with the same `statfs` type as `/`, which is the
|
|
38
|
+
APFS system volume on every macOS that Node.js 22 supports. macOS assigns
|
|
39
|
+
`f_type` dynamically, so no fixed number is compared.
|
|
40
|
+
|
|
41
|
+
Other filesystems fail closed before lock acquisition. Every other platform
|
|
42
|
+
fails closed with `platform_unsupported` before any client command is spawned.
|
|
31
43
|
|
|
32
44
|
## Ownership and transactions
|
|
33
45
|
|
|
34
46
|
Each adapter exposes inspection, apply, verify, and remove operations. Apply first inspects and refuses divergence; equivalent registrations are left unchanged. Before adding an absent registration, setup records a path-free `pending` transaction containing only the target, an opaque fingerprint, the previous `absent` state, and a transaction identifier. Post-apply inspection promotes it to `owned`. Rollback/remove require owned metadata and the same effective fingerprint. Any mismatch fails closed and leaves the registration untouched.
|
|
35
47
|
|
|
36
|
-
The production bridge derives Node from `process.execPath` and the entry point from the package root; configuration comes only from `PI_SESSION_MCP_CONFIG`. These values are internal command arguments and never CLI arguments, reports, or metadata. The opaque ownership fingerprint also binds `package.json`, the lockfile when the installation has one, and every built runtime JavaScript module.
|
|
48
|
+
The production bridge derives Node from `process.execPath` and the entry point from the package root; configuration comes only from `PI_SESSION_MCP_CONFIG`. These values are internal command arguments and never CLI arguments, reports, or metadata. The opaque ownership fingerprint also binds `package.json`, the lockfile when the installation has one, and every built runtime JavaScript module. `main.js` and the regular, non-symlink `setup-command-guardian.js` and `setup-platform.js` are mandatory release artifacts; any runtime symlink fails closed. A package/lock mismatch or later build-content drift therefore fails closed instead of silently reusing ownership. Public client commands run through a bounded `CommandRunner` (timeout, process-group termination, and output cap); undocumented config files are not parsed.
|
|
37
49
|
|
|
38
|
-
The production CLI uses an atomic durable store with restrictive filesystem modes; the in-memory store exists only for library tests. Neither form persists paths, environment values, secrets, prompts, SDK errors, or auth metadata.
|
|
50
|
+
The production CLI uses an atomic durable store with restrictive filesystem modes; the in-memory store exists only for library tests. Neither form persists paths, environment values, secrets, prompts, SDK errors, or auth metadata. A stable, never-renamed lock file is held through a kernel lock for the complete inspect → pending → client mutation → owned transaction. On Linux, the trusted util-linux `flock` locks the open file description that setup opened. On macOS, setup opens the lock file with `O_EXLOCK | O_NONBLOCK`, which takes a lock with flock(2) semantics on the open file description at open time; a busy lock is retried every 50 ms within the same acquisition bound. Newly created ownership directories and every atomic state rename are fsynced through their parent directory before success. There is no clock-based stale recovery: the kernel releases the lock only when every holder of the locked open-file description exits. A missing or untrusted `flock` or `ps`, an unsupported filesystem, a symlink/non-regular lock file, or an inode mismatch fails closed. The trusted same-UID operator must not unlink or replace the stable lock path while setup is running; setup compares the inode of the open lock file with the path after opening it, again once the fence is held, and before exposing transaction access, but same-UID filesystem tampering is outside the fence's trust boundary. A later run reconciles `pending` state: a matching equivalent registration is promoted to owned, an absent registration is safely retried, and any divergence fails closed.
|
|
39
51
|
|
|
40
|
-
Mutating public client commands run under a transaction guardian that inherits the locked file description, starts the client in its own process group, and watches both the parent PID and IPC channel. Parent EOF, `SIGINT`, `SIGTERM`, or command timeout causes TERM then KILL. The guardian releases the fence only after
|
|
52
|
+
Mutating public client commands run under a transaction guardian that inherits the locked file description, starts the client in its own process group, and watches both the parent PID and IPC channel. Parent EOF, `SIGINT`, `SIGTERM`, or command timeout causes TERM then KILL. The guardian releases the fence only after a determinate process-table scan confirms that no non-zombie group member remains: `/proc` on Linux, and on macOS a strictly parsed `/bin/ps -A -o pid=,pgid=,stat=` that must list the scanning process itself. Indeterminate cleanup returns a stable failure to the caller while the detached guardian retains the fence and continues cleanup, preventing a later setup transaction from racing the orphan. A mutation already accepted by an external client CLI or configuration backend cannot be rolled back automatically; ownership remains `pending` for reconciliation.
|
|
41
53
|
|
|
42
54
|
Here “gone” means no group member can still execute: a zombie has terminated and
|
|
43
55
|
released its file descriptors, even when its PID awaits an external reaper. The
|
package/docs/client-setup.md
CHANGED
|
@@ -7,8 +7,14 @@ installation with upgrade and removal, start with [Installation](installation.md
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
|
|
9
9
|
- Node.js **22.19.0 or newer**
|
|
10
|
-
- Linux
|
|
11
|
-
|
|
10
|
+
- Linux or macOS. For `setup` also:
|
|
11
|
+
- on Linux, a root-owned util-linux `flock` at `/usr/bin/flock` or
|
|
12
|
+
`/bin/flock`, readable `/proc`, and a supported local ownership filesystem;
|
|
13
|
+
- on macOS, the system `/bin/ps` and an ownership directory on the APFS
|
|
14
|
+
volume type of `/`.
|
|
15
|
+
|
|
16
|
+
A manual registration does not need these; see
|
|
17
|
+
[Register without setup](installation.md#register-without-setup).
|
|
12
18
|
- A local checkout of this repository
|
|
13
19
|
- Existing local Pi authentication only when sending a real prompt
|
|
14
20
|
|
|
@@ -64,11 +70,20 @@ divergent existing registration is reported and never automatically replaced.
|
|
|
64
70
|
Ownership is durable and contains only path-free, secret-free pending/owned
|
|
65
71
|
state. A kernel-fenced guardian retains transaction ownership until every
|
|
66
72
|
mutating client process-group member is gone, including after parent signals or
|
|
67
|
-
hard parent exit.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
hard parent exit. Setup on any platform other than Linux and macOS fails
|
|
74
|
+
closed before client spawn.
|
|
75
|
+
|
|
76
|
+
Supported ownership filesystems:
|
|
77
|
+
- **Linux:** ext2/3/4, XFS, Btrfs, tmpfs, overlayfs, ZFS, F2FS, UBIFS, and
|
|
78
|
+
bcachefs.
|
|
79
|
+
- **macOS:** the APFS volume type of `/`.
|
|
80
|
+
|
|
81
|
+
The following are outside the accepted setup boundary and fail closed where
|
|
82
|
+
they can be detected:
|
|
83
|
+
- network or FUSE filesystems;
|
|
84
|
+
- unreadable `/proc`;
|
|
85
|
+
- an untrusted `flock` or `ps` binary;
|
|
86
|
+
- same-UID replacement of the stable lock path.
|
|
72
87
|
|
|
73
88
|
Verify runs Doctor, checks exact registration equality, and then performs a
|
|
74
89
|
bounded direct MCP `initialize`, `tools/list`, and
|
|
@@ -103,7 +118,7 @@ Create an unversioned JSON configuration file such as `pi-session-mcp.json`:
|
|
|
103
118
|
}
|
|
104
119
|
```
|
|
105
120
|
|
|
106
|
-
Configuration is strict. A profile has exactly `default`, `permissionProfile`, `provider`, `model`, and `thinkingLevel
|
|
121
|
+
Configuration is strict. A profile has exactly `default`, `permissionProfile`, `provider`, `model`, and `thinkingLevel`, plus the optional `mcpServers` and `extensions` described in [Installation](installation.md); aliases follow the configured alias naming rules. At least one profile and exactly one default are required, and that default must be `read-only`. `thinkingLevel` is one of `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`. Workspace paths resolve relative to the configuration file. Clients can select only configured aliases, never an arbitrary working directory.
|
|
107
122
|
|
|
108
123
|
Use placeholders until a locally available provider/model pair is known. Do not put keys, tokens, credential objects, headers, base URLs, or authentication paths in this file.
|
|
109
124
|
|
|
@@ -122,6 +137,37 @@ codex mcp list
|
|
|
122
137
|
codex mcp get pi-session-mcp
|
|
123
138
|
```
|
|
124
139
|
|
|
140
|
+
`codex mcp add` and `codex mcp remove` rewrite `config.toml`. Codex CLI
|
|
141
|
+
`0.162.0` groups all `[mcp_servers.*]` tables together and drops a comment
|
|
142
|
+
directly above such a table; settings are preserved. Copy the file first if its
|
|
143
|
+
comments matter to you.
|
|
144
|
+
|
|
145
|
+
### Run Codex without prompts
|
|
146
|
+
|
|
147
|
+
Interactive Codex asks before it calls a tool that is marked
|
|
148
|
+
`destructiveHint: true`: `pi_session_prompt`, `pi_session_abort` and
|
|
149
|
+
`pi_session_close`. `codex exec` never asks, so Codex CLI `0.162.0` rejects
|
|
150
|
+
these calls with "MCP tool call requires approval, but approval policy is
|
|
151
|
+
never". A headless run can then start sessions but cannot prompt, abort or
|
|
152
|
+
close them.
|
|
153
|
+
|
|
154
|
+
Approve the three tools for each headless run:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
codex exec \
|
|
158
|
+
-c 'mcp_servers.pi-session-mcp.tools.pi_session_prompt.approval_mode="approve"' \
|
|
159
|
+
-c 'mcp_servers.pi-session-mcp.tools.pi_session_abort.approval_mode="approve"' \
|
|
160
|
+
-c 'mcp_servers.pi-session-mcp.tools.pi_session_close.approval_mode="approve"' \
|
|
161
|
+
'Your task'
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The same `approval_mode` entries can live in `config.toml` under
|
|
165
|
+
`[mcp_servers.pi-session-mcp.tools.<tool>]`. They do not change the output of
|
|
166
|
+
`codex mcp get`, so `setup` still treats the registration as equal. But
|
|
167
|
+
`codex mcp remove`, which `setup --remove` and `--rollback` use, deletes them,
|
|
168
|
+
so they are gone after an upgrade. The per-run overrides do not have this
|
|
169
|
+
problem.
|
|
170
|
+
|
|
125
171
|
## Register with Claude Code
|
|
126
172
|
|
|
127
173
|
Claude Code CLI 2.1.251 was locally confirmed with this command form. Replace every absolute-path placeholder with your local paths; use absolute paths for the Node executable, built entry point, and configuration file.
|
|
@@ -155,8 +201,8 @@ Server deadlines are profile-specific authentication/catalog work 5,000 ms per o
|
|
|
155
201
|
|
|
156
202
|
There are eight tools: `pi_capabilities_get`, `pi_session_start`, `pi_session_list`, `pi_session_get`, `pi_session_prompt`, `pi_session_abort`, `pi_session_close`, and `pi_turn_get`. Capability discovery accepts only `{}`. `pi_session_start` rejects the old `profile` input, raw `provider`, `model`, `thinkingLevel`, credential-like fields, and all unknown fields. Its safe session metadata includes the configured execution-profile alias and post-verified provider/model/effective thinking level; the existing `profile` output remains derived permission metadata.
|
|
157
203
|
|
|
158
|
-
The returned `thinkingLevel` records the effective Pi session setting.
|
|
159
|
-
|
|
204
|
+
The returned `thinkingLevel` records the effective Pi session setting. The
|
|
205
|
+
server compares it with the configured profile selection before admitting the
|
|
160
206
|
session; it does not verify provider-side reasoning or measure or cap its
|
|
161
207
|
reasoning-token budget: a provider may use reasoning tokens even when the session
|
|
162
208
|
selects `off`. Pi Session MCP does not project thinking content or provider-side
|
package/docs/doctor-design.md
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
`src/doctor-cli.ts` is a separate local entry point. It never imports the MCP
|
|
4
4
|
server, initializes transport, creates a Pi session, reads credentials, or
|
|
5
5
|
performs network activity.
|
|
6
|
+
It checks the package it was loaded from, never the current working
|
|
7
|
+
directory: an npm executable can run from any directory.
|
|
6
8
|
|
|
7
9
|
`runDoctor` returns one frozen result model (`schemaVersion: 1`, `ok`, a
|
|
8
10
|
sanitized semver `packageVersion` when metadata is available, ordered `checks`,
|
|
9
11
|
and `exitCode`). Human and JSON output are projections of that same model.
|
|
10
|
-
Subjects are only fixed component names
|
|
11
|
-
configuration values, errors,
|
|
12
|
+
Subjects are only fixed component names, validated workspace aliases, or
|
|
13
|
+
validated `<profile>:<package>` pairs; paths, configuration values, errors,
|
|
14
|
+
credentials, and provider data are never output.
|
|
12
15
|
|
|
13
16
|
Each check is `{ id, severity, subject, remediation? }`. Severity is `ok`,
|
|
14
17
|
`warning`, or `error`; remediation is a fixed safe sentence. The initial IDs
|
|
@@ -29,6 +32,10 @@ and owners are:
|
|
|
29
32
|
| `workspace_not_directory` | A resolved workspace is not a directory | validated workspace alias |
|
|
30
33
|
| `workspace_unreadable` | A resolved workspace cannot be read | validated workspace alias |
|
|
31
34
|
| `workspace_not_writable` | A workspace is not writable while a coding profile can target it | validated workspace alias |
|
|
35
|
+
| `extension_missing` | Pi has no user install of a package that a profile's `extensions` names | `<profile>:<package>` |
|
|
36
|
+
| `extension_version_mismatch` | The installed manifest's version differs from the pinned version, or the manifest or its `pi.extensions` entries are invalid | `<profile>:<package>` |
|
|
37
|
+
|
|
38
|
+
Doctor reads extension manifests from Pi's user install location only; it never installs packages and never imports extension code.
|
|
32
39
|
|
|
33
40
|
The Doctor reuses `loadConfig`; it does not duplicate alias, profile, thinking
|
|
34
41
|
level, or read-only-default rules. Workspace values are used only for local
|
package/docs/installation.md
CHANGED
|
@@ -35,19 +35,25 @@ the same arguments.
|
|
|
35
35
|
|
|
36
36
|
## Requirements
|
|
37
37
|
|
|
38
|
-
- Linux. `setup` fails closed on every other platform
|
|
39
|
-
|
|
38
|
+
- Linux or macOS. `setup` fails closed on every other platform. macOS is tested
|
|
39
|
+
in CI on macOS 15 and 26 on Apple silicon. Setup acceptance on a real Mac is
|
|
40
|
+
recorded for Claude Code only; Codex is still pending. See
|
|
41
|
+
[Acceptance](acceptance.md#status).
|
|
40
42
|
- Node.js **22.19.0 or newer**. `setup` registers the absolute path of the Node
|
|
41
43
|
executable that runs it, so run it with the Node installation that clients
|
|
42
44
|
should use.
|
|
43
|
-
- For `setup
|
|
45
|
+
- For `setup` on Linux: a root-owned util-linux `flock` at `/usr/bin/flock` or
|
|
44
46
|
`/bin/flock`, readable `/proc`, and the ownership state directory
|
|
45
47
|
`~/.local/state/pi-session-mcp` on ext2/3/4, XFS, Btrfs, tmpfs, overlayfs, ZFS,
|
|
46
48
|
F2FS, UBIFS or bcachefs. Network and FUSE filesystems fail closed.
|
|
49
|
+
- For `setup` on macOS: the system `/bin/ps`, and the ownership state directory
|
|
50
|
+
`~/.local/state/pi-session-mcp` on the same filesystem type as `/`, which is
|
|
51
|
+
APFS. A home directory on HFS+, exFAT, a network volume or any other
|
|
52
|
+
filesystem fails closed.
|
|
47
53
|
- Codex CLI or Claude Code. `setup` does not pin client versions. It accepts a
|
|
48
54
|
client whose public `mcp` command output matches the expected contract and
|
|
49
55
|
reports `unsupported` otherwise. The contract was last recorded with Codex CLI
|
|
50
|
-
`0.162.
|
|
56
|
+
`0.162.1` and Claude Code `2.1.296`. Manual registration, described in the
|
|
51
57
|
[client setup reference](client-setup.md), works independently of `setup`.
|
|
52
58
|
- For real prompts only: local Pi authentication and a provider and model pair
|
|
53
59
|
that the local Pi configuration knows. Installation, Doctor and `setup` need
|
|
@@ -146,6 +152,10 @@ Rules:
|
|
|
146
152
|
`azure`; a profile that names the old key must be changed.
|
|
147
153
|
- A profile may grant tools from local stdio MCP servers through `mcpServers`.
|
|
148
154
|
See [External MCP tools in embedded sessions](session-mcp.md).
|
|
155
|
+
- A profile may load Pi extensions that Pi has installed, through
|
|
156
|
+
`extensions`: a list of at most 8 entries of the form
|
|
157
|
+
`npm:<name>@<exact version>`, each package once. Ranges, tags, `git:`
|
|
158
|
+
sources and paths are rejected. See [Pi extensions](#pi-extensions).
|
|
149
159
|
- Never put keys, tokens, credential objects, headers, base URLs or
|
|
150
160
|
authentication paths into this file.
|
|
151
161
|
|
|
@@ -207,6 +217,13 @@ pi-session-mcp-setup --apply --target claude-code:user:pi-session-mcp
|
|
|
207
217
|
pi-session-mcp-setup --verify --target claude-code:user:pi-session-mcp
|
|
208
218
|
```
|
|
209
219
|
|
|
220
|
+
`setup` changes a Codex registration only through `codex mcp add` and
|
|
221
|
+
`codex mcp remove`, and Codex rewrites `config.toml` on each of them. Codex CLI
|
|
222
|
+
`0.162.0` groups all `[mcp_servers.*]` tables together and drops a comment
|
|
223
|
+
directly above such a table; settings are preserved. If your `config.toml`
|
|
224
|
+
contains comments worth keeping, copy it before `--apply`, `--rollback` or
|
|
225
|
+
`--remove` on `codex:user:pi-session-mcp`.
|
|
226
|
+
|
|
210
227
|
Each report has one line for the operation with its overall status, and one line
|
|
211
228
|
per target with a finding in the form `status (code)`:
|
|
212
229
|
|
|
@@ -238,6 +255,67 @@ If a registration already exists, `setup` never replaces it:
|
|
|
238
255
|
Start a new client session after registering. The client launches the server on
|
|
239
256
|
demand; there is nothing to start by hand.
|
|
240
257
|
|
|
258
|
+
### Register without setup
|
|
259
|
+
|
|
260
|
+
`setup` runs on Linux and macOS. On every other platform, Windows included, it
|
|
261
|
+
refuses every operation, `--verify` included, with `platform_unsupported`
|
|
262
|
+
before it starts a client command. On Linux and macOS, `--apply`, `--verify`,
|
|
263
|
+
`--rollback` and `--remove` also fail closed when the platform prerequisites in
|
|
264
|
+
[Requirements](#requirements) are not met. A manual registration needs none of
|
|
265
|
+
this: it uses the client's own commands and writes no ownership state. Use it
|
|
266
|
+
when `setup` cannot run, or when you manage client configuration yourself. No
|
|
267
|
+
acceptance level covers this path.
|
|
268
|
+
|
|
269
|
+
1. Install as in section 1, configure as in section 2, and run Doctor as in
|
|
270
|
+
section 3. Doctor has no platform-specific check and needs neither the lock
|
|
271
|
+
nor the process-table prerequisites of `setup`.
|
|
272
|
+
2. Register the server with the client's own command, as the
|
|
273
|
+
[Codex](client-setup.md#register-with-codex) and
|
|
274
|
+
[Claude Code](client-setup.md#register-with-claude-code) sections of the
|
|
275
|
+
client setup reference show. Use absolute paths and the Node executable that
|
|
276
|
+
clients should use. The entry point is `dist/main.js` inside the package
|
|
277
|
+
directory: in an npm installation `echo "$(npm root -g)/pi-session-mcp"`
|
|
278
|
+
prints that directory, in a release checkout it is the checkout. Write the
|
|
279
|
+
resulting absolute path into the command; a substitution inside the single
|
|
280
|
+
quotes of the Codex example would not be expanded.
|
|
281
|
+
3. Check the registration. These checks are not a replacement for
|
|
282
|
+
`setup --verify`: it also performs a bounded direct MCP handshake, and
|
|
283
|
+
outside `setup` nothing does that yet. The last check below depends on the
|
|
284
|
+
client, which can hang or hide a handshake problem, so treat the result as
|
|
285
|
+
weaker evidence than `--verify`.
|
|
286
|
+
- Doctor passed in item 1.
|
|
287
|
+
- Compare the stored registration with what you meant to register.
|
|
288
|
+
`claude mcp get pi-session-mcp` and `codex mcp get pi-session-mcp` print
|
|
289
|
+
the Node executable, the entry point and the configuration path that the
|
|
290
|
+
client stored, and Claude Code also prints the scope. Each must equal the
|
|
291
|
+
value you passed. `--verify` makes this comparison for `setup`. Without
|
|
292
|
+
it, a registration that still points at an old release directory passes
|
|
293
|
+
every other check.
|
|
294
|
+
- `claude mcp list` and `claude mcp get pi-session-mcp` report `Connected`
|
|
295
|
+
when the client could start the server and complete the MCP connection.
|
|
296
|
+
A registration with `-s project` reports `Pending approval` instead,
|
|
297
|
+
because Claude Code connects to a project server only after you approve it
|
|
298
|
+
in a session in that project. Approve it, then expect `Connected`.
|
|
299
|
+
- Start a new client session and call `pi_capabilities_get` with `{}`. The
|
|
300
|
+
client lists the eight tools, and the call returns the configured aliases.
|
|
301
|
+
|
|
302
|
+
Like `--verify`, none of these starts a Pi session or contacts a provider.
|
|
303
|
+
4. Upgrade and remove with the client's own commands. Nothing owns the
|
|
304
|
+
registration and it is not bound to the build.
|
|
305
|
+
- An npm upgrade replaces the package in place and keeps the entry point.
|
|
306
|
+
Close the client sessions, install the new version as in section 1, run
|
|
307
|
+
Doctor as in section 3, and repeat the checks of item 3. Do not run the
|
|
308
|
+
`cp` of section 2 again: it replaces your configuration with the packaged
|
|
309
|
+
example.
|
|
310
|
+
- A new release directory needs a new registration. Run
|
|
311
|
+
`codex mcp remove pi-session-mcp` or
|
|
312
|
+
`claude mcp remove pi-session-mcp -s <scope>`, add the server again with
|
|
313
|
+
the new paths, run Doctor, and repeat the checks of item 3.
|
|
314
|
+
- To remove it entirely, run the client's remove command, then uninstall the
|
|
315
|
+
package or delete the release directory and the configuration file. A
|
|
316
|
+
manual registration leaves no ownership state, so the `--remove` of the
|
|
317
|
+
Remove section does not apply.
|
|
318
|
+
|
|
241
319
|
## 5. Run a first session
|
|
242
320
|
|
|
243
321
|
From the client, call the tools in this order:
|
|
@@ -266,6 +344,53 @@ configuration or credentials.
|
|
|
266
344
|
The configuration path is part of the registration. Editing the file in place
|
|
267
345
|
needs no new registration; moving it does.
|
|
268
346
|
|
|
347
|
+
## Pi extensions
|
|
348
|
+
|
|
349
|
+
An execution profile can load Pi extensions that Pi has installed. Install them
|
|
350
|
+
with Pi (`pi install npm:<name>`; update with `pi update --extensions`), then
|
|
351
|
+
pin the installed version in the profile:
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
"code-cursor": {
|
|
355
|
+
"default": false,
|
|
356
|
+
"permissionProfile": "coding",
|
|
357
|
+
"provider": "cursor",
|
|
358
|
+
"model": "<model id from Pi's cursor catalog>",
|
|
359
|
+
"thinkingLevel": "high",
|
|
360
|
+
"extensions": ["npm:pi-cursor-sdk@0.5.3"]
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
- **Install location.** Only Pi's own user install counts:
|
|
365
|
+
`<Pi agent dir>/npm/node_modules/<name>`, normally `~/.pi/agent/npm/…`,
|
|
366
|
+
created by `pi install npm:<name>`. A package installed with `npm i -g` is
|
|
367
|
+
reported as not installed: sessions report `extension_not_installed`, Doctor
|
|
368
|
+
reports `extension_missing`.
|
|
369
|
+
- **Version pin.** The server never installs or updates extensions. If Pi has
|
|
370
|
+
another version installed, sessions of that profile fail with
|
|
371
|
+
`extension_version_mismatch` and Doctor reports it. After `pi update
|
|
372
|
+
--extensions`, update the pinned version and restart the client.
|
|
373
|
+
- **Full capabilities.** An extension loads with its providers, tools, hooks
|
|
374
|
+
and system-prompt changes. In `read-only` profiles the model still gets only
|
|
375
|
+
`read`, `grep`, `find` and `ls`; in `coding` profiles every extension tool
|
|
376
|
+
may be active. Extension commands never run from client prompts, but
|
|
377
|
+
extension input hooks can still see, rewrite or consume a client prompt.
|
|
378
|
+
- **Fail-closed start.** A session start fails with `extension_load_failed` if
|
|
379
|
+
an extension reports a hook error while binding. It fails the same way if an
|
|
380
|
+
extension registers an MCP server while loading or starting, because Pi's
|
|
381
|
+
built-in MCP extension is not loaded. An extension tool whose name collides
|
|
382
|
+
with another tool, or whose name is not a plain identifier, fails with
|
|
383
|
+
`extension_tool_conflict`.
|
|
384
|
+
- **Cursor runtime.** `pi-cursor-sdk` runs Cursor's local agent runtime. That
|
|
385
|
+
runtime starts its own processes (for example git and the MCP servers
|
|
386
|
+
configured for Cursor) and may act through Cursor's own tooling, so a
|
|
387
|
+
`read-only` profile limits only Pi's tools, not what the Cursor runtime does.
|
|
388
|
+
- **Trust.** Only list extensions you trust like any other program you run:
|
|
389
|
+
they execute inside the server process with your rights.
|
|
390
|
+
- **Cursor sign-in limit.** Without a stored Cursor key, `pi-cursor-sdk`
|
|
391
|
+
reports a placeholder key. Session start succeeds and the first prompt fails
|
|
392
|
+
with `turn_failed`. Store the key with Pi first.
|
|
393
|
+
|
|
269
394
|
## Upgrade
|
|
270
395
|
|
|
271
396
|
`setup` binds each owned registration to the package manifests and to every
|