@zackbart/connecta 0.15.1 → 0.16.1

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 (335) hide show
  1. package/AGENTS.md +40 -20
  2. package/CHANGELOG.md +653 -0
  3. package/README.md +25 -5
  4. package/bin/connecta.mjs +44 -3
  5. package/dist/access-tokens.d.ts +0 -1
  6. package/dist/access-tokens.js +0 -1
  7. package/dist/activity.d.ts +50 -2
  8. package/dist/activity.js +34 -1
  9. package/dist/apps-shell.d.ts +0 -1
  10. package/dist/apps-shell.js +0 -1
  11. package/dist/auth/bearer.d.ts +0 -1
  12. package/dist/auth/bearer.js +0 -1
  13. package/dist/auth/clerk.d.ts +0 -1
  14. package/dist/auth/clerk.js +45 -9
  15. package/dist/auth/downstream-oauth.d.ts +0 -1
  16. package/dist/auth/downstream-oauth.js +0 -1
  17. package/dist/call-admission.d.ts +0 -1
  18. package/dist/call-admission.js +0 -1
  19. package/dist/catalog-drift.d.ts +99 -0
  20. package/dist/catalog-drift.js +251 -0
  21. package/dist/catalog-fingerprint.d.ts +0 -1
  22. package/dist/catalog-fingerprint.js +0 -1
  23. package/dist/catalog-limits.d.ts +0 -1
  24. package/dist/catalog-limits.js +0 -1
  25. package/dist/catalog-service.d.ts +0 -1
  26. package/dist/catalog-service.js +41 -5
  27. package/dist/catalog.d.ts +11 -1
  28. package/dist/catalog.js +20 -1
  29. package/dist/concurrency.d.ts +0 -1
  30. package/dist/concurrency.js +0 -1
  31. package/dist/connector-scope.d.ts +0 -1
  32. package/dist/connector-scope.js +0 -1
  33. package/dist/connectors/api.d.ts +29 -21
  34. package/dist/connectors/api.js +45 -19
  35. package/dist/connectors/guarded-fetch.d.ts +106 -0
  36. package/dist/connectors/guarded-fetch.js +289 -0
  37. package/dist/connectors/remote-mcp.d.ts +0 -1
  38. package/dist/connectors/remote-mcp.js +0 -1
  39. package/dist/credentials.d.ts +0 -1
  40. package/dist/credentials.js +0 -1
  41. package/dist/errors.d.ts +30 -3
  42. package/dist/errors.js +1 -1
  43. package/dist/execute.d.ts +0 -1
  44. package/dist/execute.js +0 -1
  45. package/dist/executor-admission.d.ts +11 -1
  46. package/dist/executor-admission.js +28 -1
  47. package/dist/executor-result.d.ts +0 -1
  48. package/dist/executor-result.js +0 -1
  49. package/dist/executors/quickjs-child.d.ts +0 -1
  50. package/dist/executors/quickjs-child.js +0 -1
  51. package/dist/executors/quickjs-protocol.d.ts +0 -1
  52. package/dist/executors/quickjs-protocol.js +0 -1
  53. package/dist/executors/quickjs-runtime.d.ts +0 -1
  54. package/dist/executors/quickjs-runtime.js +0 -1
  55. package/dist/executors/quickjs.d.ts +0 -1
  56. package/dist/executors/quickjs.js +2 -1
  57. package/dist/favicon.d.ts +0 -1
  58. package/dist/favicon.js +0 -1
  59. package/dist/index.d.ts +2 -3
  60. package/dist/index.js +24 -7
  61. package/dist/invocation.d.ts +0 -1
  62. package/dist/invocation.js +0 -1
  63. package/dist/json-schema.d.ts +0 -1
  64. package/dist/json-schema.js +0 -1
  65. package/dist/mcp-result.d.ts +0 -1
  66. package/dist/mcp-result.js +0 -1
  67. package/dist/meta-tools.d.ts +0 -1
  68. package/dist/meta-tools.js +0 -1
  69. package/dist/node.d.ts +0 -1
  70. package/dist/node.js +0 -1
  71. package/dist/operator-ui/generated.d.ts +2 -3
  72. package/dist/operator-ui/generated.js +3 -4
  73. package/dist/operator-ui/model.d.ts +10 -1
  74. package/dist/operator-ui/model.js +0 -1
  75. package/dist/operator-ui/view.d.ts +167 -0
  76. package/dist/operator-ui/view.js +270 -0
  77. package/dist/providers/cloudflare.d.ts +0 -1
  78. package/dist/providers/cloudflare.js +261 -299
  79. package/dist/providers/linear.d.ts +26 -5
  80. package/dist/providers/linear.js +32 -61
  81. package/dist/providers/mixpanel.d.ts +35 -4
  82. package/dist/providers/mixpanel.js +53 -79
  83. package/dist/providers/notion.d.ts +0 -1
  84. package/dist/providers/notion.js +87 -42
  85. package/dist/providers/stripe.d.ts +13 -1
  86. package/dist/providers/stripe.js +31 -60
  87. package/dist/registry.d.ts +34 -2
  88. package/dist/registry.js +85 -5
  89. package/dist/retired-toolkits.d.ts +0 -1
  90. package/dist/retired-toolkits.js +0 -1
  91. package/dist/routes/access-tokens.d.ts +0 -1
  92. package/dist/routes/access-tokens.js +0 -1
  93. package/dist/routes/activity.d.ts +0 -1
  94. package/dist/routes/activity.js +0 -1
  95. package/dist/routes/credentials.d.ts +0 -1
  96. package/dist/routes/credentials.js +0 -1
  97. package/dist/routes/mcp.d.ts +0 -1
  98. package/dist/routes/mcp.js +0 -1
  99. package/dist/routes/oauth.d.ts +0 -1
  100. package/dist/routes/oauth.js +0 -1
  101. package/dist/routes/shared.d.ts +2 -1
  102. package/dist/routes/shared.js +0 -1
  103. package/dist/routes/ui.d.ts +0 -1
  104. package/dist/routes/ui.js +0 -1
  105. package/dist/server.d.ts +0 -1
  106. package/dist/server.js +11 -1
  107. package/dist/skills.d.ts +0 -1
  108. package/dist/skills.js +0 -1
  109. package/dist/storage/file.d.ts +0 -1
  110. package/dist/storage/file.js +0 -1
  111. package/dist/storage/memory.d.ts +0 -1
  112. package/dist/storage/memory.js +0 -1
  113. package/dist/timeout.d.ts +0 -1
  114. package/dist/timeout.js +0 -1
  115. package/dist/tool-safety.d.ts +0 -1
  116. package/dist/tool-safety.js +0 -1
  117. package/dist/types.d.ts +45 -1
  118. package/dist/types.js +0 -1
  119. package/dist/ui.d.ts +0 -1
  120. package/dist/ui.js +13 -137
  121. package/dist/validate.d.ts +13 -15
  122. package/dist/validate.js +24 -11
  123. package/dist/version.d.ts +1 -2
  124. package/dist/version.js +1 -2
  125. package/documentation/architecture.md +186 -5
  126. package/documentation/auth.md +11 -0
  127. package/documentation/call-admission.md +142 -6
  128. package/documentation/cloudflare.md +92 -10
  129. package/documentation/code-first-exploration.md +2 -2
  130. package/documentation/code-mode.md +14 -14
  131. package/documentation/connector-guides.md +172 -5
  132. package/documentation/connectors.md +89 -0
  133. package/documentation/linear.md +20 -2
  134. package/documentation/mcp-ui-design.md +2 -2
  135. package/documentation/meta-tools.md +7 -1
  136. package/documentation/mixpanel.md +42 -9
  137. package/documentation/notion.md +23 -2
  138. package/documentation/operations.md +325 -5
  139. package/documentation/operator-ui.md +120 -5
  140. package/documentation/provider-audit.md +160 -0
  141. package/documentation/provider-conventions.md +667 -0
  142. package/documentation/request-admission.md +167 -5
  143. package/documentation/rich-output-design.md +1 -2
  144. package/documentation/stripe.md +15 -0
  145. package/documentation/upgrading.md +454 -0
  146. package/ethos.md +14 -9
  147. package/examples/worker/README.md +102 -12
  148. package/examples/worker/src/index.ts +46 -1
  149. package/examples/worker/wrangler.jsonc +14 -1
  150. package/package.json +10 -6
  151. package/templates/node/.dockerignore +10 -0
  152. package/templates/node/.env.example +40 -3
  153. package/templates/node/AGENTS.md +16 -1
  154. package/templates/node/Dockerfile +49 -0
  155. package/templates/node/README.md +117 -2
  156. package/templates/node/docker-compose.yml +39 -0
  157. package/templates/node/package.json +1 -1
  158. package/templates/node/src/file-activity.ts +182 -0
  159. package/templates/node/src/index.ts +80 -4
  160. package/assets/connecta-clay-hero.png +0 -0
  161. package/dist/access-tokens.d.ts.map +0 -1
  162. package/dist/access-tokens.js.map +0 -1
  163. package/dist/activity.d.ts.map +0 -1
  164. package/dist/activity.js.map +0 -1
  165. package/dist/apps-shell.d.ts.map +0 -1
  166. package/dist/apps-shell.js.map +0 -1
  167. package/dist/auth/bearer.d.ts.map +0 -1
  168. package/dist/auth/bearer.js.map +0 -1
  169. package/dist/auth/clerk.d.ts.map +0 -1
  170. package/dist/auth/clerk.js.map +0 -1
  171. package/dist/auth/downstream-oauth.d.ts.map +0 -1
  172. package/dist/auth/downstream-oauth.js.map +0 -1
  173. package/dist/call-admission.d.ts.map +0 -1
  174. package/dist/call-admission.js.map +0 -1
  175. package/dist/catalog-fingerprint.d.ts.map +0 -1
  176. package/dist/catalog-fingerprint.js.map +0 -1
  177. package/dist/catalog-limits.d.ts.map +0 -1
  178. package/dist/catalog-limits.js.map +0 -1
  179. package/dist/catalog-service.d.ts.map +0 -1
  180. package/dist/catalog-service.js.map +0 -1
  181. package/dist/catalog.d.ts.map +0 -1
  182. package/dist/catalog.js.map +0 -1
  183. package/dist/concurrency.d.ts.map +0 -1
  184. package/dist/concurrency.js.map +0 -1
  185. package/dist/connector-scope.d.ts.map +0 -1
  186. package/dist/connector-scope.js.map +0 -1
  187. package/dist/connectors/api.d.ts.map +0 -1
  188. package/dist/connectors/api.js.map +0 -1
  189. package/dist/connectors/remote-mcp.d.ts.map +0 -1
  190. package/dist/connectors/remote-mcp.js.map +0 -1
  191. package/dist/credentials.d.ts.map +0 -1
  192. package/dist/credentials.js.map +0 -1
  193. package/dist/errors.d.ts.map +0 -1
  194. package/dist/errors.js.map +0 -1
  195. package/dist/execute.d.ts.map +0 -1
  196. package/dist/execute.js.map +0 -1
  197. package/dist/executor-admission.d.ts.map +0 -1
  198. package/dist/executor-admission.js.map +0 -1
  199. package/dist/executor-result.d.ts.map +0 -1
  200. package/dist/executor-result.js.map +0 -1
  201. package/dist/executors/quickjs-child.d.ts.map +0 -1
  202. package/dist/executors/quickjs-child.js.map +0 -1
  203. package/dist/executors/quickjs-protocol.d.ts.map +0 -1
  204. package/dist/executors/quickjs-protocol.js.map +0 -1
  205. package/dist/executors/quickjs-runtime.d.ts.map +0 -1
  206. package/dist/executors/quickjs-runtime.js.map +0 -1
  207. package/dist/executors/quickjs.d.ts.map +0 -1
  208. package/dist/executors/quickjs.js.map +0 -1
  209. package/dist/favicon.d.ts.map +0 -1
  210. package/dist/favicon.js.map +0 -1
  211. package/dist/index.d.ts.map +0 -1
  212. package/dist/index.js.map +0 -1
  213. package/dist/invocation.d.ts.map +0 -1
  214. package/dist/invocation.js.map +0 -1
  215. package/dist/json-schema.d.ts.map +0 -1
  216. package/dist/json-schema.js.map +0 -1
  217. package/dist/mcp-result.d.ts.map +0 -1
  218. package/dist/mcp-result.js.map +0 -1
  219. package/dist/meta-tools.d.ts.map +0 -1
  220. package/dist/meta-tools.js.map +0 -1
  221. package/dist/node.d.ts.map +0 -1
  222. package/dist/node.js.map +0 -1
  223. package/dist/operator-ui/generated.d.ts.map +0 -1
  224. package/dist/operator-ui/generated.js.map +0 -1
  225. package/dist/operator-ui/model.d.ts.map +0 -1
  226. package/dist/operator-ui/model.js.map +0 -1
  227. package/dist/providers/cloudflare.d.ts.map +0 -1
  228. package/dist/providers/cloudflare.js.map +0 -1
  229. package/dist/providers/linear.d.ts.map +0 -1
  230. package/dist/providers/linear.js.map +0 -1
  231. package/dist/providers/mixpanel.d.ts.map +0 -1
  232. package/dist/providers/mixpanel.js.map +0 -1
  233. package/dist/providers/notion.d.ts.map +0 -1
  234. package/dist/providers/notion.js.map +0 -1
  235. package/dist/providers/stripe.d.ts.map +0 -1
  236. package/dist/providers/stripe.js.map +0 -1
  237. package/dist/registry.d.ts.map +0 -1
  238. package/dist/registry.js.map +0 -1
  239. package/dist/retired-toolkits.d.ts.map +0 -1
  240. package/dist/retired-toolkits.js.map +0 -1
  241. package/dist/routes/access-tokens.d.ts.map +0 -1
  242. package/dist/routes/access-tokens.js.map +0 -1
  243. package/dist/routes/activity.d.ts.map +0 -1
  244. package/dist/routes/activity.js.map +0 -1
  245. package/dist/routes/credentials.d.ts.map +0 -1
  246. package/dist/routes/credentials.js.map +0 -1
  247. package/dist/routes/mcp.d.ts.map +0 -1
  248. package/dist/routes/mcp.js.map +0 -1
  249. package/dist/routes/oauth.d.ts.map +0 -1
  250. package/dist/routes/oauth.js.map +0 -1
  251. package/dist/routes/shared.d.ts.map +0 -1
  252. package/dist/routes/shared.js.map +0 -1
  253. package/dist/routes/ui.d.ts.map +0 -1
  254. package/dist/routes/ui.js.map +0 -1
  255. package/dist/server.d.ts.map +0 -1
  256. package/dist/server.js.map +0 -1
  257. package/dist/skills.d.ts.map +0 -1
  258. package/dist/skills.js.map +0 -1
  259. package/dist/storage/file.d.ts.map +0 -1
  260. package/dist/storage/file.js.map +0 -1
  261. package/dist/storage/memory.d.ts.map +0 -1
  262. package/dist/storage/memory.js.map +0 -1
  263. package/dist/timeout.d.ts.map +0 -1
  264. package/dist/timeout.js.map +0 -1
  265. package/dist/tool-safety.d.ts.map +0 -1
  266. package/dist/tool-safety.js.map +0 -1
  267. package/dist/types.d.ts.map +0 -1
  268. package/dist/types.js.map +0 -1
  269. package/dist/ui.d.ts.map +0 -1
  270. package/dist/ui.js.map +0 -1
  271. package/dist/validate.d.ts.map +0 -1
  272. package/dist/validate.js.map +0 -1
  273. package/dist/version.d.ts.map +0 -1
  274. package/dist/version.js.map +0 -1
  275. package/examples/node/README.md +0 -52
  276. package/examples/node/src/index.ts +0 -71
  277. package/src/access-tokens.ts +0 -289
  278. package/src/activity.ts +0 -234
  279. package/src/apps-shell.ts +0 -370
  280. package/src/auth/bearer.ts +0 -61
  281. package/src/auth/clerk.ts +0 -553
  282. package/src/auth/downstream-oauth.ts +0 -575
  283. package/src/call-admission.ts +0 -521
  284. package/src/catalog-fingerprint.ts +0 -52
  285. package/src/catalog-limits.ts +0 -11
  286. package/src/catalog-service.ts +0 -1153
  287. package/src/catalog.ts +0 -790
  288. package/src/concurrency.ts +0 -39
  289. package/src/connector-scope.ts +0 -66
  290. package/src/connectors/api.ts +0 -159
  291. package/src/connectors/remote-mcp.ts +0 -1090
  292. package/src/credentials.ts +0 -428
  293. package/src/errors.ts +0 -364
  294. package/src/execute.ts +0 -1406
  295. package/src/executor-admission.ts +0 -328
  296. package/src/executor-result.ts +0 -107
  297. package/src/executors/quickjs-child.ts +0 -172
  298. package/src/executors/quickjs-protocol.ts +0 -99
  299. package/src/executors/quickjs-runtime.ts +0 -470
  300. package/src/executors/quickjs.ts +0 -776
  301. package/src/favicon.ts +0 -53
  302. package/src/index.ts +0 -666
  303. package/src/invocation.ts +0 -655
  304. package/src/json-schema.ts +0 -11
  305. package/src/mcp-result.ts +0 -43
  306. package/src/meta-tools.ts +0 -1607
  307. package/src/node.ts +0 -206
  308. package/src/operator-ui/browser.css +0 -423
  309. package/src/operator-ui/browser.ts +0 -1281
  310. package/src/operator-ui/generated.ts +0 -4
  311. package/src/operator-ui/model.ts +0 -111
  312. package/src/providers/cloudflare.ts +0 -3999
  313. package/src/providers/linear.ts +0 -302
  314. package/src/providers/mixpanel.ts +0 -229
  315. package/src/providers/notion.ts +0 -1879
  316. package/src/providers/stripe.ts +0 -307
  317. package/src/registry.ts +0 -1085
  318. package/src/retired-toolkits.ts +0 -14
  319. package/src/routes/access-tokens.ts +0 -115
  320. package/src/routes/activity.ts +0 -215
  321. package/src/routes/credentials.ts +0 -254
  322. package/src/routes/mcp.ts +0 -429
  323. package/src/routes/oauth.ts +0 -322
  324. package/src/routes/shared.ts +0 -247
  325. package/src/routes/ui.ts +0 -152
  326. package/src/server.ts +0 -195
  327. package/src/skills.ts +0 -271
  328. package/src/storage/file.ts +0 -123
  329. package/src/storage/memory.ts +0 -44
  330. package/src/timeout.ts +0 -84
  331. package/src/tool-safety.ts +0 -15
  332. package/src/types.ts +0 -523
  333. package/src/ui.ts +0 -744
  334. package/src/validate.ts +0 -387
  335. package/src/version.ts +0 -7
