mcp-trellis 0.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/LICENSE +21 -0
- package/README.md +595 -0
- package/dist/adapters/node.d.ts +47 -0
- package/dist/adapters/node.d.ts.map +1 -0
- package/dist/adapters/node.js +116 -0
- package/dist/adapters/node.js.map +1 -0
- package/dist/app.d.ts +69 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +101 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/bearer.d.ts +21 -0
- package/dist/auth/bearer.d.ts.map +1 -0
- package/dist/auth/bearer.js +32 -0
- package/dist/auth/bearer.js.map +1 -0
- package/dist/clients.d.ts +33 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +63 -0
- package/dist/clients.js.map +1 -0
- package/dist/dispatch.d.ts +7 -0
- package/dist/dispatch.d.ts.map +1 -0
- package/dist/dispatch.js +79 -0
- package/dist/dispatch.js.map +1 -0
- package/dist/http.d.ts +17 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +38 -0
- package/dist/http.js.map +1 -0
- package/dist/jsonrpc.d.ts +32 -0
- package/dist/jsonrpc.d.ts.map +1 -0
- package/dist/jsonrpc.js +17 -0
- package/dist/jsonrpc.js.map +1 -0
- package/dist/methods.d.ts +52 -0
- package/dist/methods.d.ts.map +1 -0
- package/dist/methods.js +114 -0
- package/dist/methods.js.map +1 -0
- package/dist/oauth/authorize.d.ts +3 -0
- package/dist/oauth/authorize.d.ts.map +1 -0
- package/dist/oauth/authorize.js +109 -0
- package/dist/oauth/authorize.js.map +1 -0
- package/dist/oauth/base64url.d.ts +5 -0
- package/dist/oauth/base64url.d.ts.map +1 -0
- package/dist/oauth/base64url.js +17 -0
- package/dist/oauth/base64url.js.map +1 -0
- package/dist/oauth/clientauth.d.ts +19 -0
- package/dist/oauth/clientauth.d.ts.map +1 -0
- package/dist/oauth/clientauth.js +76 -0
- package/dist/oauth/clientauth.js.map +1 -0
- package/dist/oauth/codes.d.ts +30 -0
- package/dist/oauth/codes.d.ts.map +1 -0
- package/dist/oauth/codes.js +93 -0
- package/dist/oauth/codes.js.map +1 -0
- package/dist/oauth/constants.d.ts +32 -0
- package/dist/oauth/constants.d.ts.map +1 -0
- package/dist/oauth/constants.js +30 -0
- package/dist/oauth/constants.js.map +1 -0
- package/dist/oauth/metadata.d.ts +45 -0
- package/dist/oauth/metadata.d.ts.map +1 -0
- package/dist/oauth/metadata.js +36 -0
- package/dist/oauth/metadata.js.map +1 -0
- package/dist/oauth/pkce.d.ts +4 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/pkce.js +17 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/oauth/redirect.d.ts +11 -0
- package/dist/oauth/redirect.d.ts.map +1 -0
- package/dist/oauth/redirect.js +26 -0
- package/dist/oauth/redirect.js.map +1 -0
- package/dist/oauth/resource.d.ts +7 -0
- package/dist/oauth/resource.d.ts.map +1 -0
- package/dist/oauth/resource.js +44 -0
- package/dist/oauth/resource.js.map +1 -0
- package/dist/oauth/revoke.d.ts +4 -0
- package/dist/oauth/revoke.d.ts.map +1 -0
- package/dist/oauth/revoke.js +30 -0
- package/dist/oauth/revoke.js.map +1 -0
- package/dist/oauth/router.d.ts +18 -0
- package/dist/oauth/router.d.ts.map +1 -0
- package/dist/oauth/router.js +105 -0
- package/dist/oauth/router.js.map +1 -0
- package/dist/oauth/scope.d.ts +11 -0
- package/dist/oauth/scope.d.ts.map +1 -0
- package/dist/oauth/scope.js +24 -0
- package/dist/oauth/scope.js.map +1 -0
- package/dist/oauth/token.d.ts +3 -0
- package/dist/oauth/token.d.ts.map +1 -0
- package/dist/oauth/token.js +137 -0
- package/dist/oauth/token.js.map +1 -0
- package/dist/oauth/types.d.ts +115 -0
- package/dist/oauth/types.d.ts.map +1 -0
- package/dist/oauth/types.js +28 -0
- package/dist/oauth/types.js.map +1 -0
- package/dist/oauth/wellknown.d.ts +8 -0
- package/dist/oauth/wellknown.d.ts.map +1 -0
- package/dist/oauth/wellknown.js +35 -0
- package/dist/oauth/wellknown.js.map +1 -0
- package/dist/protocol.d.ts +4 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +14 -0
- package/dist/protocol.js.map +1 -0
- package/dist/registry.d.ts +37 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +61 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +11 -0
- package/dist/server.js.map +1 -0
- package/dist/tools.d.ts +80 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +97 -0
- package/dist/tools.js.map +1 -0
- package/dist/validate.d.ts +23 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +88 -0
- package/dist/validate.js.map +1 -0
- package/docs/diagrams/architecture.svg +1 -0
- package/docs/diagrams/first-connection.svg +1 -0
- package/docs/diagrams/ports.svg +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node HTTP `(req, res)` adapter — Express, Cloud Functions, `http.createServer`, etc.
|
|
3
|
+
* Bridges duck-typed Node request/response objects to Web `Request`/`Response`.
|
|
4
|
+
*/
|
|
5
|
+
import { INTERNAL_ERROR } from "../http.js";
|
|
6
|
+
const headerValue = (value) => (Array.isArray(value) ? value[0] : value);
|
|
7
|
+
/** Last comma-separated hop (closest trusted proxy). */
|
|
8
|
+
const lastForwarded = (value) => value?.split(",").pop()?.trim();
|
|
9
|
+
/** Derive origin from Host, or from forwarded headers when trustProxy. */
|
|
10
|
+
export const resolveOrigin = (req, options = {}) => {
|
|
11
|
+
const trustProxy = options.trustProxy === true;
|
|
12
|
+
const proto = trustProxy
|
|
13
|
+
? lastForwarded(headerValue(req.headers["x-forwarded-proto"])) || "https"
|
|
14
|
+
: "https";
|
|
15
|
+
const host = trustProxy
|
|
16
|
+
? lastForwarded(headerValue(req.headers["x-forwarded-host"])) ||
|
|
17
|
+
headerValue(req.headers.host)
|
|
18
|
+
: headerValue(req.headers.host);
|
|
19
|
+
if (!host) {
|
|
20
|
+
throw new Error("Cannot resolve origin: pass options.origin or set Host" +
|
|
21
|
+
(trustProxy ? " / X-Forwarded-Host" : ""));
|
|
22
|
+
}
|
|
23
|
+
return `${proto}://${host}`;
|
|
24
|
+
};
|
|
25
|
+
const BODY_BUILDERS = [
|
|
26
|
+
{
|
|
27
|
+
match: (body) => body === undefined || body === null,
|
|
28
|
+
build: () => undefined,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
match: (body) => typeof body === "string",
|
|
32
|
+
build: (body) => body,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
match: (body) => body instanceof Uint8Array,
|
|
36
|
+
build: (body) => {
|
|
37
|
+
const bytes = body;
|
|
38
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
const toBodyInit = (body) => {
|
|
43
|
+
const builder = BODY_BUILDERS.find((entry) => entry.match(body));
|
|
44
|
+
return builder ? builder.build(body) : JSON.stringify(body);
|
|
45
|
+
};
|
|
46
|
+
export const toWebRequest = (req, options) => {
|
|
47
|
+
const path = req.url ?? "/";
|
|
48
|
+
const url = path.startsWith("http") ? path : `${options.origin}${path}`;
|
|
49
|
+
const headers = new Headers(Object.entries(req.headers)
|
|
50
|
+
.map(([key, value]) => {
|
|
51
|
+
const resolved = headerValue(value);
|
|
52
|
+
return resolved === undefined ? null : [key, resolved];
|
|
53
|
+
})
|
|
54
|
+
.filter((entry) => entry !== null));
|
|
55
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
56
|
+
if (method === "GET" || method === "HEAD") {
|
|
57
|
+
return new Request(url, { method, headers });
|
|
58
|
+
}
|
|
59
|
+
const body = options.body !== undefined ? options.body : req.body;
|
|
60
|
+
return new Request(url, {
|
|
61
|
+
method,
|
|
62
|
+
headers,
|
|
63
|
+
body: toBodyInit(body),
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const isAsyncIterable = (value) => typeof value?.[Symbol.asyncIterator] === "function";
|
|
67
|
+
const concatChunks = (chunks) => {
|
|
68
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
|
|
69
|
+
const out = new Uint8Array(total);
|
|
70
|
+
chunks.reduce((offset, chunk) => {
|
|
71
|
+
out.set(chunk, offset);
|
|
72
|
+
return offset + chunk.byteLength;
|
|
73
|
+
}, 0);
|
|
74
|
+
return out;
|
|
75
|
+
};
|
|
76
|
+
/** Prefer `req.body`; else drain the stream (raw `http.createServer`). */
|
|
77
|
+
export const readNodeBody = async (req) => {
|
|
78
|
+
if (req.body !== undefined && req.body !== null)
|
|
79
|
+
return req.body;
|
|
80
|
+
if (!isAsyncIterable(req))
|
|
81
|
+
return undefined;
|
|
82
|
+
const chunks = [];
|
|
83
|
+
for await (const chunk of req) {
|
|
84
|
+
chunks.push(typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk);
|
|
85
|
+
}
|
|
86
|
+
return chunks.length > 0 ? concatChunks(chunks) : undefined;
|
|
87
|
+
};
|
|
88
|
+
export const sendWebResponse = async (res, response) => {
|
|
89
|
+
res.statusCode = response.status;
|
|
90
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
91
|
+
res.end(new Uint8Array(await response.arrayBuffer()));
|
|
92
|
+
};
|
|
93
|
+
/** Always answer — an unhandled rejection on `http.createServer` hangs the client. */
|
|
94
|
+
const sendInternalError = (res) => {
|
|
95
|
+
res.statusCode = 500;
|
|
96
|
+
res.setHeader("Content-Type", "application/json");
|
|
97
|
+
res.end(JSON.stringify({ error: INTERNAL_ERROR }));
|
|
98
|
+
};
|
|
99
|
+
/** One-liner: Node `(req, res)` → MCP / OAuth Web handler. */
|
|
100
|
+
export const asNodeHandler = (mcp, options) => {
|
|
101
|
+
if (!options?.origin && options?.trustProxy !== true) {
|
|
102
|
+
throw new Error("asNodeHandler requires options.origin or options.trustProxy: true");
|
|
103
|
+
}
|
|
104
|
+
return async (req, res) => {
|
|
105
|
+
try {
|
|
106
|
+
const origin = options.origin ?? resolveOrigin(req, { trustProxy: true });
|
|
107
|
+
const body = await readNodeBody(req);
|
|
108
|
+
const request = toWebRequest(req, { origin, body });
|
|
109
|
+
await sendWebResponse(res, await mcp.fetch(request));
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
sendInternalError(res);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/adapters/node.ts"],"names":[],"mappings":"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"}
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-call MCP server: tools + OAuth AS + routing, wired for real connectors.
|
|
3
|
+
*
|
|
4
|
+
* This layers over `createMcpHandler` and `createOAuthRouter`, which stay
|
|
5
|
+
* exported for anyone who wants to compose the pieces themselves.
|
|
6
|
+
*/
|
|
7
|
+
import { type ClientName } from "./clients.js";
|
|
8
|
+
import type { AuditEntry, Principal, ServerInfo } from "./methods.js";
|
|
9
|
+
import type { CodeStore } from "./oauth/codes.js";
|
|
10
|
+
import type { ClientStore, MintAccessTokenInput, MintedToken, OAuthUser, RefreshAccessTokenInput, RevokeTokenInput } from "./oauth/types.js";
|
|
11
|
+
import { type ToolDef } from "./registry.js";
|
|
12
|
+
/** What your token verification returns. The library checks the audience. */
|
|
13
|
+
export type VerifiedToken = {
|
|
14
|
+
userId: string;
|
|
15
|
+
scopes: string[];
|
|
16
|
+
/** RFC 8707 audience the token was minted for. */
|
|
17
|
+
audience: string;
|
|
18
|
+
};
|
|
19
|
+
export type McpAppAuth = {
|
|
20
|
+
/** HMAC secret for signing auth codes. */
|
|
21
|
+
codeSecret: string | ((req: Request) => string | Promise<string>);
|
|
22
|
+
/** Resolve the logged-in user, or null → redirect to `loginUrl`. */
|
|
23
|
+
resolveUser: (req: Request) => Promise<OAuthUser | null>;
|
|
24
|
+
/** Where to send unauthenticated authorize requests. */
|
|
25
|
+
loginUrl: (req: Request, nextPath: string) => string;
|
|
26
|
+
/** Mint an access token; embed `resource` as the audience. */
|
|
27
|
+
mintAccessToken: (input: MintAccessTokenInput) => Promise<MintedToken>;
|
|
28
|
+
/**
|
|
29
|
+
* Decode and validate a bearer token. Return its `audience` —
|
|
30
|
+
* `createMcpApp` rejects tokens minted for a different resource itself,
|
|
31
|
+
* so this check cannot be forgotten.
|
|
32
|
+
*/
|
|
33
|
+
verifyToken: (token: string, req: Request) => Promise<VerifiedToken | null>;
|
|
34
|
+
refreshAccessToken?: (input: RefreshAccessTokenInput) => Promise<MintedToken | null>;
|
|
35
|
+
/** RFC 7009 — presence mounts `/revoke` and advertises it. */
|
|
36
|
+
revokeToken?: (input: RevokeTokenInput) => Promise<void>;
|
|
37
|
+
codeStore?: CodeStore;
|
|
38
|
+
/** Required when any configured client is pre-registered (e.g. Gemini). */
|
|
39
|
+
clientStore?: ClientStore;
|
|
40
|
+
};
|
|
41
|
+
export type McpAppOptions<TCtx> = {
|
|
42
|
+
serverInfo: ServerInfo;
|
|
43
|
+
tools: ToolDef<TCtx>[];
|
|
44
|
+
auth: McpAppAuth;
|
|
45
|
+
/** Connector clients to serve. Default: `["claude"]`. */
|
|
46
|
+
clients?: ClientName[];
|
|
47
|
+
instructions?: string;
|
|
48
|
+
/** MCP endpoint path. Default `"/mcp"`. */
|
|
49
|
+
resourcePath?: string;
|
|
50
|
+
/** OAuth prefix. Default `` `${resourcePath}/oauth` ``. */
|
|
51
|
+
oauthPath?: string;
|
|
52
|
+
/** Scopes this server grants. Default `["mcp"]`. */
|
|
53
|
+
scopes?: string[];
|
|
54
|
+
realm?: string;
|
|
55
|
+
/** Extra exact-match redirect URIs beyond the client profiles. */
|
|
56
|
+
extraRedirectUris?: string[];
|
|
57
|
+
/** Allow loopback redirects (native clients). Default true. */
|
|
58
|
+
allowLoopback?: boolean;
|
|
59
|
+
validateArgs?: boolean;
|
|
60
|
+
onToolError?: (exc: unknown) => string;
|
|
61
|
+
/** Per-request context for tools. Defaults to an empty object. */
|
|
62
|
+
context?: (req: Request, principal: Principal | null) => TCtx | Promise<TCtx>;
|
|
63
|
+
audit?: (entry: AuditEntry) => void | Promise<void>;
|
|
64
|
+
};
|
|
65
|
+
export type McpApp = {
|
|
66
|
+
fetch: (request: Request) => Promise<Response>;
|
|
67
|
+
};
|
|
68
|
+
export declare const createMcpApp: <TCtx>(options: McpAppOptions<TCtx>) => McpApp;
|
|
69
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAGtB,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAOtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EACV,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAEjE,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAClE,oEAAoE;IACpE,WAAW,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACzD,wDAAwD;IACxD,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACrD,8DAA8D;IAC9D,eAAe,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IACvE;;;;OAIG;IACH,WAAW,EAAE,CACX,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,OAAO,KACT,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IACnC,kBAAkB,CAAC,EAAE,CACnB,KAAK,EAAE,uBAAuB,KAC3B,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,IAAI,IAAI;IAChC,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,yDAAyD;IACzD,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,+DAA+D;IAC/D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;IACvC,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,IAAI,EAC/B,SAAS,aAAa,CAAC,IAAI,CAAC,KAC3B,MA2FF,CAAC"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-call MCP server: tools + OAuth AS + routing, wired for real connectors.
|
|
3
|
+
*
|
|
4
|
+
* This layers over `createMcpHandler` and `createOAuthRouter`, which stay
|
|
5
|
+
* exported for anyone who wants to compose the pieces themselves.
|
|
6
|
+
*/
|
|
7
|
+
import { parseBearer } from "./auth/bearer.js";
|
|
8
|
+
import { DEFAULT_CLIENTS, assertClientsConfigured, hasDynamicClient, authMethodsFor, redirectUrisFor, } from "./clients.js";
|
|
9
|
+
import { createMcpHandler } from "./dispatch.js";
|
|
10
|
+
import { INTERNAL_ERROR, jsonResponse } from "./http.js";
|
|
11
|
+
import { createOAuthRouter } from "./oauth/router.js";
|
|
12
|
+
import { DEFAULT_RESOURCE_PATH, canonicalResource, resourcesEqual, } from "./oauth/resource.js";
|
|
13
|
+
import { createToolRegistry } from "./registry.js";
|
|
14
|
+
export const createMcpApp = (options) => {
|
|
15
|
+
const resourcePath = options.resourcePath ?? DEFAULT_RESOURCE_PATH;
|
|
16
|
+
const oauthPath = options.oauthPath ?? `${resourcePath}/oauth`;
|
|
17
|
+
const clients = options.clients ?? DEFAULT_CLIENTS;
|
|
18
|
+
const realm = options.realm ?? options.serverInfo.name;
|
|
19
|
+
assertClientsConfigured(clients, options.auth.clientStore);
|
|
20
|
+
const registry = createToolRegistry(options.tools, {
|
|
21
|
+
validateArgs: options.validateArgs,
|
|
22
|
+
onToolError: options.onToolError,
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* The audience check lives here, not in user code — a token minted for
|
|
26
|
+
* another MCP server must never be accepted by this one.
|
|
27
|
+
*/
|
|
28
|
+
const authenticate = async (req) => {
|
|
29
|
+
const token = parseBearer(req.headers.get("authorization"));
|
|
30
|
+
if (!token)
|
|
31
|
+
return null;
|
|
32
|
+
const verified = await options.auth.verifyToken(token, req);
|
|
33
|
+
if (!verified)
|
|
34
|
+
return null;
|
|
35
|
+
const expected = canonicalResource(new URL(req.url).origin, resourcePath);
|
|
36
|
+
if (!resourcesEqual(verified.audience, expected))
|
|
37
|
+
return null;
|
|
38
|
+
return { id: verified.userId, scopes: verified.scopes };
|
|
39
|
+
};
|
|
40
|
+
const handler = createMcpHandler({
|
|
41
|
+
registry,
|
|
42
|
+
serverInfo: options.serverInfo,
|
|
43
|
+
instructions: options.instructions,
|
|
44
|
+
wwwAuthenticate: (req) => ({
|
|
45
|
+
realm,
|
|
46
|
+
resourceMetadataUrl: `${new URL(req.url).origin}/.well-known/oauth-protected-resource${resourcePath}`,
|
|
47
|
+
}),
|
|
48
|
+
ports: {
|
|
49
|
+
authenticate,
|
|
50
|
+
context: options.context ?? (() => ({})),
|
|
51
|
+
audit: options.audit,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
const oauth = createOAuthRouter({
|
|
55
|
+
resourcePath,
|
|
56
|
+
oauthPath,
|
|
57
|
+
realm,
|
|
58
|
+
scopes: options.scopes,
|
|
59
|
+
tokenEndpointAuthMethods: authMethodsFor(clients),
|
|
60
|
+
allowUnregisteredClients: hasDynamicClient(clients),
|
|
61
|
+
redirect: {
|
|
62
|
+
// Client profiles are the source of truth for callbacks.
|
|
63
|
+
extra: [
|
|
64
|
+
...redirectUrisFor(clients),
|
|
65
|
+
...(options.extraRedirectUris ?? []),
|
|
66
|
+
],
|
|
67
|
+
allowClaude: false,
|
|
68
|
+
allowLoopback: options.allowLoopback ?? true,
|
|
69
|
+
},
|
|
70
|
+
ports: {
|
|
71
|
+
codeSecret: options.auth.codeSecret,
|
|
72
|
+
resolveUser: options.auth.resolveUser,
|
|
73
|
+
loginUrl: options.auth.loginUrl,
|
|
74
|
+
mintAccessToken: options.auth.mintAccessToken,
|
|
75
|
+
refreshAccessToken: options.auth.refreshAccessToken,
|
|
76
|
+
revokeToken: options.auth.revokeToken,
|
|
77
|
+
codeStore: options.auth.codeStore,
|
|
78
|
+
clientStore: options.auth.clientStore,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
fetch: async (request) => {
|
|
83
|
+
try {
|
|
84
|
+
const oauthResponse = await oauth.tryHandle(request);
|
|
85
|
+
if (oauthResponse)
|
|
86
|
+
return oauthResponse;
|
|
87
|
+
if (new URL(request.url).pathname === resourcePath) {
|
|
88
|
+
return await handler.fetch(request);
|
|
89
|
+
}
|
|
90
|
+
return jsonResponse({ error: "Not found" }, 404);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Both `oauth` and `handler` already turn port failures into a
|
|
94
|
+
// Response themselves — this is defense in depth, not the primary
|
|
95
|
+
// guard. `fetch` must never reject.
|
|
96
|
+
return jsonResponse({ error: INTERNAL_ERROR }, 500);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Bearer auth helpers for MCP (RFC 9728 WWW-Authenticate). */
|
|
2
|
+
export declare const BEARER_PREFIX = "Bearer ";
|
|
3
|
+
/** Above any real JWT / HMAC sig; prevents allocation DoS on attacker input. */
|
|
4
|
+
export declare const MAX_COMPARE_LENGTH = 4096;
|
|
5
|
+
export declare const parseBearer: (authorization: string | null) => string | null;
|
|
6
|
+
/**
|
|
7
|
+
* Constant-time string compare.
|
|
8
|
+
* Pads to max length and folds length mismatch into the accumulator
|
|
9
|
+
* so unequal lengths do not short-circuit. Rejects over-long inputs.
|
|
10
|
+
*/
|
|
11
|
+
export declare const timingSafeEqual: (a: string, b: string) => boolean;
|
|
12
|
+
export declare const matchesAny: (presented: string, accepted: string[]) => boolean;
|
|
13
|
+
export type WwwAuthenticateOptions = {
|
|
14
|
+
realm: string;
|
|
15
|
+
/** Absolute URL to RFC 9728 protected resource metadata. */
|
|
16
|
+
resourceMetadataUrl: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const wwwAuthenticateHeader: (options: WwwAuthenticateOptions) => string;
|
|
19
|
+
/** RFC 6750 §2.3 uses `access_token`; also reject legacy `token`. */
|
|
20
|
+
export declare const rejectQueryToken: (url: URL) => boolean;
|
|
21
|
+
//# sourceMappingURL=bearer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearer.d.ts","sourceRoot":"","sources":["../../src/auth/bearer.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D,eAAO,MAAM,aAAa,YAAY,CAAC;AAEvC,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,eAAO,MAAM,WAAW,GAAI,eAAe,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAInE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,OAWtD,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,WAAW,MAAM,EAAE,UAAU,MAAM,EAAE,KAAG,OAGhE,CAAC;AAEJ,MAAM,MAAM,sBAAsB,GAAG;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,SAAS,sBAAsB,KAC9B,MAC6F,CAAC;AAEjG,qEAAqE;AACrE,eAAO,MAAM,gBAAgB,GAAI,KAAK,GAAG,KAAG,OAC2B,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Bearer auth helpers for MCP (RFC 9728 WWW-Authenticate). */
|
|
2
|
+
export const BEARER_PREFIX = "Bearer ";
|
|
3
|
+
/** Above any real JWT / HMAC sig; prevents allocation DoS on attacker input. */
|
|
4
|
+
export const MAX_COMPARE_LENGTH = 4096;
|
|
5
|
+
export const parseBearer = (authorization) => {
|
|
6
|
+
if (!authorization?.startsWith(BEARER_PREFIX))
|
|
7
|
+
return null;
|
|
8
|
+
const token = authorization.slice(BEARER_PREFIX.length).trim();
|
|
9
|
+
return token.length > 0 ? token : null;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Constant-time string compare.
|
|
13
|
+
* Pads to max length and folds length mismatch into the accumulator
|
|
14
|
+
* so unequal lengths do not short-circuit. Rejects over-long inputs.
|
|
15
|
+
*/
|
|
16
|
+
export const timingSafeEqual = (a, b) => {
|
|
17
|
+
if (a.length > MAX_COMPARE_LENGTH || b.length > MAX_COMPARE_LENGTH) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const len = Math.max(a.length, b.length);
|
|
21
|
+
const diff = Array.from({ length: len }, (_, i) => i).reduce((acc, i) => {
|
|
22
|
+
const ac = i < a.length ? a.charCodeAt(i) : 0;
|
|
23
|
+
const bc = i < b.length ? b.charCodeAt(i) : 0;
|
|
24
|
+
return acc | (ac ^ bc);
|
|
25
|
+
}, a.length ^ b.length);
|
|
26
|
+
return diff === 0;
|
|
27
|
+
};
|
|
28
|
+
export const matchesAny = (presented, accepted) => accepted.some((token) => token.length > 0 && timingSafeEqual(presented, token));
|
|
29
|
+
export const wwwAuthenticateHeader = (options) => `${BEARER_PREFIX}realm="${options.realm}", resource_metadata="${options.resourceMetadataUrl}"`;
|
|
30
|
+
/** RFC 6750 §2.3 uses `access_token`; also reject legacy `token`. */
|
|
31
|
+
export const rejectQueryToken = (url) => url.searchParams.has("token") || url.searchParams.has("access_token");
|
|
32
|
+
//# sourceMappingURL=bearer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bearer.js","sourceRoot":"","sources":["../../src/auth/bearer.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AAEvC,gFAAgF;AAChF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEvC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,aAA4B,EAAiB,EAAE;IACzE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/D,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAS,EAAE,CAAS,EAAW,EAAE;IAC/D,IAAI,CAAC,CAAC,MAAM,GAAG,kBAAkB,IAAI,CAAC,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACtE,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,OAAO,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;IACzB,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,IAAI,KAAK,CAAC,CAAC;AACpB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,SAAiB,EAAE,QAAkB,EAAW,EAAE,CAC3E,QAAQ,CAAC,IAAI,CACX,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CACjE,CAAC;AAQJ,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAA+B,EACvB,EAAE,CACV,GAAG,aAAa,UAAU,OAAO,CAAC,KAAK,yBAAyB,OAAO,CAAC,mBAAmB,GAAG,CAAC;AAEjG,qEAAqE;AACrE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAQ,EAAW,EAAE,CACpD,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector client profiles.
|
|
3
|
+
*
|
|
4
|
+
* Each AI client reaches an MCP server slightly differently — different
|
|
5
|
+
* callback URLs, different registration models. Encoding that here is the
|
|
6
|
+
* point: you name your clients, the library configures the AS to match.
|
|
7
|
+
*/
|
|
8
|
+
import { type TokenEndpointAuthMethod } from "./oauth/constants.js";
|
|
9
|
+
import type { ClientStore } from "./oauth/types.js";
|
|
10
|
+
export type ClientName = "claude" | "gemini" | "codex";
|
|
11
|
+
export type ClientProfile = {
|
|
12
|
+
/** Exact-match callbacks this client redirects to. */
|
|
13
|
+
redirectUris: string[];
|
|
14
|
+
/** Token-endpoint auth this client presents. */
|
|
15
|
+
tokenEndpointAuthMethods: TokenEndpointAuthMethod[];
|
|
16
|
+
/**
|
|
17
|
+
* Whether the client is registered ahead of time rather than dynamically.
|
|
18
|
+
* Pre-registered clients need `clientStore` on the OAuth ports.
|
|
19
|
+
*/
|
|
20
|
+
preRegistered: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare const CLIENT_PROFILES: Record<ClientName, ClientProfile>;
|
|
23
|
+
export declare const DEFAULT_CLIENTS: ClientName[];
|
|
24
|
+
/** Exact-match redirect URIs contributed by the named clients. */
|
|
25
|
+
export declare const redirectUrisFor: (clients: ClientName[]) => string[];
|
|
26
|
+
/** Auth methods to advertise, in a stable order. */
|
|
27
|
+
export declare const authMethodsFor: (clients: ClientName[]) => TokenEndpointAuthMethod[];
|
|
28
|
+
/** Clients that need a `clientStore` to work at all. */
|
|
29
|
+
export declare const preRegisteredClients: (clients: ClientName[]) => ClientName[];
|
|
30
|
+
/** True when any named client registers dynamically. Drives `allowUnregisteredClients`. */
|
|
31
|
+
export declare const hasDynamicClient: (clients: ClientName[]) => boolean;
|
|
32
|
+
export declare const assertClientsConfigured: (clients: ClientName[], clientStore: ClientStore | undefined) => void;
|
|
33
|
+
//# sourceMappingURL=clients.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEvD,MAAM,MAAM,aAAa,GAAG;IAC1B,sDAAsD;IACtD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gDAAgD;IAChD,wBAAwB,EAAE,uBAAuB,EAAE,CAAC;IACpD;;;OAGG;IACH,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,UAAU,EAAE,aAAa,CAyB7D,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,UAAU,EAAe,CAAC;AAExD,kEAAkE;AAClE,eAAO,MAAM,eAAe,GAAI,SAAS,UAAU,EAAE,KAAG,MAAM,EAE7D,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,cAAc,GACzB,SAAS,UAAU,EAAE,KACpB,uBAAuB,EASzB,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,oBAAoB,GAAI,SAAS,UAAU,EAAE,KAAG,UAAU,EACR,CAAC;AAEhE,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,GAAI,SAAS,UAAU,EAAE,KAAG,OACK,CAAC;AAE/D,eAAO,MAAM,uBAAuB,GAClC,SAAS,UAAU,EAAE,EACrB,aAAa,WAAW,GAAG,SAAS,KACnC,IASF,CAAC"}
|
package/dist/clients.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connector client profiles.
|
|
3
|
+
*
|
|
4
|
+
* Each AI client reaches an MCP server slightly differently — different
|
|
5
|
+
* callback URLs, different registration models. Encoding that here is the
|
|
6
|
+
* point: you name your clients, the library configures the AS to match.
|
|
7
|
+
*/
|
|
8
|
+
import { TOKEN_ENDPOINT_AUTH_METHODS, } from "./oauth/constants.js";
|
|
9
|
+
import { CLAUDE_CALLBACK } from "./oauth/redirect.js";
|
|
10
|
+
export const CLIENT_PROFILES = {
|
|
11
|
+
/** Claude Custom Connectors — dynamic registration, public client + PKCE. */
|
|
12
|
+
claude: {
|
|
13
|
+
redirectUris: [CLAUDE_CALLBACK],
|
|
14
|
+
tokenEndpointAuthMethods: [TOKEN_ENDPOINT_AUTH_METHODS.none],
|
|
15
|
+
preRegistered: false,
|
|
16
|
+
},
|
|
17
|
+
/**
|
|
18
|
+
* Gemini Enterprise — the org registers Gemini with its own IdP and
|
|
19
|
+
* configures the resulting client_id / client_secret pair.
|
|
20
|
+
*/
|
|
21
|
+
gemini: {
|
|
22
|
+
redirectUris: [],
|
|
23
|
+
tokenEndpointAuthMethods: [
|
|
24
|
+
TOKEN_ENDPOINT_AUTH_METHODS.basic,
|
|
25
|
+
TOKEN_ENDPOINT_AUTH_METHODS.post,
|
|
26
|
+
],
|
|
27
|
+
preRegistered: true,
|
|
28
|
+
},
|
|
29
|
+
/** OpenAI Codex / ChatGPT connectors — OAuth 2.1 per the MCP auth spec. */
|
|
30
|
+
codex: {
|
|
31
|
+
redirectUris: [],
|
|
32
|
+
tokenEndpointAuthMethods: [TOKEN_ENDPOINT_AUTH_METHODS.none],
|
|
33
|
+
preRegistered: false,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
export const DEFAULT_CLIENTS = ["claude"];
|
|
37
|
+
/** Exact-match redirect URIs contributed by the named clients. */
|
|
38
|
+
export const redirectUrisFor = (clients) => [
|
|
39
|
+
...new Set(clients.flatMap((name) => CLIENT_PROFILES[name].redirectUris)),
|
|
40
|
+
];
|
|
41
|
+
/** Auth methods to advertise, in a stable order. */
|
|
42
|
+
export const authMethodsFor = (clients) => {
|
|
43
|
+
const present = new Set(clients.flatMap((name) => CLIENT_PROFILES[name].tokenEndpointAuthMethods));
|
|
44
|
+
return [
|
|
45
|
+
TOKEN_ENDPOINT_AUTH_METHODS.none,
|
|
46
|
+
TOKEN_ENDPOINT_AUTH_METHODS.basic,
|
|
47
|
+
TOKEN_ENDPOINT_AUTH_METHODS.post,
|
|
48
|
+
].filter((method) => present.has(method));
|
|
49
|
+
};
|
|
50
|
+
/** Clients that need a `clientStore` to work at all. */
|
|
51
|
+
export const preRegisteredClients = (clients) => clients.filter((name) => CLIENT_PROFILES[name].preRegistered);
|
|
52
|
+
/** True when any named client registers dynamically. Drives `allowUnregisteredClients`. */
|
|
53
|
+
export const hasDynamicClient = (clients) => clients.some((name) => !CLIENT_PROFILES[name].preRegistered);
|
|
54
|
+
export const assertClientsConfigured = (clients, clientStore) => {
|
|
55
|
+
if (clients.length === 0)
|
|
56
|
+
throw new Error("clients must name at least one connector");
|
|
57
|
+
const needsStore = preRegisteredClients(clients);
|
|
58
|
+
if (needsStore.length > 0 && !clientStore) {
|
|
59
|
+
throw new Error(`clients [${needsStore.join(", ")}] are pre-registered — ` +
|
|
60
|
+
"pass auth.clientStore so their client_id / client_secret can be resolved");
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=clients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../src/clients.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,2BAA2B,GAE5B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAiBtD,MAAM,CAAC,MAAM,eAAe,GAAsC;IAChE,6EAA6E;IAC7E,MAAM,EAAE;QACN,YAAY,EAAE,CAAC,eAAe,CAAC;QAC/B,wBAAwB,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC;QAC5D,aAAa,EAAE,KAAK;KACrB;IACD;;;OAGG;IACH,MAAM,EAAE;QACN,YAAY,EAAE,EAAE;QAChB,wBAAwB,EAAE;YACxB,2BAA2B,CAAC,KAAK;YACjC,2BAA2B,CAAC,IAAI;SACjC;QACD,aAAa,EAAE,IAAI;KACpB;IACD,2EAA2E;IAC3E,KAAK,EAAE;QACL,YAAY,EAAE,EAAE;QAChB,wBAAwB,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC;QAC5D,aAAa,EAAE,KAAK;KACrB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAiB,CAAC,QAAQ,CAAC,CAAC;AAExD,kEAAkE;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,OAAqB,EAAY,EAAE,CAAC;IAClE,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC;CAC1E,CAAC;AAEF,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,OAAqB,EACM,EAAE;IAC7B,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,wBAAwB,CAAC,CAC1E,CAAC;IACF,OAAO;QACL,2BAA2B,CAAC,IAAI;QAChC,2BAA2B,CAAC,KAAK;QACjC,2BAA2B,CAAC,IAAI;KACjC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,wDAAwD;AACxD,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,OAAqB,EAAgB,EAAE,CAC1E,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC;AAEhE,2FAA2F;AAC3F,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAqB,EAAW,EAAE,CACjE,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC;AAE/D,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,OAAqB,EACrB,WAAoC,EAC9B,EAAE;IACR,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACjD,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,YAAY,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,yBAAyB;YACxD,0EAA0E,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type McpHandlerOptions } from "./methods.js";
|
|
2
|
+
export type { Principal, AuditEntry, McpPorts, ServerInfo, McpHandlerOptions, } from "./methods.js";
|
|
3
|
+
export type McpHandler = {
|
|
4
|
+
fetch: (request: Request) => Promise<Response>;
|
|
5
|
+
};
|
|
6
|
+
export declare const createMcpHandler: <TCtx>(options: McpHandlerOptions<TCtx>) => McpHandler;
|
|
7
|
+
//# sourceMappingURL=dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,EAOL,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,SAAS,EACT,UAAU,EACV,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAChD,CAAC;AA+FF,eAAO,MAAM,gBAAgB,GAAI,IAAI,EACnC,SAAS,iBAAiB,CAAC,IAAI,CAAC,KAC/B,UA8BF,CAAC"}
|
package/dist/dispatch.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_REQUEST, JSONRPC_PARSE_ERROR, rpcError, } from "./jsonrpc.js";
|
|
2
|
+
import { INTERNAL_ERROR, emptyResponse, jsonResponse, methodNotAllowed, optionsResponse, } from "./http.js";
|
|
3
|
+
import { rejectQueryToken } from "./auth/bearer.js";
|
|
4
|
+
import { DEFAULT_PUBLIC_METHODS, dispatchRpc, isNotification, resolveWww, safeAudit, unauthorized, } from "./methods.js";
|
|
5
|
+
const parseBody = async (request) => {
|
|
6
|
+
try {
|
|
7
|
+
return await request.json();
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return jsonResponse(rpcError(null, JSONRPC_PARSE_ERROR, "Parse error"), 400);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
/** Transport-level denial: no JSON-RPC method has been parsed yet. */
|
|
14
|
+
const auditDenial = (options, error) => safeAudit(options, { method: "", ok: false, error, durationMs: 0 });
|
|
15
|
+
const guardPost = async (request, options) => {
|
|
16
|
+
if (rejectQueryToken(new URL(request.url))) {
|
|
17
|
+
await auditDenial(options, "query_string_token");
|
|
18
|
+
return unauthorized(resolveWww(options, request), "Token in query string is rejected. Use Authorization Bearer.");
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
const handleGet = async (request, options) => {
|
|
23
|
+
if (rejectQueryToken(new URL(request.url))) {
|
|
24
|
+
await auditDenial(options, "query_string_token");
|
|
25
|
+
return jsonResponse({
|
|
26
|
+
error: "Pass token via Authorization: Bearer header, not query string",
|
|
27
|
+
}, 400);
|
|
28
|
+
}
|
|
29
|
+
if (!request.headers.get("authorization")) {
|
|
30
|
+
await auditDenial(options, "unauthorized");
|
|
31
|
+
return unauthorized(resolveWww(options, request));
|
|
32
|
+
}
|
|
33
|
+
return methodNotAllowed("Use POST for MCP");
|
|
34
|
+
};
|
|
35
|
+
const handlePost = async (request, options, publicMethods) => {
|
|
36
|
+
const denied = await guardPost(request, options);
|
|
37
|
+
if (denied)
|
|
38
|
+
return denied;
|
|
39
|
+
const parsed = await parseBody(request);
|
|
40
|
+
if (parsed instanceof Response)
|
|
41
|
+
return parsed;
|
|
42
|
+
if (Array.isArray(parsed)) {
|
|
43
|
+
return jsonResponse(rpcError(null, JSONRPC_INVALID_REQUEST, "Batch requests not supported"), 400);
|
|
44
|
+
}
|
|
45
|
+
if (!parsed || typeof parsed !== "object") {
|
|
46
|
+
return jsonResponse(rpcError(null, JSONRPC_PARSE_ERROR, "Parse error"), 400);
|
|
47
|
+
}
|
|
48
|
+
const msg = parsed;
|
|
49
|
+
if (isNotification(msg))
|
|
50
|
+
return emptyResponse(202);
|
|
51
|
+
return dispatchRpc({ req: request, body: msg, options, publicMethods });
|
|
52
|
+
};
|
|
53
|
+
export const createMcpHandler = (options) => {
|
|
54
|
+
const publicMethods = options.publicMethods ?? DEFAULT_PUBLIC_METHODS;
|
|
55
|
+
const HTTP_METHODS = {
|
|
56
|
+
OPTIONS: () => optionsResponse(),
|
|
57
|
+
GET: (request) => handleGet(request, options),
|
|
58
|
+
POST: (request) => handlePost(request, options, publicMethods),
|
|
59
|
+
};
|
|
60
|
+
return {
|
|
61
|
+
fetch: async (request) => {
|
|
62
|
+
try {
|
|
63
|
+
const handler = HTTP_METHODS[request.method];
|
|
64
|
+
return await (handler
|
|
65
|
+
? handler(request, options, publicMethods)
|
|
66
|
+
: methodNotAllowed());
|
|
67
|
+
}
|
|
68
|
+
catch (exc) {
|
|
69
|
+
// A host port (authenticate/context/audit) threw. `fetch` must never
|
|
70
|
+
// reject — the caller gets an honest 500, not an unhandled rejection
|
|
71
|
+
// whose shape depends on which runtime is hosting this.
|
|
72
|
+
const error = exc instanceof Error && exc.message ? exc.message : INTERNAL_ERROR;
|
|
73
|
+
await auditDenial(options, error);
|
|
74
|
+
return jsonResponse(rpcError(null, JSONRPC_INTERNAL_ERROR, "Internal error"), 500);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
//# sourceMappingURL=dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** JSON `error` body / audit reason when a host port throws. */
|
|
2
|
+
export declare const INTERNAL_ERROR = "internal_error";
|
|
3
|
+
export declare const corsHeaders: (extra?: Record<string, string>) => Record<string, string>;
|
|
4
|
+
export type JsonResponseOptions = {
|
|
5
|
+
/** Include permissive CORS headers. Default true (MCP). OAuth token set false. */
|
|
6
|
+
cors?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare const jsonResponse: (data: unknown, status?: number, extraHeaders?: Record<string, string>, options?: JsonResponseOptions) => Response;
|
|
9
|
+
export declare const emptyResponse: (status: number) => Response;
|
|
10
|
+
export declare const optionsResponse: (options?: JsonResponseOptions) => Response;
|
|
11
|
+
export declare const methodNotAllowed: (message?: string, options?: JsonResponseOptions) => Response;
|
|
12
|
+
/**
|
|
13
|
+
* OPTIONS → 204; allowed verb → null (continue); otherwise → 405.
|
|
14
|
+
* Shared by OAuth handlers that all need the same method gate.
|
|
15
|
+
*/
|
|
16
|
+
export declare const requireHttpMethod: (request: Request, allowed: ReadonlySet<string>, options?: JsonResponseOptions) => Response | null;
|
|
17
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,eAAO,MAAM,WAAW,GACtB,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAMtB,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG;IAChC,kFAAkF;IAClF,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,MAAM,OAAO,EACb,eAAY,EACZ,eAAe,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,UAAU,mBAAmB,KAC5B,QAWF,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,QACS,CAAC;AAEzD,eAAO,MAAM,eAAe,GAAI,UAAU,mBAAmB,KAAG,QAI5D,CAAC;AAEL,eAAO,MAAM,gBAAgB,GAC3B,gBAA8B,EAC9B,UAAU,mBAAmB,KAC5B,QAAqE,CAAC;AAEzE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,OAAO,EAChB,SAAS,WAAW,CAAC,MAAM,CAAC,EAC5B,UAAU,mBAAmB,KAC5B,QAAQ,GAAG,IASb,CAAC"}
|