@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
@@ -93,6 +93,8 @@ export interface ApiOAuthAccess {
93
93
  }
94
94
  /** The context an `api()` handler receives. */
95
95
  export interface ApiHandlerContext extends ConnectorContext {
96
+ /** Fetch with final outgoing credential headers and query values registered. */
97
+ fetch: typeof fetch;
96
98
  /** Present exactly when the connector declares `oauth`. */
97
99
  oauth?: ApiOAuthAccess;
98
100
  }
@@ -104,10 +106,10 @@ export interface ApiOptions {
104
106
  authScope?: "shared" | "personal";
105
107
  /**
106
108
  * Max inline result size (bytes) for this connector's tools before
107
- * call_tool truncates and stashes the full text for get_result
109
+ * call_tool truncates and stashes the full text for connecta.result
108
110
  * paging. Overrides the deployment's `calls.maxResultBytes`; omit to inherit
109
- * it. Must be a whole number of bytes >= 1; anything else warns at startup
110
- * and is ignored.
111
+ * it. Must be a whole number of bytes >= 1; anything else refuses to
112
+ * construct.
111
113
  */
112
114
  maxResultBytes?: number;
113
115
  /** Optional per-runtime downstream call-admission policy. */
@@ -141,6 +143,54 @@ export interface ApiOptions {
141
143
  validateArgs?: boolean;
142
144
  tools: ApiTool[];
143
145
  }
146
+ /** The closed options api() accepts; see `assertKnownOptions`. */
147
+ export declare const API_OPTIONS: import("../config-schema.js").Field<unknown, unknown, true> & {
148
+ readonly shape: {
149
+ callAdmission: import("../config-schema.js").Field<unknown, unknown, true> & {
150
+ readonly shape: {
151
+ rules: import("../config-schema.js").Field<unknown[], readonly unknown[] | undefined, true>;
152
+ maxPartitions: import("../config-schema.js").Field<unknown, unknown, true>;
153
+ };
154
+ };
155
+ usageGuide: import("../config-schema.js").Field<unknown, unknown, true> & {
156
+ readonly shape: {
157
+ readonly required: import("../config-schema.js").Field<unknown, unknown, true>;
158
+ readonly content: import("../config-schema.js").Field<unknown, unknown, true>;
159
+ readonly summary: import("../config-schema.js").Field<unknown, unknown, true>;
160
+ };
161
+ };
162
+ credential: import("../config-schema.js").Field<unknown, unknown, true> & {
163
+ readonly shape: {
164
+ fields: import("../config-schema.js").Field<unknown[], readonly unknown[] | undefined, true>;
165
+ label: import("../config-schema.js").Field<unknown, unknown, true>;
166
+ description: import("../config-schema.js").Field<unknown, unknown, true>;
167
+ placeholder: import("../config-schema.js").Field<unknown, unknown, true>;
168
+ };
169
+ };
170
+ oauth: import("../config-schema.js").Field<unknown, unknown, true> & {
171
+ readonly shape: {
172
+ authorizationParams: import("../config-schema.js").Field<Record<string, string>, Readonly<Record<string, string>> | undefined, true>;
173
+ tokenRequestHeaders: import("../config-schema.js").Field<Record<string, string>, Readonly<Record<string, string>> | undefined, true>;
174
+ scope: import("../config-schema.js").Field<unknown, unknown, true>;
175
+ authorizationEndpoint: import("../config-schema.js").Field<unknown, unknown, true>;
176
+ tokenEndpoint: import("../config-schema.js").Field<unknown, unknown, true>;
177
+ apiOrigins: import("../config-schema.js").Field<unknown, unknown, true>;
178
+ tokenEndpointAuthMethod: import("../config-schema.js").Field<unknown, unknown, true>;
179
+ pkce: import("../config-schema.js").Field<unknown, unknown, true>;
180
+ clientId: import("../config-schema.js").Field<unknown, unknown, true>;
181
+ clientSecret: import("../config-schema.js").Field<unknown, unknown, true>;
182
+ };
183
+ };
184
+ tools: import("../config-schema.js").Field<unknown[], readonly unknown[] | undefined, true>;
185
+ title: import("../config-schema.js").Field<unknown, unknown, true>;
186
+ description: import("../config-schema.js").Field<unknown, unknown, true>;
187
+ testCredential: import("../config-schema.js").Field<unknown, unknown, true>;
188
+ testCredentials: import("../config-schema.js").Field<unknown, unknown, true>;
189
+ authScope: import("../config-schema.js").Field<unknown, unknown, true>;
190
+ maxResultBytes: import("../config-schema.js").Field<unknown, unknown, true>;
191
+ validateArgs: import("../config-schema.js").Field<unknown, unknown, true>;
192
+ };
193
+ };
144
194
  /**
145
195
  * The OAuth half of an `api()` connector: the hooks the callback route,
146
196
  * operator UI, and `authorize_connector` key on, and the handler accessor.
@@ -154,15 +204,17 @@ export interface ApiOAuthHooks {
154
204
  }): Promise<ConnectorStatus>;
155
205
  disconnectAuth(ctx: ConnectorContext): Promise<void>;
156
206
  verifyState(state: string | null, ctx: ConnectorContext): Promise<boolean>;
157
- finishAuth(code: string, ctx: ConnectorContext, callbackParams?: URLSearchParams): Promise<void>;
207
+ verifyCallbackIssuer(issuer: string | null, ctx: ConnectorContext): Promise<boolean>;
208
+ consumeAuthError(ctx: ConnectorContext): Promise<void>;
209
+ /** Requires callbackParams with a nonempty state, including without PKCE. */
210
+ finishAuth(code: string, ctx: ConnectorContext, callbackParams: URLSearchParams): Promise<void>;
158
211
  /** The handler-facing accessor for one call's context. */
159
212
  access(ctx: ConnectorContext): ApiOAuthAccess;
160
213
  }
