ostacky 0.9.3 → 0.9.5
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 +27 -25
- package/assets/agents/ostacky.md +5 -4
- package/assets/commands/install-stack.md +2 -2
- package/assets/mcp/ostacky-controller/bun.lock +20 -0
- package/assets/mcp/ostacky-controller/index.js +185 -29
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/{auth-B2Q9_Vxp.d.cts → auth--n_Yzytt.d.cts} +14 -9
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/{auth-CVhIKulL.d.mts.map → auth--n_Yzytt.d.cts.map} +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/{auth-CfDMXiII.cjs → auth-4-zilcqg.cjs} +3 -2
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/auth-4-zilcqg.cjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/{auth-CUe6YdwF.mjs → auth-CGP0BDVq.mjs} +3 -2
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/auth-CGP0BDVq.mjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/{auth-CVhIKulL.d.mts → auth-YUQV3RRv.d.mts} +14 -9
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/{auth-B2Q9_Vxp.d.cts.map → auth-YUQV3RRv.d.mts.map} +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/index.cjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/index.d.cts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/index.d.mts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/index.mjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/internal.cjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/internal.d.cts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/internal.d.mts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/internal.mjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/package.json +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/README.md +3 -3
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{ajvProvider-CplOkyLC.mjs → ajvProvider-CEoC__sr.mjs} +483 -38
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/ajvProvider-CEoC__sr.mjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{ajvProvider-BdSo9BII.d.mts → ajvProvider-CgI-5L6O.d.mts} +25 -8
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{ajvProvider-BdSo9BII.d.mts.map → ajvProvider-CgI-5L6O.d.mts.map} +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{ajvProvider-B_4czhCS.d.cts → ajvProvider-FOQRM7up.d.cts} +24 -7
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{ajvProvider-B_4czhCS.d.cts.map → ajvProvider-FOQRM7up.d.cts.map} +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{ajvProvider-Brn-Q2J_.cjs → ajvProvider-ZaoO9afR.cjs} +483 -38
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/ajvProvider-ZaoO9afR.cjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{cfWorkerProvider-ojcZaap3.cjs → cfWorkerProvider-BwbPzsdI.cjs} +25 -15
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-BwbPzsdI.cjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{cfWorkerProvider-BYRW5af5.d.mts → cfWorkerProvider-C03CSigr.d.mts} +21 -6
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-C03CSigr.d.mts.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{cfWorkerProvider-D9GAttn8.d.cts → cfWorkerProvider-doTkZbTr.d.cts} +20 -5
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-doTkZbTr.d.cts.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{cfWorkerProvider-Bd-iTHAF.mjs → cfWorkerProvider-p3WaZPqB.mjs} +26 -15
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-p3WaZPqB.mjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{createMcpHandler-DzBzpE4_.d.cts → createMcpHandler-BLVe3qmU.d.cts} +137 -32
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-BLVe3qmU.d.cts.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{createMcpHandler-CEckaSEs.d.mts → createMcpHandler-Bt6U_Fqb.d.mts} +138 -33
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-Bt6U_Fqb.d.mts.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/dialects-D8eXzoGv.cjs +46 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/dialects-D8eXzoGv.cjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs +34 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.cjs +209 -257
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.cjs.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.d.cts +45 -3
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.d.cts.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.d.mts +46 -4
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.d.mts.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.mjs +201 -252
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.mjs.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{mcp-DM1iHYFo.cjs → mcp-DUcCQSTW.cjs} +458 -35
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/mcp-DUcCQSTW.cjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{mcp-IJurDZVN.mjs → mcp-Dw2OlZ1f.mjs} +389 -32
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/mcp-Dw2OlZ1f.mjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsBrowser.cjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsBrowser.d.cts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsBrowser.d.mts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsBrowser.mjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsNode.cjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsNode.d.cts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsNode.d.mts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.cjs +2 -2
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.d.cts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.d.mts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/shimsWorkerd.mjs +2 -2
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{src-BX_fm-dB.cjs → src-CgFsfg3E.cjs} +161 -48
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/src-CgFsfg3E.cjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{src-D86MbS1I.mjs → src-D-y6h4N7.mjs} +161 -48
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/src-D-y6h4N7.mjs.map +1 -0
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.cjs +30 -3
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.cjs.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.d.cts +9 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.d.cts.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.d.mts +9 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.d.mts.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.mjs +30 -3
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/stdio.mjs.map +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{types-mS0yxCL0.d.mts → types-DUs7mGBv.d.mts} +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/{types-mS0yxCL0.d.mts.map → types-DUs7mGBv.d.mts.map} +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/ajv.cjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/ajv.d.cts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/ajv.d.mts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/ajv.mjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/cfWorker.cjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/cfWorker.d.cts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/cfWorker.d.mts +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/validators/cfWorker.mjs +1 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/package.json +4 -4
- package/assets/mcp/ostacky-controller/package.json +1 -1
- package/assets/plugins/ostacky-controller/index.ts +53 -11
- package/dist/cli.js +93 -53
- package/manifest.json +28 -28
- package/package.json +5 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/auth-CUe6YdwF.mjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/core/dist/auth-CfDMXiII.cjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/ajvProvider-Brn-Q2J_.cjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/ajvProvider-CplOkyLC.mjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-BYRW5af5.d.mts.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-Bd-iTHAF.mjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-D9GAttn8.d.cts.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/cfWorkerProvider-ojcZaap3.cjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-CEckaSEs.d.mts.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/createMcpHandler-DzBzpE4_.d.cts.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/mcp-DM1iHYFo.cjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/mcp-IJurDZVN.mjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/src-BX_fm-dB.cjs.map +0 -1
- package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/src-D86MbS1I.mjs.map +0 -1
package/assets/mcp/ostacky-controller/node_modules/@modelcontextprotocol/server/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as seedClientIdentityFromEnvelope, c as
|
|
2
|
-
import { $ as isTaskAugmentedRequestParams, At as LATEST_PROTOCOL_VERSION, B as scanXMcpHeaderDeclarations, Bt as TRACESTATE_META_KEY, Ct as CLIENT_CAPABILITIES_META_KEY, D as acceptedContent, Dt as INVALID_PARAMS, Et as INTERNAL_ERROR, Ft as RELATED_TASK_META_KEY, G as isInitializeRequest, Gt as resourceUrlFromServerUrl, H as assertCompleteRequestPrompt, I as classifyInboundRequest, It as SERVER_INFO_META_KEY, J as isJSONRPCErrorResponse, Jt as SdkHttpError, K as isInitializedNotification, Kt as SdkError, L as httpStatusForErrorCode, Lt as SUBSCRIPTION_ID_META_KEY, Mt as METHOD_NOT_FOUND, Nt as PARSE_ERROR, O as inputRequired, Ot as INVALID_REQUEST, P as LADDER_ERROR_HTTP_STATUS, Pt as PROTOCOL_VERSION_META_KEY, Q as isJSONRPCResultResponse, R as modernOnlyStrictRejection, Rt as SUPPORTED_PROTOCOL_VERSIONS, S as specTypeSchemas, St as BAGGAGE_META_KEY, Tt as DEFAULT_NEGOTIATED_PROTOCOL_VERSION, U as assertCompleteRequestResourceTemplate, Ut as requiredClientCapabilitiesForRequest, V as validateMcpParamHeaders, Vt as missingClientCapabilities, W as isCallToolResult, Wt as checkResourceAllowed, X as isJSONRPCRequest, Xt as OAuthErrorCode, Y as isJSONRPCNotification, Yt as OAuthError, Z as isJSONRPCResponse, _ as mergeCapabilities, a as UriTemplate, at as requestMetaOf, c as ReadBuffer, d as serializeMessage, dt as ResourceNotFoundError, et as parseJSONRPCMessage, f as getDisplayName, ft as UnsupportedProtocolVersionError, g as Protocol, h as DEFAULT_REQUEST_TIMEOUT_MSEC, i as InMemoryTransport, jt as LOG_LEVEL_META_KEY, k as inputResponse, kt as JSONRPC_VERSION, l as STDIO_DEFAULT_MAX_BUFFER_SIZE, lt as MissingRequiredClientCapabilityError, m as mediaTypeEssence, mt as ProtocolErrorCode, n as preloadSchemas, o as createFetchWithInit, p as isJsonContentType, pt as UrlElicitationRequiredError, q as isInputRequiredResult, qt as SdkErrorCode, t as fromJsonSchema$1, tt as JSONRPCMessageSchema, u as deserializeMessage, ut as ProtocolError, vt as SUPPORTED_MODERN_PROTOCOL_VERSIONS, wt as CLIENT_INFO_META_KEY, x as isSpecType, y as setNegotiatedProtocolVersion, z as validateStandardRequestHeaders, zt as TRACEPARENT_META_KEY } from "./src-
|
|
1
|
+
import { C as armSseKeepAlive, S as DEFAULT_SSE_KEEP_ALIVE_MS, T as isCompletable, _ as DEFAULT_MAX_SUBSCRIPTIONS, a as seedClientIdentityFromEnvelope, b as InMemoryServerEventBus, c as findScopeChallenge, d as buildOAuthProtectedResourceMetadata, f as getOAuthProtectedResourceMetadataUrl, g as verifyBearerToken, h as requireBearerAuth, i as installModernOnlyHandlers, l as requireScopes, m as bearerAuthChallengeResponse, n as ResourceTemplate, o as serverIdentityOf, p as oauthMetadataResponse, r as Server, s as createScopeChallengeResponse, t as McpServer, u as scopeChallengeResourceMetadataUrl, w as completable, x as createServerNotifier, y as createListenRouter } from "./mcp-Dw2OlZ1f.mjs";
|
|
2
|
+
import { $ as isTaskAugmentedRequestParams, At as LATEST_PROTOCOL_VERSION, B as scanXMcpHeaderDeclarations, Bt as TRACESTATE_META_KEY, Ct as CLIENT_CAPABILITIES_META_KEY, D as acceptedContent, Dt as INVALID_PARAMS, Et as INTERNAL_ERROR, Ft as RELATED_TASK_META_KEY, G as isInitializeRequest, Gt as resourceUrlFromServerUrl, H as assertCompleteRequestPrompt, I as classifyInboundRequest, It as SERVER_INFO_META_KEY, J as isJSONRPCErrorResponse, Jt as SdkHttpError, K as isInitializedNotification, Kt as SdkError, L as httpStatusForErrorCode, Lt as SUBSCRIPTION_ID_META_KEY, Mt as METHOD_NOT_FOUND, Nt as PARSE_ERROR, O as inputRequired, Ot as INVALID_REQUEST, P as LADDER_ERROR_HTTP_STATUS, Pt as PROTOCOL_VERSION_META_KEY, Q as isJSONRPCResultResponse, R as modernOnlyStrictRejection, Rt as SUPPORTED_PROTOCOL_VERSIONS, S as specTypeSchemas, St as BAGGAGE_META_KEY, Tt as DEFAULT_NEGOTIATED_PROTOCOL_VERSION, U as assertCompleteRequestResourceTemplate, Ut as requiredClientCapabilitiesForRequest, V as validateMcpParamHeaders, Vt as missingClientCapabilities, W as isCallToolResult, Wt as checkResourceAllowed, X as isJSONRPCRequest, Xt as OAuthErrorCode, Y as isJSONRPCNotification, Yt as OAuthError, Z as isJSONRPCResponse, _ as mergeCapabilities, a as UriTemplate, at as requestMetaOf, c as ReadBuffer, d as serializeMessage, dt as ResourceNotFoundError, et as parseJSONRPCMessage, f as getDisplayName, ft as UnsupportedProtocolVersionError, g as Protocol, h as DEFAULT_REQUEST_TIMEOUT_MSEC, i as InMemoryTransport, jt as LOG_LEVEL_META_KEY, k as inputResponse, kt as JSONRPC_VERSION, l as STDIO_DEFAULT_MAX_BUFFER_SIZE, lt as MissingRequiredClientCapabilityError, m as mediaTypeEssence, mt as ProtocolErrorCode, n as preloadSchemas, o as createFetchWithInit, p as isJsonContentType, pt as UrlElicitationRequiredError, q as isInputRequiredResult, qt as SdkErrorCode, t as fromJsonSchema$1, tt as JSONRPCMessageSchema, u as deserializeMessage, ut as ProtocolError, vt as SUPPORTED_MODERN_PROTOCOL_VERSIONS, wt as CLIENT_INFO_META_KEY, x as isSpecType, y as setNegotiatedProtocolVersion, z as validateStandardRequestHeaders, zt as TRACEPARENT_META_KEY } from "./src-D-y6h4N7.mjs";
|
|
3
3
|
import { DefaultJsonSchemaValidator } from "@modelcontextprotocol/server/_shims";
|
|
4
4
|
|
|
5
5
|
//#region src/server/perRequestTransport.ts
|
|
@@ -32,9 +32,11 @@ var PerRequestHTTPServerTransport = class {
|
|
|
32
32
|
_deferredResponse;
|
|
33
33
|
_sse;
|
|
34
34
|
_abortCleanup;
|
|
35
|
+
_keepAliveMs;
|
|
35
36
|
constructor(options) {
|
|
36
37
|
this._classification = options.classification;
|
|
37
38
|
this._responseMode = options.responseMode ?? "auto";
|
|
39
|
+
this._keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;
|
|
38
40
|
}
|
|
39
41
|
async start() {
|
|
40
42
|
if (this._started) throw new Error("PerRequestHTTPServerTransport is already started");
|
|
@@ -148,6 +150,7 @@ var PerRequestHTTPServerTransport = class {
|
|
|
148
150
|
this._closed = true;
|
|
149
151
|
this._abortCleanup?.();
|
|
150
152
|
this._abortCleanup = void 0;
|
|
153
|
+
if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
|
|
151
154
|
if (this._sse !== void 0 && !this._sse.closed) {
|
|
152
155
|
this._sse.closed = true;
|
|
153
156
|
try {
|
|
@@ -180,17 +183,19 @@ var PerRequestHTTPServerTransport = class {
|
|
|
180
183
|
encoder: new TextEncoder(),
|
|
181
184
|
closed: false
|
|
182
185
|
};
|
|
186
|
+
this._sse.keepAliveTimer = armSseKeepAlive(this._keepAliveMs, () => this.writeCommentFrame("keepalive"));
|
|
183
187
|
this.settleResponse(new Response(readable, {
|
|
184
188
|
status: 200,
|
|
185
189
|
headers: {
|
|
186
190
|
"Content-Type": "text/event-stream",
|
|
187
|
-
"Cache-Control": "no-cache",
|
|
191
|
+
"Cache-Control": "no-cache, no-transform",
|
|
188
192
|
Connection: "keep-alive",
|
|
189
193
|
"X-Accel-Buffering": "no"
|
|
190
194
|
}
|
|
191
195
|
}));
|
|
192
196
|
}
|
|
193
197
|
finalizeStream() {
|
|
198
|
+
if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
|
|
194
199
|
if (this._sse !== void 0 && !this._sse.closed) {
|
|
195
200
|
this._sse.closed = true;
|
|
196
201
|
try {
|
|
@@ -231,7 +236,8 @@ var PerRequestHTTPServerTransport = class {
|
|
|
231
236
|
async function invoke(server, message, ctx) {
|
|
232
237
|
const transport = new PerRequestHTTPServerTransport({
|
|
233
238
|
classification: ctx.classification,
|
|
234
|
-
...ctx.responseMode !== void 0 && { responseMode: ctx.responseMode }
|
|
239
|
+
...ctx.responseMode !== void 0 && { responseMode: ctx.responseMode },
|
|
240
|
+
...ctx.keepAliveMs !== void 0 && { keepAliveMs: ctx.keepAliveMs }
|
|
235
241
|
});
|
|
236
242
|
await server.connect(transport);
|
|
237
243
|
return transport.handleMessage(message, {
|
|
@@ -240,6 +246,59 @@ async function invoke(server, message, ctx) {
|
|
|
240
246
|
});
|
|
241
247
|
}
|
|
242
248
|
|
|
249
|
+
//#endregion
|
|
250
|
+
//#region src/server/requestBody.ts
|
|
251
|
+
/** Default upper bound, in bytes, on a request body read by the HTTP entry points (4 MiB). */
|
|
252
|
+
const DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
|
|
253
|
+
/** Upper bound on the number of messages accepted in one JSON-RPC batch array. */
|
|
254
|
+
const MAX_BATCH_SIZE = 100;
|
|
255
|
+
/** The message answered with 413 for a request body over `maxBytes`. */
|
|
256
|
+
function requestBodyTooLargeMessage(maxBytes) {
|
|
257
|
+
return `Payload Too Large: Request body must not exceed ${maxBytes} bytes`;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Resolves a `maxRequestBodySize` option to the bound to apply: the default when
|
|
261
|
+
* omitted, otherwise the value itself, which must be a positive finite number of
|
|
262
|
+
* bytes (a `RangeError` is thrown at configuration time for anything else).
|
|
263
|
+
*/
|
|
264
|
+
function resolveMaxRequestBodySize(value) {
|
|
265
|
+
if (value === void 0) return DEFAULT_MAX_REQUEST_BODY_SIZE;
|
|
266
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new RangeError(`maxRequestBodySize must be a positive number of bytes, got ${String(value)}`);
|
|
267
|
+
return value;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Reads a request body as text, up to `maxBytes` (default
|
|
271
|
+
* {@linkcode DEFAULT_MAX_REQUEST_BODY_SIZE}). A declared `Content-Length` over the
|
|
272
|
+
* limit is refused without reading anything; otherwise the read stops as soon as
|
|
273
|
+
* more than the limit has arrived. Stream failures propagate.
|
|
274
|
+
*/
|
|
275
|
+
async function readRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZE) {
|
|
276
|
+
if (Number(request.headers.get("content-length")) > maxBytes) return { tooLarge: true };
|
|
277
|
+
if (request.body === null) return {
|
|
278
|
+
tooLarge: false,
|
|
279
|
+
text: ""
|
|
280
|
+
};
|
|
281
|
+
const reader = request.body.getReader();
|
|
282
|
+
const decoder = new TextDecoder();
|
|
283
|
+
let received = 0;
|
|
284
|
+
let text = "";
|
|
285
|
+
try {
|
|
286
|
+
for (;;) {
|
|
287
|
+
const { done, value } = await reader.read();
|
|
288
|
+
if (done) break;
|
|
289
|
+
received += value.byteLength;
|
|
290
|
+
if (received > maxBytes) return { tooLarge: true };
|
|
291
|
+
text += decoder.decode(value, { stream: true });
|
|
292
|
+
}
|
|
293
|
+
} finally {
|
|
294
|
+
reader.releaseLock();
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
tooLarge: false,
|
|
298
|
+
text: text + decoder.decode()
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
243
302
|
//#endregion
|
|
244
303
|
//#region src/server/streamableHttp.ts
|
|
245
304
|
/**
|
|
@@ -311,6 +370,9 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
311
370
|
_enableDnsRebindingProtection;
|
|
312
371
|
_retryInterval;
|
|
313
372
|
_supportedProtocolVersions;
|
|
373
|
+
_keepAliveMs;
|
|
374
|
+
_maxRequestBodySize;
|
|
375
|
+
_scopeChallengeResolver;
|
|
314
376
|
sessionId;
|
|
315
377
|
onclose;
|
|
316
378
|
onerror;
|
|
@@ -326,6 +388,23 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
326
388
|
this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;
|
|
327
389
|
this._retryInterval = options.retryInterval;
|
|
328
390
|
this._supportedProtocolVersions = options.supportedProtocolVersions ?? SUPPORTED_PROTOCOL_VERSIONS;
|
|
391
|
+
this._keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;
|
|
392
|
+
this._maxRequestBodySize = resolveMaxRequestBodySize(options.maxRequestBodySize);
|
|
393
|
+
}
|
|
394
|
+
startKeepAlive(controller, encoder) {
|
|
395
|
+
if (this._closed) return void 0;
|
|
396
|
+
const timer = armSseKeepAlive(this._keepAliveMs, () => {
|
|
397
|
+
try {
|
|
398
|
+
controller.enqueue(encoder.encode(": keepalive\n\n"));
|
|
399
|
+
} catch {
|
|
400
|
+
if (timer !== void 0) clearInterval(timer);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
return timer;
|
|
404
|
+
}
|
|
405
|
+
/** Sets the scope challenge resolver for parsed JSON-RPC requests. */
|
|
406
|
+
setScopeChallengeResolver(resolver) {
|
|
407
|
+
this._scopeChallengeResolver = resolver;
|
|
329
408
|
}
|
|
330
409
|
/**
|
|
331
410
|
* Starts the transport. This is required by the {@linkcode Transport} interface but is a no-op
|
|
@@ -363,6 +442,11 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
363
442
|
}
|
|
364
443
|
});
|
|
365
444
|
}
|
|
445
|
+
async _checkScopeChallenge(messages, authInfo) {
|
|
446
|
+
if (!this._scopeChallengeResolver) return;
|
|
447
|
+
const challenge = await findScopeChallenge(messages.filter((message) => isJSONRPCRequest(message)), authInfo, this._scopeChallengeResolver);
|
|
448
|
+
return challenge === void 0 ? void 0 : createScopeChallengeResponse(challenge, scopeChallengeResourceMetadataUrl(authInfo));
|
|
449
|
+
}
|
|
366
450
|
/**
|
|
367
451
|
* Validates request headers for DNS rebinding protection.
|
|
368
452
|
* @returns Error response if validation fails, `undefined` if validation passes.
|
|
@@ -391,6 +475,7 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
391
475
|
* Returns a `Response` object (Web Standard)
|
|
392
476
|
*/
|
|
393
477
|
async handleRequest(req, options) {
|
|
478
|
+
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
394
479
|
const validationError = this.validateRequestHeaders(req);
|
|
395
480
|
if (validationError) return validationError;
|
|
396
481
|
switch (req.method) {
|
|
@@ -451,30 +536,35 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
451
536
|
}
|
|
452
537
|
const encoder = new TextEncoder();
|
|
453
538
|
let streamController;
|
|
539
|
+
let keepAliveTimer;
|
|
454
540
|
const readable = new ReadableStream({
|
|
455
541
|
start: (controller) => {
|
|
456
542
|
streamController = controller;
|
|
457
543
|
},
|
|
458
544
|
cancel: () => {
|
|
545
|
+
if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
|
|
459
546
|
if (this._streamMapping.get(this._standaloneSseStreamId)?.controller === streamController) this._streamMapping.delete(this._standaloneSseStreamId);
|
|
460
547
|
}
|
|
461
548
|
});
|
|
462
549
|
const headers = {
|
|
463
550
|
"Content-Type": "text/event-stream",
|
|
464
551
|
"Cache-Control": "no-cache, no-transform",
|
|
465
|
-
Connection: "keep-alive"
|
|
552
|
+
Connection: "keep-alive",
|
|
553
|
+
"X-Accel-Buffering": "no"
|
|
466
554
|
};
|
|
467
555
|
if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
|
|
468
556
|
this._streamMapping.set(this._standaloneSseStreamId, {
|
|
469
557
|
controller: streamController,
|
|
470
558
|
encoder,
|
|
471
559
|
cleanup: () => {
|
|
560
|
+
if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
|
|
472
561
|
this._streamMapping.delete(this._standaloneSseStreamId);
|
|
473
562
|
try {
|
|
474
563
|
streamController.close();
|
|
475
564
|
} catch {}
|
|
476
565
|
}
|
|
477
566
|
});
|
|
567
|
+
keepAliveTimer = this.startKeepAlive(streamController, encoder);
|
|
478
568
|
return new Response(readable, { headers });
|
|
479
569
|
}
|
|
480
570
|
/**
|
|
@@ -502,17 +592,22 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
502
592
|
const headers = {
|
|
503
593
|
"Content-Type": "text/event-stream",
|
|
504
594
|
"Cache-Control": "no-cache, no-transform",
|
|
505
|
-
Connection: "keep-alive"
|
|
595
|
+
Connection: "keep-alive",
|
|
596
|
+
"X-Accel-Buffering": "no"
|
|
506
597
|
};
|
|
507
598
|
if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
|
|
508
599
|
const encoder = new TextEncoder();
|
|
509
600
|
let streamController;
|
|
601
|
+
let keepAliveTimer;
|
|
602
|
+
let cancelled = false;
|
|
510
603
|
let replayedStreamId;
|
|
511
604
|
const readable = new ReadableStream({
|
|
512
605
|
start: (controller) => {
|
|
513
606
|
streamController = controller;
|
|
514
607
|
},
|
|
515
608
|
cancel: () => {
|
|
609
|
+
cancelled = true;
|
|
610
|
+
if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
|
|
516
611
|
if (replayedStreamId !== void 0 && this._streamMapping.get(replayedStreamId)?.controller === streamController) this._streamMapping.delete(replayedStreamId);
|
|
517
612
|
}
|
|
518
613
|
});
|
|
@@ -523,11 +618,19 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
523
618
|
streamController.close();
|
|
524
619
|
} catch {}
|
|
525
620
|
} });
|
|
621
|
+
if (this._closed || cancelled) {
|
|
622
|
+
try {
|
|
623
|
+
streamController.close();
|
|
624
|
+
} catch {}
|
|
625
|
+
return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
626
|
+
}
|
|
627
|
+
this._streamMapping.get(replayedStreamId)?.cleanup();
|
|
526
628
|
this._streamMapping.set(replayedStreamId, {
|
|
527
629
|
controller: streamController,
|
|
528
630
|
encoder,
|
|
529
631
|
replayedEventIds,
|
|
530
632
|
cleanup: () => {
|
|
633
|
+
if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
|
|
531
634
|
this._streamMapping.delete(replayedStreamId);
|
|
532
635
|
try {
|
|
533
636
|
streamController.close();
|
|
@@ -542,6 +645,7 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
542
645
|
} catch {}
|
|
543
646
|
}
|
|
544
647
|
}
|
|
648
|
+
if (this._streamMapping.get(replayedStreamId)?.controller === streamController) keepAliveTimer = this.startKeepAlive(streamController, encoder);
|
|
545
649
|
return new Response(readable, { headers });
|
|
546
650
|
} catch (error) {
|
|
547
651
|
this.onerror?.(error);
|
|
@@ -600,12 +704,22 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
600
704
|
const request = req;
|
|
601
705
|
let rawMessage;
|
|
602
706
|
if (options?.parsedBody === void 0) try {
|
|
603
|
-
|
|
707
|
+
const body = await readRequestBody(req, this._maxRequestBodySize);
|
|
708
|
+
if (body.tooLarge) {
|
|
709
|
+
const message = requestBodyTooLargeMessage(this._maxRequestBodySize);
|
|
710
|
+
this.onerror?.(new Error(message));
|
|
711
|
+
return this.createJsonErrorResponse(413, -32e3, message);
|
|
712
|
+
}
|
|
713
|
+
rawMessage = JSON.parse(body.text);
|
|
604
714
|
} catch (error) {
|
|
605
715
|
this.onerror?.(error);
|
|
606
716
|
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON");
|
|
607
717
|
}
|
|
608
718
|
else rawMessage = options.parsedBody;
|
|
719
|
+
if (Array.isArray(rawMessage) && rawMessage.length > MAX_BATCH_SIZE) {
|
|
720
|
+
this.onerror?.(/* @__PURE__ */ new Error(`Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`));
|
|
721
|
+
return this.createJsonErrorResponse(400, -32600, `Invalid Request: Batch must not exceed ${MAX_BATCH_SIZE} messages`);
|
|
722
|
+
}
|
|
609
723
|
let messages;
|
|
610
724
|
try {
|
|
611
725
|
messages = Array.isArray(rawMessage) ? rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(rawMessage)];
|
|
@@ -613,6 +727,7 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
613
727
|
this.onerror?.(error);
|
|
614
728
|
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
|
|
615
729
|
}
|
|
730
|
+
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
616
731
|
const isInitializationRequest = messages.some((element) => isInitializeRequest(element));
|
|
617
732
|
if (isInitializationRequest) {
|
|
618
733
|
if (this._initialized && this.sessionId !== void 0) {
|
|
@@ -633,6 +748,15 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
633
748
|
const protocolError = this.validateProtocolVersion(req);
|
|
634
749
|
if (protocolError) return protocolError;
|
|
635
750
|
}
|
|
751
|
+
if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
752
|
+
let scopeChallengeResponse;
|
|
753
|
+
try {
|
|
754
|
+
scopeChallengeResponse = await this._checkScopeChallenge(messages, options?.authInfo);
|
|
755
|
+
} catch (error) {
|
|
756
|
+
this.onerror?.(error);
|
|
757
|
+
return this.createJsonErrorResponse(500, -32603, "Internal server error");
|
|
758
|
+
}
|
|
759
|
+
if (scopeChallengeResponse) return scopeChallengeResponse;
|
|
636
760
|
if (!messages.some((element) => isJSONRPCRequest(element))) {
|
|
637
761
|
for (const message of messages) this.onmessage?.(message, {
|
|
638
762
|
authInfo: options?.authInfo,
|
|
@@ -658,18 +782,21 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
658
782
|
});
|
|
659
783
|
const encoder = new TextEncoder();
|
|
660
784
|
let streamController;
|
|
785
|
+
let keepAliveTimer;
|
|
661
786
|
const readable = new ReadableStream({
|
|
662
787
|
start: (controller) => {
|
|
663
788
|
streamController = controller;
|
|
664
789
|
},
|
|
665
790
|
cancel: () => {
|
|
791
|
+
if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
|
|
666
792
|
if (this._streamMapping.get(streamId)?.controller === streamController) this._streamMapping.delete(streamId);
|
|
667
793
|
}
|
|
668
794
|
});
|
|
669
795
|
const headers = {
|
|
670
796
|
"Content-Type": "text/event-stream",
|
|
671
|
-
"Cache-Control": "no-cache",
|
|
672
|
-
Connection: "keep-alive"
|
|
797
|
+
"Cache-Control": "no-cache, no-transform",
|
|
798
|
+
Connection: "keep-alive",
|
|
799
|
+
"X-Accel-Buffering": "no"
|
|
673
800
|
};
|
|
674
801
|
if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
|
|
675
802
|
for (const message of messages) if (isJSONRPCRequest(message)) {
|
|
@@ -677,6 +804,7 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
677
804
|
controller: streamController,
|
|
678
805
|
encoder,
|
|
679
806
|
cleanup: () => {
|
|
807
|
+
if (keepAliveTimer !== void 0) clearInterval(keepAliveTimer);
|
|
680
808
|
this._streamMapping.delete(streamId);
|
|
681
809
|
try {
|
|
682
810
|
streamController.close();
|
|
@@ -704,6 +832,7 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
704
832
|
closeStandaloneSSEStream
|
|
705
833
|
});
|
|
706
834
|
}
|
|
835
|
+
if (this._streamMapping.get(streamId)?.controller === streamController) keepAliveTimer = this.startKeepAlive(streamController, encoder);
|
|
707
836
|
return new Response(readable, {
|
|
708
837
|
status: 200,
|
|
709
838
|
headers
|
|
@@ -721,9 +850,12 @@ var WebStandardStreamableHTTPServerTransport = class {
|
|
|
721
850
|
if (sessionError) return sessionError;
|
|
722
851
|
const protocolError = this.validateProtocolVersion(req);
|
|
723
852
|
if (protocolError) return protocolError;
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
853
|
+
try {
|
|
854
|
+
await Promise.resolve(this._onsessionclosed?.(this.sessionId));
|
|
855
|
+
return new Response(null, { status: 200 });
|
|
856
|
+
} finally {
|
|
857
|
+
await this.close();
|
|
858
|
+
}
|
|
727
859
|
}
|
|
728
860
|
/**
|
|
729
861
|
* Validates session ID for non-initialization requests.
|
|
@@ -914,7 +1046,7 @@ function internalServerErrorResponse(id = null) {
|
|
|
914
1046
|
* The entry passes its own `onerror` here when expanding the default, so
|
|
915
1047
|
* legacy-leg failures are never silently swallowed.
|
|
916
1048
|
*/
|
|
917
|
-
function
|
|
1049
|
+
function createLegacyStatelessFallback(factory, onerror, keepAliveMs, maxRequestBodySize) {
|
|
918
1050
|
return async (request, options) => {
|
|
919
1051
|
if (request.method.toUpperCase() !== "POST") return jsonRpcErrorResponse(405, -32e3, "Method not allowed.");
|
|
920
1052
|
try {
|
|
@@ -923,7 +1055,11 @@ function legacyStatelessFallback(factory, onerror) {
|
|
|
923
1055
|
...options?.authInfo !== void 0 && { authInfo: options.authInfo },
|
|
924
1056
|
requestInfo: request
|
|
925
1057
|
});
|
|
926
|
-
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
1058
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
1059
|
+
sessionIdGenerator: void 0,
|
|
1060
|
+
...keepAliveMs !== void 0 && { keepAliveMs },
|
|
1061
|
+
...maxRequestBodySize !== void 0 && { maxRequestBodySize }
|
|
1062
|
+
});
|
|
927
1063
|
await product.connect(transport);
|
|
928
1064
|
const teardown = () => {
|
|
929
1065
|
transport.close().catch(() => {});
|
|
@@ -979,6 +1115,27 @@ function legacyStatelessFallback(factory, onerror) {
|
|
|
979
1115
|
}
|
|
980
1116
|
};
|
|
981
1117
|
}
|
|
1118
|
+
function legacyStatelessFallback(factory, onerror, options) {
|
|
1119
|
+
return createLegacyStatelessFallback(factory, onerror, void 0, resolveMaxRequestBodySize(options?.maxRequestBodySize));
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Read the SEP-2243 standard request headers off the inbound request.
|
|
1123
|
+
*
|
|
1124
|
+
* Both halves of the standard-header story need them — the body-primary
|
|
1125
|
+
* classifier for its cross-check cells, and
|
|
1126
|
+
* {@linkcode validateStandardRequestHeaders} for the presence half — and a
|
|
1127
|
+
* header read at one site but not the other is precisely how a required header
|
|
1128
|
+
* goes unenforced: that divergence is what let a modern POST omitting
|
|
1129
|
+
* `MCP-Protocol-Version` be served. Read them here once so a header added to
|
|
1130
|
+
* {@linkcode InboundHttpRequest} reaches both sites together.
|
|
1131
|
+
*/
|
|
1132
|
+
function standardHeadersOf(request) {
|
|
1133
|
+
return {
|
|
1134
|
+
protocolVersionHeader: request.headers.get("mcp-protocol-version") ?? void 0,
|
|
1135
|
+
mcpMethodHeader: request.headers.get("mcp-method") ?? void 0,
|
|
1136
|
+
mcpNameHeader: request.headers.get("mcp-name") ?? void 0
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
982
1139
|
/**
|
|
983
1140
|
* The entry's classification step: read the request body exactly once (unless
|
|
984
1141
|
* a pre-parsed body is supplied) and classify the request with
|
|
@@ -990,7 +1147,7 @@ function legacyStatelessFallback(factory, onerror) {
|
|
|
990
1147
|
* the body-preserving clone is then skipped and `forwardRequest` is the
|
|
991
1148
|
* (consumed) input request.
|
|
992
1149
|
*/
|
|
993
|
-
async function classifyEntryRequest(request, providedParsedBody, needsForward = true) {
|
|
1150
|
+
async function classifyEntryRequest(request, providedParsedBody, needsForward = true, maxRequestBodySize = DEFAULT_MAX_REQUEST_BODY_SIZE) {
|
|
994
1151
|
const httpMethod = request.method.toUpperCase();
|
|
995
1152
|
let body;
|
|
996
1153
|
let parsedBody = providedParsedBody;
|
|
@@ -1001,7 +1158,9 @@ async function classifyEntryRequest(request, providedParsedBody, needsForward =
|
|
|
1001
1158
|
if (needsForward) forwardRequest = request.clone();
|
|
1002
1159
|
let bodyText;
|
|
1003
1160
|
try {
|
|
1004
|
-
|
|
1161
|
+
const read = await readRequestBody(request, maxRequestBodySize);
|
|
1162
|
+
if (read.tooLarge) return { step: "body-too-large" };
|
|
1163
|
+
bodyText = read.text;
|
|
1005
1164
|
} catch {
|
|
1006
1165
|
return { step: "unreadable-body" };
|
|
1007
1166
|
}
|
|
@@ -1021,9 +1180,7 @@ async function classifyEntryRequest(request, providedParsedBody, needsForward =
|
|
|
1021
1180
|
step: "classified",
|
|
1022
1181
|
outcome: classifyInboundRequest({
|
|
1023
1182
|
httpMethod,
|
|
1024
|
-
|
|
1025
|
-
mcpMethodHeader: request.headers.get("mcp-method") ?? void 0,
|
|
1026
|
-
mcpNameHeader: request.headers.get("mcp-name") ?? void 0,
|
|
1183
|
+
...standardHeadersOf(request),
|
|
1027
1184
|
...body !== void 0 && { body }
|
|
1028
1185
|
}),
|
|
1029
1186
|
body,
|
|
@@ -1091,7 +1248,8 @@ async function classifyEntryRequest(request, providedParsedBody, needsForward =
|
|
|
1091
1248
|
* answers it with the unsupported-protocol-version error), a malformed
|
|
1092
1249
|
* envelope behind a present claim (answered `-32602`), a request whose
|
|
1093
1250
|
* `MCP-Protocol-Version` header names a modern revision but that lacks the
|
|
1094
|
-
* envelope (`-32602`),
|
|
1251
|
+
* envelope (`-32602`), header/body mismatches (`-32020`), and a POST body
|
|
1252
|
+
* over the size limit (413). Consumers
|
|
1095
1253
|
* routing on the predicate must send `false` traffic to the modern handler,
|
|
1096
1254
|
* never to a legacy handler — the modern path owns those error answers.
|
|
1097
1255
|
* - `server/discover` probes sent by negotiating clients always carry the
|
|
@@ -1099,8 +1257,9 @@ async function classifyEntryRequest(request, providedParsedBody, needsForward =
|
|
|
1099
1257
|
* a method named `server/discover` has no claim and classifies legacy,
|
|
1100
1258
|
* exactly as the entry itself routes it.
|
|
1101
1259
|
*/
|
|
1102
|
-
async function isLegacyRequest(request, parsedBody) {
|
|
1103
|
-
const
|
|
1260
|
+
async function isLegacyRequest(request, parsedBody, options) {
|
|
1261
|
+
const maxRequestBodySize = resolveMaxRequestBodySize(options?.maxRequestBodySize);
|
|
1262
|
+
const classified = await classifyEntryRequest(parsedBody === void 0 && request.method.toUpperCase() === "POST" ? request.clone() : request, parsedBody, false, maxRequestBodySize);
|
|
1104
1263
|
return classified.step === "no-json-body" || classified.step === "classified" && classified.outcome.kind === "legacy";
|
|
1105
1264
|
}
|
|
1106
1265
|
/**
|
|
@@ -1149,6 +1308,7 @@ async function isLegacyRequest(request, parsedBody) {
|
|
|
1149
1308
|
*/
|
|
1150
1309
|
function createMcpHandler(factory, options = {}) {
|
|
1151
1310
|
const { legacy, onerror, responseMode } = options;
|
|
1311
|
+
const maxRequestBodySize = resolveMaxRequestBodySize(options.maxRequestBodySize);
|
|
1152
1312
|
if (typeof legacy === "function") throw new TypeError("The 'legacy' option only accepts 'stateless' or 'reject', not a handler function. To serve 2025-era traffic with your own handler, route in user land with the exported isLegacyRequest(request) predicate in front of a strict (legacy: 'reject') handler.");
|
|
1153
1313
|
/** Modern per-request instances with an exchange still in flight (close() tears these down). */
|
|
1154
1314
|
const inflight = /* @__PURE__ */ new Set();
|
|
@@ -1163,11 +1323,11 @@ function createMcpHandler(factory, options = {}) {
|
|
|
1163
1323
|
const listenRouter = createListenRouter({
|
|
1164
1324
|
bus,
|
|
1165
1325
|
maxSubscriptions: options.maxSubscriptions ?? DEFAULT_MAX_SUBSCRIPTIONS,
|
|
1166
|
-
keepAliveMs: options.keepAliveMs ??
|
|
1326
|
+
keepAliveMs: options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS,
|
|
1167
1327
|
onerror: reportError
|
|
1168
1328
|
});
|
|
1169
1329
|
if (responseMode === "json") console.warn("responseMode: 'json' drops mid-call notifications. subscriptions/listen streams are always served over SSE regardless; other notifications emitted before a result are dropped.");
|
|
1170
|
-
const legacyHandler = legacy === "reject" ? void 0 :
|
|
1330
|
+
const legacyHandler = legacy === "reject" ? void 0 : createLegacyStatelessFallback(factory, reportError, options.keepAliveMs, maxRequestBodySize);
|
|
1171
1331
|
async function serveModern(route, request, authInfo) {
|
|
1172
1332
|
const claimedRevision = route.classification.revision;
|
|
1173
1333
|
if (claimedRevision === void 0 || !SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(claimedRevision)) {
|
|
@@ -1180,8 +1340,7 @@ function createMcpHandler(factory, options = {}) {
|
|
|
1180
1340
|
}
|
|
1181
1341
|
const stdHeaderRejection = validateStandardRequestHeaders({
|
|
1182
1342
|
httpMethod: request.method,
|
|
1183
|
-
|
|
1184
|
-
mcpNameHeader: request.headers.get("mcp-name") ?? void 0
|
|
1343
|
+
...standardHeadersOf(request)
|
|
1185
1344
|
}, route);
|
|
1186
1345
|
if (stdHeaderRejection !== void 0) {
|
|
1187
1346
|
reportError(/* @__PURE__ */ new Error(`Rejected inbound request (${stdHeaderRejection.cell}): ${stdHeaderRejection.message}`));
|
|
@@ -1228,6 +1387,17 @@ function createMcpHandler(factory, options = {}) {
|
|
|
1228
1387
|
}
|
|
1229
1388
|
}
|
|
1230
1389
|
}
|
|
1390
|
+
if (route.messageKind === "request" && product instanceof McpServer) try {
|
|
1391
|
+
const challenge = await findScopeChallenge([route.message], authInfo, (context) => product.resolveScopeChallenge(context));
|
|
1392
|
+
if (challenge !== void 0) {
|
|
1393
|
+
product.close().catch(reportError);
|
|
1394
|
+
return createScopeChallengeResponse(challenge, scopeChallengeResourceMetadataUrl(authInfo));
|
|
1395
|
+
}
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
product.close().catch(reportError);
|
|
1398
|
+
reportError(toError(error));
|
|
1399
|
+
return internalServerErrorResponse(route.message.id);
|
|
1400
|
+
}
|
|
1231
1401
|
setNegotiatedProtocolVersion(server, claimedRevision);
|
|
1232
1402
|
installModernOnlyHandlers(server, SUPPORTED_MODERN_PROTOCOL_VERSIONS);
|
|
1233
1403
|
if (meta !== void 0) seedClientIdentityFromEnvelope(server, {
|
|
@@ -1245,7 +1415,8 @@ function createMcpHandler(factory, options = {}) {
|
|
|
1245
1415
|
classification: route.classification,
|
|
1246
1416
|
request,
|
|
1247
1417
|
...authInfo !== void 0 && { authInfo },
|
|
1248
|
-
...responseMode !== void 0 && { responseMode }
|
|
1418
|
+
...responseMode !== void 0 && { responseMode },
|
|
1419
|
+
...options.keepAliveMs !== void 0 && { keepAliveMs: options.keepAliveMs }
|
|
1249
1420
|
});
|
|
1250
1421
|
if (route.messageKind === "notification") queueMicrotask(() => void server.close().catch(() => {}));
|
|
1251
1422
|
return response;
|
|
@@ -1273,8 +1444,9 @@ function createMcpHandler(factory, options = {}) {
|
|
|
1273
1444
|
reportError(/* @__PURE__ */ new Error("Unsupported Media Type: Content-Type must be application/json"));
|
|
1274
1445
|
return jsonRpcErrorResponse(415, -32e3, "Unsupported Media Type: Content-Type must be application/json");
|
|
1275
1446
|
}
|
|
1276
|
-
const classified = await classifyEntryRequest(request, requestOptions?.parsedBody);
|
|
1447
|
+
const classified = await classifyEntryRequest(request, requestOptions?.parsedBody, true, maxRequestBodySize);
|
|
1277
1448
|
if (classified.step === "unreadable-body") return jsonRpcErrorResponse(400, -32700, "Parse error: the request body could not be read");
|
|
1449
|
+
if (classified.step === "body-too-large") return jsonRpcErrorResponse(413, -32e3, requestBodyTooLargeMessage(maxRequestBodySize));
|
|
1278
1450
|
if (classified.step === "no-json-body") {
|
|
1279
1451
|
if (legacyHandler !== void 0) return legacyHandler(classified.forwardRequest, { ...authInfo !== void 0 && { authInfo } });
|
|
1280
1452
|
return jsonRpcErrorResponse(400, -32700, "Parse error: the request body is not valid JSON");
|
|
@@ -1316,115 +1488,6 @@ function createMcpHandler(factory, options = {}) {
|
|
|
1316
1488
|
};
|
|
1317
1489
|
}
|
|
1318
1490
|
|
|
1319
|
-
//#endregion
|
|
1320
|
-
//#region src/server/middleware/bearerAuth.ts
|
|
1321
|
-
function headerQuotedValue(value) {
|
|
1322
|
-
return value.replaceAll(/[\\"]/g, String.raw`\$&`).replaceAll(/[^\u0020-\u007E]/g, " ");
|
|
1323
|
-
}
|
|
1324
|
-
function buildWwwAuthenticateHeader(errorCode, description, requiredScopes, resourceMetadataUrl) {
|
|
1325
|
-
let header = `Bearer error="${headerQuotedValue(errorCode)}", error_description="${headerQuotedValue(description)}"`;
|
|
1326
|
-
if (requiredScopes.length > 0) header += `, scope="${requiredScopes.join(" ")}"`;
|
|
1327
|
-
if (resourceMetadataUrl) header += `, resource_metadata="${resourceMetadataUrl}"`;
|
|
1328
|
-
return header;
|
|
1329
|
-
}
|
|
1330
|
-
/**
|
|
1331
|
-
* Validate a raw `Authorization` header value as a Bearer token and return
|
|
1332
|
-
* the verified {@link AuthInfo}.
|
|
1333
|
-
*
|
|
1334
|
-
* The runtime-neutral core of Bearer authentication: it parses the header,
|
|
1335
|
-
* runs the verifier, enforces `requiredScopes`, and rejects tokens without an
|
|
1336
|
-
* expiration or past it. On any failure it throws an {@link OAuthError} —
|
|
1337
|
-
* pass that to {@link bearerAuthChallengeResponse} for the matching HTTP
|
|
1338
|
-
* answer, or use {@link requireBearerAuth} to get both steps as one call.
|
|
1339
|
-
*
|
|
1340
|
-
* Framework adapters build on this: `requireBearerAuth` from
|
|
1341
|
-
* `@modelcontextprotocol/express` feeds it `req.headers.authorization`.
|
|
1342
|
-
*/
|
|
1343
|
-
async function verifyBearerToken(authorizationHeader, options) {
|
|
1344
|
-
const { verifier, requiredScopes = [] } = options;
|
|
1345
|
-
if (!authorizationHeader) throw new OAuthError(OAuthErrorCode.InvalidToken, "Missing Authorization header");
|
|
1346
|
-
const [type, token] = authorizationHeader.split(" ");
|
|
1347
|
-
if (type?.toLowerCase() !== "bearer" || !token) throw new OAuthError(OAuthErrorCode.InvalidToken, "Invalid Authorization header format, expected 'Bearer TOKEN'");
|
|
1348
|
-
const authInfo = await verifier.verifyAccessToken(token);
|
|
1349
|
-
if (requiredScopes.length > 0) {
|
|
1350
|
-
if (!requiredScopes.every((scope) => authInfo.scopes.includes(scope))) throw new OAuthError(OAuthErrorCode.InsufficientScope, "Insufficient scope");
|
|
1351
|
-
}
|
|
1352
|
-
if (typeof authInfo.expiresAt !== "number" || Number.isNaN(authInfo.expiresAt)) throw new OAuthError(OAuthErrorCode.InvalidToken, "Token has no expiration time");
|
|
1353
|
-
else if (authInfo.expiresAt < Date.now() / 1e3) throw new OAuthError(OAuthErrorCode.InvalidToken, "Token has expired");
|
|
1354
|
-
return authInfo;
|
|
1355
|
-
}
|
|
1356
|
-
/**
|
|
1357
|
-
* Build the HTTP answer for a Bearer authentication failure.
|
|
1358
|
-
*
|
|
1359
|
-
* Maps an {@link OAuthError} to its status — `401` for `invalid_token` and
|
|
1360
|
-
* `403` for `insufficient_scope` (both carrying the `WWW-Authenticate: Bearer …`
|
|
1361
|
-
* challenge, with `resource_metadata` when configured so clients can discover
|
|
1362
|
-
* the Authorization Server), `500` for `server_error`, `400` for anything
|
|
1363
|
-
* else. A non-`OAuthError` value answers `500 server_error`. The body is the
|
|
1364
|
-
* OAuth error JSON.
|
|
1365
|
-
*/
|
|
1366
|
-
function bearerAuthChallengeResponse(error, options) {
|
|
1367
|
-
const { requiredScopes = [], resourceMetadataUrl } = options ?? {};
|
|
1368
|
-
if (!(error instanceof OAuthError)) {
|
|
1369
|
-
const serverError = new OAuthError(OAuthErrorCode.ServerError, "Internal Server Error");
|
|
1370
|
-
return Response.json(serverError.toResponseObject(), { status: 500 });
|
|
1371
|
-
}
|
|
1372
|
-
switch (error.code) {
|
|
1373
|
-
case OAuthErrorCode.InvalidToken: {
|
|
1374
|
-
const challenge = buildWwwAuthenticateHeader(error.code, error.message, requiredScopes, resourceMetadataUrl);
|
|
1375
|
-
return Response.json(error.toResponseObject(), {
|
|
1376
|
-
status: 401,
|
|
1377
|
-
headers: { "WWW-Authenticate": challenge }
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
case OAuthErrorCode.InsufficientScope: {
|
|
1381
|
-
const challenge = buildWwwAuthenticateHeader(error.code, error.message, requiredScopes, resourceMetadataUrl);
|
|
1382
|
-
return Response.json(error.toResponseObject(), {
|
|
1383
|
-
status: 403,
|
|
1384
|
-
headers: { "WWW-Authenticate": challenge }
|
|
1385
|
-
});
|
|
1386
|
-
}
|
|
1387
|
-
case OAuthErrorCode.ServerError: return Response.json(error.toResponseObject(), { status: 500 });
|
|
1388
|
-
default: return Response.json(error.toResponseObject(), { status: 400 });
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
/**
|
|
1392
|
-
* Require a valid Bearer token on web-standard requests.
|
|
1393
|
-
*
|
|
1394
|
-
* The framework-free counterpart of `requireBearerAuth` from
|
|
1395
|
-
* `@modelcontextprotocol/express`, for hosts whose HTTP surface is a
|
|
1396
|
-
* `fetch(request)` handler — Cloudflare Workers, Deno, Bun, Hono. The
|
|
1397
|
-
* returned gate resolves to the verified {@link AuthInfo}, or to the
|
|
1398
|
-
* ready-to-return challenge `Response` when the request must be refused.
|
|
1399
|
-
*
|
|
1400
|
-
* @example
|
|
1401
|
-
* ```ts source="./bearerAuth.examples.ts#requireBearerAuth_fetchGate"
|
|
1402
|
-
* const gate = requireBearerAuth({ verifier, requiredScopes: ['mcp'] });
|
|
1403
|
-
*
|
|
1404
|
-
* async function fetchHandler(request: Request): Promise<Response> {
|
|
1405
|
-
* const auth: AuthInfo | Response = await gate(request);
|
|
1406
|
-
* if (auth instanceof Response) return auth;
|
|
1407
|
-
* return handler.fetch(request, { authInfo: auth });
|
|
1408
|
-
* }
|
|
1409
|
-
* ```
|
|
1410
|
-
*/
|
|
1411
|
-
function requireBearerAuth(options) {
|
|
1412
|
-
const { verifier, requiredScopes = [], resourceMetadataUrl } = options;
|
|
1413
|
-
const resolved = {
|
|
1414
|
-
verifier,
|
|
1415
|
-
requiredScopes,
|
|
1416
|
-
resourceMetadataUrl
|
|
1417
|
-
};
|
|
1418
|
-
return async (request) => {
|
|
1419
|
-
const [authorizationHeader] = (request.headers.get("authorization") ?? "").split(",");
|
|
1420
|
-
try {
|
|
1421
|
-
return await verifyBearerToken(authorizationHeader || void 0, resolved);
|
|
1422
|
-
} catch (error) {
|
|
1423
|
-
return bearerAuthChallengeResponse(error, resolved);
|
|
1424
|
-
}
|
|
1425
|
-
};
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
1491
|
//#endregion
|
|
1429
1492
|
//#region src/server/middleware/hostHeaderValidation.ts
|
|
1430
1493
|
/**
|
|
@@ -1495,120 +1558,6 @@ function hostHeaderValidationResponse(req, allowedHostnames) {
|
|
|
1495
1558
|
});
|
|
1496
1559
|
}
|
|
1497
1560
|
|
|
1498
|
-
//#endregion
|
|
1499
|
-
//#region src/server/middleware/oauthMetadata.ts
|
|
1500
|
-
function checkIssuerUrl(issuer, allowInsecure) {
|
|
1501
|
-
if (issuer.protocol !== "https:" && issuer.hostname !== "localhost" && issuer.hostname !== "127.0.0.1" && !allowInsecure) throw new Error("Issuer URL must be HTTPS");
|
|
1502
|
-
if (issuer.hash) throw new Error(`Issuer URL must not have a fragment: ${issuer}`);
|
|
1503
|
-
if (issuer.search) throw new Error(`Issuer URL must not have a query string: ${issuer}`);
|
|
1504
|
-
}
|
|
1505
|
-
/**
|
|
1506
|
-
* Derive the RFC 9728 Protected Resource Metadata document from
|
|
1507
|
-
* {@link AuthMetadataOptions}, validating the Authorization Server issuer URL
|
|
1508
|
-
* (HTTPS required outside localhost) in the process.
|
|
1509
|
-
*
|
|
1510
|
-
* `oauthMetadataResponse` and the Express `mcpAuthMetadataRouter` both build
|
|
1511
|
-
* on this; use it directly when serving the document through your own
|
|
1512
|
-
* routing — or call it once at startup to fail fast on a misconfigured
|
|
1513
|
-
* issuer before any request arrives.
|
|
1514
|
-
*/
|
|
1515
|
-
function buildOAuthProtectedResourceMetadata(options) {
|
|
1516
|
-
checkIssuerUrl(new URL(options.oauthMetadata.issuer), options.dangerouslyAllowInsecureIssuerUrl);
|
|
1517
|
-
return {
|
|
1518
|
-
resource: options.resourceServerUrl.href,
|
|
1519
|
-
authorization_servers: [options.oauthMetadata.issuer],
|
|
1520
|
-
scopes_supported: options.scopesSupported,
|
|
1521
|
-
resource_name: options.resourceName,
|
|
1522
|
-
resource_documentation: options.serviceDocumentationUrl?.href
|
|
1523
|
-
};
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* Builds the RFC 9728 Protected Resource Metadata URL for a given MCP server
|
|
1527
|
-
* URL by inserting `/.well-known/oauth-protected-resource` ahead of the path.
|
|
1528
|
-
*
|
|
1529
|
-
* @example
|
|
1530
|
-
* ```ts
|
|
1531
|
-
* getOAuthProtectedResourceMetadataUrl(new URL('https://api.example.com/mcp'))
|
|
1532
|
-
* // → 'https://api.example.com/.well-known/oauth-protected-resource/mcp'
|
|
1533
|
-
* ```
|
|
1534
|
-
*/
|
|
1535
|
-
function getOAuthProtectedResourceMetadataUrl(serverUrl) {
|
|
1536
|
-
return new URL(protectedResourceMetadataPath(serverUrl), serverUrl).href;
|
|
1537
|
-
}
|
|
1538
|
-
/** The RFC 9728 path-aware well-known path for a resource URL. */
|
|
1539
|
-
function protectedResourceMetadataPath(resourceServerUrl) {
|
|
1540
|
-
const rsPath = stripTrailingSlash(resourceServerUrl.pathname);
|
|
1541
|
-
return `/.well-known/oauth-protected-resource${rsPath === "/" ? "" : rsPath}`;
|
|
1542
|
-
}
|
|
1543
|
-
function stripTrailingSlash(path) {
|
|
1544
|
-
return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
1545
|
-
}
|
|
1546
|
-
const ALLOWED_METHODS = "GET, HEAD, OPTIONS";
|
|
1547
|
-
function metadataDocumentResponse(request, metadata) {
|
|
1548
|
-
if (request.method === "OPTIONS") {
|
|
1549
|
-
const requestedHeaders = request.headers.get("access-control-request-headers");
|
|
1550
|
-
return new Response(null, {
|
|
1551
|
-
status: 204,
|
|
1552
|
-
headers: {
|
|
1553
|
-
"Access-Control-Allow-Origin": "*",
|
|
1554
|
-
"Access-Control-Allow-Methods": ALLOWED_METHODS,
|
|
1555
|
-
...requestedHeaders === null ? {} : {
|
|
1556
|
-
"Access-Control-Allow-Headers": requestedHeaders,
|
|
1557
|
-
Vary: "Access-Control-Request-Headers"
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
1563
|
-
const error = new OAuthError(OAuthErrorCode.MethodNotAllowed, `The method ${request.method} is not allowed for this endpoint`);
|
|
1564
|
-
return Response.json(error.toResponseObject(), {
|
|
1565
|
-
status: 405,
|
|
1566
|
-
headers: {
|
|
1567
|
-
Allow: ALLOWED_METHODS,
|
|
1568
|
-
"Access-Control-Allow-Origin": "*"
|
|
1569
|
-
}
|
|
1570
|
-
});
|
|
1571
|
-
}
|
|
1572
|
-
const response = Response.json(metadata, { headers: { "Access-Control-Allow-Origin": "*" } });
|
|
1573
|
-
return request.method === "HEAD" ? new Response(null, {
|
|
1574
|
-
status: response.status,
|
|
1575
|
-
headers: response.headers
|
|
1576
|
-
}) : response;
|
|
1577
|
-
}
|
|
1578
|
-
/**
|
|
1579
|
-
* Serve the two OAuth discovery documents an MCP server acting as a Resource
|
|
1580
|
-
* Server exposes, from a web-standard `fetch(request)` handler:
|
|
1581
|
-
*
|
|
1582
|
-
* - `/.well-known/oauth-protected-resource[/<path>]` — RFC 9728 Protected
|
|
1583
|
-
* Resource Metadata, derived from the supplied options (path-aware: the
|
|
1584
|
-
* resource URL's path is reflected in the route).
|
|
1585
|
-
* - `/.well-known/oauth-authorization-server` — RFC 8414 Authorization
|
|
1586
|
-
* Server Metadata, passed through verbatim.
|
|
1587
|
-
*
|
|
1588
|
-
* Returns the matched document `Response` (JSON with permissive CORS, `405`
|
|
1589
|
-
* with an `Allow` header for non-GET methods, `204` for CORS preflight), or
|
|
1590
|
-
* `undefined` when the request path is neither well-known route — fall
|
|
1591
|
-
* through to your own routing. The framework-free counterpart of
|
|
1592
|
-
* `mcpAuthMetadataRouter` from `@modelcontextprotocol/express`; pair it with
|
|
1593
|
-
* `requireBearerAuth` and `getOAuthProtectedResourceMetadataUrl` so
|
|
1594
|
-
* unauthenticated clients can discover the AS from the `401` challenge.
|
|
1595
|
-
*
|
|
1596
|
-
* @example
|
|
1597
|
-
* ```ts source="./oauthMetadata.examples.ts#oauthMetadataResponse_fetchHandler"
|
|
1598
|
-
* async function fetchHandler(request: Request): Promise<Response> {
|
|
1599
|
-
* return oauthMetadataResponse(request, options) ?? serveMcp(request);
|
|
1600
|
-
* }
|
|
1601
|
-
* ```
|
|
1602
|
-
*/
|
|
1603
|
-
function oauthMetadataResponse(request, options) {
|
|
1604
|
-
const requestPath = stripTrailingSlash(new URL(request.url).pathname);
|
|
1605
|
-
if (requestPath === protectedResourceMetadataPath(options.resourceServerUrl)) return metadataDocumentResponse(request, buildOAuthProtectedResourceMetadata(options));
|
|
1606
|
-
if (requestPath === "/.well-known/oauth-authorization-server") {
|
|
1607
|
-
buildOAuthProtectedResourceMetadata(options);
|
|
1608
|
-
return metadataDocumentResponse(request, options.oauthMetadata);
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
1561
|
//#endregion
|
|
1613
1562
|
//#region src/server/middleware/originValidation.ts
|
|
1614
1563
|
/**
|
|
@@ -1810,5 +1759,5 @@ function fromJsonSchema(schema, validator) {
|
|
|
1810
1759
|
}
|
|
1811
1760
|
|
|
1812
1761
|
//#endregion
|
|
1813
|
-
export { BAGGAGE_META_KEY, CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, DEFAULT_REQUEST_TIMEOUT_MSEC, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, InMemoryServerEventBus, InMemoryTransport, JSONRPC_VERSION, LATEST_PROTOCOL_VERSION, LOG_LEVEL_META_KEY, METHOD_NOT_FOUND, McpServer, MissingRequiredClientCapabilityError, OAuthError, OAuthErrorCode, PARSE_ERROR, PROTOCOL_VERSION_META_KEY, PerRequestHTTPServerTransport, Protocol, ProtocolError, ProtocolErrorCode, RELATED_TASK_META_KEY, ReadBuffer, ResourceNotFoundError, ResourceTemplate, SERVER_INFO_META_KEY, STDIO_DEFAULT_MAX_BUFFER_SIZE, SUBSCRIPTION_ID_META_KEY, SUPPORTED_PROTOCOL_VERSIONS, SdkError, SdkErrorCode, SdkHttpError, Server, TRACEPARENT_META_KEY, TRACESTATE_META_KEY, UnsupportedProtocolVersionError, UriTemplate, UrlElicitationRequiredError, WebStandardStreamableHTTPServerTransport, acceptedContent, assertCompleteRequestPrompt, assertCompleteRequestResourceTemplate, bearerAuthChallengeResponse, buildOAuthProtectedResourceMetadata, checkResourceAllowed, classifyInboundRequest, completable, createFetchWithInit, createMcpHandler, createRequestStateCodec, deserializeMessage, fromJsonSchema, getDisplayName, getOAuthProtectedResourceMetadataUrl, hostHeaderValidationResponse, inputRequired, inputResponse, isCallToolResult, isCompletable, isInitializeRequest, isInitializedNotification, isInputRequiredResult, isJSONRPCErrorResponse, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResponse, isJSONRPCResultResponse, isJsonContentType, isLegacyRequest, isSpecType, isTaskAugmentedRequestParams, legacyStatelessFallback, localhostAllowedHostnames, localhostAllowedOrigins, mergeCapabilities, oauthMetadataResponse, originValidationResponse, parseJSONRPCMessage, preloadSchemas, requireBearerAuth, resourceUrlFromServerUrl, serializeMessage, specTypeSchemas, validateHostHeader, validateOriginHeader, verifyBearerToken };
|
|
1762
|
+
export { BAGGAGE_META_KEY, CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, DEFAULT_MAX_REQUEST_BODY_SIZE, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, DEFAULT_REQUEST_TIMEOUT_MSEC, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, InMemoryServerEventBus, InMemoryTransport, JSONRPC_VERSION, LATEST_PROTOCOL_VERSION, LOG_LEVEL_META_KEY, METHOD_NOT_FOUND, McpServer, MissingRequiredClientCapabilityError, OAuthError, OAuthErrorCode, PARSE_ERROR, PROTOCOL_VERSION_META_KEY, PerRequestHTTPServerTransport, Protocol, ProtocolError, ProtocolErrorCode, RELATED_TASK_META_KEY, ReadBuffer, ResourceNotFoundError, ResourceTemplate, SERVER_INFO_META_KEY, STDIO_DEFAULT_MAX_BUFFER_SIZE, SUBSCRIPTION_ID_META_KEY, SUPPORTED_PROTOCOL_VERSIONS, SdkError, SdkErrorCode, SdkHttpError, Server, TRACEPARENT_META_KEY, TRACESTATE_META_KEY, UnsupportedProtocolVersionError, UriTemplate, UrlElicitationRequiredError, WebStandardStreamableHTTPServerTransport, acceptedContent, assertCompleteRequestPrompt, assertCompleteRequestResourceTemplate, bearerAuthChallengeResponse, buildOAuthProtectedResourceMetadata, checkResourceAllowed, classifyInboundRequest, completable, createFetchWithInit, createMcpHandler, createRequestStateCodec, deserializeMessage, fromJsonSchema, getDisplayName, getOAuthProtectedResourceMetadataUrl, hostHeaderValidationResponse, inputRequired, inputResponse, isCallToolResult, isCompletable, isInitializeRequest, isInitializedNotification, isInputRequiredResult, isJSONRPCErrorResponse, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResponse, isJSONRPCResultResponse, isJsonContentType, isLegacyRequest, isSpecType, isTaskAugmentedRequestParams, legacyStatelessFallback, localhostAllowedHostnames, localhostAllowedOrigins, mergeCapabilities, oauthMetadataResponse, originValidationResponse, parseJSONRPCMessage, preloadSchemas, readRequestBody, requireBearerAuth, requireScopes, resourceUrlFromServerUrl, serializeMessage, specTypeSchemas, validateHostHeader, validateOriginHeader, verifyBearerToken };
|
|
1814
1763
|
//# sourceMappingURL=index.mjs.map
|