mcp-trellis 1.0.0 → 2.1.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 +63 -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/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 +12 -12
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +9 -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 +35 -47
- package/docs/compatibility.md +84 -0
- package/docs/diagrams/mcp-trellis-architecture.html +305 -0
- package/docs/guide.md +37 -13
- package/docs/reference.md +54 -22
- package/docs/security.md +58 -9
- package/docs/troubleshooting.md +19 -0
- package/package.json +23 -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
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { type WwwAuthenticateOptions } from "
|
|
1
|
+
import { type WwwAuthenticateOptions } from "../auth/bearer.js";
|
|
2
2
|
import { type JsonRpcId, type JsonRpcRequest } from "./jsonrpc.js";
|
|
3
3
|
import type { ToolRegistry } from "./registry.js";
|
|
4
4
|
export type Principal = {
|
|
5
5
|
id: string;
|
|
6
|
+
/**
|
|
7
|
+
* Granted scopes. The literal `"*"` is a wildcard that satisfies **every**
|
|
8
|
+
* tool `scope` check (see `hasScope`) — reserve it for trusted internal
|
|
9
|
+
* principals; never mint it for an external connector.
|
|
10
|
+
*/
|
|
6
11
|
scopes: string[];
|
|
7
12
|
/** Optional host claims from `verifyToken` (tenant, plan, role, …). */
|
|
8
13
|
claims?: Record<string, unknown> | undefined;
|
|
@@ -19,6 +24,12 @@ export type AuditEntry = {
|
|
|
19
24
|
};
|
|
20
25
|
export type McpPorts<TCtx> = {
|
|
21
26
|
authenticate: (req: Request, method: string, tool?: string) => Promise<Principal | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Builds the tool context. Called only for a `tools/call` that passed the
|
|
29
|
+
* unknown-tool and scope checks — never for `initialize`, `ping` or
|
|
30
|
+
* `tools/list`. `principal` is null only when a host adds `tools/call`
|
|
31
|
+
* to `publicMethods`.
|
|
32
|
+
*/
|
|
22
33
|
context: (req: Request, principal: Principal | null) => TCtx | Promise<TCtx>;
|
|
23
34
|
/**
|
|
24
35
|
* Opt-in metrics hook. Pass any function to receive tool results and auth
|
|
@@ -51,10 +62,46 @@ export type McpHandlerOptions<TCtx> = {
|
|
|
51
62
|
* indefinitely — "can't fail" isn't "can't delay."
|
|
52
63
|
*/
|
|
53
64
|
auditTimeoutMs?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Browser `Origin` header allowlist for MCP (DNS rebinding / CSRF).
|
|
67
|
+
* Distinct from Node Host `allowedOrigins` on `asNodeHandler`.
|
|
68
|
+
* No `Origin` → allowed (native clients). Omitted/empty → reject any
|
|
69
|
+
* present `Origin`. `"*"` admits any Origin.
|
|
70
|
+
*/
|
|
71
|
+
allowedRequestOrigins?: string[];
|
|
72
|
+
/**
|
|
73
|
+
* Filter `tools/list` down to tools the calling principal's scopes
|
|
74
|
+
* actually satisfy (an unscoped tool is always listed). Default
|
|
75
|
+
* **false** — every tool is always listed, `tools/call` is what enforces
|
|
76
|
+
* scope (with a **403** `insufficient_scope`, see `insufficientScope`).
|
|
77
|
+
* Turning this on trades that step-up signal away: a client can no
|
|
78
|
+
* longer discover "this tool exists, ask for its scope" from `tools/list`
|
|
79
|
+
* for a scope it doesn't hold — only worth it if listing a tool at all
|
|
80
|
+
* would leak something (its name, description, or schema) you don't want
|
|
81
|
+
* a lesser-scoped principal to see.
|
|
82
|
+
*/
|
|
83
|
+
hideToolsOutsideScope?: boolean;
|
|
84
|
+
};
|
|
85
|
+
/** `McpHandlerOptions` after `createMcpHandler` resolved its defaults. */
|
|
86
|
+
export type ResolvedMcpHandlerOptions<TCtx> = McpHandlerOptions<TCtx> & {
|
|
87
|
+
publicMethods: Set<string>;
|
|
88
|
+
};
|
|
89
|
+
/** `AuditEntry.error` values for requests the handler itself denied. */
|
|
90
|
+
export declare const DENIAL_REASONS: {
|
|
91
|
+
readonly unauthorized: "unauthorized";
|
|
92
|
+
readonly queryStringToken: "query_string_token";
|
|
93
|
+
readonly originNotAllowed: "origin_not_allowed";
|
|
94
|
+
readonly unknownTool: "unknown_tool";
|
|
95
|
+
readonly missingScope: "missing_scope";
|
|
54
96
|
};
|
|
55
97
|
export declare const DEFAULT_PUBLIC_METHODS: Set<string>;
|
|
98
|
+
/**
|
|
99
|
+
* JSON-RPC 2.0: a notification omits `id` (no response). MCP also treats
|
|
100
|
+
* `notifications/*` and legacy `initialized` as notifications even when an
|
|
101
|
+
* `id` is present. Orphan result-shaped bodies (no method/id) get 202 too.
|
|
102
|
+
*/
|
|
56
103
|
export declare const isNotification: (body: JsonRpcRequest) => boolean;
|
|
57
|
-
export declare const
|
|
104
|
+
export declare const resolveWwwAuthenticate: <TCtx>(options: McpHandlerOptions<TCtx>, request: Request) => WwwAuthenticateOptions;
|
|
58
105
|
/**
|
|
59
106
|
* `id` defaults to `null` for the transport-level call sites in
|
|
60
107
|
* `dispatch.ts` — a body hasn't been parsed yet there, so there's no real
|
|
@@ -62,7 +109,16 @@ export declare const resolveWww: <TCtx>(options: McpHandlerOptions<TCtx>, req: R
|
|
|
62
109
|
* method handlers) have one and must pass it, or a client correlating
|
|
63
110
|
* responses by id sees `null` on every 401 regardless of what it sent.
|
|
64
111
|
*/
|
|
65
|
-
export declare const unauthorized: (
|
|
112
|
+
export declare const unauthorized: (wwwAuthenticate: WwwAuthenticateOptions, message?: string, id?: JsonRpcId) => Response;
|
|
113
|
+
/**
|
|
114
|
+
* RFC 6750 §3.1: a request that authenticated but lacks the required scope
|
|
115
|
+
* gets **403**, not 401 — 401 tells the client "authenticate", which a
|
|
116
|
+
* scope-step-up client obeys by re-running the exact same OAuth flow,
|
|
117
|
+
* getting back the exact same scopes, and retrying forever. 403 with
|
|
118
|
+
* `error="insufficient_scope"` says what's actually wrong: re-consent with
|
|
119
|
+
* a broader `scope` request, not re-authenticate.
|
|
120
|
+
*/
|
|
121
|
+
export declare const insufficientScope: (wwwAuthenticate: WwwAuthenticateOptions, scope: string, id?: JsonRpcId) => Response;
|
|
66
122
|
/**
|
|
67
123
|
* Invoke the audit metrics hook, swallowing any failure and racing it
|
|
68
124
|
* against `auditTimeoutMs` so a hanging sink can't stall the response
|
|
@@ -72,13 +128,6 @@ export declare const unauthorized: (www: WwwAuthenticateOptions, message?: strin
|
|
|
72
128
|
* an unhandled rejection whenever it does finish.
|
|
73
129
|
*/
|
|
74
130
|
export declare const safeAudit: <TCtx>(options: McpHandlerOptions<TCtx>, entry: AuditEntry) => Promise<void>;
|
|
131
|
+
/** True when `scope` is unset, granted, or covered by the `"*"` wildcard scope. */
|
|
75
132
|
export declare const hasScope: (principal: Principal, scope: string | undefined) => boolean;
|
|
76
|
-
export declare const dispatchRpc: <TCtx>(input: {
|
|
77
|
-
req: Request;
|
|
78
|
-
body: JsonRpcRequest;
|
|
79
|
-
options: McpHandlerOptions<TCtx>;
|
|
80
|
-
publicMethods: Set<string>;
|
|
81
|
-
/** Request start (captured in `createMcpHandler.fetch`), for denial-path audit timing. */
|
|
82
|
-
startedAt: number;
|
|
83
|
-
}) => Promise<Response>;
|
|
84
133
|
//# sourceMappingURL=methods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methods.d.ts","sourceRoot":"","sources":["../../src/mcp/methods.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,sBAAsB,EAAyB,MAAM,mBAAmB,CAAC;AAEvF,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,cAAc,EAEpB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX;;;;OAIG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC9C,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,UAAU,GAAG;IACvB,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,QAAQ,CAAC,IAAI,IAAI;IAC3B,YAAY,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACzF;;;;;OAKG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,IAAI,IAAI;IACpC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,sBAAsB,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,sBAAsB,CAAC,CAAC;IACrF,2EAA2E;IAC3E,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC;;;;;;;;;;OAUG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF,0EAA0E;AAC1E,MAAM,MAAM,yBAAyB,CAAC,IAAI,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG;IACtE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,cAAc;;;;;;CAMjB,CAAC;AAEX,eAAO,MAAM,sBAAsB,aAKjC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,MAAM,cAAc,KAAG,OAQrD,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,IAAI,EACzC,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAChC,SAAS,OAAO,KACf,sBAG0B,CAAC;AAE9B;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GACvB,iBAAiB,sBAAsB,EACvC,gBAAwB,EACxB,KAAI,SAAgB,KACnB,QAKC,CAAC;AAEL;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC5B,iBAAiB,sBAAsB,EACvC,OAAO,MAAM,EACb,KAAI,SAAgB,KACnB,QAWC,CAAC;AAEL;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAU,IAAI,EAClC,SAAS,iBAAiB,CAAC,IAAI,CAAC,EAChC,OAAO,UAAU,KAChB,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,QAAQ,GAAI,WAAW,SAAS,EAAE,OAAO,MAAM,GAAG,SAAS,KAAG,OAG1E,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { wwwAuthenticateHeader } from "../auth/bearer.js";
|
|
2
|
+
import { DEFAULT_AUDIT_TIMEOUT_MS, jsonResponse, runAuditBounded } from "../http/http.js";
|
|
3
|
+
import { JSONRPC_INSUFFICIENT_SCOPE, JSONRPC_UNAUTHORIZED, rpcError, } from "./jsonrpc.js";
|
|
4
|
+
/** `AuditEntry.error` values for requests the handler itself denied. */
|
|
5
|
+
export const DENIAL_REASONS = {
|
|
6
|
+
unauthorized: "unauthorized",
|
|
7
|
+
queryStringToken: "query_string_token",
|
|
8
|
+
originNotAllowed: "origin_not_allowed",
|
|
9
|
+
unknownTool: "unknown_tool",
|
|
10
|
+
missingScope: "missing_scope",
|
|
11
|
+
};
|
|
12
|
+
export const DEFAULT_PUBLIC_METHODS = new Set([
|
|
13
|
+
"initialize",
|
|
14
|
+
"ping",
|
|
15
|
+
"notifications/initialized",
|
|
16
|
+
"initialized",
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* JSON-RPC 2.0: a notification omits `id` (no response). MCP also treats
|
|
20
|
+
* `notifications/*` and legacy `initialized` as notifications even when an
|
|
21
|
+
* `id` is present. Orphan result-shaped bodies (no method/id) get 202 too.
|
|
22
|
+
*/
|
|
23
|
+
export const isNotification = (body) => {
|
|
24
|
+
if (body.id === undefined) {
|
|
25
|
+
return typeof body.method === "string" || (body.method === undefined && "result" in body);
|
|
26
|
+
}
|
|
27
|
+
return ((typeof body.method === "string" && body.method.startsWith("notifications/")) ||
|
|
28
|
+
body.method === "initialized");
|
|
29
|
+
};
|
|
30
|
+
export const resolveWwwAuthenticate = (options, request) => typeof options.wwwAuthenticate === "function"
|
|
31
|
+
? options.wwwAuthenticate(request)
|
|
32
|
+
: options.wwwAuthenticate;
|
|
33
|
+
/**
|
|
34
|
+
* `id` defaults to `null` for the transport-level call sites in
|
|
35
|
+
* `dispatch.ts` — a body hasn't been parsed yet there, so there's no real
|
|
36
|
+
* id to echo. Call sites past that point (inside `dispatchRpc` and the
|
|
37
|
+
* method handlers) have one and must pass it, or a client correlating
|
|
38
|
+
* responses by id sees `null` on every 401 regardless of what it sent.
|
|
39
|
+
*/
|
|
40
|
+
export const unauthorized = (wwwAuthenticate, message = "Unauthorized", id = null) => jsonResponse({
|
|
41
|
+
data: rpcError(id, JSONRPC_UNAUTHORIZED, message),
|
|
42
|
+
status: 401,
|
|
43
|
+
headers: { "WWW-Authenticate": wwwAuthenticateHeader(wwwAuthenticate) },
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* RFC 6750 §3.1: a request that authenticated but lacks the required scope
|
|
47
|
+
* gets **403**, not 401 — 401 tells the client "authenticate", which a
|
|
48
|
+
* scope-step-up client obeys by re-running the exact same OAuth flow,
|
|
49
|
+
* getting back the exact same scopes, and retrying forever. 403 with
|
|
50
|
+
* `error="insufficient_scope"` says what's actually wrong: re-consent with
|
|
51
|
+
* a broader `scope` request, not re-authenticate.
|
|
52
|
+
*/
|
|
53
|
+
export const insufficientScope = (wwwAuthenticate, scope, id = null) => jsonResponse({
|
|
54
|
+
data: rpcError(id, JSONRPC_INSUFFICIENT_SCOPE, `Missing scope: ${scope}`),
|
|
55
|
+
status: 403,
|
|
56
|
+
headers: {
|
|
57
|
+
"WWW-Authenticate": wwwAuthenticateHeader({
|
|
58
|
+
...wwwAuthenticate,
|
|
59
|
+
error: "insufficient_scope",
|
|
60
|
+
scope,
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Invoke the audit metrics hook, swallowing any failure and racing it
|
|
66
|
+
* against `auditTimeoutMs` so a hanging sink can't stall the response
|
|
67
|
+
* either — "can't fail a request" was already true; this adds "can't
|
|
68
|
+
* delay one" past a bound. The audit call keeps running in the background
|
|
69
|
+
* if the timer wins; its own `catch` below means it can never surface as
|
|
70
|
+
* an unhandled rejection whenever it does finish.
|
|
71
|
+
*/
|
|
72
|
+
export const safeAudit = async (options, entry) => {
|
|
73
|
+
const audit = options.ports.audit;
|
|
74
|
+
if (!audit)
|
|
75
|
+
return;
|
|
76
|
+
await runAuditBounded(() => audit(entry), options.auditTimeoutMs ?? DEFAULT_AUDIT_TIMEOUT_MS);
|
|
77
|
+
};
|
|
78
|
+
/** True when `scope` is unset, granted, or covered by the `"*"` wildcard scope. */
|
|
79
|
+
export const hasScope = (principal, scope) => {
|
|
80
|
+
if (!scope)
|
|
81
|
+
return true;
|
|
82
|
+
return principal.scopes.includes(scope) || principal.scopes.includes("*");
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=methods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"methods.js","sourceRoot":"","sources":["../../src/mcp/methods.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export declare const PROTOCOL_VERSIONS: Set<string>;
|
|
2
2
|
/**
|
|
3
3
|
* Answered on `initialize` when the client's requested version is missing
|
|
4
|
-
* or unrecognized
|
|
5
|
-
* server supports, preferring the latest — not the oldest one, which is
|
|
6
|
-
* what this constant did before.
|
|
4
|
+
* or unrecognized: per the spec, the latest version this server supports.
|
|
7
5
|
*/
|
|
8
6
|
export declare const DEFAULT_PROTOCOL_VERSION = "2025-06-18";
|
|
9
7
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/mcp/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,aAAsD,CAAC;AAErF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,eAAe,CAAC;AAE5D,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,MAMtE,CAAC"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export const PROTOCOL_VERSIONS = new Set(["2024-11-05", "2025-03-26", "2025-06-18"]);
|
|
2
2
|
/**
|
|
3
3
|
* Answered on `initialize` when the client's requested version is missing
|
|
4
|
-
* or unrecognized
|
|
5
|
-
* server supports, preferring the latest — not the oldest one, which is
|
|
6
|
-
* what this constant did before.
|
|
4
|
+
* or unrecognized: per the spec, the latest version this server supports.
|
|
7
5
|
*/
|
|
8
6
|
export const DEFAULT_PROTOCOL_VERSION = "2025-06-18";
|
|
9
7
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/mcp/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;AAErF;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,YAAY,CAAC;AAE5D,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAgC,EAAU,EAAE;IAC9E,MAAM,SAAS,GAAG,MAAM,EAAE,eAAe,CAAC;IAC1C,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QACtE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,wBAAwB,CAAC;AAClC,CAAC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { JsonSchema } from "./validate.js";
|
|
2
|
+
export type ToolResult = {
|
|
3
|
+
content: Array<{
|
|
4
|
+
type: "text";
|
|
5
|
+
text: string;
|
|
6
|
+
}>;
|
|
7
|
+
isError?: boolean | undefined;
|
|
8
|
+
};
|
|
9
|
+
export type ToolHandler<TCtx> = (ctx: TCtx, args: Record<string, unknown>) => Promise<ToolResult | string> | ToolResult | string;
|
|
10
|
+
export type ToolDef<TCtx> = {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
inputSchema: JsonSchema;
|
|
14
|
+
/**
|
|
15
|
+
* Required scope, or `undefined`/omitted for "any authenticated
|
|
16
|
+
* principal, whatever its scopes." On a `createMcpApp` server advertising
|
|
17
|
+
* more than one scope, an omitted `scope` is a construction-time error —
|
|
18
|
+
* see `assertToolScopesConfigured` — precisely because "any authenticated
|
|
19
|
+
* principal" silently includes a principal with zero scopes, which is
|
|
20
|
+
* rarely the intent on a multi-scope server. Pass `scope: null` to state
|
|
21
|
+
* that intentionally instead of by omission.
|
|
22
|
+
*/
|
|
23
|
+
scope?: string | null | undefined;
|
|
24
|
+
handler: ToolHandler<TCtx>;
|
|
25
|
+
};
|
|
26
|
+
export type ToolListEntry = {
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
inputSchema: JsonSchema;
|
|
30
|
+
};
|
|
31
|
+
export type ToolRegistry<TCtx> = {
|
|
32
|
+
/**
|
|
33
|
+
* `allowScope`, when passed, is consulted per tool with that tool's
|
|
34
|
+
* `scope` — omit a tool by returning `false`. Lets a caller (see
|
|
35
|
+
* `McpHandlerOptions.hideToolsOutsideScope`) filter `tools/list` by a
|
|
36
|
+
* principal's granted scopes without the registry itself knowing what a
|
|
37
|
+
* "principal" or "scope check" is — it only ever sees the raw `scope`
|
|
38
|
+
* value already on each `ToolDef`.
|
|
39
|
+
*/
|
|
40
|
+
list: (allowScope?: (scope: string | null | undefined) => boolean) => ToolListEntry[];
|
|
41
|
+
get: (name: string) => ToolDef<TCtx> | undefined;
|
|
42
|
+
call: (name: string, ctx: TCtx, args: Record<string, unknown>) => Promise<ToolResult>;
|
|
43
|
+
};
|
|
44
|
+
export type RegistryOptions = {
|
|
45
|
+
/**
|
|
46
|
+
* Validate tools/call args against inputSchema. Default **true** since 2.0 —
|
|
47
|
+
* forgetting this was a silent injection path into handlers. Set `false`
|
|
48
|
+
* only when you intentionally skip schema checks.
|
|
49
|
+
*/
|
|
50
|
+
validateArgs?: boolean | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Map tool exceptions to client-visible text.
|
|
53
|
+
* Default: `"Tool execution failed"` (no exception leakage).
|
|
54
|
+
*/
|
|
55
|
+
onToolError?: ((error: unknown) => string) | undefined;
|
|
56
|
+
};
|
|
57
|
+
export declare const createToolRegistry: <TCtx>(tools: ToolDef<TCtx>[], options?: RegistryOptions) => ToolRegistry<TCtx>;
|
|
58
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/mcp/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,IAAI,IAAI,CAC9B,GAAG,EAAE,IAAI,EACT,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC,UAAU,GAAG,MAAM,CAAC,GAAG,UAAU,GAAG,MAAM,CAAC;AAExD,MAAM,MAAM,OAAO,CAAC,IAAI,IAAI;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;IACxB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,OAAO,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,IAAI,IAAI;IAC/B;;;;;;;OAOG;IACH,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,OAAO,KAAK,aAAa,EAAE,CAAC;IACtF,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;IACjD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACvF,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CACxD,CAAC;AA8DF,eAAO,MAAM,kBAAkB,GAAI,IAAI,EACrC,OAAO,OAAO,CAAC,IAAI,CAAC,EAAE,EACtB,UAAS,eAAoB,KAC5B,YAAY,CAAC,IAAI,CAqCnB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { missingObjectType, unsupportedKeywords, validateAgainstSchema } from "./validate.js";
|
|
2
|
+
const DEFAULT_TOOL_ERROR = "Tool execution failed";
|
|
3
|
+
/**
|
|
4
|
+
* Map a tool exception to client-visible text.
|
|
5
|
+
* A throwing or non-string `onToolError` falls back to the redacted default —
|
|
6
|
+
* a mapper must never turn a tool failure into a transport failure.
|
|
7
|
+
*/
|
|
8
|
+
const resolveToolError = (caught, onToolError) => {
|
|
9
|
+
if (!onToolError)
|
|
10
|
+
return DEFAULT_TOOL_ERROR;
|
|
11
|
+
try {
|
|
12
|
+
const mapped = onToolError(caught);
|
|
13
|
+
return typeof mapped === "string" && mapped.length > 0 ? mapped : DEFAULT_TOOL_ERROR;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return DEFAULT_TOOL_ERROR;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const asToolResult = (out) => {
|
|
20
|
+
if (typeof out === "string") {
|
|
21
|
+
return { content: [{ type: "text", text: out }], isError: false };
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
};
|
|
25
|
+
const errorResult = (message) => ({
|
|
26
|
+
content: [{ type: "text", text: message }],
|
|
27
|
+
isError: true,
|
|
28
|
+
});
|
|
29
|
+
/** Throws with every violation listed at once, rather than failing on the first tool found. */
|
|
30
|
+
const assertToolSchemasValid = (tools) => {
|
|
31
|
+
const keywordViolations = tools.flatMap((tool) => unsupportedKeywords(tool.inputSchema).map((path) => `${tool.name}: unsupported JSON Schema keyword at ${path}`));
|
|
32
|
+
if (keywordViolations.length > 0) {
|
|
33
|
+
throw new Error(`validateArgs: true but inputSchema uses unsupported keywords:\n` +
|
|
34
|
+
keywordViolations.join("\n") +
|
|
35
|
+
"\nRemove the keyword, enforce it in the handler, or set validateArgs: false");
|
|
36
|
+
}
|
|
37
|
+
// `properties`/`required` without `type: "object"` validates nothing at
|
|
38
|
+
// that node, and passes the keyword check above. See `missingObjectType`.
|
|
39
|
+
const typeViolations = tools.flatMap((tool) => missingObjectType(tool.inputSchema).map((path) => `${tool.name}: ${path} uses properties/required without type: "object"`));
|
|
40
|
+
if (typeViolations.length > 0) {
|
|
41
|
+
throw new Error(`validateArgs: true but inputSchema implies an object shape without declaring it:\n` +
|
|
42
|
+
typeViolations.join("\n") +
|
|
43
|
+
'\nAdd type: "object" (or include it in a union), or set validateArgs: false');
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
export const createToolRegistry = (tools, options = {}) => {
|
|
47
|
+
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
48
|
+
if (byName.size !== tools.length) {
|
|
49
|
+
throw new Error("duplicate tool names in registry");
|
|
50
|
+
}
|
|
51
|
+
const validateArgs = options.validateArgs !== false;
|
|
52
|
+
if (validateArgs)
|
|
53
|
+
assertToolSchemasValid(tools);
|
|
54
|
+
return {
|
|
55
|
+
list: (allowScope) => tools
|
|
56
|
+
.filter((tool) => !allowScope || allowScope(tool.scope))
|
|
57
|
+
.map(({ name, description, inputSchema }) => ({
|
|
58
|
+
name,
|
|
59
|
+
description,
|
|
60
|
+
inputSchema,
|
|
61
|
+
})),
|
|
62
|
+
get: (name) => byName.get(name),
|
|
63
|
+
call: async (name, ctx, args) => {
|
|
64
|
+
const tool = byName.get(name);
|
|
65
|
+
if (!tool)
|
|
66
|
+
return errorResult(`Unknown tool: ${name}`);
|
|
67
|
+
if (validateArgs) {
|
|
68
|
+
const errors = validateAgainstSchema(args, tool.inputSchema);
|
|
69
|
+
if (errors.length > 0)
|
|
70
|
+
return errorResult(errors.join("; "));
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
return asToolResult(await tool.handler(ctx, args));
|
|
74
|
+
}
|
|
75
|
+
catch (caught) {
|
|
76
|
+
return errorResult(resolveToolError(caught, options.onToolError));
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/mcp/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AA+D9F,MAAM,kBAAkB,GAAG,uBAAuB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,CAAC,MAAe,EAAE,WAA2C,EAAU,EAAE;IAChG,IAAI,CAAC,WAAW;QAAE,OAAO,kBAAkB,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACnC,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,kBAAkB,CAAC;IAC5B,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,GAAwB,EAAc,EAAE;IAC5D,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,OAAe,EAAc,EAAE,CAAC,CAAC;IACpD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC1C,OAAO,EAAE,IAAI;CACd,CAAC,CAAC;AAEH,+FAA+F;AAC/F,MAAM,sBAAsB,GAAG,CAAO,KAAsB,EAAQ,EAAE;IACpE,MAAM,iBAAiB,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/C,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CACvC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,wCAAwC,IAAI,EAAE,CACrE,CACF,CAAC;IACF,IAAI,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,iEAAiE;YAC/D,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;YAC5B,6EAA6E,CAChF,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,0EAA0E;IAC1E,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CACrC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,KAAK,IAAI,kDAAkD,CAClF,CACF,CAAC;IACF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,oFAAoF;YAClF,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,6EAA6E,CAChF,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAsB,EACtB,UAA2B,EAAE,EACT,EAAE;IACtB,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,KAAK,KAAK,CAAC;IACpD,IAAI,YAAY;QAAE,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAEhD,OAAO;QACL,IAAI,EAAE,CAAC,UAAU,EAAE,EAAE,CACnB,KAAK;aACF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACvD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;YAC5C,IAAI;YACJ,WAAW;YACX,WAAW;SACZ,CAAC,CAAC;QAEP,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QAE/B,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI;gBAAE,OAAO,WAAW,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;YAEvD,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;gBAC7D,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAC/D,CAAC;YAED,IAAI,CAAC;gBACH,OAAO,YAAY,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACrD,CAAC;YAAC,OAAO,MAAM,EAAE,CAAC;gBAChB,OAAO,WAAW,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** JSON-RPC method handlers and authenticated dispatch. */
|
|
2
|
+
import { type JsonRpcRequest } from "./jsonrpc.js";
|
|
3
|
+
import { type ResolvedMcpHandlerOptions } from "./methods.js";
|
|
4
|
+
export declare const dispatchRpc: <TCtx>(input: {
|
|
5
|
+
req: Request;
|
|
6
|
+
body: JsonRpcRequest;
|
|
7
|
+
options: ResolvedMcpHandlerOptions<TCtx>;
|
|
8
|
+
/** Request start (captured in `createMcpHandler.fetch`), for denial-path audit timing. */
|
|
9
|
+
startedAt: number;
|
|
10
|
+
}) => Promise<Response>;
|
|
11
|
+
//# sourceMappingURL=rpc-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-handlers.d.ts","sourceRoot":"","sources":["../../src/mcp/rpc-handlers.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAI3D,OAAO,EAIL,KAAK,cAAc,EAGpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAOL,KAAK,yBAAyB,EAI/B,MAAM,cAAc,CAAC;AA8ItB,eAAO,MAAM,WAAW,GAAU,IAAI,EAAE,OAAO;IAC7C,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,yBAAyB,CAAC,IAAI,CAAC,CAAC;IACzC,0FAA0F;IAC1F,SAAS,EAAE,MAAM,CAAC;CACnB,KAAG,OAAO,CAAC,QAAQ,CAkCnB,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/** JSON-RPC method handlers and authenticated dispatch. */
|
|
2
|
+
import { jsonResponse } from "../http/http.js";
|
|
3
|
+
import { isJsonObject } from "../util/json.js";
|
|
4
|
+
import { JSONRPC_INVALID_PARAMS, JSONRPC_METHOD_NOT_FOUND, rpcError, rpcResult, } from "./jsonrpc.js";
|
|
5
|
+
import { DENIAL_REASONS, hasScope, insufficientScope, resolveWwwAuthenticate, safeAudit, unauthorized, } from "./methods.js";
|
|
6
|
+
import { pickProtocolVersion } from "./protocol.js";
|
|
7
|
+
/** The one shape every "tools/call" outcome — unknown tool, missing scope, or a real call — audits. */
|
|
8
|
+
const auditToolCall = (options, input) => safeAudit(options, {
|
|
9
|
+
method: "tools/call",
|
|
10
|
+
tool: input.tool,
|
|
11
|
+
principalId: input.principal?.id,
|
|
12
|
+
ok: input.ok,
|
|
13
|
+
error: input.error,
|
|
14
|
+
durationMs: Date.now() - input.startedAt,
|
|
15
|
+
});
|
|
16
|
+
const parseToolCallArgs = (body) => {
|
|
17
|
+
const params = body.params ?? {};
|
|
18
|
+
const name = String(params.name ?? "");
|
|
19
|
+
const args = isJsonObject(params.arguments) ? params.arguments : {};
|
|
20
|
+
return { name, args };
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A name that doesn't refer to any registered tool is a malformed request,
|
|
24
|
+
* not a tool execution outcome — MCP distinguishes protocol errors (a
|
|
25
|
+
* standard JSON-RPC error response) from a tool's own isError: true result,
|
|
26
|
+
* and "no such tool" belongs to the former. `ToolRegistry.call`'s own
|
|
27
|
+
* "Unknown tool: …" text-result fallback stays as is for direct callers of
|
|
28
|
+
* that API outside this dispatch.
|
|
29
|
+
*/
|
|
30
|
+
const unknownToolResponse = async (options, input) => {
|
|
31
|
+
await auditToolCall(options, {
|
|
32
|
+
...input,
|
|
33
|
+
tool: input.name,
|
|
34
|
+
ok: false,
|
|
35
|
+
error: DENIAL_REASONS.unknownTool,
|
|
36
|
+
});
|
|
37
|
+
return jsonResponse({
|
|
38
|
+
data: rpcError(input.id, JSONRPC_INVALID_PARAMS, `Unknown tool: ${input.name}`),
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const missingScopeResponse = async (req, options, input) => {
|
|
42
|
+
await auditToolCall(options, {
|
|
43
|
+
...input,
|
|
44
|
+
tool: input.name,
|
|
45
|
+
ok: false,
|
|
46
|
+
error: DENIAL_REASONS.missingScope,
|
|
47
|
+
});
|
|
48
|
+
const wwwAuthenticate = resolveWwwAuthenticate(options, req);
|
|
49
|
+
// No principal at all (only reachable when a host adds "tools/call" to
|
|
50
|
+
// `publicMethods`) → 401, nothing was presented to authenticate with.
|
|
51
|
+
// A principal that authenticated but lacks the tool's scope → 403
|
|
52
|
+
// insufficient_scope (RFC 6750 §3.1) — see `insufficientScope`'s doc.
|
|
53
|
+
return input.principal
|
|
54
|
+
? insufficientScope(wwwAuthenticate, input.scope, input.id)
|
|
55
|
+
: unauthorized(wwwAuthenticate, `Missing scope: ${input.scope}`, input.id);
|
|
56
|
+
};
|
|
57
|
+
const METHODS = {
|
|
58
|
+
initialize: async ({ id, body, options }) => jsonResponse({
|
|
59
|
+
data: rpcResult(id, {
|
|
60
|
+
protocolVersion: pickProtocolVersion(body.params),
|
|
61
|
+
capabilities: { tools: {} },
|
|
62
|
+
serverInfo: options.serverInfo,
|
|
63
|
+
instructions: options.instructions ?? "",
|
|
64
|
+
}),
|
|
65
|
+
}),
|
|
66
|
+
ping: async ({ id }) => jsonResponse({ data: rpcResult(id, {}) }),
|
|
67
|
+
"tools/list": async ({ id, options, principal }) => {
|
|
68
|
+
const allowScope = options.hideToolsOutsideScope
|
|
69
|
+
? (scope) => !scope || (principal ? hasScope(principal, scope) : false)
|
|
70
|
+
: undefined;
|
|
71
|
+
return jsonResponse({ data: rpcResult(id, { tools: options.registry.list(allowScope) }) });
|
|
72
|
+
},
|
|
73
|
+
"tools/call": async ({ req, body, id, principal, options, startedAt }) => {
|
|
74
|
+
const { name, args } = parseToolCallArgs(body);
|
|
75
|
+
const tool = options.registry.get(name);
|
|
76
|
+
if (!tool)
|
|
77
|
+
return unknownToolResponse(options, { name, principal, id, startedAt });
|
|
78
|
+
if (tool.scope && (!principal || !hasScope(principal, tool.scope))) {
|
|
79
|
+
return missingScopeResponse(req, options, {
|
|
80
|
+
name,
|
|
81
|
+
principal,
|
|
82
|
+
id,
|
|
83
|
+
startedAt,
|
|
84
|
+
scope: tool.scope,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Built only for a call that will actually run: `initialize`, `ping`,
|
|
88
|
+
// `tools/list` and denied calls never reach the host's `context` port.
|
|
89
|
+
const ctx = await options.ports.context(req, principal);
|
|
90
|
+
const toolStartedAt = Date.now();
|
|
91
|
+
const result = await options.registry.call(name, ctx, args);
|
|
92
|
+
await auditToolCall(options, {
|
|
93
|
+
tool: name,
|
|
94
|
+
principal,
|
|
95
|
+
ok: !result.isError,
|
|
96
|
+
error: result.isError ? result.content[0]?.text : undefined,
|
|
97
|
+
startedAt: toolStartedAt,
|
|
98
|
+
});
|
|
99
|
+
return jsonResponse({ data: rpcResult(id, result) });
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
export const dispatchRpc = async (input) => {
|
|
103
|
+
const { req, body, options, startedAt } = input;
|
|
104
|
+
// Shape-validated in `handlePost` before routing — method is a non-empty string.
|
|
105
|
+
const method = typeof body.method === "string" ? body.method : "";
|
|
106
|
+
// Notifications omit `id`; explicit null is a valid request id (JSON-RPC).
|
|
107
|
+
const id = body.id === undefined ? null : body.id;
|
|
108
|
+
const toolName = method === "tools/call" ? String(body.params?.name ?? "") : undefined;
|
|
109
|
+
// Notifications (including every `notifications/*`) already got 202 in `handlePost`.
|
|
110
|
+
const isPublic = options.publicMethods.has(method);
|
|
111
|
+
let principal = null;
|
|
112
|
+
if (!isPublic) {
|
|
113
|
+
principal = await options.ports.authenticate(req, method, toolName);
|
|
114
|
+
if (!principal) {
|
|
115
|
+
await safeAudit(options, {
|
|
116
|
+
method,
|
|
117
|
+
tool: toolName,
|
|
118
|
+
ok: false,
|
|
119
|
+
error: DENIAL_REASONS.unauthorized,
|
|
120
|
+
durationMs: Date.now() - startedAt,
|
|
121
|
+
});
|
|
122
|
+
return unauthorized(resolveWwwAuthenticate(options, req), undefined, id);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const handler = METHODS[method];
|
|
126
|
+
if (!handler) {
|
|
127
|
+
return jsonResponse({
|
|
128
|
+
data: rpcError(id, JSONRPC_METHOD_NOT_FOUND, `Method not found: ${method}`),
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return handler({ req, body, id, principal, options, startedAt });
|
|
132
|
+
};
|
|
133
|
+
//# sourceMappingURL=rpc-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-handlers.js","sourceRoot":"","sources":["../../src/mcp/rpc-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -42,7 +42,8 @@ type ToolCore = {
|
|
|
42
42
|
description: string;
|
|
43
43
|
/** Advertised via tools/list. Required — see module doc for why. */
|
|
44
44
|
inputSchema: JsonSchema;
|
|
45
|
-
scope
|
|
45
|
+
/** See `ToolDef.scope` — pass `null` to explicitly opt out of a scope check. */
|
|
46
|
+
scope?: string | null | undefined;
|
|
46
47
|
};
|
|
47
48
|
type ToolHandler<TCtx, Args> = (ctx: TCtx, args: Args) => Promise<ToolResult | string> | ToolResult | string;
|
|
48
49
|
type ValidatedInput<Input> = {
|
|
@@ -76,6 +77,24 @@ type ApiExtra<TCtx, Args> = {
|
|
|
76
77
|
*/
|
|
77
78
|
onError?: (res: Response) => Promise<ToolResult | string> | ToolResult | string;
|
|
78
79
|
fetch?: typeof fetch;
|
|
80
|
+
/**
|
|
81
|
+
* Abort the upstream request after this many ms. Default 30000. An
|
|
82
|
+
* upstream that never responds otherwise hangs the tool call (and the
|
|
83
|
+
* MCP request behind it) indefinitely. Pass `false` to disable — the
|
|
84
|
+
* request then relies entirely on whatever signal (if any) is already on
|
|
85
|
+
* the `Request` your `request` callback returns; a timeout firing surfaces
|
|
86
|
+
* as `isError: true`, not a thrown exception `onToolError` would redact.
|
|
87
|
+
*/
|
|
88
|
+
timeoutMs?: number | false;
|
|
89
|
+
/**
|
|
90
|
+
* Cap the upstream response body at this many bytes before it ever
|
|
91
|
+
* reaches `respond`/`onError`/the default error path — an unbounded
|
|
92
|
+
* `res.text()` on an attacker-influenced or merely oversized upstream
|
|
93
|
+
* response is the same memory-exhaustion shape an unbounded *incoming*
|
|
94
|
+
* request body is (see `body.ts`). Default 1 MiB. Exceeding it surfaces
|
|
95
|
+
* as `isError: true`, not a thrown exception `onToolError` would redact.
|
|
96
|
+
*/
|
|
97
|
+
maxResponseBytes?: number;
|
|
79
98
|
};
|
|
80
99
|
export type ApiToolOptions<TCtx, Input = Record<string, unknown>> = (ToolCore & ValidatedInput<Input> & ApiExtra<TCtx, Input>) | (ToolCore & RawInput & ApiExtra<TCtx, Record<string, unknown>>);
|
|
81
100
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"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"}
|