package/dist/ui.js CHANGED
@@ -369,6 +369,11 @@ export async function buildUiData(registry, baseUrl, serverInfo, credentialVault
369
369
  : {}),
370
370
  toolCount: tools.length,
371
371
  tools,
372
+ // Counts only, and only when a refresh in this runtime produced them.
373
+ // `Registry.statusFor` already rebuilt the report through
374
+ // `boundedCatalogDrift`, so what lands here cannot carry a name or a
375
+ // schema even if the plugin seam returned one.
376
+ ...(status.catalogDrift ? { catalogDrift: status.catalogDrift } : {}),
372
377
  ...(c.disconnectAuth && c.startAuth ? { oauth: true } : {}),
373
378
  ...(credential ? { credential } : {}),
374
379
  };
@@ -477,147 +482,18 @@ ${clerkScript}
477
482
  ${owner}
478
483
  <div class="mast-nav">
479
484
  ${product}
480
- <div id="appNav" class="mast-actions hidden">
481
- <nav class="page-nav" aria-label="Operator pages">
482
- <a id="connectionsNav" class="navlink" href="/"
483
- data-operator-page="connections"${page === "connections" ? ' aria-current="page"' : ""}>Connections</a>
484
- <a id="credentialsNav" class="navlink hidden" href="/credentials"
485
- data-operator-page="credentials"${page === "credentials" ? ' aria-current="page"' : ""}>Credentials</a>
486
- <a id="tokensNav" class="navlink hidden" href="/tokens"
487
- data-operator-page="tokens"${page === "tokens" ? ' aria-current="page"' : ""}>Access tokens</a>
488
- <a id="activityNav" class="navlink hidden" href="/activity"
489
- data-operator-page="activity"${page === "activity" ? ' aria-current="page"' : ""}>Activity</a>
490
- </nav>
491
- <div class="session-actions" aria-label="Session actions">
492
- <button id="change" class="navlink hidden" type="button">Change token</button>
493
- <button id="signout" class="navlink hidden" type="button">Sign out</button>
494
- </div>
495
- </div>
485
+ <div id="operatorNav"></div>
496
486
  </div>
