@herbertgao/pi-extensions 2026.9.17 → 2026.9.19
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/README.md +31 -33
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/node_modules/@herbertgao/pi-bark/package.json +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +4 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +4 -1
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +21 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +15 -4
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +43 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +7 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +75 -75
- package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
- package/node_modules/@herbertgao/pi-subagents/package.json +7 -7
- package/node_modules/@herbertgao/resume-from/package.json +4 -2
- package/node_modules/@herbertgao/sol-pi/package.json +6 -6
- package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +99 -99
- package/node_modules/@pi-plugins/fast-mode/package.json +1 -1
- package/node_modules/@tifan/pi-handoff/package.json +1 -1
- package/node_modules/@tifan/pi-handoff/src/index.ts +56 -26
- package/node_modules/@tifan/pi-mermaid-open/package.json +1 -1
- package/node_modules/@tifan/pi-preferred-thinking/package.json +1 -1
- package/node_modules/@tifan/pi-recap/package.json +1 -1
- package/node_modules/@tifan/pi-rename/package.json +1 -1
- package/node_modules/pi-antigravity/README.md +29 -6
- package/node_modules/pi-antigravity/package.json +14 -9
- package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
- package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
- package/node_modules/pi-antigravity/src/client/client.ts +1 -1
- package/node_modules/pi-antigravity/src/image/image.ts +4 -4
- package/node_modules/pi-antigravity/src/index.ts +203 -13
- package/node_modules/pi-antigravity/src/search/index.ts +1 -0
- package/node_modules/pi-antigravity/src/search/search.ts +325 -0
- package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
- package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
- package/node_modules/pi-antigravity/src/types/types.ts +5 -12
- package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
- package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
- package/node_modules/pi-lens/package.json +158 -158
- package/node_modules/pi-multi-account/package.json +2 -2
- package/node_modules/pi-typesafe/README.md +3 -1
- package/node_modules/pi-typesafe/dist/auth.d.ts +9 -5
- package/node_modules/pi-typesafe/dist/auth.js +7 -3
- package/node_modules/pi-typesafe/dist/backends.d.ts +33 -3
- package/node_modules/pi-typesafe/dist/backends.js +95 -1
- package/node_modules/pi-typesafe/dist/client.d.ts +5 -5
- package/node_modules/pi-typesafe/dist/client.js +12 -9
- package/node_modules/pi-typesafe/dist/credentials.d.ts +14 -8
- package/node_modules/pi-typesafe/dist/credentials.js +10 -7
- package/node_modules/pi-typesafe/dist/errors.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/index.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/index.js +1 -1
- package/node_modules/pi-typesafe/dist/login.d.ts +2 -2
- package/node_modules/pi-typesafe/dist/login.js +2 -2
- package/node_modules/pi-typesafe/package.json +6 -5
- package/node_modules/pi-web-access/CHANGELOG.md +72 -0
- package/node_modules/pi-web-access/README.md +34 -13
- package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
- package/node_modules/pi-web-access/curator-page.ts +5 -1
- package/node_modules/pi-web-access/curator-server.ts +2 -0
- package/node_modules/pi-web-access/dist/index.js +1337 -1009
- package/node_modules/pi-web-access/exa.ts +20 -52
- package/node_modules/pi-web-access/extract.ts +41 -4
- package/node_modules/pi-web-access/gemini-search.ts +12 -4
- package/node_modules/pi-web-access/index.ts +29 -37
- package/node_modules/pi-web-access/openai-search.ts +35 -7
- package/node_modules/pi-web-access/package.json +8 -7
- package/node_modules/pi-web-access/page-query.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +2 -3
- package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
- package/node_modules/pi-web-access/storage.ts +48 -77
- package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
- package/node_modules/pi-web-access/summary-review.ts +3 -4
- package/node_modules/pi-web-access/tavily.ts +67 -20
- package/node_modules/pi-web-access/tool-activation.ts +53 -33
- package/node_modules/pi-web-access/utils.ts +1 -1
- package/node_modules/pi-web-access/you.ts +164 -0
- package/node_modules/pi-web-access/zai.ts +161 -0
- package/node_modules/remote-pi/package.json +5 -1
- package/package.json +19 -31
- package/node_modules/pi-mcp-adapter/CHANGELOG.md +0 -1013
- package/node_modules/pi-mcp-adapter/LICENSE +0 -21
- package/node_modules/pi-mcp-adapter/OAUTH.md +0 -369
- package/node_modules/pi-mcp-adapter/README.md +0 -1021
- package/node_modules/pi-mcp-adapter/abort.ts +0 -34
- package/node_modules/pi-mcp-adapter/agent-dir.ts +0 -68
- package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +0 -416
- package/node_modules/pi-mcp-adapter/agent-plugin-provenance.ts +0 -36
- package/node_modules/pi-mcp-adapter/app-bridge.bundle.js +0 -67
- package/node_modules/pi-mcp-adapter/banner.png +0 -0
- package/node_modules/pi-mcp-adapter/bearer-command-resolver.ts +0 -193
- package/node_modules/pi-mcp-adapter/claude-plugin-loader.ts +0 -365
- package/node_modules/pi-mcp-adapter/cli.js +0 -454
- package/node_modules/pi-mcp-adapter/commands.ts +0 -861
- package/node_modules/pi-mcp-adapter/config.ts +0 -1565
- package/node_modules/pi-mcp-adapter/consent-manager.ts +0 -95
- package/node_modules/pi-mcp-adapter/direct-tool-surface.ts +0 -244
- package/node_modules/pi-mcp-adapter/direct-tools.ts +0 -428
- package/node_modules/pi-mcp-adapter/dist/abort.d.ts +0 -2
- package/node_modules/pi-mcp-adapter/dist/abort.js +0 -36
- package/node_modules/pi-mcp-adapter/dist/abort.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-dir.d.ts +0 -10
- package/node_modules/pi-mcp-adapter/dist/agent-dir.js +0 -63
- package/node_modules/pi-mcp-adapter/dist/agent-dir.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.d.ts +0 -8
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js +0 -398
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-loader.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-provenance.d.ts +0 -1
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-provenance.js +0 -31
- package/node_modules/pi-mcp-adapter/dist/agent-plugin-provenance.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js +0 -194
- package/node_modules/pi-mcp-adapter/dist/bearer-command-resolver.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/claude-plugin-loader.d.ts +0 -9
- package/node_modules/pi-mcp-adapter/dist/claude-plugin-loader.js +0 -362
- package/node_modules/pi-mcp-adapter/dist/claude-plugin-loader.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/config.d.ts +0 -134
- package/node_modules/pi-mcp-adapter/dist/config.js +0 -1361
- package/node_modules/pi-mcp-adapter/dist/config.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/consent-manager.d.ts +0 -18
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js +0 -88
- package/node_modules/pi-mcp-adapter/dist/consent-manager.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.d.ts +0 -17
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js +0 -316
- package/node_modules/pi-mcp-adapter/dist/elicitation-handler.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/errors.d.ts +0 -131
- package/node_modules/pi-mcp-adapter/dist/errors.js +0 -278
- package/node_modules/pi-mcp-adapter/dist/errors.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/http-ca.d.ts +0 -8
- package/node_modules/pi-mcp-adapter/dist/http-ca.js +0 -88
- package/node_modules/pi-mcp-adapter/dist/http-ca.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/jev-client.d.ts +0 -14
- package/node_modules/pi-mcp-adapter/dist/jev-client.js +0 -367
- package/node_modules/pi-mcp-adapter/dist/jev-client.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.d.ts +0 -77
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js +0 -2
- package/node_modules/pi-mcp-adapter/dist/jev-contracts.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.d.ts +0 -51
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js +0 -205
- package/node_modules/pi-mcp-adapter/dist/jev-key-store.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.d.ts +0 -2
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js +0 -68
- package/node_modules/pi-mcp-adapter/dist/json-schema-validator.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/lifecycle.d.ts +0 -56
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js +0 -410
- package/node_modules/pi-mcp-adapter/dist/lifecycle.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/logger.d.ts +0 -51
- package/node_modules/pi-mcp-adapter/dist/logger.js +0 -131
- package/node_modules/pi-mcp-adapter/dist/logger.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.d.ts +0 -26
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js +0 -103
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-fetch.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.d.ts +0 -118
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js +0 -1097
- package/node_modules/pi-mcp-adapter/dist/mcp-auth-flow.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.d.ts +0 -170
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js +0 -1142
- package/node_modules/pi-mcp-adapter/dist/mcp-auth.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.d.ts +0 -24
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js +0 -203
- package/node_modules/pi-mcp-adapter/dist/mcp-bearer-store.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.d.ts +0 -53
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js +0 -441
- package/node_modules/pi-mcp-adapter/dist/mcp-callback-server.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.d.ts +0 -148
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js +0 -689
- package/node_modules/pi-mcp-adapter/dist/mcp-oauth-provider.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js +0 -166
- package/node_modules/pi-mcp-adapter/dist/mcp-probe.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.d.ts +0 -102
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js +0 -369
- package/node_modules/pi-mcp-adapter/dist/mcp-tasks.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.d.ts +0 -95
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js +0 -242
- package/node_modules/pi-mcp-adapter/dist/mcp-trace.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.d.ts +0 -19
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js +0 -301
- package/node_modules/pi-mcp-adapter/dist/metadata-cache.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js +0 -505
- package/node_modules/pi-mcp-adapter/dist/npx-resolver.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.d.ts +0 -2
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js +0 -144
- package/node_modules/pi-mcp-adapter/dist/package-mcp-loader.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.d.ts +0 -10
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js +0 -312
- package/node_modules/pi-mcp-adapter/dist/request-headers-command.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/resource-tools.d.ts +0 -1
- package/node_modules/pi-mcp-adapter/dist/resource-tools.js +0 -15
- package/node_modules/pi-mcp-adapter/dist/resource-tools.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.d.ts +0 -13
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js +0 -97
- package/node_modules/pi-mcp-adapter/dist/runtime-owner.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.d.ts +0 -17
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js +0 -203
- package/node_modules/pi-mcp-adapter/dist/sampling-handler.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.d.ts +0 -11
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js +0 -172
- package/node_modules/pi-mcp-adapter/dist/secure-keyring.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/server-manager.d.ts +0 -157
- package/node_modules/pi-mcp-adapter/dist/server-manager.js +0 -1745
- package/node_modules/pi-mcp-adapter/dist/server-manager.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/session-approvals.d.ts +0 -34
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js +0 -140
- package/node_modules/pi-mcp-adapter/dist/session-approvals.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/session-recovery.d.ts +0 -26
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js +0 -142
- package/node_modules/pi-mcp-adapter/dist/session-recovery.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/state.d.ts +0 -73
- package/node_modules/pi-mcp-adapter/dist/state.js +0 -2
- package/node_modules/pi-mcp-adapter/dist/state.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/types.d.ts +0 -642
- package/node_modules/pi-mcp-adapter/dist/types.js +0 -331
- package/node_modules/pi-mcp-adapter/dist/types.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.d.ts +0 -6
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js +0 -37
- package/node_modules/pi-mcp-adapter/dist/ui-app-bridge-helpers.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.d.ts +0 -17
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js +0 -219
- package/node_modules/pi-mcp-adapter/dist/ui-resource-handler.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.d.ts +0 -104
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js +0 -59
- package/node_modules/pi-mcp-adapter/dist/ui-stream-types.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.d.ts +0 -4
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js +0 -27
- package/node_modules/pi-mcp-adapter/dist/ui-tool-visibility.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.d.ts +0 -15
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js +0 -85
- package/node_modules/pi-mcp-adapter/dist/unix-socket-transport.js.map +0 -1
- package/node_modules/pi-mcp-adapter/dist/utils.d.ts +0 -41
- package/node_modules/pi-mcp-adapter/dist/utils.js +0 -417
- package/node_modules/pi-mcp-adapter/dist/utils.js.map +0 -1
- package/node_modules/pi-mcp-adapter/elicitation-handler.ts +0 -358
- package/node_modules/pi-mcp-adapter/error-signal.ts +0 -32
- package/node_modules/pi-mcp-adapter/errors.ts +0 -364
- package/node_modules/pi-mcp-adapter/examples/jev-accessibility-loop.mjs +0 -85
- package/node_modules/pi-mcp-adapter/examples/jev-semantic-filter.mjs +0 -34
- package/node_modules/pi-mcp-adapter/failure-backoff.ts +0 -23
- package/node_modules/pi-mcp-adapter/glimpse-ui.ts +0 -80
- package/node_modules/pi-mcp-adapter/host-html-template.ts +0 -515
- package/node_modules/pi-mcp-adapter/http-ca.ts +0 -84
- package/node_modules/pi-mcp-adapter/index.ts +0 -2106
- package/node_modules/pi-mcp-adapter/init.ts +0 -721
- package/node_modules/pi-mcp-adapter/jev-client.ts +0 -300
- package/node_modules/pi-mcp-adapter/jev-contracts.ts +0 -53
- package/node_modules/pi-mcp-adapter/jev-key-store.ts +0 -192
- package/node_modules/pi-mcp-adapter/json-schema-validator.ts +0 -83
- package/node_modules/pi-mcp-adapter/lazy-loader.ts +0 -7
- package/node_modules/pi-mcp-adapter/lifecycle.ts +0 -457
- package/node_modules/pi-mcp-adapter/logger.ts +0 -169
- package/node_modules/pi-mcp-adapter/mcp-auth-fetch.ts +0 -113
- package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +0 -1296
- package/node_modules/pi-mcp-adapter/mcp-auth.ts +0 -1287
- package/node_modules/pi-mcp-adapter/mcp-bearer-store.ts +0 -250
- package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +0 -534
- package/node_modules/pi-mcp-adapter/mcp-code.ts +0 -495
- package/node_modules/pi-mcp-adapter/mcp-install.ts +0 -60
- package/node_modules/pi-mcp-adapter/mcp-keyring-helper.cjs +0 -89
- package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +0 -819
- package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +0 -511
- package/node_modules/pi-mcp-adapter/mcp-panel-theme.ts +0 -104
- package/node_modules/pi-mcp-adapter/mcp-panel.ts +0 -1099
- package/node_modules/pi-mcp-adapter/mcp-probe.ts +0 -201
- package/node_modules/pi-mcp-adapter/mcp-references.ts +0 -373
- package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +0 -137
- package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +0 -747
- package/node_modules/pi-mcp-adapter/mcp-status.ts +0 -105
- package/node_modules/pi-mcp-adapter/mcp-tasks.ts +0 -467
- package/node_modules/pi-mcp-adapter/mcp-trace.ts +0 -306
- package/node_modules/pi-mcp-adapter/metadata-cache.ts +0 -362
- package/node_modules/pi-mcp-adapter/namespace-tools.ts +0 -334
- package/node_modules/pi-mcp-adapter/npx-resolver.ts +0 -543
- package/node_modules/pi-mcp-adapter/oauth-handler.ts +0 -30
- package/node_modules/pi-mcp-adapter/oauth.ts +0 -52
- package/node_modules/pi-mcp-adapter/onboarding-state.ts +0 -76
- package/node_modules/pi-mcp-adapter/package-mcp-loader.ts +0 -156
- package/node_modules/pi-mcp-adapter/package.json +0 -219
- package/node_modules/pi-mcp-adapter/panel-keys.ts +0 -53
- package/node_modules/pi-mcp-adapter/prompts.ts +0 -385
- package/node_modules/pi-mcp-adapter/proxy-modes.ts +0 -1735
- package/node_modules/pi-mcp-adapter/request-headers-command.ts +0 -333
- package/node_modules/pi-mcp-adapter/resource-tools.ts +0 -17
- package/node_modules/pi-mcp-adapter/runtime-owner.ts +0 -106
- package/node_modules/pi-mcp-adapter/sampling-handler.ts +0 -261
- package/node_modules/pi-mcp-adapter/sandbox-proxy-template.ts +0 -144
- package/node_modules/pi-mcp-adapter/search-ranking.ts +0 -309
- package/node_modules/pi-mcp-adapter/secure-keyring.ts +0 -170
- package/node_modules/pi-mcp-adapter/semantic-search.ts +0 -186
- package/node_modules/pi-mcp-adapter/server-manager.ts +0 -2088
- package/node_modules/pi-mcp-adapter/session-approvals.ts +0 -201
- package/node_modules/pi-mcp-adapter/session-recovery.ts +0 -158
- package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +0 -49
- package/node_modules/pi-mcp-adapter/state.ts +0 -71
- package/node_modules/pi-mcp-adapter/tool-approval.ts +0 -205
- package/node_modules/pi-mcp-adapter/tool-metadata.ts +0 -302
- package/node_modules/pi-mcp-adapter/tool-registrar.ts +0 -255
- package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +0 -573
- package/node_modules/pi-mcp-adapter/ts-shape.ts +0 -149
- package/node_modules/pi-mcp-adapter/types.ts +0 -1120
- package/node_modules/pi-mcp-adapter/ui-app-bridge-helpers.ts +0 -40
- package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +0 -266
- package/node_modules/pi-mcp-adapter/ui-server.ts +0 -993
- package/node_modules/pi-mcp-adapter/ui-session.ts +0 -624
- package/node_modules/pi-mcp-adapter/ui-stream-types.ts +0 -89
- package/node_modules/pi-mcp-adapter/ui-tool-visibility.ts +0 -25
- package/node_modules/pi-mcp-adapter/unix-socket-transport.ts +0 -85
- package/node_modules/pi-mcp-adapter/utils.ts +0 -464
|
@@ -1,1021 +0,0 @@
|
|
|
1
|
-
<p>
|
|
2
|
-
<img src="banner.png" alt="pi-mcp-adapter" width="1100">
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
# Pi MCP Adapter
|
|
6
|
-
|
|
7
|
-
Use MCP servers with [Pi](https://github.com/badlogic/pi-mono/) without burning your context window.
|
|
8
|
-
|
|
9
|
-
https://github.com/user-attachments/assets/4b7c66ff-e27e-4639-b195-22c3db406a5a
|
|
10
|
-
|
|
11
|
-
## Why This Exists
|
|
12
|
-
|
|
13
|
-
Mario wrote about [why you might not need MCP](https://mariozechner.at/posts/2025-11-02-what-if-you-dont-need-mcp/). The problem: tool definitions are verbose. A single MCP server can burn 10k+ tokens, and you're paying that cost whether you use those tools or not. Connect a few servers and you've burned half your context window before the conversation starts.
|
|
14
|
-
|
|
15
|
-
His take: skip MCP entirely, write simple CLI tools instead.
|
|
16
|
-
|
|
17
|
-
But the MCP ecosystem has useful stuff - databases, browsers, APIs. This adapter gives you access without the bloat. One proxy tool (~200 tokens) instead of hundreds. The agent discovers what it needs on-demand. Servers only start when you actually use them.
|
|
18
|
-
|
|
19
|
-
## Install
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
pi install npm:pi-mcp-adapter
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
Restart Pi after installation.
|
|
26
|
-
|
|
27
|
-
> **DeepSeek Harness (third-party bridge):** Run the unmodified adapter in DSH via [pi2dsh](https://github.com/weijiafu14/pi2dsh); see the [verified dsh-TUI and Web MCP guide](https://github.com/weijiafu14/pi2dsh/tree/main/examples/tui-mcp).
|
|
28
|
-
|
|
29
|
-
## What happens on first run
|
|
30
|
-
|
|
31
|
-
The adapter reads standard MCP files automatically. No extra setup needed if you already have them.
|
|
32
|
-
|
|
33
|
-
| You already have... | What happens |
|
|
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. |
|
|
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).
|
|
40
|
-
|
|
41
|
-
## Quick Start
|
|
42
|
-
|
|
43
|
-
Preferred project config: `.mcp.json`
|
|
44
|
-
|
|
45
|
-
```json
|
|
46
|
-
{
|
|
47
|
-
"mcpServers": {
|
|
48
|
-
"chrome-devtools": {
|
|
49
|
-
"command": "npx",
|
|
50
|
-
"args": ["-y", "chrome-devtools-mcp@1.6.0"]
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
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
|
-
|
|
58
|
-
Pi-owned files are not additional normal setup choices. They hold Pi-specific settings, compatibility imports, and adapter-only overrides:
|
|
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.
|
|
64
|
-
|
|
65
|
-
Precedence is (later entries win):
|
|
66
|
-
|
|
67
|
-
1. `~/.config/mcp/mcp.json`
|
|
68
|
-
2. `~/.agents/mcp.json`
|
|
69
|
-
3. `~/.agents/mcp/mcp.json`
|
|
70
|
-
4. `<Pi agent dir>/mcp.json`
|
|
71
|
-
5. `.mcp.json`
|
|
72
|
-
6. `.pi/mcp.json`
|
|
73
|
-
|
|
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.
|
|
75
|
-
|
|
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.
|
|
77
|
-
|
|
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.
|
|
79
|
-
|
|
80
|
-
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
|
-
|
|
82
|
-
```
|
|
83
|
-
mcp({ search: "screenshot" })
|
|
84
|
-
```
|
|
85
|
-
```
|
|
86
|
-
chrome_devtools_take_screenshot
|
|
87
|
-
Take a screenshot of the page or element.
|
|
88
|
-
|
|
89
|
-
Parameters:
|
|
90
|
-
format (enum: "png", "jpeg", "webp") [default: "png"]
|
|
91
|
-
fullPage (boolean) - Full page instead of viewport
|
|
92
|
-
```
|
|
93
|
-
```
|
|
94
|
-
mcp({ tool: "chrome_devtools_take_screenshot", args: { format: "png" } })
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
`args` can be a JSON object or a JSON string. Prefer the object form when your model handles it reliably; the string form remains supported for providers that need simpler schemas.
|
|
98
|
-
|
|
99
|
-
Two calls instead of 26 tools cluttering the context.
|
|
100
|
-
|
|
101
|
-
## Config
|
|
102
|
-
|
|
103
|
-
### File Layout
|
|
104
|
-
|
|
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.
|
|
106
|
-
|
|
107
|
-
| File | Purpose |
|
|
108
|
-
|------|---------|
|
|
109
|
-
| `~/.config/mcp/mcp.json` | User-global shared MCP config |
|
|
110
|
-
| `~/.agents/mcp.json` | User-global tool-agnostic MCP config |
|
|
111
|
-
| `~/.agents/mcp/mcp.json` | User-global tool-agnostic MCP config |
|
|
112
|
-
| `.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 |
|
|
115
|
-
|
|
116
|
-
For local stdio servers, a leading `~/` is expanded to the current user's home
|
|
117
|
-
directory in `command`, `args`, and `cwd`. On Windows, the equivalent `~\\`
|
|
118
|
-
form is supported too; on POSIX, backslashes remain literal filename
|
|
119
|
-
characters. Bare commands such as
|
|
120
|
-
`node`, `bunx`, or `git` continue to resolve through `PATH`.
|
|
121
|
-
Built-in Agent Plugin arguments remain literal; this path expansion applies to native and shared MCP configuration.
|
|
122
|
-
|
|
123
|
-
Pi-specific files are the write targets for imported or shared global servers when Pi needs to persist adapter-only settings such as `directTools`.
|
|
124
|
-
|
|
125
|
-
### Agent Plugins
|
|
126
|
-
|
|
127
|
-
The adapter can load MCP servers from [Agent Plugins](https://agent-plugins.org/) packages when you list plugin directories in `settings.agentPluginPaths`:
|
|
128
|
-
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"settings": {
|
|
132
|
-
"agentPluginPaths": ["./plugins/acme-tools"]
|
|
133
|
-
},
|
|
134
|
-
"mcpServers": {}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
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
|
-
|
|
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:
|
|
141
|
-
|
|
142
|
-
```json
|
|
143
|
-
{
|
|
144
|
-
"mcpServers": {
|
|
145
|
-
"acme_tools__local": { "inheritEnv": false }
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Agent Plugins is a portable package format. Native Pi MCP config remains `.mcp.json`, `~/.config/mcp/mcp.json`, and Pi-owned overrides.
|
|
151
|
-
|
|
152
|
-
### Local Claude plugin bundles
|
|
153
|
-
|
|
154
|
-
The adapter can opt into MCP servers and Pi skills from explicitly configured local [Claude plugin](https://docs.anthropic.com/en/docs/claude-code/plugins) directories:
|
|
155
|
-
|
|
156
|
-
```json
|
|
157
|
-
{
|
|
158
|
-
"claudePlugins": [
|
|
159
|
-
{ "path": "./plugins/acme-tools", "mcp": true, "skills": true }
|
|
160
|
-
],
|
|
161
|
-
"mcpServers": {}
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
Each entry needs a non-empty `path` and must enable `mcp`, `skills`, or both. The root-level field can be set in any normal adapter config source; normal config-source precedence applies, and a higher-precedence `claudePlugins` array replaces a lower one. Relative paths in file-based config resolve from the active project cwd. For `createMcpAdapter({ config })`, relative plugin paths are normalized against `process.cwd()` when the factory is created; this explicit API-boundary snapshot keeps early registration and session startup on the same local bundle even when the host's context cwd differs. `mcp: true` reads only the plugin's root `.mcp.json`; `skills: true` discovers `skills/**/SKILL.md` inside the plugin and passes those files through Pi's normal resource discovery, including startup and `/reload`. A `.claude-plugin/plugin.json` manifest is optional, matching Claude's plugin format, but when present it must be valid JSON with a kebab-case `name` and valid standard field types. Manifest path overrides are intentionally not followed.
|
|
166
|
-
|
|
167
|
-
Claude plugin MCP server names are used as written. The first explicitly listed plugin wins same-name conflicts between plugin bundles, while every normal Pi MCP config source overrides these plugin defaults. `${CLAUDE_PLUGIN_ROOT}` is expanded in plugin MCP server fields, and stdio servers receive it in their environment. Skills use Pi's existing skill parsing and conflict handling.
|
|
168
|
-
|
|
169
|
-
This is an explicit local trust boundary: the adapter does not discover, download, install, or update plugins; execute plugin hooks; or fetch skills from MCP instructions. It resolves plugin components inside each configured directory and rejects component symlinks that escape it. Config and skills are read during discovery, but MCP commands are still lazy and run only when normal adapter lifecycle/tool use connects that server. Enable `mcp` only for plugin directories whose commands and configuration you trust.
|
|
170
|
-
|
|
171
|
-
### Pi package manifests
|
|
172
|
-
|
|
173
|
-
A Pi package can ship MCP servers for the installed adapter without requiring a separate MCP config file. Declare a package-relative config in its `package.json`:
|
|
174
|
-
|
|
175
|
-
```json
|
|
176
|
-
{
|
|
177
|
-
"pi": {
|
|
178
|
-
"mcp": "./mcp.json"
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
`pi.mcp` can also be an array of package-relative paths. Each file uses the normal `mcpServers` object shape, but package manifests load only server entries: package `settings` and `imports` are ignored. Server names are prefixed with the sanitized package name, such as `acme_tools__docs`, and user/global/project MCP config has higher precedence. The adapter loads only Pi packages listed in Pi settings; it does not scan `node_modules`.
|
|
184
|
-
|
|
185
|
-
### Runtime registration from other extensions
|
|
186
|
-
|
|
187
|
-
An extension can register MCP servers with the installed adapter at runtime, for example a plugin host that discovers plugins after load:
|
|
188
|
-
|
|
189
|
-
```ts
|
|
190
|
-
const MCP_RUNTIME_REGISTER_EVENT = "pi-mcp-adapter:runtime-register:v1";
|
|
191
|
-
type RuntimeRegistrationRequest = {
|
|
192
|
-
version: 1;
|
|
193
|
-
name: string;
|
|
194
|
-
definition: { url: string };
|
|
195
|
-
result?:
|
|
196
|
-
| { ok: true; registration: { dispose(): Promise<void> } }
|
|
197
|
-
| { ok: false; error: Error };
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
export default function pluginHost(pi) {
|
|
201
|
-
let registration: { dispose(): Promise<void> } | undefined;
|
|
202
|
-
|
|
203
|
-
pi.on("session_start", () => {
|
|
204
|
-
if (registration) return;
|
|
205
|
-
const request: RuntimeRegistrationRequest = {
|
|
206
|
-
version: 1,
|
|
207
|
-
name: "acme__docs",
|
|
208
|
-
definition: { url: "https://mcp.example.com/mcp" },
|
|
209
|
-
};
|
|
210
|
-
pi.events.emit(MCP_RUNTIME_REGISTER_EVENT, request);
|
|
211
|
-
if (!request.result) throw new Error("pi-mcp-adapter is not installed");
|
|
212
|
-
if (!request.result.ok) throw request.result.error;
|
|
213
|
-
registration = request.result.registration;
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
pi.on("session_shutdown", async () => {
|
|
217
|
-
const current = registration;
|
|
218
|
-
registration = undefined;
|
|
219
|
-
await current?.dispose();
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Cross-extension registration uses Pi's shared event bus and does not require a runtime import from `pi-mcp-adapter`. Emit during `session_start` or later so the adapter listener is installed. The adapter writes `request.result` synchronously; the first adapter listener to respond wins.
|
|
225
|
-
|
|
226
|
-
Runtime registrations are session scoped and never written to config files. Duplicate server names fail closed against configured servers and other registrations. Registered servers use the normal lazy connection, OAuth, approval, and shutdown behavior, but they are proxy-tool-only and their tools become visible at the next tool sync. To change a definition, dispose the registration and register again.
|
|
227
|
-
|
|
228
|
-
### SDK configuration
|
|
229
|
-
|
|
230
|
-
Use `createMcpAdapter` when an SDK or server integration already owns its MCP configuration:
|
|
231
|
-
|
|
232
|
-
```ts
|
|
233
|
-
import { createMcpAdapter } from "pi-mcp-adapter";
|
|
234
|
-
|
|
235
|
-
const extension = createMcpAdapter({
|
|
236
|
-
config: {
|
|
237
|
-
mcpServers: {
|
|
238
|
-
docs: {
|
|
239
|
-
url: "https://mcp.example.com/mcp",
|
|
240
|
-
lifecycle: "eager",
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
},
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Register `extension` with the host SDK.
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
The package ships TypeScript source for Pi's source-loader and SDK integrations. Use a TypeScript-capable loader/toolchain (for example `node --import tsx`) when importing the package from a standalone Node process; raw Node ESM does not execute the `.ts` entry directly.
|
|
250
|
-
|
|
251
|
-
A supplied `config` is a complete, isolated snapshot. It is not merged with files, imports, global config, project config, or `--mcp-config`, and it is never mutated. Explicit `claudePlugins` entries are the sole exception to file isolation: their configured local directories are read because they are part of that supplied snapshot. Relative programmatic plugin paths are normalized against `process.cwd()` when `createMcpAdapter` creates the factory, so the early model-facing surface, load-time initialization, and later session runtime all use the same bundle. Each adapter factory and session receives its own clone, so separate integrations can use different servers and settings safely. In this mode, server status, reconnect, explicit `/mcp-auth <server>`, proxy calls, and direct tools continue to work; setup and no-argument auth/status panels report the limitation instead of discovering or writing ambient config.
|
|
252
|
-
|
|
253
|
-
With `configPath` and no `config`, the adapter keeps normal file merge behavior, and that path takes precedence over argv and `--mcp-config`. The default export keeps the normal file-based behavior. OAuth credentials are stored in the operating system credential store and keyed by the configured server name; URL binding prevents credentials from being accepted for a different server URL. `settings.oauthDir` and `MCP_OAUTH_DIR` are used only as legacy plaintext import locations for older `tokens.json` files, not as credential namespaces. CSRF state and PKCE verifiers are flow-local, so concurrent authorization flows do not share transient secrets.
|
|
254
|
-
|
|
255
|
-
Cooperating Pi extensions can use `pi-mcp-adapter/oauth` to reuse URL-bound OAuth tokens without deep-importing private files:
|
|
256
|
-
|
|
257
|
-
```ts
|
|
258
|
-
import { getMcpOAuthTokensForUrl, updateMcpOAuthTokensForUrl } from "pi-mcp-adapter/oauth";
|
|
259
|
-
|
|
260
|
-
const tokens = await getMcpOAuthTokensForUrl("jira", "https://jira.example.com/mcp");
|
|
261
|
-
await updateMcpOAuthTokensForUrl("jira", "https://jira.example.com/mcp", { accessToken: "..." });
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
The public subpath exposes only token read/update helpers plus a status helper. The async read path uses the adapter's refresh logic before it returns tokens. For a service-protected endpoint or a pre-registered OAuth client, pass the explicit refresh configuration as `getMcpOAuthTokensForUrl(name, url, { definition: { headers, oauth } })`. This optional configuration is never loaded from ambient config or stored with the tokens; headers are bound to the supplied MCP URL's origin. The helpers keep secure-store storage, URL binding, refresh persistence, chunk handling, legacy import, and fail-closed credential-store errors. They do not expose client registration secrets, PKCE verifiers, or OAuth state.
|
|
265
|
-
|
|
266
|
-
### Runtime status snapshots
|
|
267
|
-
|
|
268
|
-
Extensions can subscribe to the adapter's versioned shared event-bus channel instead of parsing `/mcp` or `mcp({})` output:
|
|
269
|
-
|
|
270
|
-
```ts
|
|
271
|
-
import { MCP_STATUS_EVENT, type McpStatusSnapshot } from "pi-mcp-adapter";
|
|
272
|
-
|
|
273
|
-
pi.events.on(MCP_STATUS_EVENT, (snapshot) => {
|
|
274
|
-
const status = snapshot as McpStatusSnapshot;
|
|
275
|
-
// status.servers contains connected, cached, failed, needs-auth,
|
|
276
|
-
// not-connected, or disabled entries.
|
|
277
|
-
});
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
The snapshot is read-only machine-readable data with copied per-server entries. It includes `totalTools`, `totalResources`, `connectedCount`, and `disabledCount`; each server includes `name`, `status`, `toolCount`, `directToolCount` (the number of tools currently registered directly with Pi, including direct resource tools), and `disabled`, with `resourceCount` when known and `failedAgoSeconds` only for an active failure. Reading status never connects a lazy server, starts authentication, or exposes SDK clients, transports, credentials, or server definitions. An initial snapshot is emitted after initialization, updates are emitted for status and metadata changes, and an empty snapshot is emitted when the session shuts down. Initialization withholds that first snapshot until authoritative metadata has been reconciled into Pi's active direct-tool registry. A `connected` snapshot therefore follows model-facing tool-surface synchronization, including removal of stale cached tools when the authoritative catalog is empty.
|
|
281
|
-
|
|
282
|
-
In the configuration examples below, `30000` is illustrative only. If `requestTimeoutMs` is omitted or set to `<= 0`, the MCP SDK default timeout is used.
|
|
283
|
-
|
|
284
|
-
### Server Options
|
|
285
|
-
|
|
286
|
-
```json
|
|
287
|
-
{
|
|
288
|
-
"mcpServers": {
|
|
289
|
-
"my-server": {
|
|
290
|
-
"command": "npx",
|
|
291
|
-
"args": ["-y", "some-mcp-server"],
|
|
292
|
-
"lifecycle": "lazy",
|
|
293
|
-
"idleTimeout": 10,
|
|
294
|
-
"requestTimeoutMs": 30000
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
| Field | Description |
|
|
301
|
-
|-------|-------------|
|
|
302
|
-
| `command` | Executable for stdio transport; mutually exclusive with `url` and `socket` |
|
|
303
|
-
| `args` | Command arguments |
|
|
304
|
-
| `socket` | Explicit `rmcp-mux` Unix-domain socket path; supports `${VAR}`, `$env:VAR`, and `~` expansion and is mutually exclusive with `command` and `url` |
|
|
305
|
-
| `env` | Environment variables; supports `${VAR}` and `$env:VAR` interpolation. A value beginning with `!` runs a command when the stdio server connects; use `!!` for a literal leading `!`. |
|
|
306
|
-
| `inheritEnv` | Stdio only; defaults to `true` and preserves full host-environment inheritance. Set to `false` to exclude arbitrary host variables from the MCP child and SDK negotiation sibling while retaining SDK platform defaults and explicit `env` overlays. This is not an empty environment or an OS sandbox. |
|
|
307
|
-
| `cwd` | Working directory; supports `${VAR}`, `$env:VAR`, and `~` expansion |
|
|
308
|
-
| `url` | HTTP endpoint (StreamableHTTP with SSE fallback); supports raw `${VAR}` and `$env:VAR` interpolation, and missing URL variables fail before any request is sent |
|
|
309
|
-
| `headers` | HTTP headers; supports `${VAR}` and `$env:VAR` interpolation. A value beginning with `!` runs a command when the HTTP server connects or OAuth authenticates; use `!!` for a literal leading `!`. |
|
|
310
|
-
| `requestHeadersCommand` | Trusted executable run for every HTTP request. It receives a versioned JSON envelope containing `method`, `url`, and the exact `bodyBase64` on stdin, and must return a JSON object of headers on stdout. `command`, `args`, and `env` support environment interpolation. Use for caller-bound request signatures; failures stop the request. |
|
|
311
|
-
| `caFile` | HTTPS HTTP servers only: local PEM CA certificate/bundle, e.g. `"caFile": "~/certs/local-ca.pem"`. Replaces (does not add to) default roots for the resolved MCP origin. Supports environment interpolation and `~`; relative paths use the process working directory. Unreadable/invalid files fail closed; hostname and certificate-expiry verification remain enabled. |
|
|
312
|
-
| `auth` | `"bearer"` or `"oauth"` |
|
|
313
|
-
| `oauth.grantType` | `"authorization_code"` (default) or `"client_credentials"` for non-interactive machine auth |
|
|
314
|
-
| `oauth.clientId` | Pre-registered OAuth client ID. Takes precedence over `oauth.clientMetadataUrl` when both are set. |
|
|
315
|
-
| `oauth.clientSecret` | OAuth client secret for confidential clients; a value beginning with `!` runs a command when OAuth authenticates, while `!!` escapes a literal leading `!`. Combining it with `oauth.clientMetadataUrl` requires an explicit `oauth.clientId`. |
|
|
316
|
-
| `oauth.clientMetadataUrl` | Advanced opt-in for an operator-supplied public HTTPS Client ID Metadata Document (CIMD) URL with a non-root path. Used as the `client_id` when the authorization server advertises CIMD support; otherwise the adapter falls back to Dynamic Client Registration. The adapter does not provide or host a default document. |
|
|
317
|
-
| `oauth.scope` | Requested OAuth scopes |
|
|
318
|
-
| `oauth.redirectUri` | Redirect URI for browser OAuth. Dynamic clients normally omit it and use an OS-assigned localhost callback port. Local `http://` loopback URIs accept an explicit port or `{port}` for an OS-assigned port (for example, `http://127.0.0.1:{port}/callback`). Pre-registered `https://` callbacks use manual completion by pasting the full callback URL. |
|
|
319
|
-
| `oauth.clientName` | Client display name advertised during Dynamic Client Registration fallback |
|
|
320
|
-
| `oauth.clientUri` | Client homepage URI advertised during Dynamic Client Registration fallback. Defaults to `piConfig.clientUri` from the host's manifest when set, and is omitted rather than guessed under a rebranded host |
|
|
321
|
-
| `oauth.logoUri` | Client logo URL advertised during Dynamic Client Registration fallback (RFC 7591 `logo_uri`). Must be an absolute `http(s)` URL — consent screens fetch it server-side, so local paths render nothing. Omitted from the registration request when unset |
|
|
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
|
-
| `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
|
-
| `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. |
|
|
326
|
-
| `lifecycle` | `"lazy"` (default), `"eager"`, `"keep-alive"`, or `"lazy-keep-alive"` |
|
|
327
|
-
| `idleTimeout` | Minutes before idle disconnect (overrides global) |
|
|
328
|
-
| `requestTimeoutMs` | Request timeout in milliseconds for live MCP calls (overrides global; if omitted or `<= 0`, the MCP SDK default timeout is used) |
|
|
329
|
-
| `protocolVersion` | `"legacy"` (default), `"auto"`, or `"2026-07-28"`; modern negotiation is opt-in |
|
|
330
|
-
| `tasks` | MCP Tasks extension support on 2026-07-28 connections (default: true; set `false` to opt out); see [Task-augmented tool calls](#task-augmented-tool-calls) |
|
|
331
|
-
| `exposeResources` | Expose MCP resources as tools (default: true) |
|
|
332
|
-
| `directTools` | `true`, `string[]`, or `false` — register tools individually instead of through proxy |
|
|
333
|
-
| `toolPrefix` | Override global `settings.toolPrefix` for this server (`"server"`, `"short"`, `"none"`, or `"mcp"`) |
|
|
334
|
-
| `includeTools` | `string[]` of tool names or glob patterns to expose (matches original names like `get_screenshot`, generated resource names like `read_figjam`, and prefixed names like `figma_get_screenshot`) |
|
|
335
|
-
| `excludeTools` | `string[]` of tool names or glob patterns to hide (applied after `includeTools`) |
|
|
336
|
-
| `searchKeywords` | `{ "tool-or-glob": ["keyword", ...] }` — extra keywords that boost `mcp({ search })` ranking for matching tools; never shown to the model |
|
|
337
|
-
| `debug` | Show server stderr (default: false) |
|
|
338
|
-
| `trace` | Enable metadata-only JSONL protocol tracing for this server; payloads, prompts, tool arguments/results, authorization data, and URLs are never persisted |
|
|
339
|
-
| `disabled` | Keep the server visible in config and status, but prevent connections, authentication, tools, and resource calls (only literal `true` disables it) |
|
|
340
|
-
|
|
341
|
-
#### Custom HTTPS trust
|
|
342
|
-
|
|
343
|
-
`caFile` works with Streamable HTTP, SSE, and per-request header commands. Requests using this trust reject all redirects; configure the final HTTPS endpoint directly. Other origins and servers retain default trust. Layered configuration drops inherited trust when replacing the URL or switching away from HTTP. This option covers the MCP origin, including connection-owned OAuth requests to that exact origin, but not the separate interactive OAuth flow or private-CA authorization servers on other origins. Thanks to [@desmonna](https://github.com/desmonna) for #527.
|
|
344
|
-
|
|
345
|
-
#### macOS local-network access
|
|
346
|
-
|
|
347
|
-
On macOS 15+, Local Network Privacy may deny access to a LAN MCP server depending on the app responsible for hosting Pi. For HTTP URLs with literal private/link-local IPv4 or IPv6 addresses, the adapter adds a hint to `EHOSTUNREACH`, `ENETUNREACH`, or `EACCES` connection errors while retaining the original cause. These codes can also mean routing or firewall trouble; the hint is not proof of a privacy denial. Hostnames are not resolved for this diagnostic.
|
|
348
|
-
|
|
349
|
-
Check **System Settings > Privacy & Security > Local Network** for the hosting app, enable access if listed, then restart that app and Pi. If it is absent or access still fails, try launching Pi directly from Apple Terminal.app or over SSH (contexts Apple documents as exempt). Permission is attributed to responsible code, not necessarily Node or Pi; signing an unsigned CLI alone does not guarantee a permission prompt or fix host attribution. See [Apple TN3179](https://developer.apple.com/documentation/technotes/tn3179-understanding-local-network-privacy).
|
|
350
|
-
|
|
351
|
-
#### Protocol version negotiation
|
|
352
|
-
|
|
353
|
-
The adapter defaults to `protocolVersion: "legacy"`. Omitting the field uses the classic MCP initialize sequence without `server/discover` or 2026 headers, preserving compatibility with deployed 2025-era servers.
|
|
354
|
-
|
|
355
|
-
Use `"auto"` to probe for MCP 2026-07-28 and conservatively fall back to the classic handshake when the server provides legacy evidence. Set it for Cloudflare Workers `createMcpHandler` and other MCP SDK v2 stateless servers. The adapter keeps `"legacy"` as the global default for compatibility. For stdio servers, the SDK probes with a short-lived sibling process before starting the session process, so each fresh auto connection adds one process spawn and can wait for the configured request timeout. Explicit Unix sockets are custom transports and probe in place. HTTP auto negotiation uses the actual Streamable HTTP connection; the adapter falls back to legacy SSE only when the endpoint definitively rejects Streamable HTTP (for example 404/405/406/415), never for authentication failures, cancellation, timeouts, or server errors.
|
|
356
|
-
|
|
357
|
-
Use `"2026-07-28"` to pin that revision. Pinning has no legacy or SSE fallback and fails if the server does not offer the requested version.
|
|
358
|
-
|
|
359
|
-
#### Task-augmented tool calls
|
|
360
|
-
|
|
361
|
-
The adapter supports the [MCP Tasks extension](https://modelcontextprotocol.io/extensions/tasks/overview) (`io.modelcontextprotocol/tasks`, SEP-2663), which lets long-running tools return a durable task handle instead of blocking the connection. Support is negotiated per connection and needs no configuration: the task session only activates when a 2026-07-28 connection's server advertises the extension, so nothing changes for servers without task support. Set `tasks: false` on a server to opt out and keep the plain synchronous call path. Legacy (2025-11-25) experimental tasks are not supported.
|
|
362
|
-
|
|
363
|
-
When active, tool calls keep their normal contract from the model's point of view:
|
|
364
|
-
|
|
365
|
-
- A tool that returns a task handle is transparently polled to completion, honoring the server's suggested poll interval; the final result is returned as if the call had been synchronous.
|
|
366
|
-
- If the task pauses for input (`input_required`), elicitation requests are routed through the same interactive elicitation UI as direct `elicitation/create` requests, and answers are delivered back via `tasks/update`.
|
|
367
|
-
- Cancelling the Pi tool call sends a cooperative `tasks/cancel` to the server.
|
|
368
|
-
- A task that fails with a JSON-RPC error surfaces as the same error a synchronous call would have produced; a tool result with `isError: true` is returned as a normal tool error.
|
|
369
|
-
|
|
370
|
-
Task traffic is dispatched on a dedicated raw channel below the SDK client (the published MCP SDK does not yet decode task result shapes itself), built on the official `@modelcontextprotocol/ext-tasks` requester package. The channel chains onto the connected transport's handlers without replacing the transport, and raw task frames appear in `/mcp-trace` in both directions. Task status notifications (`notifications/tasks`) are not consumed; polling is used exclusively. `requestTimeoutMs` applies per task request (the initiating call and each poll), not to the overall task duration — a task that runs for hours holds the Pi tool call for as long as the model waits for it.
|
|
371
|
-
|
|
372
|
-
One trade-off while tasks are active: every `tools/call` on that connection is dispatched through the task-aware path instead of `Client.callTool`, so the SDK's client-side output-schema validation of `structuredContent` and SEP-2243 `Mcp-Param-*` header mirroring do not run for those calls. Servers still validate their own results; only the client-side double-check is skipped.
|
|
373
|
-
|
|
374
|
-
The stable SDK handles era-specific request envelopes, result decoding, list-changed subscriptions, cancellation, and multi-round-trip sampling/elicitation. The SDK's embedded-input progress callback does not expose the originating tool or resource identity, so the adapter cannot maintain a durable per-tool waiting status row; interactive sessions keep the existing input dialog visible, and proxy calls show request progress when UI is available. The adapter keeps strict OAuth issuer validation in every mode. Adapter-level roots support, standard MCP logging presentation, and configuration/UI for protocol cache hints are not yet implemented.
|
|
375
|
-
|
|
376
|
-
If an internal authorization server publishes mismatched OAuth metadata and cannot be fixed immediately, set `oauth.skipIssuerMetadataValidation: true` on that server only. This is security-weakening. It disables the RFC 8414 issuer echo check and should not be used for public or untrusted servers.
|
|
377
|
-
|
|
378
|
-
If an MCP server does not publish usable protected-resource metadata, set `oauth.authServerMetadataUrl` to its HTTPS OAuth/OIDC authorization-server metadata document. The configured document is used authoritatively, while issuer validation remains enabled by default. This is trusted configuration; use it only for a metadata endpoint you control or explicitly trust.
|
|
379
|
-
|
|
380
|
-
URL-only/default Pi OAuth continues to use Dynamic Client Registration; there is no project-hosted default Client ID Metadata Document. To explicitly opt into CIMD as an advanced operator setting, publish the OAuth client metadata at a stable public HTTPS URL and set `oauth.clientMetadataUrl` to that exact URL. The adapter uses it as the URL-based `client_id` only when discovered authorization-server metadata contains `client_id_metadata_document_supported: true`; servers without CIMD support continue through Dynamic Client Registration. An explicit `oauth.clientId` always wins, and `oauth.clientSecret` without that explicit ID cannot be combined with `oauth.clientMetadataUrl`.
|
|
381
|
-
|
|
382
|
-
#### Stdio environment boundaries
|
|
383
|
-
|
|
384
|
-
`inheritEnv: false` applies only to the actual MCP stdio server process and, for `protocolVersion: "auto"` or `"2026-07-28"`, its disposable SDK negotiation sibling. It does not change the default for other servers: omitting the field or setting it to `true` preserves the existing full host-environment inheritance. With `false`, the SDK still supplies its platform defaults and configured `env` values remain explicit overlays; the result is not a literally empty environment and is not an OS sandbox.
|
|
385
|
-
|
|
386
|
-
Environment interpolation remains intentional. `${VAR}`, `$env:VAR`, and `{env:VAR}` values still read selected host variables and can place those values in the child. `literalEnv: true` keeps its existing behavior by treating configured stdio `env` values as literals. The following helper boundaries are unchanged and still retain the full host environment even when a server uses `inheritEnv: false`:
|
|
387
|
-
|
|
388
|
-
- npm/npx cache resolution and cache-population subprocesses;
|
|
389
|
-
- `!command` secret helpers used by stdio `env` (and other secret fields); and
|
|
390
|
-
- the HTTP `requestHeadersCommand` helper.
|
|
391
|
-
|
|
392
|
-
For tighter use, configure a direct executable instead of npm/npx and avoid `!command` secret helpers. This option limits stdio child inheritance only; it does not provide complete multi-agent or helper-process isolation.
|
|
393
|
-
|
|
394
|
-
With explicit `auth: "oauth"`, configured HTTP `headers` also accompany native OAuth metadata discovery (including `oauth.authServerMetadataUrl`), dynamic registration, code exchange, and refresh, **only at the configured MCP URL's origin** (scheme, host, and port). Discovered or explicitly configured cross-origin OAuth endpoints receive no configured service headers. SDK-owned headers such as OAuth `Authorization` and content types take precedence over configured `headers`. Requests carrying configured service headers reject all HTTP redirects, including same-origin redirects; configure the final endpoint directly. Browser authorization navigation and loopback callbacks do not use these headers. Missing or empty header credentials fail closed.
|
|
395
|
-
|
|
396
|
-
`requestHeadersCommand` follows the fetch path, not the URL path: during a server connection, it wraps the SDK transport fetch (`requestFetch`), so it runs for MCP requests and SDK-owned OAuth requests using that fetch, including discovery, dynamic registration, token exchange (including `client_credentials`), and refresh. It also runs for cross-origin OAuth endpoints: unlike configured `headers`, command-produced headers are **not origin-scoped**. The command receives each request's exact method, URL, and body and must decide where its credentials belong. Its returned headers are applied last, overriding even SDK `Authorization` and content types on name collisions; avoid those names unless intentional.
|
|
397
|
-
|
|
398
|
-
Provider-owned metadata loading through `authFetch` (notably `oauth.authServerMetadataUrl`) bypasses the command, even during a connection. Standalone OAuth start/complete/refresh helpers use their own OAuth fetch, not the transport wrapper, and also bypass it. Browser authorization navigation and loopback callbacks never invoke the command. Thus this is transport-fetch signing, not a hook for every OAuth interaction.
|
|
399
|
-
|
|
400
|
-
Secret values in `headers`, `bearerToken`, `oauth.clientSecret`, and stdio `env` may use a leading `!command` to obtain their value at connection or authentication time. The command runs with stdin and stderr suppressed, stdout is limited to 1 MiB and trimmed, and it must finish within 10 seconds with non-empty output; failures stop the connection or authentication flow. Commands are not run during the preliminary MCP OAuth challenge probe or while reading, merging, previewing, hashing, or rendering configuration. OAuth header commands resolve lazily for the actual SDK backchannel requests, once per authentication leg or connection; the preliminary probe omits command headers. Use `!!` to escape a literal leading `!`; ordinary and escaped values retain environment interpolation.
|
|
401
|
-
|
|
402
|
-
For local desktop bearer tokens, `bearerTokenStore: true` can opt in to the adapter-owned credential-store namespace. It never falls back to plaintext if the store is unavailable, if the stored record is malformed, or if the stored URL differs from the effective server URL. Literal tokens, command tokens, and environment tokens keep precedence so existing configs do not change. Create or rotate a stored token with `pi-mcp-adapter token set <server>` (masked prompt on a terminal, or piped stdin such as `security find-generic-password -s my-token -w | pi-mcp-adapter token set <server>`); the record binds to the effective configured URL at write time. Token commands need Node 22.18+.
|
|
403
|
-
|
|
404
|
-
On Linux, bearer-token and System One key storage also recover automatically when a native operation fails with `KeyRevoked`, including wrapped errors from a revoked inherited session keyring. Each failed read/write/remove is retried once through `keyctl session - <current runtime> <packaged helper>`, with a 10-second timeout and no plaintext fallback. This requires `keyctl` on `PATH` and a working credential store in the fresh session; other storage errors still fail closed. Set `PI_MCP_ADAPTER_DISABLE_KEYRING_RECOVERY=1` to disable this recovery. Normal Pi and token CLI launches need no special wrapper.
|
|
405
|
-
|
|
406
|
-
### Shared MCP processes with rmcp-mux
|
|
407
|
-
|
|
408
|
-
To share one stdio MCP server across Pi sessions, run it under [`rmcp-mux`](https://github.com/VetCoders/rmcp-mux) and point each session at the service socket:
|
|
409
|
-
|
|
410
|
-
```json
|
|
411
|
-
{
|
|
412
|
-
"mcpServers": {
|
|
413
|
-
"memory": {
|
|
414
|
-
"socket": "~/.rmcp-servers/rmcp-mux/sockets/memory.sock"
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
The adapter owns only its client socket and closes that connection when the Pi runtime stops. `rmcp-mux` owns the upstream process, request routing, initialization cache, restart policy, client limits, and socket permissions. Start and configure the mux separately; the adapter never discovers, starts, adopts, or stops its daemon. A socket is an explicit trusted local endpoint, so do not point unrelated projects or users at a mux service unless its tools, state, credentials, and filesystem access are intended to be shared.
|
|
421
|
-
|
|
422
|
-
### Install from one URL
|
|
423
|
-
|
|
424
|
-
Install an MCP endpoint without editing configuration:
|
|
425
|
-
|
|
426
|
-
```js
|
|
427
|
-
mcp({ action: "install", url: "https://example.com/mcp" })
|
|
428
|
-
```
|
|
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.
|
|
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.
|
|
433
|
-
|
|
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
|
-
|
|
436
|
-
Public servers are ready immediately. For OAuth servers, the same action opens the authorization page and watches a reachable loopback callback. After the user grants consent, an `mcp-oauth-status` message returns the agent to connect the server and verify its discovered tools. Remote/headless callbacks retain the manual completion fallback below.
|
|
437
|
-
|
|
438
|
-
### Remote/headless OAuth
|
|
439
|
-
|
|
440
|
-
If Pi is running on a remote server, `/mcp-auth <server>` shows a clickable authorization URL first. Open it in your local browser and approve access, then select **Yes** in Pi to open the callback input. The browser may fail to load the localhost callback page because localhost refers to your workstation; copy the full URL from its address bar and paste it into Pi. The authorization screen closes automatically instead when the browser can reach Pi's callback directly.
|
|
441
|
-
|
|
442
|
-
The same flow is available through the proxy tool for non-interactive clients. By default, persistent OAuth requires an available OS credential store; on headless Linux that usually means an unlocked Secret Service/libsecret keyring. The adapter fails closed instead of falling back to plaintext credentials when the secure store is unavailable.
|
|
443
|
-
|
|
444
|
-
Windows OpenSSH network logons can return `ERROR_NO_SUCH_LOGON_SESSION` (1312) because Credential Manager is unavailable to that logon. For this case, explicitly set `settings.oauthCredentialStore` to `"encrypted-file"` and inject `PI_MCP_ADAPTER_OAUTH_FILE_KEY` as canonical base64 for 32 random bytes (`node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`). Encrypted entries live under the Pi agent directory's `mcp-oauth-encrypted/`; keep the key separately and reauthenticate after loss or rotation. This backend never falls back to the OS store or imports legacy plaintext; see [OAuth](OAUTH.md#token-storage) for its security model.
|
|
445
|
-
|
|
446
|
-
On Linux, if credential access fails because Pi inherited a revoked session keyring, the adapter uses a best-effort recovery path through `keyctl session - node <packaged helper>` so explicit re-authentication can write fresh credentials without killing a long-lived tmux server. This path requires `keyctl` and `node` on `PATH`; missing, locked, or otherwise unavailable credential stores still fail closed.
|
|
447
|
-
|
|
448
|
-
```js
|
|
449
|
-
mcp({ action: "auth-start", server: "linear-server" })
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
For a loopback redirect, the adapter attempts to open the returned authorization URL, watches the callback, completes token exchange, and sends an `mcp-oauth-status` event when authentication finishes. If Pi is remote or cannot open a browser, open the returned URL locally. If the browser cannot reach Pi's callback, copy the full localhost URL from the address bar and complete the flow in the same Pi session:
|
|
453
|
-
|
|
454
|
-
```js
|
|
455
|
-
mcp({
|
|
456
|
-
action: "auth-complete",
|
|
457
|
-
server: "linear-server",
|
|
458
|
-
args: { redirectUrl: "http://localhost:19876/callback?code=...&state=..." }
|
|
459
|
-
})
|
|
460
|
-
```
|
|
461
|
-
|
|
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
|
-
|
|
464
|
-
### Lifecycle Modes
|
|
465
|
-
|
|
466
|
-
- **`lazy`** (default) — Don't connect at startup. Connect on first tool call. Disconnect after idle timeout. Cached metadata keeps search/list working without connections.
|
|
467
|
-
- **`eager`** — Connect at startup but don't auto-reconnect if the connection drops. No idle timeout by default (set `idleTimeout` explicitly to enable).
|
|
468
|
-
- **`keep-alive`** — Connect at startup. Remote HTTP servers refresh their tool catalog during health checks, before user input, and before adapter-triggered turns, reconnecting when the server reports that the session expired. No idle timeout. Use for servers you always need available.
|
|
469
|
-
- **`lazy-keep-alive`** — Don't connect at startup. Connect on first tool call (like `lazy`). Once spawned, never idle-shut down and use the same catalog refresh and reconnect checks as `keep-alive`. Use for servers that are expensive to start but should stay resident after their first use.
|
|
470
|
-
|
|
471
|
-
For remote HTTP keep-alive servers, the authoritative `tools/list` refresh is also the fallback when `list_changed` notifications are unavailable or their stream is lost. Each `tools/list` or `ping` request is capped at 5 seconds, up to 10 servers are checked concurrently, and transient failures use bounded backoff. A successful refresh updates metadata without reconnecting; a response proving that the HTTP session expired triggers a full reconnect and reinstalls the notification handlers. Dynamic direct-tool registration follows the refreshed metadata unless `freezeDirectTools` is enabled.
|
|
472
|
-
|
|
473
|
-
When any enabled server uses `eager` or `keep-alive`, initialization also starts when the extension loads. This supports hosts that embed Pi programmatically and never emit `session_start`; if a session does start later, the session-owned runtime supersedes the load-time runtime.
|
|
474
|
-
|
|
475
|
-
### Settings
|
|
476
|
-
|
|
477
|
-
```json
|
|
478
|
-
{
|
|
479
|
-
"settings": {
|
|
480
|
-
"toolPrefix": "server",
|
|
481
|
-
"allowInstall": false,
|
|
482
|
-
"idleTimeout": 10,
|
|
483
|
-
"requestTimeoutMs": 30000,
|
|
484
|
-
"deferWithMissingMetadata": false,
|
|
485
|
-
"showStatusIcon": true,
|
|
486
|
-
"mcpFooterStatus": "full",
|
|
487
|
-
"toolResultRendering": "compact",
|
|
488
|
-
"collapsedResultLines": 1,
|
|
489
|
-
"notifyOnStartupConnect": true,
|
|
490
|
-
"warnOnLargeDirectTools": true,
|
|
491
|
-
"hostConfigDiscovery": "off",
|
|
492
|
-
"approveTools": ["github_delete_*", "notion_update_*"],
|
|
493
|
-
"oauthDir": ".pi/mcp-oauth",
|
|
494
|
-
"trace": {
|
|
495
|
-
"enabled": true,
|
|
496
|
-
"file": ".pi/mcp-traces/mcp.jsonl",
|
|
497
|
-
"maxBytes": 262144,
|
|
498
|
-
"maxEvents": 10000
|
|
499
|
-
}
|
|
500
|
-
},
|
|
501
|
-
"mcpServers": { }
|
|
502
|
-
}
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
| Setting | Description |
|
|
506
|
-
|---------|-------------|
|
|
507
|
-
| `toolPrefix` | `"server"` (default), `"short"` (strips `-mcp` suffix), `"none"`, or `"mcp"` (prefixes with `mcp__`, using server-mode normalization). Per-server `toolPrefix` overrides this for that server. |
|
|
508
|
-
| `allowInstall` | Allow URL installation through the `mcp` tool (default: `true`). Set to `false` to block it. |
|
|
509
|
-
| `idleTimeout` | Global idle timeout in minutes (default: 10, 0 to disable) |
|
|
510
|
-
| `requestTimeoutMs` | Global request timeout in milliseconds for live MCP calls (if omitted or `<= 0`, the MCP SDK default timeout is used) |
|
|
511
|
-
| `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
|
-
| `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. |
|
|
514
|
-
| `toolResultRendering` | MCP tool result row style: `"compact"` (default) uses self-rendered rows, or `"boxed"` restores the legacy Pi boxed tool row. |
|
|
515
|
-
| `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
|
-
| `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
|
-
| `hostConfigDiscovery` | Host-specific config policy: `"off"` (default), `"prompt"` (detect/report only), or `"on"` (explicitly load detected host configs as the lowest-precedence fallback) |
|
|
518
|
-
| `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
|
-
| `agentPluginPaths` | Agent Plugins package directories to load MCP servers from. Relative paths resolve from the active project cwd. |
|
|
520
|
-
| `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. |
|
|
521
|
-
| `oauthDir` | Legacy OAuth `tokens.json` import directory for this MCP config. Relative paths resolve from the active project cwd. `MCP_OAUTH_DIR` still wins when set. Persistent OAuth credentials are stored in the OS credential store, not this directory. |
|
|
522
|
-
| `oauthCredentialStore` | Set explicitly to `"encrypted-file"` for externally keyed AES-256-GCM storage (notably Windows OpenSSH network logons). Requires `PI_MCP_ADAPTER_OAUTH_FILE_KEY`; absent uses the OS credential store. |
|
|
523
|
-
| `mcpServers.<name>.oauth.authorizationParams` | Extra authorization URL parameters for provider-specific OAuth extensions. Flow-owned parameters such as `client_id`, `redirect_uri`, `scope`, `state`, `code_challenge`, `response_type`, and `resource` cannot be overridden. |
|
|
524
|
-
| `directTools` | Global default for all servers (default: false). `true`, `false`, or `"search"`. Per-server overrides this. |
|
|
525
|
-
| `namespaceProxyTools` | Register per-server `mcp__<server>` wrappers (default: true). Set to `false` to omit them from the model's tool list; `mcp`, `mcpScript`, and direct tools are unaffected. References such as `mcp:<server>` that rely on a wrapper will no longer resolve. Run `/reload` after changing this setting. |
|
|
526
|
-
| `strictDirectToolArguments` | Validate direct-tool inputs against their advertised schemas and recover one JSON string layer for object and array properties (default: false). |
|
|
527
|
-
| `directToolResultDetails` | Direct-tool result details: `"lean"` (default) or `"bounded"` to retain the guarded raw MCP result. |
|
|
528
|
-
| `warnOnLargeDirectTools` | Show the advisory when 75 or more direct tools resolve (default: `true`). Set to `false` to suppress only this advisory. |
|
|
529
|
-
| `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
|
-
| `scriptMode` | Register the MCP-only `mcpScript` plain-JavaScript tool (default: true). Set to `false` to hide it. |
|
|
531
|
-
| `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. |
|
|
533
|
-
| `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
|
-
| `autoAuth` | Auto-run OAuth on `connect`/tool calls when a server needs auth, then retry once (default: false). |
|
|
535
|
-
| `sampling` | Allow MCP servers to sample through Pi models, honoring `modelPreferences.hints` before current/default fallback (default: true when UI approval is available). |
|
|
536
|
-
| `samplingAutoApprove` | Skip sampling confirmation prompts. Required for sampling in non-UI sessions (default: false). |
|
|
537
|
-
| `elicitation` | Allow MCP servers to request user input through Pi dialogs (default: true when Pi UI is available). |
|
|
538
|
-
| `outputGuard` | Guard oversized MCP output: `true` (default), `false`, or `{ maxBytes, maxLines, detailsMaxBytes }`. See [Output Guard](#output-guard). |
|
|
539
|
-
| `trace` | Opt-in metadata-only protocol tracing. Set `{ enabled: true }` globally or `trace: true` on a server. The per-session JSONL file defaults to `.pi/mcp-traces/`; `file`, `maxBytes` (default 262144), and `maxEvents` (default 10000) can be set. Raw MCP payloads, prompts, tool arguments/results, auth data, and URLs are never persisted. |
|
|
540
|
-
|
|
541
|
-
Per-server `idleTimeout`, `requestTimeoutMs`, `approveTools`, and `exposeResources` override the global settings. `debug` remains stderr display and is unrelated to protocol tracing.
|
|
542
|
-
|
|
543
|
-
### Tool Approval
|
|
544
|
-
|
|
545
|
-
Use `approveTools` when a tool should stay visible but not run without confirmation. This is useful for destructive or high-cost actions where hiding the tool would make planning harder, but running it silently is too risky.
|
|
546
|
-
|
|
547
|
-
```json
|
|
548
|
-
{
|
|
549
|
-
"settings": {
|
|
550
|
-
"approveTools": ["github_delete_*", "notion_update_*"]
|
|
551
|
-
},
|
|
552
|
-
"mcpServers": {
|
|
553
|
-
"github": { "approveTools": ["delete_*", "merge_pull_request"] },
|
|
554
|
-
"docs": { "approveTools": false }
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
When a matching tool is called from the proxy tool, a direct MCP tool, a resource call, or an MCP UI iframe, Pi asks: **Allow once**, **Allow for session**, **Allow server for this session**, or **Deny**. **Allow for session** tool grants and MCP UI iframe consent decisions (including denials) persist as non-LLM custom entries on the active Pi session branch and restore on resume or branch navigation. Entries store only server/tool names and deterministic definition/argument hashes; raw arguments, results, and secrets never persist. Tool grants and iframe consent remain separate gates. In headless sessions, matching calls fail closed with an `approval_required` result; denials, abstentions, **Allow once**, and approval-required paths do not create tool grant records. `excludeTools` still removes tools entirely; `approveTools` only gates visible tools at call time.
|
|
560
|
-
|
|
561
|
-
**Allow server for this session** permits all tools and argument combinations on the selected server, including tools discovered later. It does not approve other servers. This broad grant stays in memory only: reload, session replacement, resume, and branch navigation clear it. A changed or replaced server configuration also invalidates it. It is never saved to session entries or configuration. Broker denials, tool exclusions, host security guards, and the separate MCP UI iframe consent gate still apply. Use **Allow for session** instead to approve only the displayed tool definition and arguments.
|
|
562
|
-
|
|
563
|
-
`pi-mcp-adapter/status/v1` is the documented, versioned public channel for cross-extension status. By contrast, `mcp-approval-v1` entries are adapter-owned persistence state, not a supported cross-extension contract; consumers should use documented package exports and event APIs instead.
|
|
564
|
-
|
|
565
|
-
Permission extensions can broker these decisions by listening on `pi-mcp-adapter:tool-approval-request` and claiming the request synchronously:
|
|
566
|
-
|
|
567
|
-
```ts
|
|
568
|
-
import {
|
|
569
|
-
MCP_TOOL_APPROVAL_REQUEST_EVENT,
|
|
570
|
-
type McpToolApprovalRequest,
|
|
571
|
-
} from "pi-mcp-adapter";
|
|
572
|
-
|
|
573
|
-
pi.events.on(MCP_TOOL_APPROVAL_REQUEST_EVENT, (request: McpToolApprovalRequest) => {
|
|
574
|
-
request.claim(async () => {
|
|
575
|
-
return "allow_once"; // "allow_for_session" | "deny" | "abstain"
|
|
576
|
-
});
|
|
577
|
-
});
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
The request includes `serverName`, `originalToolName`, `prefixedToolName`, `args`, `origin`, and optional `signal`. The first synchronous claim wins. Brokered approval runs for every resolved MCP call reaching the approval gate, including calls matching session grants restored from the active branch, regardless of `approveTools` configuration, across proxy, direct, `mcpScript`, resource, and iframe origins. `allow_once` permits only the current call; `allow_for_session` updates the same session-scoped approval cache and persistence path as the built-in dialog; `deny` blocks the current MCP call even if cached, without revoking its grant. Only `abstain` or no claim consults the cache, then the configured approval/UI fallback above if no matching grant exists. With no broker listener, fallback behavior is unchanged.
|
|
581
|
-
|
|
582
|
-
### Output Guard
|
|
583
|
-
|
|
584
|
-
Oversized MCP tool/resource results are guarded by default so a single huge response can't blow up the model context window or the session file:
|
|
585
|
-
|
|
586
|
-
- Inline text output is capped at **50 KiB / 2,000 lines** (matching Pi's built-in `bash` guard). Larger output is truncated to a head preview and the full text is saved to a temp file whose path is included in the result, so the agent can `read`/`grep` it.
|
|
587
|
-
- **Image content blocks pass through unchanged** — only text output is guarded. Images are delivered to the provider as native image content.
|
|
588
|
-
- Binary resource blobs up to **10 MiB** are decoded to private temp files and replaced with file references. Each session is limited to **100 MiB** and **10,000 files**. The files are removed at session teardown.
|
|
589
|
-
- In proxy mode, `details.mcpResult` is kept raw when its JSON is **≤ 16 KiB**; larger results are replaced with a compact summary (block counts, sizes, key previews) and the raw JSON is saved to a temp file. Direct tools keep lean details unless `settings.directToolResultDetails` is set to `"bounded"`, which applies the same guarded `mcpResult` limit.
|
|
590
|
-
|
|
591
|
-
Extensions consuming `details.mcpResult` must check for `omitted === true` on both the result and its `structuredContent` before treating either value as an original payload. For omitted object `structuredContent`, `preservedFields` is only a partial preview; `summary.keyCount` is the original cardinality, while `preservedCount` and `droppedCount` account for retention. Under tiny limits, the whole result may compact to an omission marker without spill metadata.
|
|
592
|
-
|
|
593
|
-
Tune the text and details limits with the object form:
|
|
594
|
-
|
|
595
|
-
```json
|
|
596
|
-
{
|
|
597
|
-
"settings": {
|
|
598
|
-
"outputGuard": { "maxBytes": 51200, "maxLines": 2000, "detailsMaxBytes": 16384 }
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
Set `"outputGuard": false` — or the env kill switch `MCP_OUTPUT_GUARD=0` — to disable text and details guarding. Binary resource materialization and its safety limits remain active. Output-guard spill files are created with mode `0600` under the system temp directory and are not cleaned up automatically; note that spilled MCP output may contain sensitive data.
|
|
604
|
-
|
|
605
|
-
### MCP Scripting
|
|
606
|
-
|
|
607
|
-
#### Jev semantic search and opt-in script evaluation
|
|
608
|
-
|
|
609
|
-
A valid System One key makes semantic search available across every enabled MCP server; it does not run Jev searches automatically. A search uses Jev only when `searchMode: "semantic"` is explicitly requested. Jev ranks matching tools but never executes them. Script evaluation remains disabled until `scriptEvaluation: true` is configured. Requests use the pinned model from `settings.jev.model` (`jev-1.13.0` by default) against the endpoint in `SYSTEMONE_ENDPOINT`, which defaults to TypeSafe at `https://api.typesafe.ai/v1/systemone`. Review your provider's current legal terms — for TypeSafe, [legal terms](https://docs.typesafe.ai/legal), including privacy and retention; a no-training commitment does not mean zero retention.
|
|
610
|
-
|
|
611
|
-
```text
|
|
612
|
-
Normal search
|
|
613
|
-
mcp({ search: "calendar" })
|
|
614
|
-
│
|
|
615
|
-
└── local lexical search
|
|
616
|
-
no Jev request
|
|
617
|
-
|
|
618
|
-
Explicit semantic search
|
|
619
|
-
mcp({ search: "calendar", searchMode: "semantic" })
|
|
620
|
-
│
|
|
621
|
-
└── Jev ranks matching tools
|
|
622
|
-
no tool is executed
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
The quickest desktop setup is:
|
|
626
|
-
|
|
627
|
-
```sh
|
|
628
|
-
pi-mcp-adapter key set systemone
|
|
629
|
-
```
|
|
630
|
-
|
|
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`.
|
|
632
|
-
|
|
633
|
-
`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
|
-
|
|
635
|
-
##### Choosing a provider endpoint
|
|
636
|
-
|
|
637
|
-
System One decisions are the same API at different origins, so pointing at another provider needs an endpoint and, usually, a model:
|
|
638
|
-
|
|
639
|
-
| Provider | `SYSTEMONE_ENDPOINT` | Model |
|
|
640
|
-
| --- | --- | --- |
|
|
641
|
-
| TypeSafe (default) | `https://api.typesafe.ai/v1/systemone` | `jev-1.13.0` |
|
|
642
|
-
| OpenCode Zen | `https://opencode.ai/zen/v1/systemone` | `jev-1.13` |
|
|
643
|
-
| Command Code | `https://api.commandcode.ai/provider/v1/systemone` | `typesafe/jev` |
|
|
644
|
-
| OpenRouter | `https://openrouter.ai/api/alpha/decisions` | `typesafe/jev-1.13` |
|
|
645
|
-
|
|
646
|
-
These are example configurations, subject to each provider's current documentation ([OpenCode Zen](https://opencode.ai/docs/zen/), [Command Code](https://commandcode.ai/docs/provider), [TypeSafe](https://docs.typesafe.ai/)).
|
|
647
|
-
|
|
648
|
-
The endpoint must be an absolute `https` URL with a path. A set-but-invalid `SYSTEMONE_ENDPOINT` disables Jev instead of falling back to the default. Treat the endpoint as trusted configuration: it receives the API key and the judgment payload. Credentials are stored per endpoint, so switching endpoints does not overwrite a saved key. Set the model with:
|
|
649
|
-
|
|
650
|
-
```json
|
|
651
|
-
{ "settings": { "jev": { "model": "jev-1.13" } } }
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
The older `TYPESAFE_API_KEY` variable still works for the default TypeSafe endpoint and is never sent to any other endpoint.
|
|
655
|
-
|
|
656
|
-
Semantic search sends the query text, server names, normalized and original tool names, tool paths, and descriptions to the configured endpoint. It does not send tool results. `allowedServers` restricts semantic search to named servers. `scriptEvaluation` is a separate opt-in that may send the state and MCP-derived results declared in each evaluation; when enabled, it requires an explicit source allowlist.
|
|
657
|
-
|
|
658
|
-
```json
|
|
659
|
-
{
|
|
660
|
-
"settings": {
|
|
661
|
-
"jev": {
|
|
662
|
-
"scriptEvaluation": true,
|
|
663
|
-
"allowedServers": ["github"],
|
|
664
|
-
"maxEvaluationTokensPerScript": 32768
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
```
|
|
669
|
-
|
|
670
|
-
Request semantic discovery explicitly with `mcp({ search: "triage customer reports", searchMode: "semantic" })` or `tools.search({ query: "triage customer reports", searchMode: "semantic" })`. Regex is incompatible. Timeout, rate-limit, and service failures return marked lexical fallback; credential, policy, configuration, and response failures do not. If no allowed server has cached tools, search explains how to connect a server or update the allowlist; if Jev decides no tool fits, the result says that Jev abstained.
|
|
671
|
-
|
|
672
|
-
Optional `jev` controls bound timeout/retries, request and script budgets, semantic candidates (at most 127), and minimum probability. The cumulative token budget uses provider-reported input plus output usage. Exact pre-response admission is unavailable without the provider tokenizer, so byte/question/state limits bound requests before dispatch; a response that exceeds the remaining token budget is discarded and exhausts it. The endpoint is set by `SYSTEMONE_ENDPOINT`; headers and SDK logging are not configurable.
|
|
673
|
-
|
|
674
|
-
`await jev.evaluate({ state, questions, sources })` returns `{ ok, data }` or `{ ok: false, error }`. `sources` must name every MCP server represented in `state`. The host also conservatively taints the whole script with every server-attributed MCP call result or error: declared and observed sources must all be enabled and in `allowedServers`, so copying data or omitting/mislabeling `sources` cannot bypass policy. The taint remains for later direct evaluations and semantic searches even when the script did not retain the call result. Direct and semantic provider attempts share the per-script count, UTF-8 request-byte, token, and deadline budgets; later `tools.call` operations still require normal authentication and approval. See `examples/jev-semantic-filter.mjs` and `examples/jev-accessibility-loop.mjs`.
|
|
675
|
-
|
|
676
|
-
Semantic search sends your request and the available tool descriptions to Jev, which works out which tools best match what you’re trying to do. In a live test with 12 everyday requests and 95 tools and resources, Jev chose the expected result first in 10 of 11 answerable cases and placed it second once. Regular text search found the expected result first in 5 cases. Jev also correctly returned no result for an unrelated request. This was a small test using one local setup, so results will vary with different tools and queries.
|
|
677
|
-
|
|
678
|
-
For multi-call MCP work, write ordinary JavaScript: discover, inspect, call, loop, filter, chain, or fan out, then return one result. Run that code with the default-on `mcpScript` tool. For a single MCP call, search, describe, status check, or auth action, use `mcp` instead. Set `settings.scriptMode` to `false` to hide both the scripting tool and its bundled skill.
|
|
679
|
-
|
|
680
|
-
The bundled `mcp-scripting` skill is manual-only by default, so its description is not added to the model's automatic skill context. Use `/skill:mcp-scripting` when you want its detailed workflow.
|
|
681
|
-
|
|
682
|
-
For example, this is the JavaScript passed as the `code` argument to `mcpScript`:
|
|
683
|
-
|
|
684
|
-
```js
|
|
685
|
-
const { items } = await tools.search({ query: "search issues", server: "github" });
|
|
686
|
-
const candidate = items[0];
|
|
687
|
-
if (!candidate) return { error: "No matching tool" };
|
|
688
|
-
|
|
689
|
-
const details = await tools.describe({ path: candidate.path });
|
|
690
|
-
if (details.error) return details;
|
|
691
|
-
|
|
692
|
-
const result = await tools.call(details.path, { query: "is:open label:bug" });
|
|
693
|
-
if (!result.ok) return result;
|
|
694
|
-
emit({ tool: details.path, completed: true });
|
|
695
|
-
return result.data;
|
|
696
|
-
```
|
|
697
|
-
|
|
698
|
-
Depending on the server, successful `result.data` may be the raw MCP `CallToolResult` envelope rather than the domain payload. Check `result.data.structuredContent` for the fields your script expects; if they are absent, inspect text blocks in `result.data.content` too. If neither shape is understood, return the envelope for inspection instead of coercing it to an empty collection.
|
|
699
|
-
|
|
700
|
-
See the bundled `mcp-scripting` skill for the complete workflow guide. The API is `await tools.search({ query, server?, limit?, offset? })`, `await tools.describe({ path })`, `tools.call(path, args)`, direct flat calls, `emit(value)`, and a captured `console`. Use ordinary JavaScript loops and Promise utilities for composition; fluent helpers such as `tools.find(...).one()`, `tools.parallel(...)`, and `tools.retry(...)` are not provided. MCP calls return `{ ok: true, data }` or `{ ok: false, error: { code, message } }`, so a failed call does not stop the rest of the script. Result details include a concise `calls` trace with each operation, its path or query, outcome, and duration. Emitted values and console output appear before the script's final return value, and the combined result uses the normal MCP output guard. The default timeout is 30 seconds; each script runs in a worker thread that is terminated at the deadline, including for infinite loops.
|
|
701
|
-
|
|
702
|
-
Successful intermediate results reach the script without presentation truncation, details summaries, or output-guard spill files. Each script has a fixed **16 MiB cumulative UTF-8 JSON transfer budget** for successful intermediate data, shared by sequential and parallel calls. A result that cannot fit returns `{ ok: false, error: { code: "intermediate_result_too_large", message } }` and a failed call trace; rejected bytes do not consume the budget, and the script can continue. Request less data or start a new script; there is no configuration option for this cap. Resource calls retain their text-result semantics. Only script-selected output (`emit`, captured console, and `return`) reaches the final output guard; ordinary MCP calls remain guarded as before.
|
|
703
|
-
|
|
704
|
-
The upstream tool executes before this check and may already have side effects. This is a transfer budget, not a total-memory limit: SDK responses, JSON serialization (including rejected results), copies, concurrent responses, and script-created values still allocate memory. Synchronous serialization can delay deadline handling.
|
|
705
|
-
|
|
706
|
-
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
|
-
|
|
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.
|
|
709
|
-
|
|
710
|
-
### MCP Prompts
|
|
711
|
-
|
|
712
|
-
MCP servers can advertise prompt templates alongside tools and resources. The adapter registers cached prompt definitions as Pi slash commands under `/mcp__<server>__<prompt>`, and refreshes their metadata whenever a server connects. Arguments support positional and `key=value` forms with quoting; required arguments are validated before `prompts/get` is called.
|
|
713
|
-
|
|
714
|
-
```text
|
|
715
|
-
/mcp__agent_board__create_plan "harden retry policy"
|
|
716
|
-
/mcp__agent_board__review_pipeline status=paused
|
|
717
|
-
/mcp prompts
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
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.
|
|
721
|
-
|
|
722
|
-
### MCP Elicitation
|
|
723
|
-
|
|
724
|
-
When Pi exposes dialog-capable UI, the adapter advertises form elicitation support. Forms use Pi's stock `select()` and `input()` dialogs, validate the response, and provide a review/edit step before submission. Empty forms use one confirmation dialog. Explicit refusal maps to MCP `decline`; dismissing a dialog maps to `cancel`.
|
|
725
|
-
|
|
726
|
-
URL mode is advertised only in TUI mode. The adapter displays the requesting server, target host, and full URL, and always requires consent before opening the browser. It also handles URL-required tool errors (`-32042`) and completion notifications; after completing the browser interaction, retry the original tool call.
|
|
727
|
-
|
|
728
|
-
### Direct Tools
|
|
729
|
-
|
|
730
|
-
By default, all MCP tools are accessed through the single `mcp` proxy tool. This keeps context small but means the LLM has to discover MCP tools via proxy search. If you want specific tools to show up directly in the agent's tool list — alongside `read`, `bash`, `edit`, etc. — add `directTools` to your config.
|
|
731
|
-
|
|
732
|
-
Per-server:
|
|
733
|
-
|
|
734
|
-
```json
|
|
735
|
-
{
|
|
736
|
-
"mcpServers": {
|
|
737
|
-
"chrome-devtools": {
|
|
738
|
-
"command": "npx",
|
|
739
|
-
"args": ["-y", "chrome-devtools-mcp@1.6.0"],
|
|
740
|
-
"directTools": true
|
|
741
|
-
},
|
|
742
|
-
"github": {
|
|
743
|
-
"command": "npx",
|
|
744
|
-
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
745
|
-
"directTools": ["search_repositories", "get_file_contents"]
|
|
746
|
-
},
|
|
747
|
-
"huge-server": {
|
|
748
|
-
"command": "npx",
|
|
749
|
-
"args": ["-y", "mega-mcp@latest"]
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
```
|
|
754
|
-
|
|
755
|
-
| Value | Behavior |
|
|
756
|
-
|-------|----------|
|
|
757
|
-
| `true` | Register all tools from this server as individual Pi tools |
|
|
758
|
-
| `["tool_a", "tool_b"]` | Register only these tools (use original MCP names) |
|
|
759
|
-
| Omitted or `false` | Proxy only (default) |
|
|
760
|
-
|
|
761
|
-
To set a global default for all servers:
|
|
762
|
-
|
|
763
|
-
```json
|
|
764
|
-
{
|
|
765
|
-
"settings": {
|
|
766
|
-
"directTools": true
|
|
767
|
-
},
|
|
768
|
-
"mcpServers": {
|
|
769
|
-
"huge-server": {
|
|
770
|
-
"directTools": false
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
```
|
|
775
|
-
|
|
776
|
-
Per-server `directTools` overrides the global setting. The example above registers direct tools for every server except `huge-server`.
|
|
777
|
-
|
|
778
|
-
### Search-activated direct tools
|
|
779
|
-
|
|
780
|
-
`directTools: true` puts every tool's definition in front of the model on every turn. Past a few dozen tools that costs context and, on smaller models, accuracy — the advisory at 75 exists for that reason. `directTools: "search"` is the middle path: the tools are registered as real direct tools with real schemas, but **inactive**, and `mcp({ search })` activates the matches additively.
|
|
781
|
-
|
|
782
|
-
```json
|
|
783
|
-
{
|
|
784
|
-
"mcpServers": {
|
|
785
|
-
"github": {
|
|
786
|
-
"command": "npx",
|
|
787
|
-
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
788
|
-
"directTools": "search"
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
```
|
|
793
|
-
|
|
794
|
-
A successful `mcp({ search })` activates matching search-mode tools additively for the rest of the session and reports newly activated names in `addedToolNames`. A successful `mcp({ tool })` call for a held search-mode tool activates it the same way, so the next call uses its real schema; a failed call (lookup, approval, or tool error) activates nothing. A restart or resumed session starts with them inactive again. Selecting `directTools: true` activates held tools, while switching back to `"search"` holds them again. Search-mode tools do not count toward the 75-tool advisory.
|
|
795
|
-
|
|
796
|
-
To expose only a subset of a noisy server, add `includeTools` on the server. Values can be exact original names, generated resource names such as `read_<resource>`, prefixed names, or simple glob patterns:
|
|
797
|
-
|
|
798
|
-
```json
|
|
799
|
-
{
|
|
800
|
-
"mcpServers": {
|
|
801
|
-
"dokploy": {
|
|
802
|
-
"url": "http://localhost:3845/mcp",
|
|
803
|
-
"directTools": true,
|
|
804
|
-
"includeTools": ["get_*", "dokploy_list_apps"]
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
```
|
|
809
|
-
|
|
810
|
-
To hide specific tools while still using `directTools: true`, add `excludeTools` on the server. `excludeTools` is applied after `includeTools`:
|
|
811
|
-
|
|
812
|
-
```json
|
|
813
|
-
{
|
|
814
|
-
"mcpServers": {
|
|
815
|
-
"figma": {
|
|
816
|
-
"url": "http://localhost:3845/mcp",
|
|
817
|
-
"directTools": true,
|
|
818
|
-
"excludeTools": ["read_figjam", "figma_get_code_connect_map"]
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
```
|
|
823
|
-
|
|
824
|
-
`includeTools` and `excludeTools` filter direct tools, proxy search/list/describe, and the `/mcp` panel view.
|
|
825
|
-
|
|
826
|
-
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
|
-
|
|
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>`.
|
|
829
|
-
|
|
830
|
-
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
|
-
|
|
832
|
-
Models sometimes encode an object or array argument as a JSON string. Set `settings.strictDirectToolArguments` to `true` to recover one such layer for schema-declared object and array properties, then validate the complete input against the advertised schema before execution.
|
|
833
|
-
|
|
834
|
-
Set `settings.directToolResultDetails` to `"bounded"` when an extension needs structured MCP result fields in Pi's direct-tool result details. The same output guard limits apply. Small leading structured fields stay available, while large fields receive bounded summaries and the complete guarded result follows the output guard's spill-file policy. The default `"lean"` mode keeps the existing server and tool metadata only.
|
|
835
|
-
|
|
836
|
-
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
|
-
|
|
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.
|
|
839
|
-
|
|
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.
|
|
841
|
-
|
|
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.
|
|
843
|
-
|
|
844
|
-
**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
|
-
|
|
846
|
-
### MCP UI Integration
|
|
847
|
-
|
|
848
|
-
MCP servers can ship interactive UIs via the [MCP UI](https://github.com/MCP-UI-Org/mcp-ui) standard. When you call a tool that has a UI resource, the adapter opens it in a native macOS window via [Glimpse](https://github.com/hazat/glimpse) if available, otherwise falls back to the browser.
|
|
849
|
-
|
|
850
|
-
**How it works:**
|
|
851
|
-
|
|
852
|
-
1. Agent calls a tool like `launch_dashboard`
|
|
853
|
-
2. The tool's metadata includes `_meta.ui.resourceUri` pointing to a UI resource
|
|
854
|
-
3. pi-mcp-adapter fetches the UI HTML and opens it in an iframe
|
|
855
|
-
4. The UI can call MCP tools and send messages back to the agent
|
|
856
|
-
|
|
857
|
-
**Native rendering:** On macOS, if [Glimpse](https://github.com/hazat/glimpse) is installed (`pi install npm:glimpseui`), UIs open in a native WKWebView window instead of a browser tab. Set `MCP_UI_VIEWER=browser` to force the browser, `MCP_UI_VIEWER=glimpse` to require native rendering, `MCP_UI_VIEWER=orca` to open in the [Orca](https://github.com/orca) built-in browser (falls back to the system browser if Orca is unavailable), or `MCP_UI_VIEWER=none` (also accepts `off` / `disabled`) to suppress the window entirely — the tool still runs and its inline result is returned to the agent, but no browser or native window opens. This is useful for headless setups, CI, or users who want the tool output delivered inline as text only. When suppressed, a one-line info notification shows the UI URL so it can still be opened manually if needed.
|
|
858
|
-
|
|
859
|
-
**Bidirectional communication:** The UI talks back. When it sends a prompt or intent, the message is stored and `triggerTurn()` wakes the agent. The agent retrieves messages via `mcp({ action: "ui-messages" })` and responds, enabling conversational UIs where the app and agent collaborate in real-time.
|
|
860
|
-
|
|
861
|
-
**Session reuse:** When the agent calls the same tool again while its UI is already open, the adapter pushes the new result to the existing window instead of replacing it. This enables live updates — the agent can refine a chart, add data, or respond to user input without losing the current view. Different tools still replace the session as before.
|
|
862
|
-
|
|
863
|
-
**Message types from UI:**
|
|
864
|
-
|
|
865
|
-
| Type | Purpose |
|
|
866
|
-
|------|---------|
|
|
867
|
-
| `prompt` | User message that triggers an agent response |
|
|
868
|
-
| `intent` | Structured action with name + params |
|
|
869
|
-
| `notify` | Fire-and-forget notification |
|
|
870
|
-
| `message` | Generic message payload |
|
|
871
|
-
| (custom) | Any other type forwarded as intent |
|
|
872
|
-
|
|
873
|
-
**Retrieving UI messages:**
|
|
874
|
-
|
|
875
|
-
```
|
|
876
|
-
mcp({ action: "ui-messages" })
|
|
877
|
-
```
|
|
878
|
-
|
|
879
|
-
Returns accumulated messages from UI sessions. Each message includes `type`, `sessionId`, `serverName`, `toolName`, and `timestamp`. Prompt messages include `prompt`, intent messages include `intent` and `params`.
|
|
880
|
-
|
|
881
|
-
**Browser controls:**
|
|
882
|
-
|
|
883
|
-
- **Cmd/Ctrl+Enter** — Complete and close
|
|
884
|
-
- **Escape** — Cancel and close
|
|
885
|
-
- **Done/Cancel buttons** — Same as keyboard shortcuts
|
|
886
|
-
|
|
887
|
-
**Technical notes:**
|
|
888
|
-
|
|
889
|
-
- Tool consent gates whether UIs can call MCP tools (never/once-per-server/always)
|
|
890
|
-
- `_meta.ui.visibility` controls audience: tools marked app-only stay out of the model tool list, and tools marked model-only cannot be called from the UI iframe.
|
|
891
|
-
- Works with both stdio and HTTP MCP servers
|
|
892
|
-
- Uses a local 408KB AppBridge bundle (MCP SDK + Zod) for browser↔server communication
|
|
893
|
-
- Enforces CSP from standard `_meta.ui.csp` and OpenAI-compatible `_meta["openai/widgetCSP"]` metadata in the response header while preserving provider HTML.
|
|
894
|
-
|
|
895
|
-
### Local Example: Interactive Visualizer
|
|
896
|
-
|
|
897
|
-
A minimal MCP UI example at `examples/interactive-visualizer` demonstrating charts, bidirectional messaging, and streaming. From that directory:
|
|
898
|
-
|
|
899
|
-
```bash
|
|
900
|
-
npm install
|
|
901
|
-
npm run build
|
|
902
|
-
npm run install-local
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
Restart pi, then ask the agent to show a chart — it calls `show_chart` and opens the UI in Glimpse (macOS) or the browser. Use `npm run uninstall-local` to remove the MCP entry.
|
|
906
|
-
|
|
907
|
-
### Import Existing Configs
|
|
908
|
-
|
|
909
|
-
Shared MCP files are loaded automatically. Use `imports` only for host-specific config formats that are not already covered by `.mcp.json` or `~/.config/mcp/mcp.json`.
|
|
910
|
-
|
|
911
|
-
```json
|
|
912
|
-
{
|
|
913
|
-
"imports": ["cursor", "claude-code", "claude-desktop", "opencode"],
|
|
914
|
-
"mcpServers": { }
|
|
915
|
-
}
|
|
916
|
-
```
|
|
917
|
-
|
|
918
|
-
Supported compatibility imports: `cursor`, `claude-code`, `claude-desktop`, `opencode`, `vscode`, `windsurf`, `codex`
|
|
919
|
-
|
|
920
|
-
`pi-mcp-adapter init` detects these host-specific configs and adds missing imports to the Pi agent dir config for you. The `opencode` import reads OpenCode V1 `mcp` entries from both `~/.config/opencode/opencode.json` and the project `opencode.json`, with project fields taking precedence. It is explicit-import only; OpenCode V2, inline content, managed configs, and remote discovery are not supported.
|
|
921
|
-
|
|
922
|
-
### Project Config
|
|
923
|
-
|
|
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.
|
|
925
|
-
|
|
926
|
-
## Usage
|
|
927
|
-
|
|
928
|
-
| Mode | Example |
|
|
929
|
-
|------|---------|
|
|
930
|
-
| Status | `mcp({ })` |
|
|
931
|
-
| List server | `mcp({ server: "name" })` |
|
|
932
|
-
| Search | `mcp({ search: "screenshot navigate", limit: 12, offset: 0 })` |
|
|
933
|
-
| Describe | `mcp({ describe: "tool_name" })` |
|
|
934
|
-
| Instructions | `mcp({ instructions: "name" })` |
|
|
935
|
-
| Call | `mcp({ tool: "...", args: { key: "value" } })` |
|
|
936
|
-
| Connect | `mcp({ connect: "server-name" })` |
|
|
937
|
-
| UI messages | `mcp({ action: "ui-messages" })` |
|
|
938
|
-
| Auth start | `mcp({ action: "auth-start", server: "name" })` |
|
|
939
|
-
| Auth complete | `mcp({ action: "auth-complete", server: "name", args: { redirectUrl: "..." } })` |
|
|
940
|
-
|
|
941
|
-
`mcp({ connect: "server-name" })` refreshes an already connected server, so new tools, resources, prompts, and instructions can load without restarting Pi.
|
|
942
|
-
|
|
943
|
-
MCP proxy and direct-tool results use compact self-rendered rows by default. Collapsed success output shows the call title, a bounded one-line input preview when arguments exist, and the first result line, with a `Ctrl+O to expand` hint when more text is hidden. The full result remains available when expanded and is still returned unchanged to the model. Set `settings.toolResultRendering` to `"boxed"` to restore the legacy boxed Pi row, or set `settings.collapsedResultLines` to `2` or `3` when you want more collapsed text.
|
|
944
|
-
|
|
945
|
-
Search includes both MCP tools and Pi tools (from extensions). Pi tools appear first with `[pi tool]` prefix. Space-separated words are ranked by weighted matches across name, server, description, and any configured `searchKeywords`, then returned one page at a time (`limit` defaults to 12). Use `details.nextOffset` for the next page. Regex search is still available with `regex: true`, but regex results are paginated without ranking.
|
|
946
|
-
|
|
947
|
-
Tool names are fuzzy-matched on hyphens and underscores — `context7_resolve_library_id` finds `context7_resolve-library-id`. When `describe` or `tool` cannot resolve a name, the result includes top suggestions so the agent can correct a typo or missing prefix in the same turn.
|
|
948
|
-
|
|
949
|
-
### Search keywords
|
|
950
|
-
|
|
951
|
-
Search uses literal matching so a tool whose name and description use different vocabulary than the query won't be found. Per-server `searchKeywords` adds extra vocabulary for matching tools:
|
|
952
|
-
|
|
953
|
-
```json
|
|
954
|
-
{
|
|
955
|
-
"mcpServers": {
|
|
956
|
-
"github": {
|
|
957
|
-
"command": "npx",
|
|
958
|
-
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
959
|
-
"searchKeywords": {
|
|
960
|
-
"search_code": ["grep"],
|
|
961
|
-
"*": ["gh"]
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
```
|
|
967
|
-
|
|
968
|
-
With this config, `mcp({ search: "grep" })` finds `github_search_code` even though neither its name nor description contains that word. Similarly, `mcp({ search: "gh" })` finds all tools provided by the github server.
|
|
969
|
-
|
|
970
|
-
Keys match a tool's original name, prefixed name, or a glob (`*` applies to every tool on the server) and all matching entries combine. Keywords are weighted like description text, with an extra boost when the query exactly matches a configured phrase. They affect ranked and regex search only (including `tools.search` in `mcpScript`): they never appear in tool schemas, `describe` output, direct-tool registration, or the metadata cache, and search with keywords works offline from cached metadata.
|
|
971
|
-
|
|
972
|
-
When `includeSchemas` is enabled, search and describe render common JSON Schema parameters as compact TypeScript shapes like `{ query: string; limit?: number; }`, with the older schema formatter retained as a fallback for unsupported schemas.
|
|
973
|
-
|
|
974
|
-
For HTTP servers, Pi reports HTTP 503 as temporary unavailability and does not add another immediate retry loop. Keep-alive servers keep cached metadata available and retry after 30 seconds, backing off to 5 minutes. Other failed connects run a one-request shape probe that can turn opaque transport errors into setup hints such as `endpoint returned HTML (200) — this URL does not appear to speak MCP`. Healthy connections are not probed.
|
|
975
|
-
|
|
976
|
-
Servers that provide usage guidance via the MCP `instructions` field surface it through discovery paths: `mcp({ server: "name" })` includes a preview, and `mcp({ instructions: "name" })` returns the full text. Instructions are captured at connect time and cached alongside tool metadata, so they stay available without a live connection.
|
|
977
|
-
|
|
978
|
-
## Commands
|
|
979
|
-
|
|
980
|
-
| Command | What it does |
|
|
981
|
-
|---------|--------------|
|
|
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 |
|
|
994
|
-
| `/mcp-auth` | Open an OAuth server picker in interactive UI sessions |
|
|
995
|
-
| `/mcp-auth <server>` | OAuth setup for a specific server |
|
|
996
|
-
|
|
997
|
-
If `settings.autoAuth` is `true`, `mcp({ connect: ... })`, `mcp({ tool: ... })`, and direct tool calls automatically run OAuth when needed and retry once.
|
|
998
|
-
|
|
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).
|
|
1000
|
-
|
|
1001
|
-
### MCP output schemas
|
|
1002
|
-
|
|
1003
|
-
Advertised tool `outputSchema` values support JSON Schema draft-07 and 2020-12. Unstamped schemas use the SDK's 2020-12 default. Returned `structuredContent` is validated against the advertised schema for both proxy and direct-tool calls.
|
|
1004
|
-
|
|
1005
|
-
## How It Works
|
|
1006
|
-
|
|
1007
|
-
- One `mcp` tool in context (~200 tokens) instead of hundreds
|
|
1008
|
-
- Servers are lazy by default — they connect on first tool call, not at startup
|
|
1009
|
-
- Tool metadata is cached to disk so search/list/describe work without live connections
|
|
1010
|
-
- Idle servers disconnect after 10 minutes (configurable), reconnect automatically on next use
|
|
1011
|
-
- npx-based servers resolve to direct binary paths, skipping the ~143 MB npm parent process
|
|
1012
|
-
- MCP server validates arguments, not the adapter
|
|
1013
|
-
- Remote keep-alive servers force-refresh their tool catalog during health checks, before user input, and before adapter-triggered turns, with bounded reconnect backoff
|
|
1014
|
-
- Specific tools can be promoted from the proxy to first-class Pi tools via `directTools` config, so the LLM sees them directly instead of having to search
|
|
1015
|
-
|
|
1016
|
-
## Limitations
|
|
1017
|
-
|
|
1018
|
-
- Cross-session server sharing not yet implemented (each Pi session runs its own server processes)
|
|
1019
|
-
- Compact MCP result rendering summarizes text, but inline images are still controlled by Pi's image display settings and may render below the compact text summary.
|
|
1020
|
-
- Pi still owns one separator row before self-rendered tool output, so compact mode reduces adapter rendering height but cannot promise true zero-gap rows.
|
|
1021
|
-
- MCP sampling support is text-only; context inclusion, tools, stop sequences, audio, and image content are rejected with explicit errors.
|