@zackbart/connecta 0.15.1 → 0.16.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 (332) hide show
  1. package/AGENTS.md +28 -15
  2. package/CHANGELOG.md +466 -0
  3. package/README.md +19 -4
  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 +0 -1
  27. package/dist/catalog.d.ts +0 -1
  28. package/dist/catalog.js +0 -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 +0 -1
  42. package/dist/errors.js +0 -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 +250 -276
  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 +58 -8
  129. package/documentation/code-mode.md +12 -12
  130. package/documentation/connector-guides.md +172 -5
  131. package/documentation/connectors.md +89 -0
  132. package/documentation/linear.md +20 -2
  133. package/documentation/mcp-ui-design.md +2 -2
  134. package/documentation/mixpanel.md +42 -9
  135. package/documentation/notion.md +16 -0
  136. package/documentation/operations.md +283 -5
  137. package/documentation/operator-ui.md +120 -5
  138. package/documentation/provider-audit.md +160 -0
  139. package/documentation/provider-conventions.md +631 -0
  140. package/documentation/request-admission.md +167 -5
  141. package/documentation/rich-output-design.md +1 -2
  142. package/documentation/stripe.md +15 -0
  143. package/ethos.md +13 -8
  144. package/examples/worker/README.md +96 -12
  145. package/examples/worker/src/index.ts +46 -1
  146. package/examples/worker/wrangler.jsonc +14 -1
  147. package/package.json +4 -5
  148. package/templates/node/.dockerignore +10 -0
  149. package/templates/node/.env.example +40 -3
  150. package/templates/node/AGENTS.md +11 -1
  151. package/templates/node/Dockerfile +49 -0
  152. package/templates/node/README.md +117 -2
  153. package/templates/node/docker-compose.yml +39 -0
  154. package/templates/node/package.json +1 -1
  155. package/templates/node/src/file-activity.ts +182 -0
  156. package/templates/node/src/index.ts +80 -4
  157. package/assets/connecta-clay-hero.png +0 -0
  158. package/dist/access-tokens.d.ts.map +0 -1
  159. package/dist/access-tokens.js.map +0 -1
  160. package/dist/activity.d.ts.map +0 -1
  161. package/dist/activity.js.map +0 -1
  162. package/dist/apps-shell.d.ts.map +0 -1
  163. package/dist/apps-shell.js.map +0 -1
  164. package/dist/auth/bearer.d.ts.map +0 -1
  165. package/dist/auth/bearer.js.map +0 -1
  166. package/dist/auth/clerk.d.ts.map +0 -1
  167. package/dist/auth/clerk.js.map +0 -1
  168. package/dist/auth/downstream-oauth.d.ts.map +0 -1
  169. package/dist/auth/downstream-oauth.js.map +0 -1
  170. package/dist/call-admission.d.ts.map +0 -1
  171. package/dist/call-admission.js.map +0 -1
  172. package/dist/catalog-fingerprint.d.ts.map +0 -1
  173. package/dist/catalog-fingerprint.js.map +0 -1
  174. package/dist/catalog-limits.d.ts.map +0 -1
  175. package/dist/catalog-limits.js.map +0 -1
  176. package/dist/catalog-service.d.ts.map +0 -1
  177. package/dist/catalog-service.js.map +0 -1
  178. package/dist/catalog.d.ts.map +0 -1
  179. package/dist/catalog.js.map +0 -1
  180. package/dist/concurrency.d.ts.map +0 -1
  181. package/dist/concurrency.js.map +0 -1
  182. package/dist/connector-scope.d.ts.map +0 -1
  183. package/dist/connector-scope.js.map +0 -1
  184. package/dist/connectors/api.d.ts.map +0 -1
  185. package/dist/connectors/api.js.map +0 -1
  186. package/dist/connectors/remote-mcp.d.ts.map +0 -1
  187. package/dist/connectors/remote-mcp.js.map +0 -1
  188. package/dist/credentials.d.ts.map +0 -1
  189. package/dist/credentials.js.map +0 -1
  190. package/dist/errors.d.ts.map +0 -1
  191. package/dist/errors.js.map +0 -1
  192. package/dist/execute.d.ts.map +0 -1
  193. package/dist/execute.js.map +0 -1
  194. package/dist/executor-admission.d.ts.map +0 -1
  195. package/dist/executor-admission.js.map +0 -1
  196. package/dist/executor-result.d.ts.map +0 -1
  197. package/dist/executor-result.js.map +0 -1
  198. package/dist/executors/quickjs-child.d.ts.map +0 -1
  199. package/dist/executors/quickjs-child.js.map +0 -1
  200. package/dist/executors/quickjs-protocol.d.ts.map +0 -1
  201. package/dist/executors/quickjs-protocol.js.map +0 -1
  202. package/dist/executors/quickjs-runtime.d.ts.map +0 -1
  203. package/dist/executors/quickjs-runtime.js.map +0 -1
  204. package/dist/executors/quickjs.d.ts.map +0 -1
  205. package/dist/executors/quickjs.js.map +0 -1
  206. package/dist/favicon.d.ts.map +0 -1
  207. package/dist/favicon.js.map +0 -1
  208. package/dist/index.d.ts.map +0 -1
  209. package/dist/index.js.map +0 -1
  210. package/dist/invocation.d.ts.map +0 -1
  211. package/dist/invocation.js.map +0 -1
  212. package/dist/json-schema.d.ts.map +0 -1
  213. package/dist/json-schema.js.map +0 -1
  214. package/dist/mcp-result.d.ts.map +0 -1
  215. package/dist/mcp-result.js.map +0 -1
  216. package/dist/meta-tools.d.ts.map +0 -1
  217. package/dist/meta-tools.js.map +0 -1
  218. package/dist/node.d.ts.map +0 -1
  219. package/dist/node.js.map +0 -1
  220. package/dist/operator-ui/generated.d.ts.map +0 -1
  221. package/dist/operator-ui/generated.js.map +0 -1
  222. package/dist/operator-ui/model.d.ts.map +0 -1
  223. package/dist/operator-ui/model.js.map +0 -1
  224. package/dist/providers/cloudflare.d.ts.map +0 -1
  225. package/dist/providers/cloudflare.js.map +0 -1
  226. package/dist/providers/linear.d.ts.map +0 -1
  227. package/dist/providers/linear.js.map +0 -1
  228. package/dist/providers/mixpanel.d.ts.map +0 -1
  229. package/dist/providers/mixpanel.js.map +0 -1
  230. package/dist/providers/notion.d.ts.map +0 -1
  231. package/dist/providers/notion.js.map +0 -1
  232. package/dist/providers/stripe.d.ts.map +0 -1
  233. package/dist/providers/stripe.js.map +0 -1
  234. package/dist/registry.d.ts.map +0 -1
  235. package/dist/registry.js.map +0 -1
  236. package/dist/retired-toolkits.d.ts.map +0 -1
  237. package/dist/retired-toolkits.js.map +0 -1
  238. package/dist/routes/access-tokens.d.ts.map +0 -1
  239. package/dist/routes/access-tokens.js.map +0 -1
  240. package/dist/routes/activity.d.ts.map +0 -1
  241. package/dist/routes/activity.js.map +0 -1
  242. package/dist/routes/credentials.d.ts.map +0 -1
  243. package/dist/routes/credentials.js.map +0 -1
  244. package/dist/routes/mcp.d.ts.map +0 -1
  245. package/dist/routes/mcp.js.map +0 -1
  246. package/dist/routes/oauth.d.ts.map +0 -1
  247. package/dist/routes/oauth.js.map +0 -1
  248. package/dist/routes/shared.d.ts.map +0 -1
  249. package/dist/routes/shared.js.map +0 -1
  250. package/dist/routes/ui.d.ts.map +0 -1
  251. package/dist/routes/ui.js.map +0 -1
  252. package/dist/server.d.ts.map +0 -1
  253. package/dist/server.js.map +0 -1
  254. package/dist/skills.d.ts.map +0 -1
  255. package/dist/skills.js.map +0 -1
  256. package/dist/storage/file.d.ts.map +0 -1
  257. package/dist/storage/file.js.map +0 -1
  258. package/dist/storage/memory.d.ts.map +0 -1
  259. package/dist/storage/memory.js.map +0 -1
  260. package/dist/timeout.d.ts.map +0 -1
  261. package/dist/timeout.js.map +0 -1
  262. package/dist/tool-safety.d.ts.map +0 -1
  263. package/dist/tool-safety.js.map +0 -1
  264. package/dist/types.d.ts.map +0 -1
  265. package/dist/types.js.map +0 -1
  266. package/dist/ui.d.ts.map +0 -1
  267. package/dist/ui.js.map +0 -1
  268. package/dist/validate.d.ts.map +0 -1
  269. package/dist/validate.js.map +0 -1
  270. package/dist/version.d.ts.map +0 -1
  271. package/dist/version.js.map +0 -1
  272. package/examples/node/README.md +0 -52
  273. package/examples/node/src/index.ts +0 -71
  274. package/src/access-tokens.ts +0 -289
  275. package/src/activity.ts +0 -234
  276. package/src/apps-shell.ts +0 -370
  277. package/src/auth/bearer.ts +0 -61
  278. package/src/auth/clerk.ts +0 -553
  279. package/src/auth/downstream-oauth.ts +0 -575
  280. package/src/call-admission.ts +0 -521
  281. package/src/catalog-fingerprint.ts +0 -52
  282. package/src/catalog-limits.ts +0 -11
  283. package/src/catalog-service.ts +0 -1153
  284. package/src/catalog.ts +0 -790
  285. package/src/concurrency.ts +0 -39
  286. package/src/connector-scope.ts +0 -66
  287. package/src/connectors/api.ts +0 -159
  288. package/src/connectors/remote-mcp.ts +0 -1090
  289. package/src/credentials.ts +0 -428
  290. package/src/errors.ts +0 -364
  291. package/src/execute.ts +0 -1406
  292. package/src/executor-admission.ts +0 -328
  293. package/src/executor-result.ts +0 -107
  294. package/src/executors/quickjs-child.ts +0 -172
  295. package/src/executors/quickjs-protocol.ts +0 -99
  296. package/src/executors/quickjs-runtime.ts +0 -470
  297. package/src/executors/quickjs.ts +0 -776
  298. package/src/favicon.ts +0 -53
  299. package/src/index.ts +0 -666
  300. package/src/invocation.ts +0 -655
  301. package/src/json-schema.ts +0 -11
  302. package/src/mcp-result.ts +0 -43
  303. package/src/meta-tools.ts +0 -1607
  304. package/src/node.ts +0 -206
  305. package/src/operator-ui/browser.css +0 -423
  306. package/src/operator-ui/browser.ts +0 -1281
  307. package/src/operator-ui/generated.ts +0 -4
  308. package/src/operator-ui/model.ts +0 -111
  309. package/src/providers/cloudflare.ts +0 -3999
  310. package/src/providers/linear.ts +0 -302
  311. package/src/providers/mixpanel.ts +0 -229
  312. package/src/providers/notion.ts +0 -1879
  313. package/src/providers/stripe.ts +0 -307
  314. package/src/registry.ts +0 -1085
  315. package/src/retired-toolkits.ts +0 -14
  316. package/src/routes/access-tokens.ts +0 -115
  317. package/src/routes/activity.ts +0 -215
  318. package/src/routes/credentials.ts +0 -254
  319. package/src/routes/mcp.ts +0 -429
  320. package/src/routes/oauth.ts +0 -322
  321. package/src/routes/shared.ts +0 -247
  322. package/src/routes/ui.ts +0 -152
  323. package/src/server.ts +0 -195
  324. package/src/skills.ts +0 -271
  325. package/src/storage/file.ts +0 -123
  326. package/src/storage/memory.ts +0 -44
  327. package/src/timeout.ts +0 -84
  328. package/src/tool-safety.ts +0 -15
  329. package/src/types.ts +0 -523
  330. package/src/ui.ts +0 -744
  331. package/src/validate.ts +0 -387
  332. package/src/version.ts +0 -7
