mcp-trellis 1.0.0 → 2.2.0
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 +65 -93
- package/dist/adapters/node-request.d.ts +53 -0
- package/dist/adapters/node-request.d.ts.map +1 -0
- package/dist/adapters/node-request.js +165 -0
- package/dist/adapters/node-request.js.map +1 -0
- package/dist/adapters/node.d.ts +13 -30
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +55 -101
- package/dist/adapters/node.js.map +1 -1
- package/dist/advanced.d.ts +18 -0
- package/dist/advanced.d.ts.map +1 -0
- package/dist/advanced.js +18 -0
- package/dist/advanced.js.map +1 -0
- package/dist/app-options.d.ts +132 -0
- package/dist/app-options.d.ts.map +1 -0
- package/dist/app-options.js +2 -0
- package/dist/app-options.js.map +1 -0
- package/dist/app.d.ts +2 -89
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +143 -63
- package/dist/app.js.map +1 -1
- package/dist/audit.d.ts +5 -5
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +19 -8
- package/dist/audit.js.map +1 -1
- package/dist/auth/bearer.d.ts +4 -1
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +19 -4
- package/dist/auth/bearer.js.map +1 -1
- package/dist/auth/signed-token.d.ts +26 -0
- package/dist/auth/signed-token.d.ts.map +1 -0
- package/dist/auth/signed-token.js +96 -0
- package/dist/auth/signed-token.js.map +1 -0
- package/dist/clients.js +1 -1
- package/dist/clients.js.map +1 -1
- package/dist/deprecated.d.ts +42 -0
- package/dist/deprecated.d.ts.map +1 -0
- package/dist/deprecated.js +35 -0
- package/dist/deprecated.js.map +1 -0
- package/dist/{body.d.ts → http/body.d.ts} +27 -6
- package/dist/http/body.d.ts.map +1 -0
- package/dist/{body.js → http/body.js} +33 -13
- package/dist/http/body.js.map +1 -0
- package/dist/http/http.d.ts +42 -0
- package/dist/http/http.d.ts.map +1 -0
- package/dist/http/http.js +100 -0
- package/dist/http/http.js.map +1 -0
- package/dist/http/origins.d.ts +37 -0
- package/dist/http/origins.d.ts.map +1 -0
- package/dist/http/origins.js +87 -0
- package/dist/http/origins.js.map +1 -0
- package/dist/{dispatch.d.ts → mcp/dispatch.d.ts} +1 -1
- package/dist/mcp/dispatch.d.ts.map +1 -0
- package/dist/mcp/dispatch.js +99 -0
- package/dist/mcp/dispatch.js.map +1 -0
- package/dist/{jsonrpc.d.ts → mcp/jsonrpc.d.ts} +9 -0
- package/dist/mcp/jsonrpc.d.ts.map +1 -0
- package/dist/mcp/jsonrpc.js +88 -0
- package/dist/mcp/jsonrpc.js.map +1 -0
- package/dist/{methods.d.ts → mcp/methods.d.ts} +60 -11
- package/dist/mcp/methods.d.ts.map +1 -0
- package/dist/mcp/methods.js +84 -0
- package/dist/mcp/methods.js.map +1 -0
- package/dist/{protocol.d.ts → mcp/protocol.d.ts} +1 -3
- package/dist/mcp/protocol.d.ts.map +1 -0
- package/dist/{protocol.js → mcp/protocol.js} +1 -3
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/registry.d.ts +58 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +81 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/rpc-handlers.d.ts +11 -0
- package/dist/mcp/rpc-handlers.d.ts.map +1 -0
- package/dist/mcp/rpc-handlers.js +133 -0
- package/dist/mcp/rpc-handlers.js.map +1 -0
- package/dist/{tools.d.ts → mcp/tools.d.ts} +20 -1
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +166 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/{validate.d.ts → mcp/validate.d.ts} +18 -21
- package/dist/mcp/validate.d.ts.map +1 -0
- package/dist/{validate.js → mcp/validate.js} +59 -41
- package/dist/mcp/validate.js.map +1 -0
- package/dist/oauth/audit.d.ts +20 -0
- package/dist/oauth/audit.d.ts.map +1 -0
- package/dist/oauth/audit.js +29 -0
- package/dist/oauth/audit.js.map +1 -0
- package/dist/oauth/cimd/cimd-parse.d.ts +4 -0
- package/dist/oauth/cimd/cimd-parse.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd-parse.js +70 -0
- package/dist/oauth/cimd/cimd-parse.js.map +1 -0
- package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -0
- package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd-ssrf.js +193 -0
- package/dist/oauth/cimd/cimd-ssrf.js.map +1 -0
- package/dist/oauth/cimd/cimd.d.ts +36 -0
- package/dist/oauth/cimd/cimd.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd.js +132 -0
- package/dist/oauth/cimd/cimd.js.map +1 -0
- package/dist/oauth/cimd/types.d.ts +12 -0
- package/dist/oauth/cimd/types.d.ts.map +1 -0
- package/dist/oauth/cimd/types.js +3 -0
- package/dist/oauth/cimd/types.js.map +1 -0
- package/dist/oauth/config.d.ts +51 -0
- package/dist/oauth/config.d.ts.map +1 -0
- package/dist/oauth/config.js +108 -0
- package/dist/oauth/config.js.map +1 -0
- package/dist/oauth/constants.d.ts +11 -0
- package/dist/oauth/constants.d.ts.map +1 -1
- package/dist/oauth/constants.js +11 -0
- package/dist/oauth/constants.js.map +1 -1
- package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +0 -1
- package/dist/oauth/crypto/base64url.d.ts.map +1 -0
- package/dist/oauth/{base64url.js → crypto/base64url.js} +0 -1
- package/dist/oauth/crypto/base64url.js.map +1 -0
- package/dist/oauth/crypto/clientauth.d.ts +13 -0
- package/dist/oauth/crypto/clientauth.d.ts.map +1 -0
- package/dist/oauth/crypto/clientauth.js +195 -0
- package/dist/oauth/crypto/clientauth.js.map +1 -0
- package/dist/oauth/crypto/codes.d.ts +65 -0
- package/dist/oauth/crypto/codes.d.ts.map +1 -0
- package/dist/oauth/crypto/codes.js +121 -0
- package/dist/oauth/crypto/codes.js.map +1 -0
- package/dist/oauth/crypto/hkdf.d.ts +13 -0
- package/dist/oauth/crypto/hkdf.d.ts.map +1 -0
- package/dist/oauth/crypto/hkdf.js +30 -0
- package/dist/oauth/crypto/hkdf.js.map +1 -0
- package/dist/oauth/crypto/keycache.d.ts +19 -0
- package/dist/oauth/crypto/keycache.d.ts.map +1 -0
- package/dist/oauth/crypto/keycache.js +44 -0
- package/dist/oauth/crypto/keycache.js.map +1 -0
- package/dist/oauth/crypto/pkce.d.ts.map +1 -0
- package/dist/oauth/{pkce.js → crypto/pkce.js} +1 -1
- package/dist/oauth/crypto/pkce.js.map +1 -0
- package/dist/oauth/{sealed.d.ts → crypto/sealed.d.ts} +12 -3
- package/dist/oauth/crypto/sealed.d.ts.map +1 -0
- package/dist/oauth/{sealed.js → crypto/sealed.js} +22 -14
- package/dist/oauth/crypto/sealed.js.map +1 -0
- package/dist/oauth/{secrethash.d.ts → crypto/secrethash.d.ts} +14 -0
- package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
- package/dist/oauth/{secrethash.js → crypto/secrethash.js} +27 -11
- package/dist/oauth/crypto/secrethash.js.map +1 -0
- package/dist/oauth/deprecated.d.ts +52 -0
- package/dist/oauth/deprecated.d.ts.map +1 -0
- package/dist/oauth/deprecated.js +49 -0
- package/dist/oauth/deprecated.js.map +1 -0
- package/dist/oauth/endpoints/authorize.d.ts +3 -0
- package/dist/oauth/endpoints/authorize.d.ts.map +1 -0
- package/dist/oauth/endpoints/authorize.js +209 -0
- package/dist/oauth/endpoints/authorize.js.map +1 -0
- package/dist/oauth/endpoints/callback.d.ts +20 -0
- package/dist/oauth/endpoints/callback.d.ts.map +1 -0
- package/dist/oauth/endpoints/callback.js +31 -0
- package/dist/oauth/endpoints/callback.js.map +1 -0
- package/dist/oauth/endpoints/consent-page.d.ts +5 -0
- package/dist/oauth/endpoints/consent-page.d.ts.map +1 -0
- package/dist/oauth/endpoints/consent-page.js +63 -0
- package/dist/oauth/endpoints/consent-page.js.map +1 -0
- package/dist/oauth/endpoints/consent.d.ts +29 -0
- package/dist/oauth/endpoints/consent.d.ts.map +1 -0
- package/dist/oauth/endpoints/consent.js +98 -0
- package/dist/oauth/endpoints/consent.js.map +1 -0
- package/dist/oauth/{metadata.d.ts → endpoints/metadata.d.ts} +8 -2
- package/dist/oauth/endpoints/metadata.d.ts.map +1 -0
- package/dist/oauth/{metadata.js → endpoints/metadata.js} +10 -4
- package/dist/oauth/endpoints/metadata.js.map +1 -0
- package/dist/oauth/endpoints/register.d.ts +10 -0
- package/dist/oauth/endpoints/register.d.ts.map +1 -0
- package/dist/oauth/endpoints/register.js +96 -0
- package/dist/oauth/endpoints/register.js.map +1 -0
- package/dist/oauth/{revoke.d.ts → endpoints/revoke.d.ts} +1 -1
- package/dist/oauth/endpoints/revoke.d.ts.map +1 -0
- package/dist/oauth/{revoke.js → endpoints/revoke.js} +8 -18
- package/dist/oauth/endpoints/revoke.js.map +1 -0
- package/dist/oauth/endpoints/token.d.ts +3 -0
- package/dist/oauth/endpoints/token.d.ts.map +1 -0
- package/dist/oauth/{token.js → endpoints/token.js} +32 -25
- package/dist/oauth/endpoints/token.js.map +1 -0
- package/dist/oauth/{wellknown.d.ts → endpoints/wellknown.d.ts} +1 -1
- package/dist/oauth/endpoints/wellknown.d.ts.map +1 -0
- package/dist/oauth/{wellknown.js → endpoints/wellknown.js} +7 -9
- package/dist/oauth/endpoints/wellknown.js.map +1 -0
- package/dist/oauth/policy/redirect.d.ts.map +1 -0
- package/dist/oauth/policy/redirect.js.map +1 -0
- package/dist/oauth/policy/reqbody.d.ts +18 -0
- package/dist/oauth/policy/reqbody.d.ts.map +1 -0
- package/dist/oauth/policy/reqbody.js +68 -0
- package/dist/oauth/policy/reqbody.js.map +1 -0
- package/dist/oauth/{resource.d.ts → policy/resource.d.ts} +1 -1
- package/dist/oauth/policy/resource.d.ts.map +1 -0
- package/dist/oauth/{resource.js → policy/resource.js} +2 -2
- package/dist/oauth/policy/resource.js.map +1 -0
- package/dist/oauth/{scope.d.ts → policy/scope.d.ts} +2 -2
- package/dist/oauth/policy/scope.d.ts.map +1 -0
- package/dist/oauth/{scope.js → policy/scope.js} +3 -3
- package/dist/oauth/policy/scope.js.map +1 -0
- package/dist/oauth/router.d.ts +10 -13
- package/dist/oauth/router.d.ts.map +1 -1
- package/dist/oauth/router.js +93 -115
- package/dist/oauth/router.js.map +1 -1
- package/dist/oauth/types.d.ts +99 -45
- package/dist/oauth/types.d.ts.map +1 -1
- package/dist/oauth/types.js +1 -90
- package/dist/oauth/types.js.map +1 -1
- package/dist/server.d.ts +13 -12
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -8
- package/dist/server.js.map +1 -1
- package/dist/util/defined.d.ts +11 -0
- package/dist/util/defined.d.ts.map +1 -0
- package/dist/util/defined.js +15 -0
- package/dist/util/defined.js.map +1 -0
- package/dist/util/json.d.ts +3 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +3 -0
- package/dist/util/json.js.map +1 -0
- package/docs/ROADMAP.md +37 -47
- package/docs/compatibility.md +86 -0
- package/docs/diagrams/demo-tool-result.png +0 -0
- package/docs/diagrams/mcp-trellis-architecture.html +305 -0
- package/docs/guide.md +46 -16
- package/docs/reference.md +55 -22
- package/docs/security.md +58 -9
- package/docs/troubleshooting.md +19 -0
- package/package.json +27 -10
- package/dist/adapters/origins.d.ts +0 -19
- package/dist/adapters/origins.d.ts.map +0 -1
- package/dist/adapters/origins.js +0 -52
- package/dist/adapters/origins.js.map +0 -1
- package/dist/body.d.ts.map +0 -1
- package/dist/body.js.map +0 -1
- package/dist/dispatch.d.ts.map +0 -1
- package/dist/dispatch.js +0 -119
- package/dist/dispatch.js.map +0 -1
- package/dist/http.d.ts +0 -17
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -38
- package/dist/http.js.map +0 -1
- package/dist/jsonrpc.d.ts.map +0 -1
- package/dist/jsonrpc.js +0 -18
- package/dist/jsonrpc.js.map +0 -1
- package/dist/methods.d.ts.map +0 -1
- package/dist/methods.js +0 -130
- package/dist/methods.js.map +0 -1
- package/dist/oauth/authorize.d.ts +0 -3
- package/dist/oauth/authorize.d.ts.map +0 -1
- package/dist/oauth/authorize.js +0 -141
- package/dist/oauth/authorize.js.map +0 -1
- package/dist/oauth/base64url.d.ts.map +0 -1
- package/dist/oauth/base64url.js.map +0 -1
- package/dist/oauth/clientauth.d.ts +0 -24
- package/dist/oauth/clientauth.d.ts.map +0 -1
- package/dist/oauth/clientauth.js +0 -119
- package/dist/oauth/clientauth.js.map +0 -1
- package/dist/oauth/codes.d.ts +0 -30
- package/dist/oauth/codes.d.ts.map +0 -1
- package/dist/oauth/codes.js +0 -85
- package/dist/oauth/codes.js.map +0 -1
- package/dist/oauth/consent.d.ts +0 -65
- package/dist/oauth/consent.d.ts.map +0 -1
- package/dist/oauth/consent.js +0 -173
- package/dist/oauth/consent.js.map +0 -1
- package/dist/oauth/metadata.d.ts.map +0 -1
- package/dist/oauth/metadata.js.map +0 -1
- package/dist/oauth/pkce.d.ts.map +0 -1
- package/dist/oauth/pkce.js.map +0 -1
- package/dist/oauth/redirect.d.ts.map +0 -1
- package/dist/oauth/redirect.js.map +0 -1
- package/dist/oauth/reqbody.d.ts +0 -10
- package/dist/oauth/reqbody.d.ts.map +0 -1
- package/dist/oauth/reqbody.js +0 -29
- package/dist/oauth/reqbody.js.map +0 -1
- package/dist/oauth/resource.d.ts.map +0 -1
- package/dist/oauth/resource.js.map +0 -1
- package/dist/oauth/revoke.d.ts.map +0 -1
- package/dist/oauth/revoke.js.map +0 -1
- package/dist/oauth/scope.d.ts.map +0 -1
- package/dist/oauth/scope.js.map +0 -1
- package/dist/oauth/sealed.d.ts.map +0 -1
- package/dist/oauth/sealed.js.map +0 -1
- package/dist/oauth/secrethash.d.ts.map +0 -1
- package/dist/oauth/secrethash.js.map +0 -1
- package/dist/oauth/token.d.ts +0 -3
- package/dist/oauth/token.d.ts.map +0 -1
- package/dist/oauth/token.js.map +0 -1
- package/dist/oauth/wellknown.d.ts.map +0 -1
- package/dist/oauth/wellknown.js.map +0 -1
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js.map +0 -1
- package/dist/registry.d.ts +0 -37
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js +0 -77
- package/dist/registry.js.map +0 -1
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -98
- package/dist/tools.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- /package/dist/oauth/{pkce.d.ts → crypto/pkce.d.ts} +0 -0
- /package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +0 -0
- /package/dist/oauth/{redirect.js → policy/redirect.js} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root-entry aliases for symbols that moved to `mcp-trellis/advanced`.
|
|
3
|
+
* Declared as consts (not `export { } from`) so `@deprecated` reaches
|
|
4
|
+
* editors. Removed in 3.0.
|
|
5
|
+
*/
|
|
6
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
7
|
+
export declare const timingSafeEqual: (a: string, b: string) => boolean;
|
|
8
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
9
|
+
export declare const matchesAny: (presented: string, accepted: string[]) => boolean;
|
|
10
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
11
|
+
export declare const corsHeaders: (extra?: Record<string, string>) => Record<string, string>;
|
|
12
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
13
|
+
export declare const emptyResponse: (status: number) => Response;
|
|
14
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
15
|
+
export declare const methodNotAllowed: (message?: string, options?: import("./server.js").JsonResponseOptions) => Response;
|
|
16
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
17
|
+
export declare const optionsResponse: (options?: import("./server.js").JsonResponseOptions) => Response;
|
|
18
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
19
|
+
export declare const pickProtocolVersion: (params?: Record<string, unknown>) => string;
|
|
20
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
21
|
+
export declare const ASSUMED_HEADER_PROTOCOL_VERSION = "2025-03-26";
|
|
22
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
23
|
+
export declare const validateAgainstSchema: (value: unknown, schema: import("./server.js").JsonSchema) => string[];
|
|
24
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
25
|
+
export declare const unsupportedKeywords: (schema: import("./server.js").JsonSchema, path?: string) => string[];
|
|
26
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
27
|
+
export declare const missingObjectType: (schema: import("./server.js").JsonSchema, path?: string) => string[];
|
|
28
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
29
|
+
export declare const JSON_SCHEMA_TYPES: {
|
|
30
|
+
readonly string: "string";
|
|
31
|
+
readonly number: "number";
|
|
32
|
+
readonly integer: "integer";
|
|
33
|
+
readonly boolean: "boolean";
|
|
34
|
+
readonly object: "object";
|
|
35
|
+
readonly array: "array";
|
|
36
|
+
readonly null: "null";
|
|
37
|
+
};
|
|
38
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
39
|
+
export declare const SUPPORTED_SCHEMA_KEYWORDS: ReadonlySet<string>;
|
|
40
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
41
|
+
export declare const IGNORED_SCHEMA_KEYWORDS: ReadonlySet<string>;
|
|
42
|
+
//# sourceMappingURL=deprecated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecated.d.ts","sourceRoot":"","sources":["../src/deprecated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,sEAAsE;AACtE,eAAO,MAAM,eAAe,mCAA2B,CAAC;AACxD,sEAAsE;AACtE,eAAO,MAAM,UAAU,oDAAsB,CAAC;AAC9C,sEAAsE;AACtE,eAAO,MAAM,WAAW,4DAAuB,CAAC;AAChD,sEAAsE;AACtE,eAAO,MAAM,aAAa,8BAAyB,CAAC;AACpD,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,qFAA4B,CAAC;AAC1D,sEAAsE;AACtE,eAAO,MAAM,eAAe,mEAA2B,CAAC;AACxD,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,8CAA+B,CAAC;AAChE,sEAAsE;AACtE,eAAO,MAAM,+BAA+B,eAA2C,CAAC;AACxF,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,wEAAiC,CAAC;AACpE,sEAAsE;AACtE,eAAO,MAAM,mBAAmB,uEAA+B,CAAC;AAChE,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,uEAA6B,CAAC;AAC5D,sEAAsE;AACtE,eAAO,MAAM,iBAAiB;;;;;;;;CAA6B,CAAC;AAC5D,sEAAsE;AACtE,eAAO,MAAM,yBAAyB,qBAAqC,CAAC;AAC5E,sEAAsE;AACtE,eAAO,MAAM,uBAAuB,qBAAmC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root-entry aliases for symbols that moved to `mcp-trellis/advanced`.
|
|
3
|
+
* Declared as consts (not `export { } from`) so `@deprecated` reaches
|
|
4
|
+
* editors. Removed in 3.0.
|
|
5
|
+
*/
|
|
6
|
+
import * as advanced from "./advanced.js";
|
|
7
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
8
|
+
export const timingSafeEqual = advanced.timingSafeEqual;
|
|
9
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
10
|
+
export const matchesAny = advanced.matchesAny;
|
|
11
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
12
|
+
export const corsHeaders = advanced.corsHeaders;
|
|
13
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
14
|
+
export const emptyResponse = advanced.emptyResponse;
|
|
15
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
16
|
+
export const methodNotAllowed = advanced.methodNotAllowed;
|
|
17
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
18
|
+
export const optionsResponse = advanced.optionsResponse;
|
|
19
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
20
|
+
export const pickProtocolVersion = advanced.pickProtocolVersion;
|
|
21
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
22
|
+
export const ASSUMED_HEADER_PROTOCOL_VERSION = advanced.ASSUMED_HEADER_PROTOCOL_VERSION;
|
|
23
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
24
|
+
export const validateAgainstSchema = advanced.validateAgainstSchema;
|
|
25
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
26
|
+
export const unsupportedKeywords = advanced.unsupportedKeywords;
|
|
27
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
28
|
+
export const missingObjectType = advanced.missingObjectType;
|
|
29
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
30
|
+
export const JSON_SCHEMA_TYPES = advanced.JSON_SCHEMA_TYPES;
|
|
31
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
32
|
+
export const SUPPORTED_SCHEMA_KEYWORDS = advanced.SUPPORTED_SCHEMA_KEYWORDS;
|
|
33
|
+
/** @deprecated Import from "mcp-trellis/advanced". Removed in 3.0. */
|
|
34
|
+
export const IGNORED_SCHEMA_KEYWORDS = advanced.IGNORED_SCHEMA_KEYWORDS;
|
|
35
|
+
//# sourceMappingURL=deprecated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecated.js","sourceRoot":"","sources":["../src/deprecated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAE1C,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AACxD,sEAAsE;AACtE,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;AAC9C,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;AAChD,sEAAsE;AACtE,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;AACpD,sEAAsE;AACtE,MAAM,CAAC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,gBAAgB,CAAC;AAC1D,sEAAsE;AACtE,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AACxD,sEAAsE;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,mBAAmB,CAAC;AAChE,sEAAsE;AACtE,MAAM,CAAC,MAAM,+BAA+B,GAAG,QAAQ,CAAC,+BAA+B,CAAC;AACxF,sEAAsE;AACtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC,qBAAqB,CAAC;AACpE,sEAAsE;AACtE,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,mBAAmB,CAAC;AAChE,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC;AAC5D,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC,iBAAiB,CAAC;AAC5D,sEAAsE;AACtE,MAAM,CAAC,MAAM,yBAAyB,GAAG,QAAQ,CAAC,yBAAyB,CAAC;AAC5E,sEAAsE;AACtE,MAAM,CAAC,MAAM,uBAAuB,GAAG,QAAQ,CAAC,uBAAuB,CAAC"}
|
|
@@ -13,13 +13,34 @@ export declare const DEFAULT_MCP_BODY_LIMIT = 1048576;
|
|
|
13
13
|
/** Default cap for OAuth endpoints (`/token`, `/revoke`, `/register`, `/consent`) — tiny form/JSON. */
|
|
14
14
|
export declare const DEFAULT_OAUTH_BODY_LIMIT = 65536;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* (some runtimes don't expose a streaming body), still enforcing the cap.
|
|
16
|
+
* Structural subset of `Request`/`Response` that `readBoundedBytes` actually
|
|
17
|
+
* needs — both are real `Request`s (every caller inside this package) and,
|
|
18
|
+
* via `tools.ts`'s `apiTool`, a `Response` from an upstream `fetch` call
|
|
19
|
+
* capping how much of that body a tool ever buffers into memory.
|
|
21
20
|
*/
|
|
22
|
-
export
|
|
21
|
+
export type BoundedBodySource = {
|
|
22
|
+
headers: {
|
|
23
|
+
get: (name: string) => string | null;
|
|
24
|
+
};
|
|
25
|
+
body: ReadableStream<Uint8Array> | null;
|
|
26
|
+
text: () => Promise<string>;
|
|
27
|
+
/** Preferred over `text` for the no-stream fallback — keeps binary bodies intact. */
|
|
28
|
+
arrayBuffer?: () => Promise<ArrayBuffer>;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Reads a bounded body as raw bytes, rejecting early on an honest oversized
|
|
32
|
+
* `Content-Length`, then aborting mid-stream — before the whole body is
|
|
33
|
+
* buffered — if a chunked body (no `Content-Length`) exceeds `maxBytes`.
|
|
34
|
+
* Falls back to `source.arrayBuffer()` (or `source.text()`) when
|
|
35
|
+
* `source.body` is null (some runtimes don't expose a streaming body),
|
|
36
|
+
* still enforcing the cap.
|
|
37
|
+
*
|
|
38
|
+
* Optional `signal` cancels the active reader on abort — needed when a hop
|
|
39
|
+
* timeout must stop a stalled stream (a locked `body.cancel()` is a no-op).
|
|
40
|
+
*/
|
|
41
|
+
export declare const readBoundedBytes: (source: BoundedBodySource, maxBytes: number, signal?: AbortSignal) => Promise<Uint8Array>;
|
|
42
|
+
/** `readBoundedBytes`, decoded as UTF-8 — for JSON / form / text bodies. */
|
|
43
|
+
export declare const readBoundedText: (source: BoundedBodySource, maxBytes: number, signal?: AbortSignal) => Promise<string>;
|
|
23
44
|
export type BoundedNodeBodyOptions = {
|
|
24
45
|
/** Declared `Content-Length` header value, checked before reading anything. */
|
|
25
46
|
contentLength?: string | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.d.ts","sourceRoot":"","sources":["../../src/http/body.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qBAAa,iBAAkB,SAAQ,KAAK;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAItC;AAED,mGAAmG;AACnG,eAAO,MAAM,sBAAsB,UAAY,CAAC;AAEhD,uGAAuG;AACvG,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAuB/C;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE;QAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAClD,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IACxC,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5B,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;CAC1C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,iBAAiB,EACzB,UAAU,MAAM,EAChB,SAAS,WAAW,KACnB,OAAO,CAAC,UAAU,CA6CpB,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,eAAe,GAC1B,QAAQ,iBAAiB,EACzB,UAAU,MAAM,EAChB,SAAS,WAAW,KACnB,OAAO,CAAC,MAAM,CAA+E,CAAC;AAEjG,MAAM,MAAM,sBAAsB,GAAG;IACnC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IACxE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC7C,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;IACzD,KAAK,IAAI,IAAI,CAAC;IACd,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;CAC7E,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,gBAG6B,CAAC;AA+DvF;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,gBAAgB,GAAG,aAAa,CAAC,UAAU,GAAG,MAAM,CAAC,EAC7D,UAAU,MAAM,EAChB,UAAS,sBAA2B,KACnC,OAAO,CAAC,UAAU,CAQpB,CAAC"}
|
|
@@ -33,30 +33,47 @@ const concatBytes = (chunks, total) => {
|
|
|
33
33
|
};
|
|
34
34
|
const toBytes = (chunk) => typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk;
|
|
35
35
|
/**
|
|
36
|
-
* Reads a
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* (some runtimes don't expose a streaming body),
|
|
36
|
+
* Reads a bounded body as raw bytes, rejecting early on an honest oversized
|
|
37
|
+
* `Content-Length`, then aborting mid-stream — before the whole body is
|
|
38
|
+
* buffered — if a chunked body (no `Content-Length`) exceeds `maxBytes`.
|
|
39
|
+
* Falls back to `source.arrayBuffer()` (or `source.text()`) when
|
|
40
|
+
* `source.body` is null (some runtimes don't expose a streaming body),
|
|
41
|
+
* still enforcing the cap.
|
|
42
|
+
*
|
|
43
|
+
* Optional `signal` cancels the active reader on abort — needed when a hop
|
|
44
|
+
* timeout must stop a stalled stream (a locked `body.cancel()` is a no-op).
|
|
41
45
|
*/
|
|
42
|
-
export const
|
|
43
|
-
|
|
46
|
+
export const readBoundedBytes = async (source, maxBytes, signal) => {
|
|
47
|
+
if (signal?.aborted)
|
|
48
|
+
throw new Error("body read aborted");
|
|
49
|
+
const declared = declaredContentLength(source.headers);
|
|
44
50
|
if (declared !== null && declared > maxBytes) {
|
|
45
51
|
throw new BodyTooLargeError(maxBytes);
|
|
46
52
|
}
|
|
47
|
-
const reader =
|
|
53
|
+
const reader = source.body?.getReader();
|
|
48
54
|
if (!reader) {
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
// ponytail: no stream → arrayBuffer/text ignore signal; fine for request
|
|
56
|
+
// bodies. CIMD always has a stream on Node/undici. Upgrade: race abort
|
|
57
|
+
// or refuse the fallback when signal is required.
|
|
58
|
+
const bytes = source.arrayBuffer
|
|
59
|
+
? new Uint8Array(await source.arrayBuffer())
|
|
60
|
+
: new TextEncoder().encode(await source.text());
|
|
61
|
+
if (bytes.byteLength > maxBytes)
|
|
51
62
|
throw new BodyTooLargeError(maxBytes);
|
|
52
|
-
|
|
53
|
-
return text;
|
|
63
|
+
return bytes;
|
|
54
64
|
}
|
|
65
|
+
const onAbort = () => {
|
|
66
|
+
void reader.cancel().catch(() => { });
|
|
67
|
+
};
|
|
68
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
55
69
|
const chunks = [];
|
|
56
70
|
let total = 0;
|
|
57
71
|
try {
|
|
58
72
|
for (;;) {
|
|
59
73
|
const { done, value } = await reader.read();
|
|
74
|
+
// Cancel may settle read() as done — still treat abort as failure.
|
|
75
|
+
if (signal?.aborted)
|
|
76
|
+
throw new Error("body read aborted");
|
|
60
77
|
if (done)
|
|
61
78
|
break;
|
|
62
79
|
// ponytail: one oversized chunk can briefly allocate maxBytes + highWaterMark
|
|
@@ -68,10 +85,13 @@ export const readBoundedText = async (request, maxBytes) => {
|
|
|
68
85
|
}
|
|
69
86
|
}
|
|
70
87
|
finally {
|
|
88
|
+
signal?.removeEventListener("abort", onAbort);
|
|
71
89
|
await reader.cancel().catch(() => { });
|
|
72
90
|
}
|
|
73
|
-
return
|
|
91
|
+
return concatBytes(chunks, total);
|
|
74
92
|
};
|
|
93
|
+
/** `readBoundedBytes`, decoded as UTF-8 — for JSON / form / text bodies. */
|
|
94
|
+
export const readBoundedText = async (source, maxBytes, signal) => new TextDecoder().decode(await readBoundedBytes(source, maxBytes, signal));
|
|
75
95
|
export const isNodeReadable = (value) => typeof value?.on === "function" &&
|
|
76
96
|
typeof value?.pause === "function" &&
|
|
77
97
|
typeof value?.removeListener === "function";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body.js","sourceRoot":"","sources":["../../src/http/body.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACrB;IAArB,YAAqB,QAAgB;QACnC,KAAK,CAAC,wBAAwB,QAAQ,QAAQ,CAAC,CAAC;QAD7B,aAAQ,GAAR,QAAQ,CAAQ;QAEnC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,mGAAmG;AACnG,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAC,CAAC,QAAQ;AAEzD,uGAAuG;AACvG,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,CAAC,SAAS;AAEzD,MAAM,qBAAqB,GAAG,CAAC,OAE9B,EAAiB,EAAE;IAClB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,MAAoB,EAAE,KAAa,EAAc,EAAE;IACtE,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAC9B,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAC5B,OAAO,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC;IACnC,CAAC,EAAE,CAAC,CAAC,CAAC;IACN,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,KAA0B,EAAc,EAAE,CACzD,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAgBtE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EACnC,MAAyB,EACzB,QAAgB,EAChB,MAAoB,EACC,EAAE;IACvB,IAAI,MAAM,EAAE,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAE1D,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;QAC7C,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,yEAAyE;QACzE,uEAAuE;QACvE,kDAAkD;QAClD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW;YAC9B,CAAC,CAAC,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC;YAC5C,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,IAAI,KAAK,CAAC,UAAU,GAAG,QAAQ;YAAE,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACvE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC;IACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3D,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,CAAC;QACH,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,mEAAmE;YACnE,IAAI,MAAM,EAAE,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC1D,IAAI,IAAI;gBAAE,MAAM;YAChB,8EAA8E;YAC9E,kEAAkE;YAClE,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;YAC1B,IAAI,KAAK,GAAG,QAAQ;gBAAE,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAC5D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9C,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAClC,MAAyB,EACzB,QAAgB,EAChB,MAAoB,EACH,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,gBAAgB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAqBjG,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAc,EAA6B,EAAE,CAC1E,OAAQ,KAAiC,EAAE,EAAE,KAAK,UAAU;IAC5D,OAAQ,KAAoC,EAAE,KAAK,KAAK,UAAU;IAClE,OAAQ,KAA6C,EAAE,cAAc,KAAK,UAAU,CAAC;AAEvF,MAAM,cAAc,GAAG,CAAC,MAAwB,EAAE,QAAgB,EAAuB,EAAE,CACzF,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,MAAM,SAAS,GAAiB,EAAE,CAAC;IACnC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,MAAM,GAAG,CAAC,KAA0B,EAAQ,EAAE;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7B,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;QAC1B,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IACzC,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,GAAU,EAAQ,EAAE;QACnC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAS,EAAE;QACzB,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAsC,CAAC,CAAC;QACtE,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,KAAqC,CAAC,CAAC;QACpE,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,OAAuC,CAAC,CAAC;IAC1E,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,GAAU,EAAQ,EAAE;QAChC,IAAI,OAAO;YAAE,OAAO;QACpB,OAAO,GAAG,IAAI,CAAC;QACf,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,CAAC;QACV,MAAM,CAAC,GAAG,CAAC,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACxB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEL,MAAM,qBAAqB,GAAG,KAAK,EACjC,MAA0C,EAC1C,QAAgB,EACK,EAAE;IACvB,MAAM,SAAS,GAAiB,EAAE,CAAC;IACnC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7B,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;QAC1B,IAAI,KAAK,GAAG,QAAQ;YAAE,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAC5D,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,WAAW,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,MAA6D,EAC7D,QAAgB,EAChB,UAAkC,EAAE,EACf,EAAE;IACvB,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7E,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;QACrD,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,CAAC;QAAE,OAAO,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACpE,OAAO,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** JSON `error` body / audit reason when a host port throws. */
|
|
2
|
+
export declare const INTERNAL_ERROR = "internal_error";
|
|
3
|
+
/** A caught value's audit-safe message — an `Error`'s own message, else its `String()` form. */
|
|
4
|
+
export declare const errorReason: (caught: unknown) => string;
|
|
5
|
+
/** Default cap on how long a response waits for an audit sink. */
|
|
6
|
+
export declare const DEFAULT_AUDIT_TIMEOUT_MS = 1000;
|
|
7
|
+
/**
|
|
8
|
+
* Run an audit sink so it can neither fail nor stall the response: errors
|
|
9
|
+
* are swallowed, and the wait is capped at `timeoutMs`. The timer is cleared
|
|
10
|
+
* as soon as the sink settles — a fast sink must not leave a pending timer
|
|
11
|
+
* holding the process (or a serverless invocation) open. Shared by the MCP
|
|
12
|
+
* `safeAudit` and OAuth `safeOAuthAudit`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const runAuditBounded: (sink: () => void | Promise<void>, timeoutMs: number) => Promise<void>;
|
|
15
|
+
export declare const corsHeaders: (extra?: Record<string, string>) => Record<string, string>;
|
|
16
|
+
export type JsonResponseOptions = {
|
|
17
|
+
/** Include permissive CORS headers. Default true (MCP). OAuth token set false. */
|
|
18
|
+
cors?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** Single-object JSON response — keeps the call site under the ≤3-params rule. */
|
|
21
|
+
export type JsonResponseInput = {
|
|
22
|
+
data: unknown;
|
|
23
|
+
status?: number;
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
cors?: boolean;
|
|
26
|
+
};
|
|
27
|
+
export declare const jsonResponse: (input: JsonResponseInput) => Response;
|
|
28
|
+
export declare const emptyResponse: (status: number) => Response;
|
|
29
|
+
export declare const optionsResponse: (options?: JsonResponseOptions) => Response;
|
|
30
|
+
/** Reflect a validated browser Origin on MCP CORS instead of `*`. */
|
|
31
|
+
export declare const withReflectedCorsOrigin: (response: Response, origin: string) => Response;
|
|
32
|
+
export declare const methodNotAllowed: (message?: string, options?: JsonResponseOptions) => Response;
|
|
33
|
+
export declare const GET_ONLY: ReadonlySet<string>;
|
|
34
|
+
export declare const POST_ONLY: ReadonlySet<string>;
|
|
35
|
+
/** For endpoints that must not send permissive CORS headers (`/token`, `/revoke`, `/consent`). */
|
|
36
|
+
export declare const NO_CORS: JsonResponseOptions;
|
|
37
|
+
/**
|
|
38
|
+
* OPTIONS → 204; allowed verb → null (continue); otherwise → 405.
|
|
39
|
+
* Shared by OAuth handlers that all need the same method gate.
|
|
40
|
+
*/
|
|
41
|
+
export declare const requireHttpMethod: (request: Request, allowed: ReadonlySet<string>, options?: JsonResponseOptions) => Response | null;
|
|
42
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http/http.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,gGAAgG;AAChG,eAAO,MAAM,WAAW,GAAI,QAAQ,OAAO,KAAG,MACa,CAAC;AAE5D,kEAAkE;AAClE,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAChC,WAAW,MAAM,KAChB,OAAO,CAAC,IAAI,CAiBd,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAUhF,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG;IAChC,kFAAkF;IAClF,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,kFAAkF;AAClF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,OAAO,iBAAiB,KAAG,QAWvD,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,QAI3C,CAAC;AAEL,eAAO,MAAM,eAAe,GAAI,UAAU,mBAAmB,KAAG,QAO5D,CAAC;AAEL,qEAAqE;AACrE,eAAO,MAAM,uBAAuB,GAAI,UAAU,QAAQ,EAAE,QAAQ,MAAM,KAAG,QAU5E,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,gBAA8B,EAC9B,UAAU,mBAAmB,KAC5B,QAKC,CAAC;AAEL,eAAO,MAAM,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAoB,CAAC;AAC9D,eAAO,MAAM,SAAS,EAAE,WAAW,CAAC,MAAM,CAAqB,CAAC;AAChE,kGAAkG;AAClG,eAAO,MAAM,OAAO,EAAE,mBAAqC,CAAC;AAE5D;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,OAAO,EAChB,SAAS,WAAW,CAAC,MAAM,CAAC,EAC5B,UAAU,mBAAmB,KAC5B,QAAQ,GAAG,IAIb,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** JSON `error` body / audit reason when a host port throws. */
|
|
2
|
+
export const INTERNAL_ERROR = "internal_error";
|
|
3
|
+
/** A caught value's audit-safe message — an `Error`'s own message, else its `String()` form. */
|
|
4
|
+
export const errorReason = (caught) => caught instanceof Error ? caught.message : String(caught);
|
|
5
|
+
/** Default cap on how long a response waits for an audit sink. */
|
|
6
|
+
export const DEFAULT_AUDIT_TIMEOUT_MS = 1000;
|
|
7
|
+
/**
|
|
8
|
+
* Run an audit sink so it can neither fail nor stall the response: errors
|
|
9
|
+
* are swallowed, and the wait is capped at `timeoutMs`. The timer is cleared
|
|
10
|
+
* as soon as the sink settles — a fast sink must not leave a pending timer
|
|
11
|
+
* holding the process (or a serverless invocation) open. Shared by the MCP
|
|
12
|
+
* `safeAudit` and OAuth `safeOAuthAudit`.
|
|
13
|
+
*/
|
|
14
|
+
export const runAuditBounded = async (sink, timeoutMs) => {
|
|
15
|
+
const settled = (async () => {
|
|
16
|
+
try {
|
|
17
|
+
await sink();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// Intentionally ignored.
|
|
21
|
+
}
|
|
22
|
+
})();
|
|
23
|
+
let timer;
|
|
24
|
+
const timeout = new Promise((resolve) => {
|
|
25
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
await Promise.race([settled, timeout]);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export const corsHeaders = (extra) => ({
|
|
35
|
+
"Access-Control-Allow-Origin": "*",
|
|
36
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
37
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, MCP-Protocol-Version",
|
|
38
|
+
// Response headers are opaque to browser JS by default unless explicitly
|
|
39
|
+
// exposed — without this, a browser-based MCP client reading a 401 can
|
|
40
|
+
// see the status but not WWW-Authenticate itself, i.e. not the
|
|
41
|
+
// resource_metadata URL RFC 9728 discovery depends on.
|
|
42
|
+
"Access-Control-Expose-Headers": "WWW-Authenticate",
|
|
43
|
+
...extra,
|
|
44
|
+
});
|
|
45
|
+
export const jsonResponse = (input) => {
|
|
46
|
+
const cors = input.cors !== false;
|
|
47
|
+
return new Response(JSON.stringify(input.data), {
|
|
48
|
+
status: input.status ?? 200,
|
|
49
|
+
headers: {
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
"Cache-Control": "no-store",
|
|
52
|
+
...(cors ? corsHeaders() : {}),
|
|
53
|
+
...input.headers,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
export const emptyResponse = (status) => new Response(null, {
|
|
58
|
+
status,
|
|
59
|
+
headers: { ...corsHeaders(), "Cache-Control": "no-store" },
|
|
60
|
+
});
|
|
61
|
+
export const optionsResponse = (options) => new Response(null, {
|
|
62
|
+
status: 204,
|
|
63
|
+
headers: options?.cors === false
|
|
64
|
+
? { "Cache-Control": "no-store" }
|
|
65
|
+
: { ...corsHeaders(), "Cache-Control": "no-store" },
|
|
66
|
+
});
|
|
67
|
+
/** Reflect a validated browser Origin on MCP CORS instead of `*`. */
|
|
68
|
+
export const withReflectedCorsOrigin = (response, origin) => {
|
|
69
|
+
const headers = new Headers(response.headers);
|
|
70
|
+
if (!headers.has("Access-Control-Allow-Origin"))
|
|
71
|
+
return response;
|
|
72
|
+
headers.set("Access-Control-Allow-Origin", origin);
|
|
73
|
+
headers.set("Vary", "Origin");
|
|
74
|
+
return new Response(response.body, {
|
|
75
|
+
status: response.status,
|
|
76
|
+
statusText: response.statusText,
|
|
77
|
+
headers,
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
export const methodNotAllowed = (message = "Method not allowed", options) => jsonResponse({
|
|
81
|
+
data: { error: message },
|
|
82
|
+
status: 405,
|
|
83
|
+
...(options?.cors !== undefined ? { cors: options.cors } : {}),
|
|
84
|
+
});
|
|
85
|
+
export const GET_ONLY = new Set(["GET"]);
|
|
86
|
+
export const POST_ONLY = new Set(["POST"]);
|
|
87
|
+
/** For endpoints that must not send permissive CORS headers (`/token`, `/revoke`, `/consent`). */
|
|
88
|
+
export const NO_CORS = { cors: false };
|
|
89
|
+
/**
|
|
90
|
+
* OPTIONS → 204; allowed verb → null (continue); otherwise → 405.
|
|
91
|
+
* Shared by OAuth handlers that all need the same method gate.
|
|
92
|
+
*/
|
|
93
|
+
export const requireHttpMethod = (request, allowed, options) => {
|
|
94
|
+
if (request.method === "OPTIONS")
|
|
95
|
+
return optionsResponse(options);
|
|
96
|
+
if (allowed.has(request.method))
|
|
97
|
+
return null;
|
|
98
|
+
return methodNotAllowed(undefined, options);
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/http/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Origin allowlisting — Host-derived Node origins (multi-tenant) and browser
|
|
3
|
+
* `Origin` headers on MCP (DNS rebinding / CSRF). Same predicate shape as
|
|
4
|
+
* redirect allowlisting — not secret comparison.
|
|
5
|
+
*/
|
|
6
|
+
export type OriginAllowlistOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Exact origins (`https://acme.example.com`) and/or `*.example.com`
|
|
9
|
+
* wildcards. Wildcards match https subdomains only (not the apex, not http).
|
|
10
|
+
* `"*"` admits any origin (loud opt-out).
|
|
11
|
+
*
|
|
12
|
+
* Empty/omitted list → admit all (only safe with a fixed `asNodeHandler`
|
|
13
|
+
* `origin`). Host-derived `asNodeHandler` requires a non-empty list at
|
|
14
|
+
* construction. Resolved origin outside the list → 400; a fixed `origin`
|
|
15
|
+
* absent from a non-empty list throws at construction.
|
|
16
|
+
*/
|
|
17
|
+
allowedOrigins?: string[];
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Host-derived / allowlist Origin check (Node `asNodeHandler` multi-tenant).
|
|
21
|
+
* Empty or omitted `allowedOrigins` → admit all (callers that derive origin
|
|
22
|
+
* from Host must require a non-empty list before reaching here).
|
|
23
|
+
* `"*"` admits any origin.
|
|
24
|
+
*/
|
|
25
|
+
export declare const isAllowedOrigin: (origin: string, options?: OriginAllowlistOptions) => boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Browser `Origin` on MCP Streamable HTTP (DNS rebinding / CSRF).
|
|
28
|
+
* No header → allow (native / server-to-server). Empty or omitted allowlist →
|
|
29
|
+
* deny any present Origin (secure default — opposite of `isAllowedOrigin`).
|
|
30
|
+
* `"*"` opts out.
|
|
31
|
+
*/
|
|
32
|
+
export declare const isAllowedRequestOrigin: (originHeader: string | null, allowedRequestOrigins?: string[]) => boolean;
|
|
33
|
+
/** 403 when a present Origin fails the allowlist. `onDenied` runs first (audit). */
|
|
34
|
+
export declare const requestOriginDenied: (request: Request, allowedRequestOrigins: string[] | undefined, onDenied?: () => Promise<void>) => Promise<Response | null>;
|
|
35
|
+
/** When a non-wildcard allowlist admitted this Origin, reflect it on CORS. */
|
|
36
|
+
export declare const reflectCorsIfNeeded: (response: Response, request: Request, allowedRequestOrigins: string[] | undefined) => Response;
|
|
37
|
+
//# sourceMappingURL=origins.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origins.d.ts","sourceRoot":"","sources":["../../src/http/origins.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;;;;;;;OASG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAyCF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,EAAE,UAAS,sBAA2B,KAAG,OAMtF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GACjC,cAAc,MAAM,GAAG,IAAI,EAC3B,wBAAwB,MAAM,EAAE,KAC/B,OAIF,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,mBAAmB,GAC9B,SAAS,OAAO,EAChB,uBAAuB,MAAM,EAAE,GAAG,SAAS,EAC3C,WAAW,MAAM,OAAO,CAAC,IAAI,CAAC,KAC7B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAMzB,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,mBAAmB,GAC9B,UAAU,QAAQ,EAClB,SAAS,OAAO,EAChB,uBAAuB,MAAM,EAAE,GAAG,SAAS,KAC1C,QAMF,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Origin allowlisting — Host-derived Node origins (multi-tenant) and browser
|
|
3
|
+
* `Origin` headers on MCP (DNS rebinding / CSRF). Same predicate shape as
|
|
4
|
+
* redirect allowlisting — not secret comparison.
|
|
5
|
+
*/
|
|
6
|
+
import { jsonResponse, withReflectedCorsOrigin } from "./http.js";
|
|
7
|
+
const originOf = (value) => {
|
|
8
|
+
try {
|
|
9
|
+
return new URL(value).origin;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const isExactOrigin = (origin, options) => {
|
|
16
|
+
const normalized = originOf(origin);
|
|
17
|
+
if (!normalized)
|
|
18
|
+
return false;
|
|
19
|
+
return Boolean(options.allowedOrigins?.some((entry) => {
|
|
20
|
+
if (entry.startsWith("*."))
|
|
21
|
+
return false;
|
|
22
|
+
return originOf(entry) === normalized;
|
|
23
|
+
}));
|
|
24
|
+
};
|
|
25
|
+
/** `*.example.com` matches `https://acme.example.com`, not the apex, not http. */
|
|
26
|
+
const isWildcardSubdomain = (origin, options) => {
|
|
27
|
+
const patterns = options.allowedOrigins ?? [];
|
|
28
|
+
try {
|
|
29
|
+
const { protocol, hostname } = new URL(origin);
|
|
30
|
+
if (protocol !== "https:")
|
|
31
|
+
return false;
|
|
32
|
+
return patterns.some((entry) => {
|
|
33
|
+
if (!entry.startsWith("*."))
|
|
34
|
+
return false;
|
|
35
|
+
const suffix = entry.slice(1); // ".example.com"
|
|
36
|
+
return hostname.endsWith(suffix) && hostname.length > suffix.length;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const ORIGIN_PREDICATES = [isExactOrigin, isWildcardSubdomain];
|
|
44
|
+
/**
|
|
45
|
+
* Host-derived / allowlist Origin check (Node `asNodeHandler` multi-tenant).
|
|
46
|
+
* Empty or omitted `allowedOrigins` → admit all (callers that derive origin
|
|
47
|
+
* from Host must require a non-empty list before reaching here).
|
|
48
|
+
* `"*"` admits any origin.
|
|
49
|
+
*/
|
|
50
|
+
export const isAllowedOrigin = (origin, options = {}) => {
|
|
51
|
+
if (!options.allowedOrigins || options.allowedOrigins.length === 0) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (options.allowedOrigins.includes("*"))
|
|
55
|
+
return true;
|
|
56
|
+
return ORIGIN_PREDICATES.some((predicate) => predicate(origin, options));
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Browser `Origin` on MCP Streamable HTTP (DNS rebinding / CSRF).
|
|
60
|
+
* No header → allow (native / server-to-server). Empty or omitted allowlist →
|
|
61
|
+
* deny any present Origin (secure default — opposite of `isAllowedOrigin`).
|
|
62
|
+
* `"*"` opts out.
|
|
63
|
+
*/
|
|
64
|
+
export const isAllowedRequestOrigin = (originHeader, allowedRequestOrigins) => {
|
|
65
|
+
if (!originHeader)
|
|
66
|
+
return true;
|
|
67
|
+
if (!allowedRequestOrigins || allowedRequestOrigins.length === 0)
|
|
68
|
+
return false;
|
|
69
|
+
return isAllowedOrigin(originHeader, { allowedOrigins: allowedRequestOrigins });
|
|
70
|
+
};
|
|
71
|
+
/** 403 when a present Origin fails the allowlist. `onDenied` runs first (audit). */
|
|
72
|
+
export const requestOriginDenied = async (request, allowedRequestOrigins, onDenied) => {
|
|
73
|
+
if (isAllowedRequestOrigin(request.headers.get("Origin"), allowedRequestOrigins)) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
await onDenied?.();
|
|
77
|
+
return jsonResponse({ data: { error: "origin not allowed" }, status: 403, cors: false });
|
|
78
|
+
};
|
|
79
|
+
/** When a non-wildcard allowlist admitted this Origin, reflect it on CORS. */
|
|
80
|
+
export const reflectCorsIfNeeded = (response, request, allowedRequestOrigins) => {
|
|
81
|
+
const origin = request.headers.get("Origin");
|
|
82
|
+
if (!origin || !allowedRequestOrigins?.length || allowedRequestOrigins.includes("*")) {
|
|
83
|
+
return response;
|
|
84
|
+
}
|
|
85
|
+
return withReflectedCorsOrigin(response, origin);
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=origins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origins.js","sourceRoot":"","sources":["../../src/http/origins.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,5 +3,5 @@ export type { AuditEntry, McpHandlerOptions, McpPorts, Principal, ServerInfo, }
|
|
|
3
3
|
export type McpHandler = {
|
|
4
4
|
fetch: (request: Request) => Promise<Response>;
|
|
5
5
|
};
|
|
6
|
-
export declare const createMcpHandler: <TCtx>(
|
|
6
|
+
export declare const createMcpHandler: <TCtx>(handlerOptions: McpHandlerOptions<TCtx>) => McpHandler;
|
|
7
7
|
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/mcp/dispatch.ts"],"names":[],"mappings":"AAmBA,OAAO,EAIL,KAAK,iBAAiB,EAKvB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,QAAQ,EACR,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD,CAAC;AAkFF,eAAO,MAAM,gBAAgB,GAAI,IAAI,EAAE,gBAAgB,iBAAiB,CAAC,IAAI,CAAC,KAAG,UAwChF,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { rejectQueryToken } from "../auth/bearer.js";
|
|
2
|
+
import { BodyTooLargeError, DEFAULT_MCP_BODY_LIMIT, readBoundedText } from "../http/body.js";
|
|
3
|
+
import { emptyResponse, errorReason, INTERNAL_ERROR, jsonResponse, methodNotAllowed, optionsResponse, } from "../http/http.js";
|
|
4
|
+
import { reflectCorsIfNeeded, requestOriginDenied } from "../http/origins.js";
|
|
5
|
+
import { JSONRPC_INTERNAL_ERROR, JSONRPC_PARSE_ERROR, JSONRPC_PAYLOAD_TOO_LARGE, protocolVersionHeaderError, rpcError, validateJsonRpcEnvelope, } from "./jsonrpc.js";
|
|
6
|
+
import { DEFAULT_PUBLIC_METHODS, DENIAL_REASONS, isNotification, resolveWwwAuthenticate, safeAudit, unauthorized, } from "./methods.js";
|
|
7
|
+
import { dispatchRpc } from "./rpc-handlers.js";
|
|
8
|
+
const parseBody = async (request) => {
|
|
9
|
+
let text;
|
|
10
|
+
try {
|
|
11
|
+
text = await readBoundedText(request, DEFAULT_MCP_BODY_LIMIT);
|
|
12
|
+
}
|
|
13
|
+
catch (caught) {
|
|
14
|
+
if (caught instanceof BodyTooLargeError) {
|
|
15
|
+
return jsonResponse({
|
|
16
|
+
data: rpcError(null, JSONRPC_PAYLOAD_TOO_LARGE, "Payload too large"),
|
|
17
|
+
status: 413,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
return jsonResponse({ data: rpcError(null, JSONRPC_PARSE_ERROR, "Parse error"), status: 400 });
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(text);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return jsonResponse({ data: rpcError(null, JSONRPC_PARSE_ERROR, "Parse error"), status: 400 });
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
/** Transport-level denial: no JSON-RPC method has been parsed yet. */
|
|
30
|
+
const auditDenial = (options, error, startedAt) => safeAudit(options, { method: "", ok: false, error, durationMs: Date.now() - startedAt });
|
|
31
|
+
const denyQueryStringToken = async (request, options, startedAt) => {
|
|
32
|
+
if (rejectQueryToken(new URL(request.url))) {
|
|
33
|
+
await auditDenial(options, DENIAL_REASONS.queryStringToken, startedAt);
|
|
34
|
+
return unauthorized(resolveWwwAuthenticate(options, request), "Token in query string is rejected. Use Authorization Bearer.");
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
};
|
|
38
|
+
const handleGet = async (request, options, startedAt) => {
|
|
39
|
+
const denied = await denyQueryStringToken(request, options, startedAt);
|
|
40
|
+
if (denied)
|
|
41
|
+
return denied;
|
|
42
|
+
if (!request.headers.get("authorization")) {
|
|
43
|
+
await auditDenial(options, DENIAL_REASONS.unauthorized, startedAt);
|
|
44
|
+
return unauthorized(resolveWwwAuthenticate(options, request));
|
|
45
|
+
}
|
|
46
|
+
return methodNotAllowed("Use POST for MCP");
|
|
47
|
+
};
|
|
48
|
+
const handlePost = async (request, options, startedAt) => {
|
|
49
|
+
const denied = await denyQueryStringToken(request, options, startedAt);
|
|
50
|
+
if (denied)
|
|
51
|
+
return denied;
|
|
52
|
+
const parsed = await parseBody(request);
|
|
53
|
+
if (parsed instanceof Response)
|
|
54
|
+
return parsed;
|
|
55
|
+
const envelope = validateJsonRpcEnvelope(parsed);
|
|
56
|
+
if (envelope instanceof Response)
|
|
57
|
+
return envelope;
|
|
58
|
+
const versionError = protocolVersionHeaderError(request, envelope);
|
|
59
|
+
if (versionError)
|
|
60
|
+
return versionError;
|
|
61
|
+
if (isNotification(envelope))
|
|
62
|
+
return emptyResponse(202);
|
|
63
|
+
return dispatchRpc({ req: request, body: envelope, options, startedAt });
|
|
64
|
+
};
|
|
65
|
+
export const createMcpHandler = (handlerOptions) => {
|
|
66
|
+
const options = {
|
|
67
|
+
...handlerOptions,
|
|
68
|
+
publicMethods: handlerOptions.publicMethods ?? DEFAULT_PUBLIC_METHODS,
|
|
69
|
+
};
|
|
70
|
+
const HTTP_METHODS = {
|
|
71
|
+
OPTIONS: () => optionsResponse(),
|
|
72
|
+
GET: (request, startedAt) => handleGet(request, options, startedAt),
|
|
73
|
+
POST: (request, startedAt) => handlePost(request, options, startedAt),
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
fetch: async (request) => {
|
|
77
|
+
const startedAt = Date.now();
|
|
78
|
+
try {
|
|
79
|
+
const originDenied = await requestOriginDenied(request, options.allowedRequestOrigins, () => auditDenial(options, DENIAL_REASONS.originNotAllowed, startedAt));
|
|
80
|
+
if (originDenied)
|
|
81
|
+
return originDenied;
|
|
82
|
+
const handler = HTTP_METHODS[request.method];
|
|
83
|
+
const response = await (handler ? handler(request, startedAt) : methodNotAllowed());
|
|
84
|
+
return reflectCorsIfNeeded(response, request, options.allowedRequestOrigins);
|
|
85
|
+
}
|
|
86
|
+
catch (caught) {
|
|
87
|
+
// A host port (authenticate/context/audit) threw. `fetch` must never
|
|
88
|
+
// reject — the caller gets an honest 500, not an unhandled rejection
|
|
89
|
+
// whose shape depends on which runtime is hosting this.
|
|
90
|
+
await auditDenial(options, errorReason(caught) || INTERNAL_ERROR, startedAt);
|
|
91
|
+
return reflectCorsIfNeeded(jsonResponse({
|
|
92
|
+
data: rpcError(null, JSONRPC_INTERNAL_ERROR, "Internal error"),
|
|
93
|
+
status: 500,
|
|
94
|
+
}), request, options.allowedRequestOrigins);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=dispatch.js.map
|