pi-mcp-adapter 2.38.0 → 3.0.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/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.0.0] - 2026-09-26
11
+
12
+ ### Highlights
13
+
14
+ - The adapter now has its own config file, `mcp-adapter.json`, so it can run alongside Pi's upcoming built-in MCP support without starting the same servers twice. If you used `mcp.json` with the adapter, rename it (see Breaking).
15
+ - Opening a repository no longer starts its MCP servers on its own. Project servers wait until you trust the project and approve each server.
16
+ - `mcpScript` code now runs in a QuickJS sandbox that scripts cannot escape to reach your files or processes.
17
+ - MCP servers can now match each request to the Pi tool call that made it.
18
+
19
+ ### Breaking
20
+
21
+ - The adapter no longer reads `<Pi agent dir>/mcp.json` or `.pi/mcp.json`. Those files now belong to Pi's built-in MCP support. Rename yours to `mcp-adapter.json` in the same folder. The format is the same, so `mv` is enough; if `mcp-adapter.json` already exists, merge the two. Until you do, Pi shows a warning with the exact command. `.mcp.json`, `~/.config/mcp/mcp.json`, and `--mcp-config` work as before.
22
+ - The interactive command is now `/mcp-adapter`. `/mcp` still works as a shortcut when Pi's built-in MCP extension is not installed.
23
+
24
+ ### Security
25
+
26
+ - `mcpScript` now runs scripts in a memory-limited QuickJS/WASM sandbox instead of Node's `vm` module, which scripts could escape to reach `process`, the filesystem, or child processes ([#676](https://github.com/nicobailon/pi-mcp-adapter/issues/676)). Each script can emit up to 16 MiB of output, and error messages are capped at 64 KiB. Values pass between the script and Pi as JSON, so values that are not plain JSON still show up but may be formatted differently than before.
27
+ - MCP servers defined by a project no longer start until the project is trusted and you approve the server. This covers `.mcp.json`, `.pi/mcp-adapter.json`, and servers a project brings in through imports, plugins, repo-local host configs, or Pi packages in its settings. In an untrusted project they stay blocked. In a trusted interactive session, Pi shows the server's command or URL and asks once; the approval is saved, and Pi asks again if the server definition changes. Headless sessions skip unapproved servers unless your user-global config sets `settings.projectServers` to `"allow"`. `/mcp-adapter status` shows why a server is blocked. Fixes [#675](https://github.com/nicobailon/pi-mcp-adapter/issues/675).
28
+
29
+ ### Added
30
+
31
+ - MCP tool calls now include the id of the Pi tool call that made them, under `_meta["pi-mcp-adapter/toolCallId"]`, so servers can match requests to Pi's tool calls in their logs and traces. Direct tools, the `mcp` tool, and `mcp__<server>` tools send it. `mcpScript` calls do not, because a script is not a single tool call. Thanks to [@sebavalaris](https://github.com/sebavalaris) for [PR #673](https://github.com/nicobailon/pi-mcp-adapter/pull/673).
32
+
10
33
  ## [2.38.0] - 2026-09-26
11
34
 
12
35
  ### Highlights
@@ -751,7 +774,7 @@ The ranked search scoring, did-you-mean suggestions, approval patterns, endpoint
751
774
  - Added a dedicated Pi-owned onboarding state file so shared-config hints behave as one-time guidance instead of repeating every session.
752
775
 
753
776
  ### Changed
754
- - Updated config precedence to prefer shared MCP files first, then Pi overrides, with `.pi/mcp.json` acting as the final Pi-specific project override.
777
+ - At the time, updated config precedence to prefer shared MCP files first, with `.pi/mcp.json` as the final project override. This legacy layout is superseded by the `mcp-adapter.json` hard cutover documented above.
755
778
  - Updated Claude Code compatibility probing to prefer modern Claude MCP config locations before legacy paths.
756
779
  - Updated project scaffolding so generated `.mcp.json` files are safe minimal shells instead of fake placeholder servers that fail on first reload.
757
780
  - Updated the setup panel and README for clearer first-run guidance, improved spacing, and a more digestible shared-MCP-first setup story.
package/OAUTH.md CHANGED
@@ -171,7 +171,7 @@ The SDK automatically:
171
171
  To clear stored OAuth credentials and force a fresh authorization:
172
172
 
173
173
  ```
174
- /mcp logout my-oauth-server
174
+ /mcp-adapter logout my-oauth-server
175
175
  ```
176
176
 
177
177
  Within one Pi process, logout is a linearizable boundary: OAuth work that began
package/README.md CHANGED
@@ -32,11 +32,11 @@ The adapter reads standard MCP files automatically. No extra setup needed if you
32
32
 
33
33
  | You already have... | What happens |
34
34
  |---------------------|--------------|
35
- | `.mcp.json` or `~/.config/mcp/mcp.json` | Pi uses it immediately. Use `.mcp.json` for project/team sharing and `~/.config/mcp/mcp.json` for all projects. The first time you open `/mcp`, you'll see a short heads-up explaining which file Pi detected and that Pi only writes adapter-specific overrides to its own files. |
36
- | Host-specific configs (Cursor, Claude Code, Codex, etc.) but no standard MCP files | Run `/mcp setup` to adopt those host configs into Pi. The setup flow shows exactly what it found, lets you pick which ones to import, and previews the exact file changes before writing. |
37
- | Nothing configured yet | Run `/mcp setup`, choose project `.mcp.json` or global `~/.config/mcp/mcp.json`, then scaffold a minimal config, add a curated known server, quick-add RepoPrompt, or inspect what the adapter discovered on your machine. |
35
+ | `.mcp.json` or `~/.config/mcp/mcp.json` | Pi uses it immediately. Use `.mcp.json` for project/team sharing and `~/.config/mcp/mcp.json` for all projects. |
36
+ | Host-specific configs (Cursor, Claude Code, Codex, etc.) but no standard MCP files | Run `/mcp-adapter setup` to adopt those host configs into Pi. The setup flow shows exactly what it found, lets you pick which ones to import, and previews the exact file changes before writing. |
37
+ | Nothing configured yet | Run `/mcp-adapter setup`, choose project `.mcp.json` or global `~/.config/mcp/mcp.json`, then scaffold a minimal config, add a curated known server, quick-add RepoPrompt, or inspect what the adapter discovered on your machine. |
38
38
 
39
- If you prefer the terminal, you can also run `pi-mcp-adapter init` after install to scan for host-specific configs and add missing compatibility imports to the Pi agent dir (`~/.pi/agent/mcp.json` by default, or `$PI_CODING_AGENT_DIR/mcp.json` when set).
39
+ If you prefer the terminal, you can also run `pi-mcp-adapter init` after install to scan for host-specific configs and add missing compatibility imports to the adapter config (`~/.pi/agent/mcp-adapter.json` by default, or `$PI_CODING_AGENT_DIR/mcp-adapter.json` when set).
40
40
 
