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
package/dist/oauth/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
import type { RedirectAllowlistOptions } from "./redirect.js";
|
|
1
|
+
import type { CimdCache } from "./cimd/types.js";
|
|
2
|
+
import type { TokenEndpointAuthMethod } from "./constants.js";
|
|
3
|
+
import type { CodeStore } from "./crypto/codes.js";
|
|
4
|
+
import type { RedirectAllowlistOptions } from "./policy/redirect.js";
|
|
5
5
|
export type OAuthUser = {
|
|
6
6
|
id: string;
|
|
7
7
|
};
|
|
@@ -16,6 +16,35 @@ export type RegisteredClient = {
|
|
|
16
16
|
redirectUris: string[];
|
|
17
17
|
tokenEndpointAuthMethod: TokenEndpointAuthMethod;
|
|
18
18
|
};
|
|
19
|
+
export type ConsentRequest = {
|
|
20
|
+
clientId: string;
|
|
21
|
+
registeredClient: RegisteredClient | null;
|
|
22
|
+
redirectUri: string;
|
|
23
|
+
scope: string[];
|
|
24
|
+
resource: string;
|
|
25
|
+
user: OAuthUser;
|
|
26
|
+
/** Embed this in your form's `consent_ticket` field (hidden input or POST body). */
|
|
27
|
+
ticket: string;
|
|
28
|
+
oauthPath: string;
|
|
29
|
+
};
|
|
30
|
+
export type ConsentOptions = {
|
|
31
|
+
/**
|
|
32
|
+
* Render the approval page yourself. Return any `Response` — HTML, or a
|
|
33
|
+
* redirect to your own route. Omit for the built-in hardened interstitial.
|
|
34
|
+
*
|
|
35
|
+
* If your page sets its own `Content-Security-Policy` with `form-action`,
|
|
36
|
+
* include `new URL(input.redirectUri).origin` in it — Chromium enforces
|
|
37
|
+
* `form-action` across the redirect your approval POST triggers, not just
|
|
38
|
+
* the immediate submission target, so `'self'` alone blocks the eventual
|
|
39
|
+
* redirect back to the client's own callback.
|
|
40
|
+
*/
|
|
41
|
+
render?: (input: ConsentRequest) => Response | Promise<Response>;
|
|
42
|
+
/**
|
|
43
|
+
* Client ids that skip approval. Only honored when `clientStore` actually
|
|
44
|
+
* resolves the id — a DCR or self-invented id is never trusted.
|
|
45
|
+
*/
|
|
46
|
+
preApprovedClientIds?: string[];
|
|
47
|
+
};
|
|
19
48
|
/**
|
|
20
49
|
* The payload sealed into a `client_id` this server itself issued via
|
|
21
50
|
* `/register` (see `sealed.ts`, type `"client"`). Zero storage: the id
|
|
@@ -23,6 +52,13 @@ export type RegisteredClient = {
|
|
|
23
52
|
*/
|
|
24
53
|
export type ClientAssertion = {
|
|
25
54
|
redirectUris: string[];
|
|
55
|
+
/**
|
|
56
|
+
* Unix seconds when `/register` sealed this assertion. Informational only
|
|
57
|
+
* — not enforced as an expiry — so it can help an operator judge how old
|
|
58
|
+
* a still-valid DCR client is. Optional on read: an assertion sealed
|
|
59
|
+
* before this field existed still unseals and works.
|
|
60
|
+
*/
|
|
61
|
+
iat?: number;
|
|
26
62
|
};
|
|
27
63
|
export type ClientStore = {
|
|
28
64
|
/** Resolve a pre-registered client, or null when unknown. */
|
|
@@ -66,6 +102,13 @@ export type RefreshAccessTokenInput = {
|
|
|
66
102
|
* Implementations MUST reject refresh tokens not originally issued for this resource.
|
|
67
103
|
*/
|
|
68
104
|
resource: string;
|
|
105
|
+
/**
|
|
106
|
+
* RFC 6749 §6 — optional scope reduction requested by the client.
|
|
107
|
+
* When set, the library validates against advertised `scopes` only.
|
|
108
|
+
* Implementations MUST also reject any scope outside what was originally
|
|
109
|
+
* granted to this refresh token (the library cannot see opaque RTs).
|
|
110
|
+
*/
|
|
111
|
+
scope?: string;
|
|
69
112
|
};
|
|
70
113
|
export type RevokeTokenInput = {
|
|
71
114
|
token: string;
|
|
@@ -88,8 +131,18 @@ export type OAuthAuditEntry = {
|
|
|
88
131
|
reason: string;
|
|
89
132
|
};
|
|
90
133
|
export type OAuthPorts = {
|
|
91
|
-
/**
|
|
92
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Secret(s) that seal auth codes, consent tickets, and self-issued DCR
|
|
136
|
+
* client assertions (`sealed.ts`), and key `ClientStore.secretHash`.
|
|
137
|
+
*
|
|
138
|
+
* Pass a single string, or for key rotation an array: the **first** entry seals new material and hashes new client
|
|
139
|
+
* secrets; **every** entry is tried when unsealing/verifying, so material
|
|
140
|
+
* sealed under an older key keeps working until you drop it from the
|
|
141
|
+
* array. `ports.audit` sees a `"legacy_code_secret_used"` event whenever
|
|
142
|
+
* anything but the first entry was the one that actually verified,
|
|
143
|
+
* so you know when it's safe to remove.
|
|
144
|
+
*/
|
|
145
|
+
codeSecret: string | string[] | ((req: Request) => string | string[] | Promise<string | string[]>);
|
|
93
146
|
/** Resolve the logged-in user, or null → redirect to login. */
|
|
94
147
|
resolveUser: (req: Request) => Promise<OAuthUser | null>;
|
|
95
148
|
/** Where to send unauthenticated authorize requests. */
|
|
@@ -106,7 +159,7 @@ export type OAuthPorts = {
|
|
|
106
159
|
* tokens not issued to `clientId` MUST no-op (not throw).
|
|
107
160
|
*/
|
|
108
161
|
revokeToken?: ((input: RevokeTokenInput) => Promise<void>) | undefined;
|
|
109
|
-
/** Shared single-use jti store for multi-instance
|
|
162
|
+
/** Shared single-use jti store for multi-instance. Required unless `allowInMemoryCodeStore`. */
|
|
110
163
|
codeStore?: CodeStore | undefined;
|
|
111
164
|
/** Pre-registered clients. Required to serve confidential clients. */
|
|
112
165
|
clientStore?: ClientStore | undefined;
|
|
@@ -118,14 +171,6 @@ export type OAuthPorts = {
|
|
|
118
171
|
*/
|
|
119
172
|
audit?: ((entry: OAuthAuditEntry) => void | Promise<void>) | undefined;
|
|
120
173
|
};
|
|
121
|
-
/**
|
|
122
|
-
* Invoke `ports.audit`, swallowing any failure — the OAuth-side twin of
|
|
123
|
-
* `methods.ts`'s `safeAudit`. No timeout wrapper here: unlike the MCP
|
|
124
|
-
* side, nothing here is expected to be a slow, request-scoped metrics
|
|
125
|
-
* call in the same way, and adding one would be complexity without a
|
|
126
|
-
* demonstrated need — revisit if that changes.
|
|
127
|
-
*/
|
|
128
|
-
export declare const safeOAuthAudit: (options: OAuthRouterOptions, entry: OAuthAuditEntry) => Promise<void>;
|
|
129
174
|
export type OAuthRouterOptions = {
|
|
130
175
|
ports: OAuthPorts;
|
|
131
176
|
resourcePath?: string;
|
|
@@ -169,44 +214,53 @@ export type OAuthRouterOptions = {
|
|
|
169
214
|
* not under `ports`. Omit for the built-in hardened interstitial.
|
|
170
215
|
*/
|
|
171
216
|
consent?: ConsentOptions;
|
|
217
|
+
/**
|
|
218
|
+
* Max time to wait for `ports.audit` before responding anyway. Default
|
|
219
|
+
* 1000ms — same guarantee as `McpHandlerOptions.auditTimeoutMs`.
|
|
220
|
+
*/
|
|
221
|
+
auditTimeoutMs?: number;
|
|
222
|
+
/**
|
|
223
|
+
* Allow the process-local in-memory jti Map when `ports.codeStore` is
|
|
224
|
+
* omitted. Default **false** since 2.0 — multi-instance deploys silently
|
|
225
|
+
* double-redeem auth codes without a shared store. Set true only for
|
|
226
|
+
* single-process demos and tests.
|
|
227
|
+
*/
|
|
228
|
+
allowInMemoryCodeStore?: boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Resolve Client ID Metadata Documents (HTTPS URL `client_id`s).
|
|
231
|
+
* Default **false** — opt in with `cimd: true`. Off until connect-pinned
|
|
232
|
+
* fetch exists; see `cimd.ts` ponytail.
|
|
233
|
+
*/
|
|
234
|
+
cimd?: boolean;
|
|
235
|
+
/**
|
|
236
|
+
* CIMD document cache (respects Cache-Control max-age, capped at 1 h).
|
|
237
|
+
* **Strongly recommended with `cimd: true`:** without it `/token` and
|
|
238
|
+
* `/revoke` re-fetch the client's document on every call, so a flaky
|
|
239
|
+
* document host fails refresh/revoke (audited as `cimd_resolve_failed`).
|
|
240
|
+
*/
|
|
241
|
+
cimdCache?: CimdCache;
|
|
242
|
+
/** Optional DNS lookup for CIMD SSRF checks — see `CimdFetchOptions.lookup`. */
|
|
243
|
+
cimdLookup?: (hostname: string) => Promise<string[]>;
|
|
172
244
|
};
|
|
173
|
-
/** Default true — see `OAuthRouterOptions.allowUnregisteredClients`. */
|
|
174
|
-
export declare const unregisteredClientsAllowed: (options: OAuthRouterOptions) => boolean;
|
|
175
|
-
/** Default true since 1.0 — see `OAuthRouterOptions.requireRegisteredClients`. */
|
|
176
|
-
export declare const registeredClientsRequired: (options: OAuthRouterOptions) => boolean;
|
|
177
|
-
/** Scopes this AS advertises and is willing to grant. */
|
|
178
|
-
export declare const advertisedScopes: (options: OAuthRouterOptions) => string[];
|
|
179
245
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* construction instead of silently over-granting here.
|
|
246
|
+
* `OAuthRouterOptions` after `createOAuthRouter` has resolved them: paths
|
|
247
|
+
* normalized and `ports.codeStore` always present, so no endpoint handler
|
|
248
|
+
* can fall back to a store of its own. Internal — not re-exported.
|
|
184
249
|
*/
|
|
185
|
-
export
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
export declare const assertCodeSecret: (secret: string) => void;
|
|
193
|
-
export declare const resolveSecret: (ports: OAuthPorts, req: Request) => Promise<string>;
|
|
250
|
+
export type ResolvedOAuthRouterOptions = OAuthRouterOptions & {
|
|
251
|
+
resourcePath: string;
|
|
252
|
+
oauthPath: string;
|
|
253
|
+
ports: OAuthPorts & {
|
|
254
|
+
codeStore: CodeStore;
|
|
255
|
+
};
|
|
256
|
+
};
|
|
194
257
|
/**
|
|
195
258
|
* An OAuth error's code and description without a `Response` built yet —
|
|
196
|
-
* lets a caller choose delivery: `oauthError`
|
|
197
|
-
*
|
|
198
|
-
* see `authorize.ts` and `consent.ts`).
|
|
259
|
+
* lets a caller choose delivery: `oauthError` for a direct JSON body, or a
|
|
260
|
+
* redirect to the client's own callback (RFC 6749 §4.1.2.1).
|
|
199
261
|
*/
|
|
200
262
|
export type OAuthErrorInfo = {
|
|
201
263
|
code: string;
|
|
202
264
|
description: string;
|
|
203
265
|
};
|
|
204
|
-
export declare const oauthError: (error: string, status: number, description?: string) => Response;
|
|
205
|
-
/** Direct JSON error when this AS does not serve unknown `client_id`s. */
|
|
206
|
-
export declare const unregisteredClientError: (options: OAuthRouterOptions, reject: {
|
|
207
|
-
code: string;
|
|
208
|
-
status: number;
|
|
209
|
-
}) => Response | null;
|
|
210
|
-
/** `grantedScope` is what the auth code carried; the port may narrow it further. */
|
|
211
|
-
export declare const tokenResponse: (minted: MintedToken, grantedScope?: string) => Response;
|
|
212
266
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth/types.js
CHANGED
|
@@ -1,91 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { DEFAULT_SCOPE } from "./constants.js";
|
|
3
|
-
/**
|
|
4
|
-
* Invoke `ports.audit`, swallowing any failure — the OAuth-side twin of
|
|
5
|
-
* `methods.ts`'s `safeAudit`. No timeout wrapper here: unlike the MCP
|
|
6
|
-
* side, nothing here is expected to be a slow, request-scoped metrics
|
|
7
|
-
* call in the same way, and adding one would be complexity without a
|
|
8
|
-
* demonstrated need — revisit if that changes.
|
|
9
|
-
*/
|
|
10
|
-
export const safeOAuthAudit = async (options, entry) => {
|
|
11
|
-
try {
|
|
12
|
-
await options.ports.audit?.(entry);
|
|
13
|
-
}
|
|
14
|
-
catch {
|
|
15
|
-
// Intentionally ignored.
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
/** Default true — see `OAuthRouterOptions.allowUnregisteredClients`. */
|
|
19
|
-
export const unregisteredClientsAllowed = (options) => options.allowUnregisteredClients !== false;
|
|
20
|
-
/** Default true since 1.0 — see `OAuthRouterOptions.requireRegisteredClients`. */
|
|
21
|
-
export const registeredClientsRequired = (options) => options.requireRegisteredClients !== false;
|
|
22
|
-
/** Scopes this AS advertises and is willing to grant. */
|
|
23
|
-
export const advertisedScopes = (options) => options.scopes ?? [DEFAULT_SCOPE];
|
|
24
|
-
/**
|
|
25
|
-
* Scopes granted when a client omits `scope`. Falls back to the full
|
|
26
|
-
* advertised set only when that set has at most one entry — see
|
|
27
|
-
* `assertScopeConfig`, which forbids the ambiguous multi-scope case at
|
|
28
|
-
* construction instead of silently over-granting here.
|
|
29
|
-
*/
|
|
30
|
-
export const defaultScopes = (options) => options.defaultScopes ?? advertisedScopes(options);
|
|
31
|
-
/**
|
|
32
|
-
* A multi-scope server MUST say what an omitted `scope` grants — the
|
|
33
|
-
* alternative (silently granting everything advertised) is exactly the
|
|
34
|
-
* escalation least-privilege scoping exists to prevent.
|
|
35
|
-
*/
|
|
36
|
-
export const assertScopeConfig = (options) => {
|
|
37
|
-
const advertised = advertisedScopes(options);
|
|
38
|
-
if (advertised.length > 1 && !options.defaultScopes) {
|
|
39
|
-
throw new Error("scopes has more than one entry — defaultScopes must say what an omitted " +
|
|
40
|
-
"scope request grants (e.g. defaultScopes: [] for least privilege)");
|
|
41
|
-
}
|
|
42
|
-
const unknown = (options.defaultScopes ?? []).find((scope) => !advertised.includes(scope));
|
|
43
|
-
if (unknown !== undefined) {
|
|
44
|
-
throw new Error(`defaultScopes contains "${unknown}", which is not in scopes`);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
/**
|
|
48
|
-
* `codeSecret` can forge an auth code for any userId/scope/resource, so a
|
|
49
|
-
* weak or copy-pasted one is a full authorization bypass, not a footgun.
|
|
50
|
-
* The literals here are the exact strings this package's own docs/examples
|
|
51
|
-
* publish — copy-paste is the realistic failure mode.
|
|
52
|
-
*/
|
|
53
|
-
const MIN_CODE_SECRET_LENGTH = 32;
|
|
54
|
-
const DENYLISTED_CODE_SECRETS = new Set([
|
|
55
|
-
"e2e-code-secret-value",
|
|
56
|
-
"change-me",
|
|
57
|
-
"test-secret-value",
|
|
58
|
-
]);
|
|
59
|
-
const GENERATE_HINT = "generate one with `openssl rand -base64 32`";
|
|
60
|
-
export const assertCodeSecret = (secret) => {
|
|
61
|
-
// Checked before the length rule so a denylisted literal is always named
|
|
62
|
-
// for what it is, even if a future literal happens to be 32+ characters.
|
|
63
|
-
if (DENYLISTED_CODE_SECRETS.has(secret)) {
|
|
64
|
-
throw new Error(`codeSecret must not be a literal published in this package's own docs or examples — ${GENERATE_HINT}`);
|
|
65
|
-
}
|
|
66
|
-
if (secret.length < MIN_CODE_SECRET_LENGTH) {
|
|
67
|
-
throw new Error(`codeSecret must be at least ${MIN_CODE_SECRET_LENGTH} characters (got ${secret.length}) — ${GENERATE_HINT}`);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
export const resolveSecret = async (ports, req) => {
|
|
71
|
-
const secret = typeof ports.codeSecret === "string" ? ports.codeSecret : await ports.codeSecret(req);
|
|
72
|
-
assertCodeSecret(secret);
|
|
73
|
-
return secret;
|
|
74
|
-
};
|
|
75
|
-
export const oauthError = (error, status, description) => jsonResponse(description ? { error, error_description: description } : { error }, status, undefined, { cors: false });
|
|
76
|
-
const UNREGISTERED_CLIENT_DESCRIPTION = "unknown client_id — this server only serves pre-registered clients";
|
|
77
|
-
/** Direct JSON error when this AS does not serve unknown `client_id`s. */
|
|
78
|
-
export const unregisteredClientError = (options, reject) => {
|
|
79
|
-
if (unregisteredClientsAllowed(options))
|
|
80
|
-
return null;
|
|
81
|
-
return oauthError(reject.code, reject.status, UNREGISTERED_CLIENT_DESCRIPTION);
|
|
82
|
-
};
|
|
83
|
-
/** `grantedScope` is what the auth code carried; the port may narrow it further. */
|
|
84
|
-
export const tokenResponse = (minted, grantedScope) => jsonResponse({
|
|
85
|
-
access_token: minted.accessToken,
|
|
86
|
-
token_type: minted.tokenType ?? "bearer",
|
|
87
|
-
expires_in: minted.expiresIn,
|
|
88
|
-
scope: minted.scope ?? grantedScope ?? DEFAULT_SCOPE,
|
|
89
|
-
...(minted.refreshToken ? { refresh_token: minted.refreshToken } : {}),
|
|
90
|
-
}, 200, undefined, { cors: false });
|
|
1
|
+
export {};
|
|
91
2
|
//# sourceMappingURL=types.js.map
|
package/dist/oauth/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/oauth/types.ts"],"names":[],"mappings":""}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
export { createMcpApp, type McpApp, type McpAppAuth, type McpAppOptions, type VerifiedToken, } from "./app.js";
|
|
1
|
+
export { createMcpApp, type McpApp, type McpAppAuth, type McpAppOptions, type UnifiedAuditEntry, type VerifiedToken, } from "./app.js";
|
|
2
2
|
export { consoleAudit } from "./audit.js";
|
|
3
|
-
export {
|
|
3
|
+
export { parseBearer, rejectQueryToken, type WwwAuthenticateOptions, wwwAuthenticateHeader, } from "./auth/bearer.js";
|
|
4
4
|
export { authMethodsFor, CLIENT_PROFILES, type ClientName, type ClientProfile, DEFAULT_CLIENTS, hasDynamicClient, preRegisteredClients, redirectUrisFor, } from "./clients.js";
|
|
5
|
-
export
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export type
|
|
9
|
-
export
|
|
5
|
+
export * from "./deprecated.js";
|
|
6
|
+
export { type JsonResponseInput, type JsonResponseOptions, jsonResponse, } from "./http/http.js";
|
|
7
|
+
export { type AuditEntry, createMcpHandler, type McpHandler, type McpHandlerOptions, type McpPorts, type Principal, type ServerInfo, } from "./mcp/dispatch.js";
|
|
8
|
+
export { JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, JSONRPC_UNAUTHORIZED, type JsonRpcId, type JsonRpcRequest, type JsonRpcResponse, rpcError, rpcResult, } from "./mcp/jsonrpc.js";
|
|
9
|
+
export { DEFAULT_PROTOCOL_VERSION, PROTOCOL_VERSIONS } from "./mcp/protocol.js";
|
|
10
|
+
export { createToolRegistry, type RegistryOptions, type ToolDef, type ToolHandler, type ToolListEntry, type ToolRegistry, type ToolResult, } from "./mcp/registry.js";
|
|
11
|
+
export { type ApiRequest, type ApiToolOptions, apiTool, type DefineToolOptions, defineTool, type StandardSchemaV1, } from "./mcp/tools.js";
|
|
12
|
+
export type { JsonSchema } from "./mcp/validate.js";
|
|
13
|
+
export type { CodeStore } from "./oauth/crypto/codes.js";
|
|
10
14
|
/** Auth-port types used by `McpAppAuth` — annotate extracted stores/callbacks. */
|
|
11
|
-
export type { ClientStore, MintAccessTokenInput, MintedToken, OAuthUser, RefreshAccessTokenInput, RegisteredClient, RevokeTokenInput, } from "./oauth/types.js";
|
|
12
|
-
export { ASSUMED_HEADER_PROTOCOL_VERSION, DEFAULT_PROTOCOL_VERSION, PROTOCOL_VERSIONS, pickProtocolVersion, } from "./protocol.js";
|
|
13
|
-
export { createToolRegistry, type RegistryOptions, type ToolDef, type ToolHandler, type ToolListEntry, type ToolRegistry, type ToolResult, } from "./registry.js";
|
|
14
|
-
export { type ApiRequest, type ApiToolOptions, apiTool, type DefineToolOptions, defineTool, type StandardSchemaV1, } from "./tools.js";
|
|
15
|
-
export { IGNORED_SCHEMA_KEYWORDS, JSON_SCHEMA_TYPES, type JsonSchema, missingObjectType, SUPPORTED_SCHEMA_KEYWORDS, unsupportedKeywords, validateAgainstSchema, } from "./validate.js";
|
|
15
|
+
export type { ClientStore, ConsentOptions, ConsentRequest, MintAccessTokenInput, MintedToken, OAuthUser, RefreshAccessTokenInput, RegisteredClient, RevokeTokenInput, } from "./oauth/types.js";
|
|
16
16
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,MAAM,EACX,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,KAAK,sBAAsB,EAC3B,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,UAAU,EACf,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,UAAU,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EACpB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,QAAQ,EACR,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EACL,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,UAAU,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,OAAO,EACP,KAAK,iBAAiB,EACtB,UAAU,EACV,KAAK,gBAAgB,GACtB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,kFAAkF;AAClF,YAAY,EACV,WAAW,EACX,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,uBAAuB,EACvB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
|
package/dist/server.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { createMcpApp, } from "./app.js";
|
|
2
2
|
export { consoleAudit } from "./audit.js";
|
|
3
|
-
export {
|
|
3
|
+
export { parseBearer, rejectQueryToken, wwwAuthenticateHeader, } from "./auth/bearer.js";
|
|
4
4
|
export { authMethodsFor, CLIENT_PROFILES, DEFAULT_CLIENTS, hasDynamicClient, preRegisteredClients, redirectUrisFor, } from "./clients.js";
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
5
|
+
// Moved to `mcp-trellis/advanced`; deprecated aliases until 3.0.
|
|
6
|
+
export * from "./deprecated.js";
|
|
7
|
+
export { jsonResponse, } from "./http/http.js";
|
|
8
|
+
export { createMcpHandler, } from "./mcp/dispatch.js";
|
|
9
|
+
export { JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, JSONRPC_UNAUTHORIZED, rpcError, rpcResult, } from "./mcp/jsonrpc.js";
|
|
10
|
+
export { DEFAULT_PROTOCOL_VERSION, PROTOCOL_VERSIONS } from "./mcp/protocol.js";
|
|
11
|
+
export { createToolRegistry, } from "./mcp/registry.js";
|
|
12
|
+
export { apiTool, defineTool, } from "./mcp/tools.js";
|
|
12
13
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,GAMb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACL,WAAW,EACX,gBAAgB,EAEhB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,cAAc,EACd,eAAe,EAGf,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,iEAAiE;AACjE,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAGL,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,gBAAgB,GAMjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,EAIpB,QAAQ,EACR,SAAS,GACV,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EACL,kBAAkB,GAOnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAGL,OAAO,EAEP,UAAU,GAEX,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** `keys` of `T` with `undefined` removed from each value type, all optional. */
|
|
2
|
+
export type DefinedProps<T, K extends keyof T> = {
|
|
3
|
+
[P in K]?: Exclude<T[P], undefined>;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Copies only the listed keys whose value is not `undefined`. Under
|
|
7
|
+
* `exactOptionalPropertyTypes` an explicit `undefined` is not assignable to
|
|
8
|
+
* an optional property, so forwarding optional options needs exactly this.
|
|
9
|
+
*/
|
|
10
|
+
export declare const pickDefined: <T extends object, K extends keyof T>(source: T, keys: readonly K[]) => DefinedProps<T, K>;
|
|
11
|
+
//# sourceMappingURL=defined.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defined.d.ts","sourceRoot":"","sources":["../../src/util/defined.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI;KAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;CAAE,CAAC;AAEzF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAC7D,QAAQ,CAAC,EACT,MAAM,SAAS,CAAC,EAAE,KACjB,YAAY,CAAC,CAAC,EAAE,CAAC,CAOnB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copies only the listed keys whose value is not `undefined`. Under
|
|
3
|
+
* `exactOptionalPropertyTypes` an explicit `undefined` is not assignable to
|
|
4
|
+
* an optional property, so forwarding optional options needs exactly this.
|
|
5
|
+
*/
|
|
6
|
+
export const pickDefined = (source, keys) => {
|
|
7
|
+
const picked = {};
|
|
8
|
+
for (const key of keys) {
|
|
9
|
+
const value = source[key];
|
|
10
|
+
if (value !== undefined)
|
|
11
|
+
picked[key] = value;
|
|
12
|
+
}
|
|
13
|
+
return picked;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=defined.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defined.js","sourceRoot":"","sources":["../../src/util/defined.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,MAAS,EACT,IAAkB,EACE,EAAE;IACtB,MAAM,MAAM,GAAuB,EAAE,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAiC,CAAC;IAC3E,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/util/json.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/util/json.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAc,EAAoC,EAAE,CAC/E,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
package/docs/ROADMAP.md
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
1
1
|
# mcp-trellis — roadmap
|
|
2
2
|
|
|
3
|
-
Status: 2026-
|
|
3
|
+
Status: 2026-09-14. Written against MCP spec revision `2026-07-28`.
|
|
4
4
|
|
|
5
5
|
## What this library is
|
|
6
6
|
|
|
7
|
-
> **
|
|
8
|
-
>
|
|
7
|
+
> **Existing SaaS → authenticated AI connector SDK** — MCP + self-hosted OAuth
|
|
8
|
+
> AS in one npm install, on any Web-standard runtime, with no database and no
|
|
9
|
+
> vendor.
|
|
9
10
|
|
|
10
11
|
The differentiating axis is **clients**, not runtimes. Runtime portability is
|
|
11
12
|
table stakes; the official SDK has it too. What nobody in TypeScript ships is
|
|
12
13
|
both halves — the MCP handler *and* a self-hosted OAuth authorization server —
|
|
13
14
|
in one package you can drop into a Next.js route or a Deno server.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
Contrast composition stacks that split the MCP runtime from the identity
|
|
17
|
+
product (e.g. xmcp + Scalekit): those are 2–3 products to wire; mcp-trellis is
|
|
18
|
+
one package with BYO-auth ports.
|
|
19
|
+
|
|
20
|
+
| | Portable runtime | Self-hosted AS | One npm install | Zero-dep |
|
|
16
21
|
|---|---|---|---|---|
|
|
17
|
-
| Official SDK v2 | yes |
|
|
22
|
+
| Official SDK v2 | yes | **no** | yes (handler only) | no |
|
|
18
23
|
| `workers-oauth-provider` | Workers only | yes | yes | — |
|
|
19
|
-
| Auth0 / Authlete / Clerk
|
|
24
|
+
| Auth0 / Authlete / Clerk | yes | **no** (SaaS) | no (IdP + MCP) | — |
|
|
20
25
|
| Better Auth | yes | yes | yes, but full stack + DB | no |
|
|
21
|
-
| **
|
|
26
|
+
| **xmcp** | yes | **no** (compose) | no — pair with IdP | — |
|
|
27
|
+
| **Scalekit** | yes | managed connector IdP | no — pair with MCP runtime | — |
|
|
28
|
+
| **WorkOS Standalone Connect** | yes | managed | no — IdP + your MCP | — |
|
|
29
|
+
| **Descope** | yes | managed | no — IdP + your MCP | — |
|
|
30
|
+
| **mcp-trellis** | yes | yes | **yes** (handler + AS) | yes |
|
|
22
31
|
|
|
23
32
|
The official SDK explicitly does not provide an authorization server — it offers
|
|
24
33
|
SDK-level opt-ins and expects you to bring the infrastructure. That gap is the
|
|
@@ -110,7 +119,9 @@ a proper JSON-RPC error.
|
|
|
110
119
|
- **Claims on `Principal`.** Optional `claims` from `verifyToken` /
|
|
111
120
|
`VerifiedToken` flow into `context` (plan/role metadata — not the
|
|
112
121
|
Host→tenant isolation key). Host-origin allowlisting (`allowedOrigins` on
|
|
113
|
-
`asNodeHandler`) is **required** when origin is Host-derived.
|
|
122
|
+
`asNodeHandler`) is **required** when origin is Host-derived. Separately,
|
|
123
|
+
browser `Origin` on MCP is gated by `allowedRequestOrigins` (fail-closed
|
|
124
|
+
when the header is present; native clients omit it).
|
|
114
125
|
|
|
115
126
|
- **`validateArgs` fail-fast.** With `validateArgs: true`, unsupported JSON
|
|
116
127
|
Schema keywords throw at `createToolRegistry` construction instead of
|
|
@@ -210,49 +221,26 @@ a proper JSON-RPC error.
|
|
|
210
221
|
MCP audit hook. Sees the real reason behind a collapsed `invalid_client`
|
|
211
222
|
or a rejected `codeSecret`, which the caller never does.
|
|
212
223
|
|
|
224
|
+
**2.0 — CIMD, unified audit, recipes.**
|
|
225
|
+
|
|
226
|
+
- **CIMD (Client ID Metadata Documents)** — opt-in (`cimd: true`). HTTPS URL
|
|
227
|
+
`client_id`s are fetched and validated with SSRF hardening (public hosts
|
|
228
|
+
only, size / redirect / timeout caps). Advertises
|
|
229
|
+
`client_id_metadata_document_supported`. Optional `cimdCache` port.
|
|
230
|
+
Off by default until connect-pinned fetch closes the DNS-rebinding ceiling.
|
|
231
|
+
- **Unified `audit` sink** on `createMcpApp` — one callback for MCP and
|
|
232
|
+
OAuth events (`source: "mcp" | "oauth"`); `auth.audit` remains an
|
|
233
|
+
OAuth-only override.
|
|
234
|
+
- **Framework recipes** — `examples/nextjs-route.ts`, `examples/express.ts`.
|
|
235
|
+
- Fail-closed defaults: `allowInMemoryCodeStore` / `validateArgs`.
|
|
236
|
+
- **RFC 9207 `iss`** on authorize / error redirects; AS metadata advertises
|
|
237
|
+
`authorization_response_iss_parameter_supported`.
|
|
238
|
+
|
|
213
239
|
---
|
|
214
240
|
|
|
215
241
|
## Next
|
|
216
242
|
|
|
217
|
-
### 1.
|
|
218
|
-
|
|
219
|
-
DCR is deprecated as of `2026-07-28`; CIMD is the recommended replacement and
|
|
220
|
-
the direction Claude and Codex are heading. The client publishes JSON at an
|
|
221
|
-
HTTPS URL and uses that URL as its `client_id`; the AS resolves it on demand.
|
|
222
|
-
|
|
223
|
-
AS-side requirements: detect URL-formatted `client_id`; fetch it; **MUST**
|
|
224
|
-
validate the document's `client_id` equals the URL exactly; **MUST** validate
|
|
225
|
-
the request's `redirect_uri` against the document's `redirect_uris`; **SHOULD**
|
|
226
|
-
cache respecting HTTP cache headers; advertise
|
|
227
|
-
`client_id_metadata_document_supported: true`.
|
|
228
|
-
|
|
229
|
-
This slots cleanly into the existing shape — pure `fetch` plus validation, with
|
|
230
|
-
caching as a port. It also completes the picture: the spec defines exactly three
|
|
231
|
-
registration mechanisms, and they map one-to-one onto the three clients.
|
|
232
|
-
|
|
233
|
-
```
|
|
234
|
-
CIMD → Claude, Codex (public + PKCE)
|
|
235
|
-
pre-registration → Gemini Enterprise (confidential) ← shipped
|
|
236
|
-
DCR → legacy fallback ← shipped
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
The security work is the real work: an AS that fetches attacker-supplied URLs is
|
|
240
|
-
an SSRF engine unless it blocks private/loopback/link-local ranges and DNS
|
|
241
|
-
rebinding, caps redirects, response size, and timeout, and refuses non-HTTPS.
|
|
242
|
-
Cloudflare's implementation is a useful reference for what "done right" means
|
|
243
|
-
(5 KB cap, 10 s timeout, public clients only).
|
|
244
|
-
|
|
245
|
-
Note: CIMD is not unclaimed. Authlete, Auth0, Clerk, and `workers-oauth-provider`
|
|
246
|
-
all ship it. The claim here is narrower and still true — none of them is a
|
|
247
|
-
portable, self-hosted, zero-dependency library.
|
|
248
|
-
|
|
249
|
-
### 2. RFC 9207 — `iss` in the authorization response
|
|
250
|
-
|
|
251
|
-
Add `iss` to the authorize redirect and
|
|
252
|
-
`authorization_response_iss_parameter_supported` to AS metadata. Small, and a
|
|
253
|
-
spec-level SHOULD.
|
|
254
|
-
|
|
255
|
-
### 3. Protocol currency — `2026-07-28`
|
|
243
|
+
### 1. Protocol currency — `2026-07-28`
|
|
256
244
|
|
|
257
245
|
Deferred deliberately: Claude, Gemini, and Codex all work against current
|
|
258
246
|
revisions today, and the official SDK already covers dual-era for anyone who
|