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.
Files changed (293) hide show
  1. package/README.md +70 -620
  2. package/dist/adapters/node-request.d.ts +53 -0
  3. package/dist/adapters/node-request.d.ts.map +1 -0
  4. package/dist/adapters/node-request.js +165 -0
  5. package/dist/adapters/node-request.js.map +1 -0
  6. package/dist/adapters/node.d.ts +16 -24
  7. package/dist/adapters/node.d.ts.map +1 -1
  8. package/dist/adapters/node.js +69 -88
  9. package/dist/adapters/node.js.map +1 -1
  10. package/dist/advanced.d.ts +18 -0
  11. package/dist/advanced.d.ts.map +1 -0
  12. package/dist/advanced.js +18 -0
  13. package/dist/advanced.js.map +1 -0
  14. package/dist/app-options.d.ts +132 -0
  15. package/dist/app-options.d.ts.map +1 -0
  16. package/dist/app-options.js +2 -0
  17. package/dist/app-options.js.map +1 -0
  18. package/dist/app.d.ts +2 -66
  19. package/dist/app.d.ts.map +1 -1
  20. package/dist/app.js +150 -57
  21. package/dist/app.js.map +1 -1
  22. package/dist/audit.d.ts +5 -5
  23. package/dist/audit.d.ts.map +1 -1
  24. package/dist/audit.js +19 -8
  25. package/dist/audit.js.map +1 -1
  26. package/dist/auth/bearer.d.ts +4 -1
  27. package/dist/auth/bearer.d.ts.map +1 -1
  28. package/dist/auth/bearer.js +25 -5
  29. package/dist/auth/bearer.js.map +1 -1
  30. package/dist/clients.d.ts.map +1 -1
  31. package/dist/clients.js +3 -6
  32. package/dist/clients.js.map +1 -1
  33. package/dist/deprecated.d.ts +42 -0
  34. package/dist/deprecated.d.ts.map +1 -0
  35. package/dist/deprecated.js +35 -0
  36. package/dist/deprecated.js.map +1 -0
  37. package/dist/http/body.d.ts +69 -0
  38. package/dist/http/body.d.ts.map +1 -0
  39. package/dist/http/body.js +165 -0
  40. package/dist/http/body.js.map +1 -0
  41. package/dist/http/http.d.ts +42 -0
  42. package/dist/http/http.d.ts.map +1 -0
  43. package/dist/http/http.js +100 -0
  44. package/dist/http/http.js.map +1 -0
  45. package/dist/http/origins.d.ts +37 -0
  46. package/dist/http/origins.d.ts.map +1 -0
  47. package/dist/http/origins.js +87 -0
  48. package/dist/http/origins.js.map +1 -0
  49. package/dist/mcp/dispatch.d.ts +7 -0
  50. package/dist/mcp/dispatch.d.ts.map +1 -0
  51. package/dist/mcp/dispatch.js +99 -0
  52. package/dist/mcp/dispatch.js.map +1 -0
  53. package/dist/{jsonrpc.d.ts → mcp/jsonrpc.d.ts} +10 -0
  54. package/dist/mcp/jsonrpc.d.ts.map +1 -0
  55. package/dist/mcp/jsonrpc.js +88 -0
  56. package/dist/mcp/jsonrpc.js.map +1 -0
  57. package/dist/mcp/methods.d.ts +133 -0
  58. package/dist/mcp/methods.d.ts.map +1 -0
  59. package/dist/mcp/methods.js +84 -0
  60. package/dist/mcp/methods.js.map +1 -0
  61. package/dist/mcp/protocol.d.ts +16 -0
  62. package/dist/mcp/protocol.d.ts.map +1 -0
  63. package/dist/mcp/protocol.js +22 -0
  64. package/dist/mcp/protocol.js.map +1 -0
  65. package/dist/mcp/registry.d.ts +58 -0
  66. package/dist/mcp/registry.d.ts.map +1 -0
  67. package/dist/mcp/registry.js +81 -0
  68. package/dist/mcp/registry.js.map +1 -0
  69. package/dist/mcp/rpc-handlers.d.ts +11 -0
  70. package/dist/mcp/rpc-handlers.d.ts.map +1 -0
  71. package/dist/mcp/rpc-handlers.js +133 -0
  72. package/dist/mcp/rpc-handlers.js.map +1 -0
  73. package/dist/{tools.d.ts → mcp/tools.d.ts} +30 -2
  74. package/dist/mcp/tools.d.ts.map +1 -0
  75. package/dist/mcp/tools.js +166 -0
  76. package/dist/mcp/tools.js.map +1 -0
  77. package/dist/{validate.d.ts → mcp/validate.d.ts} +24 -12
  78. package/dist/mcp/validate.d.ts.map +1 -0
  79. package/dist/mcp/validate.js +186 -0
  80. package/dist/mcp/validate.js.map +1 -0
  81. package/dist/oauth/audit.d.ts +20 -0
  82. package/dist/oauth/audit.d.ts.map +1 -0
  83. package/dist/oauth/audit.js +29 -0
  84. package/dist/oauth/audit.js.map +1 -0
  85. package/dist/oauth/cimd/cimd-parse.d.ts +4 -0
  86. package/dist/oauth/cimd/cimd-parse.d.ts.map +1 -0
  87. package/dist/oauth/cimd/cimd-parse.js +70 -0
  88. package/dist/oauth/cimd/cimd-parse.js.map +1 -0
  89. package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -0
  90. package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -0
  91. package/dist/oauth/cimd/cimd-ssrf.js +193 -0
  92. package/dist/oauth/cimd/cimd-ssrf.js.map +1 -0
  93. package/dist/oauth/cimd/cimd.d.ts +36 -0
  94. package/dist/oauth/cimd/cimd.d.ts.map +1 -0
  95. package/dist/oauth/cimd/cimd.js +132 -0
  96. package/dist/oauth/cimd/cimd.js.map +1 -0
  97. package/dist/oauth/cimd/types.d.ts +12 -0
  98. package/dist/oauth/cimd/types.d.ts.map +1 -0
  99. package/dist/oauth/cimd/types.js +3 -0
  100. package/dist/oauth/cimd/types.js.map +1 -0
  101. package/dist/oauth/config.d.ts +51 -0
  102. package/dist/oauth/config.d.ts.map +1 -0
  103. package/dist/oauth/config.js +108 -0
  104. package/dist/oauth/config.js.map +1 -0
  105. package/dist/oauth/constants.d.ts +15 -0
  106. package/dist/oauth/constants.d.ts.map +1 -1
  107. package/dist/oauth/constants.js +15 -0
  108. package/dist/oauth/constants.js.map +1 -1
  109. package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +1 -2
  110. package/dist/oauth/crypto/base64url.d.ts.map +1 -0
  111. package/dist/oauth/{base64url.js → crypto/base64url.js} +1 -5
  112. package/dist/oauth/crypto/base64url.js.map +1 -0
  113. package/dist/oauth/crypto/clientauth.d.ts +13 -0
  114. package/dist/oauth/crypto/clientauth.d.ts.map +1 -0
  115. package/dist/oauth/crypto/clientauth.js +195 -0
  116. package/dist/oauth/crypto/clientauth.js.map +1 -0
  117. package/dist/oauth/crypto/codes.d.ts +65 -0
  118. package/dist/oauth/crypto/codes.d.ts.map +1 -0
  119. package/dist/oauth/crypto/codes.js +121 -0
  120. package/dist/oauth/crypto/codes.js.map +1 -0
  121. package/dist/oauth/crypto/hkdf.d.ts +13 -0
  122. package/dist/oauth/crypto/hkdf.d.ts.map +1 -0
  123. package/dist/oauth/crypto/hkdf.js +30 -0
  124. package/dist/oauth/crypto/hkdf.js.map +1 -0
  125. package/dist/oauth/crypto/keycache.d.ts +19 -0
  126. package/dist/oauth/crypto/keycache.d.ts.map +1 -0
  127. package/dist/oauth/crypto/keycache.js +44 -0
  128. package/dist/oauth/crypto/keycache.js.map +1 -0
  129. package/dist/oauth/crypto/pkce.d.ts.map +1 -0
  130. package/dist/oauth/{pkce.js → crypto/pkce.js} +1 -1
  131. package/dist/oauth/crypto/pkce.js.map +1 -0
  132. package/dist/oauth/crypto/sealed.d.ts +26 -0
  133. package/dist/oauth/crypto/sealed.d.ts.map +1 -0
  134. package/dist/oauth/crypto/sealed.js +65 -0
  135. package/dist/oauth/crypto/sealed.js.map +1 -0
  136. package/dist/oauth/crypto/secrethash.d.ts +58 -0
  137. package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
  138. package/dist/oauth/crypto/secrethash.js +82 -0
  139. package/dist/oauth/crypto/secrethash.js.map +1 -0
  140. package/dist/oauth/deprecated.d.ts +52 -0
  141. package/dist/oauth/deprecated.d.ts.map +1 -0
  142. package/dist/oauth/deprecated.js +49 -0
  143. package/dist/oauth/deprecated.js.map +1 -0
  144. package/dist/oauth/endpoints/authorize.d.ts +3 -0
  145. package/dist/oauth/endpoints/authorize.d.ts.map +1 -0
  146. package/dist/oauth/endpoints/authorize.js +209 -0
  147. package/dist/oauth/endpoints/authorize.js.map +1 -0
  148. package/dist/oauth/endpoints/callback.d.ts +20 -0
  149. package/dist/oauth/endpoints/callback.d.ts.map +1 -0
  150. package/dist/oauth/endpoints/callback.js +31 -0
  151. package/dist/oauth/endpoints/callback.js.map +1 -0
  152. package/dist/oauth/endpoints/consent-page.d.ts +5 -0
  153. package/dist/oauth/endpoints/consent-page.d.ts.map +1 -0
  154. package/dist/oauth/endpoints/consent-page.js +63 -0
  155. package/dist/oauth/endpoints/consent-page.js.map +1 -0
  156. package/dist/oauth/endpoints/consent.d.ts +29 -0
  157. package/dist/oauth/endpoints/consent.d.ts.map +1 -0
  158. package/dist/oauth/endpoints/consent.js +98 -0
  159. package/dist/oauth/endpoints/consent.js.map +1 -0
  160. package/dist/oauth/{metadata.d.ts → endpoints/metadata.d.ts} +14 -8
  161. package/dist/oauth/endpoints/metadata.d.ts.map +1 -0
  162. package/dist/oauth/{metadata.js → endpoints/metadata.js} +10 -4
  163. package/dist/oauth/endpoints/metadata.js.map +1 -0
  164. package/dist/oauth/endpoints/register.d.ts +10 -0
  165. package/dist/oauth/endpoints/register.d.ts.map +1 -0
  166. package/dist/oauth/endpoints/register.js +96 -0
  167. package/dist/oauth/endpoints/register.js.map +1 -0
  168. package/dist/oauth/{revoke.d.ts → endpoints/revoke.d.ts} +1 -1
  169. package/dist/oauth/endpoints/revoke.d.ts.map +1 -0
  170. package/dist/oauth/{revoke.js → endpoints/revoke.js} +11 -9
  171. package/dist/oauth/endpoints/revoke.js.map +1 -0
  172. package/dist/oauth/endpoints/token.d.ts +3 -0
  173. package/dist/oauth/endpoints/token.d.ts.map +1 -0
  174. package/dist/oauth/{token.js → endpoints/token.js} +33 -47
  175. package/dist/oauth/endpoints/token.js.map +1 -0
  176. package/dist/oauth/{wellknown.d.ts → endpoints/wellknown.d.ts} +1 -1
  177. package/dist/oauth/endpoints/wellknown.d.ts.map +1 -0
  178. package/dist/oauth/{wellknown.js → endpoints/wellknown.js} +7 -7
  179. package/dist/oauth/endpoints/wellknown.js.map +1 -0
  180. package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +6 -1
  181. package/dist/oauth/policy/redirect.d.ts.map +1 -0
  182. package/dist/oauth/policy/redirect.js +39 -0
  183. package/dist/oauth/policy/redirect.js.map +1 -0
  184. package/dist/oauth/policy/reqbody.d.ts +18 -0
  185. package/dist/oauth/policy/reqbody.d.ts.map +1 -0
  186. package/dist/oauth/policy/reqbody.js +68 -0
  187. package/dist/oauth/policy/reqbody.js.map +1 -0
  188. package/dist/oauth/policy/resource.d.ts +37 -0
  189. package/dist/oauth/policy/resource.d.ts.map +1 -0
  190. package/dist/oauth/policy/resource.js +74 -0
  191. package/dist/oauth/policy/resource.js.map +1 -0
  192. package/dist/oauth/policy/scope.d.ts +20 -0
  193. package/dist/oauth/policy/scope.d.ts.map +1 -0
  194. package/dist/oauth/policy/scope.js +35 -0
  195. package/dist/oauth/policy/scope.js.map +1 -0
  196. package/dist/oauth/router.d.ts +12 -12
  197. package/dist/oauth/router.d.ts.map +1 -1
  198. package/dist/oauth/router.js +105 -87
  199. package/dist/oauth/router.js.map +1 -1
  200. package/dist/oauth/types.d.ts +177 -26
  201. package/dist/oauth/types.d.ts.map +1 -1
  202. package/dist/oauth/types.js +1 -27
  203. package/dist/oauth/types.js.map +1 -1
  204. package/dist/server.d.ts +14 -13
  205. package/dist/server.d.ts.map +1 -1
  206. package/dist/server.js +10 -9
  207. package/dist/server.js.map +1 -1
  208. package/dist/util/defined.d.ts +11 -0
  209. package/dist/util/defined.d.ts.map +1 -0
  210. package/dist/util/defined.js +15 -0
  211. package/dist/util/defined.js.map +1 -0
  212. package/dist/util/json.d.ts +3 -0
  213. package/dist/util/json.d.ts.map +1 -0
  214. package/dist/util/json.js +3 -0
  215. package/dist/util/json.js.map +1 -0
  216. package/docs/ROADMAP.md +283 -0
  217. package/docs/compatibility.md +84 -0
  218. package/docs/diagrams/mcp-trellis-architecture.html +305 -0
  219. package/docs/guide.md +441 -0
  220. package/docs/reference.md +210 -0
  221. package/docs/security.md +112 -0
  222. package/docs/troubleshooting.md +19 -0
  223. package/package.json +24 -8
  224. package/dist/adapters/origins.d.ts +0 -19
  225. package/dist/adapters/origins.d.ts.map +0 -1
  226. package/dist/adapters/origins.js +0 -55
  227. package/dist/adapters/origins.js.map +0 -1
  228. package/dist/dispatch.d.ts +0 -7
  229. package/dist/dispatch.d.ts.map +0 -1
  230. package/dist/dispatch.js +0 -79
  231. package/dist/dispatch.js.map +0 -1
  232. package/dist/http.d.ts +0 -17
  233. package/dist/http.d.ts.map +0 -1
  234. package/dist/http.js +0 -38
  235. package/dist/http.js.map +0 -1
  236. package/dist/jsonrpc.d.ts.map +0 -1
  237. package/dist/jsonrpc.js +0 -17
  238. package/dist/jsonrpc.js.map +0 -1
  239. package/dist/methods.d.ts +0 -61
  240. package/dist/methods.d.ts.map +0 -1
  241. package/dist/methods.js +0 -114
  242. package/dist/methods.js.map +0 -1
  243. package/dist/oauth/authorize.d.ts +0 -3
  244. package/dist/oauth/authorize.d.ts.map +0 -1
  245. package/dist/oauth/authorize.js +0 -109
  246. package/dist/oauth/authorize.js.map +0 -1
  247. package/dist/oauth/base64url.d.ts.map +0 -1
  248. package/dist/oauth/base64url.js.map +0 -1
  249. package/dist/oauth/clientauth.d.ts +0 -19
  250. package/dist/oauth/clientauth.d.ts.map +0 -1
  251. package/dist/oauth/clientauth.js +0 -76
  252. package/dist/oauth/clientauth.js.map +0 -1
  253. package/dist/oauth/codes.d.ts +0 -30
  254. package/dist/oauth/codes.d.ts.map +0 -1
  255. package/dist/oauth/codes.js +0 -93
  256. package/dist/oauth/codes.js.map +0 -1
  257. package/dist/oauth/metadata.d.ts.map +0 -1
  258. package/dist/oauth/metadata.js.map +0 -1
  259. package/dist/oauth/pkce.d.ts.map +0 -1
  260. package/dist/oauth/pkce.js.map +0 -1
  261. package/dist/oauth/redirect.d.ts.map +0 -1
  262. package/dist/oauth/redirect.js +0 -26
  263. package/dist/oauth/redirect.js.map +0 -1
  264. package/dist/oauth/resource.d.ts +0 -7
  265. package/dist/oauth/resource.d.ts.map +0 -1
  266. package/dist/oauth/resource.js +0 -44
  267. package/dist/oauth/resource.js.map +0 -1
  268. package/dist/oauth/revoke.d.ts.map +0 -1
  269. package/dist/oauth/revoke.js.map +0 -1
  270. package/dist/oauth/scope.d.ts +0 -11
  271. package/dist/oauth/scope.d.ts.map +0 -1
  272. package/dist/oauth/scope.js +0 -24
  273. package/dist/oauth/scope.js.map +0 -1
  274. package/dist/oauth/token.d.ts +0 -3
  275. package/dist/oauth/token.d.ts.map +0 -1
  276. package/dist/oauth/token.js.map +0 -1
  277. package/dist/oauth/wellknown.d.ts.map +0 -1
  278. package/dist/oauth/wellknown.js.map +0 -1
  279. package/dist/protocol.d.ts +0 -4
  280. package/dist/protocol.d.ts.map +0 -1
  281. package/dist/protocol.js +0 -14
  282. package/dist/protocol.js.map +0 -1
  283. package/dist/registry.d.ts +0 -37
  284. package/dist/registry.d.ts.map +0 -1
  285. package/dist/registry.js +0 -69
  286. package/dist/registry.js.map +0 -1
  287. package/dist/tools.d.ts.map +0 -1
  288. package/dist/tools.js +0 -97
  289. package/dist/tools.js.map +0 -1
  290. package/dist/validate.d.ts.map +0 -1
  291. package/dist/validate.js +0 -130
  292. package/dist/validate.js.map +0 -1
  293. /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 { createToolRegistry, } from "./registry.js";
