@zackbart/connecta 0.28.1 → 0.30.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/CHANGELOG.md +855 -5
  2. package/README.md +125 -116
  3. package/bin/connecta.mjs +121 -139
  4. package/bin/meta-tool-names.d.mts +2 -0
  5. package/bin/meta-tool-names.mjs +9 -0
  6. package/bin/version.mjs +3 -0
  7. package/dist/access-tokens.js +53 -63
  8. package/dist/activity-request.js +8 -0
  9. package/dist/activity.d.ts +76 -42
  10. package/dist/activity.js +110 -42
  11. package/dist/argument-redaction.js +230 -0
  12. package/dist/auth/clerk-sdk/client.d.ts +65 -0
  13. package/dist/auth/clerk-sdk/client.js +5 -0
  14. package/dist/auth/clerk-sdk/generated.js +1845 -0
  15. package/dist/auth/clerk-transport.js +19 -0
  16. package/dist/auth/clerk.d.ts +17 -7
  17. package/dist/auth/clerk.js +189 -107
  18. package/dist/auth/cloudflare-access.js +25 -26
  19. package/dist/auth/downstream-client-metadata.d.ts +29 -0
  20. package/dist/auth/downstream-client-metadata.js +133 -0
  21. package/dist/auth/downstream-oauth.js +1584 -1580
  22. package/dist/auth/downstream-revocation.js +106 -0
  23. package/dist/auth/oauth-token-response.js +231 -0
  24. package/dist/auth/oauth-v2-migration.js +116 -0
  25. package/dist/auth/static-oauth.js +93 -71
  26. package/dist/auth-elicitation.js +288 -0
  27. package/dist/authorization-handoff.js +111 -0
  28. package/dist/branding.d.ts +8 -16
  29. package/dist/branding.js +26 -59
  30. package/dist/byte-read-response.js +39 -0
  31. package/dist/call-admission.js +12 -16
  32. package/dist/catalog-cache.js +739 -0
  33. package/dist/catalog-drift.d.ts +50 -15
  34. package/dist/catalog-drift.js +365 -128
  35. package/dist/catalog-fingerprint.js +2 -4
  36. package/dist/catalog-freshness.js +13 -0
  37. package/dist/catalog-limits.js +2 -2
  38. package/dist/catalog-service.js +409 -225
  39. package/dist/catalog.js +43 -84
  40. package/dist/concurrency.js +3 -4
  41. package/dist/config-defaults.d.ts +39 -0
  42. package/dist/config-defaults.js +46 -0
  43. package/dist/config-schema.d.ts +181 -0
  44. package/dist/config-schema.js +435 -0
  45. package/dist/config-value-sources.js +102 -0
  46. package/dist/config.d.ts +633 -0
  47. package/dist/config.js +508 -0
  48. package/dist/connector-access.d.ts +1 -0
  49. package/dist/connector-access.js +13 -15
  50. package/dist/connector-caller.d.ts +8 -0
  51. package/dist/connector-caller.js +15 -6
  52. package/dist/connector-scope.js +1 -1
  53. package/dist/connectors/api-connector.d.ts +59 -7
  54. package/dist/connectors/api-connector.js +106 -19
  55. package/dist/connectors/api.js +4 -1
  56. package/dist/connectors/guarded-fetch.d.ts +144 -0
  57. package/dist/connectors/guarded-fetch.js +87 -38
  58. package/dist/connectors/negotiation-cache.js +46 -0
  59. package/dist/connectors/option-shapes.js +27 -0
  60. package/dist/connectors/remote-mcp.d.ts +30 -4
  61. package/dist/connectors/remote-mcp.js +1774 -320
  62. package/dist/connectors/resource-uri.js +335 -0
  63. package/dist/credential-contract.d.ts +10 -0
  64. package/dist/credential-rules.js +1 -3
  65. package/dist/credential-url.js +108 -0
  66. package/dist/credentials.d.ts +6 -1
  67. package/dist/credentials.js +27 -18
  68. package/dist/d1.d.ts +130 -0
  69. package/dist/d1.js +290 -0
  70. package/dist/describe-config.d.ts +165 -0
  71. package/dist/describe-config.js +315 -0
  72. package/dist/described.js +106 -0
  73. package/dist/downstream-input-context.js +128 -0
  74. package/dist/downstream-input.js +325 -0
  75. package/dist/errors.d.ts +132 -31
  76. package/dist/errors.js +175 -87
  77. package/dist/execute.js +798 -318
  78. package/dist/executor-admission.d.ts +1 -0
  79. package/dist/executor-admission.js +11 -15
  80. package/dist/executor-contract.d.ts +20 -0
  81. package/dist/executor-contract.js +48 -0
  82. package/dist/executor-result.js +30 -13
  83. package/dist/executors/quickjs-child.js +15 -9
  84. package/dist/executors/quickjs-protocol.js +5 -6
  85. package/dist/executors/quickjs-runtime.js +152 -59
  86. package/dist/executors/quickjs.js +49 -39
  87. package/dist/favicon.js +1 -2
  88. package/dist/fix-prompt.js +1 -3
  89. package/dist/guest-runtime.js +238 -0
  90. package/dist/guest-types.d.ts +218 -0
  91. package/dist/guest-types.js +1 -0
  92. package/dist/html-security.js +24 -0
  93. package/dist/identity.js +1 -3
  94. package/dist/inbound-credential.js +52 -0
  95. package/dist/index.d.ts +24 -256
  96. package/dist/index.js +65 -478
  97. package/dist/invocation-auth.js +69 -0
  98. package/dist/invocation.js +262 -79
  99. package/dist/json-schema.d.ts +1 -1
  100. package/dist/mcp-client-context.js +35 -0
  101. package/dist/mcp-result.js +11 -7
  102. package/dist/meta-output.js +61 -0
  103. package/dist/meta-tool-names.d.ts +2 -0
  104. package/dist/meta-tool-names.js +3 -0
  105. package/dist/meta-tools.js +419 -272
  106. package/dist/module-contracts.d.ts +8 -23
  107. package/dist/node.d.ts +0 -2
  108. package/dist/node.js +2 -2
  109. package/dist/oauth-callback-outcome.js +1 -3
  110. package/dist/oauth-handoff.js +194 -0
  111. package/dist/oauth-sealing.js +20 -0
  112. package/dist/operator-record.d.ts +137 -0
  113. package/dist/operator-record.js +420 -0
  114. package/dist/operator-ui/assets.js +31 -0
  115. package/dist/operator-ui/contract.d.ts +72 -0
  116. package/dist/operator-ui/contract.js +1 -0
  117. package/dist/operator-ui/fix-prompts.d.ts +18 -0
  118. package/dist/operator-ui/generated.js +5 -4
  119. package/dist/operator-ui/model.d.ts +12 -87
  120. package/dist/operator-ui/model.js +2 -9
  121. package/dist/operator-ui/setup-commands.js +1 -3
  122. package/dist/operator-ui/view.d.ts +323 -0
  123. package/dist/operator-ui/view.js +62 -189
  124. package/dist/page-styles.js +2 -3
  125. package/dist/program-result.js +5 -0
  126. package/dist/program-source.js +14 -2
  127. package/dist/{exempt-writes.js → program-writes.js} +60 -19
  128. package/dist/provider.d.ts +142 -0
  129. package/dist/provider.js +187 -0
  130. package/dist/providers/_shared/google/result-size.js +57 -0
  131. package/dist/providers/_shared/google/service-account.d.ts +58 -0
  132. package/dist/providers/_shared/google/service-account.js +500 -0
  133. package/dist/providers/_shared/google/workspace.d.ts +276 -0
  134. package/dist/providers/_shared/google/workspace.js +518 -0
  135. package/dist/providers/_shared/request-token.js +83 -0
  136. package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
  137. package/dist/providers/basecamp/index.js +997 -0
  138. package/dist/providers/basecamp/skill.generated.js +9 -0
  139. package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
  140. package/dist/providers/{breeze.js → breeze/index.js} +184 -140
  141. package/dist/providers/breeze/skill.generated.js +9 -0
  142. package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
  143. package/dist/providers/{ccb.js → ccb/index.js} +271 -116
  144. package/dist/providers/ccb/skill.generated.js +9 -0
  145. package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
  146. package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
  147. package/dist/providers/cloudflare/skill.generated.js +10 -0
  148. package/dist/providers/docs/index.d.ts +54 -0
  149. package/dist/providers/docs/index.js +1237 -0
  150. package/dist/providers/docs/skill.generated.js +9 -0
  151. package/dist/providers/drive/index.d.ts +53 -0
  152. package/dist/providers/drive/index.js +1523 -0
  153. package/dist/providers/drive/skill.generated.js +9 -0
  154. package/dist/providers/forms/index.d.ts +54 -0
  155. package/dist/providers/forms/index.js +1217 -0
  156. package/dist/providers/forms/skill.generated.js +11 -0
  157. package/dist/providers/github/auth.d.ts +32 -0
  158. package/dist/providers/github/auth.js +361 -0
  159. package/dist/providers/github/hosted.js +218 -0
  160. package/dist/providers/github/index.d.ts +13 -0
  161. package/dist/providers/github/index.js +140 -0
  162. package/dist/providers/github/key.js +100 -0
  163. package/dist/providers/github/rest.js +321 -0
  164. package/dist/providers/github/scope.d.ts +29 -0
  165. package/dist/providers/github/scope.js +124 -0
  166. package/dist/providers/github/skill.generated.js +8 -0
  167. package/dist/providers/gmail/index.d.ts +51 -0
  168. package/dist/providers/gmail/index.js +1436 -0
  169. package/dist/providers/gmail/skill.generated.js +9 -0
  170. package/dist/providers/infisical/index.d.ts +7 -0
  171. package/dist/providers/infisical/index.js +713 -0
  172. package/dist/providers/infisical/skill.generated.js +8 -0
  173. package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
  174. package/dist/providers/linear/index.js +352 -0
  175. package/dist/providers/linear/skill.generated.js +8 -0
  176. package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
  177. package/dist/providers/mixpanel/index.js +416 -0
  178. package/dist/providers/mixpanel/skill.generated.js +9 -0
  179. package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
  180. package/dist/providers/{notion.js → notion/index.js} +226 -239
  181. package/dist/providers/notion/skill.generated.js +10 -0
  182. package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
  183. package/dist/providers/{overflow.js → overflow/index.js} +74 -111
  184. package/dist/providers/overflow/skill.generated.js +8 -0
  185. package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
  186. package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
  187. package/dist/providers/planning-center/skill.generated.js +10 -0
  188. package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
  189. package/dist/providers/revenuecat/index.js +557 -0
  190. package/dist/providers/revenuecat/skill.generated.js +11 -0
  191. package/dist/providers/sheets/index.d.ts +54 -0
  192. package/dist/providers/sheets/index.js +1237 -0
  193. package/dist/providers/sheets/skill.generated.js +10 -0
  194. package/dist/providers/slides/index.d.ts +53 -0
  195. package/dist/providers/slides/index.js +2645 -0
  196. package/dist/providers/slides/skill.generated.js +9 -0
  197. package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
  198. package/dist/providers/stripe/index.js +266 -0
  199. package/dist/providers/stripe/skill.generated.js +11 -0
  200. package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
  201. package/dist/providers/{tithely.js → tithely/index.js} +60 -91
  202. package/dist/providers/tithely/skill.generated.js +8 -0
  203. package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
  204. package/dist/providers/vercel/index.js +1038 -0
  205. package/dist/providers/vercel/mcp-ownership.js +212 -0
  206. package/dist/providers/vercel/skill.generated.js +11 -0
  207. package/dist/registry.d.ts +110 -171
  208. package/dist/registry.js +588 -927
  209. package/dist/request-state.js +21 -0
  210. package/dist/resource-read.js +103 -0
  211. package/dist/result-shapes.js +17 -49
  212. package/dist/retired-toolkits.js +1 -1
  213. package/dist/routes/access-tokens.js +3 -6
  214. package/dist/routes/activity-context.js +21 -0
  215. package/dist/routes/activity-disclosure.js +40 -0
  216. package/dist/routes/activity.js +59 -21
  217. package/dist/routes/actor-labels.js +3 -50
  218. package/dist/routes/connect.js +221 -0
  219. package/dist/routes/credentials.js +15 -27
  220. package/dist/routes/mcp.js +365 -130
  221. package/dist/routes/oauth-client-metadata.js +15 -0
  222. package/dist/routes/oauth-management.js +45 -107
  223. package/dist/routes/oauth.js +110 -57
  224. package/dist/routes/operator.js +3 -3
  225. package/dist/routes/shared.d.ts +25 -64
  226. package/dist/routes/shared.js +160 -80
  227. package/dist/routes/skills.js +30 -0
  228. package/dist/routes/ui-config.js +225 -0
  229. package/dist/routes/ui-data.js +40 -35
  230. package/dist/routes/ui.js +66 -56
  231. package/dist/runtime/admission.js +4 -2
  232. package/dist/runtime/call-admission.js +3 -1
  233. package/dist/runtime/connector-scope.js +1 -4
  234. package/dist/runtime/run.js +10 -39
  235. package/dist/runtime/services.js +12 -117
  236. package/dist/runtime/shared-read.js +1 -3
  237. package/dist/runtime/storage.js +8 -1
  238. package/dist/sent-secrets.d.ts +93 -0
  239. package/dist/sent-secrets.js +1013 -0
  240. package/dist/server.js +28 -71
  241. package/dist/skills.js +416 -174
  242. package/dist/sqlite.d.ts +48 -0
  243. package/dist/sqlite.js +181 -0
  244. package/dist/storage/keys.js +443 -0
  245. package/dist/storage/memory.js +16 -4
  246. package/dist/storage/sql.d.ts +66 -0
  247. package/dist/storage/sql.js +543 -0
  248. package/dist/timeout.js +4 -2
  249. package/dist/tool-name.js +17 -0
  250. package/dist/tool-safety.d.ts +9 -35
  251. package/dist/tool-safety.js +20 -40
  252. package/dist/types.d.ts +336 -56
  253. package/dist/types.js +0 -1
  254. package/dist/ui.d.ts +7 -19
  255. package/dist/ui.js +76 -90
  256. package/dist/url-safety.js +3 -6
  257. package/dist/usage-guide.js +146 -0
  258. package/dist/validate.d.ts +7 -0
  259. package/dist/validate.js +309 -47
  260. package/dist/version.d.ts +1 -7
  261. package/dist/version.js +3 -7
  262. package/dist/worker.d.ts +11 -0
  263. package/dist/worker.js +435 -0
  264. package/documentation/README.md +63 -0
  265. package/documentation/architecture.md +696 -231
  266. package/documentation/auth.md +1069 -546
  267. package/documentation/code-mode.md +662 -356
  268. package/documentation/deploying.md +146 -0
  269. package/documentation/integrating.md +159 -0
  270. package/documentation/meta-tools.md +570 -216
  271. package/documentation/operating.md +135 -0
  272. package/documentation/operator-tests.md +97 -0
  273. package/documentation/operator-ui.md +132 -0
  274. package/documentation/provider-migration-0.29.md +65 -0
  275. package/examples/worker/AGENTS.md +34 -12
  276. package/examples/worker/README.md +293 -255
  277. package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
  278. package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
  279. package/examples/worker/src/connecta.config.ts +151 -0
  280. package/examples/worker/src/index.ts +14 -197
  281. package/examples/worker/wrangler.jsonc +15 -35
  282. package/package.json +126 -54
  283. package/templates/node/.dockerignore +1 -2
  284. package/templates/node/.env.example +20 -21
  285. package/templates/node/AGENTS.md +21 -17
  286. package/templates/node/Dockerfile +3 -5
  287. package/templates/node/README.md +140 -58
  288. package/templates/node/docker-compose.yml +11 -12
  289. package/templates/node/package.json +4 -2
  290. package/templates/node/src/connecta.config.ts +119 -0
  291. package/templates/node/src/index.ts +9 -154
  292. package/templates/node/src/provision-token.ts +17 -0
  293. package/AGENTS.md +0 -170
  294. package/dist/artifacts/connector.d.ts +0 -58
  295. package/dist/artifacts/connector.js +0 -730
  296. package/dist/artifacts/document.js +0 -86
  297. package/dist/artifacts/guide.js +0 -140
  298. package/dist/artifacts/html-scan.js +0 -329
  299. package/dist/artifacts/json.js +0 -78
  300. package/dist/artifacts/kv-store.d.ts +0 -20
  301. package/dist/artifacts/kv-store.js +0 -140
  302. package/dist/artifacts/markdown.js +0 -478
  303. package/dist/artifacts/module.d.ts +0 -37
  304. package/dist/artifacts/module.js +0 -66
  305. package/dist/artifacts/operations.d.ts +0 -197
  306. package/dist/artifacts/operations.js +0 -1002
  307. package/dist/artifacts/refresh.d.ts +0 -55
  308. package/dist/artifacts/refresh.js +0 -236
  309. package/dist/artifacts/routes.js +0 -206
  310. package/dist/artifacts/types.d.ts +0 -214
  311. package/dist/artifacts/types.js +0 -22
  312. package/dist/artifacts/validate.d.ts +0 -62
  313. package/dist/artifacts/validate.js +0 -651
  314. package/dist/artifacts.d.ts +0 -6
  315. package/dist/artifacts.js +0 -9
  316. package/dist/auth/bearer.d.ts +0 -11
  317. package/dist/auth/bearer.js +0 -50
  318. package/dist/providers/basecamp.js +0 -445
  319. package/dist/providers/linear.js +0 -217
  320. package/dist/providers/mixpanel.js +0 -247
  321. package/dist/providers/revenuecat.js +0 -317
  322. package/dist/providers/stripe.js +0 -253
  323. package/dist/providers/vercel.js +0 -1253
  324. package/dist/storage/file.d.ts +0 -15
  325. package/dist/storage/file.js +0 -455
  326. package/ethos.md +0 -93
  327. package/examples/worker/src/cloudflare-kv.ts +0 -43
  328. package/examples/worker/src/d1-activity-row.ts +0 -155
  329. package/examples/worker/src/d1-activity.ts +0 -147
  330. package/examples/worker/src/d1-storage.ts +0 -138
  331. package/examples/worker/src/r2-artifact-blobs.ts +0 -29
  332. package/templates/node/src/file-activity.ts +0 -182
