@zackbart/connecta 0.37.1 → 0.39.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 (265) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +5 -4
  3. package/bin/connecta.mjs +1 -39
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.js +2 -4
  6. package/dist/agent-identity.js +1 -1
  7. package/dist/agent-routes.js +231 -0
  8. package/dist/auth/clerk-sdk/generated.js +2 -1
  9. package/dist/auth/downstream-oauth/consent.js +519 -0
  10. package/dist/auth/downstream-oauth/grant-store.js +439 -0
  11. package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
  12. package/dist/auth/downstream-oauth/refresh-records.js +447 -0
  13. package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
  14. package/dist/auth/downstream-oauth.js +66 -2516
  15. package/dist/auth/downstream-revocation.js +1 -1
  16. package/dist/auth/native.js +27 -0
  17. package/dist/auth/oauth-grant.js +24 -0
  18. package/dist/auth/oauth-token-response.js +1 -1
  19. package/dist/auth/static-oauth.js +153 -131
  20. package/dist/auth/vercel-agent.js +2 -1
  21. package/dist/auth-elicitation.js +9 -3
  22. package/dist/authorization-handoff.js +11 -5
  23. package/dist/branding.d.ts +1 -15
  24. package/dist/branding.js +2 -2
  25. package/dist/bytes.js +42 -0
  26. package/dist/call-admission-context.js +2 -5
  27. package/dist/call-recovery.js +0 -3
  28. package/dist/catalog-cache.js +130 -43
  29. package/dist/catalog-fingerprint.js +2 -18
  30. package/dist/catalog-policy.js +18 -0
  31. package/dist/catalog-service.js +285 -48
  32. package/dist/catalog.js +18 -2
  33. package/dist/config-defaults.d.ts +1 -0
  34. package/dist/config-defaults.js +1 -0
  35. package/dist/config-schema.d.ts +1 -1
  36. package/dist/config-schema.js +6 -2
  37. package/dist/config.d.ts +57 -25
  38. package/dist/config.js +30 -77
  39. package/dist/connector-caller.d.ts +1 -1
  40. package/dist/connector-health.js +59 -0
  41. package/dist/connector-status.js +117 -0
  42. package/dist/connectors/api-connector.d.ts +7 -2
  43. package/dist/connectors/api-connector.js +11 -7
  44. package/dist/connectors/guarded-fetch.d.ts +0 -6
  45. package/dist/connectors/guarded-fetch.js +1 -11
  46. package/dist/connectors/negotiation-cache.js +22 -4
  47. package/dist/connectors/remote-mcp/call.js +164 -0
  48. package/dist/connectors/remote-mcp/catalog.js +278 -0
  49. package/dist/connectors/remote-mcp/config.js +263 -0
  50. package/dist/connectors/remote-mcp/connection.js +671 -0
  51. package/dist/connectors/remote-mcp/errors.js +488 -0
  52. package/dist/connectors/remote-mcp/oauth.js +469 -0
  53. package/dist/connectors/remote-mcp/resources.js +473 -0
  54. package/dist/connectors/remote-mcp/runtime.js +1 -0
  55. package/dist/connectors/remote-mcp/transport.js +544 -0
  56. package/dist/connectors/remote-mcp.js +61 -2898
  57. package/dist/connectors/resource-uri.js +0 -4
  58. package/dist/credential-contract.d.ts +23 -0
  59. package/dist/credential-reads.js +17 -0
  60. package/dist/credential-verification.js +53 -0
  61. package/dist/credentials.d.ts +7 -0
  62. package/dist/credentials.js +60 -25
  63. package/dist/d1.d.ts +0 -89
  64. package/dist/d1.js +0 -34
  65. package/dist/describe-config.d.ts +12 -2
  66. package/dist/describe-config.js +2 -0
  67. package/dist/discovery-health.js +199 -0
  68. package/dist/downstream-input.js +4 -2
  69. package/dist/errors.d.ts +55 -17
  70. package/dist/errors.js +95 -41
  71. package/dist/execute.js +172 -125
  72. package/dist/executor-result.js +5 -2
  73. package/dist/executors/quickjs-runtime.js +2 -25
  74. package/dist/executors/quickjs.js +1 -1
  75. package/dist/guest-functions.js +83 -0
  76. package/dist/guest-runtime.js +63 -20
  77. package/dist/guest-types.d.ts +24 -10
  78. package/dist/http/argument-problems.js +35 -0
  79. package/dist/http/client.js +1 -1
  80. package/dist/http/errors.js +7 -6
  81. package/dist/http/fields.js +192 -13
  82. package/dist/http/hatch-response.js +73 -0
  83. package/dist/http/hatch.js +88 -23
  84. package/dist/http/json-schema.js +1 -1
  85. package/dist/http/node-transport.js +0 -1
  86. package/dist/http/oauth.js +4 -3
  87. package/dist/http/paging.js +6 -9
  88. package/dist/http/provider.js +33 -5
  89. package/dist/http/safety.js +9 -1
  90. package/dist/http/services.js +1 -8
  91. package/dist/http/tools.js +45 -30
  92. package/dist/http/vendor.js +7 -7
  93. package/dist/identity.js +2 -6
  94. package/dist/index.d.ts +1 -1
  95. package/dist/index.js +9 -2
  96. package/dist/invocation-auth.js +2 -1
  97. package/dist/invocation.js +21 -6
  98. package/dist/membership.d.ts +13 -5
  99. package/dist/membership.js +34 -2
  100. package/dist/meta-tools.js +50 -35
  101. package/dist/native-recovery.js +4 -16
  102. package/dist/native-tools.js +6 -2
  103. package/dist/oauth-handoff.js +2 -2
  104. package/dist/oauth-sealing.js +10 -0
  105. package/dist/operation-vocabulary.js +136 -14
  106. package/dist/operator-record.d.ts +2 -2
  107. package/dist/operator-record.js +2 -49
  108. package/dist/operator-ui/contract.d.ts +12 -2
  109. package/dist/operator-ui/generated.js +3 -3
  110. package/dist/operator-ui/model.d.ts +22 -1
  111. package/dist/program-writes.js +3 -8
  112. package/dist/provider-call.js +0 -7
  113. package/dist/provider.d.ts +1 -1
  114. package/dist/providers/_shared/google/kit.js +1 -6
  115. package/dist/providers/_shared/google/service-account.js +1 -3
  116. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  117. package/dist/providers/_shared/guide.js +3 -0
  118. package/dist/providers/_shared/json.js +12 -0
  119. package/dist/providers/_shared/rest/compact-index.js +1 -1
  120. package/dist/providers/_shared/rest/kit.js +1 -3
  121. package/dist/providers/_shared/rest/operation-index.js +49 -53
  122. package/dist/providers/_shared/rest/value-safety.js +106 -28
  123. package/dist/providers/breeze/api.js +9 -8
  124. package/dist/providers/breeze/index.js +4 -2
  125. package/dist/providers/breeze/skill.generated.js +1 -1
  126. package/dist/providers/breeze/tools.js +7 -7
  127. package/dist/providers/ccb/api.js +3 -7
  128. package/dist/providers/ccb/index.js +7 -1
  129. package/dist/providers/ccb/skill.generated.js +1 -1
  130. package/dist/providers/ccb/tools.js +1 -6
  131. package/dist/providers/cloudflare/api.js +10 -3
  132. package/dist/providers/cloudflare/index.js +2 -1
  133. package/dist/providers/cloudflare/safety.js +3 -3
  134. package/dist/providers/cloudflare/skill.generated.js +1 -1
  135. package/dist/providers/docs/lib/schemas.js +24 -22
  136. package/dist/providers/docs/safety.js +2 -0
  137. package/dist/providers/docs/skill.generated.js +1 -1
  138. package/dist/providers/docs/tools.js +4 -14
  139. package/dist/providers/drive/api.js +1 -0
  140. package/dist/providers/drive/lib/contracts.js +8 -5
  141. package/dist/providers/drive/skill.generated.js +1 -1
  142. package/dist/providers/drive/tools.js +23 -41
  143. package/dist/providers/forms/api.js +1 -0
  144. package/dist/providers/forms/skill.generated.js +1 -1
  145. package/dist/providers/forms/tools.js +11 -16
  146. package/dist/providers/github/api.js +1 -0
  147. package/dist/providers/github/lib/discovery.js +1 -1
  148. package/dist/providers/github/skill.generated.js +1 -1
  149. package/dist/providers/github/tools.js +61 -17
  150. package/dist/providers/gmail/lib/schemas.js +60 -50
  151. package/dist/providers/gmail/skill.generated.js +1 -1
  152. package/dist/providers/gmail/tools.js +12 -26
  153. package/dist/providers/granola/index.js +2 -0
  154. package/dist/providers/granola/skill.generated.js +1 -1
  155. package/dist/providers/overflow/api.js +8 -4
  156. package/dist/providers/overflow/index.js +2 -1
  157. package/dist/providers/overflow/skill.generated.js +1 -1
  158. package/dist/providers/overflow/tools.js +3 -7
  159. package/dist/providers/planning-center/api.js +5 -2
  160. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  161. package/dist/providers/planning-center/lib/json-api.js +1 -9
  162. package/dist/providers/planning-center/skill.generated.js +1 -1
  163. package/dist/providers/planning-center/tools.js +2 -1
  164. package/dist/providers/quickbooks/safety.js +80 -39
  165. package/dist/providers/quickbooks/skill.generated.js +1 -1
  166. package/dist/providers/sheets/api.js +1 -0
  167. package/dist/providers/sheets/skill.generated.js +1 -1
  168. package/dist/providers/sheets/tools.js +14 -23
  169. package/dist/providers/slides/skill.generated.js +1 -1
  170. package/dist/providers/slides/tools.js +19 -26
  171. package/dist/providers/stripe/index.js +2 -1
  172. package/dist/providers/stripe/safety.js +2 -2
  173. package/dist/providers/stripe/skill.generated.js +1 -1
  174. package/dist/providers/tithely/api.js +5 -8
  175. package/dist/providers/tithely/index.js +3 -2
  176. package/dist/providers/tithely/skill.generated.js +1 -1
  177. package/dist/providers/tithely/tools.js +7 -5
  178. package/dist/providers/vercel/api.js +2 -7
  179. package/dist/providers/vercel/lib/pin.js +1 -1
  180. package/dist/providers/vercel/safety.js +1 -1
  181. package/dist/providers/vercel/skill.generated.js +1 -1
  182. package/dist/providers/vercel/tools.js +6 -4
  183. package/dist/redaction-marker.js +2 -2
  184. package/dist/registry.d.ts +11 -5
  185. package/dist/registry.js +46 -20
  186. package/dist/request-principal.js +1 -1
  187. package/dist/request-state.js +2 -2
  188. package/dist/routes/access-tokens.js +1 -1
  189. package/dist/routes/activity-disclosure.js +1 -1
  190. package/dist/routes/connect.js +2 -2
  191. package/dist/routes/credentials.js +130 -61
  192. package/dist/routes/dispatch.js +5 -1
  193. package/dist/routes/mcp.js +34 -17
  194. package/dist/routes/oauth-management.js +1 -1
  195. package/dist/routes/operator.js +6 -5
  196. package/dist/routes/shared.d.ts +13 -8
  197. package/dist/routes/shared.js +15 -9
  198. package/dist/routes/ui-config.js +3 -3
  199. package/dist/routes/ui-data.js +126 -20
  200. package/dist/routes/ui.js +17 -1
  201. package/dist/runtime/call-admission.js +1 -1
  202. package/dist/runtime/call.js +8 -3
  203. package/dist/runtime/native-storage.js +9 -0
  204. package/dist/runtime/request-reads.js +182 -0
  205. package/dist/runtime/request-scope.js +16 -0
  206. package/dist/runtime/settlement.js +1 -1
  207. package/dist/runtime/shared-read.js +2 -15
  208. package/dist/runtime/storage.js +12 -0
  209. package/dist/sent-secrets.d.ts +1 -2
  210. package/dist/sent-secrets.js +3 -8
  211. package/dist/server.js +4 -4
  212. package/dist/skills.js +44 -54
  213. package/dist/sqlite.d.ts +0 -19
  214. package/dist/sqlite.js +2 -83
  215. package/dist/storage/keys.js +69 -128
  216. package/dist/storage/memory.js +8 -2
  217. package/dist/storage/sql-native.js +27 -101
  218. package/dist/storage/sql.d.ts +1 -24
  219. package/dist/storage/sql.js +13 -14
  220. package/dist/storage-digest.js +32 -0
  221. package/dist/timeout.js +8 -0
  222. package/dist/tool-error.js +4 -0
  223. package/dist/types.d.ts +33 -0
  224. package/dist/url-safety.js +0 -4
  225. package/dist/usage-guide.js +18 -37
  226. package/dist/validate.d.ts +3 -0
  227. package/dist/validate.js +220 -158
  228. package/dist/version.d.ts +1 -1
  229. package/dist/version.js +1 -1
  230. package/dist/worker.js +1 -1
  231. package/dist/write-outcome.js +0 -3
  232. package/documentation/README.md +19 -55
  233. package/documentation/architecture.md +436 -515
  234. package/documentation/auth.md +229 -88
  235. package/documentation/code-mode.md +201 -94
  236. package/documentation/deploying.md +25 -43
  237. package/documentation/integrating.md +18 -19
  238. package/documentation/meta-tools.md +225 -51
  239. package/documentation/migration-0.33.md +10 -6
  240. package/documentation/migration-0.34.md +5 -5
  241. package/documentation/migration-retired-layouts.md +74 -0
  242. package/documentation/operator-tests.md +20 -120
  243. package/documentation/operator-ui.md +108 -16
  244. package/documentation/pool-membership-upgrade.md +44 -0
  245. package/documentation/provider-auth-migration.md +4 -10
  246. package/documentation/provider-migration-0.29.md +4 -15
  247. package/documentation/providers.md +105 -0
  248. package/examples/worker/AGENTS.md +14 -16
  249. package/examples/worker/README.md +24 -118
  250. package/examples/worker/src/connecta.config.ts +1 -0
  251. package/guides/connecting-agents.md +9 -31
  252. package/guides/how-it-works.md +6 -4
  253. package/guides/safety-and-trust.md +4 -1
  254. package/package.json +19 -19
  255. package/templates/node/AGENTS.md +13 -12
  256. package/templates/node/README.md +9 -26
  257. package/templates/node/package.json +1 -1
  258. package/templates/node/src/connecta.config.ts +1 -0
  259. package/dist/auth/oauth-v2-migration.js +0 -135
  260. package/dist/provider-call-facts.d.ts +0 -115
  261. package/dist/provider-call.d.ts +0 -112
  262. package/dist/storage/d1-copy.js +0 -255
  263. package/dist/storage/sql-schema.d.ts +0 -6
  264. package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
  265. package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
