@try-works/dsh-model-as-tool 0.1.1 → 0.1.3
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 +51 -0
- package/bin/dsh-tool-mode-mcp-stdio.mjs +80 -0
- package/catalog/desert-ant-models.json +77 -13
- package/lib/adapters/index.d.ts +19 -6
- package/lib/adapters/index.d.ts.map +1 -1
- package/lib/adapters/index.js +28 -13
- package/lib/adapters/tongue.d.ts +11 -3
- package/lib/adapters/tongue.d.ts.map +1 -1
- package/lib/adapters/tongue.js +12 -4
- package/lib/adapters/types.d.ts +11 -0
- package/lib/adapters/types.d.ts.map +1 -1
- package/lib/adapters/voz.d.ts +7 -1
- package/lib/adapters/voz.d.ts.map +1 -1
- package/lib/adapters/voz.js +16 -4
- package/lib/asset-acquisition.d.ts +137 -0
- package/lib/asset-acquisition.d.ts.map +1 -0
- package/lib/asset-acquisition.js +161 -0
- package/lib/authority.d.ts +153 -0
- package/lib/authority.d.ts.map +1 -0
- package/lib/authority.js +194 -0
- package/lib/bindings.d.ts +115 -0
- package/lib/bindings.d.ts.map +1 -0
- package/lib/bindings.js +239 -0
- package/lib/catalog.d.ts.map +1 -1
- package/lib/catalog.js +57 -0
- package/lib/contract-payload.d.ts +47 -0
- package/lib/contract-payload.d.ts.map +1 -0
- package/lib/contract-payload.js +137 -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/harness-budgets.d.ts +158 -0
- package/lib/harness-budgets.d.ts.map +1 -0
- package/lib/harness-budgets.js +152 -0
- package/lib/index.d.ts +41 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +424 -16
- package/lib/licensing.d.ts +221 -0
- package/lib/licensing.d.ts.map +1 -0
- package/lib/licensing.js +188 -0
- package/lib/mcp/host-binding.d.ts +91 -0
- package/lib/mcp/host-binding.d.ts.map +1 -0
- package/lib/mcp/host-binding.js +133 -0
- package/lib/mcp/http.d.ts +130 -0
- package/lib/mcp/http.d.ts.map +1 -0
- package/lib/mcp/http.js +274 -0
- package/lib/mcp/protocol.d.ts +261 -0
- package/lib/mcp/protocol.d.ts.map +1 -0
- package/lib/mcp/protocol.js +387 -0
- package/lib/mcp/server.d.ts +115 -0
- package/lib/mcp/server.d.ts.map +1 -0
- package/lib/mcp/server.js +200 -0
- package/lib/mcp/stdio.d.ts +80 -0
- package/lib/mcp/stdio.d.ts.map +1 -0
- package/lib/mcp/stdio.js +176 -0
- package/lib/mcp/subscriptions.d.ts +135 -0
- package/lib/mcp/subscriptions.d.ts.map +1 -0
- package/lib/mcp/subscriptions.js +207 -0
- package/lib/provider-bridge.d.ts +69 -0
- package/lib/provider-bridge.d.ts.map +1 -0
- package/lib/provider-bridge.js +218 -0
- package/lib/publication.d.ts +160 -0
- package/lib/publication.d.ts.map +1 -0
- package/lib/publication.js +173 -0
- package/lib/receipt.d.ts +208 -0
- package/lib/receipt.d.ts.map +1 -0
- package/lib/receipt.js +150 -0
- package/lib/registrar.d.ts +13 -0
- package/lib/registrar.d.ts.map +1 -1
- package/lib/registrar.js +20 -3
- 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/types.d.ts +21 -0
- package/lib/types.d.ts.map +1 -1
- 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/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/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 +260 -0
- package/node_modules/@try-works/tool-mode-contracts/lib/index.d.ts +23 -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 +24 -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 +81 -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 +163 -0
- package/node_modules/@try-works/tool-mode-contracts/package.json +35 -0
- package/package.json +17 -5
|
@@ -0,0 +1,130 @@
|
|
|
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-model-as-tool/mcp/http
|
|
18
|
+
*/
|
|
19
|
+
import { type McpHost } from './server.ts';
|
|
20
|
+
/** How the endpoint authenticates. */
|
|
21
|
+
export interface StaticTokenAuth {
|
|
22
|
+
/**
|
|
23
|
+
* The scheme this endpoint implements.
|
|
24
|
+
*
|
|
25
|
+
* Named `static-token` rather than `oauth` so nothing downstream can mistake it
|
|
26
|
+
* for the OAuth profile, and so a log or a status report that prints this value
|
|
27
|
+
* cannot imply a conformance that was never tested.
|
|
28
|
+
*/
|
|
29
|
+
readonly scheme: 'static-token';
|
|
30
|
+
/** The accepted token, compared in constant time. */
|
|
31
|
+
readonly token: string;
|
|
32
|
+
/** The caller identity recorded for the grant, for receipts and audit. */
|
|
33
|
+
readonly clientId: string;
|
|
34
|
+
}
|
|
35
|
+
/** Transport configuration. */
|
|
36
|
+
export interface HttpTransportConfig {
|
|
37
|
+
/** Path the endpoint serves. */
|
|
38
|
+
readonly path: string;
|
|
39
|
+
/** Bind address; only a loopback address is admitted. */
|
|
40
|
+
readonly host: string;
|
|
41
|
+
/** Port. */
|
|
42
|
+
readonly port: number;
|
|
43
|
+
/** Maximum request body size in bytes (SEC-02). */
|
|
44
|
+
readonly maxBodyBytes: number;
|
|
45
|
+
/**
|
|
46
|
+
* Origins permitted to call from a browser.
|
|
47
|
+
*
|
|
48
|
+
* Empty by default, which refuses every browser origin. SEC-02 requires
|
|
49
|
+
* unapproved origins to be REJECTED, so an empty list is a working configuration
|
|
50
|
+
* rather than a missing one, and CORS is not used as authentication anywhere.
|
|
51
|
+
*/
|
|
52
|
+
readonly allowedOrigins: readonly string[];
|
|
53
|
+
readonly auth: StaticTokenAuth;
|
|
54
|
+
}
|
|
55
|
+
/** A conservative default: loopback, the illustrative port, a small body cap. */
|
|
56
|
+
export declare const DEFAULT_HTTP_CONFIG: Omit<HttpTransportConfig, 'auth'>;
|
|
57
|
+
/** One HTTP request, described rather than received. */
|
|
58
|
+
export interface HttpRequestDescription {
|
|
59
|
+
readonly method: string;
|
|
60
|
+
/** The request path, without query. */
|
|
61
|
+
readonly path: string;
|
|
62
|
+
/** Lower-cased header names to values. */
|
|
63
|
+
readonly headers: Readonly<Record<string, string | undefined>>;
|
|
64
|
+
/** The raw body. */
|
|
65
|
+
readonly body: string;
|
|
66
|
+
/** The peer address, as the socket reports it. */
|
|
67
|
+
readonly remoteAddress: string;
|
|
68
|
+
}
|
|
69
|
+
/** One HTTP response. */
|
|
70
|
+
export interface HttpResponseDescription {
|
|
71
|
+
readonly status: number;
|
|
72
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
73
|
+
readonly body: string;
|
|
74
|
+
}
|
|
75
|
+
/** Whether an address is loopback. */
|
|
76
|
+
export declare function isLoopback(address: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Build the request handler.
|
|
79
|
+
*
|
|
80
|
+
* Ordering is deliberate and matches P26-16's list: transport identity (loopback,
|
|
81
|
+
* size, method, path) is settled before authentication, and authentication before
|
|
82
|
+
* any protocol parsing. A request that is not from the loopback is refused without
|
|
83
|
+
* its token being examined at all.
|
|
84
|
+
* @param config - the transport configuration.
|
|
85
|
+
* @param host - the MCP surface.
|
|
86
|
+
* @returns the handler.
|
|
87
|
+
*/
|
|
88
|
+
export declare function createHttpHandler(config: HttpTransportConfig, host: McpHost): (request: HttpRequestDescription) => Promise<HttpResponseDescription>;
|
|
89
|
+
/**
|
|
90
|
+
* Whether this transport may claim OAuth conformance.
|
|
91
|
+
*
|
|
92
|
+
* It may not. `P26-15` permits a static-token mode while forbidding the claim, and
|
|
93
|
+
* this returns false so a status report or a compatibility matrix cannot assert
|
|
94
|
+
* otherwise by accident.
|
|
95
|
+
* @returns always false, for the static-token scheme.
|
|
96
|
+
*/
|
|
97
|
+
export declare function claimsOAuthConformance(): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* The authentication profile this transport reports.
|
|
100
|
+
*
|
|
101
|
+
* A machine-readable statement of what is actually implemented, so a client or an
|
|
102
|
+
* operator can tell custom authentication from the OAuth profile without reading
|
|
103
|
+
* the source.
|
|
104
|
+
* @returns the auth profile description.
|
|
105
|
+
*/
|
|
106
|
+
export declare function authProfile(): Record<string, unknown>;
|
|
107
|
+
/** A bound endpoint. */
|
|
108
|
+
export interface BoundEndpoint {
|
|
109
|
+
/** The address actually bound, which for port 0 is the one the OS chose. */
|
|
110
|
+
readonly host: string;
|
|
111
|
+
readonly port: number;
|
|
112
|
+
/** The full URL clients should use. */
|
|
113
|
+
readonly url: string;
|
|
114
|
+
/** Stop serving and release the port. */
|
|
115
|
+
close(): Promise<void>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Bind the endpoint on loopback.
|
|
119
|
+
*
|
|
120
|
+
* The bind address is validated rather than trusted: a configuration naming a
|
|
121
|
+
* routable address is REFUSED at startup rather than silently exposing the endpoint,
|
|
122
|
+
* because a server that logs a warning and binds anyway is a server that is exposed.
|
|
123
|
+
* @param config - the transport configuration.
|
|
124
|
+
* @param host - the MCP surface.
|
|
125
|
+
* @param options - an optional pre-built server module, so a test can drive a fake.
|
|
126
|
+
* @returns the bound endpoint.
|
|
127
|
+
* @throws Error when the bind address is not loopback.
|
|
128
|
+
*/
|
|
129
|
+
export declare function startHttpServer(config: HttpTransportConfig, host: McpHost): Promise<BoundEndpoint>;
|
|
130
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/mcp/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AASH,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,aAAa,CAAA;AAGpD,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,+BAA+B;AAC/B,MAAM,WAAW,mBAAmB;IAClC,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,YAAY;IACZ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mDAAmD;IACnD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B;;;;;;OAMG;IACH,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAC/B;AAED,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAMjE,CAAA;AAED,wDAAwD;AACxD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IAC9D,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,kDAAkD;IAClD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAED,yBAAyB;AACzB,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,sCAAsC;AACtC,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMnD;AAgCD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,OAAO,GACZ,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAkGvE;AASD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQrD;AAED,wBAAwB;AACxB,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,uCAAuC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,yCAAyC;IACzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,aAAa,CAAC,CA8DxB"}
|
package/lib/mcp/http.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
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-model-as-tool/mcp/http
|
|
18
|
+
*/
|
|
19
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
20
|
+
import { assertHeadersMatch, ERROR_CODES, errorResponse, } from "./protocol.js";
|
|
21
|
+
import { dispatch } from "./server.js";
|
|
22
|
+
/** A conservative default: loopback, the illustrative port, a small body cap. */
|
|
23
|
+
export const DEFAULT_HTTP_CONFIG = {
|
|
24
|
+
path: '/mcp',
|
|
25
|
+
host: '127.0.0.1',
|
|
26
|
+
port: 3765,
|
|
27
|
+
maxBodyBytes: 4 * 1024 * 1024,
|
|
28
|
+
allowedOrigins: [],
|
|
29
|
+
};
|
|
30
|
+
/** Whether an address is loopback. */
|
|
31
|
+
export function isLoopback(address) {
|
|
32
|
+
// `::ffff:127.0.0.1` is how a v4 loopback appears on a dual-stack socket, and
|
|
33
|
+
// `::1` is v6 loopback. Treating either as remote would break a correct client;
|
|
34
|
+
// treating a real remote address as loopback would defeat the binding.
|
|
35
|
+
const normalised = address.startsWith('::ffff:') ? address.slice(7) : address;
|
|
36
|
+
return normalised === '127.0.0.1' || normalised === '::1' || normalised.startsWith('127.');
|
|
37
|
+
}
|
|
38
|
+
/** Render a response. */
|
|
39
|
+
function respond(status, body, headers = {}) {
|
|
40
|
+
return {
|
|
41
|
+
status,
|
|
42
|
+
headers: { 'content-type': 'application/json; charset=utf-8', ...headers },
|
|
43
|
+
body: typeof body === 'string' ? body : JSON.stringify(body),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Compare a presented token against the accepted one in constant time.
|
|
48
|
+
*
|
|
49
|
+
* Length is compared first because `timingSafeEqual` throws on a length mismatch;
|
|
50
|
+
* that leaks length, which is not a useful secret here since the token is generated
|
|
51
|
+
* by the operator rather than guessed byte by byte.
|
|
52
|
+
* @param presented - the token from the request.
|
|
53
|
+
* @param accepted - the configured token.
|
|
54
|
+
* @returns whether they match.
|
|
55
|
+
*/
|
|
56
|
+
function tokenMatches(presented, accepted) {
|
|
57
|
+
const left = Buffer.from(presented, 'utf8');
|
|
58
|
+
const right = Buffer.from(accepted, 'utf8');
|
|
59
|
+
if (left.length !== right.length)
|
|
60
|
+
return false;
|
|
61
|
+
return timingSafeEqual(left, right);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build the request handler.
|
|
65
|
+
*
|
|
66
|
+
* Ordering is deliberate and matches P26-16's list: transport identity (loopback,
|
|
67
|
+
* size, method, path) is settled before authentication, and authentication before
|
|
68
|
+
* any protocol parsing. A request that is not from the loopback is refused without
|
|
69
|
+
* its token being examined at all.
|
|
70
|
+
* @param config - the transport configuration.
|
|
71
|
+
* @param host - the MCP surface.
|
|
72
|
+
* @returns the handler.
|
|
73
|
+
*/
|
|
74
|
+
export function createHttpHandler(config, host) {
|
|
75
|
+
return async (request) => {
|
|
76
|
+
// 1. Loopback only (MCP-02, SEC-02). Checked against the SOCKET address, not a
|
|
77
|
+
// header: a header is caller-supplied, and trusting one would make the binding
|
|
78
|
+
// decorative.
|
|
79
|
+
if (!isLoopback(request.remoteAddress)) {
|
|
80
|
+
return respond(403, { error: 'this endpoint serves loopback clients only' });
|
|
81
|
+
}
|
|
82
|
+
// 2. Size limit (SEC-02), measured on the received bytes.
|
|
83
|
+
const bodyBytes = Buffer.byteLength(request.body, 'utf8');
|
|
84
|
+
if (bodyBytes > config.maxBodyBytes) {
|
|
85
|
+
// 413 rather than 400: the request may be perfectly well formed and merely
|
|
86
|
+
// too large, and a client needs to tell those apart.
|
|
87
|
+
return respond(413, {
|
|
88
|
+
jsonrpc: '2.0',
|
|
89
|
+
id: null,
|
|
90
|
+
error: { code: ERROR_CODES.invalidRequest, message: `the request body exceeds ${config.maxBodyBytes} bytes` },
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// 3. Method and path. No legacy GET/SSE-session assumptions (P26-05).
|
|
94
|
+
if (request.method !== 'POST') {
|
|
95
|
+
return respond(405, errorResponse(null, {
|
|
96
|
+
code: ERROR_CODES.invalidRequest,
|
|
97
|
+
message: 'this endpoint accepts POST only',
|
|
98
|
+
}), { allow: 'POST' });
|
|
99
|
+
}
|
|
100
|
+
if (request.path !== config.path) {
|
|
101
|
+
return respond(404, { error: `no endpoint at ${request.path}` });
|
|
102
|
+
}
|
|
103
|
+
// 4. Origin (SEC-02). An unapproved browser origin is refused, and CORS is
|
|
104
|
+
// never treated as authentication: passing this check grants nothing.
|
|
105
|
+
const origin = request.headers.origin;
|
|
106
|
+
if (origin !== undefined && !config.allowedOrigins.includes(origin)) {
|
|
107
|
+
return respond(403, { error: 'this origin is not approved for the endpoint' });
|
|
108
|
+
}
|
|
109
|
+
// 5. Authentication (P26-15). Applied to EVERY request including discovery,
|
|
110
|
+
// because an unauthenticated endpoint is not this product's default.
|
|
111
|
+
const presented = bearerOf(request.headers.authorization);
|
|
112
|
+
if (presented === undefined) {
|
|
113
|
+
return respond(401, errorResponse(null, {
|
|
114
|
+
code: ERROR_CODES.invalidRequest,
|
|
115
|
+
message: 'authentication is required',
|
|
116
|
+
}), { 'www-authenticate': 'Bearer' });
|
|
117
|
+
}
|
|
118
|
+
if (!tokenMatches(presented, config.auth.token)) {
|
|
119
|
+
// The message does not distinguish "unknown" from "expired": with a static
|
|
120
|
+
// token there is nothing to expire, and a more specific message would only
|
|
121
|
+
// help an attacker.
|
|
122
|
+
return respond(401, errorResponse(null, {
|
|
123
|
+
code: ERROR_CODES.invalidRequest,
|
|
124
|
+
message: 'the presented credential is not accepted',
|
|
125
|
+
}), { 'www-authenticate': 'Bearer' });
|
|
126
|
+
}
|
|
127
|
+
// 6. Parse, then check headers against the body (P26-05).
|
|
128
|
+
let parsed;
|
|
129
|
+
try {
|
|
130
|
+
parsed = JSON.parse(request.body);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return respond(400, errorResponse(null, { code: ERROR_CODES.parseError, message: 'the body is not valid JSON' }));
|
|
134
|
+
}
|
|
135
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
136
|
+
return respond(400, errorResponse(null, { code: ERROR_CODES.invalidRequest, message: 'the body is not a JSON-RPC object' }));
|
|
137
|
+
}
|
|
138
|
+
const wire = parsed;
|
|
139
|
+
// Built conditionally rather than with explicit `undefined` values, because
|
|
140
|
+
// `exactOptionalPropertyTypes` distinguishes "absent" from "present and
|
|
141
|
+
// undefined" — and so does the header check, which treats a missing header
|
|
142
|
+
// differently from an empty one.
|
|
143
|
+
const headerInput = {
|
|
144
|
+
...request.headers['mcp-protocol-version'] === undefined
|
|
145
|
+
? {}
|
|
146
|
+
: { protocolVersion: request.headers['mcp-protocol-version'] },
|
|
147
|
+
...request.headers['mcp-method'] === undefined ? {} : { method: request.headers['mcp-method'] },
|
|
148
|
+
...request.headers['mcp-name'] === undefined ? {} : { name: request.headers['mcp-name'] },
|
|
149
|
+
};
|
|
150
|
+
try {
|
|
151
|
+
assertHeadersMatch(headerInput, wire);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
155
|
+
// P26-05 names HTTP 400 and HeaderMismatch together.
|
|
156
|
+
return respond(400, errorResponse(wire.id ?? null, { code: ERROR_CODES.headerMismatch, message }));
|
|
157
|
+
}
|
|
158
|
+
// 7. Dispatch. A notification carries no id and is acknowledged with 202.
|
|
159
|
+
const outcome = await dispatch(wire, host, { signal: new AbortController().signal });
|
|
160
|
+
if (wire.id === undefined) {
|
|
161
|
+
return { status: 202, headers: {}, body: '' };
|
|
162
|
+
}
|
|
163
|
+
// The dispatch `httpStatus` already distinguishes invalid params (400) from a
|
|
164
|
+
// protocol-level failure that still produced a well-formed response (200).
|
|
165
|
+
return respond(outcome.httpStatus, outcome.response);
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** Extract a bearer token from an Authorization header. */
|
|
169
|
+
function bearerOf(header) {
|
|
170
|
+
if (header === undefined)
|
|
171
|
+
return undefined;
|
|
172
|
+
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
|
|
173
|
+
return match?.[1];
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Whether this transport may claim OAuth conformance.
|
|
177
|
+
*
|
|
178
|
+
* It may not. `P26-15` permits a static-token mode while forbidding the claim, and
|
|
179
|
+
* this returns false so a status report or a compatibility matrix cannot assert
|
|
180
|
+
* otherwise by accident.
|
|
181
|
+
* @returns always false, for the static-token scheme.
|
|
182
|
+
*/
|
|
183
|
+
export function claimsOAuthConformance() {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The authentication profile this transport reports.
|
|
188
|
+
*
|
|
189
|
+
* A machine-readable statement of what is actually implemented, so a client or an
|
|
190
|
+
* operator can tell custom authentication from the OAuth profile without reading
|
|
191
|
+
* the source.
|
|
192
|
+
* @returns the auth profile description.
|
|
193
|
+
*/
|
|
194
|
+
export function authProfile() {
|
|
195
|
+
return {
|
|
196
|
+
scheme: 'static-token',
|
|
197
|
+
// Stated explicitly rather than left to inference.
|
|
198
|
+
oauth21ResourceServer: false,
|
|
199
|
+
perClientRevocable: true,
|
|
200
|
+
note: 'Custom authentication. The OAuth 2.1 resource-server profile is not implemented and is not claimed.',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Bind the endpoint on loopback.
|
|
205
|
+
*
|
|
206
|
+
* The bind address is validated rather than trusted: a configuration naming a
|
|
207
|
+
* routable address is REFUSED at startup rather than silently exposing the endpoint,
|
|
208
|
+
* because a server that logs a warning and binds anyway is a server that is exposed.
|
|
209
|
+
* @param config - the transport configuration.
|
|
210
|
+
* @param host - the MCP surface.
|
|
211
|
+
* @param options - an optional pre-built server module, so a test can drive a fake.
|
|
212
|
+
* @returns the bound endpoint.
|
|
213
|
+
* @throws Error when the bind address is not loopback.
|
|
214
|
+
*/
|
|
215
|
+
export async function startHttpServer(config, host) {
|
|
216
|
+
if (!isLoopback(config.host)) {
|
|
217
|
+
// MCP-02 puts LAN and public binding out of scope; refusing at startup is the
|
|
218
|
+
// only enforcement that cannot be forgotten later.
|
|
219
|
+
throw new Error(`refusing to bind ${config.host}: this endpoint serves loopback only (MCP-02). `
|
|
220
|
+
+ 'A routable bind address would expose inference to the network.');
|
|
221
|
+
}
|
|
222
|
+
const { createServer } = await import('node:http');
|
|
223
|
+
const handler = createHttpHandler(config, host);
|
|
224
|
+
const server = createServer((incoming, outgoing) => {
|
|
225
|
+
const chunks = [];
|
|
226
|
+
let received = 0;
|
|
227
|
+
incoming.on('data', (chunk) => {
|
|
228
|
+
received += chunk.byteLength;
|
|
229
|
+
// The limit is enforced DURING the read, not after it: buffering an
|
|
230
|
+
// unbounded body in order to measure it defeats the limit.
|
|
231
|
+
if (received > config.maxBodyBytes) {
|
|
232
|
+
outgoing.writeHead(413, { 'content-type': 'application/json' });
|
|
233
|
+
outgoing.end(JSON.stringify({ error: 'request body too large' }));
|
|
234
|
+
incoming.destroy();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
chunks.push(chunk);
|
|
238
|
+
});
|
|
239
|
+
incoming.on('end', () => {
|
|
240
|
+
void handler({
|
|
241
|
+
method: incoming.method ?? 'POST',
|
|
242
|
+
path: (incoming.url ?? '/').split('?')[0],
|
|
243
|
+
headers: incoming.headers,
|
|
244
|
+
body: Buffer.concat(chunks).toString('utf8'),
|
|
245
|
+
remoteAddress: incoming.socket.remoteAddress ?? '',
|
|
246
|
+
}).then(response => {
|
|
247
|
+
outgoing.writeHead(response.status, response.headers);
|
|
248
|
+
outgoing.end(response.body);
|
|
249
|
+
}).catch((error) => {
|
|
250
|
+
// A handler that throws must not hang the connection.
|
|
251
|
+
outgoing.writeHead(500, { 'content-type': 'application/json' });
|
|
252
|
+
outgoing.end(JSON.stringify({ error: error instanceof Error ? error.message : 'internal error' }));
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
await new Promise((resolve, reject) => {
|
|
257
|
+
server.once('error', reject);
|
|
258
|
+
// Bound to the loopback interface specifically, not to all interfaces.
|
|
259
|
+
server.listen(config.port, config.host, () => resolve());
|
|
260
|
+
});
|
|
261
|
+
const address = server.address();
|
|
262
|
+
const port = typeof address === 'object' && address !== null ? address.port : config.port;
|
|
263
|
+
return {
|
|
264
|
+
host: config.host,
|
|
265
|
+
port,
|
|
266
|
+
url: `http://${config.host}:${port}${config.path}`,
|
|
267
|
+
async close() {
|
|
268
|
+
await new Promise((resolve, reject) => {
|
|
269
|
+
server.close(error => error === undefined ? resolve() : reject(error));
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The MCP 2026-07-28 wire core.
|
|
3
|
+
*
|
|
4
|
+
* The published `@modelcontextprotocol/sdk` (1.32.1) declares
|
|
5
|
+
* `LATEST_PROTOCOL_VERSION = 2025-11-25` and does not list `2026-07-28` among its
|
|
6
|
+
* supported revisions, so this is the "separately reviewed, schema-conformant
|
|
7
|
+
* adaptation" MCP-01 permits where SDK support is incomplete. It is written
|
|
8
|
+
* against the revision's own requirements rather than by changing a version string
|
|
9
|
+
* on legacy behaviour, which MCP-01 explicitly says does not satisfy the
|
|
10
|
+
* requirement.
|
|
11
|
+
*
|
|
12
|
+
* What makes this revision different, and what this module exists to enforce:
|
|
13
|
+
*
|
|
14
|
+
* - **No handshake.** `tools/list` and `tools/call` must work as the FIRST
|
|
15
|
+
* application request. There is no `initialize`, no
|
|
16
|
+
* `notifications/initialized`, no prior discovery call and no `Mcp-Session-Id`
|
|
17
|
+
* (P26-01). So there is no session state here to get wrong.
|
|
18
|
+
* - **Per-request metadata.** Version and capabilities travel in `params._meta` on
|
|
19
|
+
* EVERY request, and are never inferred from an earlier one (P26-02). That is
|
|
20
|
+
* why `_meta` is parsed per request and not cached.
|
|
21
|
+
* - **Server identity in results.** Every result envelope carries `resultType`,
|
|
22
|
+
* and the revision's guidance puts `serverInfo` in result metadata.
|
|
23
|
+
* - **Errors stay in their lanes** (P26-04): a malformed protocol request is a
|
|
24
|
+
* JSON-RPC error, while an application tool failure is a tool-error RESULT. A
|
|
25
|
+
* failure wrapped as a successful payload is the specific mistake this prevents.
|
|
26
|
+
*
|
|
27
|
+
* @module @try-works/dsh-model-as-tool/mcp/protocol
|
|
28
|
+
*/
|
|
29
|
+
/** The revision this server speaks. */
|
|
30
|
+
export declare const PROTOCOL_VERSION = "2026-07-28";
|
|
31
|
+
/**
|
|
32
|
+
* Revisions this server can also serve.
|
|
33
|
+
*
|
|
34
|
+
* Kept explicit and short. P26-03 requires an unsupported version to be answered
|
|
35
|
+
* with the supported list, so the list must be a fact rather than a guess.
|
|
36
|
+
*/
|
|
37
|
+
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly string[];
|
|
38
|
+
/** The `_meta` keys the revision defines. */
|
|
39
|
+
export declare const META_KEYS: {
|
|
40
|
+
readonly protocolVersion: "io.modelcontextprotocol/protocolVersion";
|
|
41
|
+
readonly clientInfo: "io.modelcontextprotocol/clientInfo";
|
|
42
|
+
readonly clientCapabilities: "io.modelcontextprotocol/clientCapabilities";
|
|
43
|
+
readonly serverInfo: "io.modelcontextprotocol/serverInfo";
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Error codes.
|
|
47
|
+
*
|
|
48
|
+
* The reserved range belongs to the protocol. P26-03 is explicit that custom
|
|
49
|
+
* errors must not be allocated there, so this module declares only codes the
|
|
50
|
+
* revision defines and refuses to mint new ones in that band.
|
|
51
|
+
*/
|
|
52
|
+
export declare const ERROR_CODES: {
|
|
53
|
+
/** Standard JSON-RPC. */
|
|
54
|
+
readonly parseError: -32700;
|
|
55
|
+
readonly invalidRequest: -32600;
|
|
56
|
+
readonly methodNotFound: -32601;
|
|
57
|
+
readonly invalidParams: -32602;
|
|
58
|
+
readonly internalError: -32603;
|
|
59
|
+
/** MCP-reserved. */
|
|
60
|
+
readonly headerMismatch: -32020;
|
|
61
|
+
readonly unsupportedClientCapability: -32021;
|
|
62
|
+
readonly unsupportedProtocolVersion: -32022;
|
|
63
|
+
};
|
|
64
|
+
/** The convention for a tool that ran and returned a domain failure (P26-04). */
|
|
65
|
+
export interface ToolErrorPayload {
|
|
66
|
+
/** Always true: an application failure, not a protocol failure. */
|
|
67
|
+
readonly isError: true;
|
|
68
|
+
/** A bounded, model-readable explanation. */
|
|
69
|
+
readonly content: readonly {
|
|
70
|
+
readonly type: 'text';
|
|
71
|
+
readonly text: string;
|
|
72
|
+
}[];
|
|
73
|
+
}
|
|
74
|
+
/** A JSON-RPC request as this server receives it. */
|
|
75
|
+
export interface WireRequest {
|
|
76
|
+
readonly jsonrpc: '2.0';
|
|
77
|
+
readonly id?: string | number | null;
|
|
78
|
+
readonly method: string;
|
|
79
|
+
readonly params?: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
/** Per-request metadata, parsed fresh on every request (P26-02). */
|
|
82
|
+
export interface RequestMetadata {
|
|
83
|
+
readonly protocolVersion: string;
|
|
84
|
+
readonly clientInfo: {
|
|
85
|
+
readonly name: string;
|
|
86
|
+
readonly version: string;
|
|
87
|
+
};
|
|
88
|
+
readonly clientCapabilities: Readonly<Record<string, unknown>>;
|
|
89
|
+
}
|
|
90
|
+
/** Raised for a request that must be answered with a JSON-RPC error. */
|
|
91
|
+
export declare class WireError extends Error {
|
|
92
|
+
readonly name = "WireError";
|
|
93
|
+
readonly code: number;
|
|
94
|
+
/** Structured detail, as the revision's schema defines for the code. */
|
|
95
|
+
readonly data?: Record<string, unknown>;
|
|
96
|
+
constructor(code: number, message: string, data?: Record<string, unknown>);
|
|
97
|
+
}
|
|
98
|
+
/** Whether a numeric code lies in the range reserved for the protocol. */
|
|
99
|
+
export declare function isReservedErrorCode(code: number): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Parse `params._meta` from a request (P26-02).
|
|
102
|
+
*
|
|
103
|
+
* Missing required metadata is `invalid params`, not a defaulted value: inferring
|
|
104
|
+
* a version or a capability from a preceding request is exactly what P26-02
|
|
105
|
+
* forbids, and defaulting is inference with extra steps.
|
|
106
|
+
*
|
|
107
|
+
* The REQUIRED set is taken from the pinned revision schema's `RequestMetaObject`,
|
|
108
|
+
* which requires `clientCapabilities` and `protocolVersion` — and NOT `clientInfo`.
|
|
109
|
+
* Requiring `clientInfo` here would reject a request the schema admits, which is a
|
|
110
|
+
* conformance failure in the opposite direction from accepting too much.
|
|
111
|
+
*
|
|
112
|
+
* @param params - the request parameters.
|
|
113
|
+
* @returns the parsed metadata.
|
|
114
|
+
* @throws WireError `-32602` when a required key is absent or malformed.
|
|
115
|
+
*/
|
|
116
|
+
export declare function parseRequestMetadata(params: Record<string, unknown> | undefined): RequestMetadata;
|
|
117
|
+
/**
|
|
118
|
+
* Negotiate the protocol version for one request (P26-03).
|
|
119
|
+
*
|
|
120
|
+
* @param metadata - the request metadata.
|
|
121
|
+
* @returns the negotiated version.
|
|
122
|
+
* @throws WireError `-32022` carrying the official `supported` and `requested` data.
|
|
123
|
+
*/
|
|
124
|
+
export declare function negotiateVersion(metadata: RequestMetadata): string;
|
|
125
|
+
/** A feature this server advertises, and whether it is implemented. */
|
|
126
|
+
export interface FeatureDeclaration {
|
|
127
|
+
readonly feature: string;
|
|
128
|
+
readonly implemented: boolean;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The advertised-feature matrix (P26-12).
|
|
132
|
+
*
|
|
133
|
+
* P26-12 forbids advertising Tasks, sampling, resource subscriptions, prompts or
|
|
134
|
+
* UI extensions merely because an SDK exports their types. Declaring the matrix as
|
|
135
|
+
* data — with `implemented: false` where that is the truth — means the server
|
|
136
|
+
* cannot advertise a feature it does not have by accident.
|
|
137
|
+
*/
|
|
138
|
+
export declare const ADVERTISED_FEATURES: readonly FeatureDeclaration[];
|
|
139
|
+
/**
|
|
140
|
+
* The capabilities object this server may advertise.
|
|
141
|
+
*
|
|
142
|
+
* Derived from {@link ADVERTISED_FEATURES} rather than written twice, so the
|
|
143
|
+
* matrix and the advertised capabilities cannot disagree.
|
|
144
|
+
* @returns the capability names that are genuinely implemented.
|
|
145
|
+
*/
|
|
146
|
+
export declare function implementedCapabilities(): string[];
|
|
147
|
+
/**
|
|
148
|
+
* Whether a client's REQUIRED capability is one this server supports (P26-03).
|
|
149
|
+
*
|
|
150
|
+
* @param required - capability names the client declared as required.
|
|
151
|
+
* @returns the unsupported names, empty when all are supported.
|
|
152
|
+
*/
|
|
153
|
+
export declare function unsupportedRequiredCapabilities(required: readonly string[]): string[];
|
|
154
|
+
/**
|
|
155
|
+
* The caching fields every cacheable result must carry (P26-14).
|
|
156
|
+
*
|
|
157
|
+
* Taken from the pinned schema's `CacheableResult`, which REQUIRES `cacheScope` and
|
|
158
|
+
* `ttlMs` on `DiscoverResult` and `ListToolsResult` alike. Tool Mode's
|
|
159
|
+
* authorization-filtered catalogs are private, so the default TTL is zero and the
|
|
160
|
+
* scope is private — a shared cache keyed on tool names would leak one caller's
|
|
161
|
+
* permitted set to another.
|
|
162
|
+
* @returns the caching fields.
|
|
163
|
+
*/
|
|
164
|
+
export declare function cacheableResultFields(): {
|
|
165
|
+
readonly cacheScope: string;
|
|
166
|
+
readonly ttlMs: number;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Build a successful result envelope (P26-04).
|
|
170
|
+
*
|
|
171
|
+
* Every modern result carries `resultType`, and `serverInfo` rides in result
|
|
172
|
+
* metadata per the revision's guidance — `ResultMetaObject` admits exactly that
|
|
173
|
+
* one key, which is what this writes.
|
|
174
|
+
* @param result - the method-specific fields.
|
|
175
|
+
* @param serverInfo - this server's identity.
|
|
176
|
+
* @returns the result object, including `resultType` and `_meta`.
|
|
177
|
+
*/
|
|
178
|
+
export declare function successResult(result: Record<string, unknown>, serverInfo: {
|
|
179
|
+
readonly name: string;
|
|
180
|
+
readonly version: string;
|
|
181
|
+
}): Record<string, unknown>;
|
|
182
|
+
/**
|
|
183
|
+
* Build a tool-error RESULT (P26-04).
|
|
184
|
+
*
|
|
185
|
+
* The distinction this preserves is the whole point: an application failure is a
|
|
186
|
+
* successful protocol exchange carrying `isError`, while a malformed request is a
|
|
187
|
+
* JSON-RPC error. Wrapping a domain failure as protocol success, or an
|
|
188
|
+
* infrastructure failure as a successful domain payload, are both forbidden.
|
|
189
|
+
* @param message - a bounded, model-readable explanation.
|
|
190
|
+
* @returns the tool-error result.
|
|
191
|
+
*/
|
|
192
|
+
export declare function toolErrorResult(message: string): ToolErrorPayload;
|
|
193
|
+
/**
|
|
194
|
+
* Build a JSON-RPC error response.
|
|
195
|
+
*
|
|
196
|
+
* When the request id is UNKNOWN the `id` member is omitted rather than set to
|
|
197
|
+
* `null`. JSON-RPC 2.0 conventionally allows `id: null` for an undeterminable id,
|
|
198
|
+
* but the pinned revision schema types `RequestId` as `string | integer` and does
|
|
199
|
+
* not admit null — while `id` itself is optional. Omitting it is therefore the
|
|
200
|
+
* conformant way to say "unknown", and the independent probe caught this by
|
|
201
|
+
* rejecting a captured 401 whose id was null.
|
|
202
|
+
*
|
|
203
|
+
* @param id - the request id, or null when it could not be determined.
|
|
204
|
+
* @param error - the failure.
|
|
205
|
+
* @returns the response envelope.
|
|
206
|
+
*/
|
|
207
|
+
export declare function errorResponse(id: string | number | null, error: WireError | {
|
|
208
|
+
code: number;
|
|
209
|
+
message: string;
|
|
210
|
+
data?: Record<string, unknown>;
|
|
211
|
+
}): Record<string, unknown>;
|
|
212
|
+
/** A parsed HTTP request's relevant fields. */
|
|
213
|
+
export interface HeaderInput {
|
|
214
|
+
/** The `MCP-Protocol-Version` header, if present. */
|
|
215
|
+
readonly protocolVersion?: string;
|
|
216
|
+
/** The `Mcp-Method` header, if present. */
|
|
217
|
+
readonly method?: string;
|
|
218
|
+
/** The `Mcp-Name` header, if present. */
|
|
219
|
+
readonly name?: string;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Decode the revision's Base64 sentinel encoding (P26-05).
|
|
223
|
+
*
|
|
224
|
+
* A header value may be encoded with the official `=?base64?…?=` sentinel, and it
|
|
225
|
+
* must be decoded BEFORE comparison. Comparing the encoded form would let a client
|
|
226
|
+
* present a value that decodes to something else entirely.
|
|
227
|
+
* @param value - the raw header value.
|
|
228
|
+
* @returns the decoded value.
|
|
229
|
+
*/
|
|
230
|
+
export declare function decodeHeaderValue(value: string): string;
|
|
231
|
+
/**
|
|
232
|
+
* Check the required headers against the body (P26-05).
|
|
233
|
+
*
|
|
234
|
+
* Headers are checked against BODY FIELDS, so a request cannot claim one method in
|
|
235
|
+
* a header and another in its body. Every mismatch is `HeaderMismatch` (-32020),
|
|
236
|
+
* which the HTTP layer reports as 400.
|
|
237
|
+
* @param headers - the relevant request headers.
|
|
238
|
+
* @param body - the parsed request.
|
|
239
|
+
* @returns nothing when the headers agree.
|
|
240
|
+
* @throws WireError `-32020` naming the first disagreement.
|
|
241
|
+
*/
|
|
242
|
+
export declare function assertHeadersMatch(headers: HeaderInput, body: WireRequest): void;
|
|
243
|
+
/**
|
|
244
|
+
* Parse a newline-delimited stdio frame (P26-08).
|
|
245
|
+
*
|
|
246
|
+
* Stdio carries protocol traffic on stdout and diagnostics on stderr, so a line
|
|
247
|
+
* that is not a JSON object is a protocol error rather than something to log and
|
|
248
|
+
* skip: silently dropping it would leave a client waiting for a response that
|
|
249
|
+
* never comes.
|
|
250
|
+
* @param line - one line of input.
|
|
251
|
+
* @returns the parsed request, or undefined for a blank line.
|
|
252
|
+
* @throws WireError `-32700` when the line is not a JSON object.
|
|
253
|
+
*/
|
|
254
|
+
export declare function parseStdioLine(line: string): WireRequest | undefined;
|
|
255
|
+
/**
|
|
256
|
+
* Serialize one stdio frame.
|
|
257
|
+
* @param message - the response or notification.
|
|
258
|
+
* @returns the line, newline-terminated.
|
|
259
|
+
*/
|
|
260
|
+
export declare function serializeStdioFrame(message: unknown): string;
|
|
261
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/mcp/protocol.ts"],"names":[],"mappings":"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"}
|