@@ -1,50 +0,0 @@
1
- import { assertNoRetiredToolkitOptions } from "../retired-toolkits.js";
2
- const encoder = new TextEncoder();
3
- /** Constant-time byte comparison. Differing lengths still iterate to reduce leak. */
4
- function timingSafeEqual(a, b) {
5
- if (a.length !== b.length) {
6
- let r = 1;
7
- const n = Math.max(a.length, b.length);
8
- for (let i = 0; i < n; i++)
9
- r |= (a[i] ?? 0) ^ (b[i] ?? 0);
10
- return false;
11
- }
12
- let r = 0;
13
- for (let i = 0; i < a.length; i++)
14
- r |= (a[i] ?? 0) ^ (b[i] ?? 0);
15
- return r === 0;
16
- }
17
- /**
18
- * Static bearer-token inbound auth. Constant-time compares the Bearer token
19
- * against `secret`. Checked before interactive providers in the server; a
20
- * mismatch falls through so another configured provider can admit the request.
21
- */
22
- export function bearerToken(secret, options = {}) {
23
- assertNoRetiredToolkitOptions("bearerToken", options);
24
- const secretBytes = encoder.encode(secret);
25
- return {
26
- kind: "bearer",
27
- authorize(request) {
28
- const header = request.headers.get("authorization") ?? "";
29
- const match = /^Bearer\s+(.+)$/i.exec(header);
30
- if (match && timingSafeEqual(encoder.encode(match[1]), secretBytes)) {
31
- return {
32
- ok: true,
33
- ...(options.subjectId ? { subjectId: options.subjectId } : {}),
34
- };
35
- }
36
- return {
37
- ok: false,
38
- response: new Response(JSON.stringify({ error: "unauthorized" }), {
39
- status: 401,
40
- headers: {
41
- "Content-Type": "application/json",
42
- // A configured secret has no OAuth metadata or issuer to
43
- // advertise; interactive adapters own resource discovery.
44
- "WWW-Authenticate": "Bearer",
45
- },
46
- }),
47
- };
48
- },
49
- };
50
- }
@@ -1,445 +0,0 @@
1
- import { remoteMcp } from "../connectors/remote-mcp.js";
2
- import { vettedCatalog, withVettedCatalog } from "../catalog-drift.js";
3
- import { defined } from "../connectors/api-connector.js";
4
- /**
5
- * Basecamp's one hosted MCP endpoint, streamable HTTP.
6
- *
7
- * Live but undocumented: as of 2026-10-05 37signals has announced no MCP
8
- * server, and basecamp.com/agents (where the endpoint's own 401 body points)
9
- * describes the CLI and SDKs without naming it. What the server does publish is
10
- * its OAuth discovery — protected-resource metadata naming
11
- * `https://app.basecamp.com` as the authorization server — and that is what
12
- * this provider is written against, beside a captured live `tools/list`.
13
- */
14
- export const BASECAMP_MCP_ENDPOINT = "https://mcp.basecamp.com/mcp";
15
- /**
16
- * Requested only when Basecamp stops advertising scopes of its own.
17
- *
18
- * The MCP client picks the scope from the `WWW-Authenticate` challenge first
19
- * (`mcp full` today), then the protected resource's `scopes_supported`, and
20
- * only then this value — appending `offline_access` itself whenever the
21
- * authorization server lists it. So for Basecamp as it serves today, this is a
22
- * fallback that matches what the challenge already asks for, not a lever.
23
- */
24
- const FALLBACK_SCOPE = "full mcp offline_access";
25
- /**
26
- * Reviewed reads, listed by name (P5), from the live `tools/list` captured
27
- * 2026-10-05 (227 tools, every one explicitly annotated by the server). Each
28
- * read below carries the server's own `readOnlyHint: true`; the
29
- * classification agrees rather than argues. A superset by design: tools gated
30
- * by account type or feature cost nothing when absent, while an unlisted new
31
- * one fails closed.
32
- */
33
- const READ_ONLY_TOOLS = new Set([
34
- // Orientation and identity
35
- "get_basecamp_guide",
36
- "get_me",
37
- "get_account",
38
- "get_my_profile",
39
- "get_my_preferences",
40
- "get_by_url",
41
- "search",
42
- "search_mentions",
43
- // People
44
- "get_person",
45
- "list_people",
46
- "list_pingable_people",
47
- "list_assignable_people",
48
- "list_project_people",
49
- "get_out_of_office",
50
- // Projects, docks, and templates
51
- "list_projects",
52
- "get_project",
53
- "get_project_tools",
54
- "get_dock_tool",
55
- "get_project_construction",
56
- "list_templates",
57
- "get_template",
58
- "list_folders",
59
- "get_folder",
60
- // My work and notifications. Reading the Hey! menu changes nothing; the
61
- // write that does is `mark_as_read`, below.
62
- "get_my_assignments",
63
- "get_my_completed_assignments",
64
- "get_my_due_assignments",
65
- "get_my_day",
66
- "get_catchup",
67
- "get_my_notifications",
68
- "get_bubble_ups",
69
- "get_my_note",
70
- "list_my_bookmarks",
71
- "list_my_drafts",
72
- "get_bookmark",
73
- "get_question_reminders",
74
- // Answers the same question as the reads above and renders it in a panel
75
- // for clients that draw one; connecta relays the data and draws nothing.
76
- "show_my_work",
77
- // Activity and feeds. Both feed cursors are held by the caller and passed
78
- // back, so polling advances nothing on Basecamp's side.
79
- "get_progress_report",
80
- "get_project_timeline",
81
- "get_person_progress",
82
- "list_feed_events",
83
- "get_event_inbox",
84
- "list_everything_feed",
85
- "list_everything_todos",
86
- "list_everything_cards",
87
- "summarize_recording",
88
- // Mints a short-lived, single-connection WebSocket ticket for the event
89
- // feed's live lane, and the server files it read-only. Reviewed rather than
90
- // copied: it writes no Basecamp record, and what the ticket unlocks is a
91
- // read of the same feed `list_feed_events` already serves on this path.
92
- // Filing it destructive would be the only lever that overrides the server
93
- // (an additive verdict cannot), and it would buy a standing annotation
94
- // conflict on every refresh for a tool connecta's one-request calls cannot
95
- // use anyway — so the guide tells agents not to call it instead.
96
- "create_stream_ticket",
97
- // To-dos
98
- "get_todoset",
99
- "list_todolists",
100
- "get_todolist",
101
- "list_todolist_groups",
102
- "get_todolist_group",
103
- "list_todos",
104
- "get_todo",
105
- "get_assigned_todos",
106
- "get_overdue_todos",
107
- "get_hill_chart",
108
- // Messages
109
- "get_message_board",
110
- "list_messages",
111
- "get_message",
112
- "list_message_types",
113
- "get_message_type",
114
- // Comments and boosts
115
- "list_comments",
116
- "get_comment",
117
- "list_recording_boosts",
118
- "list_event_boosts",
119
- "get_boost",
120
- // Card tables
121
- "get_card_table",
122
- "get_card_column",
123
- "list_cards",
124
- "get_card",
125
- "get_card_step",
126
- // Chat
127
- "get_campfire",
128
- "list_campfire_lines",
129
- "get_campfire_line",
130
- "list_campfire_uploads",
131
- // Schedules
132
- "get_schedule",
133
- "list_schedule_entries",
134
- "get_schedule_entry",
135
- "get_schedule_entry_occurrence",
136
- "get_upcoming_schedule",
137
- "get_calendar",
138
- "list_lineup_markers",
139
- // Docs and files
140
- "get_vault",
141
- "list_vaults",
142
- "list_documents",
143
- "get_document",
144
- "list_uploads",
145
- "get_upload",
146
- "list_upload_versions",
147
- "get_cloud_file",
148
- "get_google_document",
149
- // Check-ins
150
- "get_questionnaire",
151
- "list_questions",
152
- "get_question",
153
- "list_question_answerers",
154
- "list_answers",
155
- "get_answer",
156
- "get_answers_by_person",
157
- // Timesheets
158
- "get_timesheet_report",
159
- "get_project_timesheet",
160
- "get_recording_timesheet",
161
- "get_timesheet_entry",
162
- // Gauges
163
- "list_gauges",
164
- "list_gauge_needles",
165
- "get_gauge_needle",
166
- ]);
167
- /**
168
- * Reviewed writes with their destructive verdict. Most follow the server's
169
- * own explicit annotation, which is also what the verb says: `create_*` brings
170
- * a record into being, and `update_*`, `trash_*`, `delete_*`, `destroy_*`,
171
- * moves, repositions, and toggles act on one that exists. The four rows that
172
- * part from the server carry their reason.
173
- *
174
- * Trash and permanent deletion are both destructive, because both take a
175
- * record out of everyone's view; the difference — `trash_*` is restorable for
176
- * 25 days, `delete_*`/`destroy_*` and `remove_account_logo` are not — is the
177
- * guide's to say, not a third verdict's.
178
- */
179
- const WRITE_TOOLS = new Map([
180
- // Account and profile
181
- ["update_account_name", "destructive"],
182
- ["remove_account_logo", "destructive"],
183
- ["update_my_profile", "destructive"],
184
- ["update_my_preferences", "destructive"],
185
- // "The first update creates the note", but every later one replaces it.
186
- ["update_my_note", "destructive"],
187
- ["enable_out_of_office", "destructive"],
188
- ["disable_out_of_office", "destructive"],
189
- // Projects, docks, and templates
190
- ["create_project", "additive"],
191
- ["create_project_from_template", "additive"],
192
- ["update_project", "destructive"],
193
- // Grants and revokes access, and can create people — that is, invite an
194
- // outside address into the account (the server marks it open-world).
195
- ["update_project_access", "destructive"],
196
- ["trash_project", "destructive"],
197
- ["create_template", "additive"],
198
- ["update_template", "destructive"],
199
- ["delete_template", "destructive"],
200
- ["enable_dock_tool", "destructive"],
201
- ["disable_dock_tool", "destructive"],
202
- ["update_dock_tool", "destructive"],
203
- ["reposition_dock_tool", "destructive"],
204
- ["trash_dock_tool", "destructive"],
205
- // Destructive where the server says additive: "optionally filing projects
206
- // into it" moves projects that already sit on the person's home screen, so
207
- // the call can rearrange existing state as well as create a folder.
208
- ["create_folder", "destructive"],
209
- ["update_folder", "destructive"],
210
- ["delete_folder", "destructive"],
211
- // My work. Prioritizing adds an assignment to the person's own list and
212
- // removes nothing — RevenueCat's attach/detach argument — so it is additive
213
- // where the server says destructive; deprioritizing and reordering are the
214
- // halves that move existing state. The server's explicit `destructiveHint`
215
- // still stands at runtime: the classification fills silence and does not
216
- // soften an annotation, so this verdict is the release's record, not an
217
- // overrule.
218
- ["prioritize_assignment", "additive"],
219
- ["deprioritize_assignment", "destructive"],
220
- ["reorder_up_next", "destructive"],
221
- // Flips the read state of existing notifications, which is how a person
222
- // knows what they have not seen yet — and no tool flips it back.
223
- ["mark_as_read", "destructive"],
224
- ["create_bookmark", "additive"],
225
- ["delete_bookmark", "destructive"],
226
- // To-dos
227
- ["create_todolist", "additive"],
228
- ["update_todolist", "destructive"],
229
- ["reposition_todolist", "destructive"],
230
- ["trash_todolist", "destructive"],
231
- ["create_todolist_group", "additive"],
232
- ["update_todolist_group", "destructive"],
233
- ["reposition_todolist_group", "destructive"],
234
- ["trash_todolist_group", "destructive"],
235
- ["create_todo", "additive"],
236
- ["create_todoset_todo", "additive"],
237
- ["update_todo", "destructive"],
238
- ["complete_todo", "destructive"],
239
- ["uncomplete_todo", "destructive"],
240
- ["reposition_todo", "destructive"],
241
- ["trash_todo", "destructive"],
242
- ["update_hill_chart_settings", "destructive"],
243
- // Messages
244
- ["create_message", "additive"],
245
- ["update_message", "destructive"],
246
- ["pin_message", "destructive"],
247
- ["unpin_message", "destructive"],
248
- ["archive_message", "destructive"],
249
- ["unarchive_message", "destructive"],
250
- ["trash_message", "destructive"],
251
- ["create_message_type", "additive"],
252
- ["update_message_type", "destructive"],
253
- ["delete_message_type", "destructive"],
254
- // Comments and boosts
255
- ["create_comment", "additive"],
256
- ["update_comment", "destructive"],
257
- ["trash_comment", "destructive"],
258
- ["create_recording_boost", "additive"],
259
- ["create_event_boost", "additive"],
260
- ["delete_boost", "destructive"],
261
- // Card tables
262
- ["create_card_column", "additive"],
263
- ["update_card_column", "destructive"],
264
- ["move_card_column", "destructive"],
265
- ["set_card_column_color", "destructive"],
266
- ["enable_card_column_on_hold", "destructive"],
267
- ["disable_card_column_on_hold", "destructive"],
268
- // Watching a column adds the person to its subscribers and removes nothing:
269
- // additive for the same reason as `prioritize_assignment`, with the server's
270
- // explicit `destructiveHint` still the one a caller sees. Two endpoints, one
271
- // effect, so both names get the same verdict; the unwatching halves stay
272
- // destructive.
273
- ["watch_column", "additive"],
274
- ["subscribe_to_card_column", "additive"],
275
- ["unwatch_column", "destructive"],
276
- ["unsubscribe_from_card_column", "destructive"],
277
- ["create_wormhole", "additive"],
278
- ["update_wormhole", "destructive"],
279
- ["delete_wormhole", "destructive"],
280
- ["create_card", "additive"],
281
- ["update_card", "destructive"],
282
- ["move_card", "destructive"],
283
- ["trash_card", "destructive"],
284
- ["create_card_step", "additive"],
285
- ["update_card_step", "destructive"],
286
- ["complete_card_step", "destructive"],
287
- ["uncomplete_card_step", "destructive"],
288
- ["reposition_card_step", "destructive"],
289
- ["delete_card_step", "destructive"],
290
- // Chat
291
- ["create_campfire_line", "additive"],
292
- ["create_campfire_upload", "additive"],
293
- ["delete_campfire_line", "destructive"],
294
- // Schedules
295
- ["create_schedule_entry", "additive"],
296
- ["update_schedule_entry", "destructive"],
297
- ["trash_schedule_entry", "destructive"],
298
- ["update_schedule_settings", "destructive"],
299
- ["update_calendar", "destructive"],
300
- ["create_lineup_marker", "additive"],
301
- ["update_lineup_marker", "destructive"],
302
- ["delete_lineup_marker", "destructive"],
303
- // Docs and files. `create_attachment` uploads bytes and returns the
304
- // `attachable_sgid` that `create_upload` files into a vault.
305
- ["create_vault", "additive"],
306
- ["update_vault", "destructive"],
307
- ["create_document", "additive"],
308
- ["update_document", "destructive"],
309
- ["trash_document", "destructive"],
310
- ["create_attachment", "additive"],
311
- ["create_upload", "additive"],
312
- ["update_upload", "destructive"],
313
- ["trash_upload", "destructive"],
314
- ["create_cloud_file", "additive"],
315
- ["update_cloud_file", "destructive"],
316
- ["create_google_document", "additive"],
317
- ["update_google_document", "destructive"],
318
- // Check-ins
319
- ["create_question", "additive"],
320
- ["update_question", "destructive"],
321
- ["pause_question", "destructive"],
322
- ["resume_question", "destructive"],
323
- ["update_question_notification_settings", "destructive"],
324
- ["create_answer", "additive"],
325
- ["update_answer", "destructive"],
326
- // Timesheets
327
- ["create_timesheet_entry", "additive"],
328
- ["update_timesheet_entry", "destructive"],
329
- ["destroy_timesheet_entry", "destructive"],
330
- // Gauges
331
- ["toggle_gauge", "destructive"],
332
- ["create_gauge_needle", "additive"],
333
- ["update_gauge_needle", "destructive"],
334
- ["destroy_gauge_needle", "destructive"],
335
- ]);
336
- /**
337
- * One release-reviewed manifest, used both to classify a live tool and as the
338
- * baseline the runtime drift check compares against, so the annotation a
339
- * caller gets and the verdict a check reads can never disagree (P13). Names
340
- * and verdicts only — no schemas are vendored; the live `tools/list` response
341
- * stays authoritative.
342
- *
343
- * Basecamp publishes no tool inventory, so this list has no public document
344
- * to be checked against before a release. Catalog drift is visible at runtime
345
- * only — as unclassified-tool and unserved-name counts on connector status and
346
- * `/health` — and the credential-free provider check reads the OAuth
347
- * discovery metadata the server does publish instead.
348
- */
349
- export const BASECAMP_VETTED_CATALOG = vettedCatalog({
350
- reads: READ_ONLY_TOOLS,
351
- writes: WRITE_TOOLS,
352
- });
353
- /** The catalog's summary bound; a longer declared value throws (`src/registry.ts`). */
354
- const SUMMARY_BUDGET = 120;
355
- /**
356
- * Fit a purpose-bearing summary inside the catalog's bound. Basecamp's routing
357
- * fact is the account a grant reached, which only the operator's purpose can
358
- * name, so the summary carries the purpose and clipping is this function's
359
- * job rather than the operator's.
360
- */
361
- function boundedSummary(prefix, purpose) {
362
- const full = `${prefix}${purpose}`;
363
- if (full.length <= SUMMARY_BUDGET)
364
- return full;
365
- return `${full.slice(0, SUMMARY_BUDGET - 1).trimEnd()}…`;
366
- }
367
- function usageGuide(purpose, authScope, instructions) {
368
- const accountInstructions = instructions?.trim();
369
- // Leads the guide because discovery summarizes a connector by its first
370
- // content line, and which account a call lands in is the one thing that
371
- // decides routing between two Basecamp connections (P3).
372
- const grant = authScope === "personal"
373
- ? "Each person authorizes this connector separately and picks the account on Basecamp's consent screen, so two people can reach different accounts through it."
374
- : "One shared authorization, made on Basecamp's consent screen, decides that account for everyone who uses this connector.";
375
- return `# Basecamp usage
376
-
377
- One account per authorization: ${purpose}. Basecamp's hosted server acts in the single account the grant was made for, and no tool takes an account id. ${grant} Another account is another connector, never an argument.
378
-
379
- - Confirm the account before relying on it: \`get_me\` returns the signed-in person and a label naming the connected account. A record id from another account's URL answers "not found" here — read that as wrong connector, not missing data.
380
- - Basecamp's own reference notes are a read away: \`get_basecamp_guide\` with \`topic\` \`concepts\`, \`finding-things\`, \`my-work\`, \`posting-and-mentions\`, or \`deleting-and-trash\`. Read the topic before an unfamiliar sequence instead of guessing one.
381
- - Resolve ids before acting; never guess one. A Basecamp URL carries the account, project, and record ids, and \`get_by_url\` reads any link into those ids, the record, and where a reply goes. \`search\` finds records by text; \`list_projects\` then \`get_project\` returns a project's dock, whose tool ids (to-do set, message board, schedule, vault, card table, chat) the create tools take. People are ids from \`list_people\`, \`list_project_people\`, or \`list_pingable_people\`; mention someone by passing ids as \`create_comment\`'s \`mentions\`, not by writing markup.
382
- - Rich-text bodies (messages, documents, comments, cards, to-do descriptions) are HTML; a to-do's title and a chat line are plain text. Dates are \`YYYY-MM-DD\`.
383
- - Lists return up to \`limit\` items (100 by default). Continue with the \`next_cursor\` or \`next_page\` a result carries rather than raising the limit; \`truncated: true\` means the server cut a result to fit and says where to resume. The event feeds page by \`position\` instead. \`get_card_table\`, \`get_document\`, and the account-wide \`list_everything_*\` reads are large — reduce inside \`execute_code\` and return only the fields the question needs.
384
- - \`trash_*\` is restorable for 25 days; \`delete_*\`, \`destroy_*\`, and \`remove_account_logo\` are permanent. \`update_cloud_file\` and \`update_google_document\` are full replaces: an omitted title or description is erased, so read the record and resend every field you keep. \`update_project_access\` can invite people into the account.
385
- - Do not call \`create_stream_ticket\`: it mints a WebSocket credential for a client that holds a live connection, which a connecta call cannot. Poll \`list_feed_events\` instead. \`get_event_inbox\` serves agent accounts only; a person's grant gets \`agents_only\` back.
386
- - This connector's tool list is not a fixed set. Basecamp has not published this server and changes it without notice, and timesheets, gauges, hill charts, the Lineup, and check-ins depend on account features, so search this connector for what it actually exposes rather than assuming a tool exists.
387
- - Basecamp answers a rate limit with \`429\` and a \`Retry-After\` in seconds, and adjusts its limits dynamically. Back off for that long rather than retrying at once, and avoid speculative fan-out.
388
- - Treat every create, update, trash, delete, destroy, move, reposition, complete, pin, archive, enable, disable, watch, and mark operation as a write. Connecta routes the maintained write catalog through \`call_destructive_tool\`; newly added tools also fail closed until a release classifies them.
389
- - An \`auth_required\` failure means this connector's Basecamp authorization is missing or expired: run \`authorize_connector\` for this connector id, then retry the same call unchanged. A rejected argument, a permission gap, or a missing feature comes back in Basecamp's own words instead — read it rather than re-authorizing.
390
- ${accountInstructions
391
- ? `\n## Account instructions\n\n${accountInstructions}\n`
392
- : ""}`;
393
- }
394
- /** A maintained Basecamp hosted-MCP connection. */
395
- export function basecamp(id, options) {
396
- const purpose = options.purpose.trim();
397
- if (!purpose) {
398
- throw new Error("basecamp() requires a non-empty account purpose.");
399
- }
400
- const clientMetadataUrl = options.clientMetadataUrl;
401
- if (typeof clientMetadataUrl !== "string" || !clientMetadataUrl.trim()) {
402
- // A structural mistake, so it throws here rather than at the first
403
- // authorization, where the failure would be Basecamp refusing a
404
- // registration nobody in the conversation can repair.
405
- throw new Error(`basecamp("${id}") requires clientMetadataUrl: Basecamp restricts dynamic client registration for HTTPS redirect URIs, so connecta must present a Client ID Metadata Document instead. Host a public HTTPS JSON document whose client_id is its own URL and whose redirect_uris include <publicUrl>/oauth/callback/${id}.`);
406
- }
407
- const authScope = options.authScope ?? "shared";
408
- const connector = remoteMcp(id, {
409
- url: BASECAMP_MCP_ENDPOINT,
410
- ...(options.authScope ? { authScope: options.authScope } : {}),
411
- title: options.title ?? "Basecamp",
412
- description: `Basecamp projects, to-dos, messages, cards, schedules, and files (one account per authorization) — ${purpose}`,
413
- // OAuth only (P9's headless alternative knowingly missed). Basecamp issues
414
- // personal access tokens at app.basecamp.com/my/access_tokens, but nothing
415
- // public says this endpoint accepts one, and a PAT carries no refresh
416
- // token; shipping a slot for a framing nobody has verified is the
417
- // borrowed-convention mistake P9 names. The access mode P4 asks for is
418
- // missing for the same kind of reason: Basecamp advertises a `read` scope
419
- // beside `full`, but the MCP client requests the scope the server's own
420
- // challenge names (`mcp full`) ahead of anything configured here, and what
421
- // `read` permits on this server is unverified — a read-only option would
422
- // be a label, not a guarantee. The classification fails closed either way.
423
- auth: {
424
- type: "oauth",
425
- clientMetadataUrl,
426
- scope: FALLBACK_SCOPE,
427
- },
428
- requireHttps: true,
429
- usageGuide: {
430
- content: usageGuide(purpose, authScope, options.instructions),
431
- // Explicit rather than derived, and purpose-bearing: the derived summary
432
- // would cut the account sentence mid-clause, and two Basecamp
433
- // connections differ only by the account each grant reached (P3).
434
- summary: boundedSummary("One account per grant; get_me names it: ", purpose),
435
- // Not `required`. Basecamp's own schemas and `get_basecamp_guide` cover
436
- // each call; this guide carries account routing and the id chain, worth
437
- // reading before a run rather than before every call.
438
- },
439
- ...defined({
440
- callAdmission: options.callAdmission,
441
- maxResultBytes: options.maxResultBytes,
442
- }),
443
- });
444
- return withVettedCatalog(connector, BASECAMP_VETTED_CATALOG);
445
- }