161
214
  /**
162
215
  * A static connector; every tool passes {@link checkToolContract} first.
163
216
  *
164
- * The builder behind `api()`, without the downstream OAuth machinery: a
165
- * module that only ever builds credential-free connectors (the artifacts
166
- * connector) imports this and pays nothing for a grant it cannot declare.
217
+ * The builder behind `api()`, without the downstream OAuth machinery: credential-free
218
+ * providers import this without paying for a grant they cannot declare.
167
219
  */
168
220
  export declare function apiConnector(id: string, opts: ApiOptions, oauth?: ApiOAuthHooks): Connector;
@@ -1,5 +1,12 @@
1
- import { ConnectorCallError, networkErrorCode, unavailableCallError, } from "../errors.js";
2
- import { compileValidator, validateToolInput } from "../validate.js";
1
+ import { ConnectorCallError, networkErrorCode, unavailableCallError, WithheldTextError } from "../errors.js";
2
+ import { attachFailureFacts, carryFailureFacts, errorLabel } from "../operator-record.js";
3
+ import { compileValidator, validateCatalogToolInput } from "../validate.js";
4
+ import { array, assertKnownOptions, instance, keys, optionsOf, strings } from "../config-schema.js";
5
+ import { describedEndpoint, describedOrigin, describedTools } from "../described.js";
6
+ import { CALL_ADMISSION, CREDENTIAL, USAGE_GUIDE } from "./option-shapes.js";
7
+ import { assertStaticToolNames } from "../tool-name.js";
8
+ import { registerInvocationAuth } from "../invocation-auth.js";
9
+ import { redactCatalog, redactSentSecrets, sentSecretsFetch, trackCredentialReads } from "../sent-secrets.js";
3
10
  export function defined(value) {
4
11
  return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined));
5
12
  }
@@ -25,17 +32,65 @@ function checkToolContract(id, tool) {
25
32
  "call_destructive_tool. Connecta never infers the classification " +
26
33
  "from a tool name, description, schema, or other annotations.");
27
34
  }
35
+ if (typeof tool.handler !== "function") {
36
+ throw new Error(`api() tool "${address}" needs a handler function.`);
37
+ }
28
38
  if (tool.inputSchema)
29
39
  compileValidator(tool.inputSchema, { address });
30
40
  }