6
- export { defineTool, apiTool, } from "./tools.js";
7
- export { rpcResult, rpcError, JSONRPC_PARSE_ERROR, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_INVALID_PARAMS, JSONRPC_INTERNAL_ERROR, JSONRPC_UNAUTHORIZED, } from "./jsonrpc.js";
8
- export { pickProtocolVersion, PROTOCOL_VERSIONS, DEFAULT_PROTOCOL_VERSION, } from "./protocol.js";
9
- export { parseBearer, timingSafeEqual, matchesAny, wwwAuthenticateHeader, rejectQueryToken, } from "./auth/bearer.js";
10
- export { validateAgainstSchema, JSON_SCHEMA_TYPES, SUPPORTED_SCHEMA_KEYWORDS, IGNORED_SCHEMA_KEYWORDS, unsupportedKeywords, } from "./validate.js";
11
- export { jsonResponse, emptyResponse, optionsResponse, corsHeaders, methodNotAllowed, } from "./http.js";
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
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,GAKb,MAAM,UAAU,CAAC;AAclB,OAAO,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,gBAAgB,GAGjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,GAOjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,OAAO,EACL,kBAAkB,GAOnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,UAAU,EACV,OAAO,GAKR,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,GAIrB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,WAAW,EACX,eAAe,EACf,UAAU,EACV,qBAAqB,EACrB,gBAAgB,GAEjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,GAEpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,eAAe,EACf,WAAW,EACX,gBAAgB,GACjB,MAAM,WAAW,CAAC"}
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,3 @@
1
+ /** A parsed JSON object — not `null`, not an array. */
2
+ export declare const isJsonObject: (value: unknown) => value is Record<string, unknown>;
3
+ //# sourceMappingURL=json.d.ts.map
@@ -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,3 @@
1
+ /** A parsed JSON object — not `null`, not an array. */
2
+ export const isJsonObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3
+ //# sourceMappingURL=json.js.map
@@ -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"}
@@ -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 |