41
41
  ## Quick Start
42
42
 
@@ -55,27 +55,25 @@ Preferred project config: `.mcp.json`
55
55
 
56
56
  Preferred user-global shared config: `~/.config/mcp/mcp.json` (for all projects). Pi also reads the tool-agnostic global paths `~/.agents/mcp.json` and `~/.agents/mcp/mcp.json` as compatibility inputs.
57
57
 
58
- Pi-owned files are not additional normal setup choices. They hold Pi-specific settings, compatibility imports, and adapter-only overrides:
58
+ The adapter does not read Pi's `<Pi agent dir>/mcp.json` or `.pi/mcp.json` at all. If you previously used either file with this adapter, rename it to `mcp-adapter.json`; the format is unchanged, so a plain `mv` works (merge the files if the target already exists). This leaves `mcp.json` exclusively to Pi's built-in MCP support, so Pi and the adapter never start the same servers.
59
59
 
60
- - `<Pi agent dir>/mcp.json` — Pi global override (`~/.pi/agent/mcp.json` by default)
61
- - `.pi/mcp.json` — Pi project override
62
-
63
- Host-specific configs are detected and shown by `/mcp setup` and `pi-mcp-adapter init`, but they are compatibility inputs rather than normal setup paths and are not loaded automatically. The normal `/mcp` panel does not scan host-specific files when `settings.hostConfigDiscovery` is `"off"`. To explicitly opt in to host-config fallback discovery, set `settings.hostConfigDiscovery` to `"on"` or run `pi-mcp-adapter init --discover-host-configs`. The default is `"off"`; `"prompt"` is available for integrations that want detection without activation. Host configs are lower precedence than every shared and Pi-owned source, and `/mcp setup` continues to offer explicit import adoption. Discovery reports source paths, provenance, and same-name conflicts; it never writes to external host files or silently launches commands from them.
60
+ Host-specific configs are detected and shown by `/mcp-adapter setup` and `pi-mcp-adapter init`, but they are compatibility inputs rather than normal setup paths and are not loaded automatically. The normal `/mcp-adapter` panel does not scan host-specific files when `settings.hostConfigDiscovery` is `"off"`. To explicitly opt in to host-config fallback discovery, set `settings.hostConfigDiscovery` to `"on"` or run `pi-mcp-adapter init --discover-host-configs`. The default is `"off"`; `"prompt"` is available for integrations that want detection without activation. Host configs are lower precedence than every normal config source, and `/mcp-adapter setup` continues to offer explicit import adoption. Discovery reports source paths, provenance, and same-name conflicts; it never writes to external host files or silently launches commands from them.
64
61
 
65
62
  Precedence is (later entries win):
66
63
 
67
64
  1. `~/.config/mcp/mcp.json`
68
65
  2. `~/.agents/mcp.json`
69
66
  3. `~/.agents/mcp/mcp.json`
70
- 4. `<Pi agent dir>/mcp.json`
71
- 5. `.mcp.json`
72
- 6. `.pi/mcp.json`
67
+ 4. `<Pi agent dir>/mcp-adapter.json`
68
+ 5. opted-in ancestors, farthest first: `.mcp.json`, `.pi/mcp-adapter.json`
69
+ 6. `.mcp.json`
70
+ 7. `.pi/mcp-adapter.json`
73
71
 
74
- Ancestor discovery is off by default. To opt in, set `settings.ancestorConfigRoots` in a user-global config above, or in the explicitly selected `--mcp-config`/`configPath` file, for example `"ancestorConfigRoots": ["~/work/team"]`. Each root must be an explicit absolute path or `~/...` and resolve to an existing directory under `$HOME`. Roots that do not contain the canonical cwd are ignored. If several roots match, only the nearest (deepest) is used. Project `.mcp.json` and `.pi/mcp.json` files cannot enable discovery or extend the boundary.
72
+ Ancestor discovery is off by default. To opt in, set `settings.ancestorConfigRoots` in a user-global source (`~/.config/mcp/mcp.json`, either `~/.agents` MCP file, or the global `mcp-adapter.json`) or in the explicitly selected `--mcp-config`/`configPath` file, for example `"ancestorConfigRoots": ["~/work/team"]`. Each root must be an explicit absolute path or `~/...` and resolve to an existing directory under `$HOME`. Roots that do not contain the canonical cwd are ignored. If several roots match, only the nearest (deepest) is used. Project files cannot enable discovery or extend the boundary.
75
73
 
76
- Within the selected root, existing `.mcp.json` and `<configDir>/mcp.json` (normally `.pi/mcp.json`) files load between steps 4 and 5, from the root through parent(cwd), farthest first. Nearer directories override farther ones, Pi overrides shared config within each directory, and cwd files win over ancestors. Search never goes above the configured root or `$HOME`; the boundary limits discovery but is not a file-ownership or symlink-target sandbox. Only configure roots whose project files you trust. `/mcp setup` write targets and project-local `/mcp disable` and `/mcp enable` overrides are unchanged.
74
+ Within the selected root, `.mcp.json` and `<configDir>/mcp-adapter.json` load from the root through parent(cwd), farthest first. Nearer directories override farther ones, adapter config overrides shared config within each directory, and cwd files win over ancestors. Search never goes above the configured root or `$HOME`; the boundary limits discovery but is not a file-ownership or symlink-target sandbox. Only configure roots whose project files you trust.
77
75
 
78
- `/mcp disable <server>` and `/mcp enable <server>` persist only the `disabled` field in the project-local `.pi/mcp.json`, which is the highest-precedence Pi layer. Enabling removes the project flag when lower layers are enabled, or writes `false` when needed to override a disabled lower source. This applies even when the effective server came from a shared global/project file, an imported host config, or `configPath`; the source file is never rewritten and credentials are never copied. Run `/reload` after changing the flag so registered tool surfaces are refreshed. The manual equivalent is to add `{ "disabled": true }` to a server in any normal MCP config. Supplied in-memory `createMcpAdapter({ config })` configurations are isolated and do not read or write this project override; the commands are unavailable in that mode.
76
+ `/mcp-adapter disable <server>` and `/mcp-adapter enable <server>` persist only the `disabled` field in the project-local `.pi/mcp-adapter.json`, the highest-precedence adapter layer. The source file is never rewritten and credentials are never copied. Run `/reload` after changing the flag so registered tool surfaces are refreshed. Supplied in-memory `createMcpAdapter({ config })` configurations are isolated and do not read or write this project override; the commands are unavailable in that mode.
79
77
 
80
78
  Servers are **lazy by default** — they won't connect until you actually call one of their tools. The adapter caches tool metadata so search and describe work without live connections.
81
79
 
@@ -102,7 +100,7 @@ Two calls instead of 26 tools cluttering the context.
102
100
 
