mcp-trellis 1.0.0 → 2.2.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 (301) hide show
  1. package/README.md +65 -93
  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 +13 -30
  7. package/dist/adapters/node.d.ts.map +1 -1
  8. package/dist/adapters/node.js +55 -101
  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 -89
  19. package/dist/app.d.ts.map +1 -1
  20. package/dist/app.js +143 -63
  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 +19 -4
  29. package/dist/auth/bearer.js.map +1 -1
  30. package/dist/auth/signed-token.d.ts +26 -0
  31. package/dist/auth/signed-token.d.ts.map +1 -0
  32. package/dist/auth/signed-token.js +96 -0
  33. package/dist/auth/signed-token.js.map +1 -0
  34. package/dist/clients.js +1 -1
  35. package/dist/clients.js.map +1 -1
  36. package/dist/deprecated.d.ts +42 -0
  37. package/dist/deprecated.d.ts.map +1 -0
  38. package/dist/deprecated.js +35 -0
  39. package/dist/deprecated.js.map +1 -0
  40. package/dist/{body.d.ts → http/body.d.ts} +27 -6
  41. package/dist/http/body.d.ts.map +1 -0
  42. package/dist/{body.js → http/body.js} +33 -13
  43. package/dist/http/body.js.map +1 -0
  44. package/dist/http/http.d.ts +42 -0
  45. package/dist/http/http.d.ts.map +1 -0
  46. package/dist/http/http.js +100 -0
  47. package/dist/http/http.js.map +1 -0
  48. package/dist/http/origins.d.ts +37 -0
  49. package/dist/http/origins.d.ts.map +1 -0
  50. package/dist/http/origins.js +87 -0
  51. package/dist/http/origins.js.map +1 -0
  52. package/dist/{dispatch.d.ts → mcp/dispatch.d.ts} +1 -1
  53. package/dist/mcp/dispatch.d.ts.map +1 -0
  54. package/dist/mcp/dispatch.js +99 -0
  55. package/dist/mcp/dispatch.js.map +1 -0
  56. package/dist/{jsonrpc.d.ts → mcp/jsonrpc.d.ts} +9 -0
  57. package/dist/mcp/jsonrpc.d.ts.map +1 -0
  58. package/dist/mcp/jsonrpc.js +88 -0
  59. package/dist/mcp/jsonrpc.js.map +1 -0
  60. package/dist/{methods.d.ts → mcp/methods.d.ts} +60 -11
  61. package/dist/mcp/methods.d.ts.map +1 -0
  62. package/dist/mcp/methods.js +84 -0
  63. package/dist/mcp/methods.js.map +1 -0
  64. package/dist/{protocol.d.ts → mcp/protocol.d.ts} +1 -3
  65. package/dist/mcp/protocol.d.ts.map +1 -0
  66. package/dist/{protocol.js → mcp/protocol.js} +1 -3
  67. package/dist/mcp/protocol.js.map +1 -0
  68. package/dist/mcp/registry.d.ts +58 -0
  69. package/dist/mcp/registry.d.ts.map +1 -0
  70. package/dist/mcp/registry.js +81 -0
  71. package/dist/mcp/registry.js.map +1 -0
  72. package/dist/mcp/rpc-handlers.d.ts +11 -0
  73. package/dist/mcp/rpc-handlers.d.ts.map +1 -0
  74. package/dist/mcp/rpc-handlers.js +133 -0
  75. package/dist/mcp/rpc-handlers.js.map +1 -0
  76. package/dist/{tools.d.ts → mcp/tools.d.ts} +20 -1
  77. package/dist/mcp/tools.d.ts.map +1 -0
  78. package/dist/mcp/tools.js +166 -0
  79. package/dist/mcp/tools.js.map +1 -0
  80. package/dist/{validate.d.ts → mcp/validate.d.ts} +18 -21
  81. package/dist/mcp/validate.d.ts.map +1 -0
  82. package/dist/{validate.js → mcp/validate.js} +59 -41
  83. package/dist/mcp/validate.js.map +1 -0
  84. package/dist/oauth/audit.d.ts +20 -0
  85. package/dist/oauth/audit.d.ts.map +1 -0
  86. package/dist/oauth/audit.js +29 -0
  87. package/dist/oauth/audit.js.map +1 -0
  88. package/dist/oauth/cimd/cimd-parse.d.ts +4 -0
  89. package/dist/oauth/cimd/cimd-parse.d.ts.map +1 -0
  90. package/dist/oauth/cimd/cimd-parse.js +70 -0
  91. package/dist/oauth/cimd/cimd-parse.js.map +1 -0
  92. package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -0
  93. package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -0
  94. package/dist/oauth/cimd/cimd-ssrf.js +193 -0
  95. package/dist/oauth/cimd/cimd-ssrf.js.map +1 -0
  96. package/dist/oauth/cimd/cimd.d.ts +36 -0
  97. package/dist/oauth/cimd/cimd.d.ts.map +1 -0
  98. package/dist/oauth/cimd/cimd.js +132 -0
  99. package/dist/oauth/cimd/cimd.js.map +1 -0
  100. package/dist/oauth/cimd/types.d.ts +12 -0
  101. package/dist/oauth/cimd/types.d.ts.map +1 -0
  102. package/dist/oauth/cimd/types.js +3 -0
  103. package/dist/oauth/cimd/types.js.map +1 -0
  104. package/dist/oauth/config.d.ts +51 -0
  105. package/dist/oauth/config.d.ts.map +1 -0
  106. package/dist/oauth/config.js +108 -0
  107. package/dist/oauth/config.js.map +1 -0
  108. package/dist/oauth/constants.d.ts +11 -0
  109. package/dist/oauth/constants.d.ts.map +1 -1
  110. package/dist/oauth/constants.js +11 -0
  111. package/dist/oauth/constants.js.map +1 -1
  112. package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +0 -1
  113. package/dist/oauth/crypto/base64url.d.ts.map +1 -0
  114. package/dist/oauth/{base64url.js → crypto/base64url.js} +0 -1
  115. package/dist/oauth/crypto/base64url.js.map +1 -0
  116. package/dist/oauth/crypto/clientauth.d.ts +13 -0
  117. package/dist/oauth/crypto/clientauth.d.ts.map +1 -0
  118. package/dist/oauth/crypto/clientauth.js +195 -0
  119. package/dist/oauth/crypto/clientauth.js.map +1 -0
  120. package/dist/oauth/crypto/codes.d.ts +65 -0
  121. package/dist/oauth/crypto/codes.d.ts.map +1 -0
  122. package/dist/oauth/crypto/codes.js +121 -0
  123. package/dist/oauth/crypto/codes.js.map +1 -0
  124. package/dist/oauth/crypto/hkdf.d.ts +13 -0
  125. package/dist/oauth/crypto/hkdf.d.ts.map +1 -0
  126. package/dist/oauth/crypto/hkdf.js +30 -0
  127. package/dist/oauth/crypto/hkdf.js.map +1 -0
  128. package/dist/oauth/crypto/keycache.d.ts +19 -0
  129. package/dist/oauth/crypto/keycache.d.ts.map +1 -0
  130. package/dist/oauth/crypto/keycache.js +44 -0
  131. package/dist/oauth/crypto/keycache.js.map +1 -0
  132. package/dist/oauth/crypto/pkce.d.ts.map +1 -0
  133. package/dist/oauth/{pkce.js → crypto/pkce.js} +1 -1
  134. package/dist/oauth/crypto/pkce.js.map +1 -0
  135. package/dist/oauth/{sealed.d.ts → crypto/sealed.d.ts} +12 -3
  136. package/dist/oauth/crypto/sealed.d.ts.map +1 -0
  137. package/dist/oauth/{sealed.js → crypto/sealed.js} +22 -14
  138. package/dist/oauth/crypto/sealed.js.map +1 -0
  139. package/dist/oauth/{secrethash.d.ts → crypto/secrethash.d.ts} +14 -0
  140. package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
  141. package/dist/oauth/{secrethash.js → crypto/secrethash.js} +27 -11
  142. package/dist/oauth/crypto/secrethash.js.map +1 -0
  143. package/dist/oauth/deprecated.d.ts +52 -0
  144. package/dist/oauth/deprecated.d.ts.map +1 -0
  145. package/dist/oauth/deprecated.js +49 -0
  146. package/dist/oauth/deprecated.js.map +1 -0
  147. package/dist/oauth/endpoints/authorize.d.ts +3 -0
  148. package/dist/oauth/endpoints/authorize.d.ts.map +1 -0
  149. package/dist/oauth/endpoints/authorize.js +209 -0
  150. package/dist/oauth/endpoints/authorize.js.map +1 -0
  151. package/dist/oauth/endpoints/callback.d.ts +20 -0
  152. package/dist/oauth/endpoints/callback.d.ts.map +1 -0
  153. package/dist/oauth/endpoints/callback.js +31 -0
  154. package/dist/oauth/endpoints/callback.js.map +1 -0
  155. package/dist/oauth/endpoints/consent-page.d.ts +5 -0
  156. package/dist/oauth/endpoints/consent-page.d.ts.map +1 -0
  157. package/dist/oauth/endpoints/consent-page.js +63 -0
  158. package/dist/oauth/endpoints/consent-page.js.map +1 -0
  159. package/dist/oauth/endpoints/consent.d.ts +29 -0
  160. package/dist/oauth/endpoints/consent.d.ts.map +1 -0
  161. package/dist/oauth/endpoints/consent.js +98 -0
  162. package/dist/oauth/endpoints/consent.js.map +1 -0
  163. package/dist/oauth/{metadata.d.ts → endpoints/metadata.d.ts} +8 -2
  164. package/dist/oauth/endpoints/metadata.d.ts.map +1 -0
  165. package/dist/oauth/{metadata.js → endpoints/metadata.js} +10 -4
  166. package/dist/oauth/endpoints/metadata.js.map +1 -0
  167. package/dist/oauth/endpoints/register.d.ts +10 -0
  168. package/dist/oauth/endpoints/register.d.ts.map +1 -0
  169. package/dist/oauth/endpoints/register.js +96 -0
  170. package/dist/oauth/endpoints/register.js.map +1 -0
  171. package/dist/oauth/{revoke.d.ts → endpoints/revoke.d.ts} +1 -1
  172. package/dist/oauth/endpoints/revoke.d.ts.map +1 -0
  173. package/dist/oauth/{revoke.js → endpoints/revoke.js} +8 -18
  174. package/dist/oauth/endpoints/revoke.js.map +1 -0
  175. package/dist/oauth/endpoints/token.d.ts +3 -0
  176. package/dist/oauth/endpoints/token.d.ts.map +1 -0
  177. package/dist/oauth/{token.js → endpoints/token.js} +32 -25
  178. package/dist/oauth/endpoints/token.js.map +1 -0
  179. package/dist/oauth/{wellknown.d.ts → endpoints/wellknown.d.ts} +1 -1
  180. package/dist/oauth/endpoints/wellknown.d.ts.map +1 -0
  181. package/dist/oauth/{wellknown.js → endpoints/wellknown.js} +7 -9
  182. package/dist/oauth/endpoints/wellknown.js.map +1 -0
  183. package/dist/oauth/policy/redirect.d.ts.map +1 -0
  184. package/dist/oauth/policy/redirect.js.map +1 -0
  185. package/dist/oauth/policy/reqbody.d.ts +18 -0
  186. package/dist/oauth/policy/reqbody.d.ts.map +1 -0
  187. package/dist/oauth/policy/reqbody.js +68 -0
  188. package/dist/oauth/policy/reqbody.js.map +1 -0
  189. package/dist/oauth/{resource.d.ts → policy/resource.d.ts} +1 -1
  190. package/dist/oauth/policy/resource.d.ts.map +1 -0
  191. package/dist/oauth/{resource.js → policy/resource.js} +2 -2
  192. package/dist/oauth/policy/resource.js.map +1 -0
  193. package/dist/oauth/{scope.d.ts → policy/scope.d.ts} +2 -2
  194. package/dist/oauth/policy/scope.d.ts.map +1 -0
  195. package/dist/oauth/{scope.js → policy/scope.js} +3 -3
  196. package/dist/oauth/policy/scope.js.map +1 -0
  197. package/dist/oauth/router.d.ts +10 -13
  198. package/dist/oauth/router.d.ts.map +1 -1
  199. package/dist/oauth/router.js +93 -115
  200. package/dist/oauth/router.js.map +1 -1
  201. package/dist/oauth/types.d.ts +99 -45
  202. package/dist/oauth/types.d.ts.map +1 -1
  203. package/dist/oauth/types.js +1 -90
  204. package/dist/oauth/types.js.map +1 -1
  205. package/dist/server.d.ts +13 -12
  206. package/dist/server.d.ts.map +1 -1
  207. package/dist/server.js +10 -8
  208. package/dist/server.js.map +1 -1
  209. package/dist/util/defined.d.ts +11 -0
  210. package/dist/util/defined.d.ts.map +1 -0
  211. package/dist/util/defined.js +15 -0
  212. package/dist/util/defined.js.map +1 -0
  213. package/dist/util/json.d.ts +3 -0
  214. package/dist/util/json.d.ts.map +1 -0
  215. package/dist/util/json.js +3 -0
  216. package/dist/util/json.js.map +1 -0
  217. package/docs/ROADMAP.md +37 -47
  218. package/docs/compatibility.md +86 -0
  219. package/docs/diagrams/demo-tool-result.png +0 -0
  220. package/docs/diagrams/mcp-trellis-architecture.html +305 -0
  221. package/docs/guide.md +46 -16
  222. package/docs/reference.md +55 -22
  223. package/docs/security.md +58 -9
  224. package/docs/troubleshooting.md +19 -0
  225. package/package.json +27 -10
  226. package/dist/adapters/origins.d.ts +0 -19
  227. package/dist/adapters/origins.d.ts.map +0 -1
  228. package/dist/adapters/origins.js +0 -52
  229. package/dist/adapters/origins.js.map +0 -1
  230. package/dist/body.d.ts.map +0 -1
  231. package/dist/body.js.map +0 -1
  232. package/dist/dispatch.d.ts.map +0 -1
  233. package/dist/dispatch.js +0 -119
  234. package/dist/dispatch.js.map +0 -1
  235. package/dist/http.d.ts +0 -17
  236. package/dist/http.d.ts.map +0 -1
  237. package/dist/http.js +0 -38
  238. package/dist/http.js.map +0 -1
  239. package/dist/jsonrpc.d.ts.map +0 -1
  240. package/dist/jsonrpc.js +0 -18
  241. package/dist/jsonrpc.js.map +0 -1
  242. package/dist/methods.d.ts.map +0 -1
  243. package/dist/methods.js +0 -130
  244. package/dist/methods.js.map +0 -1
  245. package/dist/oauth/authorize.d.ts +0 -3
  246. package/dist/oauth/authorize.d.ts.map +0 -1
  247. package/dist/oauth/authorize.js +0 -141
  248. package/dist/oauth/authorize.js.map +0 -1
  249. package/dist/oauth/base64url.d.ts.map +0 -1
  250. package/dist/oauth/base64url.js.map +0 -1
  251. package/dist/oauth/clientauth.d.ts +0 -24
  252. package/dist/oauth/clientauth.d.ts.map +0 -1
  253. package/dist/oauth/clientauth.js +0 -119
  254. package/dist/oauth/clientauth.js.map +0 -1
  255. package/dist/oauth/codes.d.ts +0 -30
  256. package/dist/oauth/codes.d.ts.map +0 -1
  257. package/dist/oauth/codes.js +0 -85
  258. package/dist/oauth/codes.js.map +0 -1
  259. package/dist/oauth/consent.d.ts +0 -65
  260. package/dist/oauth/consent.d.ts.map +0 -1
  261. package/dist/oauth/consent.js +0 -173
  262. package/dist/oauth/consent.js.map +0 -1
  263. package/dist/oauth/metadata.d.ts.map +0 -1
  264. package/dist/oauth/metadata.js.map +0 -1
  265. package/dist/oauth/pkce.d.ts.map +0 -1
  266. package/dist/oauth/pkce.js.map +0 -1
  267. package/dist/oauth/redirect.d.ts.map +0 -1
  268. package/dist/oauth/redirect.js.map +0 -1
  269. package/dist/oauth/reqbody.d.ts +0 -10
  270. package/dist/oauth/reqbody.d.ts.map +0 -1
  271. package/dist/oauth/reqbody.js +0 -29
  272. package/dist/oauth/reqbody.js.map +0 -1
  273. package/dist/oauth/resource.d.ts.map +0 -1
  274. package/dist/oauth/resource.js.map +0 -1
  275. package/dist/oauth/revoke.d.ts.map +0 -1
  276. package/dist/oauth/revoke.js.map +0 -1
  277. package/dist/oauth/scope.d.ts.map +0 -1
  278. package/dist/oauth/scope.js.map +0 -1
  279. package/dist/oauth/sealed.d.ts.map +0 -1
  280. package/dist/oauth/sealed.js.map +0 -1
  281. package/dist/oauth/secrethash.d.ts.map +0 -1
  282. package/dist/oauth/secrethash.js.map +0 -1
  283. package/dist/oauth/token.d.ts +0 -3
  284. package/dist/oauth/token.d.ts.map +0 -1
  285. package/dist/oauth/token.js.map +0 -1
  286. package/dist/oauth/wellknown.d.ts.map +0 -1
  287. package/dist/oauth/wellknown.js.map +0 -1
  288. package/dist/protocol.d.ts.map +0 -1
  289. package/dist/protocol.js.map +0 -1
  290. package/dist/registry.d.ts +0 -37
  291. package/dist/registry.d.ts.map +0 -1
  292. package/dist/registry.js +0 -77
  293. package/dist/registry.js.map +0 -1
  294. package/dist/tools.d.ts.map +0 -1
  295. package/dist/tools.js +0 -98
  296. package/dist/tools.js.map +0 -1
  297. package/dist/validate.d.ts.map +0 -1
  298. package/dist/validate.js.map +0 -1
  299. /package/dist/oauth/{pkce.d.ts → crypto/pkce.d.ts} +0 -0
  300. /package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +0 -0
  301. /package/dist/oauth/{redirect.js → policy/redirect.js} +0 -0
