@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/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
- }