103
101
  ### File Layout
104
102
 
105
- Use the shared MCP files when you want one setup to work across hosts, and Pi-owned files when you need Pi-specific overrides or settings.
103
+ Use shared MCP files when you want one setup to work across hosts, and adapter-owned files for adapter-specific overrides or settings.
106
104
 
107
105
  | File | Purpose |
108
106
  |------|---------|
@@ -110,8 +108,10 @@ Use the shared MCP files when you want one setup to work across hosts, and Pi-ow
110
108
  | `~/.agents/mcp.json` | User-global tool-agnostic MCP config |
111
109
  | `~/.agents/mcp/mcp.json` | User-global tool-agnostic MCP config |
112
110
  | `.mcp.json` | Project-local shared MCP config |
113
- | `<Pi agent dir>/mcp.json` | Pi global override and compatibility imports (`~/.pi/agent/mcp.json` by default) |
114
- | `.pi/mcp.json` | Pi project override |
111
+ | `<Pi agent dir>/mcp.json` | Pi built-in MCP config; never read by this adapter |
112
+ | `<Pi agent dir>/mcp-adapter.json` | Global adapter settings, imports, and overrides (`~/.pi/agent/mcp-adapter.json` by default) |
113
+ | `.pi/mcp.json` | Project Pi built-in MCP config; never read by this adapter |
114
+ | `.pi/mcp-adapter.json` | Project adapter settings and overrides |
115
115
 
116
116
  For local stdio servers, a leading `~/` is expanded to the current user's home
117
117
  directory in `command`, `args`, and `cwd`. On Windows, the equivalent `~\\`