@@ -0,0 +1,305 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>mcp-trellis · Architecture</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Hebrew:wght@400;500;600;700;800&display=swap" rel="stylesheet">
8
+ <style>
9
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
10
+ :root {
11
+ --color-paper: #f5f5fb;
12
+ --color-ink: #1e1f29;
13
+ --color-muted: #55566b;
14
+ --color-accent: #5b5bf0;
15
+ --font-sans: 'Noto Sans Hebrew', system-ui, sans-serif;
16
+ --font-mono: ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
17
+ }
18
+
19
+ body {
20
+ font-family: var(--font-sans);
21
+ background: var(--color-paper);
22
+ color: var(--color-ink);
23
+ min-height: 100vh;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ padding: 3rem 2rem;
28
+ }
29
+
30
+ .frame { max-width: 1280px; width: 100%; }
31
+
32
+ .eyebrow {
33
+ font-family: var(--font-mono);
34
+ font-size: 0.66rem;
35
+ font-weight: 500;
36
+ letter-spacing: 0.18em;
37
+ text-transform: uppercase;
38
+ color: var(--color-muted);
39
+ margin-bottom: 0.5rem;
40
+ }
41
+
42
+ h1 {
43
+ font-family: var(--font-sans);
44
+ font-size: clamp(1.5rem, 2.4vw + 0.75rem, 2rem);
45
+ font-weight: 700;
46
+ letter-spacing: -0.02em;
47
+ line-height: 1.15;
48
+ color: var(--color-ink);
49
+ margin-bottom: 1.5rem;
50
+ }
51
+
52
+ svg { width: 100%; min-width: 960px; display: block; }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <div class="frame">
57
+ <p class="eyebrow">Architecture · mcp-trellis</p>
58
+ <h1>Host-agnostic MCP + OAuth 2.1</h1>
59
+
60
+ <svg viewBox="0 0 1280 760" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="mcp-trellis-architecture-title mcp-trellis-architecture-desc">
61
+ <title id="mcp-trellis-architecture-title">mcp-trellis architecture</title>
62
+ <desc id="mcp-trellis-architecture-desc">Connectors reach an edge host, then createMcpApp routes OAuth and MCP traffic to library handlers and your login, token, store, and tool ports.</desc>
63
+ <defs>
64
+ <marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#55566b"/></marker>
65
+ <marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#5b5bf0"/></marker>
66
+ <marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#4545c8"/></marker>
67
+ </defs>
68
+
69
+ <rect width="100%" height="100%" fill="#f5f5fb"/>
70
+
71
+ <!-- Zones (before arrows) -->
72
+ <rect x="40" y="96" width="168" height="400" rx="8"
73
+ fill="rgba(30,31,41,0.02)" stroke="rgba(30,31,41,0.10)" stroke-width="0.8"/>
74
+ <rect x="84" y="100" width="80" height="12" rx="2" fill="#f5f5fb"/>
75
+ <text x="124" y="109" fill="rgba(30,31,41,0.40)" font-size="7" font-family="ui-monospace, monospace"
76
+ text-anchor="middle" letter-spacing="0.14em">CONNECTORS</text>
77
+
78
+ <rect x="440" y="56" width="480" height="520" rx="8"
79
+ fill="rgba(30,31,41,0.02)" stroke="rgba(30,31,41,0.10)" stroke-width="0.8"/>
80
+ <rect x="624" y="60" width="112" height="12" rx="2" fill="#f5f5fb"/>
81
+ <text x="680" y="69" fill="rgba(30,31,41,0.40)" font-size="7" font-family="ui-monospace, monospace"
82
+ text-anchor="middle" letter-spacing="0.14em">MCP-TRELLIS</text>
83
+
84
+ <rect x="980" y="96" width="260" height="440" rx="8"
85
+ fill="rgba(30,31,41,0.02)" stroke="rgba(30,31,41,0.10)" stroke-width="0.8"/>
86
+ <rect x="1052" y="100" width="116" height="12" rx="2" fill="#f5f5fb"/>
87
+ <text x="1110" y="109" fill="rgba(30,31,41,0.40)" font-size="7" font-family="ui-monospace, monospace"
88
+ text-anchor="middle" letter-spacing="0.14em">YOUR PORTS</text>
89
+
90
+ <!-- Arrows -->
91
+ <!-- Clients → Edge (link) -->
92
+ <line x1="192" y1="172" x2="248" y2="172" stroke="#4545c8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
93
+ <line x1="192" y1="292" x2="248" y2="292" stroke="#4545c8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
94
+ <line x1="192" y1="412" x2="248" y2="412" stroke="#4545c8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
95
+
96
+ <!-- Edge → createMcpApp (accent primary) -->
97
+ <path d="M 392,292 H 480 Q 488,292 488,284 V 120 Q 488,112 496,112 H 592"
98
+ fill="none" stroke="#5b5bf0" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
99
+
100
+ <!-- createMcpApp → OAuth / MCP -->
101
+ <path d="M 640,144 V 168 H 558 Q 550,168 550,176 V 200"
102
+ fill="none" stroke="#55566b" stroke-width="1.2" marker-end="url(#arrow)"/>
103
+ <path d="M 720,144 V 168 H 802 Q 810,168 810,176 V 200"
104
+ fill="none" stroke="#55566b" stroke-width="1.2" marker-end="url(#arrow)"/>
105
+
106
+ <!-- OAuth → Consent / Discovery -->
107
+ <line x1="550" y1="280" x2="550" y2="312" stroke="#5b5bf0" stroke-width="1.2" marker-end="url(#arrow-accent)"/>
108
+ <line x1="550" y1="376" x2="550" y2="408" stroke="#55566b" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
109
+
110
+ <!-- MCP → Audience → Registry -->
111
+ <line x1="810" y1="280" x2="810" y2="312" stroke="#55566b" stroke-width="1.2" marker-end="url(#arrow)"/>
112
+ <line x1="810" y1="376" x2="810" y2="408" stroke="#55566b" stroke-width="1.2" marker-end="url(#arrow)"/>
113
+
114
+ <!-- Library → Ports (distinct Auth attach points: MINT@160, VERIFY@184) -->
115
+ <path d="M 640,228 H 680 Q 688,228 688,220 V 168 Q 688,160 696,160 H 1000"
116
+ fill="none" stroke="#4545c8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
117
+ <path d="M 640,256 H 680 Q 688,256 688,264 V 288 Q 688,296 696,296 H 1000"
118
+ fill="none" stroke="#55566b" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
119
+ <path d="M 900,344 H 940 Q 948,344 948,336 V 192 Q 948,184 956,184 H 1000"
120
+ fill="none" stroke="#55566b" stroke-width="1.2" marker-end="url(#arrow)"/>
121
+ <line x1="900" y1="440" x2="1000" y2="440" stroke="#55566b" stroke-width="1.2" marker-end="url(#arrow)"/>
122
+
123
+ <!-- Arrow labels (6–10px gap above stroke) -->
124
+ <rect x="204" y="152" width="36" height="12" rx="2" fill="#f5f5fb"/>
125
+ <text x="222" y="161" fill="#4545c8" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">HTTPS</text>
126
+
127
+ <rect x="500" y="92" width="48" height="12" rx="2" fill="#f5f5fb"/>
128
+ <text x="524" y="101" fill="#5b5bf0" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">FETCH</text>
129
+
130
+ <rect x="568" y="148" width="40" height="12" rx="2" fill="#f5f5fb"/>
131
+ <text x="588" y="157" fill="#55566b" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">OAUTH</text>
132
+
133
+ <rect x="744" y="148" width="32" height="12" rx="2" fill="#f5f5fb"/>
134
+ <text x="760" y="157" fill="#55566b" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">MCP</text>
135
+
136
+ <rect x="560" y="288" width="52" height="12" rx="2" fill="#f5f5fb"/>
137
+ <text x="586" y="297" fill="#5b5bf0" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">APPROVE</text>
138
+
139
+ <rect x="560" y="384" width="40" height="12" rx="2" fill="#f5f5fb"/>
140
+ <text x="580" y="393" fill="#8d8ea3" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">META</text>
141
+
142
+ <rect x="820" y="288" width="36" height="12" rx="2" fill="#f5f5fb"/>
143
+ <text x="838" y="297" fill="#55566b" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">AUD</text>
144
+
145
+ <rect x="820" y="384" width="48" height="12" rx="2" fill="#f5f5fb"/>
146
+ <text x="844" y="393" fill="#55566b" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">SCOPE</text>
147
+
148
+ <rect x="820" y="140" width="40" height="12" rx="2" fill="#f5f5fb"/>
149
+ <text x="840" y="149" fill="#4545c8" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">MINT</text>
150
+
151
+ <rect x="924" y="276" width="44" height="12" rx="2" fill="#f5f5fb"/>
152
+ <text x="946" y="285" fill="#8d8ea3" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">STORE</text>
153
+
154
+ <rect x="880" y="164" width="48" height="12" rx="2" fill="#f5f5fb"/>
155
+ <text x="904" y="173" fill="#55566b" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">VERIFY</text>
156
+
157
+ <rect x="928" y="420" width="40" height="12" rx="2" fill="#f5f5fb"/>
158
+ <text x="948" y="429" fill="#55566b" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.06em">CALL</text>
159
+
160
+ <!-- Nodes: Connectors -->
161
+ <rect x="56" y="140" width="136" height="64" rx="6" fill="#f5f5fb"/>
162
+ <rect x="56" y="140" width="136" height="64" rx="6" fill="rgba(85,86,107,0.10)" stroke="#8d8ea3" stroke-width="1"/>
163
+ <rect x="64" y="148" width="40" height="12" rx="2" fill="transparent" stroke="rgba(141,142,163,0.40)" stroke-width="0.8"/>
164
+ <text x="84" y="157" fill="#8d8ea3" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">CLIENT</text>
165
+ <text x="124" y="176" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Claude</text>
166
+ <text x="124" y="192" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">DCR + PKCE</text>
167
+
168
+ <rect x="56" y="260" width="136" height="64" rx="6" fill="#f5f5fb"/>
169
+ <rect x="56" y="260" width="136" height="64" rx="6" fill="rgba(85,86,107,0.10)" stroke="#8d8ea3" stroke-width="1"/>
170
+ <rect x="64" y="268" width="40" height="12" rx="2" fill="transparent" stroke="rgba(141,142,163,0.40)" stroke-width="0.8"/>
171
+ <text x="84" y="277" fill="#8d8ea3" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">CLIENT</text>
172
+ <text x="124" y="296" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Gemini</text>
173
+ <text x="124" y="312" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">pre-reg secret</text>
174
+
175
+ <rect x="56" y="380" width="136" height="64" rx="6" fill="#f5f5fb"/>
176
+ <rect x="56" y="380" width="136" height="64" rx="6" fill="rgba(85,86,107,0.10)" stroke="#8d8ea3" stroke-width="1"/>
177
+ <rect x="64" y="388" width="40" height="12" rx="2" fill="transparent" stroke="rgba(141,142,163,0.40)" stroke-width="0.8"/>
178
+ <text x="84" y="397" fill="#8d8ea3" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">CLIENT</text>
179
+ <text x="124" y="416" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Codex</text>
180
+ <text x="124" y="432" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">OAuth 2.1 PKCE</text>
181
+
182
+ <!-- Edge host -->
183
+ <rect x="248" y="148" width="144" height="320" rx="6" fill="#f5f5fb"/>
184
+ <rect x="248" y="148" width="144" height="320" rx="6" fill="rgba(30,31,41,0.03)" stroke="rgba(30,31,41,0.30)" stroke-width="1"/>
185
+ <rect x="256" y="156" width="36" height="12" rx="2" fill="transparent" stroke="rgba(30,31,41,0.22)" stroke-width="0.8"/>
186
+ <text x="274" y="165" fill="#8d8ea3" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">EDGE</text>
187
+ <text x="320" y="292" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Your host</text>
188
+ <text x="320" y="312" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">Workers · Next</text>
189
+ <text x="320" y="328" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">Node · Deno · Bun</text>
190
+ <text x="320" y="352" fill="#8d8ea3" font-size="8" font-family="ui-monospace, monospace" text-anchor="middle">Request → Response</text>
191
+
192
+ <!-- createMcpApp (focal) -->
193
+ <rect x="592" y="80" width="176" height="64" rx="6" fill="#f5f5fb"/>
194
+ <rect x="592" y="80" width="176" height="64" rx="6" fill="#efeeff" stroke="#5b5bf0" stroke-width="1"/>
195
+ <rect x="600" y="88" width="36" height="12" rx="2" fill="transparent" stroke="rgba(91,91,240,0.50)" stroke-width="0.8"/>
196
+ <text x="618" y="97" fill="#5b5bf0" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">APP</text>
197
+ <text x="680" y="116" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">createMcpApp</text>
198
+ <text x="680" y="132" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">oauth.tryHandle → /mcp</text>
199
+
200
+ <!-- OAuth AS -->
201
+ <rect x="460" y="200" width="180" height="80" rx="6" fill="#f5f5fb"/>
202
+ <rect x="460" y="200" width="180" height="80" rx="6" fill="#ffffff" stroke="#1e1f29" stroke-width="1"/>
203
+ <rect x="468" y="208" width="28" height="12" rx="2" fill="transparent" stroke="rgba(30,31,41,0.40)" stroke-width="0.8"/>
204
+ <text x="482" y="217" fill="#1e1f29" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">AS</text>
205
+ <text x="550" y="240" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">OAuth router</text>
206
+ <text x="550" y="256" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">/authorize · /token</text>
207
+ <text x="550" y="268" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">/register · /revoke</text>
208
+
209
+ <!-- Consent (security, 2nd accent) -->
210
+ <rect x="460" y="312" width="180" height="64" rx="6" fill="#f5f5fb"/>
211
+ <rect x="460" y="312" width="180" height="64" rx="6" fill="rgba(91,91,240,0.05)" stroke="rgba(91,91,240,0.50)" stroke-width="1" stroke-dasharray="4,4"/>
212
+ <rect x="468" y="320" width="48" height="12" rx="2" fill="transparent" stroke="rgba(91,91,240,0.40)" stroke-width="0.8"/>
213
+ <text x="492" y="329" fill="#5b5bf0" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">SECURE</text>
214
+ <text x="550" y="348" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Consent</text>
215
+ <text x="550" y="364" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">POST …/consent</text>
216
+
217
+ <!-- Discovery -->
218
+ <rect x="460" y="408" width="180" height="64" rx="6" fill="#f5f5fb"/>
219
+ <rect x="460" y="408" width="180" height="64" rx="6" fill="rgba(30,31,41,0.02)" stroke="rgba(30,31,41,0.20)" stroke-width="1" stroke-dasharray="4,3"/>
220
+ <rect x="468" y="416" width="36" height="12" rx="2" fill="transparent" stroke="rgba(30,31,41,0.25)" stroke-width="0.8"/>
221
+ <text x="486" y="425" fill="#8d8ea3" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">META</text>
222
+ <text x="550" y="444" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Discovery</text>
223
+ <text x="550" y="460" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">.well-known/*</text>
224
+
225
+ <!-- MCP Handler -->
226
+ <rect x="720" y="200" width="180" height="80" rx="6" fill="#f5f5fb"/>
227
+ <rect x="720" y="200" width="180" height="80" rx="6" fill="#ffffff" stroke="#1e1f29" stroke-width="1"/>
228
+ <rect x="728" y="208" width="36" height="12" rx="2" fill="transparent" stroke="rgba(30,31,41,0.40)" stroke-width="0.8"/>
229
+ <text x="746" y="217" fill="#1e1f29" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">MCP</text>
230
+ <text x="810" y="240" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">MCP handler</text>
231
+ <text x="810" y="256" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">JSON-RPC /mcp</text>
232
+ <text x="810" y="268" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">initialize · tools/*</text>
233
+
234
+ <!-- Audience gate (ink dashed — accent reserved for createMcpApp + Consent) -->
235
+ <rect x="720" y="312" width="180" height="64" rx="6" fill="#f5f5fb"/>
236
+ <rect x="720" y="312" width="180" height="64" rx="6" fill="rgba(30,31,41,0.02)" stroke="rgba(30,31,41,0.30)" stroke-width="1" stroke-dasharray="4,4"/>
237
+ <rect x="728" y="320" width="36" height="12" rx="2" fill="transparent" stroke="rgba(30,31,41,0.30)" stroke-width="0.8"/>
238
+ <text x="746" y="329" fill="#55566b" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">GATE</text>
239
+ <text x="810" y="348" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Audience check</text>
240
+ <text x="810" y="364" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">RFC 8707 aud</text>
241
+
242
+ <!-- Tool registry -->
243
+ <rect x="720" y="408" width="180" height="64" rx="6" fill="#f5f5fb"/>
244
+ <rect x="720" y="408" width="180" height="64" rx="6" fill="rgba(30,31,41,0.05)" stroke="#55566b" stroke-width="1"/>
245
+ <rect x="728" y="416" width="40" height="12" rx="2" fill="transparent" stroke="rgba(85,86,107,0.50)" stroke-width="0.8"/>
246
+ <text x="748" y="425" fill="#55566b" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">REG</text>
247
+ <text x="810" y="444" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Tool registry</text>
248
+ <text x="810" y="460" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">scope-gated call</text>
249
+
250
+ <!-- Your ports -->
251
+ <rect x="1000" y="140" width="220" height="72" rx="6" fill="#f5f5fb"/>
252
+ <rect x="1000" y="140" width="220" height="72" rx="6" fill="rgba(85,86,107,0.10)" stroke="#8d8ea3" stroke-width="1"/>
253
+ <rect x="1008" y="148" width="40" height="12" rx="2" fill="transparent" stroke="rgba(141,142,163,0.40)" stroke-width="0.8"/>
254
+ <text x="1028" y="157" fill="#8d8ea3" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">AUTH</text>
255
+ <text x="1110" y="176" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Auth ports</text>
256
+ <text x="1110" y="192" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">resolve · mint · verify</text>
257
+
258
+ <rect x="1000" y="260" width="220" height="72" rx="6" fill="#f5f5fb"/>
259
+ <rect x="1000" y="260" width="220" height="72" rx="6" fill="rgba(30,31,41,0.05)" stroke="#55566b" stroke-width="1"/>
260
+ <rect x="1008" y="268" width="44" height="12" rx="2" fill="transparent" stroke="rgba(85,86,107,0.50)" stroke-width="0.8"/>
261
+ <text x="1030" y="277" fill="#55566b" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">STORE</text>
262
+ <text x="1110" y="296" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Stores</text>
263
+ <text x="1110" y="312" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">clientStore · codeStore</text>
264
+
265
+ <rect x="1000" y="380" width="220" height="72" rx="6" fill="#f5f5fb"/>
266
+ <rect x="1000" y="380" width="220" height="72" rx="6" fill="#ffffff" stroke="#1e1f29" stroke-width="1"/>
267
+ <rect x="1008" y="388" width="40" height="12" rx="2" fill="transparent" stroke="rgba(30,31,41,0.40)" stroke-width="0.8"/>
268
+ <text x="1028" y="397" fill="#1e1f29" font-size="7" font-family="ui-monospace, monospace" text-anchor="middle" letter-spacing="0.08em">TOOLS</text>
269
+ <text x="1110" y="416" fill="#1e1f29" font-size="12" font-weight="600" font-family="'Noto Sans Hebrew', sans-serif" text-anchor="middle">Tool handlers</text>
270
+ <text x="1110" y="432" fill="#55566b" font-size="9" font-family="ui-monospace, monospace" text-anchor="middle">your business logic</text>
271
+
272
+ <!-- Legend -->
273
+ <line x1="40" y1="640" x2="1240" y2="640" stroke="rgba(30,31,41,0.10)" stroke-width="0.8"/>
274
+ <text x="40" y="660" fill="#55566b" font-size="8" font-family="ui-monospace, monospace" letter-spacing="0.14em">LEGEND</text>
275
+
276
+ <rect x="40" y="676" width="14" height="10" rx="2" fill="#efeeff" stroke="#5b5bf0" stroke-width="1"/>
277
+ <text x="60" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">Focal router</text>
278
+
279
+ <rect x="168" y="676" width="14" height="10" rx="2" fill="#ffffff" stroke="#1e1f29" stroke-width="1"/>
280
+ <text x="188" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">Library handler</text>
281
+
282
+ <rect x="316" y="676" width="14" height="10" rx="2" fill="rgba(91,91,240,0.05)" stroke="rgba(91,91,240,0.50)" stroke-width="1" stroke-dasharray="3,2"/>
283
+ <text x="336" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">Security control</text>
284
+
285
+ <rect x="468" y="676" width="14" height="10" rx="2" fill="rgba(30,31,41,0.05)" stroke="#55566b" stroke-width="1"/>
286
+ <text x="488" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">Store / registry</text>
287
+
288
+ <rect x="620" y="676" width="14" height="10" rx="2" fill="rgba(30,31,41,0.03)" stroke="rgba(30,31,41,0.30)" stroke-width="1"/>
289
+ <text x="640" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">Edge host</text>
290
+
291
+ <rect x="732" y="676" width="14" height="10" rx="2" fill="rgba(85,86,107,0.10)" stroke="#8d8ea3" stroke-width="1"/>
292
+ <text x="752" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">External / port</text>
293
+
294
+ <line x1="880" y1="680" x2="908" y2="680" stroke="#5b5bf0" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
295
+ <text x="916" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">Primary</text>
296
+
297
+ <line x1="1000" y1="680" x2="1028" y2="680" stroke="#4545c8" stroke-width="1.2" marker-end="url(#arrow-link)"/>
298
+ <text x="1036" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">HTTP / mint</text>
299
+
300
+ <line x1="1140" y1="680" x2="1168" y2="680" stroke="#55566b" stroke-width="1" stroke-dasharray="4,3" marker-end="url(#arrow)"/>
301
+ <text x="1176" y="684" fill="#55566b" font-size="8" font-family="'Noto Sans Hebrew', sans-serif">Optional</text>
302
+ </svg>
303
+ </div>
304
+ </body>
305
+ </html>
package/docs/guide.md CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  Practical deep dive: architecture, host recipes, ports, tools, and multi-tenant
4
4
  connectors. For a working minimal server, start from the [README](../README.md).