@@ -64,12 +64,9 @@ platform, proxy or adapter cap still wins. See
64
64
  A Node host serving `connecta.fetch` without `listen()` has no owned sender:
65
65
  every maintained-provider write, every reviewed POST read and every remote MCP
66
66
  write is refused before sending. Downstream OAuth credential POSTs also require
67
- that sender; discovery GETs retain native fetch. Resource writes worked in 0.34.1;
68
- credential POSTs used native fetch through 0.34.1. For example, an `assistant` using
67
+ that sender. For example, an `assistant` using
69
68
  `vercel_api_read` to POST `/v1/registrar/domains/availability` needs that
70
- transport even on a read-only endpoint. A Node deployment using only the root
71
- entry refuses those requests before sending them. Workers use their native
72
- fetch transport.
69
+ transport even on a read-only endpoint. Workers use their native fetch transport.
73
70
 
74
71
  By default, Node's `listen()` handles SIGTERM and SIGINT: it stops accepting
75
72
  connections, closes idle connections and accepted sockets without a first HTTP
@@ -81,9 +78,8 @@ keepalive before shutdown is unchanged. Its shutdown deadline defaults to ten se
81
78
  (`shutdownTimeoutMs`). For example, retain the template's `listen(connecta, port)` when running it under
82
79
  Docker Compose so `docker compose stop` uses this cleanup.