@@ -137,7 +137,7 @@ The adapter can load MCP servers from [Agent Plugins](https://agent-plugins.org/
137
137
 
138
138
  Each directory must contain a valid Agent Plugins 1.0 `plugin.json`. If it also has a root `mcp.json`, the adapter loads its `mcpServers` entries and prefixes them as `<plugin>__<server>`. The loader uses the Agent Plugins transport declared by each server `type` and skips invalid entries without blocking other servers. For stdio plugin servers, `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` are expanded only in `args`, `env`, and `cwd`; the adapter sets both variables for the child process and stores plugin data under the Pi agent directory.
139
139
 
140
- `inheritEnv` is an adapter-specific Pi field, not an Agent Plugins or OpenCode schema field. Do not add it to a plugin's strict `mcp.json`; to opt a plugin stdio server out of host-environment inheritance, set `inheritEnv: false` in a normal Pi override using the translated `<plugin>__<server>` name:
140
+ `inheritEnv` is an adapter-specific field, not an Agent Plugins or OpenCode schema field. Do not add it to a plugin's strict `mcp.json`; to opt a plugin stdio server out of host-environment inheritance, set `inheritEnv: false` in an `mcp-adapter.json` override using the translated `<plugin>__<server>` name:
141
141
 
142
142
  ```json
143
143
  {
@@ -147,7 +147,7 @@ Each directory must contain a valid Agent Plugins 1.0 `plugin.json`. If it also
147
147
  }
148
148
  ```
149
149
 
150
- Agent Plugins is a portable package format. Native Pi MCP config remains `.mcp.json`, `~/.config/mcp/mcp.json`, and Pi-owned overrides.
150
+ Agent Plugins is a portable package format. Native adapter config remains `.mcp.json`, `~/.config/mcp/mcp.json`, and `mcp-adapter.json` overrides.
151
151
 
152
152
  ### Local Claude plugin bundles
153
153
 
@@ -265,7 +265,7 @@ The public subpath exposes only token read/update helpers plus a status helper.
265
265
 
266
266
  ### Runtime status snapshots
267
267
 
268
- Extensions can subscribe to the adapter's versioned shared event-bus channel instead of parsing `/mcp` or `mcp({})` output:
268
+ Extensions can subscribe to the adapter's versioned shared event-bus channel instead of parsing `/mcp-adapter` or `mcp({})` output:
269
269
 
270
270
  ```ts
271
271
  import { MCP_STATUS_EVENT, type McpStatusSnapshot } from "pi-mcp-adapter";
@@ -322,7 +322,7 @@ In the configuration examples below, `30000` is illustrative only. If `requestTi
322
322
  | `oauth.authServerMetadataUrl` | HTTPS URL of an OAuth/OIDC authorization-server metadata document. When set, this document is authoritative instead of MCP protected-resource discovery; its issuer remains validated by default |
323
323
  | `oauth.skipIssuerMetadataValidation` | `true` disables the OAuth authorization-server metadata issuer check for this server. This weakens OAuth mix-up protection and should only be used for known-misconfigured internal servers while their metadata is being fixed. |
324
324
  | `bearerToken` / `bearerTokenEnv` | Token or env var name; `bearerToken` supports `${VAR}` and `$env:VAR` interpolation. A leading `!` in `bearerToken` runs a command when the HTTP server connects; use `!!` for a literal leading `!`. |
325
- | `bearerTokenStore` | Set to `true` to read a static bearer token from the adapter-owned OS credential store when `auth` is `"bearer"` and no `bearerToken` or `bearerTokenEnv` is configured. Stored records are keyed only by the server name, bind to the resolved server URL, and are never named by config. Store a token with `pi-mcp-adapter token set <server>`, which reads it from a masked prompt or stdin pipe and never from an argument. `/mcp token status <server>` and `/mcp token remove <server>` manage non-secret state inside Pi; `/mcp token set` stays disabled until Pi exposes masked secret input. |
325
+ | `bearerTokenStore` | Set to `true` to read a static bearer token from the adapter-owned OS credential store when `auth` is `"bearer"` and no `bearerToken` or `bearerTokenEnv` is configured. Stored records are keyed only by the server name, bind to the resolved server URL, and are never named by config. Store a token with `pi-mcp-adapter token set <server>`, which reads it from a masked prompt or stdin pipe and never from an argument. `/mcp-adapter token status <server>` and `/mcp-adapter token remove <server>` manage non-secret state inside Pi; `/mcp-adapter token set` stays disabled until Pi exposes masked secret input. |
326
326
  | `lifecycle` | `"lazy"` (default), `"eager"`, `"keep-alive"`, or `"lazy-keep-alive"` |
327
327
  | `idleTimeout` | Minutes before idle disconnect (overrides global) |
328
328
  | `requestTimeoutMs` | Request timeout in milliseconds for live MCP calls (overrides global; if omitted or `<= 0`, the MCP SDK default timeout is used) |
@@ -427,9 +427,9 @@ Install an MCP endpoint without editing configuration:
427
427
  mcp({ action: "install", url: "https://example.com/mcp" })
428
428
  ```
429
429
 
430
- Install validates and connects the endpoint. New entries use a name derived from the hostname and are saved to Pi's global MCP config; existing URL entries are reused without rewriting. Pass `server` to choose a name or `target: "project"` to save to the project's `.mcp.json`. Unsafe URLs, name collisions, and failed connections are not persisted.
430
+ Install validates and connects the endpoint. New entries use a name derived from the hostname and are saved to the global `mcp-adapter.json`; existing URL entries are reused without rewriting. Pass `server` to choose a name or `target: "project"` to save to the project's `.mcp.json`. Unsafe URLs, name collisions, and failed connections are not persisted.
431
431
 
432
- Set `settings.allowInstall` to `false` in an MCP config file (`mcp.json` or `.mcp.json`, not Pi's `settings.json`) to block `mcp({ action: "install" })` for constrained or headless agents. Connect, search, tool calls, authentication, runtime registration, and interactive setup are unaffected.
432
+ Set `settings.allowInstall` to `false` in `mcp-adapter.json` or another adapter config source (not Pi's `mcp.json` or `settings.json`) to block `mcp({ action: "install" })` for constrained or headless agents. Connect, search, tool calls, authentication, runtime registration, and interactive setup are unaffected.
433
433
 
434
434
  In exclusive config mode, a project target must be the active config path; otherwise use the global target. URL install cannot promote runtime-registered servers: save their complete definitions manually so required headers and transport/auth settings are retained.
435
435
 
@@ -461,6 +461,12 @@ mcp({
461
461
 
462
462
  You can also pass only the `code` query parameter with `args: { code: "..." }`. Treat authorization URLs and codes as sensitive; they can grant access to the MCP server until the flow expires or completes.
463
463
 
464
+ ### Project server trust
465
+
466
+ Servers defined or changed by project-scoped MCP files (`.mcp.json`, `.pi/mcp-adapter.json`, and opted-in ancestor project files) do not run merely because a repository was opened. This includes servers brought in through project `imports`, `claudePlugins`, `settings.agentPluginPaths`, repo-local host config files (even when imports or discovery are enabled globally), or Pi packages listed in project `.pi/settings.json`. If Pi reports the project as untrusted, the adapter blocks them. In a trusted interactive session, the adapter shows the source file and command or URL and asks once before the first connection. The approval is stored under the Pi agent directory and is tied to the canonical project path, server name, and complete effective definition; changing the definition requires a new approval. Blocked servers are identified in the panel, while MCP status includes the required trust or approval action.
467
+
468
+ In print, JSON, and RPC sessions, an unapproved project server is skipped. To intentionally allow project servers in trusted headless sessions, set `"projectServers": "allow"` in the **user-global** `settings` object. The default is `"ask"`; project files cannot change this policy. Explicit config files, programmatic configuration, global/import/plugin servers, and runtime registrations retain their existing behavior. Project servers are always excluded from extension-load initialization and are admitted only after `session_start` supplies Pi's trust context.
469
+
464
470
  ### Lifecycle Modes
465
471
 
466
472
  - **`lazy`** (default) — Don't connect at startup. Connect on first tool call. Disconnect after idle timeout. Cached metadata keeps search/list working without connections.
@@ -489,6 +495,7 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
489
495
  "notifyOnStartupConnect": true,
490
496
  "warnOnLargeDirectTools": true,
491
497
  "hostConfigDiscovery": "off",
498
+ "projectServers": "ask",
492
499
  "approveTools": ["github_delete_*", "notion_update_*"],
493
500
  "oauthDir": ".pi/mcp-oauth",
494
501
  "trace": {
@@ -510,11 +517,12 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
510
517
  | `requestTimeoutMs` | Global request timeout in milliseconds for live MCP calls (if omitted or `<= 0`, the MCP SDK default timeout is used) |
511
518
  | `deferWithMissingMetadata` | Allow lazy startup to defer when persisted metadata is missing or invalid (default: `false`). See [Direct Tools](#direct-tools) for the startup tradeoff. |
512
519
  | `showStatusIcon` | Show the plug icon in MCP status and connection text (default: `true`). Set to `false` for plain `MCP: ...` text. |
513
- | `mcpFooterStatus` | MCP footer verbosity: `"full"` (default), `"compact"` for `MCP connected/enabled`, or `"off"` to clear the persistent footer status. `/mcp status` remains available. |
520
+ | `mcpFooterStatus` | MCP footer verbosity: `"full"` (default), `"compact"` for `MCP connected/enabled`, or `"off"` to clear the persistent footer status. `/mcp-adapter status` remains available. |
514
521
  | `toolResultRendering` | MCP tool result row style: `"compact"` (default) uses self-rendered rows, or `"boxed"` restores the legacy Pi boxed tool row. |
515
522
  | `collapsedResultLines` | Number of result text lines to show before expansion: `1`, `2`, or `3`. Defaults to `1` in compact mode and `3` in boxed mode. |
516
523
  | `notifyOnStartupConnect` | Show successful startup connection notices (default: `true`). Set to `false` to suppress routine `MCP: N servers connected (M tools)` notices. Connection errors and authentication warnings remain visible. |
517
524
  | `hostConfigDiscovery` | Host-specific config policy: `"off"` (default), `"prompt"` (detect/report only), or `"on"` (explicitly load detected host configs as the lowest-precedence fallback) |
525
+ | `projectServers` | Project-server admission policy for trusted headless sessions: `"ask"` (default, skip unapproved servers) or `"allow"`. Only user-global or explicitly selected config may set it; project files are ignored. |
518
526
  | `ancestorConfigRoots` | Trusted absolute or `~/...` roots for opt-in ancestor config discovery. Only user-global or explicitly selected config may set it; roots outside cwd are ignored and the deepest matching root is used. |
519
527
  | `agentPluginPaths` | Agent Plugins package directories to load MCP servers from. Relative paths resolve from the active project cwd. |
520
528
  | `approveTools` | `true` to require approval before every MCP tool call, or an array of glob patterns such as `["github_delete_*", "notion_update_*"]`. Per-server `approveTools` overrides this. |
@@ -529,7 +537,7 @@ When any enabled server uses `eager` or `keep-alive`, initialization also starts
529
537
  | `freezeDirectTools` | Keep direct-tool registration stable after the initial sync so metadata updates and explicit reconnects do not rebuild the system prompt. Proxy/search/cache metadata still refreshes. Default: false. |
530
538
  | `scriptMode` | Register the MCP-only `mcpScript` plain-JavaScript tool (default: true). Set to `false` to hide it. |
531
539
  | `exposeResources` | Expose MCP resources as tools (default: `true`). Set to `false` to disable globally across all servers. Per-server `exposeResources` overrides this. |
532
- | `jev` | Optional System One Jev settings. A valid System One key enables semantic search across every enabled MCP server by default; `semanticSearch: false` disables it. `scriptEvaluation` remains disabled by default and requires an `allowedServers` source allowlist when enabled. `jev: false` disables both. Run `/mcp jev setup` for guided configuration. |
540
+ | `jev` | Optional System One Jev settings. A valid System One key enables semantic search across every enabled MCP server by default; `semanticSearch: false` disables it. `scriptEvaluation` remains disabled by default and requires an `allowedServers` source allowlist when enabled. `jev: false` disables both. Run `/mcp-adapter jev setup` for guided configuration. |
533
541
  | `disableProxyTool` | Hide the `mcp` proxy tool once configured direct tools are fully available from cache. Ignored while any server uses `directTools: "search"`, whose tools are registered inactive and can only be activated through the gateway (`mcp({ search })` or a successful `mcp({ tool })` call). |
534
542
  | `autoAuth` | Auto-run OAuth on `connect`/tool calls when a server needs auth, then retry once (default: false). |
535
543
  | `sampling` | Allow MCP servers to sample through Pi models, honoring `modelPreferences.hints` before current/default fallback (default: true when UI approval is available). |
@@ -628,7 +636,7 @@ The quickest desktop setup is:
628
636
  pi-mcp-adapter key set systemone
629
637
  ```
630
638
 
631
- That is enough to use semantic search across all enabled MCP tools. Run `/mcp jev setup` in Pi when you want to restrict which enabled servers may share semantic-search data. The command saves a project-scoped allowlist and reloads Pi automatically. Verify the stored credential at any time with `pi-mcp-adapter key status systemone`.
639
+ That is enough to use semantic search across all enabled MCP tools. Run `/mcp-adapter jev setup` in Pi when you want to restrict which enabled servers may share semantic-search data. The command saves a project-scoped allowlist and reloads Pi automatically. Verify the stored credential at any time with `pi-mcp-adapter key status systemone`.
632
640
 
633
641
  `SYSTEMONE_API_KEY` is for CI/headless use and overrides the keyring. Stdio MCP subprocesses inherit the host environment by default, so set `inheritEnv: false` where they must not receive it. The script worker receives no key, SDK, endpoint, headers, or environment.
634
642
 
@@ -705,7 +713,7 @@ The upstream tool executes before this check and may already have side effects.
705
713
 
706
714
  For a tool-restricted subagent, launch the child Pi with its tool allowlist set to `["mcpScript"]`. Have the parent discover MCP tool names with `mcp({ search: "..." })` and include the relevant prefixed names in the child's task; the child can then loop, filter, and chain those MCP calls without filesystem, shell, or edit tools. The adapter's ordinary lazy connection, authentication, abort handling, and approval gates still apply to every call.
707
715
 
708
- `mcpScript` is a trusted agent-authored MCP scripting layer, not an isolation boundary. If you need isolation, run Pi in an isolated environment. It is distinct from Pi's code-mode skill: Pi's skill batches general Pi tools, while `mcpScript` exposes MCP calls only and can be the child's sole tool.
716
+ `mcpScript` runs in an isolated QuickJS/WASM VM with a 64 MiB memory cap, a 16 MiB serialized output-block budget, and no Node.js, filesystem, network, timer, or process globals. Script error messages are capped at 64 KiB. MCP tool calls can still have external side effects and remain subject to the adapter's normal approval gates. It is distinct from Pi's code-mode skill: Pi's skill batches general Pi tools, while `mcpScript` exposes MCP calls only and can be the child's sole tool.
709
717
 
710
718
  ### MCP Prompts
711
719
 
@@ -714,7 +722,7 @@ MCP servers can advertise prompt templates alongside tools and resources. The ad
714
722
  ```text
715
723
  /mcp__agent_board__create_plan "harden retry policy"
716
724
  /mcp__agent_board__review_pipeline status=paused
717
- /mcp prompts
725
+ /mcp-adapter prompts
718
726
  ```
719
727
 
720
728
  Prompt results are flattened into one user message, preserving `[user]` and `[assistant]` role markers for multi-message results. Servers without the `prompts` capability are not probed.
@@ -821,11 +829,11 @@ To hide specific tools while still using `directTools: true`, add `excludeTools`
821
829
  }