5
- API tables and exports live in [reference.md](reference.md). Security guarantees
6
- and threat model: [security.md](security.md).
5
+ The **canonical example** is [`examples/project-desk`](../examples/project-desk/)
6
+ (Project desk: login → consent → user-scoped tools; local docs in
7
+ [`examples/saas-demo`](../examples/saas-demo/)). API tables and exports live
8
+ in [reference.md](reference.md). Security guarantees and threat model:
9
+ [security.md](security.md).
7
10
 
8
11
  [Architecture](#how-it-fits-together) · [Clients](#clients) · [Recipes](#recipes) ·
9
12
  [Compose the primitives](#advanced-compose-the-primitives) ·
@@ -69,7 +72,7 @@ registration.
69
72
  // Worker
70
73
  export default { fetch: (req: Request, env: Env) => buildApp(env).fetch(req) };
71
74
 
72
- // Next.js App Router — app/[[...mcp]]/route.ts
75
+ // Next.js App Router — see examples/nextjs-saas/
73
76
  export const GET = (req: Request) => app.fetch(req);
74
77
  export const POST = (req: Request) => app.fetch(req);
75
78
  export const OPTIONS = (req: Request) => app.fetch(req);
@@ -181,7 +184,7 @@ Also mount `.well-known/*` and `/mcp/oauth/*` the same way (or route everything
181
184
 
182
185
  Express, Cloud Functions, Cloud Run, `http.createServer` — bridge with
183
186
  `asNodeHandler`. Runnable recipe: [`examples/http-server.ts`](../examples/http-server.ts)
184
- (`npx tsx examples/http-server.ts`).
187
+ (set `MCP_SECRET`, e.g. from `openssl rand -base64 32`, then `npx tsx examples/http-server.ts`).
185
188
 
186
189
  ## Ports — what you implement
187
190
 
@@ -191,18 +194,22 @@ The library stays protocol-shaped. Your app plugs in the seams:
191
194
 
192
195
  ### App (`createMcpApp`)
193
196
 
197
+ Prefer `signedTokenAuth({ secret, resolveUser, loginUrl, codeStore })` to fill
198
+ `codeSecret` / `mintAccessToken` / `verifyToken` from one secret. The table
199
+ below is the advanced port surface when you outgrow the helper.
200
+
194
201
  | Port | Role |
195
202
  |------|------|
196
203
  | `verifyToken(token, req)` | Decode a bearer and return `{ userId, scopes, audience, claims? }`, or `null`. **The library compares `audience` against this server's canonical resource** — you cannot forget the check. Optional `claims` are passed to `context` on `principal` |
197
204
  | `resolveUser`, `loginUrl`, `mintAccessToken`, `codeSecret` | Same as the OAuth ports below |
198
205
  | `clientStore?` | Pre-registered clients; required for confidential clients like Gemini |
199
- | `refreshAccessToken?` | If set, metadata advertises `refresh_token` |
206
+ | `refreshAccessToken?` | If set, metadata advertises `refresh_token`. Receives `resource` and optional reduced `scope` — **MUST reject tokens not originally issued for that resource, and any `scope` outside the originally granted set** (see [security.md](security.md)) |
200
207
  | `revokeToken?` | RFC 7009; presence mounts `/revoke` and advertises `revocation_endpoint` |
201
208
 
202
209
  `consent?` (approval-page policy — see [Consent](#consent), below) and
203
210
  `defaultScopes?` sit as top-level `McpAppOptions`, not under `auth` — they
204
211
  are policy, not credentials, the same reasoning as `allowLoopback`.
205
- | `context?`, `audit?` | Same as the MCP ports below; `context` defaults to an empty object. Receives `principal.claims` when `verifyToken` set them |
212
+ | `context?`, `audit?` | `context` defaults to `{}`. Top-level `audit` is a **unified** sink for MCP and OAuth (`entry.source` is `"mcp"` or `"oauth"`). `auth.audit` overrides OAuth delivery when both are set |
206
213
 
207
214
  ### MCP (`createMcpHandler`)
208
215
 
@@ -213,7 +220,7 @@ The lower-level handler. Here the audience check is **yours** — prefer
213
220
  |------|------|
214
221
  | `authenticate(req, method, tool?)` | Return `{ id, scopes, claims? }`, or `null` → 401 + `WWW-Authenticate`. **Must reject tokens whose audience is not this server's canonical resource** |
215
222
  | `context(req, principal)` | Build per-request ctx for tools (DB, env, …). Use `principal?.claims` for tenant / plan / role without re-decoding the bearer |
216
- | `audit?(entry)` | Opt-in **metrics hook**: pass any function to receive tool results **and** auth denials (bad token, missing scope, query-string token), plus the 500 path when a host port throws. `entry.method` is `""` for transport-level denials made before parsing. Protocol errors (malformed JSON, bad `jsonrpc`, unsupported protocol version, oversized body, batch) are **not** audited. Throwing from this port never fails the request, and neither does hanging — see `auditTimeoutMs` below. Omit it and the library stays silent |
223
+ | `audit?(entry)` | Opt-in **metrics hook**: pass any function to receive tool results **and** auth denials (bad token, missing scope, query-string token, rejected browser Origin), plus the 500 path when a host port throws. `entry.method` is `""` for transport-level denials made before parsing (`query_string_token`, `unauthorized`, `origin_not_allowed`). Protocol errors (malformed JSON, bad `jsonrpc`, unsupported protocol version, oversized body, batch) are **not** audited. Throwing from this port never fails the request, and neither does hanging — see `auditTimeoutMs` below. Omit it and the library stays silent |
217
224
 
218
225
  `auditTimeoutMs` (default 1000ms) races `audit` against a timeout, so a
219
226
  slow sink can't stall a response past that bound either.
@@ -233,14 +240,14 @@ createMcpApp({ /* … */, audit: consoleAudit });
233
240
  createMcpApp({
234
241
  /* … */,
235
242
  audit: (entry) => {
236
- // entry: { method, tool?, principalId?, ok, error?, durationMs }
243
+ // entry.source === "mcp" | "oauth"
237
244
  void metrics.record(entry);
238
245
  },
239
246
  });
240
247
  ```
241
248
 
242
- Failures (denials and tool exceptions) go to `console.error` when using
243
- `consoleAudit`; everything else to `console.log`. Entries never carry raw
249
+ Failures (MCP denials and tool exceptions) go to `console.error` when using
250
+ `consoleAudit`; MCP successes and OAuth events go to `console.log`. Entries never carry raw
244
251
  exceptions or stack traces, so console output is safe. For anything beyond
245
252
  stdout, write your own `audit` function — that is the intended production path.
246
253
 
@@ -248,15 +255,21 @@ stdout, write your own `audit` function — that is the intended production path
248
255
 
249
256
  By default `initialize`, `ping`, and notifications are public. Override `publicMethods` if every method must require Bearer.
250
257
 
258
+ **Browser `Origin`.** MCP Streamable HTTP expects servers to validate the
259
+ request `Origin` header. Pass `allowedRequestOrigins` on `createMcpHandler` /
260
+ `createMcpApp`. Native connectors omit the header and are admitted. An omitted
261
+ allowlist **rejects** any present `Origin` (use `["*"]` only if you mean it).
262
+ This is separate from Node Host `allowedOrigins` on `asNodeHandler`.
263
+
251
264
  ### OAuth (`createOAuthRouter`)
252
265
 
253
266
  | Port | Role |
254
267
  |------|------|
255
- | `codeSecret` | HMAC secret for signed auth codes (string or `(req) => string`) |
268
+ | `codeSecret` | Secret(s) sealing auth codes, consent tickets, and DCR client assertions, and keying `secretHash` (string, `string[]`, or `(req) => string \| string[]`). A single string works as before; pass an array to rotate keys — see [security.md](security.md#rotating-codesecret) |
256
269
  | `resolveUser` | Consenting user, or `null` → redirect to `loginUrl` |
257
270
  | `loginUrl` | Where unauthenticated authorize requests go |
258
271
  | `mintAccessToken` | Issue a **user-bound**, **audience-bound** access token (`resource` is the RFC 8707 URI) — never a shared god token |
259
- | `refreshAccessToken?` | If set, metadata advertises `refresh_token`; also receives `resource` |
272
+ | `refreshAccessToken?` | If set, metadata advertises `refresh_token`; receives `resource` and optional client `scope` (advertised-scope check is library-side). **MUST reject refresh tokens not originally issued for that `resource`, and any `scope` outside the originally granted set** — opaque RT, same pattern as audience |
260
273
  | `revokeToken?` | If set, mounts `/revoke` and advertises `revocation_endpoint`. Well-formed authenticated revoke → 200 even if the token is unknown; `invalid_request` 400 and `invalid_client` 401 still apply |
261
274
  | `codeStore?` | Shared single-use jti store for multi-instance (`consume(jti, expMs)`); pruning in-memory default otherwise. Also backs consent-ticket single-use — see below |
262
275
  | `clientStore?` | Pre-registered clients: `get(clientId)` returns registered redirect URIs and auth method. Two ways to authenticate a confidential client's secret — **prefer `secretHash(clientId)`**: return a stored hash from `hashClientSecret`, and the library compares it with the same constant-time primitive it uses everywhere else; `verifySecret(clientId, presented)` is the fallback when you'd rather compare yourself. Credentials never enter the library either way |
@@ -315,17 +328,25 @@ createToolRegistry(tools, { validateArgs: false }) // default: off, easy adoptio
315
328
  ```
316
329
 
317
330
  - Handler may return a `string` or `{ content, isError? }`
318
- - Unknown tools and thrown errors become `isError: true` (not transport errors)
331
+ - A thrown handler exception becomes `isError: true` (not a transport error)
332
+ - `registry.call(name, ...)` itself returns `isError: true` for an unknown
333
+ `name` too — but the JSON-RPC `tools/call` dispatch (`createMcpHandler` /
334
+ `createMcpApp`) checks the name against the registry *before* calling it,
335
+ and answers an unrecognized one with a protocol-level `-32602` error
336
+ instead: MCP distinguishes "this request doesn't refer to a real tool"
337
+ from a tool's own execution outcome. The registry's `isError: true`
338
+ fallback only surfaces to a caller that invokes `registry.call` directly,
339
+ outside that dispatch.
319
340
  - Thrown exceptions are **redacted** by default (`"Tool execution failed"`); pass `onToolError` to map them. An `onToolError` that itself throws or returns a non-string falls back to the redacted default
320
341
  - Duplicate tool names throw at registry construction
321
342
  - Turn `validateArgs: true` once clients send schema-valid args. The evaluated
322
343
  subset is `type`, `enum`, `required`, `properties`, `items`, `minimum`,
323
- `maximum`. Pure metadata (`description`, `title`, `$schema`, `$id`,
344
+ `maximum`, and boolean `additionalProperties`. Pure metadata (`description`, `title`, `$schema`, `$id`,
324
345
  `$comment`, `default`, `examples`, `deprecated`, `readOnly`, `writeOnly`,
325
346
  `format`) is allowed and never evaluated. Any other keyword is not
326
347
  evaluated — and with `validateArgs: true` `createToolRegistry` throws at
327
- construction so a schema like `pattern` or `additionalProperties` cannot
328
- silently look enforced
348
+ construction so a schema like `pattern` or a schema-valued
349
+ `additionalProperties` cannot silently look enforced
329
350
 
330
351
  ### Wrapping an API as a tool
331
352
 
@@ -370,6 +391,15 @@ const getWeather = apiTool({
370
391
  echoes back). Pass `onError(res)` to shape it yourself, e.g. include a
371
392
  redacted version of the body. `fetch` is overridable — testing, request
372
393
  signing, a custom agent.
394
+ - **`timeoutMs`** (default `30000`) aborts the upstream call past that many
395
+ ms — an upstream that never responds otherwise hangs the tool call (and
396
+ the MCP request behind it) indefinitely. Pass `false` to disable; a
397
+ timeout firing surfaces as `isError: true` with a clear message, not a
398
+ thrown exception `onToolError` would redact.
399
+ - **`maxResponseBytes`** (default 1 MiB) caps the upstream response body
400
+ before `respond`/`onError` ever see it — the same memory-exhaustion shape
401
+ an unbounded *incoming* request body is. Exceeding it also surfaces as
402
+ `isError: true`, not a thrown exception.
373
403
  - `defineTool` alone (without `request`/`respond`) is just the typed-args
374
404
  layer over a regular `ToolDef` — use it for anything, not only REST calls.
375
405