83
80
 
84
- The owned Node sender connects directly by default. To send maintained-provider
85
- requests (including reads), remote MCP writes and OAuth credential POSTs through an HTTP(S) CONNECT
86
- proxy, set `proxy` explicitly on `listen`:
81
+ The owned Node sender connects directly by default. For HTTP(S) CONNECT proxy
82
+ egress, set `proxy` explicitly on `listen`:
87
83
 
88
84
  ```ts
89
85
  listen(connecta, { port: 3000, proxy: "http://proxy.example.com:8080" });
@@ -108,9 +104,7 @@ replayed by the sender, including after HTTP 421, a reset or a 503. An uncertain
108
104
  refresh requires authorization again; Connecta blocks the spent refresh
109
105
  fingerprint rather than redeeming it twice. Once a refresh claim has been
110
106
  submitted, that protection remains conservative even if proxy setup later fails.
111
- Use a fresh authorization flow to reconcile. OAuth discovery remains on native
112
- fetch and needs its own host proxy policy. These credential POST requirements
113
- change the native-fetch behavior of 0.34.1.
107
+ Use a fresh authorization flow to reconcile.
114
108
 
115
109
  ## Start on Workers
116
110
 
@@ -136,10 +130,8 @@ verified ingress behavior and the cases that still require verification.
136
130
  Agents use `/agent/<pool>`, which connecta authenticates with the `cta_` token
137
131
  alone and which ignores Access context entirely
138
132
  ([agent endpoints](./auth.md#agent-endpoints)). Behind Access, the edge must
139
- let those requests through without an Access login or service token. The P0
140
- probe ([observations](./architecture.md#production-access-bypass-for-agent-endpoints))
141
- verified this shape on 2026-10-15. The Worker example's `alchemy.run.ts`
142
- declares both applications:
133
+ let those requests through without an Access login or service token.
134
+ The Worker example's `alchemy.run.ts` declares both applications:
143
135
 
144
136
  1. Keep the Worker-level application, whose destination is
145
137
  `{ "type": "worker", "worker_id": "<the Worker script tag>" }`, exactly as
@@ -170,12 +162,9 @@ declares both applications:
170
162
  3. Point each agent at `https://<host>/agent/<pool>` with
171
163
  `Authorization: Bearer cta_…` and drop its `CF-Access-Client-Id` and
172
164
  `CF-Access-Client-Secret` headers. The pool must list `"agent:<name>"`.
173
- 4. Verify after the change has settled. Access changes took 15–19 seconds to
174
- settle in the probe, and while they propagate the edge can answer with
175
- transient 500 (`error code: 1104`) or 404 (`error code: 1042` or `1043`)
176
- responses, and in one round a 403 on a bypassed path after more than 30
177
- seconds. Do not rely on a fixed wait: repeat these checks until they agree
178
- for several consecutive rounds. Without credentials, `/agent/<pool>`
165
+ 4. Verify after the change has settled. Do not rely on a fixed wait: repeat
166
+ these checks until they agree for several consecutive rounds. Without
167
+ credentials, `/agent/<pool>`
179
168
  reaches connecta (its 401 carries `WWW-Authenticate: Bearer`, not Access's
180
169
  403 page), while `/mcp` and `/` still get Access's refusal. Then run
181
170
  `connecta doctor --pool <pool>` with the agent's token.
@@ -185,11 +174,8 @@ Connecta answers there only an issued token of an agent listed in the pool,
185
174
  and gives every other spelling of the agent namespace a 404 before auth,
186
175
  because Access matches `/agent/*` after resolving dot segments, folding case,
187
176
  decoding unreserved escapes, and collapsing a leading `//`, while the Worker
188
- sees the raw path. Two cases were not tested: a human's `CF_Authorization`
189
- cookie on a bypassed path, whose `ctx.access` identity is unknown (connecta
190
- never reads it at `/agent/*`), and Custom Domains, which use the same
191
- documented hostname-and-path form but were not observed; only `workers.dev`
192
- was probed. Pinning the Worker-level application's AUD on human routes is
177
+ sees the raw path. Connecta never reads `ctx.access` at `/agent/*`.
178
+ Pinning the Worker-level application's AUD on human routes is
193
179
  recommended hardening that connecta does not yet do.
194
180
 
195
181
  ## Compose capabilities and access
@@ -221,9 +207,11 @@ existing-record migration.
221
207
 
222
208
  One deployment serves one tenant and may admit several people. Node uses Clerk
223
209
  for human auth; Access is Workers-only. With inbound auth, `maintainers` is
224
- required: only they reach plain `/mcp` and the operator UI. Everyone else
225
- reaches a pool that lists them in `members`: people at `/mcp/<pool>`, agents
226
- at `/agent/<pool>`. Maintainers alone administer shared credentials, manage
210
+ required: they receive the whole catalog at plain `/mcp` and the operator UI.
211
+ Everyone else reaches a pool that lists them in `members`: people at
212
+ `/mcp/<pool>`, agents at `/agent/<pool>`. A person in exactly one pool may also
213
+ use plain `/mcp`, which serves them that pool, so one client URL can be handed
214
+ to every person; a person in several pools must use `/mcp/<pool>`. Maintainers alone administer shared credentials, manage
227
215
  agent tokens, and read activity; members connect their own accounts on
228
216
  personal connectors in their pools.
