@bastani/atomic 0.9.5-alpha.8 → 0.9.5-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +11 -0
- package/dist/builtin/mcp/README.md +6 -6
- package/dist/builtin/mcp/direct-tools.ts +41 -23
- package/dist/builtin/mcp/index.ts +31 -4
- package/dist/builtin/mcp/init.ts +4 -43
- package/dist/builtin/mcp/metadata-hydration.ts +37 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/mcp/proxy-info-modes.ts +67 -14
- package/dist/builtin/mcp/startup-warmup.ts +98 -0
- package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
- package/dist/builtin/subagents/CHANGELOG.md +12 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.ts +14 -25
- package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
- package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +23 -18
- package/dist/builtin/subagents/src/runs/background/result-watcher.ts +20 -10
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +13 -3
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +10 -2
- package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +28 -1
- package/dist/builtin/subagents/src/tui/render-widget.ts +29 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +18 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/builtin/goal-runner.ts +39 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
- package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
- package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
- package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
- package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
- package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
- package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
- package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
- package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
- package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
- package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
- package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
- package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
- package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
- package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
- package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
- package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
- package/dist/builtin/workflows/src/durable/backend.ts +12 -0
- package/dist/builtin/workflows/src/durable/dbos-backend.ts +17 -1
- package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
- package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
- package/dist/builtin/workflows/src/durable/types.ts +16 -0
- package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
- package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
- package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
- package/dist/builtin/workflows/src/engine/run.ts +20 -19
- package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
- package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
- package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
- package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
- package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
- package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
- package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
- package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
- package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
- package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
- package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
- package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
- package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
- package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
- package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +2 -1
- package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +3 -2
- package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
- package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
- package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
- package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
- package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
- package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
- package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
- package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
- package/dist/core/agent-session-auto-compaction.d.ts +23 -3
- package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
- package/dist/core/agent-session-auto-compaction.js +129 -17
- package/dist/core/agent-session-auto-compaction.js.map +1 -1
- package/dist/core/agent-session-events.d.ts.map +1 -1
- package/dist/core/agent-session-events.js +7 -0
- package/dist/core/agent-session-events.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +6 -3
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/agent-session-prompt.d.ts.map +1 -1
- package/dist/core/agent-session-prompt.js +1 -1
- package/dist/core/agent-session-prompt.js.map +1 -1
- package/dist/core/agent-session.d.ts +4 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +2 -1
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +6 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +72 -29
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
- package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
- package/dist/core/openai-responses-payload-sanitizer.js +24 -9
- package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
- package/dist/core/slash-commands.d.ts +2 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +141 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/main-deferred-startup.d.ts.map +1 -1
- package/dist/main-deferred-startup.js +2 -2
- package/dist/main-deferred-startup.js.map +1 -1
- package/dist/main-early-input.d.ts.map +1 -1
- package/dist/main-early-input.js +7 -0
- package/dist/main-early-input.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
- package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-agent-events.js +1 -0
- package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +21 -9
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-deferred-startup.js +41 -28
- package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
- package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-input-handling.js +41 -6
- package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
- package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-base.js +3 -0
- package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js +1 -1
- package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -2
- package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +3 -1
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
- package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-render-chat.js +15 -1
- package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-session-routing.js +5 -1
- package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +20 -21
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/global-theme.js +26 -2
- package/dist/modes/interactive/theme/global-theme.js.map +1 -1
- package/dist/utils/fs-watch.d.ts +24 -1
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +60 -5
- package/dist/utils/fs-watch.js.map +1 -1
- package/docs/compaction.md +5 -1
- package/docs/development.md +4 -0
- package/docs/extensions.md +5 -0
- package/docs/json.md +1 -1
- package/docs/quickstart.md +1 -1
- package/docs/settings.md +1 -1
- package/docs/subagents.md +4 -0
- package/docs/windows.md +4 -0
- package/docs/workflows.md +8 -6
- package/npm-shrinkwrap.json +23 -23
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.9.5-alpha.9] - 2026-07-09
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Improved interactive startup responsiveness by keeping footer git watcher setup out of first paint and deferring it until the first input handler is ready, while preserving lazy footer branch reads and post-frame branch-change re-rendering. Deferred extension/resource loading no longer starts on a blind post-paint timer that can freeze live typing; instead, Atomic starts it in the background once input is ready and uses a readiness gate before the first normal prompt/model turn if the background load has not settled. This keeps first-paint/typeahead responsive while ensuring the first interactive prompt sees extension tools, skills, prompt templates, resources, and extension-registered provider/model updates. Built-in slash commands stay available immediately, and bundled extension slash commands expose lightweight metadata for autocomplete before their heavy implementations load; explicitly submitted extension slash commands load the implementation on demand and then route the same command. Explicit provider/model selections stay on the synchronous startup path because they must resolve against extension-registered providers before the session is created. Changelog and first-run notice materialization now happen after the first frame, and startup/input timing probes cover raw-mode enablement, first captured raw key, first frame, input-handler callback installation, and first submit for benchmark/probe runs.
|
|
10
|
+
- Documented the built-in extension lazy-startup model: lightweight tools/commands register immediately while MCP connection warmup, workflow discovery, subagent maintenance, and web-access heavy provider loading run in the background or on explicit use.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed Windows native filesystem watchers to canonicalize watched paths before calling `fs.watch`, reject unresolved 8.3 short-name paths, and fall back to polling for unsafe watcher paths. This prevents libuv fs-event assertion crashes when temp, session, footer, or theme paths contain short-name components such as `USERNA~1`.
|
|
15
|
+
- Fixed responses truncated at the output-token cap (`stopReason: "length"`) dead-ending the model's work with a "maximum output token limit" error when the context was still below the auto-compaction threshold. Previously auto-continuation only happened as a side effect of threshold compaction, so a length truncation with input room to spare left the task half-finished. Length-truncated turns now continue directly without compacting: the incomplete assistant is removed from retry context and the generation resumes automatically so the model finishes where it left off, bounded by a small consecutive-continuation cap so a turn that keeps exceeding the per-turn output cap still terminates. Compaction-driven `willRetry: true` continuations are now consistently tracked through the normal prompt lifecycle, so `AgentSession.prompt()` waits for threshold length-stop continuation before resolving instead of treating it as fire-and-forget.
|
|
16
|
+
- Fixed auto-compaction after OpenAI Responses output-budget underflow errors such as `Invalid 'max_output_tokens': integer below minimum value. Expected a value >= 16, but got 1 instead.` Atomic now classifies that specific context-pressure failure as retry-worthy when the live context crosses the compaction threshold, removes the empty error assistant from retry context after compaction, and automatically continues from the preceding work anchor instead of waiting for the user to type `Continue`. Generic `invalid_request_body` errors such as malformed tool schemas are still not auto-retried, OpenAI Responses payload sanitization now prevents `max_output_tokens` values below the provider minimum of 16 from being sent, and repeated output-budget underflow continuation is capped at one compact-and-retry attempt so an unrecoverable context terminates visibly instead of looping or stalling.
|
|
17
|
+
- Fixed bundled workflow durable resume for reusable `git_worktree_dir` worktrees so resumed runs reuse the original invocation repository/cwd and report slow Git subprocess timeouts as Git timeouts instead of repository-detection failures.
|
|
18
|
+
- Removed the queued-message restore success status from the interactive UI so restoring queued messages silently returns them to the editor without showing transient restore copy.
|
|
19
|
+
- Fixed bundled MCP proxy metadata paths so explicit cold-cache `search`, `describe`, and server-list requests hydrate lazy server metadata on demand without reconnecting all lazy servers during startup.
|
|
20
|
+
|
|
5
21
|
## [0.9.5-alpha.8] - 2026-07-08
|
|
6
22
|
|
|
7
23
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/cursor",
|
|
3
|
-
"version": "0.9.5-alpha.
|
|
3
|
+
"version": "0.9.5-alpha.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Experimental first-party Atomic extension for Cursor OAuth, model discovery, and streaming provider registration.",
|
|
6
6
|
"contributors": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@bastani/atomic-natives": "0.9.5-alpha.
|
|
43
|
+
"@bastani/atomic-natives": "0.9.5-alpha.9",
|
|
44
44
|
"@bufbuild/protobuf": "^2.12.1",
|
|
45
45
|
"@earendil-works/pi-ai": "^0.80.3"
|
|
46
46
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.9.5-alpha.
|
|
3
|
+
"version": "0.9.5-alpha.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
|
|
6
6
|
"contributors": [
|
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.9.5-alpha.9] - 2026-07-09
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Improved startup responsiveness by keeping first-run/default lazy MCP metadata bootstrap out of `initializeMcp()`'s synchronous path. Cached direct tools and the MCP proxy still register immediately, explicit `eager`/`keep-alive` servers still connect during initialization, missing configured or `MCP_DIRECT_TOOLS`-selected direct-tool metadata is warmed in the background, and explicit proxy `search`, `describe`, and server-list requests hydrate cold-cache lazy server metadata on demand instead of broadening startup warmup. Warmed direct tools are registered live in the current session after metadata refresh rather than requiring restart. Cold-cache `describe` first narrows hydration to prefix-matched or explicitly requested servers, treats hyphen/underscore prefixes as aliases, and does not fan out to unrelated lazy servers after a prefix-directed miss; cold-cache proxy `search` intentionally hydrates all matching lazy servers so unscoped searches can see every configured tool.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Aligned MCP tool result expansion hints with the CLI-wide `Ctrl+o` keybinding copy.
|
|
19
|
+
- Fixed MCP background direct-tool warmup cancellation so stale in-flight connects are discarded before metadata/cache mutation, direct-tool refresh callbacks are guarded to the active session, completed warmups clear their active handle safely, and env-selected direct tool servers hydrate without forcing every lazy server eager.
|
|
20
|
+
|
|
10
21
|
## [0.9.4] - 2026-07-03
|
|
11
22
|
|
|
12
23
|
### Changed
|
|
@@ -65,7 +65,7 @@ Precedence is:
|
|
|
65
65
|
3. `.mcp.json`
|
|
66
66
|
4. `.pi/mcp.json`
|
|
67
67
|
|
|
68
|
-
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.
|
|
68
|
+
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 when a valid cache exists; on a cold cache, explicit proxy search/describe/server-list requests may connect lazy server(s) once to hydrate metadata on demand. `describe` narrows cold-cache hydration to an explicitly requested server or the server identified by the configured tool-name prefix before falling back to broader discovery. Unscoped `search` intentionally hydrates every configured lazy server that lacks cached metadata so the search can see all available tools.
|
|
69
69
|
|
|
70
70
|
```
|
|
71
71
|
mcp({ search: "screenshot" })
|
|
@@ -136,7 +136,7 @@ Pi-specific files are the write targets for imported or shared global servers wh
|
|
|
136
136
|
|
|
137
137
|
### Lifecycle Modes
|
|
138
138
|
|
|
139
|
-
- **`lazy`** (default) — Don't connect at startup. Connect on first tool call. Disconnect after idle timeout. Cached metadata keeps search/list working without connections.
|
|
139
|
+
- **`lazy`** (default) — Don't connect at startup. Connect on first tool call or explicit cold-cache proxy metadata request (`search`, `describe`, or `server` list). Disconnect after idle timeout. Cached metadata keeps search/list working without connections.
|
|
140
140
|
- **`eager`** — Connect at startup but don't auto-reconnect if the connection drops. No idle timeout by default (set `idleTimeout` explicitly to enable).
|
|
141
141
|
- **`keep-alive`** — Connect at startup. Auto-reconnect via health checks. No idle timeout. Use for servers you always need available.
|
|
142
142
|
|
|
@@ -232,7 +232,7 @@ To exclude specific tools while still using `directTools: true`, add `excludeToo
|
|
|
232
232
|
|
|
233
233
|
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[]`.
|
|
234
234
|
|
|
235
|
-
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
|
|
235
|
+
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 when the cache is warm. On the first session after adding `directTools` to a new server, or when a child/subagent selects tools through `MCP_DIRECT_TOOLS`, the cache may not exist yet — tools fall back to proxy-only while the selected/configured direct-tool servers populate in the background, then the extension refreshes tool registration so the warmed direct tools become available in the current session. To force it immediately: `/mcp reconnect <server>`.
|
|
236
236
|
|
|
237
237
|
When you change direct-tool toggles in `/mcp` or write new config through `/mcp setup`, the extension triggers Pi's normal reload flow automatically. That refreshes extensions, prompts, skills, and MCP tool registration in one shot, so newly configured direct tools can appear without a manual restart.
|
|
238
238
|
|
|
@@ -332,7 +332,7 @@ Prefer `.mcp.json` for project-local shared MCP config. Use `.pi/mcp.json` only
|
|
|
332
332
|
| Connect | `mcp({ connect: "server-name" })` |
|
|
333
333
|
| UI messages | `mcp({ action: "ui-messages" })` |
|
|
334
334
|
|
|
335
|
-
MCP proxy and direct-tool results render compactly by default: long text shows the first three lines plus a `
|
|
335
|
+
MCP proxy and direct-tool results render compactly by default: long text shows the first three lines plus a `Ctrl+o Expand` hint, while the full result remains available when expanded and is still returned unchanged to the model.
|
|
336
336
|
|
|
337
337
|
Search includes both MCP tools and Pi tools (from extensions). Pi tools appear first with `[pi tool]` prefix. Space-separated words are OR'd.
|
|
338
338
|
|
|
@@ -358,8 +358,8 @@ In interactive sessions, you can also authenticate from `/mcp` with `CTRL+A` or
|
|
|
358
358
|
## How It Works
|
|
359
359
|
|
|
360
360
|
- One `mcp` tool in context (~200 tokens) instead of hundreds
|
|
361
|
-
- Servers are lazy by default — they connect on first tool call, not at startup
|
|
362
|
-
- Tool metadata is cached to disk so search/list/describe work without live connections
|
|
361
|
+
- Servers are lazy by default — they connect on first tool call or explicit cold-cache proxy metadata request, not at startup
|
|
362
|
+
- Tool metadata is cached to disk so search/list/describe work without live connections when the cache is valid; cold-cache explicit search/describe/server-list requests hydrate metadata on demand
|
|
363
363
|
- Idle servers disconnect after 10 minutes (configurable), reconnect automatically on next use
|
|
364
364
|
- npx-based servers resolve to direct binary paths, skipping the ~143 MB npm parent process
|
|
365
365
|
- MCP server validates arguments, not the adapter
|
|
@@ -72,6 +72,37 @@ async function attemptDirectAutoAuth(
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
interface DirectToolSelection {
|
|
76
|
+
readonly servers: ReadonlySet<string>;
|
|
77
|
+
readonly toolsByServer: ReadonlyMap<string, ReadonlySet<string>>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseDirectToolSelection(items: readonly string[] | undefined): DirectToolSelection {
|
|
81
|
+
const servers = new Set<string>();
|
|
82
|
+
const toolsByServer = new Map<string, Set<string>>();
|
|
83
|
+
for (const rawItem of items ?? []) {
|
|
84
|
+
const item = rawItem.replace(/\/+$/, "");
|
|
85
|
+
if (!item) continue;
|
|
86
|
+
if (!item.includes("/")) {
|
|
87
|
+
servers.add(item);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const [server, tool] = item.split("/", 2);
|
|
91
|
+
if (!server) continue;
|
|
92
|
+
if (!tool) {
|
|
93
|
+
servers.add(server);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!toolsByServer.has(server)) toolsByServer.set(server, new Set());
|
|
97
|
+
toolsByServer.get(server)!.add(tool);
|
|
98
|
+
}
|
|
99
|
+
return { servers, toolsByServer };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function directToolSelectionIncludes(selection: DirectToolSelection, serverName: string): boolean {
|
|
103
|
+
return selection.servers.has(serverName) || selection.toolsByServer.has(serverName);
|
|
104
|
+
}
|
|
105
|
+
|
|
75
106
|
export function resolveDirectTools(
|
|
76
107
|
config: McpConfig,
|
|
77
108
|
cache: MetadataCache | null,
|
|
@@ -83,24 +114,7 @@ export function resolveDirectTools(
|
|
|
83
114
|
|
|
84
115
|
const seenNames = new Set<string>();
|
|
85
116
|
|
|
86
|
-
const
|
|
87
|
-
const envTools = new Map<string, Set<string>>();
|
|
88
|
-
if (envOverride) {
|
|
89
|
-
for (let item of envOverride) {
|
|
90
|
-
item = item.replace(/\/+$/, "");
|
|
91
|
-
if (item.includes("/")) {
|
|
92
|
-
const [server, tool] = item.split("/", 2);
|
|
93
|
-
if (server && tool) {
|
|
94
|
-
if (!envTools.has(server)) envTools.set(server, new Set());
|
|
95
|
-
envTools.get(server)!.add(tool);
|
|
96
|
-
} else if (server) {
|
|
97
|
-
envServers.add(server);
|
|
98
|
-
}
|
|
99
|
-
} else if (item) {
|
|
100
|
-
envServers.add(item);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
117
|
+
const envSelection = parseDirectToolSelection(envOverride);
|
|
104
118
|
|
|
105
119
|
const globalDirect = config.settings?.directTools;
|
|
106
120
|
|
|
@@ -111,10 +125,10 @@ export function resolveDirectTools(
|
|
|
111
125
|
let toolFilter: true | string[] | false = false;
|
|
112
126
|
|
|
113
127
|
if (envOverride) {
|
|
114
|
-
if (
|
|
128
|
+
if (envSelection.servers.has(serverName)) {
|
|
115
129
|
toolFilter = true;
|
|
116
|
-
} else if (
|
|
117
|
-
toolFilter = [...
|
|
130
|
+
} else if (envSelection.toolsByServer.has(serverName)) {
|
|
131
|
+
toolFilter = [...envSelection.toolsByServer.get(serverName)!];
|
|
118
132
|
}
|
|
119
133
|
} else {
|
|
120
134
|
if (definition.directTools !== undefined) {
|
|
@@ -182,14 +196,18 @@ export function resolveDirectTools(
|
|
|
182
196
|
export function getMissingConfiguredDirectToolServers(
|
|
183
197
|
config: McpConfig,
|
|
184
198
|
cache: MetadataCache | null,
|
|
199
|
+
envOverride?: readonly string[],
|
|
185
200
|
): string[] {
|
|
186
201
|
const missing: string[] = [];
|
|
187
202
|
const globalDirect = config.settings?.directTools;
|
|
188
|
-
|
|
203
|
+
const envSelection = parseDirectToolSelection(envOverride);
|
|
189
204
|
for (const [serverName, definition] of Object.entries(config.mcpServers)) {
|
|
190
|
-
const
|
|
205
|
+
const hasConfiguredDirectTools = definition.directTools !== undefined
|
|
191
206
|
? !!definition.directTools
|
|
192
207
|
: !!globalDirect;
|
|
208
|
+
const hasDirectTools = envOverride
|
|
209
|
+
? directToolSelectionIncludes(envSelection, serverName)
|
|
210
|
+
: hasConfiguredDirectTools;
|
|
193
211
|
|
|
194
212
|
if (!hasDirectTools) continue;
|
|
195
213
|
|
|
@@ -39,6 +39,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
39
39
|
let lifecycleGeneration = 0;
|
|
40
40
|
let registeredDirectToolNames = new Set<string>();
|
|
41
41
|
let registeredProxyTool = false;
|
|
42
|
+
let startupWarmupCancel: (() => void) | null = null;
|
|
42
43
|
|
|
43
44
|
async function registerDirectToolsFromConfig(
|
|
44
45
|
config: McpConfig,
|
|
@@ -50,13 +51,14 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
50
51
|
]);
|
|
51
52
|
const prefix = config.settings?.toolPrefix ?? "server";
|
|
52
53
|
const envRaw = process.env.MCP_DIRECT_TOOLS;
|
|
54
|
+
const envDirectTools = envRaw?.split(",").map(s => s.trim()).filter(Boolean);
|
|
53
55
|
const directSpecs = envRaw === "__none__"
|
|
54
56
|
? []
|
|
55
57
|
: resolveDirectTools(
|
|
56
58
|
config,
|
|
57
59
|
cache,
|
|
58
60
|
prefix,
|
|
59
|
-
|
|
61
|
+
envDirectTools,
|
|
60
62
|
);
|
|
61
63
|
for (const spec of directSpecs) {
|
|
62
64
|
if (registeredDirectToolNames.has(spec.prefixedName)) continue;
|
|
@@ -75,7 +77,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
75
77
|
refreshTools?.();
|
|
76
78
|
return {
|
|
77
79
|
directToolCount: directSpecs.length,
|
|
78
|
-
missingConfiguredDirectToolServers: getMissingConfiguredDirectToolServers(config, cache),
|
|
80
|
+
missingConfiguredDirectToolServers: getMissingConfiguredDirectToolServers(config, cache, envDirectTools),
|
|
79
81
|
};
|
|
80
82
|
}
|
|
81
83
|
|
|
@@ -129,12 +131,18 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
129
131
|
type: "string",
|
|
130
132
|
});
|
|
131
133
|
|
|
134
|
+
function cancelStartupWarmup(): void {
|
|
135
|
+
startupWarmupCancel?.();
|
|
136
|
+
startupWarmupCancel = null;
|
|
137
|
+
}
|
|
138
|
+
|
|
132
139
|
pi.on("session_start", async (_event, ctx) => {
|
|
133
140
|
const generation = ++lifecycleGeneration;
|
|
134
141
|
const previousState = state;
|
|
135
142
|
state = null;
|
|
136
143
|
initPromise = null;
|
|
137
144
|
registeredDirectToolNames = new Set<string>();
|
|
145
|
+
cancelStartupWarmup();
|
|
138
146
|
|
|
139
147
|
try {
|
|
140
148
|
const config = loadMcpConfig(earlyConfigPath, ctx.cwd);
|
|
@@ -168,7 +176,10 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
168
176
|
throw new Error("Stale MCP session initialization cancelled before startup");
|
|
169
177
|
}
|
|
170
178
|
|
|
171
|
-
const { initializeMcp, updateStatusBar } = await
|
|
179
|
+
const [{ initializeMcp, updateStatusBar }, { scheduleMcpStartupWarmup }] = await Promise.all([
|
|
180
|
+
import("./init.ts"),
|
|
181
|
+
import("./startup-warmup.ts"),
|
|
182
|
+
]);
|
|
172
183
|
if (generation !== lifecycleGeneration || !isContextActive(ctx)) {
|
|
173
184
|
throw new Error("Stale MCP session initialization cancelled before startup");
|
|
174
185
|
}
|
|
@@ -193,6 +204,21 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
193
204
|
) {
|
|
194
205
|
registerProxyTool();
|
|
195
206
|
}
|
|
207
|
+
let cancelWarmup: (() => void) | null = null;
|
|
208
|
+
const warmup = scheduleMcpStartupWarmup(nextState, {
|
|
209
|
+
shouldContinue: () => generation === lifecycleGeneration && state === nextState,
|
|
210
|
+
onDirectToolsChanged: async () => {
|
|
211
|
+
if (generation !== lifecycleGeneration || state !== nextState) return;
|
|
212
|
+
await registerDirectTools(nextState);
|
|
213
|
+
},
|
|
214
|
+
onSettled: () => {
|
|
215
|
+
if (generation === lifecycleGeneration && state === nextState && startupWarmupCancel === cancelWarmup) {
|
|
216
|
+
startupWarmupCancel = null;
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
cancelWarmup = () => warmup.cancel();
|
|
221
|
+
startupWarmupCancel = cancelWarmup;
|
|
196
222
|
if (initPromise === promiseRef.current) {
|
|
197
223
|
initPromise = null;
|
|
198
224
|
}
|
|
@@ -226,6 +252,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
226
252
|
state = null;
|
|
227
253
|
initPromise = null;
|
|
228
254
|
registeredDirectToolNames = new Set<string>();
|
|
255
|
+
cancelStartupWarmup();
|
|
229
256
|
|
|
230
257
|
try {
|
|
231
258
|
await Promise.all([
|
|
@@ -409,7 +436,7 @@ export default function mcpAdapter(pi: ExtensionAPI) {
|
|
|
409
436
|
return executeConnect(state, params.connect);
|
|
410
437
|
}
|
|
411
438
|
if (params.describe) {
|
|
412
|
-
return executeDescribe(state, params.describe);
|
|
439
|
+
return executeDescribe(state, params.describe, params.server);
|
|
413
440
|
}
|
|
414
441
|
if (params.search) {
|
|
415
442
|
return executeSearch(state, params.search, params.regex, params.server, params.includeSchemas);
|
package/dist/builtin/mcp/init.ts
CHANGED
|
@@ -21,7 +21,6 @@ import { buildToolMetadata, totalToolCount } from "./tool-metadata.ts";
|
|
|
21
21
|
import { UiResourceHandler } from "./ui-resource-handler.ts";
|
|
22
22
|
import { openUrl, parallelLimit } from "./utils.ts";
|
|
23
23
|
import { logger } from "./logger.ts";
|
|
24
|
-
import { getMissingConfiguredDirectToolServers } from "./direct-tools.ts";
|
|
25
24
|
|
|
26
25
|
const FAILURE_BACKOFF_MS = 60 * 1000;
|
|
27
26
|
|
|
@@ -75,10 +74,7 @@ export async function initializeMcp(
|
|
|
75
74
|
const cachePath = getMetadataCachePath();
|
|
76
75
|
const cacheFileExists = existsSync(cachePath);
|
|
77
76
|
let cache = loadMetadataCache();
|
|
78
|
-
let bootstrapAll = false;
|
|
79
|
-
|
|
80
77
|
if (!cacheFileExists) {
|
|
81
|
-
bootstrapAll = true;
|
|
82
78
|
saveMetadataCache({ version: 1, servers: {} });
|
|
83
79
|
} else if (!cache) {
|
|
84
80
|
cache = { version: 1, servers: {} };
|
|
@@ -105,12 +101,10 @@ export async function initializeMcp(
|
|
|
105
101
|
}
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
const startupServers =
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return mode === "keep-alive" || mode === "eager";
|
|
113
|
-
});
|
|
104
|
+
const startupServers = serverEntries.filter(([, definition]) => {
|
|
105
|
+
const mode = definition.lifecycle ?? "lazy";
|
|
106
|
+
return mode === "keep-alive" || mode === "eager";
|
|
107
|
+
});
|
|
114
108
|
|
|
115
109
|
if (ctx.hasUI && startupServers.length > 0) {
|
|
116
110
|
ctx.ui.setStatus("mcp", `MCP: connecting to ${startupServers.length} servers...`);
|
|
@@ -160,39 +154,6 @@ export async function initializeMcp(
|
|
|
160
154
|
ctx.ui.notify(msg, "info");
|
|
161
155
|
}
|
|
162
156
|
|
|
163
|
-
const envDirect = process.env.MCP_DIRECT_TOOLS;
|
|
164
|
-
if (envDirect !== "__none__") {
|
|
165
|
-
const currentCache = loadMetadataCache();
|
|
166
|
-
const missingCacheServers = getMissingConfiguredDirectToolServers(config, currentCache);
|
|
167
|
-
|
|
168
|
-
if (missingCacheServers.length > 0) {
|
|
169
|
-
const bootstrapResults = await parallelLimit(
|
|
170
|
-
missingCacheServers.filter(name => !results.some(r => r.name === name && r.connection)),
|
|
171
|
-
10,
|
|
172
|
-
async (name) => {
|
|
173
|
-
const definition = config.mcpServers[name];
|
|
174
|
-
try {
|
|
175
|
-
const connection = await manager.connect(name, definition);
|
|
176
|
-
if (connection.status === "needs-auth") {
|
|
177
|
-
return { name, ok: false };
|
|
178
|
-
}
|
|
179
|
-
const { metadata } = buildToolMetadata(connection.tools, connection.resources, definition, name, prefix);
|
|
180
|
-
toolMetadata.set(name, metadata);
|
|
181
|
-
updateMetadataCache(state, name);
|
|
182
|
-
return { name, ok: true };
|
|
183
|
-
} catch (error) {
|
|
184
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
185
|
-
logger.debug(`MCP: direct-tools bootstrap failed for ${name}: ${message}`);
|
|
186
|
-
return { name, ok: false };
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
);
|
|
190
|
-
const bootstrapped = bootstrapResults.filter(r => r.ok).map(r => r.name);
|
|
191
|
-
if (bootstrapped.length > 0 && ctx.hasUI) {
|
|
192
|
-
ctx.ui.notify(`MCP: direct tools for ${bootstrapped.join(", ")} will be available after restart`, "info");
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
157
|
|
|
197
158
|
lifecycle.setReconnectCallback((serverName) => {
|
|
198
159
|
updateServerMetadata(state, serverName);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { McpExtensionState } from "./state.ts";
|
|
2
|
+
import { lazyConnect, updateMetadataCache, updateServerMetadata } from "./init.ts";
|
|
3
|
+
import { parallelLimit } from "./utils.ts";
|
|
4
|
+
|
|
5
|
+
export async function hydrateServerMetadata(
|
|
6
|
+
state: McpExtensionState,
|
|
7
|
+
serverName: string,
|
|
8
|
+
): Promise<boolean> {
|
|
9
|
+
if (state.toolMetadata.has(serverName)) return true;
|
|
10
|
+
if (!state.config.mcpServers[serverName]) return false;
|
|
11
|
+
|
|
12
|
+
const connection = state.manager.getConnection(serverName);
|
|
13
|
+
if (connection?.status === "connected") {
|
|
14
|
+
updateServerMetadata(state, serverName);
|
|
15
|
+
updateMetadataCache(state, serverName);
|
|
16
|
+
return state.toolMetadata.has(serverName);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return lazyConnect(state, serverName);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function hydrateMissingMetadata(
|
|
23
|
+
state: McpExtensionState,
|
|
24
|
+
options?: { server?: string },
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
if (options?.server) {
|
|
27
|
+
await hydrateServerMetadata(state, options.server);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const missingServers = Object.keys(state.config.mcpServers).filter(
|
|
32
|
+
(serverName) => !state.toolMetadata.has(serverName),
|
|
33
|
+
);
|
|
34
|
+
await parallelLimit(missingServers, 10, async (serverName) => {
|
|
35
|
+
await hydrateServerMetadata(state, serverName);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/mcp",
|
|
3
|
-
"version": "0.9.5-alpha.
|
|
3
|
+
"version": "0.9.5-alpha.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
|
|
6
6
|
"contributors": [
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { McpExtensionState } from "./state.ts";
|
|
2
2
|
import type { ToolMetadata } from "./types.ts";
|
|
3
|
-
import { parseUiPromptHandoff } from "./types.ts";
|
|
3
|
+
import { getServerPrefix, parseUiPromptHandoff } from "./types.ts";
|
|
4
4
|
import { getFailureAgeSeconds } from "./init.ts";
|
|
5
5
|
import { findToolByName, formatSchema } from "./tool-metadata.ts";
|
|
6
6
|
import { truncateAtWord } from "./utils.ts";
|
|
7
|
+
import { hydrateMissingMetadata } from "./metadata-hydration.ts";
|
|
7
8
|
import type { ProxyToolResult } from "./proxy-types.ts";
|
|
8
9
|
|
|
9
10
|
export function executeUiMessages(state: McpExtensionState): ProxyToolResult {
|
|
@@ -140,26 +141,73 @@ export function executeStatus(state: McpExtensionState): ProxyToolResult {
|
|
|
140
141
|
};
|
|
141
142
|
}
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
function findToolMetadata(
|
|
145
|
+
state: McpExtensionState,
|
|
146
|
+
toolName: string,
|
|
147
|
+
server?: string,
|
|
148
|
+
): { serverName: string; toolMeta: ToolMetadata } | null {
|
|
149
|
+
for (const [serverName, metadata] of state.toolMetadata.entries()) {
|
|
150
|
+
if (server && serverName !== server) continue;
|
|
151
|
+
const toolMeta = findToolByName(metadata, toolName);
|
|
152
|
+
if (toolMeta) return { serverName, toolMeta };
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
146
156
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
157
|
+
function normalizeToolAlias(value: string): string {
|
|
158
|
+
return value.replace(/-/g, "_");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function prefixHydrationCandidates(state: McpExtensionState, toolName: string): string[] {
|
|
162
|
+
const prefixMode = state.config.settings?.toolPrefix ?? "server";
|
|
163
|
+
if (prefixMode === "none") return [];
|
|
164
|
+
const normalizedToolName = normalizeToolAlias(toolName);
|
|
165
|
+
return Object.keys(state.config.mcpServers)
|
|
166
|
+
.map((serverName) => ({ serverName, prefix: normalizeToolAlias(getServerPrefix(serverName, prefixMode)) }))
|
|
167
|
+
.filter(({ prefix }) => prefix.length > 0 && normalizedToolName.startsWith(`${prefix}_`))
|
|
168
|
+
.sort((a, b) => b.prefix.length - a.prefix.length)
|
|
169
|
+
.map(({ serverName }) => serverName);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function hydrateDescribeMetadata(state: McpExtensionState, toolName: string, server?: string): Promise<void> {
|
|
173
|
+
if (server) {
|
|
174
|
+
await hydrateMissingMetadata(state, { server });
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const candidates = prefixHydrationCandidates(state, toolName);
|
|
179
|
+
if (candidates.length > 0) {
|
|
180
|
+
for (const candidate of candidates) {
|
|
181
|
+
await hydrateMissingMetadata(state, { server: candidate });
|
|
182
|
+
if (findToolMetadata(state, toolName, candidate)) return;
|
|
153
183
|
}
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (findToolMetadata(state, toolName)) return;
|
|
188
|
+
await hydrateMissingMetadata(state);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function executeDescribe(
|
|
192
|
+
state: McpExtensionState,
|
|
193
|
+
toolName: string,
|
|
194
|
+
server?: string,
|
|
195
|
+
): Promise<ProxyToolResult> {
|
|
196
|
+
let found = findToolMetadata(state, toolName, server);
|
|
197
|
+
if (!found) {
|
|
198
|
+
await hydrateDescribeMetadata(state, toolName, server);
|
|
199
|
+
found = findToolMetadata(state, toolName, server);
|
|
154
200
|
}
|
|
155
201
|
|
|
156
|
-
if (!
|
|
202
|
+
if (!found) {
|
|
157
203
|
return {
|
|
158
204
|
content: [{ type: "text" as const, text: `Tool "${toolName}" not found. Use mcp({ search: "..." }) to search.` }],
|
|
159
205
|
details: { mode: "describe", error: "tool_not_found", requestedTool: toolName },
|
|
160
206
|
};
|
|
161
207
|
}
|
|
162
208
|
|
|
209
|
+
const { serverName, toolMeta } = found;
|
|
210
|
+
|
|
163
211
|
let text = `${toolMeta.name}\n`;
|
|
164
212
|
text += `Server: ${serverName}\n`;
|
|
165
213
|
if (toolMeta.resourceUri) {
|
|
@@ -181,13 +229,13 @@ export function executeDescribe(state: McpExtensionState, toolName: string): Pro
|
|
|
181
229
|
};
|
|
182
230
|
}
|
|
183
231
|
|
|
184
|
-
export function executeSearch(
|
|
232
|
+
export async function executeSearch(
|
|
185
233
|
state: McpExtensionState,
|
|
186
234
|
query: string,
|
|
187
235
|
regex?: boolean,
|
|
188
236
|
server?: string,
|
|
189
237
|
includeSchemas?: boolean,
|
|
190
|
-
): ProxyToolResult {
|
|
238
|
+
): Promise<ProxyToolResult> {
|
|
191
239
|
const showSchemas = includeSchemas !== false;
|
|
192
240
|
|
|
193
241
|
const matches: Array<{ server: string; tool: ToolMetadata }> = [];
|
|
@@ -213,6 +261,7 @@ export function executeSearch(
|
|
|
213
261
|
details: { mode: "search", error: "invalid_pattern", query },
|
|
214
262
|
};
|
|
215
263
|
}
|
|
264
|
+
await hydrateMissingMetadata(state, { server });
|
|
216
265
|
|
|
217
266
|
for (const [serverName, metadata] of state.toolMetadata.entries()) {
|
|
218
267
|
if (server && serverName !== server) continue;
|
|
@@ -270,7 +319,7 @@ export function executeSearch(
|
|
|
270
319
|
};
|
|
271
320
|
}
|
|
272
321
|
|
|
273
|
-
export function executeList(state: McpExtensionState, server: string): ProxyToolResult {
|
|
322
|
+
export async function executeList(state: McpExtensionState, server: string): Promise<ProxyToolResult> {
|
|
274
323
|
if (!state.config.mcpServers[server]) {
|
|
275
324
|
return {
|
|
276
325
|
content: [{ type: "text" as const, text: `Server "${server}" not found. Use mcp({}) to see available servers.` }],
|
|
@@ -278,6 +327,10 @@ export function executeList(state: McpExtensionState, server: string): ProxyTool
|
|
|
278
327
|
};
|
|
279
328
|
}
|
|
280
329
|
|
|
330
|
+
if (!state.toolMetadata.has(server)) {
|
|
331
|
+
await hydrateMissingMetadata(state, { server });
|
|
332
|
+
}
|
|
333
|
+
|
|
281
334
|
const metadata = state.toolMetadata.get(server);
|
|
282
335
|
const toolNames = metadata?.map(m => m.name) ?? [];
|
|
283
336
|
const connection = state.manager.getConnection(server);
|