822
830
  ```
823
831
 
824
- `includeTools` and `excludeTools` filter direct tools, proxy search/list/describe, and the `/mcp` panel view.
832
+ `includeTools` and `excludeTools` filter direct tools, proxy search/list/describe, and the `/mcp-adapter` panel view.
825
833
 
826
834
  Each direct tool costs ~150-300 tokens in the system prompt (name + description + schema). Good for targeted sets of 5-20 tools. For servers with 75+ tools, stick with the proxy or pick specific tools with a `string[]`. If 75+ direct tools resolve, the adapter prints an advisory but still registers the tools you configured. Set `settings.warnOnLargeDirectTools` to `false` to suppress this advisory.
827
835
 
828
- Direct tools register from the metadata cache in the Pi agent dir (`~/.pi/agent/mcp-cache.json` by default, or `$PI_CODING_AGENT_DIR/mcp-cache.json` when set), so no server connections are needed at startup. On the first session after adding `directTools` to a new server, the cache won't exist yet — tools fall back to proxy-only while the cache populates, then the extension hot-loads the refreshed direct tools into the current session. When `mcp({ connect: "<server>" })` is what discovers them, the connect result lists the new tools in `addedToolNames`, so Pi can load their definitions from that point in the transcript instead of rewriting the active tool list. Servers that advertise MCP list-change notifications refresh the current session when their tool or resource list changes. On Pi versions that expose `pi.unregisterTool()`, stale direct tools are removed from the registry during refresh; older Pi versions still deactivate them from the active tool set. To force a refresh: `/mcp reconnect <server>`.
836
+ Direct tools register from the metadata cache in the Pi agent dir (`~/.pi/agent/mcp-cache.json` by default, or `$PI_CODING_AGENT_DIR/mcp-cache.json` when set), so no server connections are needed at startup. On the first session after adding `directTools` to a new server, the cache won't exist yet — tools fall back to proxy-only while the cache populates, then the extension hot-loads the refreshed direct tools into the current session. When `mcp({ connect: "<server>" })` is what discovers them, the connect result lists the new tools in `addedToolNames`, so Pi can load their definitions from that point in the transcript instead of rewriting the active tool list. Servers that advertise MCP list-change notifications refresh the current session when their tool or resource list changes. On Pi versions that expose `pi.unregisterTool()`, stale direct tools are removed from the registry during refresh; older Pi versions still deactivate them from the active tool set. To force a refresh: `/mcp-adapter reconnect <server>`.
829
837
 
830
838
  For faster startup, set `settings.deferWithMissingMetadata` to `true`. Servers with missing or invalid metadata (expired, mismatched, or non-cacheable) then contribute no tools, prompts, resources, or search entries until the first MCP operation starts the runtime and loads live metadata; the `mcp` gateway stays available. Because Pi cannot unregister slash commands, cached prompt commands also wait for live metadata under this setting. `eager`/`keep-alive` servers and cold `MCP_DIRECT_TOOLS` selections still start immediately.
831
839
 
@@ -835,11 +843,11 @@ Set `settings.directToolResultDetails` to `"bounded"` when an extension needs st
835
843
 
836
844
  If prompt-cache stability matters more than direct-tool hot-loading, set `settings.freezeDirectTools` to `true`. The initial direct-tool sync still runs, but later metadata updates and explicit reconnects keep the registered tool surface unchanged while proxy/search/cache metadata refreshes normally.
837
845
 
838
- When you change direct-tool toggles in `/mcp`, the extension updates direct tool registration in the current session. Broader setup writes from `/mcp setup` still use Pi's normal reload flow because they can add or restructure MCP config files.
846
+ When you change direct-tool toggles in `/mcp-adapter`, the extension updates direct tool registration in the current session. Broader setup writes from `/mcp-adapter setup` still use Pi's normal reload flow because they can add or restructure MCP config files.
839
847
 
840
- **Interactive configuration:** Run `/mcp` to open an interactive panel showing all servers with connection status, tools, and direct/proxy toggles. You can reconnect servers, toggle tools between direct and proxy, and enable or disable servers (`ctrl+d`) from the same overlay. For OAuth, press Enter on a server that needs auth or `ctrl+a` on any OAuth server. The Save action defaults to `ctrl+s` and can be remapped with the `mcp.panel.save` keybinding.
848
+ **Interactive configuration:** Run `/mcp-adapter` to open an interactive panel showing all servers with connection status, tools, and direct/proxy toggles. `/mcp` is also available as an alias when Pi's built-in MCP extension is not installed. You can reconnect servers, toggle tools between direct and proxy, and enable or disable servers (`ctrl+d`) from the same overlay. For OAuth, press Enter on a server that needs auth or `ctrl+a` on any OAuth server. The Save action defaults to `ctrl+s` and can be remapped with the `mcp.panel.save` keybinding.
841
849
 
842
- **Guided first-run setup:** Run `/mcp setup` to choose the normal write target for new shared servers — project `.mcp.json` or global `~/.config/mcp/mcp.json` — inspect detected shared MCP files, adopt compatibility imports from other hosts, open discovered config paths, preview exact before/after file diffs for writes, scaffold a minimal selected config, add a curated known server (DeepWiki, Context7, Notion, GitHub, or Chrome DevTools), or quick-add RepoPrompt into a standard/shared MCP file.
850
+ **Guided first-run setup:** Run `/mcp-adapter setup` to choose the normal write target for new shared servers — project `.mcp.json` or global `~/.config/mcp/mcp.json` — inspect detected shared MCP files, adopt compatibility imports from other hosts, open discovered config paths, preview exact before/after file diffs for writes, scaffold a minimal selected config, add a curated known server (DeepWiki, Context7, Notion, GitHub, or Chrome DevTools), or quick-add RepoPrompt into a standard/shared MCP file.
843
851
 
844
852
  **Subagent integration:** If you use the subagent extension, agents can request direct MCP tools in their frontmatter with `mcp:server-name` syntax. See the subagent README for details.
845
853
 
@@ -921,7 +929,7 @@ Supported compatibility imports: `cursor`, `claude-code`, `claude-desktop`, `ope
921
929
 