229
217
  [Auth](./auth.md#principals-visibility-and-operators) owns maintainers and members;
@@ -314,22 +302,16 @@ Bump the exact package pin, read the [changelog](../CHANGELOG.md) for every
314
302
  intervening release, and apply the relevant migration before startup. `init`
315
303
  does not merge changes into an existing deployment.
316
304
 
305
+ Before upgrading past 0.37.1 with pre-0.29 state, follow
306
+ [Retired layout migrations](./migration-retired-layouts.md). Version 0.37.1 is
307
+ the last bridge for KV → D1, JSON → SQLite, and OAuth layout-2 conversions.
308
+
317
309
  For an existing Node deployment, add the Node entry's pinned optional peer
318
310
  before starting the upgraded package: `npm install --save-exact undici@6.29.1`.
319
311
  The current Node template already declares this dependency.
320
312
 
321
- For 0.35.0, serve Node with `listen` from `@zackbart/connecta/node`, as the
322
- current template does. Without `listen()`, a host serving `connecta.fetch` has
323
- no owned sender: every maintained-provider write, every reviewed POST read and
324
- every remote MCP write is refused before sending, although each worked in 0.34.1.
325
- Downstream OAuth token, registration and revocation POSTs also require that
326
- sender. With `listen()`, every maintained-provider request, reads included,
327
- remote MCP writes and those credential POSTs use the owned sender. Configure
328
- `listen(..., { proxy })` explicitly for HTTP(S) CONNECT proxy egress; ambient
329
- `NODE_USE_ENV_PROXY`, `HTTP_PROXY`, `HTTPS_PROXY` and host proxy dispatchers do
330
- not configure it. Remote MCP reads and OAuth discovery GETs retain native fetch
331
- and need their own host proxy policy. An uncertain refresh requires fresh
332
- authorization rather than replaying the spent token. See [Start on Node](#start-on-node).
313
+ For Node upgrades, retain the template's `listen` entrypoint and apply the
314
+ [Node transport and proxy requirements](#start-on-node).
333
315
 
334
316
  Authenticated MCP bodies now default to 4 MiB through
335
317
  `admission.requests.maxBodyBytes`; Node's separate pre-auth buffer also defaults
@@ -352,10 +334,10 @@ and [Eve agents](./meta-tools.md#eve-agents).
352
334
 
353
335
  - [Provider migration for 0.35](./provider-migration-0.35.md) collects provider upgrade actions for 0.35.0, including removal of Infisical, Notion, and Mixpanel factories.
354
336
  - [Migration to 0.34](./migration-0.34.md) covers the Overview landing page, Endpoints at `/endpoints`, default browser titles, and Workspace discovery in owner-less agent pools. No storage migration or token rotation is needed from 0.33.
337
+ - [Split mixed pools](./pool-membership-upgrade.md) before starting a deployment that lists people and agents together; mixed membership now throws at construction.
355
338
  - [Migration to 0.33](./migration-0.33.md) maps every removed access option to `maintainers`, pool `members`, and `agents`, moves agents to `/agent/<pool>`, and covers pool owners, token revisions, operator contract v2 with its operator route changes, and the pre-auth listen fix.
356
339
  - [Tokens issued before agents](./auth.md#agent-tokens-and-upgrading-from-earlier-tokens) stop admitting until each machine's token is bound once to a declared agent with `npm run adopt-token -- <token-id> <agent>` (Node) or `AccessTokenManager.adopt`, which keeps its secret. Human-bound tokens stay refused; issue those agents new tokens. Pool members name `"agent:<name>"` instead of matching token record ids.
357
- - [Node storage from 0.28](../templates/node/README.md#storage) uses `connecta migrate-state` to copy the JSON state into SQLite. Activity JSONL is not imported.
358
- - [Worker storage from 0.28](../examples/worker/README.md#upgrading-from-028) copies KV into D1 with a one-shot migration.
340
+ - [Retired layout migrations](./migration-retired-layouts.md) requires completing Node JSON → SQLite, Workers KV → D1, and every OAuth layout-2 grant conversion on 0.37.1 before upgrading. The new package no longer supplies those migrations.
359
341
  - [Worker deployments on Wrangler](../examples/worker/README.md#moving-a-wrangler-deployed-worker-to-alchemy) move to Alchemy once, adopting the Worker, D1 database, and Access applications so the script tag and Access AUD stay unchanged.
360
342
  - [Notion, Vercel, and Cloudflare for 0.29](./provider-migration-0.29.md) records historical defaults and retained API tool names.
361
343
  - [Executor wiring](./code-mode.md#deploy-time-capability) requires branded shipped executors or a self-managed custom executor.
@@ -57,6 +57,7 @@ const crm = remoteMcp("crm", {
57
57
  });
58
58
  const base = ["crm", "tickets"]; // the agent's lowest tier
59
59
  // connectors: [crm, tickets], agents: ["assistant"],
60
+ // Agent tiers list only agents; people use a separate pool sharing base.
60
61
  // pools: { assistant: { tools: base, members: ["agent:assistant"] } }
61
62
  ```
62
63
 
@@ -219,26 +220,11 @@ failure also reports an unknown outcome with no retry or wait advice; a proven
219
220
  unsent failure retains its own recovery advice. Connector `readOnly` remains
220
221
  optional and composes with endpoint policy.
221
222
 
222
- On Node, serve with `listen` from `@zackbart/connecta/node`: it installs the
223
- owned transport that prevents native fetch or a host-configured dispatcher
224
- from silently resending a remote MCP write. Without that transport, Node
225
- refuses remote writes before dispatch. A connector with its own `fetch` hands
226
- writes to that function instead, which owns their single attempt. Downstream OAuth token, registration and
227
- revocation POSTs also require the owned sender. Read calls and OAuth discovery
228
- GETs keep native fetch. Workers
229
- use their native fetch with manual redirects; transport failures after
223
+ On Node, follow the [Node transport and proxy requirements](./deploying.md#start-on-node).
224
+ A connector with its own `fetch` hands writes to that function, which owns their
225
+ single attempt. Workers use native fetch with manual redirects; failures after
230
226
  dispatch remain unknown and non-retryable.
231
227
 
232
- The owned sender connects directly by default. For HTTP(S) CONNECT proxy egress,
233
- configure `listen(connecta, { port: 3000, proxy: "http://proxy.example.com:8080" })`.
234
- This controls maintained-provider requests, remote MCP writes and OAuth
235
- credential POSTs; it ignores `NODE_USE_ENV_PROXY`, `HTTP_PROXY`, `HTTPS_PROXY`
236
- and host proxy dispatchers. Remote MCP reads and OAuth discovery GETs retain
237
- native fetch and need their own host proxy policy. An uncertain refresh requires
238
- fresh authorization rather than replaying the spent token. See
239
- [Node startup](./deploying.md#start-on-node) for the proxy URL, credentials and
240
- TLS trust rules.
241
-
242
228
  Remote catalogs evolve. Discovery, direct calls, and programs enforce the
243
229
  current annotations and `ConnectaConfig.classification` overrides. A leftover
244
230
  `classify` option throws at construction and names that map. Provider reviews,
@@ -282,6 +268,14 @@ which binds the grant to configured API origins and manages authentication.
282
268
  [Downstream OAuth on api()](./auth.md#downstream-oauth-on-api) owns a concrete
283
269
  HTTP example and the client setup contract.
284
270
 
271
+ Text a handler writes for the agent that names a Connecta route, such as "call
272
+ authorize_connector" in an auth error, uses `ctx.routes` (`ctx.routes.authorize`,
273
+ `ctx.routes.read`, `ctx.routes.write`, `ctx.routes.search`, `ctx.routes.program`,
274
+ `ctx.routes.skills`). Connecta sets it on every context it builds to the names of
275
+ the surface serving the call: the meta-tool names on the default surface, and
276
+ the native contract's wording on a native pool. It is optional in the type, so
277
+ fall back to the meta names for a context built elsewhere.
278
+
285
279
  Mark every side-effecting tool `readOnlyHint: false`. On a writable endpoint
286
280
  writes use `call_destructive_tool` or a program, within the caller's grants; a
287
281
  `readOnly` endpoint refuses them and omits them from search. Client approval is independent of
@@ -320,6 +314,11 @@ literal header credentials with personal scope. Review
320
314
  [management permissions](./auth.md#management-permissions) before exposing a
321
315
  new integration or its authentication controls.
322
316
 
317
+ Plain `/mcp` serves maintainers the whole catalog and every other person their
318
+ single pool, so one URL can serve the meta-tools to `alice` and a native pool's
319
+ tools to `bob`. After changing a pool's `tools`, `readOnly`, `surface` or
320
+ `executeCode`, check it as a member at plain `/mcp` as well as at `/mcp/<pool>`.
321
+
323
322
  After editing configuration, run the deployment's typecheck and doctor, then
324
323
  perform a bounded read. Add negative checks for target restrictions and writes
325
324
  when authoring a handler or provider. Follow repository verification policy
@@ -346,7 +345,7 @@ check shared behavior. Provider-specific tests remain beside each provider.
346
345
 
347
346
  Use [`@zackbart/connecta-eve`](https://github.com/zackbart/connecta/blob/main/packages/connecta-eve/README.md)
348
347
  in the Eve app for an app-owned connection and endpoint instructions refreshed
349
- once per turn. It requires Node 24 and Eve 0.84.x, and installs separately from
348
+ once per turn. It requires Node 24 and Eve 0.90.x, and installs separately from
350
349
  Connecta. Follow its README for the two agent files, token environment variables,
351
350
  optional write approval and Vercel OIDC token source. Configure the server's
352
351
  [agent and pool membership](./auth.md#issue-rotate-or-revoke-an-agent-token)
@@ -49,7 +49,12 @@ write tools from `search_tools` results and describe, and refuses every write
49
49
  call. Connector `readOnly` applies the same restriction on every endpoint; effective read-only is endpoint OR connector. Neither setting changes connector visibility or read grants. Guides
50
50
  (`queryAnalysis.guideSummary`, `skills`, `connecta.skill`, skill
51
51
  `resources/read`) are documentation and may name tools the endpoint refuses. The four tools with structured-only success results
52
- advertise `outputSchema`. The two direct-call tools omit it because their default
52
+ advertise `outputSchema`. `search_tools`'s schema defines its typed failure,
53
+ including every authorization-handoff field, once under `$defs.failure`;
54
+ `catalogErrors[]` references it through `allOf` with `connector` required, and
55
+ `queryAnalysis.catalogError` references it directly. The schema declares no
56
+ `$schema`, and validators for drafts 4, 7, 2019-09 and 2020-12 accept the same
57
+ pages. The two direct-call tools omit it because their default
53
58
  mode returns native `content`. Default-mode successes declare
54
59
  `_meta["dev.connecta/format"]: "json" | "text"`; value mode places the value in
55
60
  `data` and declares `format` in its complete result envelope.
@@ -134,16 +139,25 @@ Live connector probing is not a fourth: the operator pages and `/health` own it.
134
139
 
135
140
  ## Discovery context
136
141
 
137
- The deployment-derived `execute_code` description carries a live connector
138
- inventory before any catalog search — registry order, each canonical id, and a
139
- distinct configured title, with no second name minted for programs. Titles
140
- normalize whitespace and cap at 48 UTF-8 bytes, the complete line caps at 256,
141
- entries stay whole, and a truncated line ends with the exact `+N more` count, so
142
- account and environment hints cannot eat the inventory. It reads only the
143
- configured registry: no catalog load, no credential probe, no capability, and no
144
- replacement for canonical discovery or addressing. Program search results carry
145
- the same bounded `connectorTitle` per tool, so choosing an account or
146
- environment costs no provider read. The inventory does not probe live access.
142
+ The deployment-derived `execute_code` description carries a connector
143
+ inventory before any catalog search — registry order and each canonical id,
144
+ with no second name minted for programs. An entry adds the configured title
145
+ only when it says more than the id: a title whose words the id already spells,
146
+ such as `Google Calendar` for `google_calendar`, is dropped. Titles normalize
147
+ whitespace and cap at 48 UTF-8 bytes. A connector with a guide says how to
148
+ fetch it: `read first: skills({name:"connector:<id>"})` for a required guide,
149
+ `guide: skills({name:"connector:<id>"})` otherwise. For example:
150
+ `Connectors: crm (Customer CRM; read first: skills({name:"connector:crm"})), tickets.`
151
+
152
+ The complete line caps at 1,024 UTF-8 bytes, which names every connector of a
153
+ typical deployment. Past the cap, detail degrades before names do: titles are
154
+ dropped first, then optional guide pointers. Only when bare ids and required
155
+ guide pointers still exceed it do whole entries give way, in registry order, to
156
+ an exact `+N more` count. The inventory reads only the configured registry: no
157
+ catalog load, no credential probe, no live status, no capability, and no
158
+ replacement for canonical discovery or addressing. Search pages carry the same
159
+ bounded title once per connector in `connectors[<id>].title`, so choosing an
160
+ account or environment costs no provider read.
147
161
 
148
162
  Read-only lookup belongs in `connecta.search` inside the program; top-level
149
163
  `search_tools` stays for explicit catalog inspection and approval-required
@@ -171,6 +185,56 @@ read/write filter. On `approvalRequired` searches the returned discovery tool
171
185
  is still a read. Hidden tools, missing grants and read-only restrictions remain
172
186
  in force. Browsing does not consult operation vocabulary.
173
187
 
188
+ When a matched operation's call is fully determined, the first page also
189
+ carries it in `readyCalls`, so a program can call it without operation search or
190
+ details:
191
+
192
+ ```json
193
+ {
194
+ "readyCalls": [
195
+ {
196
+ "address": "notes.notes_api_read",
197
+ "args": { "path": "/v1/notes" },
198
+ "classification": "read",
199
+ "summary": "List notes"
200
+ }
201
+ ]
202
+ }
203
+ ```
204
+
205
+ Pass `args` unchanged to `connecta.call(address, args)` or `call_tool`. An
206
+ operation is fully determined only when it is a reviewed read served by the
207
+ hatch's `_api_read` tool itself (no named tool), uses GET, the read tool's
208
+ default method, and declares no path template, required parameter or required
209
+ body. Anything needing an id, a method choice or an argument the agent must
210
+ fill in stays behind `_api_search` and `_api_details`. Ready calls pass the same
211
+ endpoint, connector read-only, safety, hidden-tool and grant checks as the
212
+ vocabulary match, through a visible read tool in the caller's view. They are
213
+ computed offline from the construction-time index, read no account data, and
214
+ start no authorization.
215
+
216
+ A ready call must also be what the query names. Each path segment, and the
217
+ operationId without its action and version words, is one whole resource name:
218
+ `event_destinations` is the name "event destination", never "event". Every
219
+ query word other than an action (`list`, `get`, `count`, `show`, `create`,
220
+ `update`, `delete` and their vocabulary aliases) must belong to a resource name
221
+ whose words the query states in order, with singular/plural normalization and
222
+ the provider's declared synonyms. Version segments such as `v1` never count, and
223
+ a summary may supply only actions. A query with no object word gets no ready
224
+ call. Ready calls are omitted entirely when the top result is an exact tool name
225
+ or address, or a complete match on an authored tool, not the hatch's own search
226
+ or read tool, whose name adds nothing to the query: each of its name words is an
227
+ action or a query word, up to plural/singular and declared synonyms. For
228
+ example, `list teams` returns `list_teams` and no ready call, even when other
229
+ operations mention teams in their summaries, while `list projects` still offers
230
+ `/v10/projects` beside `list_project_env_vars`, whose name adds `env` and `vars`.
231
+ Each hatch offers only its top-ranked operation: a last path segment naming the
232
+ resource first, then the fewest resource words the query did not ask for, then
233
+ the shortest path. A connector offers at most one ready call and a page at most
234
+ three, in connector order. Later pages carry none. A ready
235
+ call keeps its read tool's `guideRequired` and `guideRequiredReasons`, and its
236
+ connector appears in `connectors`.
237
+
174
238
  Vocabulary is bounded: queries at most 200 characters and eight terms; each
175
239
  hatch caches at most 8,192 rows and 2,097,152 characters, with at most 1,024
176
240
  characters per row and 128 query intersections. Longer queries and vocabulary
@@ -281,9 +345,11 @@ guide's first meaningful body paragraph. `required: true` is reserved for
281
345
  generic API wrappers and cross-operation conventions a complete downstream
282
346
  schema cannot express.
283
347
 
284
- Search and describe results carry a `guide: "connector:<id>"` pointer and a
285
- `guideSummary`. A matching tool also carries `guideRequired: true` and
286
- `guideRequiredReasons` when Connecta can prove review is necessary:
348
+ Describe results carry a `guide: "connector:<id>"` pointer and a
349
+ `guideSummary`; a search page carries both once per connector in
350
+ `connectors[<id>]`. A matching tool row or description also carries
351
+ `guideRequired: true` and `guideRequiredReasons` when Connecta can prove review
352
+ is necessary:
287
353
 
288
354
  | Reason | Raised by | Survives exact schema expansion |
289
355
  | -------------------- | ------------------------------------ | -------------------------------------------------------- |
@@ -297,8 +363,17 @@ is merely told to fetch the guide first, for any reason listed. Otherwise the
297
363
  bounded summary decides — connector-specific sequencing, units, pagination,
298
364
  aliases, and generic API conventions still need the guide when they affect the
299
365
  task, while a complete and unambiguous one-read schema proceeds directly. Guide
300
- lookup always uses an exact name returned by `skills({})`, search, or describe;
301
- callers never manufacture `connector:<id>` from an unmarked connector.
366
+ lookup uses an exact name returned by `skills({})`, search, describe, or the
367
+ `execute_code` inventory; callers never manufacture `connector:<id>` from an
368
+ unmarked connector. A bare connector id, such as `skills({ name: "crm" })` or
369
+ `connecta.skill("crm")`, also serves that connector's guide. Built-in skill
370
+ names stay reserved: `usage` and `investigate` always fetch the built-in skill,
371
+ even when a connector has that id, whose guide stays at `connector:usage` or
372
+ `connector:investigate`. A bare id naming a connector without a guide fails with
373
+ `Connector "<id>" has no usage guide.`; any other unknown name fails as an
374
+ unknown skill. Names containing `:`, including URIs, never fall back to a bare
375
+ id. MCP `skills/get` resolves names the same way; `resources/read` accepts only
376
+ document URIs.
302
377
 
303
378
  A connector-scoped lexical miss keeps that connector's guide metadata under
304
379
  `queryAnalysis`. This matters for generic wrappers whose broad tool name
@@ -307,12 +382,29 @@ disappearing with the zero-tool page.
307
382
 
308
383
  Two built-in skills are byte-identical across deployments. `usage` is fetched
309
384
  only for missing guidance or program repair, and owns program selection detail, examples, runtime
310
- differences, and repair guidance; `investigate` is on-demand guidance for
311
- purchase verification, experiment checks, and customer or deployment
312
- investigations — shared guidance, not a saved workflow and not a source of
313
- deployment-specific ids. The always-loaded instructions and tool definitions keep
385
+ differences, and repair guidance; `investigate` is domain-neutral on-demand
386
+ guidance for an investigation that spans several services — shared guidance,
387
+ not a saved workflow and not a source of deployment-specific ids. `skills`
388
+ lists it; the `execute_code` description does not advertise it. The always-loaded instructions and tool definitions keep
314
389
  route selection, the fail-closed boundary, and the minimum guest syntax, so a
315
390
  client that never fetches a skill can still write a valid first program.
391
+
392
+ The server instructions state the complete routing rule once. Some clients
393
+ drop server instructions, so each tool description stays sufficient for its own
394
+ correct use without restating that rule: `call_tool` names its scope (one
395
+ known-address read) and points other read work to `execute_code`;
396
+ `search_tools` names its scope (catalog inspection, or finding a write) and
397
+ points read work into a program; `execute_code` opens with the terse
398
+ three-route lead (`One known read: call_tool. One known write:
399
+ call_destructive_tool. Everything else: execute_code.`, or the read-only
400
+ two-route form). The `usage` skill points to those two sources instead of
401
+ repeating them.
402
+
403
+ Tool descriptions and output schemas carry deployment configuration only: the
404
+ connector inventory, budgets, the endpoint's read-only setting, and whether any
405
+ visible connector has a guide. No catalog outcome, call, authorization state,
406
+ or other live status reaches `tools/list` or the instructions, so the
407
+ always-loaded surface stays byte-stable for a client's prompt cache.
316
408
  The instructions are the same on every endpoint except one addition: at
317
409
  `/mcp/<pool>` and `/agent/<pool>`, a pool that sets a
318
410
  [`description`](./auth.md#pools) has it appended after `This endpoint: `, so
@@ -528,8 +620,10 @@ The default cap sits under both lines with room to spare. Preview plus notice
528
620
  stays under 25,000 bytes, which is under 50,000 characters for any text, because
529
621
  a character is at least one UTF-8 byte, and under 25,000 tokens for any text,
530
622
  because a token covers at least one byte. A `connecta.result` page is bounded by the same
531
- inline byte cap, and 24,000 matches the program result boundary in
532
- [code mode](./code-mode.md). A deployment whose
623
+ inline byte cap, and 24,000 matches the default program result boundary in
624
+ [code mode](./code-mode.md) (`execute.maxResultChars`). The `maxBytes` in
625
+ `nextAction` is sized so one page fits that boundary under worst-case JSON
626
+ escaping, capped by the call's own inline cap: 3,829 bytes by default. A deployment whose
533
627
  clients take more can raise the cap, and one whose clients take less can lower
534
628
  it, per connector if need be; a result between 24,000 and 50,000 bytes that
535
629
  used to arrive whole now pages. A client that spills rather than rejects keeps
@@ -616,7 +710,7 @@ a fixed warning without storage error prose. Never repeat a write to recover out
616
710
  page. Select tool rows from `page.tools`.
617
711
  With the same schema format, schema-key option, and scoped registry, both paths
618
712
  return identical data. Top-level search omits schemas unless requested; programs
619
- default to JSON schemas and schema-key metadata.
713
+ default to TypeScript signatures and schema-key metadata.
620
714
 
621
715
  ```json
622
716
  {
@@ -625,12 +719,12 @@ default to JSON schemas and schema-key metadata.
625
719
  {
626
720
  "name": "get_ticket",
627
721
  "address": "tickets.get_ticket",
628
- "connectorTitle": "Tickets",
629
722
  "classification": "read",
630
723
  "schemaFormat": "json",
631
724
  "inputSchema": { "type": "object", "properties": { "id": { "type": "string" } }, "required": ["id"] }
632
725
  }
633
726
  ],
727
+ "connectors": { "tickets": { "title": "Tickets", "guide": "connector:tickets", "guideSummary": "Ticket ids and states." } },
634
728
  "total": 1,
635
729
  "offset": 0,
636
730
  "limit": 8,
@@ -650,8 +744,26 @@ and connector id, never connector-supplied error text. Other failures retain
650
744
  `retryAfterMs`. A scoped search also keeps its bounded failure subset under
651
745
  `queryAnalysis.catalogError` for clients that already use it.
652
746
 
653
- Tool rows carry the address, configured display title when present, guide and
654
- guide summary when available, and stored classification. Requested schemas have
747
+ An unscoped search gives each connector at most 5 seconds (or
748
+ `discovery.probeTimeoutMs` when shorter) and ends within a 9-second discovery
749
+ budget. A connector that recently timed out or failed slowly is skipped for up
750
+ to 60 seconds: its entry keeps the recorded code with `retryable: true`,
751
+ `retryAfterMs`, and the message `Connector "X" was skipped because its catalog
752
+ lookup failed recently (CODE). Search with connector "X" to try it now.` A
753
+ scoped search always probes. Records are kept per owner, admitted endpoint and
754
+ described connector configuration. A healthy connector whose catalog always
755
+ takes more than 5 seconds can stay out of unscoped search; search it with
756
+ `connector`, or let any longer-deadline operation warm its complete catalog.
757
+ See [Discovery health](./architecture.md#storage-credentials-and-connectors).
758
+
759
+ Tool rows carry the address, stored classification, and tool-specific
760
+ `guideRequired` and `guideRequiredReasons`. Facts shared by every row of one
761
+ connector appear once per page in `connectors`, keyed by the connector id that
762
+ prefixes each row's address: the configured display `title` when present and
763
+ `guide` and `guideSummary` when the connector has a guide. The map names only
764
+ connectors with a row or ready call on this page and is omitted when none has
765
+ such facts.
766
+ Requested schemas have
655
767
  `schemaFormat: "json"` for JSON Schema values or `schemaFormat: "text"` for
656
768
  compact schema strings and TypeScript signatures. Compact schemas are text;
657
769
  `inputSchema.properties` is meaningful only in JSON mode. Pagination counts
@@ -664,7 +776,19 @@ rare domain terms above ubiquitous action terms. Exact tool names and canonical
664
776
  addresses rank first, so `get_issue` beats `get_issue_status` even when a
665
777
  connector is named `issue`. Exact configured connector IDs, full display titles,
666
778
  and recognized service names rank next. Complete lexical matches then precede
667
- partial matches, with score and catalog order breaking ties.
779
+ partial matches. Within a tier, a query whose first word outside connector names
780
+ is `list`, `get`, `find`, `search`, `show`, `read`, `fetch`, or `count` ranks
781
+ read rows before write rows; writes stay on the page. Score and catalog order
782
+ break the remaining ties.
783
+
784
+ A maintained provider may declare `searchSynonyms`: groups of single lowercase
785
+ words its users and its vendor use for the same record, such as `household`
786
+ and `family` for Church Community Builder. A query word then matches that
787
+ provider's tools and operation vocabulary through any word in its group, as an
788
+ ordinary whole-token match ranked below the word itself. Synonyms never widen
789
+ another connector's matches. Church Community Builder declares
790
+ `person`/`individual` and `household`/`family`; Granola declares
791
+ `meeting`/`note`.
668
792
  An exact connector ID or title query browses that connector's tools, even when
669
793
  individual tool names or descriptions mention that identity. Connector identity
670
794
  terms that never occur in its tools need not be repeated in every tool description.
@@ -1025,28 +1149,77 @@ A remote MCP tool's advertised `inputSchema` is checked in the shared invocation
1025
1149
  path before admission and provider dispatch, so a mismatch is the non-retryable
1026
1150
  `invalid_args` identically on `call_tool`, `call_destructive_tool`,
1027
1151
  generated-code failures, and rejected promises. The error names the connector and
1028
- operation and carries `validation.issues`: JSON Pointer `path`, schema-keyword
1029
- `code`, and expected shape, with submitted values never copied into a finding. At
1030
- most three are returned, and `validation.truncated` says when more exist — the
1031
- same three-item bound describe's nearby-address list uses.
1152
+ operation and carries one recovery envelope, `validation`:
1153
+
1154
+ ```json
1155
+ {
1156
+ "issues": [
1157
+ { "path": "/limit", "expected": "at least 5", "received": "4" },
1158
+ { "path": "/mode", "expected": "one of \"fast\", \"safe\"", "received": "string" },
1159
+ { "path": "/<key>", "expected": "one of the accepted keys: mode, limit" }
1160
+ ],
1161
+ "acceptedKeys": ["mode", "limit"],
1162
+ "example": { "mode": "fast", "limit": 5 }
1163
+ }
1164
+ ```
1165
+
1166
+ Every finding is listed, and the message repeats them, so one corrected call
1167
+ fixes them all.
1168
+
1169
+ A refusal contains only names and literals the published schema declares at
1170
+ non-sensitive positions, numbers allowed by the bound rule below, and fixed
1171
+ placeholder text. It never contains a submitted string or object key, in the
1172
+ message, a finding, `acceptedKeys` or the example. A position is sensitive when
1173
+ it or an ancestor is `writeOnly`; a named tool's input schema marks each request
1174
+ secret that way. API hatch findings name only the operation contract's
1175
+ properties and enum members, as `*_api_details` publishes them.
1176
+
1177
+ - `path` is a JSON Pointer naming declared properties and array indices. The
1178
+ first key the schema does not declare (a misspelled argument, a map key)
1179
+ reads `<key>` and ends the path, e.g. `/headers/<key>`.
1180
+ - `expected` states the constraint that failed, then every other constraint
1181
+ the position declares: bounds, property and item counts, `multipleOf`,
1182
+ `pattern`, `format`, `uniqueItems`, enum members, `dependentRequired`
1183
+ (`string (required with tags)`). A sensitive position never renders an enum,
1184
+ `const` or pattern literal. Each position gets one finding.
1185
+ - `received` is the submitted JSON type, `missing`, or, for a numeric bound at
1186
+ a non-sensitive position of a named tool, the submitted number.
1187
+
1188
+ At most eight findings are returned, each field clipped (path and expected 256
1189
+ characters, received 64), and `truncated: true` says something was omitted or
1190
+ clipped.
1191
+
1192
+ `example` is one valid call synthesized from the published schema and checked
1193
+ against the same validator. It never copies a submitted value, and a sensitive
1194
+ position never renders a schema literal (`enum`, `const`, `default`,
1195
+ `examples`). Complex or unsatisfiable schemas get `exampleUnavailable` instead.
1196
+ Synthesis detects recursive references and shares a 128-node work budget.
1197
+ Invocation uses only this schema example, never a connector's copy of the
1198
+ call, unless the connector states `exampleUnavailable`. API hatches
1199
+ (`*_api_read`, `*_api_write`) always do: their generic schema says nothing
1200
+ about an operation's arguments, so the reason points to the operation's
1201
+ `*_api_details`.
1202
+
1203
+ `acceptedKeys` names the tool's top-level arguments. `conditionalRequirements`
1204
+ lists known `dependentRequired` and `dependentSchemas` requirements by
1205
+ property name; `if`/`then`/`else` conditions are schema subtrees and are not
1206
+ rendered. The envelope has a 4 KiB budget:
1207
+ optional detail, then the example, then trailing findings are dropped whole,
1208
+ with `truncated: true`. A `repair` object a custom connector passes contributes
1209
+ its received types and schema facts. A custom connector's own findings must
1210
+ follow the same rule. Operator records contain only checked failure facts,
1211
+ never findings or examples.
1032
1212
 
1033
1213
  `nextAction` points to discovery scoped to the same connector and tool name when
1034
1214
  the compact schema is needed, routed like any other miss: a program to
1035
1215
  `connecta.search`, a top-level call to `search_tools`. `retry` says to correct
1036
- the listed arguments and reissue the original operation. Which keyword a finding
1037
- names, and what the local validator declines to evaluate, is
1038
- [code mode](./code-mode.md#errors)'s `E8`.
1039
-
1040
- `repair` adds agent-only detail from that tool's published schema: accepted
1041
- keys, received JSON types, enum values, numeric/length/item bounds, and known
1042
- conditional requirements such as `dependentRequired` and `if`/`then`/`else`.
1043
- Its `example` is synthesized without submitted values and checked against the
1044
- same validator. Complex or unsatisfiable schemas get `exampleUnavailable`
1045
- rather than an invalid example. Synthesis detects recursive references and
1046
- shares a 128-node work budget. Alternative branches do not emit dependency
1047
- advice unless the branch condition is known. Repair detail has a 4 KiB budget; oversized
1048
- detail is withheld whole with `truncated: true`. Operator records still contain
1049
- only checked failure facts, never findings or examples.
1216
+ the listed arguments and reissue the original operation. What the local
1217
+ validator declines to evaluate is [code mode](./code-mode.md#errors)'s `E8`.
1218
+
1219
+ Earlier releases returned `validation.issues[].code` (the schema keyword)
1220
+ and a separate `repair` object. Both are gone from responses; a custom
1221
+ connector may still pass `code` and `repair` to `ConnectorCallError`, which
1222
+ folds `repair` into `validation`.
1050
1223
 
1051
1224
  Unknown-address errors list `configuredConnectors` from the current endpoint's
1052
1225
  scoped registry, including an empty list when none are accessible. They never
@@ -1144,7 +1317,9 @@ its code and message for diagnostics but is `retryable: false` with
1144
1317
  a program's `writes.unknown`. An HTTP refusal of the call's own write stays
1145
1318
  known only under the refusal rules above and a complete ledger proving the
1146
1319
  refusal covers the whole call; its recovery advice and program accounting both
1147
- report failure. Partial or unknown ledgers remain conservative. An auth failure after entering a write with
1320
+ report failure. A `remoteMcp()` write answered with a 4xx (other than 401, 403,
1321
+ 408 or 429) whose body is not a JSON-RPC error is unknown, never a refusal: a
1322
+ gateway may have answered after the server acted. Partial or unknown ledgers remain conservative. An auth failure after entering a write with
1148
1323
  no call-owned dispatch reports an unknown outcome and requires reconciliation,
1149
1324
  including credential refusals, static-OAuth refresh failures, and `remoteMcp()`
1150
1325
  connect or initialize 401s before `tools/call`. A non-auth refusal before
@@ -1309,7 +1484,7 @@ window, a native `tools/list` carries a 300-second private cache hint, against
1309
1484
  an hour on meta endpoints. This hint does not guarantee that a host refreshes
1310
1485
  its cached annotations; the stale-approval race is accepted on writable pools.
1311
1486
 
1312
- **Listing.** `tools/list` is one page with no cursor (Eve 0.84 reads only the
1487
+ **Listing.** `tools/list` is one page with no cursor (Eve 0.90 reads only the
1313
1488
  first page). A pool listing more than 1,000 tools fails `tools/list` with a
1314
1489
  JSON-RPC error asking the operator to narrow the pool; nothing is silently
1315
1490
  dropped. A connector whose catalog cannot load is left out of that listing
@@ -1326,7 +1501,8 @@ never repeat a write; page a truncated result as its notice says. A pool
1326
1501
  **Recovery.** Repair and validation fields retain the exact constraint details.
1327
1502
  Discovery `nextAction` points to the host's tool search (`host: "search"`), with
1328
1503
  only a `query` argument. Retry actions and retry text name the native tool,
1329
- never a meta wrapper. Auth actions carry `authorizationUrl` or `operatorUrl`;
1504
+ never a meta wrapper; a program on a native pool sees the same native wording
1505
+ in a failure it catches. An unknown name is reported as sent. Auth actions carry `authorizationUrl` or `operatorUrl`;
1330
1506
  give the returned URL and instructions to the user or operator. Capable modern
1331
1507
  clients may resume native auth and downstream form/URL input with the same
1332
1508
  wire name, arguments and returned `requestState`. Changed names, targets or
@@ -1380,8 +1556,6 @@ listing, and call translation live in
1380
1556
  [search tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-search.test.ts),
1381
1557
  [call tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-call.test.ts),
1382
1558
  [skills tests](https://github.com/zackbart/connecta/blob/main/test/skills-extension.test.ts),
1383
- [native surface tests](https://github.com/zackbart/connecta/blob/main/test/native-tools.test.ts),
1384
- [surface tests](https://github.com/zackbart/connecta/blob/main/test/code-first-surface.test.ts),
1385
- and [pool description tests](https://github.com/zackbart/connecta/blob/main/test/pool-description.test.ts)
1559
+ and [native surface tests](https://github.com/zackbart/connecta/blob/main/test/native-tools.test.ts)
1386
1560
  check the published contract. Guest parity and clause evidence are in
1387
1561
  [code-mode verification](./code-mode.md#verification).