41
+ /** The closed options api() accepts; see `assertKnownOptions`. */
42
+ export const API_OPTIONS = optionsOf()({
43
+ ...keys("title", "description", "authScope", "maxResultBytes", "testCredential", "testCredentials", "validateArgs"),
44
+ callAdmission: CALL_ADMISSION,
45
+ usageGuide: USAGE_GUIDE,
46
+ credential: CREDENTIAL,
47
+ oauth: optionsOf()({
48
+ ...keys("authorizationEndpoint", "tokenEndpoint", "clientId", "clientSecret", "tokenEndpointAuthMethod", "scope", "pkce", "apiOrigins"),
49
+ authorizationParams: strings(),
50
+ tokenRequestHeaders: strings(),
51
+ }),
52
+ // A tool's annotations stay open: MCP lets a tool carry hints Connecta
53
+ // does not interpret, and its schemas are JSON Schema, not options. A tool
54
+ // carries its handler, so it is checked in place and kept, never copied.
55
+ tools: array(instance(optionsOf()(keys("name", "description", "inputSchema", "outputSchema", "annotations", "handler")))),
56
+ });
57
+ /**
58
+ * How an `api()` connector authenticates, as names and public endpoints. The
59
+ * client id and secret, extra parameter values, and token-request header
60
+ * values never leave the options.
61
+ */
62
+ function describedApiAuth(opts) {
63
+ const oauth = opts.oauth;
64
+ if (!oauth)
65
+ return { mode: opts.credential ? "credential" : "none" };
66
+ const confidential = typeof oauth.clientSecret === "string" && oauth.clientSecret !== "";
67
+ const authorizationEndpoint = describedEndpoint(oauth.authorizationEndpoint);
68
+ const tokenEndpoint = describedEndpoint(oauth.tokenEndpoint);
69
+ const apiOrigins = Array.isArray(oauth.apiOrigins)
70
+ ? oauth.apiOrigins.flatMap((origin) => describedOrigin(origin) ?? [])
71
+ : [];
72
+ return {
73
+ mode: "oauth",
74
+ ...(authorizationEndpoint ? { authorizationEndpoint } : {}),
75
+ ...(tokenEndpoint ? { tokenEndpoint } : {}),
76
+ apiOrigins,
77
+ tokenEndpointAuthMethod: oauth.tokenEndpointAuthMethod ?? (confidential ? "client_secret_basic" : "none"),
78
+ confidentialClient: confidential,
79
+ pkce: oauth.pkce ?? true,
80
+ ...(oauth.scope !== undefined ? { scope: oauth.scope } : {}),
81
+ ...(oauth.authorizationParams ? { authorizationParamNames: Object.keys(oauth.authorizationParams) } : {}),
82
+ ...(oauth.tokenRequestHeaders ? { tokenRequestHeaderNames: Object.keys(oauth.tokenRequestHeaders) } : {}),
83
+ };
84
+ }
31
85
  /**
32
86
  * A static connector; every tool passes {@link checkToolContract} first.
33
87
  *
34
- * The builder behind `api()`, without the downstream OAuth machinery: a
35
- * module that only ever builds credential-free connectors (the artifacts
36
- * connector) imports this and pays nothing for a grant it cannot declare.
88
+ * The builder behind `api()`, without the downstream OAuth machinery: credential-free
89
+ * providers import this without paying for a grant they cannot declare.
37
90
  */
38
91
  export function apiConnector(id, opts, oauth) {
92
+ opts = assertKnownOptions(opts, `api(${JSON.stringify(id)})`, API_OPTIONS);
93
+ assertStaticToolNames(opts.tools, `api(${JSON.stringify(id)}).tools`);
39
94
  if (opts.oauth !== undefined && oauth === undefined) {
40
95
  throw new Error(`api() connector "${id}" declares oauth but was built without its grant; construct it with api().`);
41
96
  }
@@ -58,10 +113,12 @@ export function apiConnector(id, opts, oauth) {
58
113
  }));
59
114
  const byName = new Map(opts.tools.map((t) => [t.name, t]));
60
115
  const validateArgs = opts.validateArgs ?? true;
