@try-works/dsh-model-as-tool 0.1.2 → 0.3.6
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/bin/dsh-tool-mode-credentials.mjs +20 -0
- package/bin/dsh-tool-mode.mjs +202 -0
- package/catalog/desert-ant-models.json +693 -693
- package/client/client.model-tools.js +592 -0
- package/client/index.js +1679 -0
- package/lib/adapters/index.d.ts.map +1 -1
- package/lib/adapters/index.js +4 -4
- package/lib/bindings.d.ts.map +1 -1
- package/lib/bindings.js +26 -25
- package/lib/capability.d.ts.map +1 -1
- package/lib/capability.js +17 -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/diagnostics.d.ts +166 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +135 -0
- package/lib/extension-dependencies.d.ts +197 -0
- package/lib/extension-dependencies.d.ts.map +1 -0
- package/lib/extension-dependencies.js +369 -0
- package/lib/extension-dispatch.d.ts +117 -0
- package/lib/extension-dispatch.d.ts.map +1 -0
- package/lib/extension-dispatch.js +124 -0
- package/lib/extension-registrar.d.ts +100 -0
- package/lib/extension-registrar.d.ts.map +1 -0
- package/lib/extension-registrar.js +134 -0
- package/lib/extensions-api.d.ts +217 -0
- package/lib/extensions-api.d.ts.map +1 -0
- package/lib/extensions-api.js +17 -0
- package/lib/extensions-remote.d.ts +54 -0
- package/lib/extensions-remote.d.ts.map +1 -0
- package/lib/extensions-remote.js +66 -0
- package/lib/extensions-service.d.ts +334 -0
- package/lib/extensions-service.d.ts.map +1 -0
- package/lib/extensions-service.js +1351 -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 +95 -0
- package/lib/harness-serving.d.ts.map +1 -0
- package/lib/harness-serving.js +157 -0
- package/lib/headless.d.ts +97 -0
- package/lib/headless.d.ts.map +1 -0
- package/lib/headless.js +349 -0
- package/lib/http-acquisition.d.ts +27 -0
- package/lib/http-acquisition.d.ts.map +1 -0
- package/lib/http-acquisition.js +136 -0
- package/lib/index.d.ts +250 -7
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2625 -70
- 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/legacy-migration.d.ts +95 -0
- package/lib/legacy-migration.d.ts.map +1 -0
- package/lib/legacy-migration.js +429 -0
- package/lib/managed-workers.d.ts +151 -0
- package/lib/managed-workers.d.ts.map +1 -0
- package/lib/managed-workers.js +249 -0
- package/lib/mcp/client-grant.d.ts +74 -0
- package/lib/mcp/client-grant.d.ts.map +1 -0
- package/lib/mcp/client-grant.js +60 -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 +454 -0
- package/lib/mcp/host-binding.d.ts +25 -0
- package/lib/mcp/host-binding.d.ts.map +1 -1
- package/lib/mcp/host-binding.js +107 -14
- package/lib/mcp/http.d.ts +68 -18
- package/lib/mcp/http.d.ts.map +1 -1
- package/lib/mcp/http.js +390 -62
- 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 +46 -7
- package/lib/mcp/protocol.d.ts.map +1 -1
- package/lib/mcp/protocol.js +63 -16
- 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 +181 -0
- package/lib/mcp/server.d.ts +11 -1
- package/lib/mcp/server.d.ts.map +1 -1
- package/lib/mcp/server.js +80 -13
- package/lib/mcp/stdio.d.ts.map +1 -1
- package/lib/mcp/stdio.js +9 -1
- package/lib/mcp/subscriptions.d.ts +43 -1
- package/lib/mcp/subscriptions.d.ts.map +1 -1
- package/lib/mcp/subscriptions.js +90 -9
- 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 +246 -0
- package/lib/planning.d.ts.map +1 -0
- package/lib/planning.js +457 -0
- package/lib/prepared-assets.d.ts +178 -0
- package/lib/prepared-assets.d.ts.map +1 -0
- package/lib/prepared-assets.js +286 -0
- package/lib/provider-bridge.d.ts +37 -0
- package/lib/provider-bridge.d.ts.map +1 -1
- package/lib/provider-bridge.js +189 -12
- package/lib/ptc-authority.d.ts +140 -0
- package/lib/ptc-authority.d.ts.map +1 -0
- package/lib/ptc-authority.js +216 -0
- package/lib/ptc-runtime.d.ts +156 -0
- package/lib/ptc-runtime.d.ts.map +1 -0
- package/lib/ptc-runtime.js +191 -0
- package/lib/publication.d.ts +2 -1
- package/lib/publication.d.ts.map +1 -1
- package/lib/publication.js +13 -5
- package/lib/receipt.d.ts.map +1 -1
- package/lib/receipt.js +8 -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-mode-run.d.ts +235 -0
- package/lib/tool-mode-run.d.ts.map +1 -0
- package/lib/tool-mode-run.js +283 -0
- package/lib/types.d.ts +3 -1
- package/lib/types.d.ts.map +1 -1
- 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/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 +85 -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 +48 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/meta.d.ts +104 -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 +55 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/registry.d.ts +107 -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 +34 -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 +83 -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 +125 -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 +31 -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 +31 -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 +185 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/dependencies.d.ts +126 -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 +297 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/lifecycle.d.ts +216 -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 +800 -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 +235 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/lib/install/store.d.ts +367 -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 +807 -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/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 +118 -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/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 +150 -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 +670 -0
- package/node_modules/@try-works/tool-mode-extension-sdk/package.json +53 -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 +427 -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 +592 -0
- package/node_modules/@try-works/tool-mode-recipes/lib/cli.d.ts +60 -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 +936 -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 +200 -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 +105 -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 +129 -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 +325 -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 +1310 -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 +227 -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 +407 -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 +50 -17
- package/remote/spec.mjs +439 -0
- package/remote/typert.host.mjs +14 -0
- package/remote/typert.remote-client.mjs +16 -0
- package/lib/adapters/tongue.d.ts +0 -31
- package/lib/adapters/tongue.d.ts.map +0 -1
- package/lib/adapters/tongue.js +0 -93
- package/lib/adapters/voz.d.ts +0 -28
- package/lib/adapters/voz.d.ts.map +0 -1
- package/lib/adapters/voz.js +0 -102
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts +0 -0
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.d.ts.map +0 -0
- /package/{lib → node_modules/@try-works/tool-mode-extension-sdk/lib}/licensing.js +0 -0
package/lib/mcp/protocol.js
CHANGED
|
@@ -46,8 +46,15 @@ export const META_KEYS = {
|
|
|
46
46
|
* Error codes.
|
|
47
47
|
*
|
|
48
48
|
* The reserved range belongs to the protocol. P26-03 is explicit that custom
|
|
49
|
-
* errors must not be allocated there,
|
|
50
|
-
*
|
|
49
|
+
* errors must not be allocated there, and the pinned revision partitions the
|
|
50
|
+
* bands: `-32768..-32000` is JSON-RPC's own predefined-error allocation,
|
|
51
|
+
* `-32020..-32099` is reserved for MCP-defined codes (allocated sequentially,
|
|
52
|
+
* and this module declares exactly those the revision defines), while
|
|
53
|
+
* `-32000..-32019` is the IMPLEMENTATION-DEFINED sub-range — the specification
|
|
54
|
+
* will never define codes there, and receivers must not assume cross-
|
|
55
|
+
* implementation semantics. `rateLimited` lives in that sub-range deliberately:
|
|
56
|
+
* it is a documented implementation allocation, never a minted code in the
|
|
57
|
+
* reserved band.
|
|
51
58
|
*/
|
|
52
59
|
export const ERROR_CODES = {
|
|
53
60
|
/** Standard JSON-RPC. */
|
|
@@ -60,6 +67,16 @@ export const ERROR_CODES = {
|
|
|
60
67
|
headerMismatch: -32020,
|
|
61
68
|
unsupportedClientCapability: -32021,
|
|
62
69
|
unsupportedProtocolVersion: -32022,
|
|
70
|
+
/**
|
|
71
|
+
* Implementation-defined (-32000..-32019, never allocated by the specification).
|
|
72
|
+
*
|
|
73
|
+
* The revision defines NO rate-limit error, so a refusal uses this documented
|
|
74
|
+
* implementation code rather than pretending the request was invalid (-32600) or
|
|
75
|
+
* minting a code in the MCP-reserved band. The HTTP 429 status carries the
|
|
76
|
+
* semantics; receivers that do not know this code still get a conformant
|
|
77
|
+
* `JSONRPCErrorResponse` envelope and a `Retry-After` header.
|
|
78
|
+
*/
|
|
79
|
+
rateLimited: -32001,
|
|
63
80
|
};
|
|
64
81
|
/** Raised for a request that must be answered with a JSON-RPC error. */
|
|
65
82
|
export class WireError extends Error {
|
|
@@ -158,10 +175,14 @@ export function negotiateVersion(metadata) {
|
|
|
158
175
|
export const ADVERTISED_FEATURES = [
|
|
159
176
|
{ feature: 'tools', implemented: true },
|
|
160
177
|
{ feature: 'server/discover', implemented: true },
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Implemented for TOOL-catalog changes only, and ONLY when a registry is composed.
|
|
180
|
+
*
|
|
181
|
+
* This row is re-decided by dvertisedFeatures(), which defaults it to alse:
|
|
182
|
+
* the production transport passes no registry, so the row as written here is the
|
|
183
|
+
* capability's SHAPE rather than a claim about the deployment. Declaring it true
|
|
184
|
+
* statically is what let an unservable feature be advertised.
|
|
185
|
+
*/
|
|
165
186
|
{ feature: 'subscriptions/listen', implemented: true },
|
|
166
187
|
{ feature: 'tasks', implemented: false },
|
|
167
188
|
{ feature: 'sampling', implemented: false },
|
|
@@ -171,15 +192,37 @@ export const ADVERTISED_FEATURES = [
|
|
|
171
192
|
{ feature: 'resources/subscribe', implemented: false },
|
|
172
193
|
{ feature: 'logging', implemented: false },
|
|
173
194
|
];
|
|
195
|
+
/**
|
|
196
|
+
* What the server may advertise, given what is ACTUALLY WIRED (P26-12).
|
|
197
|
+
*
|
|
198
|
+
* The static matrix declared `subscriptions/listen` as implemented unconditionally — while
|
|
199
|
+
* the only production transport never received a registry and therefore answered `-32601`
|
|
200
|
+
* for it. An advertised feature that no client can obtain is worse than an absent one: a
|
|
201
|
+
* client plans around it and fails later.
|
|
202
|
+
*
|
|
203
|
+
* The feature is now conditional on the wiring, and the DEFAULT is `false` because that is
|
|
204
|
+
* the production reality. A caller that has genuinely composed a subscription registry says
|
|
205
|
+
* so; nothing else can make the claim true by declaring it.
|
|
206
|
+
*
|
|
207
|
+
* @param wiring - which optional surfaces are composed.
|
|
208
|
+
* @returns the advertised-feature matrix for this deployment.
|
|
209
|
+
*/
|
|
210
|
+
export function advertisedFeatures(wiring = {}) {
|
|
211
|
+
return ADVERTISED_FEATURES.map(entry =>
|
|
212
|
+
// Only the composed feature is re-decided; everything else keeps its declared truth.
|
|
213
|
+
entry.feature === 'subscriptions/listen'
|
|
214
|
+
? { feature: entry.feature, implemented: wiring.subscriptions === true }
|
|
215
|
+
: entry);
|
|
216
|
+
}
|
|
174
217
|
/**
|
|
175
218
|
* The capabilities object this server may advertise.
|
|
176
219
|
*
|
|
177
|
-
* Derived from
|
|
178
|
-
*
|
|
179
|
-
* @returns the capability names that are genuinely implemented.
|
|
220
|
+
* Derived from the matrix rather than written twice, so the two cannot disagree.
|
|
221
|
+
* @param wiring - which optional surfaces are composed.
|
|
222
|
+
* @returns the capability names that are genuinely implemented HERE.
|
|
180
223
|
*/
|
|
181
|
-
export function implementedCapabilities() {
|
|
182
|
-
return
|
|
224
|
+
export function implementedCapabilities(wiring = {}) {
|
|
225
|
+
return advertisedFeatures(wiring).filter(entry => entry.implemented).map(entry => entry.feature);
|
|
183
226
|
}
|
|
184
227
|
/**
|
|
185
228
|
* Whether a client's REQUIRED capability is one this server supports (P26-03).
|
|
@@ -187,11 +230,11 @@ export function implementedCapabilities() {
|
|
|
187
230
|
* @param required - capability names the client declared as required.
|
|
188
231
|
* @returns the unsupported names, empty when all are supported.
|
|
189
232
|
*/
|
|
190
|
-
export function unsupportedRequiredCapabilities(required) {
|
|
191
|
-
const implemented = new Set(implementedCapabilities());
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
233
|
+
export function unsupportedRequiredCapabilities(required, wiring = {}) {
|
|
234
|
+
const implemented = new Set(implementedCapabilities(wiring));
|
|
235
|
+
// The supported set is deployment-specific: `subscriptions/listen` is available only
|
|
236
|
+
// when the caller composed a subscription registry. A static default here rejected a
|
|
237
|
+
// capability that the very same dispatch call could serve.
|
|
195
238
|
return required.filter(name => !implemented.has(name));
|
|
196
239
|
}
|
|
197
240
|
/**
|
|
@@ -327,6 +370,10 @@ export function assertHeadersMatch(headers, body) {
|
|
|
327
370
|
return fail(`the MCP-Protocol-Version header (${version}) does not match the request body (${bodyVersion})`);
|
|
328
371
|
}
|
|
329
372
|
const method = headers.method === undefined ? undefined : decodeHeaderValue(headers.method);
|
|
373
|
+
// Pinned Streamable HTTP: Mcp-Method is REQUIRED for every request, not
|
|
374
|
+
// merely checked when supplied. Never synthesize a missing authority header.
|
|
375
|
+
if (body.id !== undefined && method === undefined)
|
|
376
|
+
return fail('the Mcp-Method header is required');
|
|
330
377
|
if (method !== undefined && method !== body.method) {
|
|
331
378
|
return fail(`the Mcp-Method header (${method}) does not match the request method (${body.method})`);
|
|
332
379
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-client request rate limiting for the MCP HTTP transport (AT-12).
|
|
3
|
+
*
|
|
4
|
+
* AT-12's named gap was that the host had ZERO rate limiting: an authenticated client could
|
|
5
|
+
* drive unbounded inference traffic, and nothing anywhere counted it. This module is the
|
|
6
|
+
* counter. Its discipline is deliberate:
|
|
7
|
+
*
|
|
8
|
+
* - **Keyed on the AUTHENTICATED client identity, never the socket.** The remote address is
|
|
9
|
+
* caller-forgeable (proxies, NAT, header tricks) and the endpoint is loopback-only anyway,
|
|
10
|
+
* so every client would share one address and one bucket. The identity comes from the
|
|
11
|
+
* transport's resolved authentication — the static-token `clientId` or the OAuth subject —
|
|
12
|
+
* which is the one thing the transport already proved.
|
|
13
|
+
* - **Fixed-window buckets.** One counter per client per window. Simpler to reason about than
|
|
14
|
+
* a token bucket, and its boundary semantics are exactly testable: a client that exhausted
|
|
15
|
+
* a window is admitted again when the next window opens, with `retryAfterMs` naming when.
|
|
16
|
+
* - **Bounded in-memory state, never persisted.** Entries carry only a window, a count and a
|
|
17
|
+
* last-seen timestamp; when the map is at capacity a NEW client evicts the longest-idle
|
|
18
|
+
* entry, so a deployment cannot be grown without limit by many short-lived clients. A
|
|
19
|
+
* restart forgets everything, which is correct for a burst guard: persistence would turn
|
|
20
|
+
* a request limiter into an availability policy.
|
|
21
|
+
* - **Counting is not an auth oracle.** The HTTP layer admits a client only AFTER the
|
|
22
|
+
* credential was accepted; an unauthenticated or malformed-credential request is refused
|
|
23
|
+
* before any counter moves. Counting authentication failures would let a caller learn
|
|
24
|
+
* whether a credential exists from the timing of a 429, which is the leak this refuses.
|
|
25
|
+
*
|
|
26
|
+
* @module @try-works/dsh-model-as-tool/mcp/rate-limit
|
|
27
|
+
*/
|
|
28
|
+
/** The policy one deployment enforces. */
|
|
29
|
+
export interface RateLimitPolicy {
|
|
30
|
+
/** Requests admitted per window for ONE client. */
|
|
31
|
+
readonly maxRequests: number;
|
|
32
|
+
/** Window length in milliseconds. */
|
|
33
|
+
readonly windowMs: number;
|
|
34
|
+
/** Most clients tracked; a new client at capacity evicts the longest-idle entry. */
|
|
35
|
+
readonly maxClients: number;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The default policy: 60 requests per client per minute, 10 000 tracked clients.
|
|
39
|
+
*
|
|
40
|
+
* Sensible for a loopback single-user endpoint while still bounding a runaway client; an
|
|
41
|
+
* operator overrides it through the transport configuration.
|
|
42
|
+
*/
|
|
43
|
+
export declare const DEFAULT_RATE_LIMIT_POLICY: RateLimitPolicy;
|
|
44
|
+
/** The verdict of one admission. */
|
|
45
|
+
export type RateLimitDecision = {
|
|
46
|
+
readonly admitted: true;
|
|
47
|
+
} | {
|
|
48
|
+
readonly admitted: false;
|
|
49
|
+
readonly retryAfterMs: number;
|
|
50
|
+
};
|
|
51
|
+
/** A per-client limiter. */
|
|
52
|
+
export interface RateLimiter {
|
|
53
|
+
/** Consume one request for a client, refusing it when the window is exhausted. */
|
|
54
|
+
admit(clientId: string): RateLimitDecision;
|
|
55
|
+
/** Requests still available to this client in the current window. */
|
|
56
|
+
remaining(clientId: string): number;
|
|
57
|
+
/** How many clients are currently tracked. */
|
|
58
|
+
trackedClients(): number;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a per-client fixed-window rate limiter.
|
|
62
|
+
* @param policy - the enforced policy; fields are validated at construction so a broken
|
|
63
|
+
* configuration fails when the transport starts, not on the first request.
|
|
64
|
+
* @param now - a clock, injectable so window boundaries are testable.
|
|
65
|
+
* @returns the limiter.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createRateLimiter(policy: RateLimitPolicy, now?: () => number): RateLimiter;
|
|
68
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/mcp/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,0CAA0C;AAC1C,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,oFAAoF;IACpF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,EAAE,eAIvC,CAAA;AAED,oCAAoC;AACpC,MAAM,MAAM,iBAAiB,GACzB;IAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,kFAAkF;IAClF,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAAA;IAC1C,qEAAqE;IACrE,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;IACnC,8CAA8C;IAC9C,cAAc,IAAI,MAAM,CAAA;CACzB;AAmBD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,GAAG,GAAE,MAAM,MAAiB,GAC3B,WAAW,CA8Db"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-client request rate limiting for the MCP HTTP transport (AT-12).
|
|
3
|
+
*
|
|
4
|
+
* AT-12's named gap was that the host had ZERO rate limiting: an authenticated client could
|
|
5
|
+
* drive unbounded inference traffic, and nothing anywhere counted it. This module is the
|
|
6
|
+
* counter. Its discipline is deliberate:
|
|
7
|
+
*
|
|
8
|
+
* - **Keyed on the AUTHENTICATED client identity, never the socket.** The remote address is
|
|
9
|
+
* caller-forgeable (proxies, NAT, header tricks) and the endpoint is loopback-only anyway,
|
|
10
|
+
* so every client would share one address and one bucket. The identity comes from the
|
|
11
|
+
* transport's resolved authentication — the static-token `clientId` or the OAuth subject —
|
|
12
|
+
* which is the one thing the transport already proved.
|
|
13
|
+
* - **Fixed-window buckets.** One counter per client per window. Simpler to reason about than
|
|
14
|
+
* a token bucket, and its boundary semantics are exactly testable: a client that exhausted
|
|
15
|
+
* a window is admitted again when the next window opens, with `retryAfterMs` naming when.
|
|
16
|
+
* - **Bounded in-memory state, never persisted.** Entries carry only a window, a count and a
|
|
17
|
+
* last-seen timestamp; when the map is at capacity a NEW client evicts the longest-idle
|
|
18
|
+
* entry, so a deployment cannot be grown without limit by many short-lived clients. A
|
|
19
|
+
* restart forgets everything, which is correct for a burst guard: persistence would turn
|
|
20
|
+
* a request limiter into an availability policy.
|
|
21
|
+
* - **Counting is not an auth oracle.** The HTTP layer admits a client only AFTER the
|
|
22
|
+
* credential was accepted; an unauthenticated or malformed-credential request is refused
|
|
23
|
+
* before any counter moves. Counting authentication failures would let a caller learn
|
|
24
|
+
* whether a credential exists from the timing of a 429, which is the leak this refuses.
|
|
25
|
+
*
|
|
26
|
+
* @module @try-works/dsh-model-as-tool/mcp/rate-limit
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* The default policy: 60 requests per client per minute, 10 000 tracked clients.
|
|
30
|
+
*
|
|
31
|
+
* Sensible for a loopback single-user endpoint while still bounding a runaway client; an
|
|
32
|
+
* operator overrides it through the transport configuration.
|
|
33
|
+
*/
|
|
34
|
+
export const DEFAULT_RATE_LIMIT_POLICY = {
|
|
35
|
+
maxRequests: 60,
|
|
36
|
+
windowMs: 60_000,
|
|
37
|
+
maxClients: 10_000,
|
|
38
|
+
};
|
|
39
|
+
/** Read a positive integer policy field, refusing a value that would mis-count. */
|
|
40
|
+
function positiveInteger(value, name) {
|
|
41
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
42
|
+
throw new Error(`rate limit ${name} must be a positive integer, not ${String(value)}`);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a per-client fixed-window rate limiter.
|
|
48
|
+
* @param policy - the enforced policy; fields are validated at construction so a broken
|
|
49
|
+
* configuration fails when the transport starts, not on the first request.
|
|
50
|
+
* @param now - a clock, injectable so window boundaries are testable.
|
|
51
|
+
* @returns the limiter.
|
|
52
|
+
*/
|
|
53
|
+
export function createRateLimiter(policy, now = Date.now) {
|
|
54
|
+
const maxRequests = positiveInteger(policy.maxRequests, 'maxRequests');
|
|
55
|
+
const windowMs = positiveInteger(policy.windowMs, 'windowMs');
|
|
56
|
+
const maxClients = positiveInteger(policy.maxClients, 'maxClients');
|
|
57
|
+
const buckets = new Map();
|
|
58
|
+
/** Evict the longest-idle client, so the map stays bounded while new clients arrive. */
|
|
59
|
+
const evictIdle = () => {
|
|
60
|
+
let oldest;
|
|
61
|
+
for (const [id, bucket] of buckets) {
|
|
62
|
+
if (oldest === undefined || bucket.lastSeen < oldest.lastSeen) {
|
|
63
|
+
oldest = { id, lastSeen: bucket.lastSeen };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (oldest !== undefined)
|
|
67
|
+
buckets.delete(oldest.id);
|
|
68
|
+
};
|
|
69
|
+
const bucketFor = (clientId, at) => {
|
|
70
|
+
const existing = buckets.get(clientId);
|
|
71
|
+
if (existing !== undefined)
|
|
72
|
+
return existing;
|
|
73
|
+
if (buckets.size >= maxClients)
|
|
74
|
+
evictIdle();
|
|
75
|
+
const created = { window: 0, count: 0, lastSeen: at };
|
|
76
|
+
buckets.set(clientId, created);
|
|
77
|
+
return created;
|
|
78
|
+
};
|
|
79
|
+
const currentWindow = (at) => Math.floor(at / windowMs);
|
|
80
|
+
return {
|
|
81
|
+
admit(clientId) {
|
|
82
|
+
const at = now();
|
|
83
|
+
const bucket = bucketFor(clientId, at);
|
|
84
|
+
bucket.lastSeen = at;
|
|
85
|
+
const window = currentWindow(at);
|
|
86
|
+
// A NEW WINDOW RESETS the count: the client exhausted the PREVIOUS window, not all time.
|
|
87
|
+
if (bucket.window !== window) {
|
|
88
|
+
bucket.window = window;
|
|
89
|
+
bucket.count = 0;
|
|
90
|
+
}
|
|
91
|
+
if (bucket.count >= maxRequests) {
|
|
92
|
+
return {
|
|
93
|
+
admitted: false,
|
|
94
|
+
// The client is admitted again at the next window boundary.
|
|
95
|
+
retryAfterMs: windowMs - (at % windowMs),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
bucket.count += 1;
|
|
99
|
+
return { admitted: true };
|
|
100
|
+
},
|
|
101
|
+
remaining(clientId) {
|
|
102
|
+
const bucket = buckets.get(clientId);
|
|
103
|
+
if (bucket === undefined)
|
|
104
|
+
return maxRequests;
|
|
105
|
+
if (bucket.window !== currentWindow(now()))
|
|
106
|
+
return maxRequests;
|
|
107
|
+
return Math.max(0, maxRequests - bucket.count);
|
|
108
|
+
},
|
|
109
|
+
trackedClients() {
|
|
110
|
+
return buckets.size;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ExternalCaller } from './client-grant.ts';
|
|
2
|
+
import type { McpHost } from './server.ts';
|
|
3
|
+
import { type WireRequest } from './protocol.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Construct official serving around a caller already verified by the transport.
|
|
6
|
+
* No credentials or grants are accepted from JSON-RPC params. Each factory call
|
|
7
|
+
* reads the LIVE surface and creates a fresh server; business execution and its
|
|
8
|
+
* accounting remain exclusively in host.callTool / createMcpHost.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createSdkV2Handler(host: McpHost, caller: ExternalCaller, incoming?: WireRequest): import("@modelcontextprotocol/server").McpHttpHandler;
|
|
11
|
+
/**
|
|
12
|
+
* Live Fetch face. The per-request SDK handler belongs to the response body:
|
|
13
|
+
* close it on drain, cancellation, abort or error, never when fetch returns SSE.
|
|
14
|
+
* Linking a controller also makes a Web reader's cancel abort business execution.
|
|
15
|
+
*/
|
|
16
|
+
export declare function serveSdkV2Request(host: McpHost, caller: ExternalCaller, request: Request, token: string): Promise<Response>;
|
|
17
|
+
//# sourceMappingURL=sdk-v2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-v2.d.ts","sourceRoot":"","sources":["../../src/mcp/sdk-v2.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAmB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAA;AAEjE;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,WAAW,yDA2E/F;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CA+DnB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Official SDK v2 serving adapter, not a second business dispatcher.
|
|
3
|
+
*
|
|
4
|
+
* Upstream main inspected at 9bc40e903fde15534ec06ff4c1347d47000dbe16:
|
|
5
|
+
* https://github.com/modelcontextprotocol/typescript-sdk/blob/9bc40e903fde15534ec06ff4c1347d47000dbe16/docs/serving/http.md
|
|
6
|
+
* That revision's packages/server/package.json is 2.3.1; runtime uses the exact
|
|
7
|
+
* npm release @modelcontextprotocol/server@2.3.1 (not a floating git dependency).
|
|
8
|
+
* Registry provenance: https://registry.npmjs.org/@modelcontextprotocol/server/2.3.1
|
|
9
|
+
* tarball SHA-1 a15440d5c06f3bad67360952fe58e6965b4a4ac7; integrity
|
|
10
|
+
* sha512-e59MfWuSssj6DoQ96JCTqdW1AXT5/XtnTl0WWd6MU1Ka434fFUGGosCLJbGNySDhHGvxQWoRWK137FXYFkYNWw==
|
|
11
|
+
* Protocol revision: 2026-07-28. Legacy handshakes/sessions are deliberately refused.
|
|
12
|
+
*/
|
|
13
|
+
import { createMcpHandler, fromJsonSchema, McpServer } from '@modelcontextprotocol/server';
|
|
14
|
+
import { toolErrorResult } from "./protocol.js";
|
|
15
|
+
/**
|
|
16
|
+
* Construct official serving around a caller already verified by the transport.
|
|
17
|
+
* No credentials or grants are accepted from JSON-RPC params. Each factory call
|
|
18
|
+
* reads the LIVE surface and creates a fresh server; business execution and its
|
|
19
|
+
* accounting remain exclusively in host.callTool / createMcpHost.
|
|
20
|
+
*/
|
|
21
|
+
export function createSdkV2Handler(host, caller, incoming) {
|
|
22
|
+
return createMcpHandler(({ authInfo }) => {
|
|
23
|
+
if (authInfo?.clientId !== caller.clientId) {
|
|
24
|
+
throw new Error('SDK serving requires the transport-verified caller');
|
|
25
|
+
}
|
|
26
|
+
const server = new McpServer(host.serverInfo, {
|
|
27
|
+
supportedProtocolVersions: ['2026-07-28'],
|
|
28
|
+
// Keep tools/list available for an empty caller-specific catalog; SDK
|
|
29
|
+
// handlers otherwise appear only after the first registerTool call.
|
|
30
|
+
capabilities: { tools: { listChanged: false } },
|
|
31
|
+
});
|
|
32
|
+
for (const tool of [...host.listTools()].sort((a, b) => a.name.localeCompare(b.name))) {
|
|
33
|
+
if (!caller.allowedTools.includes(tool.name) || tool.importedFromMcp)
|
|
34
|
+
continue;
|
|
35
|
+
server.registerTool(tool.name, {
|
|
36
|
+
...tool.title === undefined ? {} : { title: tool.title },
|
|
37
|
+
description: tool.description,
|
|
38
|
+
inputSchema: fromJsonSchema(tool.inputSchema),
|
|
39
|
+
// A definition with no declared result shape must register no
|
|
40
|
+
// outputSchema: the SDK's output validator converts ANY registered
|
|
41
|
+
// outputSchema plus an undefined structuredContent into a tool error
|
|
42
|
+
// ("has an output schema but no structured content was provided"),
|
|
43
|
+
// while the protocol dispatch serves the same host outcome as a
|
|
44
|
+
// content-only success (server.ts). An empty schema is the
|
|
45
|
+
// schema-less sentinel.
|
|
46
|
+
...tool.outputSchema === undefined || Object.keys(tool.outputSchema).length === 0
|
|
47
|
+
? {}
|
|
48
|
+
: { outputSchema: fromJsonSchema(tool.outputSchema) },
|
|
49
|
+
}, async (args, ctx) => {
|
|
50
|
+
const outcome = await host.callTool(tool.name, args, ctx.mcpReq.signal, caller);
|
|
51
|
+
if (outcome.isError === true) {
|
|
52
|
+
// An application failure is a tool-error RESULT (P26-04), clamped
|
|
53
|
+
// exactly like the protocol dispatch clamps it: toolErrorResult
|
|
54
|
+
// bounds the message to 2000 chars + '…' (protocol.ts:358-365).
|
|
55
|
+
// The installed SDK's CallToolResult.content is mutable, so the
|
|
56
|
+
// readonly payload content is copied, as the denied-call path does.
|
|
57
|
+
const failure = toolErrorResult(outcome.text);
|
|
58
|
+
return { isError: failure.isError, content: [...failure.content] };
|
|
59
|
+
}
|
|
60
|
+
// The pinned 2026-07-28 schema types structuredContent as `unknown` —
|
|
61
|
+
// ANY JSON value (object, array, string, number, boolean, null:
|
|
62
|
+
// docs/tool-mode/specs/mcp-2026-07-28/schema.ts CallToolResult). The
|
|
63
|
+
// installed SDK's CallToolResultSchema is z.ZodUnknown and its 2026-era
|
|
64
|
+
// projection is identity, so no runtime guard or cast is needed;
|
|
65
|
+
// undefined means "no structured value" and is omitted.
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: 'text', text: outcome.text }],
|
|
68
|
+
...outcome.structured === undefined ? {} : { structuredContent: outcome.structured },
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Withheld known tools must remain invisible to tools/list, but SEC-03
|
|
73
|
+
// requires policy denials in the tool-error lane. Override only this fresh
|
|
74
|
+
// request's denied-call handler; accepted calls retain McpServer's genuine
|
|
75
|
+
// registration/input/output pipeline. The shared host boundary owns the
|
|
76
|
+
// refusal and its accounting; no unauthorized tool is ever registered.
|
|
77
|
+
if (incoming !== undefined) {
|
|
78
|
+
const deniedName = incoming.params?.name;
|
|
79
|
+
if (incoming.method === 'tools/call' && typeof deniedName === 'string'
|
|
80
|
+
&& !caller.allowedTools.includes(deniedName)
|
|
81
|
+
&& host.listTools().some(tool => tool.name === deniedName && !tool.importedFromMcp)) {
|
|
82
|
+
server.server.setRequestHandler('tools/call', async (request, ctx) => {
|
|
83
|
+
if (request.params.name !== deniedName)
|
|
84
|
+
throw new Error('request-specific denial name changed');
|
|
85
|
+
const outcome = await host.callTool(deniedName, request.params.arguments ?? {}, ctx.mcpReq.signal, caller);
|
|
86
|
+
if (outcome.isError !== true)
|
|
87
|
+
throw new Error('host accepted a tool outside the authenticated grant');
|
|
88
|
+
return { isError: true, content: [...toolErrorResult(outcome.text).content] };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return server;
|
|
93
|
+
}, {
|
|
94
|
+
legacy: 'reject',
|
|
95
|
+
// NEVER 'json': that mode silently drops mid-call notifications. Auto keeps
|
|
96
|
+
// every message, upgrading the response to SSE if one is emitted.
|
|
97
|
+
responseMode: 'auto',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Live Fetch face. The per-request SDK handler belongs to the response body:
|
|
102
|
+
* close it on drain, cancellation, abort or error, never when fetch returns SSE.
|
|
103
|
+
* Linking a controller also makes a Web reader's cancel abort business execution.
|
|
104
|
+
*/
|
|
105
|
+
export async function serveSdkV2Request(host, caller, request, token) {
|
|
106
|
+
const incoming = await request.clone().json();
|
|
107
|
+
const handler = createSdkV2Handler(host, caller, incoming);
|
|
108
|
+
const abort = new AbortController();
|
|
109
|
+
let reader;
|
|
110
|
+
let stopped = false;
|
|
111
|
+
let closing;
|
|
112
|
+
const close = () => {
|
|
113
|
+
if (closing === undefined) {
|
|
114
|
+
stopped = true;
|
|
115
|
+
request.signal.removeEventListener('abort', onAbort);
|
|
116
|
+
closing = handler.close();
|
|
117
|
+
}
|
|
118
|
+
return closing;
|
|
119
|
+
};
|
|
120
|
+
const cancel = async (reason) => {
|
|
121
|
+
abort.abort(reason);
|
|
122
|
+
try {
|
|
123
|
+
await reader?.cancel(reason);
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
await close();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const onAbort = () => { void cancel(request.signal.reason).catch(() => { }); };
|
|
130
|
+
request.signal.addEventListener('abort', onAbort, { once: true });
|
|
131
|
+
if (request.signal.aborted)
|
|
132
|
+
onAbort();
|
|
133
|
+
try {
|
|
134
|
+
const response = await handler.fetch(new Request(request, { signal: abort.signal }), {
|
|
135
|
+
authInfo: { token, clientId: caller.clientId, scopes: [] },
|
|
136
|
+
});
|
|
137
|
+
if (response.body === null) {
|
|
138
|
+
await close();
|
|
139
|
+
return response;
|
|
140
|
+
}
|
|
141
|
+
reader = response.body.getReader();
|
|
142
|
+
if (abort.signal.aborted) {
|
|
143
|
+
await cancel(abort.signal.reason);
|
|
144
|
+
throw abort.signal.reason ?? new Error('request aborted');
|
|
145
|
+
}
|
|
146
|
+
const body = new ReadableStream({
|
|
147
|
+
async pull(controller) {
|
|
148
|
+
try {
|
|
149
|
+
const next = await reader.read();
|
|
150
|
+
if (stopped) {
|
|
151
|
+
controller.close();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (next.done) {
|
|
155
|
+
await close();
|
|
156
|
+
reader.releaseLock();
|
|
157
|
+
controller.close();
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
controller.enqueue(next.value);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
await close();
|
|
165
|
+
controller.error(error);
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
async cancel(reason) { await cancel(reason); },
|
|
169
|
+
}, { highWaterMark: 0 });
|
|
170
|
+
return new Response(body, {
|
|
171
|
+
status: response.status,
|
|
172
|
+
statusText: response.statusText,
|
|
173
|
+
headers: response.headers,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
await close();
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=sdk-v2.js.map
|
package/lib/mcp/server.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { type HeaderInput, type WireRequest } from './protocol.ts';
|
|
17
17
|
import type { SubscriptionRegistry } from './subscriptions.ts';
|
|
18
|
+
import type { ExternalCaller } from './client-grant.ts';
|
|
18
19
|
/** One tool as the MCP surface describes it. */
|
|
19
20
|
export interface McpToolDefinition {
|
|
20
21
|
/** The caller-facing name. */
|
|
@@ -56,9 +57,11 @@ export interface McpHost {
|
|
|
56
57
|
* @param name - the tool name.
|
|
57
58
|
* @param args - the tool arguments.
|
|
58
59
|
* @param signal - the caller's abort signal.
|
|
60
|
+
* @param caller - the AUTHENTICATED external caller and its derived grant (SEC-04),
|
|
61
|
+
* resolved by the transport; absent for non-HTTP transports that carry no client.
|
|
59
62
|
* @returns the outcome.
|
|
60
63
|
*/
|
|
61
|
-
callTool(name: string, args: Readonly<Record<string, unknown>>, signal: AbortSignal): Promise<ToolInvocationOutcome>;
|
|
64
|
+
callTool(name: string, args: Readonly<Record<string, unknown>>, signal: AbortSignal, caller?: ExternalCaller): Promise<ToolInvocationOutcome>;
|
|
62
65
|
}
|
|
63
66
|
/** One dispatched response. */
|
|
64
67
|
export interface DispatchResult {
|
|
@@ -111,5 +114,12 @@ export declare function dispatch(request: WireRequest, host: McpHost, options?:
|
|
|
111
114
|
readonly subscriptions?: SubscriptionRegistry;
|
|
112
115
|
/** The authenticated client, which a subscription is bound to. */
|
|
113
116
|
readonly clientId?: string;
|
|
117
|
+
/**
|
|
118
|
+
* The authenticated external caller and its derived grant (SEC-04).
|
|
119
|
+
*
|
|
120
|
+
* Resolved by the TRANSPORT after authentication, never read from the request body:
|
|
121
|
+
* a caller cannot supply or forge its own identity or grant (BASE-05).
|
|
122
|
+
*/
|
|
123
|
+
readonly caller?: ExternalCaller;
|
|
114
124
|
}): Promise<DispatchResult>;
|
|
115
125
|
//# sourceMappingURL=server.d.ts.map
|
package/lib/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAaL,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAaL,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,oEAAoE;IACpE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACvD,4CAA4C;IAC5C,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACxD,8DAA8D;IAC9D,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAClC;AAED,wCAAwC;AACxC,MAAM,WAAW,qBAAqB;IACpC,kDAAkD;IAClD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,sCAAsC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,yCAAyC;AACzC,MAAM,WAAW,OAAO;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACxE;;;OAGG;IACH,SAAS,IAAI,SAAS,iBAAiB,EAAE,CAAA;IACzC;;;;;;;;OAQG;IACH,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACvC,MAAM,EAAE,WAAW,EACnB,MAAM,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,qBAAqB,CAAC,CAAA;CAClC;AAED,+BAA+B;AAC/B,MAAM,WAAW,cAAc;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC3C,gDAAgD;IAChD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;CACvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQhF;AAED;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,OAAO,EACb,OAAO,GAAE;IACP,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAA;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,oBAAoB,CAAA;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAA;CAC5B,GACL,OAAO,CAAC,cAAc,CAAC,CA2IzB"}
|