922
930
  ### Project Config
923
931
 
924
- Prefer `.mcp.json` for project-local shared MCP config and `~/.config/mcp/mcp.json` for user-global shared MCP config. Use `.pi/mcp.json` only when you need a Pi-specific project override. Project files override both user-global shared MCP config and Pi global overrides.
932
+ Prefer `.mcp.json` for project-local shared MCP config and `~/.config/mcp/mcp.json` for user-global shared MCP config. Use `.pi/mcp-adapter.json` for adapter-specific project overrides. Pi `mcp.json` files are not adapter inputs; project files override user-global sources.
925
933
 
926
934
  ## Usage
927
935
 
@@ -979,24 +987,24 @@ Servers that provide usage guidance via the MCP `instructions` field surface it
979
987
 
980
988
  | Command | What it does |
981
989
  |---------|--------------|
982
- | `/mcp` | Interactive panel and first-run onboarding surface |
983
- | `/pi-mcp` | Alias for `/mcp` when the host reserves `/mcp` |
984
- | `/mcp setup` | Guided setup for imports, a minimal `.mcp.json`, curated known servers, RepoPrompt quick-add, and config-path inspection |
985
- | `/mcp jev setup` | Restrict which servers may share semantic-search data, save the project policy, and reload Pi |
986
- | `/mcp edit [project\|global]` | Open `.mcp.json` (default) or `~/.config/mcp/mcp.json` in an editor; Ctrl+G opens `$EDITOR`; saves a valid JSONC object and reloads |
987
- | `/mcp tools` | List all tools |
988
- | `/mcp prompts` | List all MCP prompts registered as slash commands |
989
- | `/mcp reconnect` | Reconnect all servers |
990
- | `/mcp reconnect <server>` | Connect or reconnect a single server |
991
- | `/mcp disable <server>` | Disable a server in the project-local `.pi/mcp.json` (requires `/reload` to apply) |
992
- | `/mcp enable <server>` | Enable through the project-local override layer (requires `/reload` to apply) |
993
- | `/mcp logout <server>` | Clear stored OAuth credentials for a server and disconnect it |
990
+ | `/mcp-adapter` | Interactive panel and first-run onboarding surface |
991
+ | `/mcp` | Alias for `/mcp-adapter` only when Pi's built-in MCP extension is not installed |
992
+ | `/mcp-adapter setup` | Guided setup for imports, a minimal `.mcp.json`, curated known servers, RepoPrompt quick-add, and config-path inspection |
993
+ | `/mcp-adapter jev setup` | Restrict which servers may share semantic-search data, save the project policy, and reload Pi |
994
+ | `/mcp-adapter edit [project\|global]` | Open `.mcp.json` (default) or `~/.config/mcp/mcp.json` in an editor; Ctrl+G opens `$EDITOR`; saves a valid JSONC object and reloads |
995
+ | `/mcp-adapter tools` | List all tools |
996
+ | `/mcp-adapter prompts` | List all MCP prompts registered as slash commands |
997
+ | `/mcp-adapter reconnect` | Reconnect all servers |
998
+ | `/mcp-adapter reconnect <server>` | Connect or reconnect a single server |
999
+ | `/mcp-adapter disable <server>` | Disable a server in the project-local `.pi/mcp-adapter.json` (requires `/reload` to apply) |
1000
+ | `/mcp-adapter enable <server>` | Enable through the project-local override layer (requires `/reload` to apply) |
1001
+ | `/mcp-adapter logout <server>` | Clear stored OAuth credentials for a server and disconnect it |
994
1002
  | `/mcp-auth` | Open an OAuth server picker in interactive UI sessions |