497
487
  </header>
498
488
 
499
489
  <main id="operatorContent" class="page shell" tabindex="-1">
500
- <section id="gate" class="hidden">
501
- <div class="lead pgrid">
502
- <h1 id="gateHeading" class="pcap" tabindex="-1">${OPERATOR_PAGE_LABELS[page]}</h1>
503
- <div class="pbody lead-copy">
504
- <p>${escapeHtmlAttr(brand.description)}</p>
505
- <p id="gateCopy" class="meta"></p>
506
- <div id="tokenGate" class="row gate-actions hidden">
507
- <input id="token" type="password" placeholder="Bearer token" autocomplete="off"
508
- aria-label="Bearer token">
509
- <button id="save" class="linklike" type="button">Open operator pages</button>
510
- </div>
511
- <div id="clerkGate" class="actions gate-actions hidden">
512
- <button id="signin" class="linklike" type="button">Team sign in</button>
513
- <button id="gateSignout" class="linklike hidden" type="button">Sign out</button>
514
- </div>
515
- <p id="err" role="alert"></p>
516
- </div>
517
- </div>
518
- </section>
519
-
520
- <div id="app" class="hidden">
521
- <section id="connectionsView"${page === "connections" ? "" : ' class="hidden"'}>
522
- <div class="lead pgrid">
523
- <h1 id="connectionsHeading" class="pcap" tabindex="-1">Connections</h1>
524
- <div class="pbody lead-copy">
525
- <p>Use this endpoint to give an MCP client access to the tools below.</p>
526
- <div class="endpoint">
527
- <div class="endpoint-row">
528
- <code id="mcpUrl" class="mono"></code>
529
- <button id="copyMcpUrl" class="linklike" type="button">Copy URL</button>
530
- </div>
531
- </div>
532
- <p class="cap" id="serverInfo">${escapeHtmlAttr(brand.productName)} operator</p>
533
- <p id="oauthNotice" class="meta" role="status" aria-live="polite" tabindex="-1"></p>
534
- </div>
535
- </div>
536
- <section class="section pgrid" aria-labelledby="connectorLedgerHeading">
537
- <h2 class="pcap" id="connectorLedgerHeading">Connectors</h2>
538
- <div class="pbody">
539
- <div class="row toolbar">
540
- <input id="filter" type="search" placeholder="Filter connectors or tools…"
541
- aria-label="Filter connectors or tools">
542
- </div>
543
- <div id="list" class="connector-tools" aria-busy="false"></div>
544
- </div>
545
- </section>
546
- </section>
547
-
548
- <section id="credentialsView"${page === "credentials" ? "" : ' class="hidden"'}>
549
- <div class="lead pgrid">
550
- <h1 id="credentialsHeading" class="pcap" tabindex="-1">Credentials</h1>
551
- <div class="pbody">
552
- <p class="activity-copy">Rotate operator-managed connector credentials. Stored values are never returned or displayed.</p>
553
- <p id="credentialNotice" class="meta" role="status" aria-live="polite"
554
- tabindex="-1"></p>
555
- <div id="credentialUnavailable" class="unavailable hidden"></div>
556
- <div id="credentialList" class="credential-ledger" aria-busy="false"></div>
557
- </div>
558
- </div>
559
- </section>
560
-
561
- <section id="tokensView"${page === "tokens" ? "" : ' class="hidden"'}>
562
- <div class="lead pgrid">
563
- <h1 id="tokensHeading" class="pcap" tabindex="-1">Access tokens</h1>
564
- <div class="pbody">
565
- <p class="activity-copy">Create named Bearer tokens for MCP clients. Each secret is shown once; revoke it when that client should lose access.</p>
566
- <p id="tokenNotice" class="meta" role="status" aria-live="polite"
567
- tabindex="-1"></p>
568
- <div id="tokenUnavailable" class="unavailable hidden"></div>
569
- <div id="tokenAvailable" class="hidden">
570
- <form id="tokenCreateForm" class="token-create">
571
- <label for="tokenName">Client name</label>
572
- <div class="row">
573
- <input id="tokenName" type="text" maxlength="80"
574
- placeholder="Claude desktop, ChatGPT production…"
575
- autocomplete="off">
576
- <button id="createToken" class="linklike" type="submit">Create token</button>
577
- </div>
578
- </form>
579
- <section id="tokenReveal" class="token-reveal hidden"
580
- aria-labelledby="tokenRevealHeading">
581
- <div class="token-reveal-head">
582
- <h2 id="tokenRevealHeading" tabindex="-1">Copy this token now</h2>
583
- <span class="cap">Shown once</span>
584
- </div>
585
- <p class="meta">Store it in the MCP client before leaving this page. It cannot be displayed again.</p>
586
- <div class="endpoint-row token-secret">
587
- <code id="createdToken" class="mono"></code>
588
- <button id="copyCreatedToken" class="linklike" type="button">Copy token</button>
589
- </div>
590
- <button id="dismissCreatedToken" class="linklike" type="button">I stored it</button>
591
- </section>
592
- <div id="tokenList" class="token-ledger" aria-busy="false"></div>
593
- </div>
594
- </div>
595
- </div>
596
- </section>
597
-
598
- <section id="activityView"${page === "activity" ? "" : ' class="hidden"'}>
599
- <div class="lead pgrid">
600
- <h1 id="activityHeading" class="pcap" tabindex="-1">Activity</h1>
601
- <div class="pbody">
602
- <p class="activity-copy" id="activitySummary">Arguments and results are never stored.</p>
603
- <div id="activityUnavailable" class="unavailable hidden">
604
- Activity history is not configured. Add an <span class="mono">activity.store</span>
605
- with a list reader to enable this page.
606
- </div>
607
- <div id="activityAvailable">
608
- <div class="row activity-controls">
609
- <input id="activitySearch" type="search"
610
- placeholder="Search user, tool, or outcome…"
611
- aria-label="Search loaded activity">
612
- <button id="refreshActivity" class="linklike" type="button">Refresh</button>
613
- </div>
614
- <p id="activityNotice" class="meta" role="status" aria-live="polite"></p>
615
- <div id="activityList" class="activity-ledger" aria-busy="false"></div>
616
- <button id="moreActivity" class="linklike activity-more hidden" type="button">Load older</button>
617
- </div>
618
- </div>
490
+ <div class="lead pgrid">
491
+ <h1 class="pcap">${OPERATOR_PAGE_LABELS[page]}</h1>
492
+ <div class="pbody lead-copy">
493
+ <p>${escapeHtmlAttr(brand.description)}</p>
494
+ <noscript><p class="msg">The operator pages need JavaScript. Nothing else here
495
+ does — agents reach this deployment through <span class="mono">/mcp</span>.</p></noscript>
619
496
  </div>