61
- return {
116
+ const auth = describedApiAuth(opts);
117
+ const connector = {
62
118
  id,
63
119
  ...defined({ title: opts.title }),
64
120
  kind: "api",
121
+ describe: () => ({ source: { kind: "api" }, auth, tools: describedTools(defs) }),
65
122
  ...defined({
66
123
  description: opts.description,
67
124
  authScope: opts.authScope,
@@ -78,28 +135,31 @@ export function apiConnector(id, opts, oauth) {
78
135
  startAuth: oauth.startAuth,
79
136
  disconnectAuth: oauth.disconnectAuth,
80
137
  verifyState: oauth.verifyState,
138
+ verifyCallbackIssuer: oauth.verifyCallbackIssuer,
139
+ consumeAuthError: oauth.consumeAuthError,
81
140
  finishAuth: oauth.finishAuth,
82
141
  }
83
142
  : {}),
84
143
  staticTools: defs,
85
- async listTools() {
86
- return defs;
144
+ async listTools(ctx) {
145
+ return redactCatalog(ctx, defs);
87
146
  },
88
147
  async callTool(name, args, ctx) {
148
+ trackCredentialReads(ctx);
89
149
  const tool = byName.get(name);
90
150
  if (!tool) {
91
151
  throw new Error(`Unknown tool "${name}" on connector "${id}"`);
92
152
  }
93
153
  const input = args ?? {};
94
154
  if (validateArgs && tool.inputSchema) {
95
- const invalid = validateToolInput(tool.inputSchema, input, {
155
+ const invalid = validateCatalogToolInput(tool.inputSchema, input, {
96
156
  address: `${id}.${name}`,
97
157
  logger: ctx.logger,
98
158
  // Always: the schema compiled at construction, so anything that
99
159
  // fails here is a schema that cannot be enforced, and a surface we
100
160
  // wrote ourselves does not get to admit unvalidated input quietly.
101
161
  failClosed: true,
102
- });
162
+ }, { connector: id, tool });
103
163
  if (invalid)
104
164
  throw invalid;
105
165
  }
@@ -107,21 +167,48 @@ export function apiConnector(id, opts, oauth) {
107
167
  // its first await never sits handler-less for the thenable-adoption
108
168
  // microtask — workerd and vitest both report that gap as an unhandled
109
169
  // rejection even though the caller catches the failure.
110
- // The accessor closes over the registry's own context, not this copy,
111
- // so the grant it reaches is the one that context's owner holds.
112
- const handlerCtx = oauth
113
- ? { ...ctx, oauth: oauth.access(ctx) }
114
- : ctx;
170
+ // Preserve the registry's context identity: private caller/provenance
171
+ // markers belong to it, as does the grant its accessor reaches.
172
+ const handlerCtx = Object.assign(ctx, {
173
+ fetch: sentSecretsFetch(ctx),
174
+ ...(oauth ? { oauth: oauth.access(ctx) } : {}),
175
+ });
115
176
  try {
116
- return await tool.handler(input, handlerCtx);
177
+ return redactSentSecrets(ctx, await tool.handler(input, handlerCtx));
117
178
  }
118
179
  catch (error) {
180
+ // The request's own abort reason, by identity, is the caller's.
181
+ if (ctx.signal?.aborted === true && error === ctx.signal.reason)
182
+ throw error;
119
183
  // A handler owns its destinations. ctx.baseUrl is Connecta's inbound
120
184
  // URL, so it must never masquerade as the failed downstream host.
121
- if (error instanceof ConnectorCallError || !networkErrorCode(error))
122
- throw error;
123
- throw unavailableCallError(error);
185
+ if (error instanceof ConnectorCallError)
186
+ throw redactSentSecrets(ctx, error);
187
+ if (!networkErrorCode(error)) {
188
+ // Anything else is a runtime's, parser's, or stream's account of
189
+ // what the handler read (a JSON parser quotes the reply it choked
190
+ // on; a body stream rejects with whatever the runtime says), so it
191
+ // is told in connecta's words, classified as the original would
192
+ // have been, and rebuilt without its cause or nested errors. A
193
+ // handler that means the agent to read its words throws a
194
+ // ConnectorCallError. No host is named: connecta does not know
195
+ // which destination the handler read.
196
+ const kind = errorLabel(error);
197
+ throw attachFailureFacts(carryFailureFacts(error, new WithheldTextError(`Connector "${id}" tool "${name}" handler failed` +
198
+ `${kind ? ` (${kind})` : ""}. Its text is withheld because it ` +
199
+ "can quote what the downstream sent.", error)), { step: "handler" });
200
+ }
201
+ throw unavailableCallError(error, undefined, undefined, ctx.signal);
124
202
  }
125
203
  },
126
204
  };
205
+ if (oauth)
206
+ registerInvocationAuth(connector, async (ctx) => {
207
+ // The built-in static OAuth status only reads the stored grant. It never
208
+ // refreshes, invokes a handler, or sends a downstream request.
209
+ if ((await oauth.status(ctx)).state === "auth_required") {
210
+ throw new ConnectorCallError("downstream_oauth_required", `Connector "${id}" requires authorization. Call authorize_connector.`);
211
+ }
212
+ });
213
+ return connector;
127
214
  }
@@ -1,11 +1,14 @@
1
1
  import { staticOAuth } from "../auth/static-oauth.js";
2
- import { apiConnector } from "./api-connector.js";
2
+ import { assertKnownOptions } from "../config-schema.js";
3
+ import { API_OPTIONS, apiConnector } from "./api-connector.js";
3
4
  /**
4
5
  * A hand-written connector: static tools, each with a handler. With `oauth`,
5
6
  * its handlers reach the API through the calling owner's downstream grant
6
7
  * (`ctx.oauth`), managed exactly like a `remoteMcp()` grant.
7
8
  */
8
9
  export function api(id, opts) {
10
+ // Read the options once, as plain data, before choosing a grant from them.
11
+ opts = assertKnownOptions(opts, `api(${JSON.stringify(id)})`, API_OPTIONS);
9
12
  if (opts.oauth !== undefined && opts.credential !== undefined) {
10
13
  throw new Error(`api() connector "${id}" declares both oauth and credential. Declare ` +
11
14
  "one: auth_required must name a single recovery. A provider offering " +
@@ -0,0 +1,144 @@
1
+ import type { ConnectorContext } from "../types.js";
2
+ /** The methods a hand-written provider surface actually uses. */
3
+ type GuardedMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
4
+ export interface GuardedRequest {
5
+ method: GuardedMethod;
6
+ /**
7
+ * Provider-relative path beginning with one `/`, resolved against the base
8
+ * URL's own path. Never an absolute URL, and never carrying `?` or `#` —
9
+ * query parameters go in `query`, where they are encoded rather than parsed.
10
+ */
11
+ path: string;
12
+ /**
13
+ * Search parameters; `undefined` values are dropped, others stringified. An
14
+ * array repeats its key once per element, in order — the spelling of the key
15
+ * itself (`ids` or `ids[]`) is the provider's convention, not the helper's.
16
+ */
17
+ query?: Record<string, string | number | boolean | readonly (string | number | boolean)[] | undefined>;
18
+ /**
19
+ * Per-request headers. `undefined` values are dropped, and a header that
20
+ * collides with an authentication header is refused rather than allowed to
21
+ * shadow it.
22
+ */
23
+ headers?: Record<string, string | undefined>;
24
+ /** JSON request body. Serialized here, with the `Content-Type` to match. */
25
+ body?: unknown;
26
+ /**
27
+ * A pre-framed body — `FormData`, text, bytes. Mutually exclusive with
28
+ * `body`, and no `Content-Type` is supplied: multipart needs `fetch` to pick
29
+ * the boundary, and anything else knows its own type.
30
+ */
31
+ rawBody?: BodyInit;
32
+ /**
33
+ * The mapper reads only a bounded prefix of a successful body, with
34
+ * `response.prefix()`, so a declared length past the ceiling is not refused
35
+ * up front: the ceiling still bounds what is read, never what is sent.
36
+ */
37
+ prefixOnly?: boolean;
38
+ }
39
+ /**
40
+ * One downstream response, read under the connector's byte ceiling.
41
+ *
42
+ * The accessors are the whole read surface — there is no escape to the
43
+ * underlying `Response`, because an unbounded `.text()` on it is exactly the
44
+ * mistake this type exists to remove. `status`, `ok`, and `headers` are the
45
+ * raw facts a provider mapper needs to make its own classification.
46
+ */
47
+ interface GuardedResponse {
48
+ readonly status: number;
49
+ readonly ok: boolean;
50
+ readonly headers: Headers;
51
+ /** The body as bytes. */
52
+ bytes(): Promise<Uint8Array>;
53
+ /** The body decoded as UTF-8. */
54
+ text(): Promise<string>;
55
+ /** The body parsed as JSON; `undefined` for an empty body, throws on junk. */
56
+ json(): Promise<unknown>;
57
+ /** Parse JSON while distinguishing malformed content from transport failure. */
58
+ jsonResult(): Promise<{
59
+ value: unknown;
60
+ } | {
61
+ parseError: unknown;
62
+ }>;
63
+ /**
64
+ * At most `maxBytes` of the body (never past the ceiling), then the rest of
65
+ * the stream cancelled unread. Exactly: it retains at most `maxBytes` bytes,
66
+ * and consumes from the transport at most `maxBytes` plus one chunk — the
67
+ * chunk that crosses the bound, whose overflow is how it learns there was
68
+ * more (`truncated`). Chunks are whatever size the runtime delivers (64 KiB
69
+ * is common), so that one chunk is the whole of the overrun. Exclusive with
70
+ * the other readers: a body is read once.
71
+ */
72
+ prefix(maxBytes: number): Promise<{
73
+ bytes: Uint8Array;
74
+ truncated: boolean;
75
+ }>;
76
+ }
77
+ /** Parse delta-seconds or an HTTP-date into a non-negative wait window. */
78
+ export declare function retryAfterMs(headers: Headers): number | undefined;
79
+ /**
80
+ * Turn one response into the provider's own result, or throw the provider's
81
+ * own typed failure. This is where status codes acquire meaning, and it is
82
+ * always the provider's code.
83
+ */
84
+ type GuardedResponseMapper<T> = (response: GuardedResponse, ctx: ConnectorContext) => T | Promise<T>;
85
+ export interface GuardedFetchOptions {
86
+ /**
87
+ * Provider name as it appears in normalized failure prose — "Cloudflare",
88
+ * "Notion". Used for messages only; it is never parsed.
89
+ */
90
+ provider: string;
91
+ /**
92
+ * Absolute base URL. Its origin and path prefix are the confinement: every
93
+ * request resolves beneath it or is refused.
94
+ */
95
+ baseUrl: string;
96
+ /**
97
+ * Headers proving the caller's identity, resolved once per request. Throwing
98
+ * a typed `auth_required` from here is the documented way to report a
99
+ * missing credential — the helper has no opinion about what a credential is.
100
+ */
101
+ authenticate: (ctx: ConnectorContext) => Record<string, string> | Promise<Record<string, string>>;
102
+ /**
103
+ * What actually sends a request, after confinement, framing, and
104
+ * authentication. Defaults to the global `fetch`. A connector that declares
105
+ * `oauth` supplies {@link oauthBearer}'s, so the calling owner's grant — not
106
+ * a header this helper assembled — authenticates the request. A rejection
107
+ * that is already a `ConnectorCallError` passes through unchanged; any other
108
+ * is normalized to `unavailable`, exactly as a global `fetch` failure is.
109
+ */
110
+ fetch?: GuardedFetcher;
111
+ /** Constant headers sent with every request — `Accept`, an API version. */
112
+ headers?: Record<string, string>;
113
+ /**
114
+ * Ceiling on the response body, in bytes. Required, because a default here
115
+ * would be the helper guessing on a provider's behalf: what counts as an
116
+ * absurd response is a fact about the API, not about HTTP.
117
+ */
118
+ maxResponseBytes: number;
119
+ }
120
+ /** Sends one already-guarded request on behalf of one call's context. */
121
+ type GuardedFetcher = (url: string, init: RequestInit, ctx: ConnectorContext) => Promise<Response>;
122
+ /**
123
+ * The transport half of a guarded connector that authenticates through its
124
+ * own `api()` OAuth grant: no header of the helper's making, and every request
125
+ * sent through `ctx.oauth.fetch`, which adds the calling owner's bearer token,
126
+ * refuses any origin outside the grant's `apiOrigins`, and spends one
127
+ * coordinated refresh and one replay on a 401 before answering
128
+ * `auth_required`. Spread it into the options:
129
+ * `guardedFetch({ provider, baseUrl, maxResponseBytes, ...oauthBearer(provider) })`.
130
+ *
131
+ * Confinement still happens here first, so a request reaches the grant only
132
+ * once it is provably beneath `baseUrl`; the grant's origin check is the second
133
+ * fence, not the only one.
134
+ */
135
+ export declare function oauthBearer(provider: string): Pick<GuardedFetchOptions, "authenticate" | "fetch">;
136
+ /** Send one guarded request and map its response with provider knowledge. */
137
+ export type GuardedTransport = <T>(request: GuardedRequest, ctx: ConnectorContext, map: GuardedResponseMapper<T>) => Promise<T>;
138
+ /**
139
+ * Build the transport a connector sends every request through. `maxResponseBytes`
140
+ * is required rather than defaulted: what counts as an absurd response is a fact
141
+ * about the API, not about HTTP.
142
+ */
143
+ export declare function guardedFetch(options: GuardedFetchOptions): GuardedTransport;
144
+ export {};