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
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ergonomic tool authoring on top of `ToolDef` — turning "I have an API" into
|
|
3
|
+
* "I have an MCP tool" without hand-rolling validation or fetch plumbing.
|
|
4
|
+
*
|
|
5
|
+
* `inputSchema` (JSON Schema) stays required: it's what `tools/list` actually
|
|
6
|
+
* advertises to clients, and no library-agnostic way exists to derive it from
|
|
7
|
+
* an arbitrary Standard Schema validator. `input`, when supplied, is what
|
|
8
|
+
* actually parses and types `args` at call time — keep the two in sync.
|
|
9
|
+
*/
|
|
10
|
+
import { BodyTooLargeError, DEFAULT_MCP_BODY_LIMIT, readBoundedBytes } from "../http/body.js";
|
|
11
|
+
import { pickDefined } from "../util/defined.js";
|
|
12
|
+
const pathToString = (path) => (path ?? [])
|
|
13
|
+
.map((segment) => segment && typeof segment === "object" && "key" in segment
|
|
14
|
+
? String(segment.key)
|
|
15
|
+
: String(segment))
|
|
16
|
+
.join(".");
|
|
17
|
+
const parseWithSchema = async (schema, raw) => {
|
|
18
|
+
const result = await schema["~standard"].validate(raw);
|
|
19
|
+
const issues = result.issues;
|
|
20
|
+
if (issues?.length) {
|
|
21
|
+
return {
|
|
22
|
+
issues: issues.map((issue) => {
|
|
23
|
+
const path = pathToString(issue.path);
|
|
24
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return { value: result.value };
|
|
29
|
+
};
|
|
30
|
+
const issuesResult = (issues) => ({
|
|
31
|
+
content: [{ type: "text", text: issues.join("; ") }],
|
|
32
|
+
isError: true,
|
|
33
|
+
});
|
|
34
|
+
/**
|
|
35
|
+
* A `ToolDef` with typed, validated `args` — drop straight into
|
|
36
|
+
* `createToolRegistry` / `createMcpApp`'s `tools` array like any other tool.
|
|
37
|
+
*/
|
|
38
|
+
export const defineTool = (options) => ({
|
|
39
|
+
name: options.name,
|
|
40
|
+
description: options.description,
|
|
41
|
+
inputSchema: options.inputSchema,
|
|
42
|
+
...pickDefined(options, ["scope"]),
|
|
43
|
+
handler: async (ctx, rawArgs) => {
|
|
44
|
+
if (!options.input)
|
|
45
|
+
return options.handler(ctx, rawArgs);
|
|
46
|
+
const parsed = await parseWithSchema(options.input, rawArgs);
|
|
47
|
+
if ("issues" in parsed)
|
|
48
|
+
return issuesResult(parsed.issues);
|
|
49
|
+
return options.handler(ctx, parsed.value);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const defaultRespond = (res) => res.text();
|
|
53
|
+
/**
|
|
54
|
+
* Status only — the upstream body is never forwarded by default. This is a
|
|
55
|
+
* return value, not a thrown exception, so `onToolError` redaction never
|
|
56
|
+
* sees it; an upstream error page can echo internal detail, tokens or SQL
|
|
57
|
+
* into the tool result and audit log. Use `onError` to forward (a redacted
|
|
58
|
+
* version of) the body on purpose.
|
|
59
|
+
*/
|
|
60
|
+
const defaultErrorResult = (res) => {
|
|
61
|
+
const status = [res.status, res.statusText].filter(Boolean).join(" ");
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: "text", text: `Request failed: ${status}` }],
|
|
64
|
+
isError: true,
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const DEFAULT_API_TOOL_TIMEOUT_MS = 30_000;
|
|
68
|
+
const isTimeoutError = (caught) => {
|
|
69
|
+
if (!(caught instanceof Error))
|
|
70
|
+
return false;
|
|
71
|
+
return (caught.name === "TimeoutError" ||
|
|
72
|
+
caught.name === "AbortError" ||
|
|
73
|
+
caught.message === "body read aborted");
|
|
74
|
+
};
|
|
75
|
+
const timedOutResult = (timeoutMs) => ({
|
|
76
|
+
content: [{ type: "text", text: `Request timed out after ${timeoutMs}ms` }],
|
|
77
|
+
isError: true,
|
|
78
|
+
});
|
|
79
|
+
/** A timed-out fetch becomes a `ToolResult`; every other throw still propagates. */
|
|
80
|
+
const fetchWithTimeout = async (fetchImpl, input, timeoutMs) => {
|
|
81
|
+
if (timeoutMs === false)
|
|
82
|
+
return { response: await fetchImpl(input) };
|
|
83
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
84
|
+
try {
|
|
85
|
+
return { response: await fetchImpl(input, { signal }), signal };
|
|
86
|
+
}
|
|
87
|
+
catch (caught) {
|
|
88
|
+
if (isTimeoutError(caught))
|
|
89
|
+
return timedOutResult(timeoutMs);
|
|
90
|
+
throw caught;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Capped before `respond`/`onError` ever see it — an unbounded res.text()
|
|
95
|
+
* on the upstream body is the same memory-exhaustion shape an unbounded
|
|
96
|
+
* *incoming* body is (see body.ts). Rebuilt as a fresh Response from the
|
|
97
|
+
* raw bytes — not decoded text — so `respond`/`onError` can still call
|
|
98
|
+
* res.arrayBuffer()/res.blob() on binary bodies as well as res.text()/
|
|
99
|
+
* res.json(), exactly as if they'd read the original response themselves.
|
|
100
|
+
* An oversized body, or the timeout signal firing mid-body, becomes a
|
|
101
|
+
* `ToolResult`; every other throw still propagates.
|
|
102
|
+
*/
|
|
103
|
+
const boundResponse = async (res, maxResponseBytes, timeoutMs, signal) => {
|
|
104
|
+
let bytes;
|
|
105
|
+
try {
|
|
106
|
+
bytes = (await readBoundedBytes(res, maxResponseBytes, signal));
|
|
107
|
+
}
|
|
108
|
+
catch (caught) {
|
|
109
|
+
if (caught instanceof BodyTooLargeError) {
|
|
110
|
+
return {
|
|
111
|
+
content: [{ type: "text", text: `Response exceeded ${maxResponseBytes} bytes` }],
|
|
112
|
+
isError: true,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
if (isTimeoutError(caught))
|
|
116
|
+
return timedOutResult(timeoutMs);
|
|
117
|
+
throw caught;
|
|
118
|
+
}
|
|
119
|
+
// The buffered body is already decoded and re-framed — the upstream
|
|
120
|
+
// encoding/length headers no longer describe it.
|
|
121
|
+
const headers = new Headers(res.headers);
|
|
122
|
+
headers.delete("content-encoding");
|
|
123
|
+
headers.delete("content-length");
|
|
124
|
+
return new Response(bytes, { status: res.status, statusText: res.statusText, headers });
|
|
125
|
+
};
|
|
126
|
+
const runApi = async (options, ctx, args) => {
|
|
127
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
128
|
+
const input = await options.request(ctx, args);
|
|
129
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_API_TOOL_TIMEOUT_MS;
|
|
130
|
+
const fetched = await fetchWithTimeout(fetchImpl, input, timeoutMs);
|
|
131
|
+
if (!("response" in fetched))
|
|
132
|
+
return fetched;
|
|
133
|
+
const maxResponseBytes = options.maxResponseBytes ?? DEFAULT_MCP_BODY_LIMIT;
|
|
134
|
+
const bounded = await boundResponse(fetched.response, maxResponseBytes, timeoutMs, fetched.signal);
|
|
135
|
+
if (!(bounded instanceof Response))
|
|
136
|
+
return bounded;
|
|
137
|
+
if (!bounded.ok)
|
|
138
|
+
return options.onError ? options.onError(bounded) : defaultErrorResult(bounded);
|
|
139
|
+
return (options.respond ?? defaultRespond)(bounded);
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Wrap a single REST call as an MCP tool in one call: build the request from
|
|
143
|
+
* typed args, run it, and shape the response — no hand-rolled fetch/error
|
|
144
|
+
* plumbing per tool. Built on `defineTool`, so `input` gets the same
|
|
145
|
+
* validation-before-call treatment.
|
|
146
|
+
*/
|
|
147
|
+
export const apiTool = (options) => {
|
|
148
|
+
const core = {
|
|
149
|
+
name: options.name,
|
|
150
|
+
description: options.description,
|
|
151
|
+
inputSchema: options.inputSchema,
|
|
152
|
+
...pickDefined(options, ["scope"]),
|
|
153
|
+
};
|
|
154
|
+
if (options.input) {
|
|
155
|
+
return defineTool({
|
|
156
|
+
...core,
|
|
157
|
+
input: options.input,
|
|
158
|
+
handler: (ctx, args) => runApi(options, ctx, args),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return defineTool({
|
|
162
|
+
...core,
|
|
163
|
+
handler: (ctx, args) => runApi(options, ctx, args),
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,16 +7,27 @@ export type JsonSchema = {
|
|
|
7
7
|
items?: JsonSchema;
|
|
8
8
|
minimum?: number;
|
|
9
9
|
maximum?: number;
|
|
10
|
+
/** Only the boolean form is evaluated; a schema-valued form is unsupported. */
|
|
11
|
+
additionalProperties?: boolean | JsonSchema;
|
|
10
12
|
description?: string;
|
|
11
13
|
title?: string;
|
|
12
14
|
[key: string]: unknown;
|
|
13
15
|
};
|
|
16
|
+
export declare const JSON_SCHEMA_TYPES: {
|
|
17
|
+
readonly string: "string";
|
|
18
|
+
readonly number: "number";
|
|
19
|
+
readonly integer: "integer";
|
|
20
|
+
readonly boolean: "boolean";
|
|
21
|
+
readonly object: "object";
|
|
22
|
+
readonly array: "array";
|
|
23
|
+
readonly null: "null";
|
|
24
|
+
};
|
|
14
25
|
/** Keywords `validateAgainstSchema` actually evaluates. */
|
|
15
26
|
export declare const SUPPORTED_SCHEMA_KEYWORDS: ReadonlySet<string>;
|
|
16
27
|
/**
|
|
17
28
|
* Allowed on inputSchema but never evaluated (tools/list / generator metadata).
|
|
18
|
-
* Semantically-enforcing keywords (`pattern`, `
|
|
19
|
-
*
|
|
29
|
+
* Semantically-enforcing keywords (`pattern`, `oneOf`, …) are not ignored —
|
|
30
|
+
* with `validateArgs: true` they throw at construction.
|
|
20
31
|
*/
|
|
21
32
|
export declare const IGNORED_SCHEMA_KEYWORDS: ReadonlySet<string>;
|
|
22
33
|
/**
|
|
@@ -26,27 +37,13 @@ export declare const IGNORED_SCHEMA_KEYWORDS: ReadonlySet<string>;
|
|
|
26
37
|
export declare const unsupportedKeywords: (schema: JsonSchema, path?: string) => string[];
|
|
27
38
|
/**
|
|
28
39
|
* Paths where `properties`/`required` implies an object shape but `type`
|
|
29
|
-
* doesn't say so — absent, or a
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* `properties`/`items` the same way `unsupportedKeywords` does. Under
|
|
35
|
-
* `validateArgs: true` this throws at construction instead of silently
|
|
36
|
-
* validating nothing at runtime — `validateNode`'s dispatch-by-actual-type
|
|
37
|
-
* (see below) is the separate runtime backstop for schemas that reach
|
|
38
|
-
* validation unchecked (e.g. loaded from outside the tool definition).
|
|
40
|
+
* doesn't say so — absent, or a type/union without `"object"`. Such a
|
|
41
|
+
* schema would otherwise accept any input, `{}` included. Under
|
|
42
|
+
* `validateArgs: true` this throws at construction; `validateNode`'s
|
|
43
|
+
* dispatch on the value's runtime type is the backstop for schemas that
|
|
44
|
+
* reach validation unchecked.
|
|
39
45
|
*/
|
|
40
46
|
export declare const missingObjectType: (schema: JsonSchema, path?: string) => string[];
|
|
41
|
-
export declare const JSON_SCHEMA_TYPES: {
|
|
42
|
-
readonly string: "string";
|
|
43
|
-
readonly number: "number";
|
|
44
|
-
readonly integer: "integer";
|
|
45
|
-
readonly boolean: "boolean";
|
|
46
|
-
readonly object: "object";
|
|
47
|
-
readonly array: "array";
|
|
48
|
-
readonly null: "null";
|
|
49
|
-
};
|
|
50
47
|
/** Minimal JSON Schema subset used by MCP tool inputSchema. Empty = valid. */
|
|
51
48
|
export declare const validateAgainstSchema: (value: unknown, schema: JsonSchema) => string[];
|
|
52
49
|
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/mcp/validate.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,oBAAoB,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,iBAAiB;;;;;;;;CAQpB,CAAC;AAkBX,2DAA2D;AAC3D,eAAO,MAAM,yBAAyB,EAAE,WAAW,CAAC,MAAM,CASxD,CAAC;AAUH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAYtD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,QAAQ,UAAU,EAAE,aAAa,KAAG,MAAM,EAU7E,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,UAAU,EAAE,aAAa,KAAG,MAAM,EAS3E,CAAC;AAiHF,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,EAAE,QAAQ,UAAU,KAAG,MAAM,EAC5C,CAAC"}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
export const JSON_SCHEMA_TYPES = {
|
|
2
|
+
string: "string",
|
|
3
|
+
number: "number",
|
|
4
|
+
integer: "integer",
|
|
5
|
+
boolean: "boolean",
|
|
6
|
+
object: "object",
|
|
7
|
+
array: "array",
|
|
8
|
+
null: "null",
|
|
9
|
+
};
|
|
10
|
+
/** `schema.type` as an array either way — a single type is a union of one. */
|
|
11
|
+
const declaredTypesOf = (schema) => schema.type ? (Array.isArray(schema.type) ? schema.type : [schema.type]) : [];
|
|
12
|
+
/**
|
|
13
|
+
* Direct subschemas the walkers below descend into (`properties.*`, `items`),
|
|
14
|
+
* each with the path it is reported under.
|
|
15
|
+
*/
|
|
16
|
+
const childSchemas = (schema, path) => [
|
|
17
|
+
...Object.entries(schema.properties ?? {}).map(([key, child]) => [
|
|
18
|
+
child,
|
|
19
|
+
`${path}.properties.${key}`,
|
|
20
|
+
]),
|
|
21
|
+
...(schema.items ? [[schema.items, `${path}.items`]] : []),
|
|
22
|
+
];
|
|
1
23
|
/** Keywords `validateAgainstSchema` actually evaluates. */
|
|
2
24
|
export const SUPPORTED_SCHEMA_KEYWORDS = new Set([
|
|
3
25
|
"type",
|
|
@@ -7,11 +29,17 @@ export const SUPPORTED_SCHEMA_KEYWORDS = new Set([
|
|
|
7
29
|
"items",
|
|
8
30
|
"minimum",
|
|
9
31
|
"maximum",
|
|
32
|
+
"additionalProperties",
|
|
10
33
|
]);
|
|
34
|
+
/** Supported only in these value shapes; any other value is reported as unsupported. */
|
|
35
|
+
const SUPPORTED_KEYWORD_VALUES = {
|
|
36
|
+
additionalProperties: (value) => typeof value === "boolean",
|
|
37
|
+
};
|
|
38
|
+
const isSupportedKeyword = (key, value) => SUPPORTED_SCHEMA_KEYWORDS.has(key) && (SUPPORTED_KEYWORD_VALUES[key]?.(value) ?? true);
|
|
11
39
|
/**
|
|
12
40
|
* Allowed on inputSchema but never evaluated (tools/list / generator metadata).
|
|
13
|
-
* Semantically-enforcing keywords (`pattern`, `
|
|
14
|
-
*
|
|
41
|
+
* Semantically-enforcing keywords (`pattern`, `oneOf`, …) are not ignored —
|
|
42
|
+
* with `validateArgs: true` they throw at construction.
|
|
15
43
|
*/
|
|
16
44
|
export const IGNORED_SCHEMA_KEYWORDS = new Set([
|
|
17
45
|
"description",
|
|
@@ -31,46 +59,27 @@ export const IGNORED_SCHEMA_KEYWORDS = new Set([
|
|
|
31
59
|
* Walks nested `properties` / `items`.
|
|
32
60
|
*/
|
|
33
61
|
export const unsupportedKeywords = (schema, path = "args") => {
|
|
34
|
-
const local = Object.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return [...local, ...fromProperties, ...fromItems];
|
|
62
|
+
const local = Object.entries(schema).flatMap(([key, value]) => isSupportedKeyword(key, value) || IGNORED_SCHEMA_KEYWORDS.has(key) ? [] : [`${path}.${key}`]);
|
|
63
|
+
return [
|
|
64
|
+
...local,
|
|
65
|
+
...childSchemas(schema, path).flatMap(([child, childPath]) => unsupportedKeywords(child, childPath)),
|
|
66
|
+
];
|
|
40
67
|
};
|
|
41
68
|
/**
|
|
42
69
|
* Paths where `properties`/`required` implies an object shape but `type`
|
|
43
|
-
* doesn't say so — absent, or a
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* `properties`/`items` the same way `unsupportedKeywords` does. Under
|
|
49
|
-
* `validateArgs: true` this throws at construction instead of silently
|
|
50
|
-
* validating nothing at runtime — `validateNode`'s dispatch-by-actual-type
|
|
51
|
-
* (see below) is the separate runtime backstop for schemas that reach
|
|
52
|
-
* validation unchecked (e.g. loaded from outside the tool definition).
|
|
70
|
+
* doesn't say so — absent, or a type/union without `"object"`. Such a
|
|
71
|
+
* schema would otherwise accept any input, `{}` included. Under
|
|
72
|
+
* `validateArgs: true` this throws at construction; `validateNode`'s
|
|
73
|
+
* dispatch on the value's runtime type is the backstop for schemas that
|
|
74
|
+
* reach validation unchecked.
|
|
53
75
|
*/
|
|
54
76
|
export const missingObjectType = (schema, path = "args") => {
|
|
55
|
-
const declaredTypes = schema.type
|
|
56
|
-
? Array.isArray(schema.type)
|
|
57
|
-
? schema.type
|
|
58
|
-
: [schema.type]
|
|
59
|
-
: [];
|
|
60
77
|
const impliesObject = schema.properties !== undefined || schema.required !== undefined;
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
export const JSON_SCHEMA_TYPES = {
|
|
67
|
-
string: "string",
|
|
68
|
-
number: "number",
|
|
69
|
-
integer: "integer",
|
|
70
|
-
boolean: "boolean",
|
|
71
|
-
object: "object",
|
|
72
|
-
array: "array",
|
|
73
|
-
null: "null",
|
|
78
|
+
const declaresObject = declaredTypesOf(schema).includes(JSON_SCHEMA_TYPES.object);
|
|
79
|
+
return [
|
|
80
|
+
...(impliesObject && !declaresObject ? [path] : []),
|
|
81
|
+
...childSchemas(schema, path).flatMap(([child, childPath]) => missingObjectType(child, childPath)),
|
|
82
|
+
];
|
|
74
83
|
};
|
|
75
84
|
const runtimeTypeOf = (value) => {
|
|
76
85
|
if (value === null)
|
|
@@ -83,8 +92,6 @@ const TYPE_MATCHERS = {
|
|
|
83
92
|
[JSON_SCHEMA_TYPES.integer]: (actual, value) => actual === JSON_SCHEMA_TYPES.number && Number.isInteger(value),
|
|
84
93
|
};
|
|
85
94
|
const matchesDeclaredType = (actual, value, declared) => TYPE_MATCHERS[declared]?.(actual, value) ?? actual === declared;
|
|
86
|
-
/** `schema.type` as an array either way — a single type is a union of one. */
|
|
87
|
-
const declaredTypesOf = (schema) => schema.type ? (Array.isArray(schema.type) ? schema.type : [schema.type]) : [];
|
|
88
95
|
const typeMismatch = (value, schema, path) => {
|
|
89
96
|
const declared = declaredTypesOf(schema);
|
|
90
97
|
if (declared.length === 0)
|
|
@@ -124,13 +131,24 @@ const objectErrors = (value, schema, path) => {
|
|
|
124
131
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
125
132
|
return [];
|
|
126
133
|
const record = value;
|
|
134
|
+
// `Object.hasOwn`, not `key in record` — `in` also matches inherited
|
|
135
|
+
// Object.prototype members (`toString`, `constructor`, `hasOwnProperty`,
|
|
136
|
+
// …), so `required: ["toString"]` would pass against `{}` and a
|
|
137
|
+
// `properties: { constructor: {...} }` entry would validate against the
|
|
138
|
+
// prototype's own `constructor`, neither of which is an actual argument
|
|
139
|
+
// the caller supplied.
|
|
127
140
|
const requiredErrors = (schema.required ?? [])
|
|
128
|
-
.filter((key) => !(key
|
|
141
|
+
.filter((key) => !Object.hasOwn(record, key))
|
|
129
142
|
.map((key) => `${path}.${key}: required`);
|
|
130
143
|
const propertyErrors = Object.entries(schema.properties ?? {})
|
|
131
|
-
.filter(([key]) => key
|
|
144
|
+
.filter(([key]) => Object.hasOwn(record, key))
|
|
132
145
|
.flatMap(([key, propSchema]) => validateNode(record[key], propSchema, `${path}.${key}`));
|
|
133
|
-
|
|
146
|
+
const extraErrors = schema.additionalProperties === false
|
|
147
|
+
? Object.keys(record)
|
|
148
|
+
.filter((key) => !Object.hasOwn(schema.properties ?? {}, key))
|
|
149
|
+
.map((key) => `${path}.${key}: not allowed`)
|
|
150
|
+
: [];
|
|
151
|
+
return [...requiredErrors, ...propertyErrors, ...extraErrors];
|
|
134
152
|
};
|
|
135
153
|
const arrayErrors = (value, schema, path) => {
|
|
136
154
|
const items = schema.items;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/mcp/validate.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;CACJ,CAAC;AAEX,8EAA8E;AAC9E,MAAM,eAAe,GAAG,CAAC,MAAkB,EAAY,EAAE,CACvD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAEhF;;;GAGG;AACH,MAAM,YAAY,GAAG,CAAC,MAAkB,EAAE,IAAY,EAA+B,EAAE,CAAC;IACtF,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAwB,EAAE,CAAC;QACrF,KAAK;QACL,GAAG,IAAI,eAAe,GAAG,EAAE;KAC5B,CAAC;IACF,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,IAAI,QAAQ,CAAyB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;CACnF,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,yBAAyB,GAAwB,IAAI,GAAG,CAAC;IACpE,MAAM;IACN,MAAM;IACN,UAAU;IACV,YAAY;IACZ,OAAO;IACP,SAAS;IACT,SAAS;IACT,sBAAsB;CACvB,CAAC,CAAC;AAEH,wFAAwF;AACxF,MAAM,wBAAwB,GAAgD;IAC5E,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,SAAS;CAC5D,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAE,KAAc,EAAW,EAAE,CAClE,yBAAyB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;AAEzF;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAC;IAClE,aAAa;IACb,OAAO;IACP,SAAS;IACT,KAAK;IACL,UAAU;IACV,SAAS;IACT,UAAU;IACV,YAAY;IACZ,UAAU;IACV,WAAW;IACX,QAAQ;CACT,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAkB,EAAE,IAAI,GAAG,MAAM,EAAY,EAAE;IACjF,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAC5D,kBAAkB,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,uBAAuB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAC7F,CAAC;IACF,OAAO;QACL,GAAG,KAAK;QACR,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,CAC3D,mBAAmB,CAAC,KAAK,EAAE,SAAS,CAAC,CACtC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAkB,EAAE,IAAI,GAAG,MAAM,EAAY,EAAE;IAC/E,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC;IACvF,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAClF,OAAO;QACL,GAAG,CAAC,aAAa,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,GAAG,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,CAC3D,iBAAiB,CAAC,KAAK,EAAE,SAAS,CAAC,CACpC;KACF,CAAC;AACJ,CAAC,CAAC;AAIF,MAAM,aAAa,GAAG,CAAC,KAAc,EAAU,EAAE;IAC/C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,iBAAiB,CAAC,IAAI,CAAC;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,iBAAiB,CAAC,KAAK,CAAC;IACzD,OAAO,OAAO,KAAK,CAAC;AACtB,CAAC,CAAC;AAEF,MAAM,aAAa,GAAgE;IACjF,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAC7C,MAAM,KAAK,iBAAiB,CAAC,MAAM,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC;CACjE,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,MAAc,EAAE,KAAc,EAAE,QAAgB,EAAW,EAAE,CACxF,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,KAAK,QAAQ,CAAC;AAElE,MAAM,YAAY,GAAG,CAAC,KAAc,EAAE,MAAkB,EAAE,IAAY,EAAY,EAAE;IAClF,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAClF,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,cAAc,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;AACrF,CAAC,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,KAAc,EAAE,MAAkB,EAAE,IAAY,EAAY,EAAE;IAChF,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3D,OAAO,CAAC,GAAG,IAAI,qBAAqB,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,YAAY,GAIb;IACH;QACE,GAAG,EAAE,SAAS;QACd,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK;QACtC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,IAAI,mBAAmB,KAAK,EAAE;KAC5D;IACD;QACE,GAAG,EAAE,SAAS;QACd,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK;QACtC,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,IAAI,mBAAmB,KAAK,EAAE;KAC5D;CACF,CAAC;AAEF,MAAM,WAAW,GAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IACvD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;QACtD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC;QAC3D,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,YAAY,GAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IACxD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3E,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,qEAAqE;IACrE,yEAAyE;IACzE,gEAAgE;IAChE,wEAAwE;IACxE,wEAAwE;IACxE,uBAAuB;IACvB,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;SAC3C,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;SAC5C,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,GAAG,YAAY,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;SAC3D,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;SAC7C,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC;IAC3F,MAAM,WAAW,GACf,MAAM,CAAC,oBAAoB,KAAK,KAAK;QACnC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;aAChB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;aAC7D,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,GAAG,eAAe,CAAC;QAChD,CAAC,CAAC,EAAE,CAAC;IACT,OAAO,CAAC,GAAG,cAAc,EAAE,GAAG,cAAc,EAAE,GAAG,WAAW,CAAC,CAAC;AAChE,CAAC,CAAC;AAEF,MAAM,WAAW,GAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;IACvD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAC/C,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;AAC1F,CAAC,CAAC;AAEF,MAAM,aAAa,GAAgC;IACjD,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,YAAY;IACxC,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,WAAW;IACtC,CAAC,iBAAiB,CAAC,MAAM,CAAC,EAAE,WAAW;IACvC,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,WAAW;CACzC,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,KAAc,EAAE,MAAkB,EAAE,IAAY,EAAY,EAAE;IAClF,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACrD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC;IAC7C,mEAAmE;IACnE,wEAAwE;IACxE,uEAAuE;IACvE,wEAAwE;IACxE,oEAAoE;IACpE,kEAAkE;IAClE,sDAAsD;IACtD,sEAAsE;IACtE,4DAA4D;IAC5D,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO;QACL,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC;QAClC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;KACxD,CAAC;AACJ,CAAC,CAAC;AAEF,8EAA8E;AAC9E,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,KAAc,EAAE,MAAkB,EAAY,EAAE,CACpF,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke `ports.audit`, swallowing any failure and racing it against
|
|
3
|
+
* `auditTimeoutMs` — the OAuth-side twin of `methods.ts`'s `safeAudit`.
|
|
4
|
+
* A hanging sink must not stall `/token` or `/authorize` error responses.
|
|
5
|
+
*/
|
|
6
|
+
import type { OAuthAuditEntry, OAuthRouterOptions } from "./types.js";
|
|
7
|
+
export declare const safeOAuthAudit: (options: OAuthRouterOptions, entry: OAuthAuditEntry) => Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Fires `legacy_code_secret_used` when a value was recovered with a
|
|
10
|
+
* rotation-array entry other than the primary (`keyIndex > 0`) — the signal
|
|
11
|
+
* every codeSecret rotation site (`/authorize`, `/consent`, `/token`,
|
|
12
|
+
* `firstClientAuthError`) uses to tell an operator which old keys are still
|
|
13
|
+
* live. A no-op for the common case (`keyIndex === 0`, or none at all).
|
|
14
|
+
*/
|
|
15
|
+
export declare const auditLegacyKeyUsage: (options: OAuthRouterOptions, usage: {
|
|
16
|
+
clientId: string;
|
|
17
|
+
keyIndex: number | null | undefined;
|
|
18
|
+
source: string;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/oauth/audit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEtE,eAAO,MAAM,cAAc,GACzB,SAAS,kBAAkB,EAC3B,OAAO,eAAe,KACrB,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,kBAAkB,EAC3B,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KAC/E,OAAO,CAAC,IAAI,CAOd,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke `ports.audit`, swallowing any failure and racing it against
|
|
3
|
+
* `auditTimeoutMs` — the OAuth-side twin of `methods.ts`'s `safeAudit`.
|
|
4
|
+
* A hanging sink must not stall `/token` or `/authorize` error responses.
|
|
5
|
+
*/
|
|
6
|
+
import { DEFAULT_AUDIT_TIMEOUT_MS, runAuditBounded } from "../http/http.js";
|
|
7
|
+
export const safeOAuthAudit = async (options, entry) => {
|
|
8
|
+
const audit = options.ports.audit;
|
|
9
|
+
if (!audit)
|
|
10
|
+
return;
|
|
11
|
+
await runAuditBounded(() => audit(entry), options.auditTimeoutMs ?? DEFAULT_AUDIT_TIMEOUT_MS);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Fires `legacy_code_secret_used` when a value was recovered with a
|
|
15
|
+
* rotation-array entry other than the primary (`keyIndex > 0`) — the signal
|
|
16
|
+
* every codeSecret rotation site (`/authorize`, `/consent`, `/token`,
|
|
17
|
+
* `firstClientAuthError`) uses to tell an operator which old keys are still
|
|
18
|
+
* live. A no-op for the common case (`keyIndex === 0`, or none at all).
|
|
19
|
+
*/
|
|
20
|
+
export const auditLegacyKeyUsage = async (options, usage) => {
|
|
21
|
+
if (!usage.keyIndex)
|
|
22
|
+
return;
|
|
23
|
+
await safeOAuthAudit(options, {
|
|
24
|
+
event: "legacy_code_secret_used",
|
|
25
|
+
clientId: usage.clientId,
|
|
26
|
+
reason: `${usage.source} with codeSecret[${usage.keyIndex}]`,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/oauth/audit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG5E,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,OAA2B,EAC3B,KAAsB,EACP,EAAE;IACjB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO;IAEnB,MAAM,eAAe,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC,CAAC;AAChG,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,OAA2B,EAC3B,KAAgF,EACjE,EAAE;IACjB,IAAI,CAAC,KAAK,CAAC,QAAQ;QAAE,OAAO;IAC5B,MAAM,cAAc,CAAC,OAAO,EAAE;QAC5B,KAAK,EAAE,yBAAyB;QAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,oBAAoB,KAAK,CAAC,QAAQ,GAAG;KAC7D,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd-parse.d.ts","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd-parse.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,GAAG,IAAI,KAAG,MAGxD,CAAC;AAoCF,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,KAAG,YAsB9D,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isJsonObject } from "../../util/json.js";
|
|
2
|
+
import { MAX_CLIENT_REDIRECT_URIS } from "../constants.js";
|
|
3
|
+
import { isLoopbackHostname } from "./cimd-ssrf.js";
|
|
4
|
+
const DEFAULT_CACHE_SECONDS = 300;
|
|
5
|
+
const MAX_CACHE_SECONDS = 3600;
|
|
6
|
+
export const parseCacheMaxAge = (header) => {
|
|
7
|
+
const maxAge = header ? /(?:^|,)\s*max-age=(\d+)/i.exec(header)?.[1] : undefined;
|
|
8
|
+
return maxAge === undefined ? DEFAULT_CACHE_SECONDS : Math.min(Number(maxAge), MAX_CACHE_SECONDS);
|
|
9
|
+
};
|
|
10
|
+
const assertHttpRedirectUri = (uri) => {
|
|
11
|
+
let parsed;
|
|
12
|
+
try {
|
|
13
|
+
parsed = new URL(uri);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error("CIMD redirect_uris entries must be absolute URLs");
|
|
17
|
+
}
|
|
18
|
+
if (parsed.username || parsed.password || parsed.hash) {
|
|
19
|
+
throw new Error("CIMD redirect_uris must not carry credentials or a fragment");
|
|
20
|
+
}
|
|
21
|
+
if (parsed.protocol === "https:")
|
|
22
|
+
return;
|
|
23
|
+
if (parsed.protocol === "http:") {
|
|
24
|
+
if (!isLoopbackHostname(parsed.hostname)) {
|
|
25
|
+
throw new Error("CIMD http redirect_uris must be loopback (127.0.0.1 / ::1)");
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
throw new Error("CIMD redirect_uris must be http(s)");
|
|
30
|
+
};
|
|
31
|
+
const parseRedirectUris = (rawUris) => {
|
|
32
|
+
if (!Array.isArray(rawUris) || rawUris.length === 0) {
|
|
33
|
+
throw new Error("CIMD document must include a non-empty redirect_uris array");
|
|
34
|
+
}
|
|
35
|
+
if (rawUris.length > MAX_CLIENT_REDIRECT_URIS) {
|
|
36
|
+
throw new Error(`CIMD redirect_uris must have at most ${MAX_CLIENT_REDIRECT_URIS} entries`);
|
|
37
|
+
}
|
|
38
|
+
return rawUris.map((u) => {
|
|
39
|
+
if (typeof u !== "string" || !u)
|
|
40
|
+
throw new Error("CIMD redirect_uris entries must be strings");
|
|
41
|
+
assertHttpRedirectUri(u);
|
|
42
|
+
return u;
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
export const parseDocument = (clientId, text) => {
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error("CIMD document is not JSON");
|
|
52
|
+
}
|
|
53
|
+
if (!isJsonObject(parsed))
|
|
54
|
+
throw new Error("CIMD document must be a JSON object");
|
|
55
|
+
const record = parsed;
|
|
56
|
+
if (record.client_id !== clientId) {
|
|
57
|
+
throw new Error("CIMD document client_id must equal the request URL exactly");
|
|
58
|
+
}
|
|
59
|
+
const authMethod = record.token_endpoint_auth_method;
|
|
60
|
+
if (authMethod !== undefined && authMethod !== "none") {
|
|
61
|
+
throw new Error("CIMD clients must be public (token_endpoint_auth_method none)");
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
client_id: clientId,
|
|
65
|
+
redirect_uris: parseRedirectUris(record.redirect_uris),
|
|
66
|
+
...(typeof record.client_name === "string" ? { client_name: record.client_name } : {}),
|
|
67
|
+
token_endpoint_auth_method: "none",
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=cimd-parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd-parse.js","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd-parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guards for CIMD URL fetch: hostname normalization, private/NAT64
|
|
3
|
+
* IP blocks, and DNS lookup before connect.
|
|
4
|
+
*/
|
|
5
|
+
export declare const isLoopbackHostname: (hostname: string) => boolean;
|
|
6
|
+
export declare const defaultLookup: (hostname: string) => Promise<string[]>;
|
|
7
|
+
export declare const assertSafeUrl: (url: URL, lookup: (hostname: string) => Promise<string[]>) => Promise<void>;
|
|
8
|
+
//# sourceMappingURL=cimd-ssrf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd-ssrf.d.ts","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd-ssrf.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoLH,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,OAEL,CAAC;AAElD,eAAO,MAAM,aAAa,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CAQtE,CAAC;AAiBF,eAAO,MAAM,aAAa,GACxB,KAAK,GAAG,EACR,QAAQ,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,KAC9C,OAAO,CAAC,IAAI,CASd,CAAC"}
|