995
1003
  | `/mcp-auth <server>` | OAuth setup for a specific server |
996
1004
 
997
1005
  If `settings.autoAuth` is `true`, `mcp({ connect: ... })`, `mcp({ tool: ... })`, and direct tool calls automatically run OAuth when needed and retry once.
998
1006
 
999
- In interactive sessions, you can also authenticate from `/mcp` with `ctrl+a` or Enter on a server that needs auth. `/mcp-auth` without a server only opens a picker in the interactive UI. For gateway authorization and manual callback completion, see [Remote/headless OAuth](#remoteheadless-oauth).
1007
+ In interactive sessions, you can also authenticate from `/mcp-adapter` with `ctrl+a` or Enter on a server that needs auth. `/mcp-auth` without a server only opens a picker in the interactive UI. For gateway authorization and manual callback completion, see [Remote/headless OAuth](#remoteheadless-oauth).
1000
1008
 
1001
1009
  ### MCP output schemas
1002
1010
 
package/cli.js CHANGED
@@ -38,12 +38,12 @@ function getAgentDir() {
38
38
  }
39
39
 
40
40
  const AGENT_DIR = getAgentDir();
41
- const PI_CONFIG_PATH = path.join(AGENT_DIR, "mcp.json");
41
+ const PI_CONFIG_PATH = path.join(AGENT_DIR, "mcp-adapter.json");
42
42
  const GENERIC_GLOBAL_CONFIG_PATH = path.join(HOME, ".config", "mcp", "mcp.json");
43
43
  const AGENTS_GLOBAL_CONFIG_PATH = path.join(HOME, ".agents", "mcp.json");
44
44
  const AGENTS_NESTED_GLOBAL_CONFIG_PATH = path.join(HOME, ".agents", "mcp", "mcp.json");
45
45
  const PROJECT_CONFIG_PATH = path.resolve(process.cwd(), ".mcp.json");
46
- const PROJECT_PI_CONFIG_PATH = path.resolve(process.cwd(), getConfigDirName(), "mcp.json");
46
+ const PROJECT_PI_CONFIG_PATH = path.resolve(process.cwd(), getConfigDirName(), "mcp-adapter.json");
47
47
 
48
48
  const IMPORT_PATHS = {
49
49
  cursor: [path.join(HOME, ".cursor", "mcp.json")],
@@ -131,9 +131,9 @@ function printDiscovery(log, imports) {
131
131
  ["User-global standard MCP", GENERIC_GLOBAL_CONFIG_PATH],
132
132
  ["User-global .agents MCP", AGENTS_GLOBAL_CONFIG_PATH],
133
133
  ["User-global .agents nested MCP", AGENTS_NESTED_GLOBAL_CONFIG_PATH],
134
- ["Pi global override", PI_CONFIG_PATH],
134
+ ["MCP adapter global override", PI_CONFIG_PATH],
135
135
  ["Project standard MCP", PROJECT_CONFIG_PATH],
136
- ["Project Pi override", PROJECT_PI_CONFIG_PATH],
136
+ ["Project MCP adapter override", PROJECT_PI_CONFIG_PATH],
137
137
  ];
138
138
 
139
139
  for (const [label, filePath] of paths) {
@@ -171,8 +171,8 @@ async function runInit(argv, log = console.log) {
171
171
 
172
172
  const discoverySettingChanged = discoverHostConfigs && existingConfig.settings?.hostConfigDiscovery !== "on";
173
173
  if (importsToAdd.length === 0 && !discoverySettingChanged) {
174
- log("\nNo Pi config changes needed.");
175
- log("Standard MCP configs are discovered automatically, and host-specific imports are already configured or unavailable.");
174
+ log("\nNo MCP adapter config changes needed.");
175
+ log("Standard MCP configs are discovered automatically. Pi's mcp.json files are reserved for built-in MCP; adapter settings belong in mcp-adapter.json.");
176
176
  return 0;
177
177
  }
178
178
 
@@ -184,10 +184,10 @@ async function runInit(argv, log = console.log) {
184
184
  };
185
185
 
186
186
  if (importsToAdd.length > 0) {
187
- log(`\nDetected host configs to import into Pi: ${importsToAdd.join(", ")}`);
187
+ log(`\nDetected host configs to import into the MCP adapter: ${importsToAdd.join(", ")}`);
188
188
  }
189
189
  if (discoverySettingChanged) {
190
- log("Opting in to host-specific fallback discovery (standard and Pi-owned configs still take precedence).");
190
+ log("Opting in to host-specific fallback discovery (standard and adapter-owned configs still take precedence).");
191
191
  }
192
192
 
193
193
  if (dryRun) {
@@ -197,7 +197,7 @@ async function runInit(argv, log = console.log) {
197
197
 
198
198
  writePiConfig(nextConfig);
199
199
  log(`Updated ${PI_CONFIG_PATH}`);
200
- log("Pi will now keep reading standard MCP configs automatically, while these imports cover host-specific config formats.");
200
+ log("The adapter reads standard MCP configs automatically and stores adapter-specific imports in mcp-adapter.json; Pi's mcp.json is never read by the adapter.");
201
201
  if (discoverySettingChanged) {
202
202
  log("Host config discovery is explicit and does not write to or execute commands from external host files.");
203
203
  }
package/commands.ts CHANGED
@@ -32,6 +32,7 @@ import { loadOnboardingState, markSetupCompleted as persistSetupCompleted, markS
32
32
  import { formatTerminalError, openPath, resolveServerUrl, sanitizeTerminalText } from "./utils.ts";
33
33
  import { isAbortError } from "./runtime-owner.ts";
34
34
  import { resolveJevCredential } from "./jev-key-store.ts";
35
+ import { describeProjectServerBlock } from "./project-server-trust.ts";
35
36
 
36
37
  function terminalHyperlink(label: string, url: string): string {
37
38
  return `\u001B]8;;${sanitizeTerminalText(url)}\u001B\\${sanitizeTerminalText(label)}\u001B]8;;\u001B\\`;
@@ -75,7 +76,7 @@ export async function setupJevSemanticSearch(
75
76
  if (credential.status !== "present") {
76
77
  const detail = credential.status === "unavailable" ? ` ${credential.message}` : "";
77
78
  ctx.ui.notify(
78
- `Jev needs a System One API key.${detail}\nRun \`pi-mcp-adapter key set systemone\` in a terminal, then run \`/mcp jev setup\` again.`,
79
+ `Jev needs a System One API key.${detail}\nRun \`pi-mcp-adapter key set systemone\` in a terminal, then run \`/mcp-adapter jev setup\` again.`,
79
80
  "error",
80
81
  );
81
82
  return false;
@@ -141,18 +142,26 @@ export async function showStatus(state: McpExtensionState, ctx: ExtensionContext
141
142
  if (!ctx.hasUI) return;
142
143
 
143
144
  const lines: string[] = ["MCP Server Status:", ""];
145
+ if (state.migrationNotices?.length) {
146
+ lines.push(...state.migrationNotices.map((notice) => `⚠ ${notice}`), "");
147
+ }
144
148
  if (!state.programmaticConfig) {
145
149
  lines.push(
146
150
  "Shared MCP config: .mcp.json for this project/team or ~/.config/mcp/mcp.json for all projects.",
147
- "Pi-owned files hold compatibility imports and adapter-specific overrides.",
151
+ "mcp-adapter.json files hold compatibility imports and adapter-specific overrides; Pi's mcp.json files are reserved for Pi and ignored.",
148
152
  "",
149
153
  );
150
154
  }
151
155
 
152
156
  for (const name of Object.keys(state.config.mcpServers)) {
153
157
  const definition = state.config.mcpServers[name];
158
+ const block = state.blockedProjectServers?.get(name);
159
+ if (block) {
160
+ lines.push(`⊘ ${name}: ${describeProjectServerBlock(block.reason)}`);
161
+ continue;
162
+ }
154
163
  if (isServerDisabled(definition)) {
155
- lines.push(`⊘ ${name}: disabled (run /mcp enable ${name}, then /reload)`);
164
+ lines.push(`⊘ ${name}: disabled (run /mcp-adapter enable ${name}, then /reload)`);
156
165
  continue;
157
166
  }
158
167
  const connection = state.manager.getConnection(name);
@@ -198,7 +207,7 @@ export async function showStatus(state: McpExtensionState, ctx: ExtensionContext
198
207
 
199
208
  if (Object.keys(state.config.mcpServers).length === 0) {
200
209
  lines.push("No MCP servers configured");
201
- lines.push("Run /mcp setup to add a server to .mcp.json or ~/.config/mcp/mcp.json");
210
+ lines.push("Run /mcp-adapter setup to add a server to .mcp.json or ~/.config/mcp/mcp.json");
202
211
  }
203
212
 
204
213
  ctx.ui.notify(lines.join("\n"), "info");
@@ -280,7 +289,7 @@ export async function reconnectServer(
280
289
  return false;
281
290
  }
282
291
  if (isServerDisabled(definition)) {
283
- if (ui) ui.notify(`MCP: ${name} is disabled. Run /mcp enable ${name}, then /reload.`, "warning");
292
+ if (ui) ui.notify(`MCP: ${name} is disabled. Run /mcp-adapter enable ${name}, then /reload.`, "warning");
284
293
  return false;
285
294
  }
286
295
 
@@ -378,7 +387,7 @@ export async function authenticateServer(
378
387
  return { ok: false, message };
379
388
  }
380
389
  if (isServerDisabled(definition)) {
381
- const message = `Server "${serverName}" is disabled. Run /mcp enable ${serverName}, then /reload.`;
390
+ const message = `Server "${serverName}" is disabled. Run /mcp-adapter enable ${serverName}, then /reload.`;
382
391
  ui.notify(message, "warning");
383
392
  return { ok: false, message };
384
393
  }
@@ -492,7 +501,7 @@ function validateBearerTokenStoreServer(
492
501
  const safeName = sanitizeTerminalText(serverName);
493
502
  const definition = state.config.mcpServers[serverName];
494
503
  if (!definition) return { ok: false, message: `Server "${safeName}" not found in config`, type: "error" };
495
- if (isServerDisabled(definition)) return { ok: false, message: `Server "${safeName}" is disabled. Run /mcp enable ${safeName}, then /reload.`, type: "warning" };
504
+ if (isServerDisabled(definition)) return { ok: false, message: `Server "${safeName}" is disabled. Run /mcp-adapter enable ${safeName}, then /reload.`, type: "warning" };
496
505
  if (definition.auth !== "bearer" || definition.bearerTokenStore !== true) {
497
506
  return { ok: false, message: `Server "${safeName}" is not configured for bearerTokenStore.`, type: "error" };
498
507
  }
@@ -572,7 +581,7 @@ function buildSharedConfigNoticeLines(configOverridePath: string | undefined, cw
572
581
  return {
573
582
  lines: [
574
583
  `Using standard MCP config from ${sourceList}.`,
575
- "Use .mcp.json for project/team config or ~/.config/mcp/mcp.json for all projects. Pi only writes compatibility imports and adapter-specific overrides into Pi-owned files when needed.",
584
+ "Use .mcp.json for project/team config or ~/.config/mcp/mcp.json for all projects. The adapter writes compatibility imports and adapter-specific overrides into mcp-adapter.json files when needed.",
576
585
  ],
577
586
  fingerprint: discovery.fingerprint,
578
587
  };
@@ -588,7 +597,7 @@ export async function openMcpSetup(
588
597
  ): Promise<PanelFlowResult> {
589
598
  if (!ctx.hasUI) return { configChanged: false };
590
599
  if (!canRenderPanel(ctx)) {
591
- ctx.ui.notify(`The interactive MCP setup panel is only available in the terminal UI (current mode: ${ctx.mode}). Edit .mcp.json directly, or run /mcp status to review servers.`, "info");
600
+ ctx.ui.notify(`The interactive MCP setup panel is only available in the terminal UI (current mode: ${ctx.mode}). Edit .mcp.json directly, or run /mcp-adapter status to review servers.`, "info");
592
601
  return { configChanged: false };
593
602
  }
594
603
  if (state.programmaticConfig) {
@@ -685,6 +694,7 @@ function buildMcpPanelCallbacks(
685
694
  getConnectionStatus: (serverName: string) => {
686
695
  authStatusFailures.delete(serverName);
687
696
  const definition = config.mcpServers[serverName];
697
+ if (state.blockedProjectServers?.has(serverName)) return "blocked";
688
698
  if (isServerDisabled(definition)) return "disabled";
689
699
  const connection = state.manager.getConnection(serverName);
690
700
  let serverUrl: string | undefined;
@@ -713,7 +723,8 @@ function buildMcpPanelCallbacks(
713
723
  if (getFailureAgeSeconds(state, serverName) !== null) return "failed";
714
724
  return "idle";
715
725
  },
716
- getFailureMessage: (serverName: string) => authStatusFailures.get(serverName) ?? getFailureMessage(state, serverName),
726
+ getFailureMessage: (serverName: string) => state.blockedProjectServers?.get(serverName)?.reason
727
+ ?? authStatusFailures.get(serverName) ?? getFailureMessage(state, serverName),
717
728
  refreshCacheAfterReconnect: (serverName: string) => {
718
729
  const freshCache = loadMetadataCache();
719
730
  return freshCache?.servers?.[serverName] ?? null;