package/src/routes/mcp.ts DELETED
@@ -1,429 +0,0 @@
1
- import {
2
- createMcpHandler,
3
- isLegacyRequest,
4
- McpServer,
5
- WebStandardStreamableHTTPServerTransport,
6
- } from "@modelcontextprotocol/server";
7
- import type { ActivityActor, ActivityRequestContext } from "../activity.js";
8
- import {
9
- MCP_APPS_EXTENSION,
10
- PROGRAM_UI_MIME_TYPE,
11
- PROGRAM_UI_RESOURCE_URI,
12
- PROGRAM_UI_SHELL_HTML,
13
- } from "../apps-shell.js";
14
- import { registerExecuteTool } from "../execute.js";
15
- import {
16
- ExecutorAdmissionError,
17
- type AdmissionLease,
18
- } from "../executor-admission.js";
19
- import { registerMetaTools } from "../meta-tools.js";
20
- import type { RegistryView } from "../registry.js";
21
- import { instructionsFor } from "../skills.js";
22
- import type { Logger } from "../types.js";
23
- import {
24
- authorize,
25
- type RouteContext,
26
- type RuntimeExecutionContext,
27
- type ServerOptions,
28
- } from "./shared.js";
29
-
30
- export const MCP_CORS_HEADERS = {
31
- "Access-Control-Allow-Origin": "*",
32
- "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
33
- "Access-Control-Allow-Headers":
34
- "Content-Type, Authorization, mcp-protocol-version, mcp-session-id, mcp-method, mcp-name",
35
- };
36
-
37
- // Browser-based MCP clients call /mcp cross-origin. Without CORS on every
38
- // response — errors included — the browser hides the 401, the client cannot
39
- // read WWW-Authenticate, and OAuth discovery silently never starts.
40
- function withMcpCors(response: Response): Response {
41
- const headers = new Headers(response.headers);
42
- for (const [name, value] of Object.entries(MCP_CORS_HEADERS)) {
43
- headers.set(name, value);
44
- }
45
- headers.set(
46
- "Access-Control-Expose-Headers",
47
- "WWW-Authenticate, Retry-After, mcp-session-id, mcp-protocol-version",
48
- );
49
- return new Response(response.body, {
50
- status: response.status,
51
- statusText: response.statusText,
52
- headers,
53
- });
54
- }
55
-
56
- function requestAdmissionFailure(error: ExecutorAdmissionError): Response {
57
- const overloaded = error.code === "executor_overloaded";
58
- const data = {
59
- code: overloaded ? "server_overloaded" : "server_shutting_down",
60
- retryable: overloaded,
61
- ...(overloaded && error.retryAfterMs !== undefined
62
- ? { retryAfterMs: error.retryAfterMs }
63
- : {}),
64
- };
65
- const headers = new Headers({
66
- "Content-Type": "application/json",
67
- "Cache-Control": "no-store",
68
- });
69
- if (overloaded && error.retryAfterMs !== undefined) {
70
- headers.set(
71
- "Retry-After",
72
- String(Math.max(1, Math.ceil(error.retryAfterMs / 1_000))),
73
- );
74
- }
75
- return new Response(
76
- JSON.stringify({
77
- jsonrpc: "2.0",
78
- id: null,
79
- error: {
80
- code: overloaded ? -32001 : -32002,
81
- message: overloaded
82
- ? "Server capacity is exhausted. Retry later."
83
- : "Server is shutting down.",
84
- data,
85
- },
86
- }),
87
- { status: 503, headers },
88
- );
89
- }
90
-
91
- /**
92
- * A request owns its permit through the response body, not merely until the
93
- * handler returns. This is what makes slow clients and response-stream failure
94
- * part of the same bounded lifecycle as success, error, and cancellation.
95
- */
96
- function releaseAdmissionWithResponse(
97
- response: Response,
98
- lease: AdmissionLease,
99
- signal: AbortSignal,
100
- ): Response {
101
- let released = false;
102
- let onAbort = () => {};
103
- const release = () => {
104
- if (released) return;
105
- released = true;
106
- signal.removeEventListener("abort", onAbort);
107
- lease.release();
108
- };
109
- if (!response.body) {
110
- release();
111
- return response;
112
- }
113
- const reader = response.body.getReader();
114
- onAbort = () => {
115
- // `cancel()` belongs to an operator/auth/SDK-provided stream and may
116
- // reject. Consume both outcomes: `.finally(release)` would release the
117
- // permit but preserve the rejection as an unhandled promise.
118
- void reader.cancel(signal.reason).then(release, release);
119
- };
120
- signal.addEventListener("abort", onAbort, { once: true });
121
- if (signal.aborted) onAbort();
122
- const body = new ReadableStream<Uint8Array>({
123
- async pull(controller) {
124
- try {
125
- const next = await reader.read();
126
- if (next.done) {
127
- release();
128
- controller.close();
129
- } else {
130
- controller.enqueue(next.value);
131
- }
132
- } catch (error) {
133
- release();
134
- controller.error(error);
135
- }
136
- },
137
- async cancel(reason) {
138
- try {
139
- await reader.cancel(reason);
140
- } finally {
141
- release();
142
- }
143
- },
144
- });
145
- return new Response(body, {
146
- status: response.status,
147
- statusText: response.statusText,
148
- headers: response.headers,
149
- });
150
- }
151
-
152
- /**
153
- * 404 for any `?toolkit=` value, kept after the feature's retirement (#178).
154
- *
155
- * Toolkits are gone, but the URLs that named them are not: clients were handed
156
- * MCP endpoint URLs with `?toolkit=` baked in, and nothing about upgrading the
157
- * server rotates them. Silently serving those clients the full registry would
158
- * turn a former scoping boundary into fail-open — so a request still sending
159
- * the param gets the same explicit 404 an unknown toolkit got before, plus an
160
- * operator-side log line, which (as ever — issue #47) is the channel that
161
- * actually reaches a human.
162
- */
163
- function toolkitRetired(logger: Logger): Response {
164
- logger.warn(
165
- "[connecta] rejected an /mcp connection carrying ?toolkit= with 404: " +
166
- "toolkits were retired in issue #178 (see ethos.md) — one deployment " +
167
- "serves one audience. The client sees a transport-level failure and " +
168
- "never the reason, so remove the ?toolkit= value from its MCP endpoint " +
169
- "URL, or point it at the deployment for its audience.",
170
- );
171
- return new Response(
172
- JSON.stringify({
173
- jsonrpc: "2.0",
174
- id: null,
175
- error: {
176
- code: -32600,
177
- message:
178
- "This deployment does not accept ?toolkit=. Toolkits were retired " +
179
- "in issue #178 — remove the ?toolkit= value from the MCP endpoint " +
180
- "URL, or ask the operator for the deployment serving this audience.",
181
- },
182
- }),
183
- {
184
- status: 404,
185
- headers: {
186
- "Content-Type": "application/json",
187
- "Cache-Control": "no-store",
188
- },
189
- },
190
- );
191
- }
192
-
193
- /**
194
- * U5: one static template, served by a handler that answers exactly one URI
195
- * and fails on every other. Registering it is also what declares the
196
- * `resources` capability — which is why `resources/list` has to answer, and
197
- * why it answers with nothing. That is the Apps spec's permitted omission of
198
- * UI-only resources from listing, taken exactly: the capability stays honest
199
- * because the method answers, and nothing downstream is ever listed or
200
- * aggregated. Widening this handler to proxy downstream templates is a
201
- * decision (see the design record), not a diff.
202
- */
203
- function registerProgramUiResource(server: McpServer): void {
204
- server.registerResource(
205
- "connecta-program-ui",
206
- PROGRAM_UI_RESOURCE_URI,
207
- {
208
- title: "connecta program view",
209
- description:
210
- "The MCP Apps shell that renders HTML an execute_code program handed connecta.ui.",
211
- mimeType: PROGRAM_UI_MIME_TYPE,
212
- },
213
- (uri) => ({
214
- contents: [
215
- {
216
- uri: uri.href,
217
- mimeType: PROGRAM_UI_MIME_TYPE,
218
- text: PROGRAM_UI_SHELL_HTML,
219
- },
220
- ],
221
- }),
222
- );
223
- // The SDK's generated listing would advertise the template it just
224
- // registered. Replace it rather than accept that: the URI reaches the host
225
- // through tool metadata, so the listing has nothing to carry.
226
- server.server.setRequestHandler("resources/list", () => ({ resources: [] }));
227
- }
228
-
229
- async function serveMcp(
230
- request: Request,
231
- opts: ServerOptions,
232
- baseUrl: string,
233
- actor: ActivityActor,
234
- registry: RegistryView,
235
- runtimeContext?: RuntimeExecutionContext,
236
- ): Promise<Response> {
237
- const createServer = (): McpServer => {
238
- const server = new McpServer(opts.serverInfo, {
239
- instructions: instructionsFor(),
240
- // U11: the Apps extension must be explicitly negotiated, and a
241
- // conforming client acts on an extension only when both sides declare
242
- // it — without this line no host reads execute_code's _meta.ui, no host
243
- // fetches the shell, and the whole design is inert. This is the one
244
- // extension connecta advertises; the versioned extensions framework
245
- // stays declined as a general surface (documentation/mcp-2026-07-28.md).
246
- capabilities: {
247
- extensions: {
248
- [MCP_APPS_EXTENSION]: { mimeTypes: [PROGRAM_UI_MIME_TYPE] },
249
- },
250
- // Registering the shell below declares `resources` on its own, but it
251
- // would default `listChanged` to true. Connecta serves one build-time
252
- // template and never sends a list_changed notification, so say so:
253
- // a client that subscribes on the strength of that flag would wait
254
- // forever for an event this server has no way to produce.
255
- resources: { listChanged: false },
256
- },
257
- cacheHints: {
258
- "tools/list": {
259
- ttlMs: 3_600_000,
260
- cacheScope: "private",
261
- },
262
- },
263
- });
264
- registerProgramUiResource(server);
265
- const activity: ActivityRequestContext | undefined = opts.activity
266
- ? {
267
- sink: opts.activity,
268
- actor,
269
- requestId: crypto.randomUUID(),
270
- serverInfo: opts.serverInfo,
271
- ...(opts.activityDeploymentId
272
- ? { deploymentId: opts.activityDeploymentId }
273
- : {}),
274
- ...(runtimeContext?.waitUntil
275
- ? { defer: runtimeContext.waitUntil.bind(runtimeContext) }
276
- : {}),
277
- logger: opts.logger,
278
- }
279
- : undefined;
280
- registerMetaTools(server, registry, {
281
- baseUrl,
282
- ...(activity ? { activity } : {}),
283
- ...(opts.defaultToolTimeoutMs !== undefined
284
- ? { defaultToolTimeoutMs: opts.defaultToolTimeoutMs }
285
- : {}),
286
- ...(opts.probeTimeoutMs !== undefined
287
- ? { probeTimeoutMs: opts.probeTimeoutMs }
288
- : {}),
289
- ...(opts.discoveryConcurrency !== undefined
290
- ? { discoveryConcurrency: opts.discoveryConcurrency }
291
- : {}),
292
- requestSignal: request.signal,
293
- });
294
- registerExecuteTool(server, registry, {
295
- baseUrl,
296
- executor: opts.executor,
297
- logger: opts.logger,
298
- ...(activity ? { activity } : {}),
299
- requestSignal: request.signal,
300
- ...(opts.discoveryConcurrency !== undefined
301
- ? { discoveryConcurrency: opts.discoveryConcurrency }
302
- : {}),
303
- ...(opts.probeTimeoutMs !== undefined
304
- ? { probeTimeoutMs: opts.probeTimeoutMs }
305
- : {}),
306
- ...(opts.maxEmittedBytes !== undefined
307
- ? { maxEmittedBytes: opts.maxEmittedBytes }
308
- : {}),
309
- ...(opts.maxEmittedBlocks !== undefined
310
- ? { maxEmittedBlocks: opts.maxEmittedBlocks }
311
- : {}),
312
- });
313
- return server;
314
- };
315
-
316
- // The v2 entry's built-in legacy fallback streams 2025 results as SSE.
317
- // Connecta's established wire contract is JSON, so retain the documented
318
- // user-land legacy branch with the same transport setting while the modern
319
- // branch uses the fetch-native handler.
320
- if (!(await isLegacyRequest(request))) {
321
- return createMcpHandler(createServer, {
322
- legacy: "reject",
323
- onerror: (error) => opts.logger.error("[connecta] MCP handler error", error),
324
- }).fetch(request);
325
- }
326
-
327
- // Fresh server + transport per legacy request, stateless and JSON-shaped.
328
- const server = createServer();
329
- const transport = new WebStandardStreamableHTTPServerTransport({
330
- enableJsonResponse: true,
331
- });
332
- await server.connect(transport);
333
- return transport.handleRequest(request);
334
- }
335
-
336
- export function createMcpRoute(
337
- opts: ServerOptions,
338
- ): (context: RouteContext) => Promise<Response | null> {
339
- let lastAdmissionWarningAt = 0;
340
- let suppressedAdmissionWarnings = 0;
341
- const warnAdmissionRejected = (error: ExecutorAdmissionError): void => {
342
- const now = Date.now();
343
- if (now - lastAdmissionWarningAt < 1_000) {
344
- suppressedAdmissionWarnings++;
345
- return;
346
- }
347
- opts.logger.warn("[connecta] MCP request admission rejected", {
348
- retryAfterMs: error.retryAfterMs,
349
- active: opts.requestAdmission.activeCount,
350
- queued: opts.requestAdmission.queuedCount,
351
- suppressedSinceLastWarning: suppressedAdmissionWarnings,
352
- });
353
- lastAdmissionWarningAt = now;
354
- suppressedAdmissionWarnings = 0;
355
- };
356
-
357
- return async function routeMcp(
358
- context: RouteContext,
359
- ): Promise<Response | null> {
360
- const {
361
- path,
362
- request,
363
- baseUrl,
364
- runtimeContext,
365
- } = context;
366
- if (path !== "/mcp") return null;
367
- let admission: AdmissionLease;
368
- try {
369
- admission = await opts.requestAdmission.acquire({
370
- signal: request.signal,
371
- });
372
- if (admission.waitMs > 0) {
373
- opts.logger.debug("[connecta] MCP request admitted after queue wait", {
374
- waitMs: admission.waitMs,
375
- active: opts.requestAdmission.activeCount,
376
- queued: opts.requestAdmission.queuedCount,
377
- });
378
- }
379
- } catch (error) {
380
- if (
381
- error instanceof ExecutorAdmissionError &&
382
- error.code === "executor_cancelled"
383
- ) {
384
- throw request.signal.reason ?? error;
385
- }
386
- if (error instanceof ExecutorAdmissionError) {
387
- if (error.code === "executor_overloaded") {
388
- warnAdmissionRejected(error);
389
- }
390
- return withMcpCors(requestAdmissionFailure(error));
391
- }
392
- throw error;
393
- }
394
- try {
395
- const authz = await authorize(request, baseUrl, opts.auth);
396
- if (!authz.ok) {
397
- return releaseAdmissionWithResponse(
398
- withMcpCors(authz.response),
399
- admission,
400
- request.signal,
401
- );
402
- }
403
- if (new URL(request.url).searchParams.has("toolkit")) {
404
- return releaseAdmissionWithResponse(
405
- withMcpCors(toolkitRetired(opts.logger)),
406
- admission,
407
- request.signal,
408
- );
409
- }
410
- return releaseAdmissionWithResponse(
411
- withMcpCors(
412
- await serveMcp(
413
- request,
414
- opts,
415
- baseUrl,
416
- authz.actor,
417
- opts.registry,
418
- runtimeContext,
419
- ),
420
- ),
421
- admission,
422
- request.signal,
423
- );
424
- } catch (error) {
425
- admission.release();
426
- throw error;
427
- }
428
- };
429
- }