620
- </section>
621
497
  </div>
622
498
  </main>
623
499
 
@@ -627,9 +503,9 @@ const MCP_URL = ${jsonForInlineScript(mcpUrl)};
627
503
  const INITIAL_PAGE = ${jsonForInlineScript(page)};
628
504
  const TITLE_SUFFIX = ${jsonForInlineScript(brand.pageTitle)};
629
505
  const PRODUCT_NAME = ${stringForInlineScript(brand.productName)};
506
+ const PRODUCT_DESCRIPTION = ${stringForInlineScript(brand.description)};
630
507
  const PRODUCT_OPERATOR_LABEL = ${stringForInlineScript(brand.productName + " operator")};
631
508
  ${OPERATOR_UI_SCRIPT}</script>
632
509
  </body>
633
510
  </html>`;
634
511
  }
635
- //# sourceMappingURL=ui.js.map
@@ -21,17 +21,12 @@ export interface ValidateToolInputOptions {
21
21
  */
22
22
  failClosed?: boolean;
23
23
  }
24
- export interface PrecompileValidatorOptions {
24
+ export interface CompileValidatorOptions {
25
25
  /**
26
- * Tool address used in the warning text, conventionally
26
+ * Tool address used in the error text, conventionally
27
27
  * `"connectorId.toolName"`.
28
28
  */
29
29
  address: string;
30
- /**
31
- * Destination for the warning emitted when the schema cannot be compiled.
32
- * Default console.
33
- */
34
- logger?: Logger;
35
30
  }
36
31
  /**
37
32
  * Validate call arguments against a tool's JSON Schema.
@@ -60,12 +55,15 @@ export interface PrecompileValidatorOptions {
60
55
  */
61
56
  export declare function validateToolInput(schema: JsonSchema, args: unknown, opts: ValidateToolInputOptions): ConnectorCallError | null;
62
57
  /**
63
- * Eagerly compile and cache a tool's inputSchema so a schema the validator
64
- * cannot use surfaces once at connector construction rather than silently on
65
- * the first call. Reuses the same module-level cache `validateToolInput` reads,
66
- * so the runtime path hits the cache. Warning-only: it never throws and never
67
- * changes call behavior. A schema that only fails on first `validate()` (e.g.
68
- * an unresolvable `$ref`) still slips through here and is caught at call time.
58
+ * Eagerly compile and cache a tool's inputSchema, throwing when the validator
59
+ * cannot use it. Reuses the same module-level cache `validateToolInput` reads,
60
+ * so the runtime path hits the cache.
61
+ *
62
+ * This is the construction-time half of the contract hand-written tools sign:
63
+ * a schema connecta cannot enforce is the author's bug, and a deployment that
64
+ * boots with one is a deployment quietly promising validation it will not do.
65
+ * A schema that only fails on first `validate()` (an unresolvable `$ref`, say)
66
+ * still slips through here — the validator resolves those lazily — and is
67
+ * caught at call time by the caller's `failClosed`.
69
68
  */
70
- export declare function precompileValidator(schema: JsonSchema, opts: PrecompileValidatorOptions): void;
71
- //# sourceMappingURL=validate.d.ts.map
69
+ export declare function compileValidator(schema: JsonSchema, opts: CompileValidatorOptions): void;
package/dist/validate.js CHANGED
@@ -186,6 +186,11 @@ function validationDetails(schema, units) {
186
186
  function unevaluableSchema(address) {
187
187
  return new ConnectorCallError("invalid_args", `Cannot validate arguments for "${address}": its inputSchema could not be evaluated`);
188
188
  }
189
+ function unusableSchema(address, detail) {
190
+ return new Error(`Tool "${address}" has an inputSchema the validator cannot use ` +
191
+ `(${detail}) — fix the schema or drop it; a schema that cannot be ` +
192
+ "enforced must not ship as one that can.");
193
+ }
189
194
  function disableValidation(schema, address, logger, err) {
190
195
  validators.set(schema, null);
191
196
  logger.warn(`[connecta] tool "${address}" has an inputSchema the validator cannot use (${err instanceof Error ? err.message : String(err)}) — arguments are not validated`);
@@ -254,22 +259,30 @@ export function validateToolInput(schema, args, opts) {
254
259
  return null;
255
260
  }
256
261
  /**
257
- * Eagerly compile and cache a tool's inputSchema so a schema the validator
258
- * cannot use surfaces once at connector construction rather than silently on
259
- * the first call. Reuses the same module-level cache `validateToolInput` reads,
260
- * so the runtime path hits the cache. Warning-only: it never throws and never
261
- * changes call behavior. A schema that only fails on first `validate()` (e.g.
262
- * an unresolvable `$ref`) still slips through here and is caught at call time.
262
+ * Eagerly compile and cache a tool's inputSchema, throwing when the validator
263
+ * cannot use it. Reuses the same module-level cache `validateToolInput` reads,
264
+ * so the runtime path hits the cache.
265
+ *
266
+ * This is the construction-time half of the contract hand-written tools sign:
267
+ * a schema connecta cannot enforce is the author's bug, and a deployment that
268
+ * boots with one is a deployment quietly promising validation it will not do.
269
+ * A schema that only fails on first `validate()` (an unresolvable `$ref`, say)
270
+ * still slips through here — the validator resolves those lazily — and is
271
+ * caught at call time by the caller's `failClosed`.
263
272
  */
264
- export function precompileValidator(schema, opts) {
265
- if (validators.has(schema))
273
+ export function compileValidator(schema, opts) {
274
+ const cached = validators.get(schema);
275
+ if (cached)
266
276
  return;
267
- const logger = opts.logger ?? console;
277
+ // null marks a schema an earlier call already found unusable; recompiling it
278
+ // into a working validator would be the fail-open behavior wearing a hat.
279
+ if (cached === null) {
280
+ throw unusableSchema(opts.address, "an earlier call could not evaluate it");
281
+ }
268
282
  try {
269
283
  validators.set(schema, new Validator(schema, "2020-12", false));
270
284
  }
271
285
  catch (err) {
272
- disableValidation(schema, opts.address, logger, err);
286
+ throw unusableSchema(opts.address, err instanceof Error ? err.message : String(err));
273
287
  }
274
288
  }
275
- //# sourceMappingURL=validate.js.map
package/dist/version.d.ts CHANGED
@@ -4,5 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export declare const CONNECTA_VERSION = "0.15.1";
8
- //# sourceMappingURL=version.d.ts.map
7
+ export declare const CONNECTA_VERSION = "0.16.1";
package/dist/version.js CHANGED
@@ -4,5 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export const CONNECTA_VERSION = "0.15.1";
8
- //# sourceMappingURL=version.js.map
7
+ export const CONNECTA_VERSION = "0.16.1";
@@ -1,7 +1,188 @@
1
1
  # Architecture
2
2
 
3
- > **Stub.** The old manual was retired in the phase-1 docs restructure. This
4
- > document will be rewritten as an agent-facing guide what the subsystem is
5
- > for, how to work on it, and what it must never do — once the ideas in
6
- > [ethos.md](../ethos.md) settle. The prior text lives in git history as
7
- > `docs/architecture.md`.
3
+ One Web-standard `fetch(request) => Promise<Response>` handler, a long-lived
4
+ registry behind it, and a strict rule about what may be imported. Everything
5
+ else in this repository is a detail of those three things.
6
+
7
+ Read [`ethos.md`](../ethos.md) first. This guide says how the shape it
8
+ describes is actually assembled, and where a change is likely to break it.
9
+
10
+ ## The two lifetimes
11
+
12
+ Almost every bug in this codebase is a lifetime mistake, so the split is worth
13
+ stating before anything else.
14
+
15
+ **Per isolate, built once.** `createConnecta(config)` returns
16
+ `{ fetch, registry, close }`. The `Registry` owns the connector set, address
17
+ resolution, catalog caches, connector health, and the per-connector call
18
+ limiters. It is constructed once and lives as long as the isolate or process —
19
+ on Workers that means a lazy module-scope singleton, which is why both
20
+ deployment shapes build it outside the request handler.
21
+
22
+ **Per request, and no longer.** The MCP server, its transport, downstream MCP
23
+ clients, abort signals, and the connector scope a probe opens all belong to the
24
+ request that created them. `Nothing request-bound survives a request` is an
25
+ ethos invariant, not a style preference: a client retained across requests on
26
+ Workers is a cross-request capability leak, and a promise awaited after the
27
+ response is work the runtime may have already torn down. Deferred work has one
28
+ sanctioned channel — `ctx.waitUntil`, threaded through `fetch(request, env,
29
+ ctx)` and used for best-effort activity writes.
30
+
31
+ The registry is deliberately on the long side of that line and the MCP server
32
+ deliberately on the short side. A fresh `McpServer` per request is what makes
33
+ the deployment stateless: no sessions, no server push, no resumability, and
34
+ scope resolved from the request rather than remembered.
35
+
36
+ ## Request lifecycle
37
+
38
+ `src/server.ts` is the composition root. It does three things in order: upgrade
39
+ the scheme when it must, run the route table, then wrap whatever came back in
40
+ security headers. Route *order* is the contract — several routes would behave
41
+ differently if they were reachable in another order — so the table below is
42
+ read top to bottom.
43
+
44
+ | Order | Route | Notes |
45
+ | --- | --- | --- |
46
+ | 0 | HTTPS upgrade | 308 to `publicUrl` when it is HTTPS and the request arrived over HTTP. Path and query are *assigned* onto the configured URL, never resolved against it, so a `//host` pathname cannot replace the deployment origin. `/health` is exempt: a loopback container probe must not depend on public DNS and TLS. `/ui` is canonicalized to `/` while upgrading. |
47
+ | 1 | `/ui/access-tokens[/<id>]`, `/ui/credentials/<id>[/<action>]`, `/ui/oauth/<id>` | Private mutation routes, matched **first** so nothing can shadow them and so they own their own `OPTIONS` — they answer it with a refusal rather than inheriting the wildcard CORS preflight. |
48
+ | 2 | `OPTIONS` | Each auth provider's `handleMetadata` gets a chance (CORS preflight for browser MCP clients); otherwise 204 with MCP CORS. |
49
+ | 3 | `/.well-known/*` | Auth providers' `handleMetadata`, open. 404 when none handles it. |
50
+ | 4 | `/health` | Open JSON: status, connector count, `serverInfo`, the configured executor's sanitized name when it has one, catalog-drift counts, admission snapshots, reserved route names, and `deployment` when `deploymentInfo` is set. Payload-free by construction, and it never joins the MCP queue. |
51
+ | 5 | `/oauth/callback/<connectorId>` | Downstream-OAuth completion, open, `verifyState` before `finishAuth`. |
52
+ | 6 | `/favicon.*`, `/ui` → `/`, the operator shells, `/ui/data` | The operator surface ([operator UI](./operator-ui.md)). The shells are open and data-free; `/ui/data` behind them is gated. Built-ins are matched before connector routes, so a connector cannot shadow a page. |
53
+ | 7 | `/ui/activity` | Gated, plus the optional `activity.readGate`. `GET` only; 404 with no `activity.store.list`. |
54
+ | 8 | `/mcp` | **Admission before auth**, then the auth gate, then a fresh MCP server. |
55
+ | 9 | connector `handleRequest` | Registration order, open. Dispatched only after every built-in misses, so a connector can *add* a route and never shadow one of connecta's. First non-null response wins; a throw is a 500, not a fall-through. |
56
+ | 10 | — | 404. |
57
+
58
+ Every response leaves through `withSecurityHeaders`: `nosniff`, a no-referrer
59
+ policy, HSTS on HTTPS, and — on the operator shells — a nonce-based script CSP
60
+ and framing denial. `test/server-route-contracts.test.ts` pins this ordering
61
+ and the exact refusal bodies; it exists because the ordering is invisible in
62
+ any one file and a reordering reads like a harmless refactor.
63
+
64
+ `/mcp` itself is four steps, in this order and for these reasons:
65
+
66
+ 1. **Admit.** One permit from the deployment-wide FIFO pool, taken before auth
67
+ so an unauthenticated flood costs a permit rather than a Clerk lookup
68
+ ([request admission](./request-admission.md)). The permit is held until the
69
+ response *body* completes, not until the handler returns.
70
+ 2. **Authorize.** Each `InboundAuth` provider's `authorize` in order, bearer
71
+ before Clerk. First `ok` admits; if all fail, the last provider's challenge
72
+ response is returned. No providers configured means open — development
73
+ only, and it warns at construction.
74
+ 3. **Refuse `?toolkit=`.** Toolkits were removed ([#178](https://github.com/zackbart/connecta/issues/178))
75
+ but the URLs naming them were handed out, so the parameter is a 404 rather
76
+ than silently serving the full registry. Retiring a scoping boundary into
77
+ fail-open is the one outcome worse than the 404.
78
+ 4. **Serve.** A fresh `McpServer` per request, the seven meta-tools registered
79
+ against the registry, the Apps shell resource registered (and
80
+ `resources/list` deliberately answering with nothing), and the response
81
+ handed back.
82
+
83
+ ## Layers below the meta-tools
84
+
85
+ The meta-tool handlers are thin. The work sits in four services the registry
86
+ owns or hands out, and a change usually belongs in exactly one of them:
87
+
88
+ | Module | Owns |
89
+ | --- | --- |
90
+ | `src/registry.ts` | The connector set, id validation, address resolution, connector health, per-connector call limiters, and the drift snapshot. Construction-time refusal of structural mistakes lives here. |
91
+ | `src/catalog-service.ts` | Tool listing: cold-load coalescing, TTL, persistence as manifest plus revision-addressed chunks, stale fallback, and the completeness rule — a partial catalog is a failure, never a cache write. |
92
+ | `src/invocation.ts` | One tool call: argument validation, call admission, per-attempt timeout, retry with the connector's own `Retry-After` honoured exactly or declined, result unwrapping, size capping, and the activity record. |
93
+ | `src/catalog.ts` | Ranking, description summarizing, and the compact schema renderer discovery shows. |
94
+
95
+ `src/meta-tools.ts` and `src/execute.ts` are two front doors onto the same
96
+ three services. That is the point: a program's `connecta.call` and a top-level
97
+ `call_tool` reach `InvocationService.invoke` by different routes and get the
98
+ same admission, the same credential resolution, and the same fail-closed
99
+ read-only check. `test/execute.test.ts` asserts that parity directly, because
100
+ the alternative — a sandbox path that quietly diverges — is how generated code
101
+ would mint a capability.
102
+
103
+ ## Import-graph purity
104
+
105
+ Nothing reachable from `src/index.ts` may import a `node:` builtin. The core is
106
+ Web-API only so the same code runs unchanged in workerd and in Node.
107
+
108
+ The Node-touching paths are `src/node.ts` (the `node:http` adapter),
109
+ `src/storage/file.ts`, and the QuickJS process pool
110
+ (`src/executors/quickjs.ts` and its child entry). Each lives behind an explicit
111
+ subpath export — `@zackbart/connecta/node`, `@zackbart/connecta/quickjs` — and
112
+ must stay unreachable from the root entry. The optional Clerk adapter is behind
113
+ `./auth/clerk` for the adjacent reason: `@clerk/backend` is an optional peer,
114
+ not a dependency.
115
+
116
+ `test/purity.test.ts` walks the relative-import graph from `src/index.ts` and
117
+ fails on (a) any `node:` specifier in a reachable file and (b) the Node
118
+ adapter, file storage, QuickJS parent or child, or the Clerk adapter being
119
+ reachable at all. `test/package-surface.test.ts` and
120
+ `scripts/check-package.mjs` guard the other half — that the published tarball
121
+ matches the same boundary.
122
+
123
+ The failure mode this prevents is not theoretical: a single convenience import
124
+ of `node:crypto` in a shared helper makes the whole Worker deployment shape
125
+ stop building, and it will do so in someone else's repository rather than
126
+ this one.
127
+
128
+ ## Where things live
129
+
130
+ ```
131
+ src/
132
+ index.ts createConnecta + the public re-exports (Workers-clean)
133
+ server.ts route ordering, HTTPS upgrade, security wrapper
134
+ routes/ one file per surface; shared.ts holds the auth gate
135
+ meta-tools.ts the six non-execute meta-tools over the registry
136
+ execute.ts execute_code, the sandbox host bridge, emit and ui
137
+ apps-shell.ts the one build-time MCP Apps template
138
+ skills.ts MCP instructions, the usage skill, connector guides
139
+ registry.ts connector set, addresses, health, call limiters
140
+ catalog-service.ts catalog loading, caching, persistence, stale fallback
141
+ catalog.ts ranking, summaries, compact schema rendering
142
+ invocation.ts one tool call, end to end
143
+ catalog-drift.ts vetted manifests and the counts a refresh produces
144
+ credentials.ts the AES-GCM connector vault over KVStorage
145
+ access-tokens.ts operator-issued MCP bearer tokens
146
+ activity.ts payload-free event contracts + best-effort recorder
147
+ call-admission.ts connector-partitioned downstream permits and budgets
148
+ executor-admission.ts the portable bounded queue both pools use
149
+ ui.ts the served operator shell and /ui/data payload
150
+ operator-ui/ the Preact app, its pure rules, and the built bundle
151
+ connectors/ remote-mcp.ts, api.ts, guarded-fetch.ts
152
+ providers/ the maintained prebuilt connections
153
+ auth/ bearer, clerk (optional peer), downstream OAuth
154
+ executors/ the QuickJS pool and child (Node only)
155
+ storage/ memory.ts, file.ts (Node only)
156
+ node.ts listen() + fileStorage re-export (Node only)
157
+ ```
158
+
159
+ ## Sharp edges
160
+
161
+ - **The registry is shared; the request is not.** Anything you cache on the
162
+ registry is visible to every later request in that isolate. Anything you
163
+ cache per request dies with it. Putting a downstream client on the wrong side
164
+ of that line is the highest-severity mistake available here.
165
+ - **Route order is behavior.** Moving a built-in below the connector dispatch
166
+ hands a connector the ability to shadow it. Moving a mutation route below the
167
+ wildcard `OPTIONS` opts it into CORS preflight.
168
+ - **Admission runs before auth, on purpose.** Reordering them to "authenticate
169
+ first" makes the cheapest possible attack the most expensive request.
170
+ - **`close()` is idempotent and ordered.** It closes both admission pools and
171
+ the connector limiters, then the executor. Node's `listen()` calls it on
172
+ SIGTERM/SIGINT.
173
+ - **Structural mistakes throw at construction.** A duplicate connector id, an
174
+ invalid admission rule, `accessTokens` without a Clerk provider, a missing
175
+ executor: all refuse to boot. A deployment that starts in the wrong shape is
176
+ worse than one that does not start.
177
+
178
+ ## Tests that enforce this
179
+
180
+ | Invariant | Suite |
181
+ | --- | --- |
182
+ | The core imports no `node:` builtin and reaches no Node-only module | `test/purity.test.ts` |
183
+ | The published surface matches the same boundary | `test/package-surface.test.ts`, `scripts/check-package.mjs` |
184
+ | Route order, per-route auth, and byte-exact refusals | `test/server-route-contracts.test.ts` |
185
+ | `/mcp` end to end, the open routes, exactly seven tools | `test/server.test.ts`, `test/code-first-surface.test.ts` |
186
+ | Construction-time refusals and the grouped config boundary | `test/config.test.ts`, `test/registry.test.ts` |
187
+ | Program and top-level calls take the same enforced path | `test/execute.test.ts` |
188
+ | Both deployment shapes still compile and configure the real thing | `test/deployment-shapes.test.ts`, `npm run check:examples` |
@@ -4,6 +4,17 @@ Inbound auth decides who may reach the MCP endpoint. A deployment may admit a
4
4
  static bearer, operator-issued access tokens, Clerk identities, or a mixture.
5
5
  Static bearers are checked first.
6
6
 
7
+ ## Clerk configuration is checked at construction
8
+
9
+ `clerkAuth` reads its Frontend API origin out of `publishableKey`, so a key that
10
+ is not `pk_test_`/`pk_live_` followed by the base64-encoded domain cannot
11
+ produce one. That throws where `allowedDomains` throws — when `clerkAuth` is
12
+ called — with a message naming the option, never quoting the rejected value
13
+ back: the usual way to land here is pasting the *secret* key into the
14
+ publishable slot, and a startup error is a log line. A deployment that builds
15
+ per request, as the Workers shape does, sees the same error on its first
16
+ request instead of a base64 stack on every route.
17
+
7
18
  ## Operator-issued access tokens
8
19
 
9
20
  Set `accessTokens: {}` to let eligible Clerk operators create named Bearer