@vibesdotdev/mcp 0.0.0-stage → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +18 -0
- package/README.md +96 -2
- package/dist/adapters/auth-middleware.d.ts +68 -0
- package/dist/adapters/auth-middleware.d.ts.map +1 -0
- package/dist/adapters/auth-middleware.js +161 -0
- package/dist/adapters/auth-middleware.js.map +1 -0
- package/dist/adapters/compatibility.d.ts +20 -0
- package/dist/adapters/compatibility.d.ts.map +1 -0
- package/dist/adapters/compatibility.js +57 -0
- package/dist/adapters/compatibility.js.map +1 -0
- package/dist/adapters/host-context.consumer.d.ts +12 -0
- package/dist/adapters/host-context.consumer.d.ts.map +1 -0
- package/dist/adapters/host-context.consumer.js +10 -0
- package/dist/adapters/host-context.consumer.js.map +1 -0
- package/dist/adapters/host-context.d.ts +32 -0
- package/dist/adapters/host-context.d.ts.map +1 -0
- package/dist/adapters/host-context.js +17 -0
- package/dist/adapters/host-context.js.map +1 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +13 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mcp/ambient-envelope.d.ts +26 -0
- package/dist/adapters/mcp/ambient-envelope.d.ts.map +1 -0
- package/dist/adapters/mcp/ambient-envelope.js +70 -0
- package/dist/adapters/mcp/ambient-envelope.js.map +1 -0
- package/dist/adapters/mcp/catalog.d.ts +15 -0
- package/dist/adapters/mcp/catalog.d.ts.map +1 -0
- package/dist/adapters/mcp/catalog.js +26 -0
- package/dist/adapters/mcp/catalog.js.map +1 -0
- package/dist/adapters/mcp/connection-identity.d.ts +81 -0
- package/dist/adapters/mcp/connection-identity.d.ts.map +1 -0
- package/dist/adapters/mcp/connection-identity.js +114 -0
- package/dist/adapters/mcp/connection-identity.js.map +1 -0
- package/dist/adapters/mcp/context.d.ts +137 -0
- package/dist/adapters/mcp/context.d.ts.map +1 -0
- package/dist/adapters/mcp/context.js +230 -0
- package/dist/adapters/mcp/context.js.map +1 -0
- package/dist/adapters/mcp/formatters.d.ts +41 -0
- package/dist/adapters/mcp/formatters.d.ts.map +1 -0
- package/dist/adapters/mcp/formatters.js +102 -0
- package/dist/adapters/mcp/formatters.js.map +1 -0
- package/dist/adapters/mcp/from-api-endpoint.d.ts +59 -0
- package/dist/adapters/mcp/from-api-endpoint.d.ts.map +1 -0
- package/dist/adapters/mcp/from-api-endpoint.js +206 -0
- package/dist/adapters/mcp/from-api-endpoint.js.map +1 -0
- package/dist/adapters/mcp/generation-contract.d.ts +5 -0
- package/dist/adapters/mcp/generation-contract.d.ts.map +1 -0
- package/dist/adapters/mcp/generation-contract.js +34 -0
- package/dist/adapters/mcp/generation-contract.js.map +1 -0
- package/dist/adapters/mcp/host-registry.d.ts +159 -0
- package/dist/adapters/mcp/host-registry.d.ts.map +1 -0
- package/dist/adapters/mcp/host-registry.js +237 -0
- package/dist/adapters/mcp/host-registry.js.map +1 -0
- package/dist/adapters/mcp/index.d.ts +12 -0
- package/dist/adapters/mcp/index.d.ts.map +1 -0
- package/dist/adapters/mcp/index.js +17 -0
- package/dist/adapters/mcp/index.js.map +1 -0
- package/dist/adapters/mcp/isolated-notice.d.ts +111 -0
- package/dist/adapters/mcp/isolated-notice.d.ts.map +1 -0
- package/dist/adapters/mcp/isolated-notice.js +217 -0
- package/dist/adapters/mcp/isolated-notice.js.map +1 -0
- package/dist/adapters/mcp/json-rpc.d.ts +16 -0
- package/dist/adapters/mcp/json-rpc.d.ts.map +1 -0
- package/dist/adapters/mcp/json-rpc.js +32 -0
- package/dist/adapters/mcp/json-rpc.js.map +1 -0
- package/dist/adapters/mcp/local-origin.d.ts +18 -0
- package/dist/adapters/mcp/local-origin.d.ts.map +1 -0
- package/dist/adapters/mcp/local-origin.js +37 -0
- package/dist/adapters/mcp/local-origin.js.map +1 -0
- package/dist/adapters/mcp/logging/mcp-cloud.descriptor.d.ts +11 -0
- package/dist/adapters/mcp/logging/mcp-cloud.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/logging/mcp-cloud.descriptor.js +21 -0
- package/dist/adapters/mcp/logging/mcp-cloud.descriptor.js.map +1 -0
- package/dist/adapters/mcp/logging/mcp-http.descriptor.d.ts +10 -0
- package/dist/adapters/mcp/logging/mcp-http.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/logging/mcp-http.descriptor.js +20 -0
- package/dist/adapters/mcp/logging/mcp-http.descriptor.js.map +1 -0
- package/dist/adapters/mcp/logging/mcp-server.descriptor.d.ts +9 -0
- package/dist/adapters/mcp/logging/mcp-server.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/logging/mcp-server.descriptor.js +25 -0
- package/dist/adapters/mcp/logging/mcp-server.descriptor.js.map +1 -0
- package/dist/adapters/mcp/logging/mcp-stdio.descriptor.d.ts +10 -0
- package/dist/adapters/mcp/logging/mcp-stdio.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/logging/mcp-stdio.descriptor.js +20 -0
- package/dist/adapters/mcp/logging/mcp-stdio.descriptor.js.map +1 -0
- package/dist/adapters/mcp/mcp-http-session.d.ts +140 -0
- package/dist/adapters/mcp/mcp-http-session.d.ts.map +1 -0
- package/dist/adapters/mcp/mcp-http-session.js +797 -0
- package/dist/adapters/mcp/mcp-http-session.js.map +1 -0
- package/dist/adapters/mcp/mcp-wire-name.d.ts +9 -0
- package/dist/adapters/mcp/mcp-wire-name.d.ts.map +1 -0
- package/dist/adapters/mcp/mcp-wire-name.js +52 -0
- package/dist/adapters/mcp/mcp-wire-name.js.map +1 -0
- package/dist/adapters/mcp/projection-doctor.d.ts +101 -0
- package/dist/adapters/mcp/projection-doctor.d.ts.map +1 -0
- package/dist/adapters/mcp/projection-doctor.js +285 -0
- package/dist/adapters/mcp/projection-doctor.js.map +1 -0
- package/dist/adapters/mcp/projection.d.ts +36 -0
- package/dist/adapters/mcp/projection.d.ts.map +1 -0
- package/dist/adapters/mcp/projection.js +136 -0
- package/dist/adapters/mcp/projection.js.map +1 -0
- package/dist/adapters/mcp/prompts/continuity-wake.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/continuity-wake.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/continuity-wake.prompt.descriptor.js +15 -0
- package/dist/adapters/mcp/prompts/continuity-wake.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/empty-tool-kind.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/empty-tool-kind.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/empty-tool-kind.prompt.descriptor.js +18 -0
- package/dist/adapters/mcp/prompts/empty-tool-kind.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/index.d.ts +113 -0
- package/dist/adapters/mcp/prompts/index.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/index.js +24 -0
- package/dist/adapters/mcp/prompts/index.js.map +1 -0
- package/dist/adapters/mcp/prompts/isolated-runtime.prompt.descriptor.d.ts +114 -0
- package/dist/adapters/mcp/prompts/isolated-runtime.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/isolated-runtime.prompt.descriptor.js +37 -0
- package/dist/adapters/mcp/prompts/isolated-runtime.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/local-tools.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/local-tools.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/local-tools.prompt.descriptor.js +20 -0
- package/dist/adapters/mcp/prompts/local-tools.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/max-tools.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/max-tools.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/max-tools.prompt.descriptor.js +20 -0
- package/dist/adapters/mcp/prompts/max-tools.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/private-runtime.prompt.descriptor.d.ts +114 -0
- package/dist/adapters/mcp/prompts/private-runtime.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/private-runtime.prompt.descriptor.js +21 -0
- package/dist/adapters/mcp/prompts/private-runtime.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/registration-failure.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/registration-failure.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/registration-failure.prompt.descriptor.js +19 -0
- package/dist/adapters/mcp/prompts/registration-failure.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/required-checklist.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/required-checklist.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/required-checklist.prompt.descriptor.js +18 -0
- package/dist/adapters/mcp/prompts/required-checklist.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/required-tools-missing.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/required-tools-missing.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/required-tools-missing.prompt.descriptor.js +19 -0
- package/dist/adapters/mcp/prompts/required-tools-missing.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/prompts/unknown-client.prompt.descriptor.d.ts +113 -0
- package/dist/adapters/mcp/prompts/unknown-client.prompt.descriptor.d.ts.map +1 -0
- package/dist/adapters/mcp/prompts/unknown-client.prompt.descriptor.js +18 -0
- package/dist/adapters/mcp/prompts/unknown-client.prompt.descriptor.js.map +1 -0
- package/dist/adapters/mcp/request-context.d.ts +28 -0
- package/dist/adapters/mcp/request-context.d.ts.map +1 -0
- package/dist/adapters/mcp/request-context.js +49 -0
- package/dist/adapters/mcp/request-context.js.map +1 -0
- package/dist/adapters/mcp/roster-announcer.d.ts +3 -0
- package/dist/adapters/mcp/roster-announcer.d.ts.map +1 -0
- package/dist/adapters/mcp/roster-announcer.js +22 -0
- package/dist/adapters/mcp/roster-announcer.js.map +1 -0
- package/dist/adapters/mcp/sampling-context.d.ts +12 -0
- package/dist/adapters/mcp/sampling-context.d.ts.map +1 -0
- package/dist/adapters/mcp/sampling-context.js +30 -0
- package/dist/adapters/mcp/sampling-context.js.map +1 -0
- package/dist/adapters/mcp/sampling.d.ts +17 -0
- package/dist/adapters/mcp/sampling.d.ts.map +1 -0
- package/dist/adapters/mcp/sampling.js +43 -0
- package/dist/adapters/mcp/sampling.js.map +1 -0
- package/dist/adapters/mcp/schemas/index.d.ts +37 -0
- package/dist/adapters/mcp/schemas/index.d.ts.map +1 -0
- package/dist/adapters/mcp/schemas/index.js +5 -0
- package/dist/adapters/mcp/schemas/index.js.map +1 -0
- package/dist/adapters/mcp/schemas/tool-selection.d.ts +12 -0
- package/dist/adapters/mcp/schemas/tool-selection.d.ts.map +1 -0
- package/dist/adapters/mcp/schemas/tool-selection.js +2 -0
- package/dist/adapters/mcp/schemas/tool-selection.js.map +1 -0
- package/dist/adapters/mcp/serve-identity.d.ts +64 -0
- package/dist/adapters/mcp/serve-identity.d.ts.map +1 -0
- package/dist/adapters/mcp/serve-identity.js +220 -0
- package/dist/adapters/mcp/serve-identity.js.map +1 -0
- package/dist/adapters/mcp/server.consumer.d.ts +5 -0
- package/dist/adapters/mcp/server.consumer.d.ts.map +1 -0
- package/dist/adapters/mcp/server.consumer.js +300 -0
- package/dist/adapters/mcp/server.consumer.js.map +1 -0
- package/dist/adapters/mcp/server.d.ts +56 -0
- package/dist/adapters/mcp/server.d.ts.map +1 -0
- package/dist/adapters/mcp/server.js +1423 -0
- package/dist/adapters/mcp/server.js.map +1 -0
- package/dist/adapters/mcp/stale-body.consumer.d.ts +85 -0
- package/dist/adapters/mcp/stale-body.consumer.d.ts.map +1 -0
- package/dist/adapters/mcp/stale-body.consumer.js +183 -0
- package/dist/adapters/mcp/stale-body.consumer.js.map +1 -0
- package/dist/adapters/mcp/startup-refusal.d.ts +8 -0
- package/dist/adapters/mcp/startup-refusal.d.ts.map +1 -0
- package/dist/adapters/mcp/startup-refusal.js +141 -0
- package/dist/adapters/mcp/startup-refusal.js.map +1 -0
- package/dist/adapters/mcp/stdio-relay.d.ts +232 -0
- package/dist/adapters/mcp/stdio-relay.d.ts.map +1 -0
- package/dist/adapters/mcp/stdio-relay.js +1169 -0
- package/dist/adapters/mcp/stdio-relay.js.map +1 -0
- package/dist/adapters/mcp/tool-audit-live.d.ts +109 -0
- package/dist/adapters/mcp/tool-audit-live.d.ts.map +1 -0
- package/dist/adapters/mcp/tool-audit-live.js +538 -0
- package/dist/adapters/mcp/tool-audit-live.js.map +1 -0
- package/dist/adapters/mcp/tool-audit.d.ts +44 -0
- package/dist/adapters/mcp/tool-audit.d.ts.map +1 -0
- package/dist/adapters/mcp/tool-audit.js +168 -0
- package/dist/adapters/mcp/tool-audit.js.map +1 -0
- package/dist/adapters/mcp/tool-catalog.d.ts +179 -0
- package/dist/adapters/mcp/tool-catalog.d.ts.map +1 -0
- package/dist/adapters/mcp/tool-catalog.js +330 -0
- package/dist/adapters/mcp/tool-catalog.js.map +1 -0
- package/dist/adapters/mcp/tool-selection.d.ts +77 -0
- package/dist/adapters/mcp/tool-selection.d.ts.map +1 -0
- package/dist/adapters/mcp/tool-selection.js +266 -0
- package/dist/adapters/mcp/tool-selection.js.map +1 -0
- package/dist/adapters/mcp/transport-registry.d.ts +5 -0
- package/dist/adapters/mcp/transport-registry.d.ts.map +1 -0
- package/dist/adapters/mcp/transport-registry.js +13 -0
- package/dist/adapters/mcp/transport-registry.js.map +1 -0
- package/dist/adapters/mcp/trust-tag.d.ts +69 -0
- package/dist/adapters/mcp/trust-tag.d.ts.map +1 -0
- package/dist/adapters/mcp/trust-tag.js +120 -0
- package/dist/adapters/mcp/trust-tag.js.map +1 -0
- package/dist/adapters/mcp/vibes-types.d.ts +115 -0
- package/dist/adapters/mcp/vibes-types.d.ts.map +1 -0
- package/dist/adapters/mcp/vibes-types.js +7 -0
- package/dist/adapters/mcp/vibes-types.js.map +1 -0
- package/dist/adapters/mcp/web-handler.d.ts +49 -0
- package/dist/adapters/mcp/web-handler.d.ts.map +1 -0
- package/dist/adapters/mcp/web-handler.js +129 -0
- package/dist/adapters/mcp/web-handler.js.map +1 -0
- package/dist/adapters/session-id.d.ts +3 -0
- package/dist/adapters/session-id.d.ts.map +1 -0
- package/dist/adapters/session-id.js +42 -0
- package/dist/adapters/session-id.js.map +1 -0
- package/dist/adapters/transport-bun-http.d.ts +39 -0
- package/dist/adapters/transport-bun-http.d.ts.map +1 -0
- package/dist/adapters/transport-bun-http.js +42 -0
- package/dist/adapters/transport-bun-http.js.map +1 -0
- package/dist/adapters/types.d.ts +135 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/adapters/web-compatibility.d.ts +6 -0
- package/dist/adapters/web-compatibility.d.ts.map +1 -0
- package/dist/adapters/web-compatibility.js +13 -0
- package/dist/adapters/web-compatibility.js.map +1 -0
- package/dist/adapters/zod-mcp.d.ts +139 -0
- package/dist/adapters/zod-mcp.d.ts.map +1 -0
- package/dist/adapters/zod-mcp.js +617 -0
- package/dist/adapters/zod-mcp.js.map +1 -0
- package/dist/cli/mcp/audit/dev.audit.tools.descriptor.d.ts +15 -0
- package/dist/cli/mcp/audit/dev.audit.tools.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/audit/dev.audit.tools.descriptor.js +58 -0
- package/dist/cli/mcp/audit/dev.audit.tools.descriptor.js.map +1 -0
- package/dist/cli/mcp/audit/dev.audit.tools.impl.consumer.d.ts +6 -0
- package/dist/cli/mcp/audit/dev.audit.tools.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/audit/dev.audit.tools.impl.consumer.js +180 -0
- package/dist/cli/mcp/audit/dev.audit.tools.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.descriptor.d.ts +22 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.descriptor.js +19 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.descriptor.js.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.impl.consumer.d.ts +23 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.impl.consumer.js +41 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.restart.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.serve.descriptor.d.ts +75 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.serve.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.serve.descriptor.js +84 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.serve.descriptor.js.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.descriptor.d.ts +35 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.descriptor.js +28 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.descriptor.js.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.impl.consumer.d.ts +11 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.impl.consumer.js +47 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.start.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.descriptor.d.ts +18 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.descriptor.js +18 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.descriptor.js.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.impl.consumer.d.ts +12 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.impl.consumer.js +17 -0
- package/dist/cli/mcp/lifecycle/dev.mcp.stop.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.install.descriptor.d.ts +41 -0
- package/dist/cli/mcp/management/dev.mcp.install.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.install.descriptor.js +49 -0
- package/dist/cli/mcp/management/dev.mcp.install.descriptor.js.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.install.impl.consumer.d.ts +16 -0
- package/dist/cli/mcp/management/dev.mcp.install.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.install.impl.consumer.js +244 -0
- package/dist/cli/mcp/management/dev.mcp.install.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.list.descriptor.d.ts +22 -0
- package/dist/cli/mcp/management/dev.mcp.list.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.list.descriptor.js +24 -0
- package/dist/cli/mcp/management/dev.mcp.list.descriptor.js.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.list.impl.consumer.d.ts +29 -0
- package/dist/cli/mcp/management/dev.mcp.list.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.list.impl.consumer.js +127 -0
- package/dist/cli/mcp/management/dev.mcp.list.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.status.descriptor.d.ts +48 -0
- package/dist/cli/mcp/management/dev.mcp.status.descriptor.d.ts.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.status.descriptor.js +56 -0
- package/dist/cli/mcp/management/dev.mcp.status.descriptor.js.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.status.impl.consumer.d.ts +9 -0
- package/dist/cli/mcp/management/dev.mcp.status.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/management/dev.mcp.status.impl.consumer.js +253 -0
- package/dist/cli/mcp/management/dev.mcp.status.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/management/install-helpers.d.ts +26 -0
- package/dist/cli/mcp/management/install-helpers.d.ts.map +1 -0
- package/dist/cli/mcp/management/install-helpers.js +95 -0
- package/dist/cli/mcp/management/install-helpers.js.map +1 -0
- package/dist/cli/mcp/management/process-audit.impl.consumer.d.ts +38 -0
- package/dist/cli/mcp/management/process-audit.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/management/process-audit.impl.consumer.js +145 -0
- package/dist/cli/mcp/management/process-audit.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/management/rules-debt-summary.impl.consumer.d.ts +21 -0
- package/dist/cli/mcp/management/rules-debt-summary.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/management/rules-debt-summary.impl.consumer.js +82 -0
- package/dist/cli/mcp/management/rules-debt-summary.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/management/status-doctor.d.ts +22 -0
- package/dist/cli/mcp/management/status-doctor.d.ts.map +1 -0
- package/dist/cli/mcp/management/status-doctor.js +153 -0
- package/dist/cli/mcp/management/status-doctor.js.map +1 -0
- package/dist/cli/mcp/schemas/status-doctor.d.ts +75 -0
- package/dist/cli/mcp/schemas/status-doctor.d.ts.map +1 -0
- package/dist/cli/mcp/schemas/status-doctor.js +2 -0
- package/dist/cli/mcp/schemas/status-doctor.js.map +1 -0
- package/dist/cli/mcp/support/host-port.d.ts +31 -0
- package/dist/cli/mcp/support/host-port.d.ts.map +1 -0
- package/dist/cli/mcp/support/host-port.js +31 -0
- package/dist/cli/mcp/support/host-port.js.map +1 -0
- package/dist/cli/mcp/support/server-process.impl.consumer.d.ts +24 -0
- package/dist/cli/mcp/support/server-process.impl.consumer.d.ts.map +1 -0
- package/dist/cli/mcp/support/server-process.impl.consumer.js +86 -0
- package/dist/cli/mcp/support/server-process.impl.consumer.js.map +1 -0
- package/dist/cli/mcp/support/status-doctor.d.ts +33 -0
- package/dist/cli/mcp/support/status-doctor.d.ts.map +1 -0
- package/dist/cli/mcp/support/status-doctor.js +104 -0
- package/dist/cli/mcp/support/status-doctor.js.map +1 -0
- package/dist/cli/print.d.ts +12 -0
- package/dist/cli/print.d.ts.map +1 -0
- package/dist/cli/print.js +25 -0
- package/dist/cli/print.js.map +1 -0
- package/dist/cli/surface-mcp/client-connection-identity.consumer.d.ts +19 -0
- package/dist/cli/surface-mcp/client-connection-identity.consumer.d.ts.map +1 -0
- package/dist/cli/surface-mcp/client-connection-identity.consumer.js +53 -0
- package/dist/cli/surface-mcp/client-connection-identity.consumer.js.map +1 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.cloud.d.ts +7 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.cloud.d.ts.map +1 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.cloud.js +345 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.cloud.js.map +1 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.consumer.d.ts +12 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.consumer.d.ts.map +1 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.consumer.js +657 -0
- package/dist/cli/surface-mcp/dev.mcp.serve.impl.consumer.js.map +1 -0
- package/dist/cli/surface-mcp/local-dev-flag.d.ts +24 -0
- package/dist/cli/surface-mcp/local-dev-flag.d.ts.map +1 -0
- package/dist/cli/surface-mcp/local-dev-flag.js +37 -0
- package/dist/cli/surface-mcp/local-dev-flag.js.map +1 -0
- package/dist/cli/surface-mcp/shared-host-startup.d.ts +14 -0
- package/dist/cli/surface-mcp/shared-host-startup.d.ts.map +1 -0
- package/dist/cli/surface-mcp/shared-host-startup.js +46 -0
- package/dist/cli/surface-mcp/shared-host-startup.js.map +1 -0
- package/dist/cli/surface-mcp/stdio-relay-transport.d.ts +2 -0
- package/dist/cli/surface-mcp/stdio-relay-transport.d.ts.map +1 -0
- package/dist/cli/surface-mcp/stdio-relay-transport.js +21 -0
- package/dist/cli/surface-mcp/stdio-relay-transport.js.map +1 -0
- package/dist/cli/surface-mcp/teardown.d.ts +17 -0
- package/dist/cli/surface-mcp/teardown.d.ts.map +1 -0
- package/dist/cli/surface-mcp/teardown.js +28 -0
- package/dist/cli/surface-mcp/teardown.js.map +1 -0
- package/dist/client.d.ts +49 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +131 -0
- package/dist/client.js.map +1 -0
- package/dist/config/load.d.ts +6 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +18 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/mcp.config.descriptor.d.ts +108 -0
- package/dist/config/mcp.config.descriptor.d.ts.map +1 -0
- package/dist/config/mcp.config.descriptor.js +63 -0
- package/dist/config/mcp.config.descriptor.js.map +1 -0
- package/dist/docs/mcp.docs.descriptor.d.ts +4 -0
- package/dist/docs/mcp.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/mcp.docs.descriptor.js +44 -0
- package/dist/docs/mcp.docs.descriptor.js.map +1 -0
- package/dist/docs/prompts.docs.descriptor.d.ts +4 -0
- package/dist/docs/prompts.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/prompts.docs.descriptor.js +130 -0
- package/dist/docs/prompts.docs.descriptor.js.map +1 -0
- package/dist/docs/resources.docs.descriptor.d.ts +4 -0
- package/dist/docs/resources.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/resources.docs.descriptor.js +390 -0
- package/dist/docs/resources.docs.descriptor.js.map +1 -0
- package/dist/docs/server.docs.descriptor.d.ts +4 -0
- package/dist/docs/server.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/server.docs.descriptor.js +383 -0
- package/dist/docs/server.docs.descriptor.js.map +1 -0
- package/dist/docs/tools.docs.descriptor.d.ts +4 -0
- package/dist/docs/tools.docs.descriptor.d.ts.map +1 -0
- package/dist/docs/tools.docs.descriptor.js +316 -0
- package/dist/docs/tools.docs.descriptor.js.map +1 -0
- package/dist/harness-identity.descriptors.d.ts +11 -0
- package/dist/harness-identity.descriptors.d.ts.map +1 -0
- package/dist/harness-identity.descriptors.js +41 -0
- package/dist/harness-identity.descriptors.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/kinds/harness-identity.kind.d.ts +3 -0
- package/dist/kinds/harness-identity.kind.d.ts.map +1 -0
- package/dist/kinds/harness-identity.kind.js +16 -0
- package/dist/kinds/harness-identity.kind.js.map +1 -0
- package/dist/kinds/sampling.kind.d.ts +15 -0
- package/dist/kinds/sampling.kind.d.ts.map +1 -0
- package/dist/kinds/sampling.kind.js +24 -0
- package/dist/kinds/sampling.kind.js.map +1 -0
- package/dist/lifecycle-repeat.d.ts +23 -0
- package/dist/lifecycle-repeat.d.ts.map +1 -0
- package/dist/lifecycle-repeat.js +21 -0
- package/dist/lifecycle-repeat.js.map +1 -0
- package/dist/mcp/dev.mcp.descriptor.d.ts +18 -0
- package/dist/mcp/dev.mcp.descriptor.d.ts.map +1 -0
- package/dist/mcp/dev.mcp.descriptor.js +18 -0
- package/dist/mcp/dev.mcp.descriptor.js.map +1 -0
- package/dist/mcp/index.d.ts +20 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +19 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcp.plugin.d.ts +9 -0
- package/dist/mcp/mcp.plugin.d.ts.map +1 -0
- package/dist/mcp/mcp.plugin.js +59 -0
- package/dist/mcp/mcp.plugin.js.map +1 -0
- package/dist/mcp-client.plugin.d.ts +4 -0
- package/dist/mcp-client.plugin.d.ts.map +1 -0
- package/dist/mcp-client.plugin.js +13 -0
- package/dist/mcp-client.plugin.js.map +1 -0
- package/dist/mcp.client.descriptor.d.ts +27 -0
- package/dist/mcp.client.descriptor.d.ts.map +1 -0
- package/dist/mcp.client.descriptor.js +7 -0
- package/dist/mcp.client.descriptor.js.map +1 -0
- package/dist/mcp.consumer.plugin.d.ts +4 -0
- package/dist/mcp.consumer.plugin.d.ts.map +1 -0
- package/dist/mcp.consumer.plugin.js +19 -0
- package/dist/mcp.consumer.plugin.js.map +1 -0
- package/dist/mcp.runtime.plugin.d.ts +10 -0
- package/dist/mcp.runtime.plugin.d.ts.map +1 -0
- package/dist/mcp.runtime.plugin.js +40 -0
- package/dist/mcp.runtime.plugin.js.map +1 -0
- package/dist/root-state.d.ts +3 -0
- package/dist/root-state.d.ts.map +1 -0
- package/dist/root-state.js +3 -0
- package/dist/root-state.js.map +1 -0
- package/dist/schemas/api-endpoint.d.ts +12 -0
- package/dist/schemas/api-endpoint.d.ts.map +1 -0
- package/dist/schemas/api-endpoint.js +11 -0
- package/dist/schemas/api-endpoint.js.map +1 -0
- package/dist/schemas/audit.d.ts +172 -0
- package/dist/schemas/audit.d.ts.map +1 -0
- package/dist/schemas/audit.js +82 -0
- package/dist/schemas/audit.js.map +1 -0
- package/dist/schemas/harness-identity.d.ts +41 -0
- package/dist/schemas/harness-identity.d.ts.map +1 -0
- package/dist/schemas/harness-identity.js +19 -0
- package/dist/schemas/harness-identity.js.map +1 -0
- package/dist/schemas/host-adapters.d.ts +14 -0
- package/dist/schemas/host-adapters.d.ts.map +1 -0
- package/dist/schemas/host-adapters.js +7 -0
- package/dist/schemas/host-adapters.js.map +1 -0
- package/dist/schemas/projection.d.ts +10 -0
- package/dist/schemas/projection.d.ts.map +1 -0
- package/dist/schemas/projection.js +9 -0
- package/dist/schemas/projection.js.map +1 -0
- package/dist/schemas/tool.d.ts +8 -0
- package/dist/schemas/tool.d.ts.map +1 -0
- package/dist/schemas/tool.js +8 -0
- package/dist/schemas/tool.js.map +1 -0
- package/dist/surface-mcp/build-provenance.d.ts +67 -0
- package/dist/surface-mcp/build-provenance.d.ts.map +1 -0
- package/dist/surface-mcp/build-provenance.js +287 -0
- package/dist/surface-mcp/build-provenance.js.map +1 -0
- package/dist/surface-mcp/crash-reporting.d.ts +28 -0
- package/dist/surface-mcp/crash-reporting.d.ts.map +1 -0
- package/dist/surface-mcp/crash-reporting.js +41 -0
- package/dist/surface-mcp/crash-reporting.js.map +1 -0
- package/dist/surface-mcp/executable-freshness-watch.d.ts +89 -0
- package/dist/surface-mcp/executable-freshness-watch.d.ts.map +1 -0
- package/dist/surface-mcp/executable-freshness-watch.js +225 -0
- package/dist/surface-mcp/executable-freshness-watch.js.map +1 -0
- package/dist/surface-mcp/host-provenance-banner.d.ts +49 -0
- package/dist/surface-mcp/host-provenance-banner.d.ts.map +1 -0
- package/dist/surface-mcp/host-provenance-banner.js +135 -0
- package/dist/surface-mcp/host-provenance-banner.js.map +1 -0
- package/dist/surface-mcp/index.d.ts +51 -0
- package/dist/surface-mcp/index.d.ts.map +1 -0
- package/dist/surface-mcp/index.js +92 -0
- package/dist/surface-mcp/index.js.map +1 -0
- package/dist/surface-mcp/local-tools-bootstrap.d.ts +36 -0
- package/dist/surface-mcp/local-tools-bootstrap.d.ts.map +1 -0
- package/dist/surface-mcp/local-tools-bootstrap.js +43 -0
- package/dist/surface-mcp/local-tools-bootstrap.js.map +1 -0
- package/dist/surface-mcp/mcp-serve.plugin.d.ts +9 -0
- package/dist/surface-mcp/mcp-serve.plugin.d.ts.map +1 -0
- package/dist/surface-mcp/mcp-serve.plugin.js +24 -0
- package/dist/surface-mcp/mcp-serve.plugin.js.map +1 -0
- package/dist/surface-mcp/project-config.d.ts +35 -0
- package/dist/surface-mcp/project-config.d.ts.map +1 -0
- package/dist/surface-mcp/project-config.js +74 -0
- package/dist/surface-mcp/project-config.js.map +1 -0
- package/dist/surface-mcp/serve-lifecycle.d.ts +102 -0
- package/dist/surface-mcp/serve-lifecycle.d.ts.map +1 -0
- package/dist/surface-mcp/serve-lifecycle.js +343 -0
- package/dist/surface-mcp/serve-lifecycle.js.map +1 -0
- package/dist/surface-mcp/session-bridge.d.ts +18 -0
- package/dist/surface-mcp/session-bridge.d.ts.map +1 -0
- package/dist/surface-mcp/session-bridge.js +26 -0
- package/dist/surface-mcp/session-bridge.js.map +1 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.descriptor.d.ts +13 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.descriptor.d.ts.map +1 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.descriptor.js +13 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.descriptor.js.map +1 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.impl.consumer.d.ts +11 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.impl.consumer.d.ts.map +1 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.impl.consumer.js +36 -0
- package/dist/surface-mcp/subscriptions/session.bound.workspace.impl.consumer.js.map +1 -0
- package/dist/surface-mcp/workspace-warning.d.ts +16 -0
- package/dist/surface-mcp/workspace-warning.d.ts.map +1 -0
- package/dist/surface-mcp/workspace-warning.js +44 -0
- package/dist/surface-mcp/workspace-warning.js.map +1 -0
- package/dist/svelte/index.d.ts +12 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +11 -0
- package/dist/svelte/mcp-svelte.plugin.d.ts +10 -0
- package/dist/svelte/mcp-svelte.plugin.d.ts.map +1 -0
- package/dist/svelte/mcp-svelte.plugin.js +24 -0
- package/dist/svelte/tool-widgets/VibesContextToolCard.svelte +91 -0
- package/dist/svelte/tool-widgets/VibesContextToolCard.svelte.d.ts +9 -0
- package/dist/svelte/tool-widgets/VibesContextToolCard.svelte.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/index.d.ts +3 -0
- package/dist/svelte/tool-widgets/index.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/index.js +2 -0
- package/dist/svelte/tool-widgets/summary.d.ts +48 -0
- package/dist/svelte/tool-widgets/summary.d.ts.map +1 -0
- package/dist/svelte/tool-widgets/summary.js +117 -0
- package/dist/tools/vibes-context/schemas.d.ts +85 -0
- package/dist/tools/vibes-context/schemas.d.ts.map +1 -0
- package/dist/tools/vibes-context/schemas.js +76 -0
- package/dist/tools/vibes-context/schemas.js.map +1 -0
- package/dist/tools/vibes-context/vibes-context.descriptor.d.ts +4 -0
- package/dist/tools/vibes-context/vibes-context.descriptor.d.ts.map +1 -0
- package/dist/tools/vibes-context/vibes-context.descriptor.js +30 -0
- package/dist/tools/vibes-context/vibes-context.descriptor.js.map +1 -0
- package/dist/tools/vibes-context/vibes-context.impl.d.ts +34 -0
- package/dist/tools/vibes-context/vibes-context.impl.d.ts.map +1 -0
- package/dist/tools/vibes-context/vibes-context.impl.js +207 -0
- package/dist/tools/vibes-context/vibes-context.impl.js.map +1 -0
- package/dist/tui/index.d.ts +4 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +4 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/mcp-health.d.ts +7 -0
- package/dist/tui/mcp-health.d.ts.map +1 -0
- package/dist/tui/mcp-health.js +16 -0
- package/dist/tui/mcp-health.js.map +1 -0
- package/dist/tui/mcp-tui.plugin.d.ts +3 -0
- package/dist/tui/mcp-tui.plugin.d.ts.map +1 -0
- package/dist/tui/mcp-tui.plugin.js +18 -0
- package/dist/tui/mcp-tui.plugin.js.map +1 -0
- package/dist/tui/mcp.tui-app.descriptor.d.ts +4 -0
- package/dist/tui/mcp.tui-app.descriptor.d.ts.map +1 -0
- package/dist/tui/mcp.tui-app.descriptor.js +19 -0
- package/dist/tui/mcp.tui-app.descriptor.js.map +1 -0
- package/dist/tui/mcp.tui-app.impl.consumer.d.ts +4 -0
- package/dist/tui/mcp.tui-app.impl.consumer.d.ts.map +1 -0
- package/dist/tui/mcp.tui-app.impl.consumer.js +48 -0
- package/dist/tui/mcp.tui-app.impl.consumer.js.map +1 -0
- package/dist/tui/mcp.tui-state.descriptor.d.ts +8 -0
- package/dist/tui/mcp.tui-state.descriptor.d.ts.map +1 -0
- package/dist/tui/mcp.tui-state.descriptor.js +37 -0
- package/dist/tui/mcp.tui-state.descriptor.js.map +1 -0
- package/dist/tui/mcp.tui-state.impl.consumer.d.ts +5 -0
- package/dist/tui/mcp.tui-state.impl.consumer.d.ts.map +1 -0
- package/dist/tui/mcp.tui-state.impl.consumer.js +97 -0
- package/dist/tui/mcp.tui-state.impl.consumer.js.map +1 -0
- package/dist/tui/mcp.tui-state.key.d.ts +45 -0
- package/dist/tui/mcp.tui-state.key.d.ts.map +1 -0
- package/dist/tui/mcp.tui-state.key.js +25 -0
- package/dist/tui/mcp.tui-state.key.js.map +1 -0
- package/dist/types.core.d.ts +25 -0
- package/dist/types.core.d.ts.map +1 -0
- package/dist/types.core.js +8 -0
- package/dist/types.core.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/types.registry.d.ts +93 -0
- package/dist/types.registry.d.ts.map +1 -0
- package/dist/types.registry.js +32 -0
- package/dist/types.registry.js.map +1 -0
- package/package.json +323 -4
|
@@ -0,0 +1,1423 @@
|
|
|
1
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { CallToolRequestSchema, CallToolResultSchema, TextContentSchema, ResourceLinkSchema, CreateMessageResultSchema, ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import z from 'zod/v4';
|
|
4
|
+
import { safeParseAsync } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
5
|
+
const registeredTools = new WeakMap();
|
|
6
|
+
const explicitResumeInputSchema = z.object({
|
|
7
|
+
action: z.literal('resume'),
|
|
8
|
+
sessionId: z.string().trim().min(1)
|
|
9
|
+
});
|
|
10
|
+
const receiptRestoreInputSchema = z.object({
|
|
11
|
+
action: z.literal('status'),
|
|
12
|
+
receiptOnly: z.literal(true),
|
|
13
|
+
sessionId: z.string(),
|
|
14
|
+
participantId: z.string()
|
|
15
|
+
});
|
|
16
|
+
const explicitSessionTargetSchema = z.object({ sessionId: z.string().trim().min(1) });
|
|
17
|
+
import { admitConnectionCaller } from "./connection-identity.js";
|
|
18
|
+
import { PROMPTS_MANIFEST_DESCRIPTOR_PREFIX, PROMPTS_MANIFEST_KIND_ID, promptsManifestDescriptorSchema } from '@vibesdotdev/ai-schemas';
|
|
19
|
+
import { resolvePromptText } from '@vibesdotdev/tools/prompts/manifests';
|
|
20
|
+
import { isDiscriminatedUnion, getDiscriminator, getDiscriminatorOptions, flattenDiscriminatedUnionToObject, isPlainUnion, resolveMcpDescription } from "../zod-mcp.js";
|
|
21
|
+
import { CapabilityError, CaughtFailureSchema, isCapabilityError, settle, settleNow, valueFreeFailure } from '@vibesdotdev/kernel';
|
|
22
|
+
import { JsonObjectSchema } from '@vibesdotdev/kernel/schemas/json';
|
|
23
|
+
import { redactedExcerpt } from '@vibesdotdev/shared/utils';
|
|
24
|
+
import { requireMcpClient } from "../../client.js";
|
|
25
|
+
import { resolveMcpTransportRegistry } from "./transport-registry.js";
|
|
26
|
+
import { ToolError } from '@vibesdotdev/tools/errors/tool-error';
|
|
27
|
+
import { getLogger } from '@vibesdotdev/logging';
|
|
28
|
+
import { invokeTool } from '@vibesdotdev/tools/execution/invoke';
|
|
29
|
+
import { ToolInputValidationError } from '@vibesdotdev/tools/execution/input-validation';
|
|
30
|
+
import { buildDirectToolCallContext } from '@vibesdotdev/tools/execution/direct-context';
|
|
31
|
+
import { DEFAULT_AGENT_CAPABILITY_CEILING, TOOL_AGENT_CONTEXT_METADATA_KEY } from '@vibesdotdev/tools/schemas/runtime/capability-tier';
|
|
32
|
+
import { isLocalToolId, localToolsOf } from '@vibesdotdev/tools/local/node/ensure-local-tools';
|
|
33
|
+
import { detectMcpClientId } from '@vibesdotdev/tools/schemas/presets/registry';
|
|
34
|
+
import { loadMcpConfig } from "../../config/load.js";
|
|
35
|
+
import { formatLocalMcpToolDescription, vibesToolOrigin } from "./local-origin.js";
|
|
36
|
+
import { normalizeHarness } from "./serve-identity.js";
|
|
37
|
+
import { missingRequiredTools, selectMcpTools } from "./tool-selection.js";
|
|
38
|
+
import { deriveTrustTier, wrapToolOutputText } from "./trust-tag.js";
|
|
39
|
+
import { createProjectionBirthResource, PROJECTION_BIRTH_RESOURCE_URI, projectionCatalogHash } from "./projection.js";
|
|
40
|
+
import { createMcpWireNameMap, mcpWireToolName } from "./mcp-wire-name.js";
|
|
41
|
+
import { ambientMeta, formatAmbientPrefix, liftAmbientEnvelope } from "./ambient-envelope.js";
|
|
42
|
+
import { computeMcpToolCatalog, createMcpToolCatalogCache, formatRegistrationFailureInstructions } from "./tool-catalog.js";
|
|
43
|
+
import { runWithMcpSamplingCallback } from "./sampling-context.js";
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the effective capability tier ceiling for an MCP connection from the
|
|
46
|
+
* runtime's `mcp` ConfigManifest (`clientCapabilityCeilings`).
|
|
47
|
+
*
|
|
48
|
+
* Verified transport harness identity (`admitConnectionCaller`) takes
|
|
49
|
+
* precedence over server-level `mcpClientId` and `preset`. Unconfigured
|
|
50
|
+
* profiles fall back to the tools default ceiling (`'1'`).
|
|
51
|
+
*/
|
|
52
|
+
export function resolveMcpClientCapabilityCeiling(config, input, source) {
|
|
53
|
+
const ceilings = config.clientCapabilityCeilings;
|
|
54
|
+
const candidates = [];
|
|
55
|
+
const pushCandidate = (raw) => {
|
|
56
|
+
const trimmed = raw?.trim().toLowerCase();
|
|
57
|
+
if (!trimmed)
|
|
58
|
+
return;
|
|
59
|
+
if (!candidates.includes(trimmed))
|
|
60
|
+
candidates.push(trimmed);
|
|
61
|
+
const normalized = (source ? normalizeHarness(source, trimmed) : undefined) ?? detectMcpClientId(trimmed) ?? undefined;
|
|
62
|
+
if (normalized && !candidates.includes(normalized))
|
|
63
|
+
candidates.push(normalized);
|
|
64
|
+
};
|
|
65
|
+
const verifiedHarness = input.admittedCaller?.harness;
|
|
66
|
+
if (verifiedHarness && verifiedHarness !== 'custom') {
|
|
67
|
+
pushCandidate(verifiedHarness);
|
|
68
|
+
}
|
|
69
|
+
pushCandidate(input.mcpClientId);
|
|
70
|
+
if (input.preset) {
|
|
71
|
+
pushCandidate(input.preset.replace(/^mcp:/i, ''));
|
|
72
|
+
}
|
|
73
|
+
if (verifiedHarness === 'custom') {
|
|
74
|
+
pushCandidate(verifiedHarness);
|
|
75
|
+
}
|
|
76
|
+
for (const candidate of candidates) {
|
|
77
|
+
const configured = ceilings[candidate];
|
|
78
|
+
if (configured !== undefined) {
|
|
79
|
+
return { profile: candidate, ceiling: configured };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { profile: candidates[0], ceiling: DEFAULT_AGENT_CAPABILITY_CEILING };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Project-local catalog ids (CLI `list --source=local` parity). Explicit
|
|
86
|
+
* options win (tests, serve bootstrap); otherwise the tools this runtime's
|
|
87
|
+
* discovery pass registered from `.vibes/tools`.
|
|
88
|
+
*
|
|
89
|
+
* This is the one node-scoped read in the build path, so it lives here rather
|
|
90
|
+
* than in `tool-catalog.ts`, which stays isomorphic.
|
|
91
|
+
*/
|
|
92
|
+
export function resolveLocalToolIds(runtime, options) {
|
|
93
|
+
return options.localToolIds ?? localToolsOf(runtime).toolIds;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The catalog cache a serving surface should hold: one fold of the registry,
|
|
97
|
+
* shared by every connection, re-folded the moment the registry moves.
|
|
98
|
+
*
|
|
99
|
+
* `loaderSettlement` stays `'advisory'` unless the caller can name the
|
|
100
|
+
* registration bound for its serving surface (see {@link McpLoaderSettlement}).
|
|
101
|
+
* A settled cache excludes descriptors whose loader is unresolvable at fold
|
|
102
|
+
* time, and admits them on the next fold if one registers — the revision key
|
|
103
|
+
* is the guarantee.
|
|
104
|
+
*/
|
|
105
|
+
export function createMcpServerCatalogCache(runtime, options, logger, loaderSettlement = 'advisory') {
|
|
106
|
+
return createMcpToolCatalogCache(runtime, options, logger, () => resolveLocalToolIds(runtime, options), loaderSettlement);
|
|
107
|
+
}
|
|
108
|
+
function isStringValue(value) {
|
|
109
|
+
return typeof value === 'string';
|
|
110
|
+
}
|
|
111
|
+
function asJsonObject(value) {
|
|
112
|
+
const parsed = JsonObjectSchema.safeParse(value);
|
|
113
|
+
return parsed.success ? parsed.data : undefined;
|
|
114
|
+
}
|
|
115
|
+
const JsonTextSchema = z.string();
|
|
116
|
+
/** Re-admit a JSON text: objects become structuredContent; arrays and null stay text. */
|
|
117
|
+
function wireJsonText(text) {
|
|
118
|
+
const parsed = settleNow(() => JSON.parse(text));
|
|
119
|
+
if (!parsed.ok) {
|
|
120
|
+
throw new CapabilityError('the tool output is not JSON', 'INVALID_INPUT', {
|
|
121
|
+
reason: 'mcp-tool-output-not-json',
|
|
122
|
+
cause: parsed.cause
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const structured = JsonObjectSchema.safeParse(parsed.value);
|
|
126
|
+
return structured.success ? { text, structured: structured.data } : { text };
|
|
127
|
+
}
|
|
128
|
+
const ZodIssueListSchema = z.object({
|
|
129
|
+
issues: z.array(z.object({
|
|
130
|
+
path: z.array(z.union([z.string(), z.number()])).optional(),
|
|
131
|
+
message: z.string().optional(),
|
|
132
|
+
values: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional()
|
|
133
|
+
}))
|
|
134
|
+
});
|
|
135
|
+
const ToolHookFailureSchema = z.object({
|
|
136
|
+
code: z.string().optional(),
|
|
137
|
+
recoveryHint: z.string().optional(),
|
|
138
|
+
retryable: z.boolean().optional(),
|
|
139
|
+
metadata: JsonObjectSchema.optional(),
|
|
140
|
+
alternatives: z.array(z.string()).optional()
|
|
141
|
+
});
|
|
142
|
+
const SafetyReadOnlySchema = z.object({ readOnly: z.boolean().optional() });
|
|
143
|
+
// SDK metadata and content contracts govern passthrough. Content stays required:
|
|
144
|
+
// the SDK's default empty array must not classify ordinary domain output as MCP.
|
|
145
|
+
const mcpToolResponseSchema = CallToolResultSchema.extend({
|
|
146
|
+
content: z.array(z.union([TextContentSchema, ResourceLinkSchema]))
|
|
147
|
+
});
|
|
148
|
+
function readMcpToolResponse(value) {
|
|
149
|
+
const parsed = mcpToolResponseSchema.safeParse(value);
|
|
150
|
+
return parsed.success ? parsed.data : undefined;
|
|
151
|
+
}
|
|
152
|
+
function formatZodIssues(error) {
|
|
153
|
+
const parsed = ZodIssueListSchema.safeParse(error);
|
|
154
|
+
if (!parsed.success || parsed.data.issues.length === 0)
|
|
155
|
+
return 'Invalid input';
|
|
156
|
+
return parsed.data.issues
|
|
157
|
+
.slice(0, 4)
|
|
158
|
+
.map((issue) => {
|
|
159
|
+
const path = issue.path && issue.path.length > 0 ? issue.path.join('.') : '<root>';
|
|
160
|
+
const allowed = issue.values && issue.values.length > 0
|
|
161
|
+
? ` (allowed: ${issue.values.map(String).join(', ')})`
|
|
162
|
+
: '';
|
|
163
|
+
const message = issue.message ?? 'Invalid input';
|
|
164
|
+
return `${path}: ${message}${allowed}`;
|
|
165
|
+
})
|
|
166
|
+
.join('; ');
|
|
167
|
+
}
|
|
168
|
+
/** The most characters of a typed failure's text the model sees. */
|
|
169
|
+
const MODEL_FAILURE_TEXT_MAX_CHARS = 4000;
|
|
170
|
+
/**
|
|
171
|
+
* The text a tool failure may put in the model's context. Typed failures
|
|
172
|
+
* (CapabilityError, input validation, ToolError) carry deliberate, value-free
|
|
173
|
+
* messages and pass the shared credential redactor; anything else is a fixed
|
|
174
|
+
* INTERNAL sentence naming only its value-free facts. The raw text goes to the
|
|
175
|
+
* log as a redacted excerpt, never to the model.
|
|
176
|
+
*/
|
|
177
|
+
function modelSafeFailureText(error) {
|
|
178
|
+
if (error instanceof ToolInputValidationError || error instanceof ToolError || isCapabilityError(error)) {
|
|
179
|
+
return redactedExcerpt(error.message, { maxChars: MODEL_FAILURE_TEXT_MAX_CHARS });
|
|
180
|
+
}
|
|
181
|
+
const facts = valueFreeFailure(error);
|
|
182
|
+
return `The tool failed with an internal error (${facts.name}${facts.code === undefined ? '' : ` ${facts.code}`}).`;
|
|
183
|
+
}
|
|
184
|
+
function projectMcpToolFailure(thrown) {
|
|
185
|
+
const error = CaughtFailureSchema.parse(thrown);
|
|
186
|
+
if (error instanceof ToolInputValidationError) {
|
|
187
|
+
const message = modelSafeFailureText(error);
|
|
188
|
+
const capability = new CapabilityError(message, 'INVALID_INPUT', {
|
|
189
|
+
recoveryHint: 'Fix the listed input issues and retry.',
|
|
190
|
+
details: { issues: error.issues }
|
|
191
|
+
});
|
|
192
|
+
return { message, code: 'INVALID_INPUT', envelope: { ...capability.toEnvelope() } };
|
|
193
|
+
}
|
|
194
|
+
if (isCapabilityError(error)) {
|
|
195
|
+
return { message: modelSafeFailureText(error), code: error.code, envelope: { ...error.toEnvelope() } };
|
|
196
|
+
}
|
|
197
|
+
const message = modelSafeFailureText(error);
|
|
198
|
+
// A timeout is read from the failure's own text but never copied out of it.
|
|
199
|
+
const timedOut = error instanceof Error && /timed out/i.test(error.message);
|
|
200
|
+
const code = timedOut ? 'TIMEOUT' : 'INTERNAL';
|
|
201
|
+
const failureOptions = { retryable: timedOut };
|
|
202
|
+
if (timedOut)
|
|
203
|
+
failureOptions.details = { timedOut: true };
|
|
204
|
+
const envelope = new CapabilityError(message, code, failureOptions).toEnvelope();
|
|
205
|
+
return { message, code, envelope };
|
|
206
|
+
}
|
|
207
|
+
function getSuggestedToolCallFromErrorMetadata(metadata) {
|
|
208
|
+
if (!metadata)
|
|
209
|
+
return undefined;
|
|
210
|
+
const containmentRequest = metadata.containmentRequest;
|
|
211
|
+
const containment = asJsonObject(containmentRequest);
|
|
212
|
+
if (!containment)
|
|
213
|
+
return undefined;
|
|
214
|
+
return {
|
|
215
|
+
toolId: 'workspace-fs',
|
|
216
|
+
input: {
|
|
217
|
+
action: 'write',
|
|
218
|
+
writeAction: 'request-access',
|
|
219
|
+
...containment
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function isZodLike(value) {
|
|
224
|
+
return value instanceof z.ZodType;
|
|
225
|
+
}
|
|
226
|
+
function isSafetyCategory(safety) {
|
|
227
|
+
return typeof safety === 'string';
|
|
228
|
+
}
|
|
229
|
+
function deriveMcpAnnotations(safety) {
|
|
230
|
+
if (!safety)
|
|
231
|
+
return undefined;
|
|
232
|
+
const category = isSafetyCategory(safety) ? safety : safety.category;
|
|
233
|
+
const hints = isSafetyCategory(safety) ? undefined : safety;
|
|
234
|
+
// Default values based on category
|
|
235
|
+
const defaults = {
|
|
236
|
+
read: {
|
|
237
|
+
readOnlyHint: true,
|
|
238
|
+
destructiveHint: false,
|
|
239
|
+
idempotentHint: true,
|
|
240
|
+
openWorldHint: false
|
|
241
|
+
},
|
|
242
|
+
write: {
|
|
243
|
+
readOnlyHint: false,
|
|
244
|
+
destructiveHint: true,
|
|
245
|
+
idempotentHint: false,
|
|
246
|
+
openWorldHint: false
|
|
247
|
+
},
|
|
248
|
+
execute: {
|
|
249
|
+
readOnlyHint: false,
|
|
250
|
+
destructiveHint: false,
|
|
251
|
+
idempotentHint: false,
|
|
252
|
+
openWorldHint: true
|
|
253
|
+
}
|
|
254
|
+
}[category] ?? {
|
|
255
|
+
readOnlyHint: false,
|
|
256
|
+
destructiveHint: false,
|
|
257
|
+
idempotentHint: false,
|
|
258
|
+
openWorldHint: false
|
|
259
|
+
};
|
|
260
|
+
// Apply explicit overrides. `readOnly` is read defensively (the
|
|
261
|
+
// SafetyField type does not declare it yet) so a descriptor that declares
|
|
262
|
+
// an explicit readOnly hint is honored instead of silently taking the
|
|
263
|
+
// category default.
|
|
264
|
+
const parsedReadOnly = hints ? SafetyReadOnlySchema.safeParse(hints) : undefined;
|
|
265
|
+
const readOnly = parsedReadOnly?.success ? parsedReadOnly.data.readOnly : undefined;
|
|
266
|
+
return {
|
|
267
|
+
readOnlyHint: readOnly ?? defaults.readOnlyHint,
|
|
268
|
+
destructiveHint: hints?.destructive ?? defaults.destructiveHint,
|
|
269
|
+
idempotentHint: hints?.idempotent ?? defaults.idempotentHint,
|
|
270
|
+
openWorldHint: hints?.openWorld ?? defaults.openWorldHint
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function uniqueName(base, used) {
|
|
274
|
+
if (!used.has(base)) {
|
|
275
|
+
used.add(base);
|
|
276
|
+
return base;
|
|
277
|
+
}
|
|
278
|
+
let index = 2;
|
|
279
|
+
let candidate = `${base}.${index}`;
|
|
280
|
+
while (used.has(candidate)) {
|
|
281
|
+
index += 1;
|
|
282
|
+
candidate = `${base}.${index}`;
|
|
283
|
+
}
|
|
284
|
+
used.add(candidate);
|
|
285
|
+
return candidate;
|
|
286
|
+
}
|
|
287
|
+
function getDescriptorMcpDescription(descriptor) {
|
|
288
|
+
return resolveMcpDescription(descriptor);
|
|
289
|
+
}
|
|
290
|
+
function buildResourceText(descriptor, resourceName, uri, variables) {
|
|
291
|
+
const description = getDescriptorMcpDescription(descriptor);
|
|
292
|
+
return JSON.stringify({
|
|
293
|
+
kind: 'descriptor-metadata',
|
|
294
|
+
note: 'Descriptor metadata payload; not live resource data.',
|
|
295
|
+
tool: {
|
|
296
|
+
id: descriptor.id,
|
|
297
|
+
name: descriptor.name ?? descriptor.id,
|
|
298
|
+
description: description || null
|
|
299
|
+
},
|
|
300
|
+
resource: {
|
|
301
|
+
name: resourceName,
|
|
302
|
+
uri
|
|
303
|
+
},
|
|
304
|
+
variables
|
|
305
|
+
}, null, 2);
|
|
306
|
+
}
|
|
307
|
+
/** MCP prompt arguments arrive as strings; each manifest-declared argument becomes one. */
|
|
308
|
+
function buildPromptArgsSchema(argumentsSchema) {
|
|
309
|
+
if (!argumentsSchema || argumentsSchema.length === 0)
|
|
310
|
+
return undefined;
|
|
311
|
+
const argumentSchemas = {};
|
|
312
|
+
for (const arg of argumentsSchema) {
|
|
313
|
+
// Describe the outer schema: the SDK lists an optional argument's own description.
|
|
314
|
+
const description = arg.description ?? `${arg.name} argument`;
|
|
315
|
+
argumentSchemas[arg.name] = arg.required ? z.string().describe(description) : z.string().optional().describe(description);
|
|
316
|
+
}
|
|
317
|
+
return argumentSchemas;
|
|
318
|
+
}
|
|
319
|
+
function registerDescriptorMcpDefinitions(server, descriptors, runtime, logger, options = { announce: true }) {
|
|
320
|
+
const resourceUris = new Set();
|
|
321
|
+
const resourceNames = new Set();
|
|
322
|
+
const promptNames = new Set();
|
|
323
|
+
const promptIds = new Set();
|
|
324
|
+
const mcpLog = logger.child('mcp-definitions');
|
|
325
|
+
const canResolvePromptIds = runtime.hasKind(PROMPTS_MANIFEST_KIND_ID);
|
|
326
|
+
for (const descriptor of descriptors) {
|
|
327
|
+
const mcp = descriptor.mcp;
|
|
328
|
+
if (!mcp)
|
|
329
|
+
continue;
|
|
330
|
+
const resources = [...(mcp.resource ? [mcp.resource] : []), ...(mcp.resources ?? [])];
|
|
331
|
+
for (const resource of resources) {
|
|
332
|
+
if (resource.uri === PROJECTION_BIRTH_RESOURCE_URI)
|
|
333
|
+
throw new Error('Reserved projection birth resource URI');
|
|
334
|
+
if (resourceUris.has(resource.uri))
|
|
335
|
+
continue;
|
|
336
|
+
resourceUris.add(resource.uri);
|
|
337
|
+
const name = uniqueName(resource.name || `${descriptor.id}.resource`, resourceNames);
|
|
338
|
+
server.registerResource(name, resource.uri, {
|
|
339
|
+
description: resource.description
|
|
340
|
+
? `${resource.description} (descriptor metadata)`
|
|
341
|
+
: `Descriptor metadata for ${descriptor.id}`,
|
|
342
|
+
mimeType: resource.mimeType,
|
|
343
|
+
_meta: {
|
|
344
|
+
vibes: {
|
|
345
|
+
toolId: descriptor.id,
|
|
346
|
+
cacheable: resource.cacheable,
|
|
347
|
+
subscriptions: resource.subscriptions
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}, (uri) => ({
|
|
351
|
+
contents: [
|
|
352
|
+
{
|
|
353
|
+
uri: uri.href,
|
|
354
|
+
mimeType: resource.mimeType ?? 'application/json',
|
|
355
|
+
text: buildResourceText(descriptor, name, uri.href)
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}));
|
|
359
|
+
}
|
|
360
|
+
for (const template of mcp.resourceTemplates ?? []) {
|
|
361
|
+
if (template.uriTemplate === PROJECTION_BIRTH_RESOURCE_URI)
|
|
362
|
+
throw new Error('Reserved projection birth resource URI');
|
|
363
|
+
const name = uniqueName(template.name || `${descriptor.id}.resource-template`, resourceNames);
|
|
364
|
+
const uriTemplate = new ResourceTemplate(template.uriTemplate, { list: undefined });
|
|
365
|
+
server.registerResource(name, uriTemplate, {
|
|
366
|
+
description: template.description
|
|
367
|
+
? `${template.description} (descriptor metadata)`
|
|
368
|
+
: `Descriptor metadata template for ${descriptor.id}`,
|
|
369
|
+
mimeType: template.mimeType,
|
|
370
|
+
_meta: {
|
|
371
|
+
vibes: {
|
|
372
|
+
toolId: descriptor.id,
|
|
373
|
+
cacheable: template.cacheable,
|
|
374
|
+
subscriptions: template.subscriptions
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}, (uri, variables) => ({
|
|
378
|
+
contents: [
|
|
379
|
+
{
|
|
380
|
+
uri: uri.href,
|
|
381
|
+
mimeType: template.mimeType ?? 'application/json',
|
|
382
|
+
text: buildResourceText(descriptor, name, uri.href, variables)
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
}));
|
|
386
|
+
}
|
|
387
|
+
if (canResolvePromptIds) {
|
|
388
|
+
for (const promptId of mcp.promptIds ?? []) {
|
|
389
|
+
if (promptIds.has(promptId))
|
|
390
|
+
continue;
|
|
391
|
+
promptIds.add(promptId);
|
|
392
|
+
const name = uniqueName(`ai.prompt.${promptId}`, promptNames);
|
|
393
|
+
const registered = runtime.query(PROMPTS_MANIFEST_KIND_ID)
|
|
394
|
+
.withId(`${PROMPTS_MANIFEST_DESCRIPTOR_PREFIX}${promptId}`)
|
|
395
|
+
.firstDescriptor();
|
|
396
|
+
const manifest = registered ? promptsManifestDescriptorSchema.parse(registered).manifest : undefined;
|
|
397
|
+
const promptDescription = manifest
|
|
398
|
+
? resolveMcpDescription({ id: promptId, name: manifest.name, description: manifest.description || undefined })
|
|
399
|
+
: `Prompt descriptor ${promptId}`;
|
|
400
|
+
const argsSchema = buildPromptArgsSchema(manifest?.arguments) ?? {};
|
|
401
|
+
server.registerPrompt(name, {
|
|
402
|
+
description: promptDescription,
|
|
403
|
+
argsSchema
|
|
404
|
+
}, async (args) => {
|
|
405
|
+
// THE prompts kind renders the prompt with its composition, declared defaults and the caller's arguments.
|
|
406
|
+
const text = await resolvePromptText(runtime, promptId, { args });
|
|
407
|
+
return {
|
|
408
|
+
description: promptDescription,
|
|
409
|
+
messages: [
|
|
410
|
+
{
|
|
411
|
+
role: 'user',
|
|
412
|
+
content: {
|
|
413
|
+
type: 'text',
|
|
414
|
+
text: text || `Prompt '${promptId}' has no prompts/manifest content to expose.`
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
]
|
|
418
|
+
};
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Announced once per catalog fold; identical on every later connection.
|
|
424
|
+
const summary = { resources: resourceUris.size, prompts: promptNames.size };
|
|
425
|
+
if (options.announce) {
|
|
426
|
+
mcpLog.info('Registered MCP descriptor definitions', summary);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
mcpLog.debug('Registered MCP descriptor definitions', summary);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Construct one `McpServer` for one connection.
|
|
434
|
+
*
|
|
435
|
+
* The catalog fold (discover → enable → select → instructions) is NOT done
|
|
436
|
+
* here: pass a `catalog` from `createMcpToolCatalogCache` so many connections
|
|
437
|
+
* share one fold. When omitted the fold happens inline, which keeps every
|
|
438
|
+
* single-server caller (stdio, tests) working exactly as before.
|
|
439
|
+
*/
|
|
440
|
+
export function buildServer(runtime, options, logger, catalog = computeMcpToolCatalog(runtime, options, logger, resolveLocalToolIds(runtime, options))) {
|
|
441
|
+
const buildLog = logger.child('build');
|
|
442
|
+
const serverName = options.serverName ?? 'vibes';
|
|
443
|
+
const serverVersion = options.serverVersion ?? '0.0.0';
|
|
444
|
+
if (catalog.degradedKind) {
|
|
445
|
+
// Constitution rule 13: empty tools/list must not look healthy. The
|
|
446
|
+
// banner was composed (and error-logged) by the catalog fold.
|
|
447
|
+
return new McpServer({ name: serverName, version: serverVersion }, {
|
|
448
|
+
capabilities: catalog.capabilities,
|
|
449
|
+
instructions: catalog.instructions
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
const selected = catalog.selected;
|
|
453
|
+
if (!selected) {
|
|
454
|
+
throw new Error('MCP tool catalog is non-degraded but carries no selection');
|
|
455
|
+
}
|
|
456
|
+
const enabledDescriptors = catalog.enabledDescriptors;
|
|
457
|
+
const localToolIds = catalog.localToolIds;
|
|
458
|
+
const descriptors = catalog.descriptors;
|
|
459
|
+
createMcpWireNameMap(descriptors.map(descriptor => descriptor.id), options.mcpClientId, catalog.purposePresets);
|
|
460
|
+
const capabilities = catalog.capabilities;
|
|
461
|
+
const instructions = catalog.instructions;
|
|
462
|
+
const preScanFailedTools = [...catalog.preScanFailedTools];
|
|
463
|
+
const failedTools = [...preScanFailedTools];
|
|
464
|
+
let registeredCount = 0;
|
|
465
|
+
const registeredToolIds = [];
|
|
466
|
+
const server = new McpServer({ name: serverName, version: serverVersion }, {
|
|
467
|
+
capabilities,
|
|
468
|
+
instructions: instructions || undefined
|
|
469
|
+
});
|
|
470
|
+
for (const descriptor of descriptors) {
|
|
471
|
+
if (!descriptor.schemas?.input) {
|
|
472
|
+
// Already counted in preFailed / instructions.
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
const registered = settleNow(() => registerTool(server, descriptor, runtime, { ...options, customPresets: catalog.purposePresets }, logger, localToolIds));
|
|
476
|
+
if (!registered.ok) {
|
|
477
|
+
const facts = valueFreeFailure(registered.cause);
|
|
478
|
+
failedTools.push(`${descriptor.id}: ${facts.name}`);
|
|
479
|
+
buildLog.error(`Failed to register tool: ${descriptor.id}`, { error: facts });
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
if (registered.value) {
|
|
483
|
+
registeredCount++;
|
|
484
|
+
registeredToolIds.push(descriptor.id);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
failedTools.push(`${descriptor.id}: schema conversion failed`);
|
|
488
|
+
buildLog.warn(`Tool registration returned false: ${descriptor.id}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
// One INFO per catalog fold, DEBUG for every later connection served from the
|
|
492
|
+
// same fold: the work is identical, so repeating the line per connection is
|
|
493
|
+
// noise that hides real events (7,291 of these in 53 h of uptime).
|
|
494
|
+
const announce = !catalog.announced;
|
|
495
|
+
catalog.announced = true;
|
|
496
|
+
const registrationSummary = `Registered ${registeredCount}/${descriptors.length} tools with MCP server`;
|
|
497
|
+
if (announce) {
|
|
498
|
+
buildLog.info(registrationSummary, { descriptorRevision: catalog.revision });
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
buildLog.debug(registrationSummary, { descriptorRevision: catalog.revision });
|
|
502
|
+
}
|
|
503
|
+
if (failedTools.length > 0) {
|
|
504
|
+
buildLog.error(`MCP tool registration incomplete: ${failedTools.length} tool(s) not advertised to agents`, { failedTools, registeredCount, selectedCount: descriptors.length });
|
|
505
|
+
}
|
|
506
|
+
// Late failures were not known at construct — patch `_instructions` so the
|
|
507
|
+
// host still sees DEGRADED TOOLSET at session start (rule 13).
|
|
508
|
+
const lateFailedTools = failedTools.slice(preScanFailedTools.length);
|
|
509
|
+
if (lateFailedTools.length > 0) {
|
|
510
|
+
appendMcpInstructions(server, formatRegistrationFailureInstructions(lateFailedTools));
|
|
511
|
+
}
|
|
512
|
+
// Continuity contract: required tools that were selected but lack schemas.input
|
|
513
|
+
// (pre-scan) fail closed — same injury class as missingRequired at selection.
|
|
514
|
+
const requiredSet = new Set(selected.preset?.requiredTools ?? []);
|
|
515
|
+
const requiredMissingSchema = descriptors
|
|
516
|
+
.filter((d) => requiredSet.has(d.id) && !d.schemas?.input)
|
|
517
|
+
.map((d) => d.id);
|
|
518
|
+
if (requiredMissingSchema.length > 0) {
|
|
519
|
+
const message = `REQUIRED MCP TOOLS CANNOT BE ADVERTISED for preset ${selected.presetId}: ` +
|
|
520
|
+
`${requiredMissingSchema.join(', ')} (no schemas.input). ` +
|
|
521
|
+
`These continuity tools must not silently vanish from tools/list.`;
|
|
522
|
+
buildLog.error(message, { requiredMissingSchema, failedTools });
|
|
523
|
+
throw new Error(message);
|
|
524
|
+
}
|
|
525
|
+
// Determinism gate (czar D2 / c198b7bc):
|
|
526
|
+
// Serve-time registry hash vs selected policy ids asserted equal every boot.
|
|
527
|
+
const selectedToolIds = descriptors.map((descriptor) => descriptor.id);
|
|
528
|
+
const selectedCatalogHash = projectionCatalogHash(selectedToolIds);
|
|
529
|
+
const registeredCatalogHash = projectionCatalogHash(registeredToolIds);
|
|
530
|
+
if (selectedCatalogHash !== registeredCatalogHash) {
|
|
531
|
+
const unregisteredIds = selectedToolIds.filter((id) => !registeredToolIds.includes(id));
|
|
532
|
+
buildLog.error(`MCP serve catalog indeterminism: registered toolset hash (${registeredCatalogHash}) differs from selected policy ids (${selectedCatalogHash})`, {
|
|
533
|
+
selectedCatalogHash,
|
|
534
|
+
registeredCatalogHash,
|
|
535
|
+
unregisteredIds
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
// Snapshot before client detection or generation changes can add served tools.
|
|
539
|
+
if (options.buildProvenance) {
|
|
540
|
+
const birth = createProjectionBirthResource({ provenance: options.buildProvenance, selectedToolIds, registeredToolIds });
|
|
541
|
+
server.registerResource('vibes-projection-birth', PROJECTION_BIRTH_RESOURCE_URI, { description: 'Immutable startup snapshot; not a host-visible catalog attestation.', mimeType: 'application/json' }, () => ({ contents: [{ uri: PROJECTION_BIRTH_RESOURCE_URI, mimeType: 'application/json', text: birth.text }] }));
|
|
542
|
+
const warning = options.buildProvenance.match(/^⚠ (?:EXECUTABLE SOURCE UNKNOWN|DIRTY BUILD|STALE BUILD):/);
|
|
543
|
+
if (warning)
|
|
544
|
+
appendMcpInstructions(server, `${warning[0]} Read ${PROJECTION_BIRTH_RESOURCE_URI} for source identity and recovery guidance.`);
|
|
545
|
+
appendMcpInstructions(server, birth.instructions);
|
|
546
|
+
}
|
|
547
|
+
registerDescriptorMcpDefinitions(server, descriptors, runtime, logger, { announce });
|
|
548
|
+
// Client auto-detection (2025-era scar tissue): when serve was spawned without
|
|
549
|
+
// --client, the base `mcp` preset applies and client-curated tools stay hidden.
|
|
550
|
+
// After initialize, we additive-register the detected preset and emit
|
|
551
|
+
// tools/listChanged.
|
|
552
|
+
//
|
|
553
|
+
// MCP 2026-07-28: tools/list MUST NOT vary per connection. This path mutates
|
|
554
|
+
// the catalog after initialize and is therefore gated behind an explicit
|
|
555
|
+
// opt-in (`allowClientAutoDetectUpgrade`, default OFF). Pass --client (or
|
|
556
|
+
// pack config) so the catalog is fixed at start.
|
|
557
|
+
// See docs/proposals/mcp-2026-07-28/FINDINGS.md.
|
|
558
|
+
if (!options.mcpClientId && options.allowClientAutoDetectUpgrade) {
|
|
559
|
+
const baselineIds = new Set(descriptors.map((descriptor) => descriptor.id));
|
|
560
|
+
server.server.oninitialized = () => {
|
|
561
|
+
const detected = settleNow(() => {
|
|
562
|
+
const clientName = server.server.getClientVersion()?.name;
|
|
563
|
+
const detectedClientId = detectMcpClientId(clientName, catalog.purposePresets);
|
|
564
|
+
if (!detectedClientId)
|
|
565
|
+
return;
|
|
566
|
+
buildLog.warn('MCP catalog mutated after initialize (client auto-detect). ' +
|
|
567
|
+
'Illegal under protocol 2026-07-28 — pass --client so the preset is fixed at start.', { clientName, detectedClientId });
|
|
568
|
+
// Same registration bound as the construct-time fold: a settled
|
|
569
|
+
// surface must not re-admit an unavailable descriptor through
|
|
570
|
+
// this opt-in mutation path. `zombieCandidates` is exactly the
|
|
571
|
+
// fold's unavailable set.
|
|
572
|
+
const settlementExcluded = catalog.loaderSettlement === 'settled' ? new Set(catalog.zombieCandidates) : null;
|
|
573
|
+
const upgraded = selectMcpTools(settlementExcluded
|
|
574
|
+
? enabledDescriptors.filter((descriptor) => !settlementExcluded.has(descriptor.id))
|
|
575
|
+
: [...enabledDescriptors], { mcpClientId: detectedClientId, customPresets: catalog.purposePresets });
|
|
576
|
+
const additions = upgraded.descriptors.filter((descriptor) => !baselineIds.has(descriptor.id));
|
|
577
|
+
if (additions.length === 0)
|
|
578
|
+
return;
|
|
579
|
+
let added = 0;
|
|
580
|
+
const upgradeFailures = [];
|
|
581
|
+
for (const descriptor of additions) {
|
|
582
|
+
const registered = settleNow(() => registerTool(server, descriptor, runtime, { ...options, customPresets: catalog.purposePresets }, logger, localToolIds));
|
|
583
|
+
if (!registered.ok) {
|
|
584
|
+
const facts = valueFreeFailure(registered.cause);
|
|
585
|
+
upgradeFailures.push(`${descriptor.id}: ${facts.name}`);
|
|
586
|
+
// Error (not warn): continuity tools often land only via
|
|
587
|
+
// this path when --client was omitted at serve start.
|
|
588
|
+
buildLog.error(`Client-detected tool failed to register: ${descriptor.id}`, { error: facts });
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
if (registered.value) {
|
|
592
|
+
baselineIds.add(descriptor.id);
|
|
593
|
+
added += 1;
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
upgradeFailures.push(`${descriptor.id}: schema conversion failed`);
|
|
597
|
+
buildLog.error(`Client-detected tool failed to register: ${descriptor.id}`, { error: 'schema conversion failed' });
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// Initialize already returned construct-time instructions; patch
|
|
601
|
+
// so agents that re-read server state / later sessions see the gap.
|
|
602
|
+
// Hosts that only saw initialize may still miss this — error log
|
|
603
|
+
// remains load-bearing until listChanged + instructions re-push.
|
|
604
|
+
if (upgradeFailures.length > 0) {
|
|
605
|
+
appendMcpInstructions(server, formatRegistrationFailureInstructions(upgradeFailures));
|
|
606
|
+
buildLog.error(`Client-detected preset upgrade incomplete: ${upgradeFailures.length} tool(s) not advertised`, { upgradeFailures, added, detectedClientId });
|
|
607
|
+
}
|
|
608
|
+
// Required continuity tools for the detected client must not
|
|
609
|
+
// vanish silently (brain/presence class outage via auto-detect).
|
|
610
|
+
const stillMissingRequired = missingRequiredTools([...baselineIds], upgraded.preset);
|
|
611
|
+
if (stillMissingRequired.length > 0) {
|
|
612
|
+
const section = `⚠ DEGRADED SURFACE: required tools missing after client auto-detect ` +
|
|
613
|
+
`(${detectedClientId}): ${stillMissingRequired.join(', ')}. ` +
|
|
614
|
+
`Report this to the user IMMEDIATELY; do NOT silently work around it.`;
|
|
615
|
+
appendMcpInstructions(server, section);
|
|
616
|
+
buildLog.error('REQUIRED MCP TOOLS MISSING after client auto-detect', {
|
|
617
|
+
detectedClientId,
|
|
618
|
+
stillMissingRequired
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
buildLog.info('Client-detected preset upgrade', {
|
|
622
|
+
clientName,
|
|
623
|
+
detectedClientId,
|
|
624
|
+
presetId: upgraded.presetId,
|
|
625
|
+
added,
|
|
626
|
+
addedTools: additions.map((descriptor) => descriptor.id),
|
|
627
|
+
upgradeFailures
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
if (!detected.ok) {
|
|
631
|
+
const facts = valueFreeFailure(detected.cause);
|
|
632
|
+
// Whole-path failure (not per-tool): still a rule-13 injury —
|
|
633
|
+
// thin base preset can look healthy without continuity tools.
|
|
634
|
+
appendMcpInstructions(server, '⚠ DEGRADED SURFACE: client preset auto-detection failed after initialize. ' +
|
|
635
|
+
'Continuity tools for the connected client may be missing. ' +
|
|
636
|
+
'Report this to the user IMMEDIATELY; do NOT silently work around it. ' +
|
|
637
|
+
'Diagnose: pass --client explicitly or check MCP initialize clientInfo.name.');
|
|
638
|
+
buildLog.error('Client preset auto-detection failed', { error: facts });
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
// Own admission before asynchronous SDK schema parsing. RegisteredTool is
|
|
643
|
+
// a public SDK handle; replacing it leaves admitted calls' schemas intact.
|
|
644
|
+
//
|
|
645
|
+
// Protocol shape: an unknown tool or unsupported task execution is a
|
|
646
|
+
// request error, not a tool-execution error — per MCP spec these are
|
|
647
|
+
// JSON-RPC -32602 responses, not HTTP-200 `isError` results. Validation
|
|
648
|
+
// failures stay tool-result-shaped but stamp `_meta.errorCode`
|
|
649
|
+
// (`INVALID_INPUT`), mirroring the execution path's capability-error
|
|
650
|
+
// projection below.
|
|
651
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
652
|
+
const invocation = runtime.acquireInvocation();
|
|
653
|
+
try {
|
|
654
|
+
const attempted = await settle(() => invocation.runtime.runInvocation(async () => {
|
|
655
|
+
const tool = registeredTools.get(server)?.get(request.params.name);
|
|
656
|
+
if (!tool?.registration.enabled)
|
|
657
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown tool: ${request.params.name}`);
|
|
658
|
+
if (request.params.task)
|
|
659
|
+
throw new McpError(ErrorCode.InvalidParams, 'This tool does not support task execution');
|
|
660
|
+
const parsed = tool.registration.inputSchema ? await safeParseAsync(tool.registration.inputSchema, request.params.arguments) : { success: true, data: request.params.arguments };
|
|
661
|
+
if (!parsed.success) {
|
|
662
|
+
const issues = formatZodIssues(parsed.error);
|
|
663
|
+
const text = `Invalid input for '${request.params.name}': ${issues}`;
|
|
664
|
+
// Omit structuredContent on isError: MCP clients that kept
|
|
665
|
+
// outputSchema validate it even on errors, and {ok:false,error}
|
|
666
|
+
// does not match tool output schemas.
|
|
667
|
+
return {
|
|
668
|
+
isError: true,
|
|
669
|
+
content: [{ type: 'text', text }],
|
|
670
|
+
_meta: { errorCode: 'INVALID_INPUT' }
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
return await tool.invoke(request.params.arguments, extra, invocation);
|
|
674
|
+
}));
|
|
675
|
+
if (attempted.ok)
|
|
676
|
+
return attempted.value;
|
|
677
|
+
// McpError must escape so the SDK answers with a JSON-RPC error
|
|
678
|
+
// response carrying its code and the request id.
|
|
679
|
+
if (attempted.cause instanceof McpError)
|
|
680
|
+
throw attempted.cause;
|
|
681
|
+
const failure = projectMcpToolFailure(attempted.cause);
|
|
682
|
+
return {
|
|
683
|
+
isError: true,
|
|
684
|
+
content: [{ type: 'text', text: failure.message }],
|
|
685
|
+
_meta: { errorCode: failure.code }
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
finally {
|
|
689
|
+
invocation.release();
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
const unsubscribe = runtime.onGenerationChange(() => {
|
|
693
|
+
// Same isolation contract as the initial registration loop above: one
|
|
694
|
+
// bad descriptor must not leave the server with its tools removed but
|
|
695
|
+
// not re-registered, skip sendToolListChanged(), or escape into the
|
|
696
|
+
// event subscription.
|
|
697
|
+
const refreshed = settleNow(() => {
|
|
698
|
+
// Same registration bound as the fold this server was built from:
|
|
699
|
+
// the refresh must not widen a settled surface with descriptors a
|
|
700
|
+
// fresh connection would still exclude.
|
|
701
|
+
const next = computeMcpToolCatalog(runtime, options, logger, resolveLocalToolIds(runtime, options), catalog.loaderSettlement);
|
|
702
|
+
// Validate the replacement before retracting the current, unambiguous catalog.
|
|
703
|
+
createMcpWireNameMap(next.descriptors.map(descriptor => descriptor.id), options.mcpClientId, next.purposePresets);
|
|
704
|
+
for (const entry of registeredTools.get(server)?.values() ?? [])
|
|
705
|
+
entry.registration.remove();
|
|
706
|
+
registeredTools.set(server, new Map());
|
|
707
|
+
const refreshFailures = [];
|
|
708
|
+
for (const descriptor of next.descriptors) {
|
|
709
|
+
const registered = settleNow(() => registerTool(server, descriptor, runtime, { ...options, customPresets: next.purposePresets }, logger, next.localToolIds));
|
|
710
|
+
if (!registered.ok) {
|
|
711
|
+
const facts = valueFreeFailure(registered.cause);
|
|
712
|
+
refreshFailures.push(`${descriptor.id}: ${facts.name}`);
|
|
713
|
+
buildLog.error(`Failed to re-register tool on generation change: ${descriptor.id}`, { error: facts });
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (!registered.value)
|
|
717
|
+
refreshFailures.push(`${descriptor.id}: schema conversion failed`);
|
|
718
|
+
}
|
|
719
|
+
if (refreshFailures.length > 0) {
|
|
720
|
+
appendMcpInstructions(server, formatRegistrationFailureInstructions(refreshFailures));
|
|
721
|
+
buildLog.error(`MCP tool re-registration incomplete after generation change: ${refreshFailures.length} tool(s) not advertised`, { refreshFailures });
|
|
722
|
+
}
|
|
723
|
+
server.sendToolListChanged();
|
|
724
|
+
});
|
|
725
|
+
if (!refreshed.ok) {
|
|
726
|
+
const facts = valueFreeFailure(refreshed.cause);
|
|
727
|
+
appendMcpInstructions(server, '⚠ DEGRADED SURFACE: tool catalog refresh after a registry generation change failed. ' +
|
|
728
|
+
'The previously registered tool list may be stale or empty. ' +
|
|
729
|
+
'Report this to the user IMMEDIATELY; do NOT silently work around it.');
|
|
730
|
+
buildLog.error('MCP tool catalog refresh on generation change failed', { error: facts });
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
const onclose = server.server.onclose;
|
|
734
|
+
server.server.onclose = () => { unsubscribe(); onclose?.(); };
|
|
735
|
+
return server;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* SDK keeps instructions on Server as private `_instructions` and reads that
|
|
739
|
+
* field on initialize. Construct-time options cannot list late registerTool
|
|
740
|
+
* failures — mutate the field after registration so rule 13 still holds.
|
|
741
|
+
*/
|
|
742
|
+
function appendMcpInstructions(server, section) {
|
|
743
|
+
const inner = server.server;
|
|
744
|
+
// The SDK Server constructor always assigns its own `_instructions`
|
|
745
|
+
// property (possibly undefined). If that own property disappears, the SDK
|
|
746
|
+
// renamed the private field and every degraded-toolset banner written here
|
|
747
|
+
// would be silently dropped — fail loudly instead of rotting.
|
|
748
|
+
if (!('_instructions' in inner) || !Object.prototype.hasOwnProperty.call(inner, '_instructions')) {
|
|
749
|
+
throw new Error('MCP SDK Server no longer declares the private `_instructions` field; ' +
|
|
750
|
+
'appendMcpInstructions cannot patch degraded-toolset instructions. ' +
|
|
751
|
+
'Upstream an SDK API for late instructions and update this adapter.');
|
|
752
|
+
}
|
|
753
|
+
const previousInstructions = z.object({ _instructions: z.string().optional() }).safeParse(inner);
|
|
754
|
+
const existing = previousInstructions.success ? previousInstructions.data._instructions ?? '' : '';
|
|
755
|
+
if (!Reflect.set(inner, '_instructions', existing ? `${existing}\n\n${section}` : section)) {
|
|
756
|
+
throw new Error('MCP SDK Server instructions are no longer writable.');
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Zod introspection is pure in its schema, so it is cached against the schema
|
|
761
|
+
* OBJECT rather than the descriptor id or the registry revision. A WeakMap
|
|
762
|
+
* cannot go stale: a changed descriptor brings a different schema instance, and
|
|
763
|
+
* an unchanged instance cannot derive differently.
|
|
764
|
+
*/
|
|
765
|
+
const derivedInputSchemas = new WeakMap();
|
|
766
|
+
const normalizableOutputSchemas = new WeakMap();
|
|
767
|
+
function requireToolZodSchema(value, toolId) {
|
|
768
|
+
const parsed = z.custom((candidate) => candidate instanceof z.ZodType).safeParse(value);
|
|
769
|
+
if (!parsed.success)
|
|
770
|
+
throw new CapabilityError(`Tool '${toolId}' must supply a Zod schema`, 'INVALID_INPUT');
|
|
771
|
+
return parsed.data;
|
|
772
|
+
}
|
|
773
|
+
function deriveInputSchema(zodSchema) {
|
|
774
|
+
const cached = derivedInputSchemas.get(zodSchema);
|
|
775
|
+
if (cached)
|
|
776
|
+
return cached;
|
|
777
|
+
const isUnion = isDiscriminatedUnion(zodSchema);
|
|
778
|
+
const derived = {
|
|
779
|
+
isUnion,
|
|
780
|
+
discriminator: isUnion ? getDiscriminator(zodSchema) : null,
|
|
781
|
+
unionOptions: isUnion ? getDiscriminatorOptions(zodSchema) : [],
|
|
782
|
+
// The SDK's normalizeObjectSchema only handles z.object() — discriminated
|
|
783
|
+
// unions produce { type: "object", properties: {} } in tools/list.
|
|
784
|
+
// Flatten union variants into a merged z.object() so the SDK serializes all params.
|
|
785
|
+
inputSchema: flattenDiscriminatedUnionToObject(zodSchema)
|
|
786
|
+
};
|
|
787
|
+
derivedInputSchemas.set(zodSchema, derived);
|
|
788
|
+
return derived;
|
|
789
|
+
}
|
|
790
|
+
function normalizableOutputSchema(rawOutputSchema) {
|
|
791
|
+
const cached = normalizableOutputSchemas.get(rawOutputSchema);
|
|
792
|
+
if (cached)
|
|
793
|
+
return cached.schema;
|
|
794
|
+
const schema = !isPlainUnion(rawOutputSchema) && !isDiscriminatedUnion(rawOutputSchema)
|
|
795
|
+
? rawOutputSchema
|
|
796
|
+
: undefined;
|
|
797
|
+
normalizableOutputSchemas.set(rawOutputSchema, { schema });
|
|
798
|
+
return schema;
|
|
799
|
+
}
|
|
800
|
+
// One fallback identity per server, shared by its tools and preserved across registration refresh.
|
|
801
|
+
const serverConnectionKeys = new WeakMap();
|
|
802
|
+
function registerTool(server, descriptor, runtime, options, logger, localToolIds = []) {
|
|
803
|
+
let serverConnectionKey = serverConnectionKeys.get(server);
|
|
804
|
+
if (!serverConnectionKey) {
|
|
805
|
+
serverConnectionKey = `mcp-server:${crypto.randomUUID()}`;
|
|
806
|
+
serverConnectionKeys.set(server, serverConnectionKey);
|
|
807
|
+
}
|
|
808
|
+
if (!descriptor.schemas?.input) {
|
|
809
|
+
logger.child('schema').error(`No input schema for ${descriptor.id} — skipping tool`);
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
const zodSchema = requireToolZodSchema(descriptor.schemas.input, descriptor.id);
|
|
813
|
+
// Pre-computed once per schema OBJECT, not once per connection. Every HTTP
|
|
814
|
+
// session builds its own McpServer and re-registers all ~89 tools, and the
|
|
815
|
+
// zod introspection below (union walk + variant merge) was the bulk of that
|
|
816
|
+
// work. Keying a WeakMap on the schema instance needs no invalidation: a
|
|
817
|
+
// re-registered descriptor arrives with a new schema object, and for the
|
|
818
|
+
// same object these are pure functions.
|
|
819
|
+
const derived = deriveInputSchema(zodSchema);
|
|
820
|
+
const inputSchema = derived.inputSchema;
|
|
821
|
+
// Output schema - pass only if it can be normalized by the SDK.
|
|
822
|
+
// The SDK's normalizeObjectSchema() only handles z.object() — unions cause
|
|
823
|
+
// safeParseAsync(undefined, ...) which throws "undefined is not an object (evaluating 's._zod')".
|
|
824
|
+
// Skip output schemas that are plain unions or discriminated unions to avoid this SDK bug.
|
|
825
|
+
const rawOutputSchema = descriptor.schemas.output === undefined ? undefined : requireToolZodSchema(descriptor.schemas.output, descriptor.id);
|
|
826
|
+
const outputSchema = rawOutputSchema ? normalizableOutputSchema(rawOutputSchema) : undefined;
|
|
827
|
+
// Derive MCP annotations from safety field
|
|
828
|
+
const annotations = deriveMcpAnnotations(descriptor.safety);
|
|
829
|
+
// Trust-tag tier is derived from `safety` until `descriptor.capability.tier`
|
|
830
|
+
// lands in the tier-spec PR (AI-SURFACE-2026-05.md §4.3 step 1). Captured
|
|
831
|
+
// once at registration so it composes with cache-stable system prompts.
|
|
832
|
+
const trustTier = deriveTrustTier(descriptor);
|
|
833
|
+
const toolAccessor = runtime.assets('tools/tool');
|
|
834
|
+
const isDiscriminated = toolAccessor.isDiscriminated(descriptor.id);
|
|
835
|
+
let variantSafety;
|
|
836
|
+
if (isDiscriminated) {
|
|
837
|
+
const discriminatedDescriptor = toolAccessor.getDiscriminatedDescriptor(descriptor.id);
|
|
838
|
+
if (discriminatedDescriptor) {
|
|
839
|
+
const entries = discriminatedDescriptor.variantNames
|
|
840
|
+
.map((variantName) => [variantName, discriminatedDescriptor.getVariantSafety(variantName)])
|
|
841
|
+
.filter((entry) => entry[1] !== undefined);
|
|
842
|
+
if (entries.length > 0) {
|
|
843
|
+
variantSafety = Object.fromEntries(entries);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
const isLocal = isLocalToolId(descriptor.id, localToolIds);
|
|
848
|
+
const origin = vibesToolOrigin(isLocal);
|
|
849
|
+
const description = formatLocalMcpToolDescription(getDescriptorMcpDescription(descriptor), isLocal);
|
|
850
|
+
const dispatchRuntime = runtime;
|
|
851
|
+
const vibesRegistrationMeta = {
|
|
852
|
+
origin,
|
|
853
|
+
category: descriptor.category,
|
|
854
|
+
subcategory: descriptor.subcategory,
|
|
855
|
+
tags: descriptor.tags,
|
|
856
|
+
requiresApproval: descriptor.requiresApproval,
|
|
857
|
+
safety: descriptor.safety,
|
|
858
|
+
mcpClientId: options.mcpClientId
|
|
859
|
+
};
|
|
860
|
+
if (variantSafety)
|
|
861
|
+
vibesRegistrationMeta.variants = variantSafety;
|
|
862
|
+
const registrationOptions = {
|
|
863
|
+
title: descriptor.name ?? descriptor.id,
|
|
864
|
+
description,
|
|
865
|
+
inputSchema: inputSchema,
|
|
866
|
+
outputSchema: outputSchema,
|
|
867
|
+
annotations,
|
|
868
|
+
_meta: { vibes: vibesRegistrationMeta }
|
|
869
|
+
};
|
|
870
|
+
const handler = async (args, extra, boundInvocation) => {
|
|
871
|
+
const callId = `mcp_${Date.now()}_${crypto.randomUUID()}`;
|
|
872
|
+
// Per-call nonce for trust-tag wrapper (F4). Shared across all text items
|
|
873
|
+
// and error paths in this tool call so the model can verify boundary integrity.
|
|
874
|
+
const callNonce = crypto.randomUUID();
|
|
875
|
+
// A body whose executable was replaced on disk says so on EVERY result and
|
|
876
|
+
// error until it is respawned (stale-body.consumer.ts, handed in by the
|
|
877
|
+
// consumer serve impl) — the one channel an agent reads on every call.
|
|
878
|
+
// Computed before `try` so the error path shares it.
|
|
879
|
+
const stalePrefix = options.staleBodyBanner?.() ?? null;
|
|
880
|
+
let toolCallCtx;
|
|
881
|
+
const callStarted = Date.now();
|
|
882
|
+
const traceStage = (stage) => logger.info('MCP invocation stage', { callId, tool: descriptor.id, stage, elapsedMs: Date.now() - callStarted });
|
|
883
|
+
traceStage('start');
|
|
884
|
+
const invocation = boundInvocation?.retain() ?? dispatchRuntime.acquireInvocation();
|
|
885
|
+
const runtime = invocation.runtime;
|
|
886
|
+
let callDescriptor = descriptor;
|
|
887
|
+
try {
|
|
888
|
+
const attempted = await settle(async () => {
|
|
889
|
+
// SAFETY: This accessor belongs to the registered tools/tool kind, whose descriptor contract is ToolAssetDescriptor; AssetAccessor erases the descriptor type, not its owner.
|
|
890
|
+
const currentDescriptor = runtime.assets('tools/tool').descriptor(descriptor.id);
|
|
891
|
+
if (!currentDescriptor?.schemas?.input)
|
|
892
|
+
throw new Error(`Tool '${descriptor.id}' is unavailable in this generation`);
|
|
893
|
+
callDescriptor = currentDescriptor;
|
|
894
|
+
const zodSchema = requireToolZodSchema(callDescriptor.schemas.input, callDescriptor.id);
|
|
895
|
+
const { isUnion, discriminator, unionOptions } = deriveInputSchema(zodSchema);
|
|
896
|
+
const defaultAction = callDescriptor.schemas.defaultAction;
|
|
897
|
+
await options.beforeToolCall?.();
|
|
898
|
+
// Tools parses these raw JSON arguments once with the owner schema.
|
|
899
|
+
// This handler does not parse them. A missing union discriminator
|
|
900
|
+
// still defaults only on an admitted object, for the session stamp.
|
|
901
|
+
const wire = JsonObjectSchema.safeParse(args);
|
|
902
|
+
if (isUnion && discriminator && !wire.success) {
|
|
903
|
+
throw new CapabilityError('the tool arguments are invalid', 'INVALID_INPUT', {
|
|
904
|
+
reason: 'mcp-tool-args-invalid'
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
let resolvedArgs = wire.success ? wire.data : {};
|
|
908
|
+
if (isUnion && discriminator && wire.success && !(discriminator in resolvedArgs)) {
|
|
909
|
+
if (defaultAction && unionOptions.includes(defaultAction)) {
|
|
910
|
+
resolvedArgs = { [discriminator]: defaultAction, ...resolvedArgs };
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
// The SDK callback does not currently populate `extra.sessionId` for
|
|
914
|
+
// WebStandard Streamable HTTP. The HTTP adapter therefore gives each
|
|
915
|
+
// connection a private identity cell when it builds the server. This is
|
|
916
|
+
// call data, never mutable process-global runtime scope.
|
|
917
|
+
const transportSessionId = extra.sessionId ?? options.resolveTransportSessionId?.();
|
|
918
|
+
// A session-family call may name the participant it continues as
|
|
919
|
+
// (the documented bind flow). That is a binding hint the session layer
|
|
920
|
+
// verifies, not identity: agent identity comes only from the connection.
|
|
921
|
+
const callMeta = '_meta' in extra ? asJsonObject(extra._meta) : undefined;
|
|
922
|
+
const metaParticipantId = (isStringValue(callMeta?.participantId) && callMeta.participantId) ||
|
|
923
|
+
(isStringValue(callMeta?.['vibes/participantId']) &&
|
|
924
|
+
callMeta['vibes/participantId']) ||
|
|
925
|
+
undefined;
|
|
926
|
+
const connectionKey = transportSessionId ?? serverConnectionKey;
|
|
927
|
+
// Build context with progress/cancellation support
|
|
928
|
+
const baseToolCallCtx = {
|
|
929
|
+
callId,
|
|
930
|
+
logger: logger.child(callDescriptor.id)
|
|
931
|
+
};
|
|
932
|
+
if (extra.signal)
|
|
933
|
+
baseToolCallCtx.signal = extra.signal;
|
|
934
|
+
if (extra.sendProgress)
|
|
935
|
+
baseToolCallCtx.sendProgress = (progress) => {
|
|
936
|
+
const update = { progress: progress.progress, total: progress.total };
|
|
937
|
+
if (progress.message)
|
|
938
|
+
update.message = progress.message;
|
|
939
|
+
extra.sendProgress?.(update);
|
|
940
|
+
};
|
|
941
|
+
// Stamp trusted identity from the runtime's `auth/identity` (the same
|
|
942
|
+
// builder the tools-cli surface uses). The MCP stdio surface runs as an
|
|
943
|
+
// authenticated local account; without this, owner-scoped tools
|
|
944
|
+
// (applet/knowledge/work) fell back to SYSTEM_OWNER and orphaned the
|
|
945
|
+
// user's artifacts. buildDirectToolCallContext strips any caller-supplied
|
|
946
|
+
// identity keys (anti-laundering) before stamping userId/actorUserId(+org).
|
|
947
|
+
traceStage('direct-auth');
|
|
948
|
+
const directContext = await buildDirectToolCallContext(runtime, baseToolCallCtx);
|
|
949
|
+
const caller = metaParticipantId && !directContext.caller?.participantId
|
|
950
|
+
? { ...directContext.caller, participantId: metaParticipantId }
|
|
951
|
+
: { ...directContext.caller };
|
|
952
|
+
// Admission (RC08 U7): identity only from what this connection's
|
|
953
|
+
// transport verified, before the hook bus or any observer runs.
|
|
954
|
+
const admittedCaller = admitConnectionCaller({
|
|
955
|
+
verified: options.resolveVerifiedConnectionIdentity?.() ?? options.verifiedConnectionIdentity,
|
|
956
|
+
declared: options.resolveConnectionIdentity?.(),
|
|
957
|
+
call: caller,
|
|
958
|
+
principal: directContext.metadata
|
|
959
|
+
});
|
|
960
|
+
const mcpSettings = await loadMcpConfig(runtime);
|
|
961
|
+
const resolvedCeiling = resolveMcpClientCapabilityCeiling(mcpSettings, {
|
|
962
|
+
admittedCaller,
|
|
963
|
+
mcpClientId: options.mcpClientId,
|
|
964
|
+
preset: options.preset
|
|
965
|
+
}, runtime);
|
|
966
|
+
const agentContext = {
|
|
967
|
+
agentId: admittedCaller?.agentId ?? resolvedCeiling.profile ?? 'mcp-client',
|
|
968
|
+
ceiling: resolvedCeiling.ceiling,
|
|
969
|
+
callChain: []
|
|
970
|
+
};
|
|
971
|
+
const metadata = {
|
|
972
|
+
...directContext.metadata,
|
|
973
|
+
'mcp.transportMode': options.mode,
|
|
974
|
+
[TOOL_AGENT_CONTEXT_METADATA_KEY]: agentContext,
|
|
975
|
+
agentCapabilityCeiling: resolvedCeiling.ceiling
|
|
976
|
+
};
|
|
977
|
+
if (transportSessionId)
|
|
978
|
+
metadata['mcp.transportSessionId'] = transportSessionId;
|
|
979
|
+
if (connectionKey)
|
|
980
|
+
metadata['mcp.connectionKey'] = connectionKey;
|
|
981
|
+
const directToolCallCtx = {
|
|
982
|
+
...directContext,
|
|
983
|
+
caller: admittedCaller,
|
|
984
|
+
metadata
|
|
985
|
+
};
|
|
986
|
+
traceStage('prepare-context');
|
|
987
|
+
const preparation = {
|
|
988
|
+
context: directToolCallCtx, toolId: callDescriptor.id, input: resolvedArgs, descriptor: callDescriptor
|
|
989
|
+
};
|
|
990
|
+
if (transportSessionId)
|
|
991
|
+
preparation.transportSessionId = transportSessionId;
|
|
992
|
+
const preparedToolCallCtx = options.prepareToolCallContext
|
|
993
|
+
? await options.prepareToolCallContext(preparation)
|
|
994
|
+
: directToolCallCtx;
|
|
995
|
+
traceStage('stamp-session');
|
|
996
|
+
const sessionStampedCtx = await stampMcpSessionCaller(runtime, server, preparedToolCallCtx, transportSessionId ?? connectionKey, options.mode, callDescriptor.id, resolvedArgs, logger);
|
|
997
|
+
const stampedToolCallCtx = {
|
|
998
|
+
...sessionStampedCtx,
|
|
999
|
+
metadata: {
|
|
1000
|
+
...sessionStampedCtx.metadata,
|
|
1001
|
+
[TOOL_AGENT_CONTEXT_METADATA_KEY]: agentContext,
|
|
1002
|
+
agentCapabilityCeiling: resolvedCeiling.ceiling
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
toolCallCtx = stampedToolCallCtx;
|
|
1006
|
+
// Route through invokeTool so tools/tool hook bus events
|
|
1007
|
+
// (contextBuild / beforeExecute / afterExecute / error) fire
|
|
1008
|
+
// around execution. invokeTool handles discriminated dispatch
|
|
1009
|
+
// internally and propagates hook-enriched error metadata.
|
|
1010
|
+
traceStage('invoke');
|
|
1011
|
+
const execution = {
|
|
1012
|
+
runtime, toolId: callDescriptor.id, input: args, ctx: stampedToolCallCtx
|
|
1013
|
+
};
|
|
1014
|
+
if (options.toolExecutionTimeoutMs !== undefined && options.toolExecutionTimeoutMs > 0) {
|
|
1015
|
+
execution.timeoutMs = options.toolExecutionTimeoutMs;
|
|
1016
|
+
}
|
|
1017
|
+
const detachAgentContextHook = runtime
|
|
1018
|
+
.hooks('tools/tool')
|
|
1019
|
+
.on('contextBuild', (event) => {
|
|
1020
|
+
if (event.callId === stampedToolCallCtx.callId) {
|
|
1021
|
+
event.metadata.set(TOOL_AGENT_CONTEXT_METADATA_KEY, agentContext);
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
const rawOutput = await (async () => {
|
|
1025
|
+
try {
|
|
1026
|
+
return await runWithMcpSamplingCallback(options.scopedValues, (request) => server.server.request({ method: 'sampling/createMessage', params: request }, CreateMessageResultSchema, { relatedRequestId: extra.requestId }), () => invokeTool(execution));
|
|
1027
|
+
}
|
|
1028
|
+
finally {
|
|
1029
|
+
detachAgentContextHook();
|
|
1030
|
+
}
|
|
1031
|
+
})();
|
|
1032
|
+
traceStage('result');
|
|
1033
|
+
const outputSchema = callDescriptor.schemas.output;
|
|
1034
|
+
const lifted = liftAmbientEnvelope(rawOutput, isZodLike(outputSchema) ? outputSchema : undefined);
|
|
1035
|
+
const output = lifted.output;
|
|
1036
|
+
const ambientPrefix = formatAmbientPrefix(lifted);
|
|
1037
|
+
const envelopeMeta = ambientMeta(lifted);
|
|
1038
|
+
const unboundNotice = isStringValue(toolCallCtx.metadata?.['mcp.unboundSessionNotice'])
|
|
1039
|
+
? toolCallCtx.metadata['mcp.unboundSessionNotice']
|
|
1040
|
+
: undefined;
|
|
1041
|
+
if (isZodLike(callDescriptor.schemas.output)) {
|
|
1042
|
+
const res = callDescriptor.schemas.output.safeParse(output);
|
|
1043
|
+
if (!res.success) {
|
|
1044
|
+
const issues = (res.error?.issues ?? [])
|
|
1045
|
+
.slice(0, 4)
|
|
1046
|
+
.map((issue) => `${issue.path.join('.') || '<root>'}: ${issue.message}`)
|
|
1047
|
+
.join('; ');
|
|
1048
|
+
throw new Error(`Invalid output from '${callDescriptor.id}': ${issues}`);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
// Build content array
|
|
1052
|
+
const content = [];
|
|
1053
|
+
// Trust-tag wrapper (F4 / VIBA-34). Every text payload that reaches the
|
|
1054
|
+
// model gets a nonce-randomized <tool_output> envelope so injected
|
|
1055
|
+
// instructions inside tool output can't impersonate system content.
|
|
1056
|
+
// One nonce per call: shared across all text items and error paths.
|
|
1057
|
+
const resultPrefix = [stalePrefix, unboundNotice, ambientPrefix]
|
|
1058
|
+
.filter(Boolean)
|
|
1059
|
+
.join('\n');
|
|
1060
|
+
const wrapText = (text) => wrapToolOutputText({
|
|
1061
|
+
toolId: callDescriptor.id,
|
|
1062
|
+
tier: trustTier,
|
|
1063
|
+
text: resultPrefix ? `${resultPrefix}\n${text}` : text,
|
|
1064
|
+
nonce: callNonce
|
|
1065
|
+
}).text;
|
|
1066
|
+
// Passthrough for tools that return MCP content directly
|
|
1067
|
+
const mcpResponse = readMcpToolResponse(output);
|
|
1068
|
+
if (mcpResponse) {
|
|
1069
|
+
const wrappedContent = mcpResponse.content.map((item) => item.type === 'text' ? { ...item, text: wrapText(item.text) } : item);
|
|
1070
|
+
const result = {
|
|
1071
|
+
...mcpResponse,
|
|
1072
|
+
content: wrappedContent
|
|
1073
|
+
};
|
|
1074
|
+
if (envelopeMeta)
|
|
1075
|
+
result._meta = { ...mcpResponse._meta, ...envelopeMeta };
|
|
1076
|
+
return result;
|
|
1077
|
+
}
|
|
1078
|
+
// Handle string output
|
|
1079
|
+
if (isStringValue(output)) {
|
|
1080
|
+
const result = {
|
|
1081
|
+
content: [{ type: 'text', text: wrapText(output) }]
|
|
1082
|
+
};
|
|
1083
|
+
if (envelopeMeta)
|
|
1084
|
+
result._meta = envelopeMeta;
|
|
1085
|
+
return result;
|
|
1086
|
+
}
|
|
1087
|
+
// JSON wire form: undefined fields omitted, arrays kept. Never String() an object or array.
|
|
1088
|
+
const serialized = settleNow(() => JSON.stringify(output));
|
|
1089
|
+
if (!serialized.ok) {
|
|
1090
|
+
throw new CapabilityError('the tool output is not JSON', 'INVALID_INPUT', {
|
|
1091
|
+
reason: 'mcp-tool-output-not-json',
|
|
1092
|
+
cause: serialized.cause
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
const jsonText = JsonTextSchema.safeParse(serialized.value);
|
|
1096
|
+
if (!jsonText.success) {
|
|
1097
|
+
throw new CapabilityError('the tool output is not JSON', 'INVALID_INPUT', {
|
|
1098
|
+
reason: 'mcp-tool-output-not-json'
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
const wired = wireJsonText(jsonText.data);
|
|
1102
|
+
content.push({
|
|
1103
|
+
type: 'text',
|
|
1104
|
+
text: wrapText(wired.text)
|
|
1105
|
+
});
|
|
1106
|
+
// Add resource links if present (must include required 'name' field per MCP ResourceSchema)
|
|
1107
|
+
const resourceLinkValue = wired.structured?.resourceLinks;
|
|
1108
|
+
const resourceLinks = Array.isArray(resourceLinkValue)
|
|
1109
|
+
? z.array(ResourceLinkSchema.pick({ uri: true, mimeType: true })).parse(resourceLinkValue)
|
|
1110
|
+
: [];
|
|
1111
|
+
if (resourceLinks.length > 0) {
|
|
1112
|
+
for (const link of resourceLinks) {
|
|
1113
|
+
if (link?.uri) {
|
|
1114
|
+
const resourceLink = {
|
|
1115
|
+
type: 'resource_link',
|
|
1116
|
+
name: link.uri.split('/').pop() || link.uri,
|
|
1117
|
+
uri: link.uri
|
|
1118
|
+
};
|
|
1119
|
+
if (link.mimeType)
|
|
1120
|
+
resourceLink.mimeType = link.mimeType;
|
|
1121
|
+
content.push(resourceLink);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
const result = { content };
|
|
1126
|
+
// Plain object outputs retain typed data without requiring clients to parse text.
|
|
1127
|
+
if (wired.structured)
|
|
1128
|
+
result.structuredContent = wired.structured;
|
|
1129
|
+
if (envelopeMeta)
|
|
1130
|
+
result._meta = envelopeMeta;
|
|
1131
|
+
return result;
|
|
1132
|
+
});
|
|
1133
|
+
if (attempted.ok)
|
|
1134
|
+
return attempted.value;
|
|
1135
|
+
const error = attempted.cause;
|
|
1136
|
+
const caught = error;
|
|
1137
|
+
const message = modelSafeFailureText(caught);
|
|
1138
|
+
logger.child('tool').error(`Execution failed: ${callDescriptor.id}`, {
|
|
1139
|
+
failure: valueFreeFailure(caught),
|
|
1140
|
+
excerpt: caught instanceof Error ? redactedExcerpt(caught.message) : undefined
|
|
1141
|
+
});
|
|
1142
|
+
// Format error with structured metadata for MCP
|
|
1143
|
+
const parsedHookFailure = ToolHookFailureSchema.safeParse(error);
|
|
1144
|
+
const toolError = parsedHookFailure.success ? parsedHookFailure.data : undefined;
|
|
1145
|
+
const suggestedToolCall = getSuggestedToolCallFromErrorMetadata(toolError?.metadata);
|
|
1146
|
+
// invokeTool attaches `recoveryHint` / `retryable` onto the
|
|
1147
|
+
// thrown error after hook-bus `error` subscribers run. Surface
|
|
1148
|
+
// them even when the error isn't a structured ToolError.
|
|
1149
|
+
const hookRecoveryHint = toolError?.recoveryHint;
|
|
1150
|
+
const hookRetryable = toolError?.retryable;
|
|
1151
|
+
// Wrap error text in the same trust-tag envelope: error messages flow
|
|
1152
|
+
// back into model context exactly like success output and so share the
|
|
1153
|
+
// same prompt-injection surface. Uses the same per-call nonce for
|
|
1154
|
+
// boundary integrity across success and error paths.
|
|
1155
|
+
const unboundNotice = isStringValue(toolCallCtx?.metadata?.['mcp.unboundSessionNotice'])
|
|
1156
|
+
? toolCallCtx.metadata['mcp.unboundSessionNotice']
|
|
1157
|
+
: undefined;
|
|
1158
|
+
const errorPrefix = [stalePrefix, unboundNotice].filter(Boolean).join('\n');
|
|
1159
|
+
const errorContent = {
|
|
1160
|
+
type: 'text',
|
|
1161
|
+
text: wrapToolOutputText({
|
|
1162
|
+
toolId: callDescriptor.id,
|
|
1163
|
+
tier: trustTier,
|
|
1164
|
+
text: errorPrefix ? `${errorPrefix}\n${message}` : message,
|
|
1165
|
+
nonce: callNonce
|
|
1166
|
+
}).text
|
|
1167
|
+
};
|
|
1168
|
+
// CapabilityError is the runtime-owned cross-surface outcome contract
|
|
1169
|
+
// (@vibesdotdev/kernel, outcome/capability-error.ts) — the harvested
|
|
1170
|
+
// generalization of ToolError with the TOOL_ prefix dropped from the
|
|
1171
|
+
// code vocabulary. The explicit naming correspondence between the two
|
|
1172
|
+
// small enums (do NOT re-derive by string surgery):
|
|
1173
|
+
// NOT_FOUND ↔ TOOL_NOT_FOUND, PERMISSION_DENIED ↔ TOOL_PERMISSION_DENIED,
|
|
1174
|
+
// RATE_LIMITED ↔ TOOL_RATE_LIMITED, TIMEOUT ↔ TOOL_TIMEOUT,
|
|
1175
|
+
// CANCELLED ↔ TOOL_CANCELLED, INTERNAL ↔ TOOL_INTERNAL_ERROR,
|
|
1176
|
+
// INVALID_INPUT ↔ TOOL_VALIDATION_FAILED;
|
|
1177
|
+
// AUTH_REQUIRED / CONFLICT / UNAVAILABLE have no ToolError twin.
|
|
1178
|
+
// Per the contract ("MCP → _meta.errorCode + retryable + recoveryHint")
|
|
1179
|
+
// the stable capability code goes on the wire UNTRANSLATED — squashing
|
|
1180
|
+
// the twin-less codes into a generic TOOL_* code would lose exactly the
|
|
1181
|
+
// signal the contract exists to carry. Same _meta shape as ToolError.
|
|
1182
|
+
// This branch runs FIRST: CapabilityError also has a truthy `.code`, so
|
|
1183
|
+
// the ToolError duck-typed branch below would otherwise swallow it and
|
|
1184
|
+
// drop the contract's `retryable`/`details` projection.
|
|
1185
|
+
let structuredMeta;
|
|
1186
|
+
if (error instanceof Error && isCapabilityError(error)) {
|
|
1187
|
+
structuredMeta = {
|
|
1188
|
+
errorCode: error.code,
|
|
1189
|
+
recoveryHint: error.recoveryHint ?? hookRecoveryHint,
|
|
1190
|
+
retryable: error.retryable
|
|
1191
|
+
};
|
|
1192
|
+
if (error.details !== undefined)
|
|
1193
|
+
structuredMeta.errorMetadata = error.details;
|
|
1194
|
+
}
|
|
1195
|
+
else if (toolError?.code) {
|
|
1196
|
+
structuredMeta = {
|
|
1197
|
+
errorCode: toolError.code,
|
|
1198
|
+
recoveryHint: toolError.recoveryHint ?? hookRecoveryHint,
|
|
1199
|
+
alternatives: toolError.alternatives,
|
|
1200
|
+
errorMetadata: toolError.metadata
|
|
1201
|
+
};
|
|
1202
|
+
if (hookRetryable !== undefined)
|
|
1203
|
+
structuredMeta.retryable = hookRetryable;
|
|
1204
|
+
}
|
|
1205
|
+
else if (hookRecoveryHint || hookRetryable !== undefined || suggestedToolCall) {
|
|
1206
|
+
structuredMeta = {};
|
|
1207
|
+
if (hookRecoveryHint)
|
|
1208
|
+
structuredMeta.recoveryHint = hookRecoveryHint;
|
|
1209
|
+
if (hookRetryable !== undefined)
|
|
1210
|
+
structuredMeta.retryable = hookRetryable;
|
|
1211
|
+
}
|
|
1212
|
+
if (structuredMeta && suggestedToolCall)
|
|
1213
|
+
structuredMeta.suggestedToolCall = suggestedToolCall;
|
|
1214
|
+
const failure = projectMcpToolFailure(error);
|
|
1215
|
+
const existingCode = structuredMeta && 'errorCode' in structuredMeta
|
|
1216
|
+
? structuredMeta.errorCode
|
|
1217
|
+
: undefined;
|
|
1218
|
+
const errorResponse = {
|
|
1219
|
+
content: [errorContent],
|
|
1220
|
+
isError: true,
|
|
1221
|
+
_meta: { ...structuredMeta, errorCode: existingCode ?? failure.code }
|
|
1222
|
+
};
|
|
1223
|
+
return errorResponse;
|
|
1224
|
+
}
|
|
1225
|
+
finally {
|
|
1226
|
+
invocation.release();
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
const registration = server.registerTool(mcpWireToolName(descriptor.id, options.mcpClientId, options.customPresets), registrationOptions, handler);
|
|
1230
|
+
const entries = registeredTools.get(server) ?? new Map();
|
|
1231
|
+
entries.set(mcpWireToolName(descriptor.id, options.mcpClientId, options.customPresets), { registration, invoke: handler });
|
|
1232
|
+
registeredTools.set(server, entries);
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Stamp MCP client + session-participant identity onto the tool ctx so
|
|
1237
|
+
* session-collaborate send/mark-read can resolve the caller without
|
|
1238
|
+
* fromParticipant / participantId.
|
|
1239
|
+
*/
|
|
1240
|
+
/** One unbound-session notice per MCP transport connection, per root (views and generations share it). */
|
|
1241
|
+
function takeUnboundSessionNoticeSlot(runtime, connectionId) {
|
|
1242
|
+
const key = connectionId ?? 'stdio';
|
|
1243
|
+
const issued = requireMcpClient(runtime).unboundSessionNotices;
|
|
1244
|
+
if (issued.has(key))
|
|
1245
|
+
return false;
|
|
1246
|
+
issued.add(key);
|
|
1247
|
+
if (issued.size > 256) {
|
|
1248
|
+
const first = issued.values().next().value;
|
|
1249
|
+
if (first)
|
|
1250
|
+
issued.delete(first);
|
|
1251
|
+
}
|
|
1252
|
+
return true;
|
|
1253
|
+
}
|
|
1254
|
+
async function resolveWorkspacePath(runtime) {
|
|
1255
|
+
const attempted = await settle(() => runtime.context('runtime/workspace-path'));
|
|
1256
|
+
if (!attempted.ok) {
|
|
1257
|
+
// An unbound workspace is a fact, not a failure; anything else is logged with its reason.
|
|
1258
|
+
const error = attempted.cause;
|
|
1259
|
+
if (!(error instanceof Error && isCapabilityError(error) && error.reason === 'workspace-unbound')) {
|
|
1260
|
+
getLogger('mcp', runtime).debug('workspace path unavailable for the tool call', { failure: valueFreeFailure(error) });
|
|
1261
|
+
}
|
|
1262
|
+
return undefined;
|
|
1263
|
+
}
|
|
1264
|
+
return isStringValue(attempted.value) && attempted.value.length > 0 ? attempted.value : undefined;
|
|
1265
|
+
}
|
|
1266
|
+
async function stampMcpSessionCaller(runtime, server, ctx, transportSessionId, mode, toolId, input, logger) {
|
|
1267
|
+
const mcpSessionId = transportSessionId ?? null;
|
|
1268
|
+
let transportParticipantId;
|
|
1269
|
+
let sessionUtils;
|
|
1270
|
+
const stamped = await settle(async () => {
|
|
1271
|
+
sessionUtils = await import('@vibesdotdev/session/mcp/utils/participant');
|
|
1272
|
+
const mcpSessionRegistry = await resolveMcpTransportRegistry(runtime);
|
|
1273
|
+
const clientVersion = server.server.getClientVersion?.();
|
|
1274
|
+
const existing = mcpSessionRegistry.getClientInfo(mcpSessionId);
|
|
1275
|
+
// The connection declares its harness session; a shared host never reads
|
|
1276
|
+
// its own environment for it (that is whichever harness started the host).
|
|
1277
|
+
const harnessSessionId = ctx.caller?.harnessSessionId ?? existing?.harnessSessionId;
|
|
1278
|
+
const agentId = ctx.caller?.agentId ?? existing?.agentId;
|
|
1279
|
+
if (clientVersion?.name || existing || harnessSessionId || agentId) {
|
|
1280
|
+
const clientInfo = {
|
|
1281
|
+
...existing,
|
|
1282
|
+
name: clientVersion?.name ?? existing?.name ?? 'mcp-client',
|
|
1283
|
+
version: clientVersion?.version ?? existing?.version,
|
|
1284
|
+
transport: mode === 'http' ? 'http' : 'stdio',
|
|
1285
|
+
connectionId: mcpSessionId ?? existing?.connectionId
|
|
1286
|
+
};
|
|
1287
|
+
if (agentId)
|
|
1288
|
+
clientInfo.agentId = agentId;
|
|
1289
|
+
if (harnessSessionId)
|
|
1290
|
+
clientInfo.harnessSessionId = harnessSessionId;
|
|
1291
|
+
mcpSessionRegistry.setClientInfo(mcpSessionId, clientInfo);
|
|
1292
|
+
}
|
|
1293
|
+
if (mcpSessionId) {
|
|
1294
|
+
const previous = mcpSessionRegistry.getConnectionState(mcpSessionId);
|
|
1295
|
+
transportParticipantId =
|
|
1296
|
+
(previous && 'participantId' in previous && isStringValue(previous.participantId) ? previous.participantId : undefined) ??
|
|
1297
|
+
previous?.agentId ??
|
|
1298
|
+
undefined;
|
|
1299
|
+
mcpSessionRegistry.setConnectionState(mcpSessionId, {
|
|
1300
|
+
...previous,
|
|
1301
|
+
transport: mode === 'http' ? 'http' : 'stdio',
|
|
1302
|
+
connectionId: mcpSessionId,
|
|
1303
|
+
sessionId: previous?.sessionId,
|
|
1304
|
+
agentId: previous?.agentId ?? null,
|
|
1305
|
+
agentSlug: previous?.agentSlug ?? null
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
if (!stamped.ok) {
|
|
1310
|
+
// Session registry is optional for non-session MCP surfaces, but a real
|
|
1311
|
+
// failure still degrades downstream participant resolution — leave a
|
|
1312
|
+
// diagnostic instead of vanishing.
|
|
1313
|
+
logger.warn('MCP session caller stamping failed; participant resolution may degrade', {
|
|
1314
|
+
failure: valueFreeFailure(stamped.cause),
|
|
1315
|
+
toolId
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
const isSessionFamilyTool = toolId === 'session' || toolId.startsWith('session-');
|
|
1319
|
+
const { participantId: _hintParticipant, sessionId: _hintSession, ...unboundCaller } = ctx.caller ?? {};
|
|
1320
|
+
if (!runtime.hasContext('session/manager')) {
|
|
1321
|
+
return { ...ctx, caller: unboundCaller };
|
|
1322
|
+
}
|
|
1323
|
+
const receiptRestore = toolId === 'session' ? receiptRestoreInputSchema.safeParse(input) : undefined;
|
|
1324
|
+
const receiptRestoreParticipantId = receiptRestore?.success
|
|
1325
|
+
? receiptRestore.data.participantId
|
|
1326
|
+
: undefined;
|
|
1327
|
+
let authoritativeParticipantId = ctx.caller?.participantId ?? transportParticipantId ?? receiptRestoreParticipantId;
|
|
1328
|
+
// The session family is the only consumer of caller.sessionId stamping; the
|
|
1329
|
+
// no-durable-session branch below exempted nothing, which gated stateless
|
|
1330
|
+
// tools behind session-authority repair.
|
|
1331
|
+
const reconciled = await settle(async () => {
|
|
1332
|
+
const manager = await runtime.context('session/manager');
|
|
1333
|
+
if (!isSessionFamilyTool) {
|
|
1334
|
+
// Ordinary tools cannot self-identify as another seat through request metadata.
|
|
1335
|
+
authoritativeParticipantId = await manager.resolveMcpParticipant({}, mcpSessionId);
|
|
1336
|
+
}
|
|
1337
|
+
else {
|
|
1338
|
+
authoritativeParticipantId ??= await manager.resolveMcpParticipant({}, mcpSessionId, ctx.caller);
|
|
1339
|
+
}
|
|
1340
|
+
if (!authoritativeParticipantId) {
|
|
1341
|
+
throw new Error('Session manager did not resolve an MCP participant');
|
|
1342
|
+
}
|
|
1343
|
+
const participantSession = await manager.ensureParticipantSession(authoritativeParticipantId);
|
|
1344
|
+
if (!participantSession) {
|
|
1345
|
+
// A brand-new MCP body has no session by definition. Permit discovery
|
|
1346
|
+
// (list/search/status/get) and init so the caller can bind or create,
|
|
1347
|
+
// and strip any stale prepared session so neither can inherit another
|
|
1348
|
+
// body's custody.
|
|
1349
|
+
const workspacePath = await resolveWorkspacePath(runtime);
|
|
1350
|
+
if (!isSessionFamilyTool) {
|
|
1351
|
+
// Tools outside the session family never consume
|
|
1352
|
+
// caller.sessionId; failing them here gated stateless tools
|
|
1353
|
+
// (calculate & co.) behind session-authority repair. Stamp the
|
|
1354
|
+
// participant identity only, and tell the agent once per
|
|
1355
|
+
// connection that it is unbound.
|
|
1356
|
+
const { sessionId: _staleSessionId, ...caller } = ctx.caller ?? {};
|
|
1357
|
+
let notice;
|
|
1358
|
+
if (takeUnboundSessionNoticeSlot(runtime, mcpSessionId) && sessionUtils) {
|
|
1359
|
+
const guidance = await sessionUtils.buildUnboundSessionGuidance(manager, {
|
|
1360
|
+
workspacePath
|
|
1361
|
+
});
|
|
1362
|
+
notice = sessionUtils.renderUnboundSessionGuidance(guidance);
|
|
1363
|
+
}
|
|
1364
|
+
const metadata = { ...ctx.metadata };
|
|
1365
|
+
if (notice)
|
|
1366
|
+
metadata['mcp.unboundSessionNotice'] = notice;
|
|
1367
|
+
return {
|
|
1368
|
+
...ctx,
|
|
1369
|
+
caller: { ...caller, participantId: authoritativeParticipantId },
|
|
1370
|
+
metadata
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
const getNeedsId = toolId === 'session' &&
|
|
1374
|
+
input.action === 'get' &&
|
|
1375
|
+
!explicitSessionTargetSchema.safeParse(input).success;
|
|
1376
|
+
// Resume repairs an inactive binding. It must reach its owner with the
|
|
1377
|
+
// participant identity, but without borrowing the closed session's custody.
|
|
1378
|
+
const explicitResume = toolId === 'session' && explicitResumeInputSchema.safeParse(input).success;
|
|
1379
|
+
if (!getNeedsId &&
|
|
1380
|
+
(explicitResume || sessionUtils?.isSessionDiscoveryCall(toolId, input.action) ||
|
|
1381
|
+
(!sessionUtils &&
|
|
1382
|
+
toolId === 'session' &&
|
|
1383
|
+
(input.action === 'init' || input.action === 'status')))) {
|
|
1384
|
+
const { sessionId: _staleSessionId, ...caller } = ctx.caller ?? {};
|
|
1385
|
+
return {
|
|
1386
|
+
...ctx,
|
|
1387
|
+
caller: { ...caller, participantId: authoritativeParticipantId }
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
if (sessionUtils) {
|
|
1391
|
+
const fields = await sessionUtils.unboundSessionErrorFields(manager, { workspacePath });
|
|
1392
|
+
throw sessionUtils.createSessionToolError('SESSION_NOT_ACTIVE', fields.message, fields.hint, fields.details);
|
|
1393
|
+
}
|
|
1394
|
+
throw new Error(`Participant ${authoritativeParticipantId} has no durable active session (call session { action: "init" } to bind one, or resume the archived session explicitly)`);
|
|
1395
|
+
}
|
|
1396
|
+
if (mcpSessionId) {
|
|
1397
|
+
sessionUtils?.bindRuntimeConnectionReceipt(runtime, mcpSessionId, authoritativeParticipantId, participantSession.id);
|
|
1398
|
+
}
|
|
1399
|
+
return {
|
|
1400
|
+
...ctx,
|
|
1401
|
+
caller: {
|
|
1402
|
+
...ctx.caller,
|
|
1403
|
+
participantId: authoritativeParticipantId,
|
|
1404
|
+
sessionId: participantSession.id
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
});
|
|
1408
|
+
if (reconciled.ok)
|
|
1409
|
+
return reconciled.value;
|
|
1410
|
+
const error = reconciled.cause;
|
|
1411
|
+
if (!isSessionFamilyTool) {
|
|
1412
|
+
// Session-authority failures (storage unavailable, unreadable routing
|
|
1413
|
+
// rows) must not take down tools that never touch session state. Run
|
|
1414
|
+
// unqualified rather than wedging stateless tools behind identity
|
|
1415
|
+
// repair.
|
|
1416
|
+
logger.warn('MCP participant reconciliation failed; running non-session tool without session identity', { toolId, failure: valueFreeFailure(error) });
|
|
1417
|
+
return { ...ctx, caller: unboundCaller };
|
|
1418
|
+
}
|
|
1419
|
+
if (sessionUtils?.isSessionToolError(error, 'SESSION_NOT_ACTIVE'))
|
|
1420
|
+
throw error;
|
|
1421
|
+
throw new Error(`Cannot reconcile MCP participant ${authoritativeParticipantId ?? 'from the session authority'} to a session`, { cause: error });
|
|
1422
|
+
}
|
|
1423
|
+
//# sourceMappingURL=server.js.map
|