@try-works/dsh-tool-mode 0.0.0-stage → 0.5.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -2
- package/bin/dsh-tool-mode-credentials.mjs +20 -0
- package/bin/dsh-tool-mode-mcp-stdio.mjs +80 -0
- package/bin/dsh-tool-mode.mjs +211 -0
- package/builtins.lock.json +26 -0
- package/catalog/desert-ant-models.json +693 -0
- package/catalog/playbooks.json +531 -0
- package/client/client.model-tools.js +1910 -0
- package/client/index.js +4039 -0
- package/cordis.patch.yml +39 -0
- package/lib/adapters/index.d.ts +61 -0
- package/lib/adapters/index.d.ts.map +1 -0
- package/lib/adapters/index.js +82 -0
- package/lib/adapters/types.d.ts +106 -0
- package/lib/adapters/types.d.ts.map +1 -0
- package/lib/adapters/types.js +85 -0
- package/lib/approval.d.ts +189 -0
- package/lib/approval.d.ts.map +1 -0
- package/lib/approval.js +510 -0
- package/lib/asset-acquisition.d.ts +149 -0
- package/lib/asset-acquisition.d.ts.map +1 -0
- package/lib/asset-acquisition.js +175 -0
- package/lib/authority.d.ts +185 -0
- package/lib/authority.d.ts.map +1 -0
- package/lib/authority.js +223 -0
- package/lib/bindings.d.ts +144 -0
- package/lib/bindings.d.ts.map +1 -0
- package/lib/bindings.js +314 -0
- package/lib/builtins.d.ts +27 -0
- package/lib/builtins.d.ts.map +1 -0
- package/lib/builtins.js +37 -0
- package/lib/capability.d.ts +58 -0
- package/lib/capability.d.ts.map +1 -0
- package/lib/capability.js +192 -0
- package/lib/capture-path.d.ts +97 -0
- package/lib/capture-path.d.ts.map +1 -0
- package/lib/capture-path.js +152 -0
- package/lib/catalog.d.ts +37 -0
- package/lib/catalog.d.ts.map +1 -0
- package/lib/catalog.js +274 -0
- package/lib/contract-payload.d.ts +39 -0
- package/lib/contract-payload.d.ts.map +1 -0
- package/lib/contract-payload.js +56 -0
- package/lib/custom-tools/compile.d.ts +25 -0
- package/lib/custom-tools/compile.d.ts.map +1 -0
- package/lib/custom-tools/compile.js +61 -0
- package/lib/custom-tools/composite-executor.d.ts +42 -0
- package/lib/custom-tools/composite-executor.d.ts.map +1 -0
- package/lib/custom-tools/composite-executor.js +113 -0
- package/lib/custom-tools/dispatch.d.ts +28 -0
- package/lib/custom-tools/dispatch.d.ts.map +1 -0
- package/lib/custom-tools/dispatch.js +34 -0
- package/lib/custom-tools/http-executor.d.ts +48 -0
- package/lib/custom-tools/http-executor.d.ts.map +1 -0
- package/lib/custom-tools/http-executor.js +241 -0
- package/lib/custom-tools/index.d.ts +12 -0
- package/lib/custom-tools/index.d.ts.map +1 -0
- package/lib/custom-tools/index.js +12 -0
- package/lib/custom-tools/mcp-surface.d.ts +14 -0
- package/lib/custom-tools/mcp-surface.d.ts.map +1 -0
- package/lib/custom-tools/mcp-surface.js +41 -0
- package/lib/custom-tools/prompt-executor.d.ts +39 -0
- package/lib/custom-tools/prompt-executor.d.ts.map +1 -0
- package/lib/custom-tools/prompt-executor.js +59 -0
- package/lib/custom-tools/registry.d.ts +69 -0
- package/lib/custom-tools/registry.d.ts.map +1 -0
- package/lib/custom-tools/registry.js +130 -0
- package/lib/custom-tools/schema.d.ts +26 -0
- package/lib/custom-tools/schema.d.ts.map +1 -0
- package/lib/custom-tools/schema.js +375 -0
- package/lib/custom-tools/service.d.ts +90 -0
- package/lib/custom-tools/service.d.ts.map +1 -0
- package/lib/custom-tools/service.js +203 -0
- package/lib/custom-tools/store.d.ts +61 -0
- package/lib/custom-tools/store.d.ts.map +1 -0
- package/lib/custom-tools/store.js +240 -0
- package/lib/custom-tools/types.d.ts +131 -0
- package/lib/custom-tools/types.d.ts.map +1 -0
- package/lib/custom-tools/types.js +11 -0
- package/lib/decision-combine.d.ts +113 -0
- package/lib/decision-combine.d.ts.map +1 -0
- package/lib/decision-combine.js +156 -0
- package/lib/decision-evaluate.d.ts +185 -0
- package/lib/decision-evaluate.d.ts.map +1 -0
- package/lib/decision-evaluate.js +227 -0
- package/lib/deployment-coordinator.d.ts +213 -0
- package/lib/deployment-coordinator.d.ts.map +1 -0
- package/lib/deployment-coordinator.js +274 -0
- package/lib/diagnostics.d.ts +169 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +136 -0
- package/lib/discovery-contracts.d.ts +14 -0
- package/lib/discovery-contracts.d.ts.map +1 -0
- package/lib/discovery-contracts.js +90 -0
- package/lib/discovery-describe.d.ts +35 -0
- package/lib/discovery-describe.d.ts.map +1 -0
- package/lib/discovery-describe.js +99 -0
- package/lib/embedding-index.d.ts +55 -0
- package/lib/embedding-index.d.ts.map +1 -0
- package/lib/embedding-index.js +126 -0
- package/lib/extension-dependencies.d.ts +355 -0
- package/lib/extension-dependencies.d.ts.map +1 -0
- package/lib/extension-dependencies.js +987 -0
- package/lib/extension-dispatch.d.ts +150 -0
- package/lib/extension-dispatch.d.ts.map +1 -0
- package/lib/extension-dispatch.js +178 -0
- package/lib/extension-page-state.d.ts +69 -0
- package/lib/extension-page-state.d.ts.map +1 -0
- package/lib/extension-page-state.js +82 -0
- package/lib/extension-registrar.d.ts +116 -0
- package/lib/extension-registrar.d.ts.map +1 -0
- package/lib/extension-registrar.js +144 -0
- package/lib/extensions-api.d.ts +346 -0
- package/lib/extensions-api.d.ts.map +1 -0
- package/lib/extensions-api.js +17 -0
- package/lib/extensions-remote.d.ts +57 -0
- package/lib/extensions-remote.d.ts.map +1 -0
- package/lib/extensions-remote.js +69 -0
- package/lib/extensions-service.d.ts +568 -0
- package/lib/extensions-service.d.ts.map +1 -0
- package/lib/extensions-service.js +2507 -0
- package/lib/harness-budgets.d.ts +158 -0
- package/lib/harness-budgets.d.ts.map +1 -0
- package/lib/harness-budgets.js +152 -0
- package/lib/harness-llm.d.ts +138 -0
- package/lib/harness-llm.d.ts.map +1 -0
- package/lib/harness-llm.js +218 -0
- package/lib/harness-serving.d.ts +115 -0
- package/lib/harness-serving.d.ts.map +1 -0
- package/lib/harness-serving.js +176 -0
- package/lib/headless.d.ts +97 -0
- package/lib/headless.d.ts.map +1 -0
- package/lib/headless.js +407 -0
- package/lib/http-acquisition.d.ts +27 -0
- package/lib/http-acquisition.d.ts.map +1 -0
- package/lib/http-acquisition.js +140 -0
- package/lib/index.d.ts +546 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +5197 -0
- package/lib/invocation-accounting.d.ts +169 -0
- package/lib/invocation-accounting.d.ts.map +1 -0
- package/lib/invocation-accounting.js +148 -0
- package/lib/jev.d.ts +218 -0
- package/lib/jev.d.ts.map +1 -0
- package/lib/jev.js +366 -0
- package/lib/legacy-migration.d.ts +95 -0
- package/lib/legacy-migration.d.ts.map +1 -0
- package/lib/legacy-migration.js +429 -0
- package/lib/license-basis.d.ts +29 -0
- package/lib/license-basis.d.ts.map +1 -0
- package/lib/license-basis.js +58 -0
- package/lib/managed-workers.d.ts +158 -0
- package/lib/managed-workers.d.ts.map +1 -0
- package/lib/managed-workers.js +269 -0
- package/lib/mcp/auth-selection.d.ts +35 -0
- package/lib/mcp/auth-selection.d.ts.map +1 -0
- package/lib/mcp/auth-selection.js +64 -0
- package/lib/mcp/client-grant.d.ts +89 -0
- package/lib/mcp/client-grant.d.ts.map +1 -0
- package/lib/mcp/client-grant.js +63 -0
- package/lib/mcp/credential-cli.d.ts +38 -0
- package/lib/mcp/credential-cli.d.ts.map +1 -0
- package/lib/mcp/credential-cli.js +148 -0
- package/lib/mcp/credential-store.d.ts +143 -0
- package/lib/mcp/credential-store.d.ts.map +1 -0
- package/lib/mcp/credential-store.js +461 -0
- package/lib/mcp/export-policy.d.ts +25 -0
- package/lib/mcp/export-policy.d.ts.map +1 -0
- package/lib/mcp/export-policy.js +31 -0
- package/lib/mcp/host-binding.d.ts +144 -0
- package/lib/mcp/host-binding.d.ts.map +1 -0
- package/lib/mcp/host-binding.js +280 -0
- package/lib/mcp/http.d.ts +180 -0
- package/lib/mcp/http.d.ts.map +1 -0
- package/lib/mcp/http.js +602 -0
- package/lib/mcp/oauth.d.ts +149 -0
- package/lib/mcp/oauth.d.ts.map +1 -0
- package/lib/mcp/oauth.js +396 -0
- package/lib/mcp/protocol.d.ts +329 -0
- package/lib/mcp/protocol.d.ts.map +1 -0
- package/lib/mcp/protocol.js +459 -0
- package/lib/mcp/rate-limit.d.ts +68 -0
- package/lib/mcp/rate-limit.d.ts.map +1 -0
- package/lib/mcp/rate-limit.js +114 -0
- package/lib/mcp/sdk-v2.d.ts +17 -0
- package/lib/mcp/sdk-v2.d.ts.map +1 -0
- package/lib/mcp/sdk-v2.js +204 -0
- package/lib/mcp/server.d.ts +149 -0
- package/lib/mcp/server.d.ts.map +1 -0
- package/lib/mcp/server.js +302 -0
- package/lib/mcp/stdio.d.ts +80 -0
- package/lib/mcp/stdio.d.ts.map +1 -0
- package/lib/mcp/stdio.js +184 -0
- package/lib/mcp/subscriptions.d.ts +177 -0
- package/lib/mcp/subscriptions.d.ts.map +1 -0
- package/lib/mcp/subscriptions.js +288 -0
- package/lib/mcp-status.d.ts +96 -0
- package/lib/mcp-status.d.ts.map +1 -0
- package/lib/mcp-status.js +70 -0
- package/lib/mcp-usage.d.ts +110 -0
- package/lib/mcp-usage.d.ts.map +1 -0
- package/lib/mcp-usage.js +189 -0
- package/lib/model-qualification.d.ts +192 -0
- package/lib/model-qualification.d.ts.map +1 -0
- package/lib/model-qualification.js +178 -0
- package/lib/planning.d.ts +256 -0
- package/lib/planning.d.ts.map +1 -0
- package/lib/planning.js +471 -0
- package/lib/playbook.d.ts +42 -0
- package/lib/playbook.d.ts.map +1 -0
- package/lib/playbook.js +65 -0
- package/lib/prepared-assets.d.ts +269 -0
- package/lib/prepared-assets.d.ts.map +1 -0
- package/lib/prepared-assets.js +471 -0
- package/lib/provider-bridge.d.ts +107 -0
- package/lib/provider-bridge.d.ts.map +1 -0
- package/lib/provider-bridge.js +423 -0
- package/lib/ptc-authority.d.ts +154 -0
- package/lib/ptc-authority.d.ts.map +1 -0
- package/lib/ptc-authority.js +242 -0
- package/lib/ptc-confinement.d.ts +60 -0
- package/lib/ptc-confinement.d.ts.map +1 -0
- package/lib/ptc-confinement.js +212 -0
- package/lib/ptc-runtime.d.ts +166 -0
- package/lib/ptc-runtime.d.ts.map +1 -0
- package/lib/ptc-runtime.js +231 -0
- package/lib/publication.d.ts +161 -0
- package/lib/publication.d.ts.map +1 -0
- package/lib/publication.js +181 -0
- package/lib/receipt-store.d.ts +57 -0
- package/lib/receipt-store.d.ts.map +1 -0
- package/lib/receipt-store.js +143 -0
- package/lib/receipt.d.ts +231 -0
- package/lib/receipt.d.ts.map +1 -0
- package/lib/receipt.js +165 -0
- package/lib/registrar.d.ts +75 -0
- package/lib/registrar.d.ts.map +1 -0
- package/lib/registrar.js +152 -0
- package/lib/resource-governance.d.ts +204 -0
- package/lib/resource-governance.d.ts.map +1 -0
- package/lib/resource-governance.js +213 -0
- package/lib/server-lifecycle.d.ts +175 -0
- package/lib/server-lifecycle.d.ts.map +1 -0
- package/lib/server-lifecycle.js +269 -0
- package/lib/skills.d.ts +31 -0
- package/lib/skills.d.ts.map +1 -0
- package/lib/skills.js +170 -0
- package/lib/speech-bridge.d.ts +74 -0
- package/lib/speech-bridge.d.ts.map +1 -0
- package/lib/speech-bridge.js +115 -0
- package/lib/state.d.ts +100 -0
- package/lib/state.d.ts.map +1 -0
- package/lib/state.js +160 -0
- package/lib/title-proposal.d.ts +194 -0
- package/lib/title-proposal.d.ts.map +1 -0
- package/lib/title-proposal.js +244 -0
- package/lib/tool-inventory.d.ts +90 -0
- package/lib/tool-inventory.d.ts.map +1 -0
- package/lib/tool-inventory.js +102 -0
- package/lib/tool-mode-run.d.ts +310 -0
- package/lib/tool-mode-run.d.ts.map +1 -0
- package/lib/tool-mode-run.js +411 -0
- package/lib/tool-sources.d.ts +80 -0
- package/lib/tool-sources.d.ts.map +1 -0
- package/lib/tool-sources.js +123 -0
- package/lib/tool-usage-projection.d.ts +390 -0
- package/lib/tool-usage-projection.d.ts.map +1 -0
- package/lib/tool-usage-projection.js +1038 -0
- package/lib/tool-usage-report.d.ts +153 -0
- package/lib/tool-usage-report.d.ts.map +1 -0
- package/lib/tool-usage-report.js +274 -0
- package/lib/transport.d.ts +158 -0
- package/lib/transport.d.ts.map +1 -0
- package/lib/transport.js +332 -0
- package/lib/types.d.ts +134 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +8 -0
- package/lib/update-surface.d.ts +79 -0
- package/lib/update-surface.d.ts.map +1 -0
- package/lib/update-surface.js +224 -0
- package/lib/version.d.ts +3 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +27 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts +13 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contract-metadata.js +85 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts +16 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/lang-detect.js +87 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts +38 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/standard.js +445 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts +20 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/stt-transcribe.js +112 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts +14 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-embed.js +63 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts +14 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/contracts/text-rerank.js +76 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts +82 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/dsh.js +274 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +26 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.js +53 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +133 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.js +64 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +121 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.js +246 -0
- package/node_modules/@try-works/tool-mode-contracts/package.json +35 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/bin/dsh-tool-mode.mjs +20 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts +80 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/cli.js +533 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts +86 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/declarative-recipes.js +145 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts +70 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/discovery.js +201 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts +57 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/errors.js +83 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts +33 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/index.js +33 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts +86 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/archive.js +191 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +137 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.js +349 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +274 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.js +932 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts +210 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/migration.js +368 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts +63 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/runtime-link.js +149 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts +35 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/source-directory.js +179 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts +179 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/staged.js +299 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +395 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.js +839 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts +44 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/tar-gzip-source.js +255 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts +185 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/update-flow.js +504 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts +17 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/zip-source.js +80 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts +221 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/licensing.js +188 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts +52 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/load.js +102 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts +171 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/manifest.js +271 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts +242 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/runtime.js +58 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts +128 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/state.js +113 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts +41 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/test-host.js +52 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts +2 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker/worker-child.js +351 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts +157 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/worker.js +688 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/package.json +54 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/schema/extension-manifest-1.1.0.schema.json +253 -0
- package/node_modules/@try-works/tool-mode-recipes/bin/tool-mode-recipe.mjs +15 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts +460 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/broker.js +647 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +61 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.js +1106 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts +237 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/dsh-adapter.js +205 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts +85 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/fixture-provider.js +176 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts +119 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/images.js +201 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts +25 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/index.js +25 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts +345 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/recipe-format.js +1465 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts +51 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/templates.js +310 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts +231 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.d.ts.map +1 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/titles.js +435 -0
- package/node_modules/@try-works/tool-mode-recipes/package.json +56 -0
- package/node_modules/@try-works/tool-mode-recipes/recipes/describe-image.json +38 -0
- package/node_modules/@try-works/tool-mode-recipes/recipes/text-title.json +45 -0
- package/node_modules/@try-works/tool-mode-recipes/schema/harness-model-recipe.schema.json +273 -0
- package/package.json +127 -4
- package/remote/spec.mjs +993 -0
- package/remote/typert.host.mjs +14 -0
- package/remote/typert.remote-client.mjs +16 -0
package/lib/mcp/http.js
ADDED
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The loopback HTTP transport (MCP-02, MCP-03, SEC-02, P26-05..P26-08, P26-15).
|
|
3
|
+
*
|
|
4
|
+
* `P26-15` is decisive about the shape of this: *"Plain unauthenticated loopback is
|
|
5
|
+
* not this product's default."* So the endpoint requires a credential, and because
|
|
6
|
+
* the OAuth 2.1 resource-server profile is NOT implemented, the credential is
|
|
7
|
+
* offered explicitly as **custom static-token authentication** and makes no OAuth
|
|
8
|
+
* conformance claim. The requirement permits that mode while forbidding the claim;
|
|
9
|
+
* implementing half of OAuth and calling it OAuth would be the failure it names.
|
|
10
|
+
*
|
|
11
|
+
* The handler is a pure function of a request description rather than a socket
|
|
12
|
+
* listener. Every security property here — the loopback check, Host and Origin
|
|
13
|
+
* validation, size limits, authentication — is then testable without binding a
|
|
14
|
+
* port, which matters because these are exactly the paths a socket-based test
|
|
15
|
+
* would leave to manual checking.
|
|
16
|
+
*
|
|
17
|
+
* @module @try-works/dsh-tool-mode/mcp/http
|
|
18
|
+
*/
|
|
19
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
20
|
+
import { toWebRequest } from '@modelcontextprotocol/node';
|
|
21
|
+
import { assertHeadersMatch, decodeHeaderValue, ERROR_CODES, errorResponse, negotiateVersion, parseRequestMetadata, unsupportedRequiredCapabilities, WireError, } from "./protocol.js";
|
|
22
|
+
import { serveSdkV2Request } from "./sdk-v2.js";
|
|
23
|
+
import { oauthChallenge, oauthErrorStatus } from "./oauth.js";
|
|
24
|
+
import { createRateLimiter, DEFAULT_RATE_LIMIT_POLICY, } from "./rate-limit.js";
|
|
25
|
+
import { resolveClientGrant } from "./client-grant.js";
|
|
26
|
+
/** A conservative default: loopback, the illustrative port, a small body cap,
|
|
27
|
+
* and the default per-client rate limit (AT-12). */
|
|
28
|
+
export const DEFAULT_HTTP_CONFIG = {
|
|
29
|
+
path: '/mcp',
|
|
30
|
+
host: '127.0.0.1',
|
|
31
|
+
port: 3765,
|
|
32
|
+
maxBodyBytes: 4 * 1024 * 1024,
|
|
33
|
+
allowedOrigins: [],
|
|
34
|
+
rateLimit: DEFAULT_RATE_LIMIT_POLICY,
|
|
35
|
+
};
|
|
36
|
+
/** Whether an address is loopback. */
|
|
37
|
+
export function isLoopback(address) {
|
|
38
|
+
// `::ffff:127.0.0.1` is how a v4 loopback appears on a dual-stack socket, and
|
|
39
|
+
// `::1` is v6 loopback. Treating either as remote would break a correct client;
|
|
40
|
+
// treating a real remote address as loopback would defeat the binding.
|
|
41
|
+
const normalised = address.startsWith('::ffff:') ? address.slice(7) : address;
|
|
42
|
+
return normalised === '127.0.0.1' || normalised === '::1' || normalised.startsWith('127.');
|
|
43
|
+
}
|
|
44
|
+
/** Render a response. */
|
|
45
|
+
function respond(status, body, headers = {}) {
|
|
46
|
+
return {
|
|
47
|
+
status,
|
|
48
|
+
headers: { 'content-type': 'application/json; charset=utf-8', ...headers },
|
|
49
|
+
body: typeof body === 'string' ? body : JSON.stringify(body),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Compare a presented token against the accepted one in constant time.
|
|
54
|
+
*
|
|
55
|
+
* Length is compared first because `timingSafeEqual` throws on a length mismatch;
|
|
56
|
+
* that leaks length, which is not a useful secret here since the token is generated
|
|
57
|
+
* by the operator rather than guessed byte by byte.
|
|
58
|
+
* @param presented - the token from the request.
|
|
59
|
+
* @param accepted - the configured token.
|
|
60
|
+
* @returns whether they match.
|
|
61
|
+
*/
|
|
62
|
+
function tokenMatches(presented, accepted) {
|
|
63
|
+
const left = Buffer.from(presented, 'utf8');
|
|
64
|
+
const right = Buffer.from(accepted, 'utf8');
|
|
65
|
+
if (left.length !== right.length)
|
|
66
|
+
return false;
|
|
67
|
+
return timingSafeEqual(left, right);
|
|
68
|
+
}
|
|
69
|
+
/** Validate the Host header when the client supplies one. */
|
|
70
|
+
function hostHeaderAllowed(header, config) {
|
|
71
|
+
if (header === undefined || header.length === 0)
|
|
72
|
+
return true;
|
|
73
|
+
const value = header.toLowerCase();
|
|
74
|
+
const authority = value.startsWith('[')
|
|
75
|
+
? value.slice(1, value.indexOf(']'))
|
|
76
|
+
: (value.split(':')[0] ?? '');
|
|
77
|
+
const configured = config.host.toLowerCase();
|
|
78
|
+
const allowed = new Set([configured]);
|
|
79
|
+
if (isLoopback(configured)) {
|
|
80
|
+
allowed.add('localhost');
|
|
81
|
+
allowed.add('127.0.0.1');
|
|
82
|
+
allowed.add('::1');
|
|
83
|
+
}
|
|
84
|
+
return allowed.has(authority);
|
|
85
|
+
}
|
|
86
|
+
function createRequestGate(config, host) {
|
|
87
|
+
/**
|
|
88
|
+
* THE LIMITER IS PER-HANDLER STATE, in memory and unpersisted (AT-12).
|
|
89
|
+
*
|
|
90
|
+
* One limiter per bound endpoint, keyed by the authenticated client identity.
|
|
91
|
+
* Absent configuration means the DEFAULT policy applies, so the endpoint is
|
|
92
|
+
* bounded even when the operator configured nothing; `false` disables it; a
|
|
93
|
+
* policy overrides it. Construction validates the policy, so a broken
|
|
94
|
+
* configuration fails when the transport starts rather than mid-request.
|
|
95
|
+
*/
|
|
96
|
+
const effectivePolicy = config.rateLimit === false
|
|
97
|
+
? DEFAULT_RATE_LIMIT_POLICY
|
|
98
|
+
: (config.rateLimit ?? DEFAULT_RATE_LIMIT_POLICY);
|
|
99
|
+
const limiter = config.rateLimit === false
|
|
100
|
+
? undefined
|
|
101
|
+
: createRateLimiter(effectivePolicy);
|
|
102
|
+
return async (request) => {
|
|
103
|
+
// 0. DISCOVERY METADATA (RFC 8414/9728), when the OAuth scheme serves it.
|
|
104
|
+
// Metadata endpoints are the ONE thing served unauthenticated: a client must
|
|
105
|
+
// learn the issuer, audience and token endpoint BEFORE it can hold a token.
|
|
106
|
+
if (config.auth.scheme === 'oauth-resource-server') {
|
|
107
|
+
if (request.method === 'GET' && request.path === '/.well-known/oauth-protected-resource') {
|
|
108
|
+
return respond(200, config.auth.server.protectedResourceMetadata());
|
|
109
|
+
}
|
|
110
|
+
if (request.method === 'GET' && request.path === '/.well-known/oauth-authorization-server') {
|
|
111
|
+
return respond(200, config.auth.server.authorizationServerMetadata());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 1. Loopback only (MCP-02, SEC-02). Checked against the SOCKET address, not a
|
|
115
|
+
// header: a header is caller-supplied, and trusting one would make the binding
|
|
116
|
+
// decorative.
|
|
117
|
+
if (!isLoopback(request.remoteAddress)) {
|
|
118
|
+
return respond(403, { error: 'this endpoint serves loopback clients only' });
|
|
119
|
+
}
|
|
120
|
+
if (!hostHeaderAllowed(request.headers.host, config)) {
|
|
121
|
+
return respond(403, { error: 'the Host header is not approved for this endpoint' });
|
|
122
|
+
}
|
|
123
|
+
// 2. Size limit (SEC-02), measured on the received bytes.
|
|
124
|
+
const bodyBytes = Buffer.byteLength(request.body, 'utf8');
|
|
125
|
+
if (bodyBytes > config.maxBodyBytes) {
|
|
126
|
+
// 413 rather than 400: the request may be perfectly well formed and merely
|
|
127
|
+
// too large, and a client needs to tell those apart.
|
|
128
|
+
/**
|
|
129
|
+
* NO `id` — the pinned schema neither permits nor requires one here.
|
|
130
|
+
*
|
|
131
|
+
* This wrote `id: null`, which `RequestId` (`type: ["string","integer"]`) REJECTS:
|
|
132
|
+
* `null` is not a string and not an integer. `JSONRPCErrorResponse` requires only
|
|
133
|
+
* `["error","jsonrpc"]`, so the id is optional — and when the body was too large to
|
|
134
|
+
* read there is no id to echo anyway. Omitting it is both valid and honest.
|
|
135
|
+
*/
|
|
136
|
+
return respond(413, {
|
|
137
|
+
jsonrpc: '2.0',
|
|
138
|
+
error: { code: ERROR_CODES.invalidRequest, message: `the request body exceeds ${config.maxBodyBytes} bytes` },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// 3. Method and path. No legacy GET/SSE-session assumptions (P26-05).
|
|
142
|
+
if (request.method !== 'POST') {
|
|
143
|
+
return respond(405, errorResponse(null, {
|
|
144
|
+
code: ERROR_CODES.invalidRequest,
|
|
145
|
+
message: 'this endpoint accepts POST only',
|
|
146
|
+
}), { allow: 'POST' });
|
|
147
|
+
}
|
|
148
|
+
if (request.path !== config.path) {
|
|
149
|
+
return respond(404, { error: `no endpoint at ${request.path}` });
|
|
150
|
+
}
|
|
151
|
+
// 4. Origin (SEC-02). An unapproved browser origin is refused, and CORS is
|
|
152
|
+
// never treated as authentication: passing this check grants nothing.
|
|
153
|
+
const origin = request.headers.origin;
|
|
154
|
+
if (origin !== undefined && !config.allowedOrigins.includes(origin)) {
|
|
155
|
+
return respond(403, { error: 'this origin is not approved for the endpoint' });
|
|
156
|
+
}
|
|
157
|
+
// 5. Authentication (P26-15). Applied to EVERY request including discovery,
|
|
158
|
+
// because an unauthenticated endpoint is not this product's default.
|
|
159
|
+
const presented = bearerOf(request.headers.authorization);
|
|
160
|
+
if (presented === undefined) {
|
|
161
|
+
return respond(401, errorResponse(null, {
|
|
162
|
+
code: ERROR_CODES.invalidRequest,
|
|
163
|
+
message: 'authentication is required',
|
|
164
|
+
}), { 'www-authenticate': 'Bearer' });
|
|
165
|
+
}
|
|
166
|
+
/** The AUTHENTICATED client identity, resolved once and used for rate limiting. */
|
|
167
|
+
let clientId;
|
|
168
|
+
if (config.auth.scheme === 'static-token') {
|
|
169
|
+
let matched;
|
|
170
|
+
if (tokenMatches(presented, config.auth.token))
|
|
171
|
+
matched = config.auth.clientId;
|
|
172
|
+
// SEC-01: a GENERATED static-token credential authenticates through the
|
|
173
|
+
// durable store, carrying its OWN client identity into grants and receipts.
|
|
174
|
+
if (matched === undefined && config.auth.registry !== undefined) {
|
|
175
|
+
matched = config.auth.registry.bySecret(presented)?.clientId;
|
|
176
|
+
}
|
|
177
|
+
if (matched === undefined) {
|
|
178
|
+
// The message does not distinguish "unknown" from "expired": with a static
|
|
179
|
+
// token there is nothing to expire, and a more specific message would only
|
|
180
|
+
// help an attacker.
|
|
181
|
+
return respond(401, errorResponse(null, {
|
|
182
|
+
code: ERROR_CODES.invalidRequest,
|
|
183
|
+
message: 'the presented credential is not accepted',
|
|
184
|
+
}), { 'www-authenticate': 'Bearer' });
|
|
185
|
+
}
|
|
186
|
+
clientId = matched;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
const verdict = await config.auth.server.verify(presented, config.auth.requiredScope);
|
|
190
|
+
if (!verdict.ok) {
|
|
191
|
+
// An RFC 6750 challenge names WHY: invalid_token vs insufficient_scope are
|
|
192
|
+
// different client instructions, and flattening them would misdirect a
|
|
193
|
+
// client that could otherwise self-correct. The STATUS follows §3.1 the
|
|
194
|
+
// same way: invalid_request 400, invalid_token 401, insufficient_scope
|
|
195
|
+
// 403 (the F4 gap was one flat 401 for all three).
|
|
196
|
+
return respond(oauthErrorStatus(verdict.error), errorResponse(null, {
|
|
197
|
+
code: ERROR_CODES.invalidRequest,
|
|
198
|
+
message: verdict.error.description,
|
|
199
|
+
}), { 'www-authenticate': oauthChallenge(verdict.error) });
|
|
200
|
+
}
|
|
201
|
+
clientId = verdict.token.clientId;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* 5.5. Per-client rate limiting (AT-12), AFTER authentication and BEFORE the
|
|
205
|
+
* body is examined.
|
|
206
|
+
*
|
|
207
|
+
* A refused request is answered here without parsing, so a rate-limited caller
|
|
208
|
+
* cannot make the transport pay body-processing cost per attempt, and the
|
|
209
|
+
* counter is keyed on the identity the transport just proved — never the
|
|
210
|
+
* remote address, which every loopback client shares. Unauthenticated and
|
|
211
|
+
* malformed-credential requests were already refused at step 5 WITHOUT moving
|
|
212
|
+
* a counter: counting them would turn the limiter into an authentication
|
|
213
|
+
* oracle, where a 429 after N attempts tells a caller whether a credential
|
|
214
|
+
* exists.
|
|
215
|
+
*
|
|
216
|
+
* The `id` is OMITTED, exactly as the oversize-body refusal omits it: the body
|
|
217
|
+
* was never examined, so there is no id to echo, and the pinned `RequestId`
|
|
218
|
+
* does not admit `null`. The envelope is still a conformant
|
|
219
|
+
* `JSONRPCErrorResponse`, and `Retry-After` names when the window opens.
|
|
220
|
+
*/
|
|
221
|
+
if (limiter !== undefined) {
|
|
222
|
+
const decision = limiter.admit(clientId);
|
|
223
|
+
if (!decision.admitted) {
|
|
224
|
+
return respond(429, errorResponse(null, {
|
|
225
|
+
code: ERROR_CODES.rateLimited,
|
|
226
|
+
message: `rate limit exceeded: at most ${effectivePolicy.maxRequests} `
|
|
227
|
+
+ `requests per client per ${Math.round(effectivePolicy.windowMs / 1000)}s window (AT-12)`,
|
|
228
|
+
data: { retryAfterMs: decision.retryAfterMs },
|
|
229
|
+
}), { 'retry-after': String(Math.ceil(decision.retryAfterMs / 1000)) });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* 5.5. The per-client external grant (SEC-04), DERIVED here because the
|
|
234
|
+
* authentication verdict just happened: the grant is built from the verified
|
|
235
|
+
* client identity, the operator's grant configuration and the LIVE published
|
|
236
|
+
* surface — never from anything in the request body, which is what BASE-05
|
|
237
|
+
* forbids. The derived identity travels with the dispatch as CALLER identity,
|
|
238
|
+
* so the surface can authorize against the CLIENT's grant instead of the
|
|
239
|
+
* host's own interactive grant.
|
|
240
|
+
*/
|
|
241
|
+
const caller = resolveClientGrant(clientId, config.grants, host.listTools().map(tool => tool.name));
|
|
242
|
+
// 6. Parse, then check headers against the body (P26-05).
|
|
243
|
+
let parsed;
|
|
244
|
+
try {
|
|
245
|
+
parsed = JSON.parse(request.body);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return respond(400, errorResponse(null, { code: ERROR_CODES.parseError, message: 'the body is not valid JSON' }));
|
|
249
|
+
}
|
|
250
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
251
|
+
return respond(400, errorResponse(null, { code: ERROR_CODES.invalidRequest, message: 'the body is not a JSON-RPC object' }));
|
|
252
|
+
}
|
|
253
|
+
const wire = parsed;
|
|
254
|
+
if (wire.jsonrpc !== '2.0' || typeof wire.method !== 'string'
|
|
255
|
+
|| (wire.id !== undefined && typeof wire.id !== 'string'
|
|
256
|
+
&& !(typeof wire.id === 'number' && Number.isInteger(wire.id)))) {
|
|
257
|
+
return respond(400, errorResponse(null, {
|
|
258
|
+
code: ERROR_CODES.invalidRequest, message: 'the body is not a JSON-RPC 2.0 request or notification',
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
// Built conditionally rather than with explicit `undefined` values, because
|
|
262
|
+
// `exactOptionalPropertyTypes` distinguishes "absent" from "present and
|
|
263
|
+
// undefined" — and so does the header check, which treats a missing header
|
|
264
|
+
// differently from an empty one.
|
|
265
|
+
const headerInput = {
|
|
266
|
+
...request.headers['mcp-protocol-version'] === undefined
|
|
267
|
+
? {}
|
|
268
|
+
: { protocolVersion: request.headers['mcp-protocol-version'] },
|
|
269
|
+
...request.headers['mcp-method'] === undefined ? {} : { method: request.headers['mcp-method'] },
|
|
270
|
+
...request.headers['mcp-name'] === undefined ? {} : { name: request.headers['mcp-name'] },
|
|
271
|
+
};
|
|
272
|
+
try {
|
|
273
|
+
assertHeadersMatch(headerInput, wire);
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
277
|
+
// P26-05 names HTTP 400 and HeaderMismatch together.
|
|
278
|
+
return respond(400, errorResponse(wire.id ?? null, { code: ERROR_CODES.headerMismatch, message }));
|
|
279
|
+
}
|
|
280
|
+
// Pinned metadata failures are transport rejections (HTTP 400), not SDK
|
|
281
|
+
// dispatch errors under HTTP 200. Keep the shared wire validation, not a
|
|
282
|
+
// second business dispatcher. Optional clientInfo remains optional.
|
|
283
|
+
if (wire.id !== undefined && wire.method !== 'initialize') {
|
|
284
|
+
try {
|
|
285
|
+
const metadata = parseRequestMetadata(wire.params);
|
|
286
|
+
negotiateVersion(metadata);
|
|
287
|
+
const declared = metadata.clientCapabilities.required;
|
|
288
|
+
const required = Array.isArray(declared)
|
|
289
|
+
? declared.filter((name) => typeof name === 'string') : [];
|
|
290
|
+
const unsupported = unsupportedRequiredCapabilities(required);
|
|
291
|
+
if (unsupported.length > 0) {
|
|
292
|
+
throw new WireError(ERROR_CODES.unsupportedClientCapability, `the client requires unsupported capability: ${unsupported.join(', ')}`, { requiredCapabilities: Object.fromEntries(unsupported.map(name => [name, {}])) });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
if (!(error instanceof WireError))
|
|
297
|
+
throw error;
|
|
298
|
+
return respond(400, errorResponse(wire.id, error));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// 7. The OFFICIAL v2 server handles the protocol, not our handwritten dispatch.
|
|
302
|
+
// This finite response-description interface cannot carry open subscriptions.
|
|
303
|
+
// Refuse before creating a subscription rather than swallowing notifications.
|
|
304
|
+
if (wire.id !== undefined && wire.method === 'subscriptions/listen') {
|
|
305
|
+
return respond(404, errorResponse(wire.id ?? null, {
|
|
306
|
+
code: ERROR_CODES.methodNotFound,
|
|
307
|
+
message: 'this HTTP transport does not offer open subscription streams',
|
|
308
|
+
}));
|
|
309
|
+
}
|
|
310
|
+
// Pinned Streamable HTTP defines NO client-to-server notifications;
|
|
311
|
+
// notifications/cancelled is stdio-only. 202/no body is mandatory only IF a
|
|
312
|
+
// notification is accepted. We implement no extension notification handlers,
|
|
313
|
+
// so explicitly refuse rather than silently dropping unsupported messages.
|
|
314
|
+
if (wire.id === undefined) {
|
|
315
|
+
return respond(400, errorResponse(null, {
|
|
316
|
+
code: ERROR_CODES.invalidRequest,
|
|
317
|
+
message: 'this stateless endpoint does not accept standalone notifications',
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
const headers = new Headers();
|
|
321
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
322
|
+
if (value !== undefined)
|
|
323
|
+
headers.set(name, value);
|
|
324
|
+
}
|
|
325
|
+
// Normalize sentinel values only AFTER shared validation. The pinned
|
|
326
|
+
// transport requires Mcp-Method; do not synthesize a missing header here or
|
|
327
|
+
// bypass assertHeadersMatch's required-header/mismatch rejection.
|
|
328
|
+
if (headerInput.method !== undefined)
|
|
329
|
+
headers.set('mcp-method', decodeHeaderValue(headerInput.method));
|
|
330
|
+
if (headerInput.protocolVersion !== undefined) {
|
|
331
|
+
headers.set('mcp-protocol-version', decodeHeaderValue(headerInput.protocolVersion));
|
|
332
|
+
}
|
|
333
|
+
// The SDK already decodes Mcp-Name correctly (including non-ASCII and
|
|
334
|
+
// literal sentinels), so preserve its original wire encoding here.
|
|
335
|
+
return { caller, token: presented, headers };
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Shared, guarded Fetch/stream core. The Node adapter's toNodeHandler is NOT
|
|
340
|
+
* used directly: its early 413 has a different wire envelope, it does not watch
|
|
341
|
+
* IncomingMessage.aborted, and its close/drain listeners are not removed.
|
|
342
|
+
* Use its official bounded Request conversion only AFTER our unchanged gate.
|
|
343
|
+
*/
|
|
344
|
+
export function createHttpStreamHandler(config, host) {
|
|
345
|
+
const gate = createRequestGate(config, host);
|
|
346
|
+
return async (request, signal) => {
|
|
347
|
+
const verdict = await gate(request);
|
|
348
|
+
if ('status' in verdict) {
|
|
349
|
+
return new Response(verdict.body, { status: verdict.status, headers: verdict.headers });
|
|
350
|
+
}
|
|
351
|
+
signal?.throwIfAborted();
|
|
352
|
+
const webRequest = await toWebRequest({
|
|
353
|
+
method: request.method,
|
|
354
|
+
url: config.path,
|
|
355
|
+
// Host was validated already; never let an untrusted authority affect URL
|
|
356
|
+
// construction. Keep the actual normalized headers on the Request below.
|
|
357
|
+
headers: { host: 'localhost' },
|
|
358
|
+
async *[Symbol.asyncIterator]() { yield Buffer.from(request.body, 'utf8'); },
|
|
359
|
+
}, undefined, {
|
|
360
|
+
maxRequestBodySize: config.maxBodyBytes,
|
|
361
|
+
...signal === undefined ? {} : { signal },
|
|
362
|
+
});
|
|
363
|
+
return serveSdkV2Request(host, verdict.caller, new Request(webRequest, { headers: verdict.headers }), verdict.token);
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
/** Finite, materializing test facade; production sockets use the stream core. */
|
|
367
|
+
export function createHttpHandler(config, host) {
|
|
368
|
+
const stream = createHttpStreamHandler(config, host);
|
|
369
|
+
return async (request) => {
|
|
370
|
+
const response = await stream(request);
|
|
371
|
+
return {
|
|
372
|
+
status: response.status,
|
|
373
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
374
|
+
body: await response.text(),
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/** Extract a bearer token from an Authorization header. */
|
|
379
|
+
function bearerOf(header) {
|
|
380
|
+
if (header === undefined)
|
|
381
|
+
return undefined;
|
|
382
|
+
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
|
|
383
|
+
return match?.[1];
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Whether this transport may claim OAuth conformance.
|
|
387
|
+
*
|
|
388
|
+
* The static-token scheme may not; the oauth-resource-server scheme may, because
|
|
389
|
+
* it verifies HS256 credentials, audience, expiry and scope against the embedded
|
|
390
|
+
* server and answers RFC 6750 challenges — a conformant subset, not an invention.
|
|
391
|
+
* The claim is per-DEPLOYMENT, which is what a status report needs: a host using
|
|
392
|
+
* static tokens must still report false.
|
|
393
|
+
* @param auth - the live auth configuration.
|
|
394
|
+
* @returns whether the live scheme may claim the OAuth 2.1 resource-server profile.
|
|
395
|
+
*/
|
|
396
|
+
export function claimsOAuthConformance(auth) {
|
|
397
|
+
return auth.scheme === 'oauth-resource-server';
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* The authentication profile this transport reports.
|
|
401
|
+
*
|
|
402
|
+
* A machine-readable statement of what is actually implemented, so a client or an
|
|
403
|
+
* operator can tell custom authentication from the OAuth profile without reading
|
|
404
|
+
* the source.
|
|
405
|
+
* @param auth - the live auth configuration.
|
|
406
|
+
* @returns the auth profile description.
|
|
407
|
+
*/
|
|
408
|
+
export function authProfile(auth) {
|
|
409
|
+
if (auth.scheme === 'static-token') {
|
|
410
|
+
return {
|
|
411
|
+
scheme: 'static-token',
|
|
412
|
+
// Stated explicitly rather than left to inference.
|
|
413
|
+
oauth21ResourceServer: false,
|
|
414
|
+
perClientRevocable: true,
|
|
415
|
+
note: 'Custom authentication. The OAuth 2.1 resource-server profile is not used and is not claimed.',
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
scheme: 'oauth-resource-server',
|
|
420
|
+
oauth21ResourceServer: true,
|
|
421
|
+
perClientRevocable: true,
|
|
422
|
+
issuer: auth.server.authorizationServerMetadata().issuer,
|
|
423
|
+
audience: auth.server.protectedResourceMetadata().resource,
|
|
424
|
+
note: 'OAuth 2.1 resource-server profile via the embedded local authorization server: '
|
|
425
|
+
+ 'HS256 credentials, RFC 6750 challenges, audience/expiry/scope validation, per-client revocation.',
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
/** Wait for capacity without stranding a promise on a disconnected socket. */
|
|
429
|
+
function waitForDrain(outgoing, signal) {
|
|
430
|
+
return new Promise((resolve, reject) => {
|
|
431
|
+
const cleanup = () => {
|
|
432
|
+
outgoing.off('drain', onDrain);
|
|
433
|
+
outgoing.off('error', onError);
|
|
434
|
+
outgoing.off('close', onAbort);
|
|
435
|
+
signal.removeEventListener('abort', onAbort);
|
|
436
|
+
};
|
|
437
|
+
const onDrain = () => { cleanup(); resolve(); };
|
|
438
|
+
const onError = (error) => { cleanup(); reject(error); };
|
|
439
|
+
const onAbort = () => { cleanup(); reject(signal.reason ?? new Error('HTTP response closed')); };
|
|
440
|
+
outgoing.once('drain', onDrain);
|
|
441
|
+
outgoing.once('error', onError);
|
|
442
|
+
outgoing.once('close', onAbort);
|
|
443
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
444
|
+
if (signal.aborted || outgoing.destroyed)
|
|
445
|
+
onAbort();
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
/** Pump bytes, never text-materialize SSE, and cancel even a blocked read. */
|
|
449
|
+
async function writeStreamResponse(response, outgoing, signal) {
|
|
450
|
+
const reader = response.body?.getReader();
|
|
451
|
+
const onAbort = () => { void reader?.cancel(signal.reason).catch(() => { }); };
|
|
452
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
453
|
+
let drained = false;
|
|
454
|
+
try {
|
|
455
|
+
signal.throwIfAborted();
|
|
456
|
+
outgoing.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
457
|
+
outgoing.flushHeaders();
|
|
458
|
+
if (reader !== undefined) {
|
|
459
|
+
while (true) {
|
|
460
|
+
const next = await reader.read();
|
|
461
|
+
signal.throwIfAborted();
|
|
462
|
+
if (next.done)
|
|
463
|
+
break;
|
|
464
|
+
if (!outgoing.write(next.value))
|
|
465
|
+
await waitForDrain(outgoing, signal);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
drained = true;
|
|
469
|
+
outgoing.end();
|
|
470
|
+
}
|
|
471
|
+
finally {
|
|
472
|
+
signal.removeEventListener('abort', onAbort);
|
|
473
|
+
if (!drained)
|
|
474
|
+
await reader?.cancel(signal.reason).catch(() => { });
|
|
475
|
+
reader?.releaseLock();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Bind the endpoint on loopback.
|
|
480
|
+
*
|
|
481
|
+
* The bind address is validated rather than trusted: a configuration naming a
|
|
482
|
+
* routable address is REFUSED at startup rather than silently exposing the endpoint,
|
|
483
|
+
* because a server that logs a warning and binds anyway is a server that is exposed.
|
|
484
|
+
* @param config - the transport configuration.
|
|
485
|
+
* @param host - the MCP surface.
|
|
486
|
+
* @param options - an optional pre-built server module, so a test can drive a fake.
|
|
487
|
+
* @returns the bound endpoint.
|
|
488
|
+
* @throws Error when the bind address is not loopback.
|
|
489
|
+
*/
|
|
490
|
+
export async function startHttpServer(config, host) {
|
|
491
|
+
if (!isLoopback(config.host)) {
|
|
492
|
+
// MCP-02 puts LAN and public binding out of scope; refusing at startup is the
|
|
493
|
+
// only enforcement that cannot be forgotten later.
|
|
494
|
+
throw new Error(`refusing to bind ${config.host}: this endpoint serves loopback only (MCP-02). `
|
|
495
|
+
+ 'A routable bind address would expose inference to the network.');
|
|
496
|
+
}
|
|
497
|
+
const { createServer } = await import('node:http');
|
|
498
|
+
const handler = createHttpStreamHandler(config, host);
|
|
499
|
+
const active = new Set();
|
|
500
|
+
const server = createServer((incoming, outgoing) => {
|
|
501
|
+
const abort = new AbortController();
|
|
502
|
+
active.add(abort);
|
|
503
|
+
const onAborted = () => { abort.abort(new Error('HTTP client disconnected')); };
|
|
504
|
+
const onClose = () => { if (!outgoing.writableFinished)
|
|
505
|
+
onAborted(); };
|
|
506
|
+
const cleanup = () => {
|
|
507
|
+
incoming.off('aborted', onAborted);
|
|
508
|
+
incoming.off('error', onAborted);
|
|
509
|
+
outgoing.off('close', onClose);
|
|
510
|
+
outgoing.off('error', onAborted);
|
|
511
|
+
active.delete(abort);
|
|
512
|
+
};
|
|
513
|
+
incoming.once('aborted', onAborted);
|
|
514
|
+
incoming.once('error', onAborted);
|
|
515
|
+
outgoing.once('close', onClose);
|
|
516
|
+
outgoing.once('error', onAborted);
|
|
517
|
+
const chunks = [];
|
|
518
|
+
let received = 0;
|
|
519
|
+
let refused = false;
|
|
520
|
+
incoming.on('data', (chunk) => {
|
|
521
|
+
if (refused || abort.signal.aborted)
|
|
522
|
+
return;
|
|
523
|
+
received += chunk.byteLength;
|
|
524
|
+
// Bound DURING receipt, and free already buffered chunks on refusal.
|
|
525
|
+
if (received > config.maxBodyBytes) {
|
|
526
|
+
refused = true;
|
|
527
|
+
chunks.length = 0;
|
|
528
|
+
outgoing.writeHead(413, { 'content-type': 'application/json', connection: 'close' });
|
|
529
|
+
outgoing.end(JSON.stringify({
|
|
530
|
+
jsonrpc: '2.0',
|
|
531
|
+
error: {
|
|
532
|
+
code: ERROR_CODES.invalidRequest,
|
|
533
|
+
message: `the request body exceeds ${config.maxBodyBytes} bytes`,
|
|
534
|
+
},
|
|
535
|
+
}));
|
|
536
|
+
cleanup();
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
chunks.push(chunk);
|
|
540
|
+
});
|
|
541
|
+
incoming.once('end', () => {
|
|
542
|
+
if (refused || abort.signal.aborted) {
|
|
543
|
+
cleanup();
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const body = Buffer.concat(chunks).toString('utf8');
|
|
547
|
+
chunks.length = 0;
|
|
548
|
+
void (async () => {
|
|
549
|
+
try {
|
|
550
|
+
const response = await handler({
|
|
551
|
+
method: incoming.method ?? 'POST',
|
|
552
|
+
path: (incoming.url ?? '/').split('?')[0],
|
|
553
|
+
headers: incoming.headers,
|
|
554
|
+
body,
|
|
555
|
+
remoteAddress: incoming.socket.remoteAddress ?? '',
|
|
556
|
+
}, abort.signal);
|
|
557
|
+
await writeStreamResponse(response, outgoing, abort.signal);
|
|
558
|
+
}
|
|
559
|
+
catch (error) {
|
|
560
|
+
if (abort.signal.aborted || outgoing.destroyed)
|
|
561
|
+
return;
|
|
562
|
+
if (outgoing.headersSent) {
|
|
563
|
+
// A partial SSE response cannot be replaced by a JSON error.
|
|
564
|
+
outgoing.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
outgoing.writeHead(500, { 'content-type': 'application/json' });
|
|
568
|
+
outgoing.end(JSON.stringify({ error: error instanceof Error ? error.message : 'internal error' }));
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
finally {
|
|
572
|
+
cleanup();
|
|
573
|
+
}
|
|
574
|
+
})();
|
|
575
|
+
});
|
|
576
|
+
// No 'end' event is guaranteed on a truncated upload.
|
|
577
|
+
incoming.once('close', () => { if (!incoming.complete)
|
|
578
|
+
cleanup(); });
|
|
579
|
+
});
|
|
580
|
+
await new Promise((resolve, reject) => {
|
|
581
|
+
server.once('error', reject);
|
|
582
|
+
// Bound to the loopback interface specifically, not to all interfaces.
|
|
583
|
+
server.listen(config.port, config.host, () => resolve());
|
|
584
|
+
});
|
|
585
|
+
const address = server.address();
|
|
586
|
+
const port = typeof address === 'object' && address !== null ? address.port : config.port;
|
|
587
|
+
return {
|
|
588
|
+
host: config.host,
|
|
589
|
+
port,
|
|
590
|
+
url: `http://${config.host}:${port}${config.path}`,
|
|
591
|
+
async close() {
|
|
592
|
+
for (const abort of active)
|
|
593
|
+
abort.abort(new Error('HTTP endpoint closed'));
|
|
594
|
+
await new Promise((resolve, reject) => {
|
|
595
|
+
server.close(error => error === undefined ? resolve() : reject(error));
|
|
596
|
+
// server.close alone waits indefinitely for active/keep-alive sockets.
|
|
597
|
+
server.closeAllConnections();
|
|
598
|
+
});
|
|
599
|
+
},
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
//# sourceMappingURL=http.js.map
|