mcp-trellis 0.2.1 → 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 +70 -620
- 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 +16 -24
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +69 -88
- 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 -66
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +150 -57
- 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 +25 -5
- package/dist/auth/bearer.js.map +1 -1
- package/dist/clients.d.ts.map +1 -1
- package/dist/clients.js +3 -6
- 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/http/body.d.ts +69 -0
- package/dist/http/body.d.ts.map +1 -0
- package/dist/http/body.js +165 -0
- 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/mcp/dispatch.d.ts +7 -0
- 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} +10 -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/mcp/methods.d.ts +133 -0
- 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/mcp/protocol.d.ts +16 -0
- package/dist/mcp/protocol.d.ts.map +1 -0
- package/dist/mcp/protocol.js +22 -0
- 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} +30 -2
- 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} +24 -12
- package/dist/mcp/validate.d.ts.map +1 -0
- package/dist/mcp/validate.js +186 -0
- 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 +15 -0
- package/dist/oauth/constants.d.ts.map +1 -1
- package/dist/oauth/constants.js +15 -0
- package/dist/oauth/constants.js.map +1 -1
- package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +1 -2
- package/dist/oauth/crypto/base64url.d.ts.map +1 -0
- package/dist/oauth/{base64url.js → crypto/base64url.js} +1 -5
- 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/crypto/sealed.d.ts +26 -0
- package/dist/oauth/crypto/sealed.d.ts.map +1 -0
- package/dist/oauth/crypto/sealed.js +65 -0
- package/dist/oauth/crypto/sealed.js.map +1 -0
- package/dist/oauth/crypto/secrethash.d.ts +58 -0
- package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
- package/dist/oauth/crypto/secrethash.js +82 -0
- 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} +14 -8
- 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} +11 -9
- 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} +33 -47
- 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 -7
- package/dist/oauth/endpoints/wellknown.js.map +1 -0
- package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +6 -1
- package/dist/oauth/policy/redirect.d.ts.map +1 -0
- package/dist/oauth/policy/redirect.js +39 -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/policy/resource.d.ts +37 -0
- package/dist/oauth/policy/resource.d.ts.map +1 -0
- package/dist/oauth/policy/resource.js +74 -0
- package/dist/oauth/policy/resource.js.map +1 -0
- package/dist/oauth/policy/scope.d.ts +20 -0
- package/dist/oauth/policy/scope.d.ts.map +1 -0
- package/dist/oauth/policy/scope.js +35 -0
- package/dist/oauth/policy/scope.js.map +1 -0
- package/dist/oauth/router.d.ts +12 -12
- package/dist/oauth/router.d.ts.map +1 -1
- package/dist/oauth/router.js +105 -87
- package/dist/oauth/router.js.map +1 -1
- package/dist/oauth/types.d.ts +177 -26
- package/dist/oauth/types.d.ts.map +1 -1
- package/dist/oauth/types.js +1 -27
- package/dist/oauth/types.js.map +1 -1
- package/dist/server.d.ts +14 -13
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -9
- 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 +283 -0
- package/docs/compatibility.md +84 -0
- package/docs/diagrams/mcp-trellis-architecture.html +305 -0
- package/docs/guide.md +441 -0
- package/docs/reference.md +210 -0
- package/docs/security.md +112 -0
- package/docs/troubleshooting.md +19 -0
- package/package.json +24 -8
- package/dist/adapters/origins.d.ts +0 -19
- package/dist/adapters/origins.d.ts.map +0 -1
- package/dist/adapters/origins.js +0 -55
- package/dist/adapters/origins.js.map +0 -1
- package/dist/dispatch.d.ts +0 -7
- package/dist/dispatch.d.ts.map +0 -1
- package/dist/dispatch.js +0 -79
- 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 -17
- package/dist/jsonrpc.js.map +0 -1
- package/dist/methods.d.ts +0 -61
- package/dist/methods.d.ts.map +0 -1
- package/dist/methods.js +0 -114
- 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 -109
- 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 -19
- package/dist/oauth/clientauth.d.ts.map +0 -1
- package/dist/oauth/clientauth.js +0 -76
- 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 -93
- package/dist/oauth/codes.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 +0 -26
- package/dist/oauth/redirect.js.map +0 -1
- package/dist/oauth/resource.d.ts +0 -7
- package/dist/oauth/resource.d.ts.map +0 -1
- package/dist/oauth/resource.js +0 -44
- 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 +0 -11
- package/dist/oauth/scope.d.ts.map +0 -1
- package/dist/oauth/scope.js +0 -24
- package/dist/oauth/scope.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 +0 -4
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -14
- 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 -69
- package/dist/registry.js.map +0 -1
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -97
- package/dist/tools.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js +0 -130
- package/dist/validate.js.map +0 -1
- /package/dist/oauth/{pkce.d.ts → crypto/pkce.d.ts} +0 -0
package/dist/server.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
export { createMcpApp, } from "./app.js";
|
|
2
|
-
export { CLIENT_PROFILES, DEFAULT_CLIENTS, authMethodsFor, redirectUrisFor, preRegisteredClients, hasDynamicClient, } from "./clients.js";
|
|
3
|
-
export { createMcpHandler, } from "./dispatch.js";
|
|
4
2
|
export { consoleAudit } from "./audit.js";
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
export
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
3
|
+
export { parseBearer, rejectQueryToken, wwwAuthenticateHeader, } from "./auth/bearer.js";
|
|
4
|
+
export { authMethodsFor, CLIENT_PROFILES, DEFAULT_CLIENTS, hasDynamicClient, preRegisteredClients, redirectUrisFor, } from "./clients.js";
|
|
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
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# mcp-trellis — roadmap
|
|
2
|
+
|
|
3
|
+
Status: 2026-09-14. Written against MCP spec revision `2026-07-28`.
|
|
4
|
+
|
|
5
|
+
## What this library is
|
|
6
|
+
|
|
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.
|
|
10
|
+
|
|
11
|
+
The differentiating axis is **clients**, not runtimes. Runtime portability is
|
|
12
|
+
table stakes; the official SDK has it too. What nobody in TypeScript ships is
|
|
13
|
+
both halves — the MCP handler *and* a self-hosted OAuth authorization server —
|
|
14
|
+
in one package you can drop into a Next.js route or a Deno server.
|
|
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 |
|
|
21
|
+
|---|---|---|---|---|
|
|
22
|
+
| Official SDK v2 | yes | **no** | yes (handler only) | no |
|
|
23
|
+
| `workers-oauth-provider` | Workers only | yes | yes | — |
|
|
24
|
+
| Auth0 / Authlete / Clerk | yes | **no** (SaaS) | no (IdP + MCP) | — |
|
|
25
|
+
| Better Auth | yes | yes | yes, but full stack + DB | no |
|
|
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 |
|
|
31
|
+
|
|
32
|
+
The official SDK explicitly does not provide an authorization server — it offers
|
|
33
|
+
SDK-level opt-ins and expects you to bring the infrastructure. That gap is the
|
|
34
|
+
product.
|
|
35
|
+
|
|
36
|
+
### The rule that decides design questions
|
|
37
|
+
|
|
38
|
+
**If a user can forget to do something and the result is a security hole, it
|
|
39
|
+
goes in the code, not the README.** Audience validation moved into
|
|
40
|
+
`createMcpApp` for exactly this reason.
|
|
41
|
+
|
|
42
|
+
### The acceptance test
|
|
43
|
+
|
|
44
|
+
From `npm install` to a connected client: **under 15 lines and one secret.**
|
|
45
|
+
Anything that forces the user to route requests, read an RFC, or install a
|
|
46
|
+
second package fails.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Shipped
|
|
51
|
+
|
|
52
|
+
**Phase 0 — trust.** Query-string tokens now cover RFC 6750's `access_token`;
|
|
53
|
+
audit fires on every denial including transport-level ones, and a throwing audit
|
|
54
|
+
port can no longer fail a request; tool exceptions are redacted by default;
|
|
55
|
+
a relative `loginUrl` resolves instead of throwing; explicit `id: null` returns
|
|
56
|
+
a proper JSON-RPC error.
|
|
57
|
+
|
|
58
|
+
**Phase 1 — the app layer and the Gemini unblock.**
|
|
59
|
+
|
|
60
|
+
- `createMcpApp` — one call mounts tools, the AS, both discovery documents, and
|
|
61
|
+
the routing between them. `createMcpHandler` / `createOAuthRouter` stay
|
|
62
|
+
exported for anyone who wants to compose the pieces.
|
|
63
|
+
- **Audience validation is enforced by the library.** `verifyToken` returns the
|
|
64
|
+
token's audience; `createMcpApp` compares it against `canonicalResource` and
|
|
65
|
+
rejects mismatches before any tool runs.
|
|
66
|
+
- **Client profiles** (`claude` / `gemini` / `codex`) compose the redirect
|
|
67
|
+
allowlist and the advertised token-endpoint auth methods. Client-specific
|
|
68
|
+
knowledge is the asset, so it lives in the library.
|
|
69
|
+
- **Confidential clients and pre-registration** via a `clientStore` port —
|
|
70
|
+
`client_secret_basic` and `client_secret_post`. This is what Gemini Enterprise
|
|
71
|
+
needs, and it was previously impossible: the AS advertised `["none"]` only.
|
|
72
|
+
Secret storage and comparison stay in the port; credentials never enter the
|
|
73
|
+
library.
|
|
74
|
+
- **Naming only confidential clients is enforced, not just advertised.**
|
|
75
|
+
The first cut of this let `clients: ["gemini"]` configure the AS's metadata
|
|
76
|
+
while leaving DCR and the loopback allowlist wide open — a self-registered
|
|
77
|
+
public client could still complete a normal PKCE flow and get a token,
|
|
78
|
+
quietly defeating the "confidential clients only" intent. Fixed:
|
|
79
|
+
`allowUnregisteredClients: false` (derived automatically by `createMcpApp`
|
|
80
|
+
when no configured client registers dynamically) unmounts `/register`, drops
|
|
81
|
+
it from AS metadata, and rejects any `client_id` the `clientStore` doesn't
|
|
82
|
+
resolve — `unauthorized_client` at `authorize`, `invalid_client` at `token`.
|
|
83
|
+
Public type break: `AuthorizationServerMetadata.registration_endpoint` is
|
|
84
|
+
now optional (`string | undefined`), omitted when DCR is off.
|
|
85
|
+
- **Scope threaded end-to-end.** `authorize` validates the requested scope
|
|
86
|
+
against the advertised set, the auth code carries the grant, and
|
|
87
|
+
`mintAccessToken` receives it. `ToolDef.scope` finally sits on a chain that
|
|
88
|
+
reaches the OAuth layer.
|
|
89
|
+
- Node adapter drains the request stream when `req.body` is absent, so raw
|
|
90
|
+
`http.createServer` works without a body-parser.
|
|
91
|
+
- `[::1]` added to the loopback redirect allowlist.
|
|
92
|
+
- **`defineTool` / `apiTool`.** The library was easy to run but not easy to
|
|
93
|
+
*author tools for* — every tool hand-wrote its own JSON Schema disconnected
|
|
94
|
+
from its handler's types, and every API-backed tool re-invented fetch/error
|
|
95
|
+
plumbing. `defineTool` adds optional Standard Schema v1 parsing in front of
|
|
96
|
+
parsing in front of a handler — typed args, validation failures short-circuit
|
|
97
|
+
before the handler runs. `apiTool` builds on it: give it `request` (+
|
|
98
|
+
optional `respond`) and get a working REST-call tool, non-2xx handled as
|
|
99
|
+
`isError` automatically. `inputSchema` (JSON Schema, for `tools/list`) stays
|
|
100
|
+
required and separate — no library-agnostic way exists to derive it from an
|
|
101
|
+
arbitrary Standard Schema validator, so the two must be kept in sync by hand.
|
|
102
|
+
- **No host port can crash `fetch`.** Nothing guarded `authenticate`,
|
|
103
|
+
`context`, `resolveUser`, `mintAccessToken`, `clientStore`, etc. — any of
|
|
104
|
+
them throwing turned `.fetch()` into a rejected promise instead of a
|
|
105
|
+
`Response`, with the fallout depending on the runtime (a generic error page
|
|
106
|
+
on Workers, a hung socket with no response ever sent on raw
|
|
107
|
+
`http.createServer`). `createMcpHandler`, `createOAuthRouter`,
|
|
108
|
+
`createMcpApp`, and `asNodeHandler` now each catch at their own boundary and
|
|
109
|
+
return a real `500` instead.
|
|
110
|
+
|
|
111
|
+
- **RFC 7009 token revocation.** The library never stores access or refresh
|
|
112
|
+
tokens, so revocation is a host port — `revokeToken` — advertised and mounted
|
|
113
|
+
only when implemented (same honest-advertisement pattern as refresh).
|
|
114
|
+
Well-formed authenticated `POST /revoke` returns 200 even if the token is
|
|
115
|
+
unknown (`invalid_request` 400 and `invalid_client` 401 still apply).
|
|
116
|
+
Stolen-token invalidation lives in the host store that `verifyToken` and
|
|
117
|
+
`refreshAccessToken` consult.
|
|
118
|
+
|
|
119
|
+
- **Claims on `Principal`.** Optional `claims` from `verifyToken` /
|
|
120
|
+
`VerifiedToken` flow into `context` (plan/role metadata — not the
|
|
121
|
+
Host→tenant isolation key). Host-origin allowlisting (`allowedOrigins` on
|
|
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).
|
|
125
|
+
|
|
126
|
+
- **`validateArgs` fail-fast.** With `validateArgs: true`, unsupported JSON
|
|
127
|
+
Schema keywords throw at `createToolRegistry` construction instead of
|
|
128
|
+
silently looking enforced. Default (off) is unchanged.
|
|
129
|
+
|
|
130
|
+
**Phase 2 — the authorize endpoint (0.2.2 / 0.3.0).**
|
|
131
|
+
|
|
132
|
+
- **Consent step at `/authorize`.** A resolved session no longer issues a
|
|
133
|
+
code and redirects on its own; it renders an approval interstitial (built
|
|
134
|
+
in, or your own via `consent.render`) and only issues a code after an
|
|
135
|
+
explicit `POST ${oauthPath}/consent` approval. Closes a cross-site flow
|
|
136
|
+
where an attacker-controlled page could otherwise walk a logged-in user
|
|
137
|
+
through `/authorize` with an attacker-chosen `client_id`/`code_challenge`
|
|
138
|
+
and a loopback `redirect_uri`, catching the code with a local listener —
|
|
139
|
+
PKCE alone does not defend against this, it proves possession of the
|
|
140
|
+
verifier, not the identity of a legitimate client. Approval tickets are
|
|
141
|
+
AES-GCM sealed (`sealed.ts`), single-use, and re-checked against the
|
|
142
|
+
resolved user at redemption.
|
|
143
|
+
- **DCR `client_id` is bound to its own `redirect_uris`**, zero storage — the
|
|
144
|
+
id issued by `/register` is a sealed, self-verifying assertion of the list
|
|
145
|
+
it registered. Stops a substituted `redirect_uri` on a legitimate
|
|
146
|
+
connector's id (not impersonation via self-registration — the consent step
|
|
147
|
+
above is what stops that). `requireRegisteredClients` (default `true`
|
|
148
|
+
since 1.0) is the stricter default, and a direct stepping stone to CIMD below.
|
|
149
|
+
- **`codeSecret` is validated** — minimum 32 characters, and the literal
|
|
150
|
+
values this package's own docs/examples publish are denylisted by name.
|
|
151
|
+
It is the single key that can forge an auth code, consent ticket, or DCR
|
|
152
|
+
client assertion for any userId/scope/resource, and had no validation at
|
|
153
|
+
all before this.
|
|
154
|
+
- **Loopback redirect allowlist narrowed** — `localhost` and `https:`
|
|
155
|
+
loopback are no longer accepted (RFC 8252 §8.3); a `redirect_uri` carrying
|
|
156
|
+
a fragment or embedded credentials is rejected for every predicate, not
|
|
157
|
+
only loopback.
|
|
158
|
+
- **`defaultScopes` required for multi-scope servers.** `createOAuthRouter`
|
|
159
|
+
now throws at construction when `scopes` has more than one entry and
|
|
160
|
+
`defaultScopes` is unset, instead of silently granting the full advertised
|
|
161
|
+
set to a client that asked for nothing.
|
|
162
|
+
- **`timingSafeEqual("", "")` now returns `false`**, not `true` — a public
|
|
163
|
+
export, so a host comparing a presented secret against an unset stored one
|
|
164
|
+
no longer authenticates.
|
|
165
|
+
- **Request body size limits** on `/mcp`, `/token`, `/revoke`, and
|
|
166
|
+
`/register` — previously unbounded on every one of them.
|
|
167
|
+
|
|
168
|
+
**Phase 3 — credentials and correctness (0.4.0).**
|
|
169
|
+
|
|
170
|
+
- **Auth codes sealed (AES-GCM), not signed-only.** v1 (HMAC) codes were
|
|
171
|
+
readable by anyone who saw one; v2 codes are encrypted too. Soft
|
|
172
|
+
migration in 0.4.0 / 0.5.0 (read both, write v2); **1.0.0 drops v1 reading**.
|
|
173
|
+
- **`ClientStore.secretHash`**, preferred over `verifySecret`: return a
|
|
174
|
+
stored hash from the new `hashClientSecret` and the library does the
|
|
175
|
+
comparison with the same constant-time primitive it uses everywhere
|
|
176
|
+
else. Keyed by `codeSecret` — no second secret to manage.
|
|
177
|
+
- **Client-auth enumeration oracle closed.** `/token` and `/revoke`
|
|
178
|
+
collapsed five distinguishable error messages into one; a locked-down
|
|
179
|
+
server also pays a fixed-cost dummy comparison for an unknown
|
|
180
|
+
`client_id`, so response timing can't distinguish "unknown" from
|
|
181
|
+
"known, wrong secret" either. Unaffected: public-client traffic, and
|
|
182
|
+
`/authorize`'s deliberately-still-descriptive `unauthorized_client`.
|
|
183
|
+
- **`/revoke` accepts JSON**, matching `/token` — previously form-only,
|
|
184
|
+
so a JSON revoke silently failed while the caller believed it worked.
|
|
185
|
+
- **`apiTool` no longer forwards the upstream error body by default** —
|
|
186
|
+
status only; opt in via the new `onError`.
|
|
187
|
+
- **`missingObjectType`** — a schema using `properties`/`required`
|
|
188
|
+
without `type: "object"` now throws at construction under
|
|
189
|
+
`validateArgs: true`, and is enforced at runtime by dispatching on the
|
|
190
|
+
value's actual shape rather than trusting a schema author remembered to
|
|
191
|
+
declare `type`.
|
|
192
|
+
- **Union `type` arrays** (`type: ["string", "null"]`) now evaluate
|
|
193
|
+
correctly — previously always failed.
|
|
194
|
+
- **`/authorize` errors redirect to the client's callback** (RFC 6749
|
|
195
|
+
§4.1.2.1) once `redirect_uri` is validated, instead of a bare JSON body
|
|
196
|
+
the connector never parses — this was silently connector-breaking.
|
|
197
|
+
|
|
198
|
+
**Phase 4 — protocol correctness and observability (0.5.0).**
|
|
199
|
+
|
|
200
|
+
- **`jsonrpc` validated on every `/mcp` request** — `-32600` on anything
|
|
201
|
+
but exactly `"2.0"`, including a missing field. Previously accepted
|
|
202
|
+
silently.
|
|
203
|
+
- **`MCP-Protocol-Version` header read and validated**, per the
|
|
204
|
+
2025-06-18 spec — unsupported → 400, missing → assumed `2025-03-26`
|
|
205
|
+
(the spec's own compatibility default), not checked on `initialize`
|
|
206
|
+
(which is what negotiates the version). Previously only appeared in the
|
|
207
|
+
CORS allow-list, never actually read.
|
|
208
|
+
- **`initialize` defaults to the newest supported protocol version**
|
|
209
|
+
(`2025-06-18`) for a missing/unrecognized request, not the oldest.
|
|
210
|
+
- **401 responses echo the real request id** instead of hardcoding `null`.
|
|
211
|
+
- **`/mcp/` routes identically to `/mcp`.** `canonicalResource` normalizes
|
|
212
|
+
its `resourcePath` argument internally, so route matching, `/authorize`,
|
|
213
|
+
and `/token` all agree — previously only some call sites normalized a
|
|
214
|
+
trailing slash and others read a raw, unnormalized value.
|
|
215
|
+
- **`createOAuthRouter` refuses to construct** if `oauthPath` would equal
|
|
216
|
+
`resourcePath` (they'd shadow each other) or if `resourcePath` starts
|
|
217
|
+
with `/.well-known` (reserved for discovery documents).
|
|
218
|
+
- **`auditTimeoutMs`** (default 1000ms) — a hanging `ports.audit` can no
|
|
219
|
+
longer stall a response indefinitely; it already couldn't fail one.
|
|
220
|
+
- **`OAuthPorts.audit` / `McpAppAuth.audit`** — the OAuth-side twin of the
|
|
221
|
+
MCP audit hook. Sees the real reason behind a collapsed `invalid_client`
|
|
222
|
+
or a rejected `codeSecret`, which the caller never does.
|
|
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
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Next
|
|
242
|
+
|
|
243
|
+
### 1. Protocol currency — `2026-07-28`
|
|
244
|
+
|
|
245
|
+
Deferred deliberately: Claude, Gemini, and Codex all work against current
|
|
246
|
+
revisions today, and the official SDK already covers dual-era for anyone who
|
|
247
|
+
needs it now. But a batteries-included server that speaks an outdated revision
|
|
248
|
+
is worse than useless, so this becomes mandatory once real clients move.
|
|
249
|
+
|
|
250
|
+
Work: remove `initialize` / `ping`, add `server/discover` (MUST), carry protocol
|
|
251
|
+
version and client capabilities in `_meta` per request, `resultType` on every
|
|
252
|
+
result, `ttlMs` / `cacheScope` on `tools/list`, `Mcp-Method` / `Mcp-Name`
|
|
253
|
+
headers, deterministic `tools/list` ordering. Add `2025-11-25` and `2026-07-28`
|
|
254
|
+
to the version table and stop defaulting to the oldest revision.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Non-goals
|
|
259
|
+
|
|
260
|
+
Backed by the spec, not just by our own scoping:
|
|
261
|
+
|
|
262
|
+
- **No sampling / roots / logging** — deprecated in `2026-07-28`
|
|
263
|
+
- **No sessions** — removed from the protocol
|
|
264
|
+
- **No SSE resumability** — removed from the transport
|
|
265
|
+
- **No HTTP+SSE transport** — deprecated; Gemini Enterprise refuses it outright
|
|
266
|
+
- **No login UI, token store, or IdP** — the ports stay the product. The
|
|
267
|
+
`/authorize` consent/approval screen is shipped (built in, overridable),
|
|
268
|
+
since it's a security control determined by protocol data the library
|
|
269
|
+
already holds, not by your IdP or branding; login itself stays yours
|
|
270
|
+
- **No resources / prompts** — a real limitation; revisit on demand
|
|
271
|
+
- **No DPoP** — sender-constrained tokens; connectors use bearer + PKCE
|
|
272
|
+
- **No PAR (RFC 9126)** — pushed authorization requests
|
|
273
|
+
- **No `client_credentials`** — connectors are user-delegated; this AS is authorization_code (+ optional refresh)
|
|
274
|
+
|
|
275
|
+
Deferred until asked: MRTR (`input_required`), `subscriptions/listen`,
|
|
276
|
+
`io.modelcontextprotocol/tasks`.
|
|
277
|
+
|
|
278
|
+
## Known gaps
|
|
279
|
+
|
|
280
|
+
Tracked, not scheduled: `pruneMemory` scans the whole jti map on every code
|
|
281
|
+
consume; `mcpWwwAuthenticate` and `wwwAuthenticateHeader` build the same
|
|
282
|
+
header twice. (Unsupported JSON Schema keywords under `validateArgs: true`
|
|
283
|
+
now throw at `createToolRegistry` construction — no longer a silent gap.)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Compatibility evidence
|
|
2
|
+
|
|
3
|
+
Updated 2026-09-14. Repository implementation and automated coverage are in good
|
|
4
|
+
shape after the pre-prod security pass. **Vendor-client UI sessions are still
|
|
5
|
+
Pending** — do not convert product rows to ✅ until a live Claude/ChatGPT/Codex/Gemini
|
|
6
|
+
check below is filled in.
|
|
7
|
+
|
|
8
|
+
| Product | Server profile | Repository evidence | Live evidence |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| Claude custom connector | `claude`: DCR / CIMD, public client, S256 PKCE; built-in callback | `test/oauth/policy/clients.test.ts`, `test/oauth/endpoints/dcr.test.ts`, `test/oauth/cimd/cimd.test.ts`, `test/oauth/crypto/pkce.test.ts`; saas-demo e2e + `scripts/prep-claude-oauth.mjs` | Pending (UI) |
|
|
11
|
+
| ChatGPT | `codex`: public-client profile; exact hosted callback must be configured | Profile tests only; no ChatGPT UI test | Pending |
|
|
12
|
+
| Codex | `codex`: public client, PKCE, loopback callbacks allowed | Profile/redirect tests; no actual Codex login run | Pending |
|
|
13
|
+
| Gemini Enterprise | `gemini`: `clientStore`, pre-registration, `client_secret_basic` / `client_secret_post` | Client authentication tests; no Enterprise environment | Pending |
|
|
14
|
+
|
|
15
|
+
The Gemini profile does not imply compatibility with Gemini CLI or every Gemini product. ChatGPT and Codex need separate live checks even though the package groups them under one profile.
|
|
16
|
+
|
|
17
|
+
`src/mcp/protocol.ts` implements `2024-11-05`, `2025-03-26`, `2025-06-18`. Initialization defaults to `2025-06-18`; absent request headers use `2025-03-26`. This is an implementation inventory, not a claim to implement the latest revision. **CIMD is shipped opt-in** (`cimd: true` on `createMcpApp` / `createOAuthRouter`; off by default — see [security.md](security.md) SSRF ceiling). Live vendor UI evidence stays **Pending** until an operator connects a real client.
|
|
18
|
+
|
|
19
|
+
## Demo deploy (Worker)
|
|
20
|
+
|
|
21
|
+
Compile-checked Worker sketch: [`examples/cloudflare-worker.ts`](../examples/cloudflare-worker.ts).
|
|
22
|
+
|
|
23
|
+
One-liner after you add a minimal `wrangler.toml` (name, `compatibility_date`, and secrets
|
|
24
|
+
`OAUTH_CODE_SECRET` / `ACCESS_TOKEN_SECRET`):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx wrangler secret put OAUTH_CODE_SECRET
|
|
28
|
+
npx wrangler secret put ACCESS_TOKEN_SECRET
|
|
29
|
+
npx wrangler deploy examples/cloudflare-worker.ts
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Point a connector at `https://YOUR-WORKER/mcp` only after secrets and an origin
|
|
33
|
+
allowlist match your route. This path proves the Worker mounts; it does **not**
|
|
34
|
+
replace the live client rows below.
|
|
35
|
+
|
|
36
|
+
## Host contract covered in-repo
|
|
37
|
+
|
|
38
|
+
`examples/saas-demo` now mints refresh tokens and enforces RFC 6749 §6: requested
|
|
39
|
+
refresh `scope` must be ⊆ the scopes originally bound to that refresh token
|
|
40
|
+
(`isScopeSubset` pattern also in `examples/stores.ts`). Escalation →
|
|
41
|
+
`invalid_grant`. Covered by `test/e2e/saas-demo.test.ts`.
|
|
42
|
+
|
|
43
|
+
## Automated package smoke (not a vendor live pass)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm run smoke:pack
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Installs the local `npm pack` tarball in a temp directory and imports
|
|
50
|
+
`createMcpApp` / `asNodeHandler`. Record date/commit when you run it before a
|
|
51
|
+
release; it does **not** replace the live client row.
|
|
52
|
+
|
|
53
|
+
| Date | Commit / package | Command | Result |
|
|
54
|
+
|---|---|---|---|
|
|
55
|
+
| 2026-09-12 | unreleased working tree (`1.1.0` pack) | `npm run smoke:pack` | Pass — `createMcpApp` + `asNodeHandler` import from tarball |
|
|
56
|
+
|
|
57
|
+
## Evidence required for a live pass
|
|
58
|
+
|
|
59
|
+
Record client product, version/build (or web UI and test date), account tier, package version/commit, hosting environment and configured callback. Capture discovery → registration → app login → consent → token exchange → initialize → tools/list → `list_my_projects`. Verify wrong audience/invalid token rejection and user isolation separately. Save sanitized output or recording; exclude cookies, passwords, codes and tokens.
|
|
60
|
+
|
|
61
|
+
Against a public HTTPS demo (`PUBLIC_ORIGIN` + `DEMO_PASSWORD`):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run demo # reverse-proxy to HTTPS; set PUBLIC_ORIGIN
|
|
65
|
+
node scripts/prep-claude-oauth.mjs # scripted OAuth + refresh scope + tool
|
|
66
|
+
# Then open the vendor UI with MCP URL https://YOUR-HOST/mcp and record below.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Date | Client/build | Package/commit | Protocol negotiated | Tool + expected result | Evidence | Result |
|
|
70
|
+
|---|---|---|---|---|---|---|
|
|
71
|
+
| — | — | — | — | — | — | Pending |
|
|
72
|
+
|
|
73
|
+
## Under-ten-minute onboarding test
|
|
74
|
+
|
|
75
|
+
Use a clean folder and the published npm tarball, start a timer at `npm install`, and stop at a real client's first correct user-scoped tool response. Record setup prerequisites, elapsed time, every edit and any error. A local source import or simulated OAuth flow is insufficient. Target: under ten minutes with hosting and a supported client account available; no time claim is published until measured.
|
|
76
|
+
|
|
77
|
+
| Field | Value |
|
|
78
|
+
|---|---|
|
|
79
|
+
| Start (`npm install`) | — |
|
|
80
|
+
| Stop (first correct tool in real client) | — |
|
|
81
|
+
| Elapsed | — |
|
|
82
|
+
| Prerequisites (hosting, account, callback) | — |
|
|
83
|
+
| Edits / errors | — |
|
|
84
|
+
| Result | Pending |
|