@zackbart/connecta 0.24.3 → 0.24.4

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 (67) hide show
  1. package/AGENTS.md +18 -20
  2. package/CHANGELOG.md +64 -1
  3. package/README.md +5 -6
  4. package/dist/branding.d.ts +31 -2
  5. package/dist/branding.js +116 -8
  6. package/dist/connectors/api.d.ts +1 -1
  7. package/dist/connectors/api.js +10 -2
  8. package/dist/connectors/guarded-fetch.d.ts +5 -1
  9. package/dist/connectors/guarded-fetch.js +34 -4
  10. package/dist/connectors/remote-mcp.js +8 -4
  11. package/dist/errors.d.ts +11 -3
  12. package/dist/errors.js +2 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +12 -1
  15. package/dist/meta-tools.js +105 -29
  16. package/dist/operator-ui/generated.js +2 -2
  17. package/dist/operator-ui/view.d.ts +38 -1
  18. package/dist/operator-ui/view.js +71 -0
  19. package/dist/providers/cloudflare.d.ts +14 -2
  20. package/dist/providers/cloudflare.js +107 -16
  21. package/dist/providers/linear.d.ts +26 -4
  22. package/dist/providers/linear.js +19 -4
  23. package/dist/providers/mixpanel.d.ts +16 -3
  24. package/dist/providers/mixpanel.js +13 -2
  25. package/dist/providers/notion.d.ts +8 -1
  26. package/dist/providers/notion.js +83 -10
  27. package/dist/providers/revenuecat.d.ts +30 -4
  28. package/dist/providers/revenuecat.js +42 -4
  29. package/dist/providers/stripe.d.ts +7 -1
  30. package/dist/providers/stripe.js +30 -4
  31. package/dist/providers/vercel.js +11 -1
  32. package/dist/registry.d.ts +12 -4
  33. package/dist/registry.js +22 -8
  34. package/dist/types.d.ts +37 -0
  35. package/dist/ui.js +18 -10
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. package/documentation/architecture.md +193 -181
  39. package/documentation/auth.md +197 -176
  40. package/documentation/code-mode.md +426 -321
  41. package/documentation/meta-tools.md +356 -416
  42. package/examples/worker/AGENTS.md +2 -1
  43. package/examples/worker/README.md +12 -10
  44. package/examples/worker/src/index.ts +12 -15
  45. package/package.json +1 -2
  46. package/templates/node/.env.example +3 -3
  47. package/templates/node/AGENTS.md +5 -4
  48. package/templates/node/README.md +2 -1
  49. package/templates/node/package.json +1 -1
  50. package/templates/node/src/index.ts +23 -22
  51. package/documentation/call-admission.md +0 -158
  52. package/documentation/cloudflare.md +0 -471
  53. package/documentation/connector-guides.md +0 -176
  54. package/documentation/connectors.md +0 -431
  55. package/documentation/linear.md +0 -193
  56. package/documentation/mixpanel.md +0 -160
  57. package/documentation/notion.md +0 -308
  58. package/documentation/operations.md +0 -359
  59. package/documentation/operator-ui.md +0 -135
  60. package/documentation/optional-modules-upgrade.md +0 -243
  61. package/documentation/provider-conventions.md +0 -729
  62. package/documentation/request-admission.md +0 -204
  63. package/documentation/revenuecat.md +0 -305
  64. package/documentation/storage-and-credentials.md +0 -254
  65. package/documentation/stripe.md +0 -262
  66. package/documentation/upgrading.md +0 -768
  67. package/documentation/vercel.md +0 -241
@@ -2,56 +2,74 @@
2
2
 
3
3
  Connecta keeps one small tool surface in model context and resolves downstream
4
4
  tools behind it. `search_tools` finds addresses, the call tools enforce safety
5
- annotations, and `get_result` pages bounded results.
6
-
7
- ## The deployment surface
8
-
9
- Every deployment requires an executor and `tools/list` is exactly seven:
10
- `execute_code`, `search_tools`, `call_tool`, `call_destructive_tool`,
11
- `authorize_connector`, `get_result`, and `skills`. Discovery uses `connecta.search` and `connecta.describe`; programs compose
12
- calls with JavaScript promises ([#273](https://github.com/zackbart/connecta/issues/273)).
13
-
14
- Code-first is what a model sees. Read-only work has two routes: `call_tool` for
15
- one known address, and `execute_code` when discovery or any wider work is
16
- needed. Real hosted catalogs reversed the earlier synthetic result that made a
17
- top-level cold search look cheaper. Keeping discovery inside the program avoids
18
- returning every candidate schema to the model and removes a model round trip.
19
- The [guest API contract](./code-mode.md) is what a program is promised.
20
-
21
- The route is chosen before discovery. An unknown address, a result that will be
22
- reduced, a call whose arguments depend on an earlier result, or work with
23
- multiple operations starts with `execute_code` and keeps discovery, calls, and reduction inside it
24
- when the schemas and result shapes suffice. An unfamiliar provider result may
25
- return a small sample for inspection before continuing in another call. This
26
- exception avoids repeated guesses at text formats or collection roots; it does
27
- not restore a mandatory discovery-only round trip. Distinct operations get distinct short
28
- `connecta.search` queries in that program. A known address needs only
29
- `call_tool`.
30
-
31
- That routing is about read-only work, because that is the only work a program
32
- can do. Anything unannotated, write-capable, or destructive is inadmissible
33
- inside the sandbox, so multi-step destructive work discovers at the top level
34
- and runs each step through `call_destructive_tool` where the host can put the
35
- question to a human. Telling an agent never to search at the top level for
36
- multiple calls would close the only route that work has
37
- ([#295](https://github.com/zackbart/connecta/issues/295)).
38
-
39
- `execute_code` accepts optional `diagnostics: true` when a caller is measuring
40
- a workflow. It adds only compact request-local timing and serialized-size
41
- aggregates; normal calls carry no diagnostics block or response-context cost.
42
- The measurements never contain program source, arguments, values, addresses,
5
+ annotations, `execute_code` runs read-only work as a program, and `get_result`
6
+ pages bounded results.
7
+
8
+ This guide is the contract an MCP client sees. The in-program `connecta.*` API
9
+ those tools imply belongs to [code mode](./code-mode.md); inbound identity and
10
+ credential administration belong to [auth](./auth.md).
11
+
12
+ ## The seven tools
13
+
14
+ Every deployment requires an executor, so `tools/list` is exactly seven. No
15
+ configuration adds an eighth or removes one.
16
+
17
+ | Tool | Arguments | Returns |
18
+ | --- | --- | --- |
19
+ | `execute_code` | `code`, `diagnostics?` | the program's reduced return value, plus a `diagnostics` block when asked |
20
+ | `search_tools` | `query?`, `connector?`, `safety?`, `limit?`, `offset?`, `fullDescriptions?`, `includeSchemas?: "compact" \| "json"` | `{ connectors: [{ id, tools }], total, offset, limit, hasMore }`, plus `queryAnalysis` on a partial or failed search |
21
+ | `call_tool` | `address`, `args?`, `resultMode?: "mcp" \| "value"`, `timeoutMs?`, `diagnostics?` | the downstream result, bounded as [result representation](#result-representation) describes |
22
+ | `call_destructive_tool` | the same, plus `reason?` | the same |
23
+ | `authorize_connector` | `connector`, `force?` | the class-specific handoff in [authorization recovery](#authorization-recovery) |
24
+ | `get_result` | `id`, `offset?`, `maxBytes?` | `{ offset, nextOffset?, totalBytes, text }` |
25
+ | `skills` | `name?` | the listing when `name` is absent, that skill's markdown when it is present |
26
+
27
+ `limit` defaults to 8 and is capped at 100, as is one `connecta.describe` batch.
28
+ `get_result.offset` is a whole number of bytes 0 defaulting to 0 and
29
+ `maxBytes` a whole number ≥ 1 defaulting to the deployment result cap — itself
30
+ 50,000 bytes unless `calls.maxResultBytes` or a per-connector override says
31
+ otherwise. Both are validated rather than clamped: a bad value is an input
32
+ error. `reason` is at most 500 characters of context for the host's human
33
+ approval view; Connecta neither treats it as authority nor sends it downstream,
34
+ and an empty or whitespace-only one reads as no reason rather than as grounds to
35
+ refuse a consequential call.
36
+
37
+ `diagnostics: true` adds compact request-local timing and serialized-size
38
+ aggregates for a caller measuring a workflow: a `diagnostics` block from
39
+ `execute_code`, a `timing` block on a call response that is already structured
40
+ (value mode, or a failure carrying recovery). Normal calls pay nothing for it,
41
+ and the measurements never contain program source, arguments, values, addresses,
43
42
  credentials, logs, or raw error text.
44
43
 
45
- Nothing became unreachable. `connecta.describe` takes the same addresses and
46
- formats as the internal catalog service, ordinary promises compose read-only
47
- calls, and an unfiltered
48
- `connecta.search({})` browses every catalog a program can reach. Live connector
49
- probing is an operator concern: the operator pages and `/health` own it.
50
-
51
- Program search includes a bounded `connectorTitle` on each tool when configured, so choosing an account or environment does not require a provider read. It is context, not a ranking input or proof of live access.
52
-
53
- The three discovery routes use deliberately different envelopes. These are
54
- their smallest successful one-tool shapes:
44
+ Connecta's own tools carry the annotations it demands of downstream tools:
45
+ read-only hints on all but `authorize_connector`, which mutates stored auth
46
+ state, and `call_destructive_tool`. Otherwise a host that gates on annotations
47
+ would prompt for every search, and a connecta aggregated behind another connecta
48
+ would be refused by its own policy.
49
+
50
+ ## Routing between the call surfaces
51
+
52
+ The route is chosen before discovery, and read-only work has exactly two:
53
+ `call_tool` for one known address, `execute_code` for everything wider — an
54
+ unknown address, a result that will be reduced, a call whose arguments depend on
55
+ an earlier result, or several operations. A program keeps discovery, calls, and
56
+ reduction together when the schemas and result shapes suffice, and gives each
57
+ distinct operation its own short `connecta.search` query. That is cheaper than it
58
+ looks: discovery inside the program returns no candidate schema to the model and
59
+ costs no round trip. One exception — an unfamiliar provider result may come back
60
+ as a small sample for inspection before continuing in another call, which avoids
61
+ repeated guesses at text formats and collection roots without restoring a
62
+ mandatory discovery-only round trip.
63
+
64
+ That covers read-only work, because that is the only work a program can do.
65
+ Anything unannotated, write-capable, or destructive is inadmissible inside the
66
+ sandbox, so multi-step destructive work discovers at the top level and runs each
67
+ step through `call_destructive_tool`, where the host can put the question to a
68
+ human. Telling an agent never to search at the top level for multiple calls
69
+ would close the only route that work has.
70
+
71
+ The three discovery routes use deliberately different envelopes. These are their
72
+ smallest successful one-tool shapes:
55
73
 
56
74
  ```js
57
75
  // Top-level search_tools
@@ -62,295 +80,251 @@ their smallest successful one-tool shapes:
62
80
  { tools: [{ name: "get_run", address: "ci.get_run", inputSchema: "{ runId: integer }" }] } // connecta.describe
63
81
  ```
64
82
 
83
+ Live connector probing is not a fourth: the operator pages and `/health` own it.
84
+
65
85
  ## Discovery context
66
86
 
67
- The deployment-derived `execute_code` description includes a live connector
68
- inventory before any catalog search. It preserves registry order and uses each
69
- canonical id and a distinct configured title without generating a second name for programs. Titles normalize whitespace and are capped at 48 UTF-8 bytes, so account and environment hints cannot consume the entire inventory.
70
- The complete inventory line is capped at 256 UTF-8 bytes. Entries stay whole,
71
- and a truncated line ends with the exact `+N more` count. This reads only the
72
- configured registry: it loads no catalog, probes no credential, grants no
73
- capability, and does not replace canonical discovery or addressing.
74
-
75
- Start a lookup with two to four distinctive action/object terms, not the full
76
- request. Read-only lookup belongs in `connecta.search` inside the program.
77
- Top-level `search_tools` remains available for explicit catalog inspection and
78
- approval-required discovery. Omit `limit` initially so the default
79
- eight-result page stays small. When the integration is obvious, set
80
- `connector` to its id: a scoped search loads that catalog alone, while an
81
- unscoped search must fan out across every configured connector. Leave the
82
- search unscoped when the right integration is genuinely ambiguous. Set
83
- `safety: "readOnly"` for generated code; `safety: "approvalRequired"` finds the
84
- complementary set that must cross `call_destructive_tool`. Omitting `safety`,
85
- or setting it to `"all"`, preserves the complete configured catalog. This is
86
- only a discovery filter: it neither grants authority nor changes invocation admission.
87
+ The deployment-derived `execute_code` description carries a live connector
88
+ inventory before any catalog search registry order, each canonical id, and a
89
+ distinct configured title, with no second name minted for programs. Titles
90
+ normalize whitespace and cap at 48 UTF-8 bytes, the complete line caps at 256,
91
+ entries stay whole, and a truncated line ends with the exact `+N more` count, so
92
+ account and environment hints cannot eat the inventory. It reads only the
93
+ configured registry: no catalog load, no credential probe, no capability, and no
94
+ replacement for canonical discovery or addressing. Program search results carry
95
+ the same bounded `connectorTitle` per tool, so choosing an account or
96
+ environment costs no provider read. Context, not a ranking input and not proof
97
+ of live access.
98
+
99
+ Read-only lookup belongs in `connecta.search` inside the program; top-level
100
+ `search_tools` stays for explicit catalog inspection and approval-required
101
+ discovery. Both take the same arguments.
102
+
103
+ | Argument | What it does |
104
+ | --- | --- |
105
+ | `query` | two to four action/object terms; empty or whitespace-only browses |
106
+ | `connector` | scopes to one id, loading that catalog alone instead of fanning out across every configured connector. Set it when the integration is obvious, omit it when the right one is genuinely ambiguous |
107
+ | `safety` | `"readOnly"` for what generated code may call, `"approvalRequired"` for the complementary set that must cross `call_destructive_tool`, omitted or `"all"` for the complete configured catalog |
108
+ | `limit` / `offset` | page the ranked results; omit `limit` initially so the default eight-result page stays small |
109
+ | `includeSchemas` | `"compact"` for the rendered routing view, `"json"` for the exact schema |
110
+ | `fullDescriptions` | unabridged tool purposes, at the obvious cost |
111
+
112
+ Neither `connector` nor `safety` grants authority or changes invocation
113
+ admission; they select what discovery shows and nothing else.
114
+
87
115
  `includeSchemas: "compact"` adds each match's input and any provider-declared
88
- output shape. When the provider declared none but an earlier successful call
89
- learned one, the same field carries the open observed schema beside
90
- `outputSchemaSource: "observed"`. That marker matters: observed fields and broad
91
- JSON types are routing evidence, not a provider contract, and every object field
92
- remains optional and open to unseen names. A provider declaration always wins.
93
- Bounded plain-object schemas also expose `inputKeys`,
94
- `requiredInputKeys`, and `outputKeys`; a zero-input object keeps
95
- `requiredInputKeys: []`, while an output object with no declared properties
96
- omits `outputKeys`. A truncated shape omits its corresponding list rather than
97
- repeating a large partial inventory. Matches carry declared
98
- behavior annotations. Lexical rank is only one signal: select a candidate whose
99
- required inputs are available, whose schema is complete enough for the call,
100
- and whose safety and available outputs fit the work. A reducer uses `outputKeys`
101
- before inspecting the value; it does not assume a collection is named `items`
102
- or `results`. When that shape is sufficient, call the returned address directly. Reserve schema
103
- expansion through `connecta.describe` for a search without schemas, an
104
- ambiguous compact shape, or exact
105
- constraints that require `format: "json"`.
106
-
107
- Observed schemas originate no provider traffic. A successful explicitly
108
- read-only call the user already made contributes names and broad types after
109
- Connecta unwraps the result. Arguments, scalar values, raw results, code,
110
- credentials, and errors are not retained, though property names may themselves
111
- be user-authored. Shapes merge in a 256-entry runtime cache for 24 hours under
112
- the exact tool definition that produced them. A changed definition, process
113
- restart, or Worker isolate eviction starts cold. Observation cannot fail the
114
- call, and the declared catalog remains the fallback.
115
-
116
- Compact search is deliberately a routing view, not a second copy of connector
117
- documentation. Tool purposes are capped at 160 characters, connector
118
- descriptions and property prose are omitted, required input fields render
119
- before optional ones, and each input or output shape is capped at 1,024 UTF-8
120
- bytes. Within that unchanged total, each enum node and each constraint
121
- annotation may spend at most 256 UTF-8 bytes. Numeric bounds, string length
122
- bounds, patterns, and formats render beside their type. A constraint that does
123
- not fit is dropped whole. If constraints push the full shape over 1,024 bytes,
124
- search retries the shape without them. Compact describe keeps declared
125
- constraints and property prose within its own 8,192-byte shape cap, sharing
126
- search's 2,000-visit rendering budget; a capped shape sets
127
- `inputSchemaTruncated` or `outputSchemaTruncated`, and `format: "json"` or
128
- JSON search returns the exact schema. A large enum keeps the longest whole-value prefix that fits, then
129
- adds `unknown` and a comment with the exact omitted-value count. An empty enum
130
- renders as the valid `never` type. A capped object becomes a valid
131
- required-first shape with `unknown` types; other shapes become
132
- `unknown /* truncated */`. Any cap marks the match with
133
- `inputSchemaTruncated` or `outputSchemaTruncated`; repeat the search with
134
- `includeSchemas: "json"` or use the existing describe path when exact
135
- constraints matter. `prefixItems` renders as a tuple, with the `items` type
136
- as its rest, an `unknown[]` rest when open, and no rest for `items: false`.
137
- `dependentSchemas` and `if`/`then`/`else` preserve the base shape and append
138
- `/* conditional */`, setting the truncation flag so the caller reads the exact
139
- JSON schema. `$dynamicRef` resolves a same-named definition like `$ref`; an
140
- unresolved dynamic reference renders as `unknown` with the truncation flag.
141
- These shapes share the same byte and work budgets. Small enums and both exact
142
- paths remain complete.
143
-
144
- ## Connector guide selection
145
-
146
- A connector may attach a deployment-owned guide as markdown, preserving the
116
+ output shape, plus `inputKeys`, `requiredInputKeys`, and `outputKeys` for
117
+ bounded plain objects. Where the provider declared no output shape but an
118
+ earlier successful call learned one, the same field carries that open observed
119
+ schema beside `outputSchemaSource: "observed"`. The marker is load-bearing:
120
+ observed names and broad types are routing evidence, never a provider contract,
121
+ and a provider declaration always wins. Observation originates no provider
122
+ traffic and cannot fail a call; the mechanism and its bounds are
123
+ [code mode](./code-mode.md#connectasearch)'s `S9`, and this surface only labels
124
+ what it returns.
125
+
126
+ Lexical rank is one signal among several: pick a candidate whose required inputs
127
+ are available, whose schema is complete enough for the call, and whose safety
128
+ and outputs fit the work. When that shape suffices, call the returned address
129
+ directly; reserve `connecta.describe` for a search without schemas, an ambiguous
130
+ compact shape, or exact constraints that need `format: "json"`.
131
+
132
+ Compact search is a routing view, not a second copy of connector documentation,
133
+ so it spends bytes on shape and none on prose: tool purposes cap at 160
134
+ characters, connector descriptions and property prose are dropped, and required
135
+ input fields render before optional ones. Both surfaces share one renderer, so
136
+ its byte and work budgets, its truncation renderings, and its handling of enums,
137
+ tuples, and conditional keywords are documented once under
138
+ [`connecta.search`](./code-mode.md#connectasearch) and
139
+ [`connecta.describe`](./code-mode.md#connectadescribe). The part a client must
140
+ act on is the flag: any cap sets `inputSchemaTruncated` or
141
+ `outputSchemaTruncated`, and that means repeat with `includeSchemas: "json"`, or
142
+ describe, when the exact constraints matter.
143
+
144
+ ## Connector guides and skills
145
+
146
+ A connector may attach a deployment-owned guide as markdown, keeping the
147
147
  original `usageGuide: string` configuration, or as
148
- `{ content, summary?, required? }`. The structured form does not register a
149
- connector or create a shared runtime template. `content` remains the markdown
150
- returned verbatim by `skills`; `summary` is normalized and refuses construction
151
- when it exceeds 120 characters. When it is absent, Connecta derives the same
152
- bounded fallback used by the skills listing: the first meaningful body
153
- paragraph, joined across physical Markdown line wraps and shortened at a
154
- sentence, clause, or word boundary, with a heading used only when the guide has
155
- no body. `required: true` is reserved for generic API wrappers and
156
- cross-operation conventions a complete downstream schema cannot express.
157
-
158
- Search and describe results keep the existing `guide: "connector:<id>"`
159
- pointer and add `guideSummary`. A matching tool also carries
160
- `guideRequired: true` and `guideRequiredReasons` when Connecta can prove review
161
- is necessary:
162
- `connector_required` for the explicit configuration above,
163
- `approval_required` for an unannotated or write-capable tool, and
164
- `schema_truncated` when a requested compact input or output shape was capped.
165
- The boolean is an instruction, not a server-side gate — nothing refuses the
166
- call, so the agent is told to fetch the guide before making it, for any reason
167
- listed. `connector_required` and `approval_required` survive exact schema
168
- expansion; `schema_truncated` is cleared by the describe that returns the exact
169
- shape, and describe reports whatever reasons remain in the same two fields.
170
- Otherwise it reads the
171
- bounded summary: connector-specific sequencing, units, pagination, aliases,
172
- and generic API conventions still require the guide when they affect the task,
173
- while a complete and unambiguous one-read schema proceeds directly.
174
- Guide lookup always uses an exact name returned by `skills({})`, search, or
175
- describe; callers do not manufacture `connector:<id>` from an unmarked
176
- connector.
177
-
178
- A connector-scoped lexical miss retains that connector's guide metadata under
179
- `queryAnalysis`. This matters for generic wrappers whose broad tool name does
180
- not contain endpoint vocabulary: a required guide remains discoverable before
181
- the caller falls back to an empty-query browse, rather than disappearing with
182
- the zero-tool page.
183
-
184
- The built-in `usage` skill is byte-identical across deployments and says to
185
- read it at most once per task. Connector guides remain scoped to the deployment
186
- that listed them, even when two deployments happen to use identical content.
187
- The always-loaded instructions and seven tool definitions own route selection,
188
- the fail-closed boundary, and the minimum guest syntax. The usage skill owns
189
- program selection detail, examples, runtime differences, and repair guidance.
190
- This split avoids two normative copies while preserving a valid first program
191
- for clients that never fetch the skill. Deployments without connector guides
192
- receive none of the short conditional guide pointers in their definitions.
193
-
194
- ## Task guidance
195
-
196
- `skills({ name: "investigate" })` provides on-demand guidance for purchase
197
- verification, experiment checks, and customer or deployment investigations.
198
- The execute description points to it when planning is unclear; routine reads need no additional guide fetch. It explains how to
199
- resolve app/account/environment, follow evidence across services, establish
200
- capability limits, and stop with a clear answer or a specific gap. It is shared
201
- guidance, not a saved workflow or a source of deployment-specific ids. Existing
202
- connector titles, purposes, and guides still own those distinctions.
203
-
204
- The usage skill keeps the executable mechanics. Its dependent-call example
205
- searches each operation separately, uses the page's `tools` array and canonical
206
- addresses, and reports unresolved evidence instead of inventing an address or
207
- querying another account. Its source runs against local fixtures in the QuickJS
208
- suite, including missing and approval-required evidence.
148
+ `{ content, summary?, required? }` which registers no connector and creates no
149
+ shared runtime template. `content` is the markdown `skills` returns verbatim.
150
+ `summary` is normalized and refuses construction over 120 characters; absent,
151
+ Connecta derives the same bounded fallback the skills listing uses from the
152
+ guide's first meaningful body paragraph. `required: true` is reserved for
153
+ generic API wrappers and cross-operation conventions a complete downstream
154
+ schema cannot express.
155
+
156
+ Search and describe results carry a `guide: "connector:<id>"` pointer and a
157
+ `guideSummary`. A matching tool also carries `guideRequired: true` and
158
+ `guideRequiredReasons` when Connecta can prove review is necessary:
159
+
160
+ | Reason | Raised by | Survives exact schema expansion |
161
+ | --- | --- | --- |
162
+ | `connector_required` | the explicit `required: true` above | yes |
163
+ | `approval_required` | an unannotated or write-capable tool | yes |
164
+ | `schema_truncated` | a requested compact shape was capped | no — the describe that returns the exact shape clears it |
165
+
166
+ Describe reports whatever reasons remain in the same two fields. The boolean is
167
+ an instruction, not a server-side gate: nothing refuses the call, so the agent
168
+ is merely told to fetch the guide first, for any reason listed. Otherwise the
169
+ bounded summary decides connector-specific sequencing, units, pagination,
170
+ aliases, and generic API conventions still need the guide when they affect the
171
+ task, while a complete and unambiguous one-read schema proceeds directly. Guide
172
+ lookup always uses an exact name returned by `skills({})`, search, or describe;
173
+ callers never manufacture `connector:<id>` from an unmarked connector.
174
+
175
+ A connector-scoped lexical miss keeps that connector's guide metadata under
176
+ `queryAnalysis`. This matters for generic wrappers whose broad tool name
177
+ contains no endpoint vocabulary: a required guide stays discoverable instead of
178
+ disappearing with the zero-tool page.
179
+
180
+ Two built-in skills are byte-identical across deployments. `usage` says to read
181
+ it at most once per task and owns program selection detail, examples, runtime
182
+ differences, and repair guidance; `investigate` is on-demand guidance for
183
+ purchase verification, experiment checks, and customer or deployment
184
+ investigations shared guidance, not a saved workflow and not a source of
185
+ deployment-specific ids. The always-loaded instructions and tool definitions keep
186
+ route selection, the fail-closed boundary, and the minimum guest syntax, so a
187
+ client that never fetches a skill can still write a valid first program. Connector
188
+ guides stay scoped to the deployment that listed them even when two deployments
189
+ use identical content, and a deployment with no connector guides receives none of
190
+ the short conditional guide pointers in its tool definitions.
209
191
 
210
192
  ## Result representation
211
193
 
212
- For object results, `structuredContent` is the canonical full-fidelity value.
194
+ For object results, `structuredContent` is the canonical full-fidelity value and
213
195
  `content` carries the same complete value as compact JSON for clients that only
214
196
  consume text. Keeping both follows MCP's backwards-compatibility guidance;
215
- removing or summarizing the text copy is deferred until host-forwarding
216
- measurements demonstrate that supported clients do not need it.
217
-
218
- Plain-text guidance and errors remain text-only. A downstream MCP tool's native
219
- content blocks pass through when `call_tool` uses MCP result mode. When no
220
- text block exists and `structuredContent` is present, Connecta appends a text
221
- block containing its compact JSON, then applies the same content size guard.
222
- This preserves structured-only results, including `null`, arrays, and scalars.
223
- An existing text mirror stays unchanged; Connecta does not add another copy. Newly stashed JSON and
224
- downstream content envelopes use compact serialization, so `get_result` byte
225
- offsets and totals refer to that exact compact text.
226
-
227
- The direct-call stash keeps results for 15 minutes. `results.maxStashBytes`
228
- defaults to 8 MiB and `results.maxStashEntries` to 64 per `createConnecta`
229
- runtime, shared across all subjects and pools. Both accept non-negative safe
230
- integers; zero disables stashing. The byte budget counts the stored ASCII
231
- paging envelope, including base64 overhead, rather than only the result text.
232
- Capacity is reserved before each storage write, so concurrent requests cannot
233
- oversubscribe it. A full stash refuses new entries. A later stash attempt
234
- deletes expired entries before reusing their capacity; a failed deletion keeps
235
- the charge. These bounds cover writes by this runtime, not other processes,
236
- Worker isolates, or entries left by a previous runtime.
237
-
238
- Results belong to the authenticated subject whenever auth supplies a subject
239
- or user id, independently of activity configuration. The provider's namespace
240
- is used when present; otherwise the namespace is `connecta:auth:<provider kind>`.
241
- Keep subject ids distinct within that namespace. An explicit principal is the
242
- fallback subject when neither id is supplied. Open deployments and auth
243
- providers that supply no identity share one partition.
244
-
245
- New entries store UTF-8 bytes in a base64 envelope. After the KV read,
246
- `get_result` decodes only the requested byte range and a few boundary bytes;
247
- it does not encode the full text on every page. Storage still reads one full
248
- value. Pre-upgrade raw-text entries remain readable during their TTL using
249
- one full encoding per page. Offsets and `totalBytes` always describe the
197
+ dropping the text copy waits on host-forwarding measurements showing supported
198
+ clients do not need it. Plain-text guidance and errors stay text-only, and a
199
+ downstream MCP tool's native content blocks pass through in MCP result mode.
200
+ When no text block exists and `structuredContent` is present, Connecta appends a
201
+ text block carrying its compact JSON and then applies the same content size
202
+ guard, which preserves structured-only results including `null`, arrays, and
203
+ scalars. An existing text mirror stays unchanged; Connecta adds no second copy.
204
+ Newly stashed JSON and downstream content envelopes use compact serialization,
205
+ so `get_result` offsets and totals describe that exact compact text.
206
+
207
+ | Bound | Value |
208
+ | --- | --- |
209
+ | Stashed result TTL | 15 minutes |
210
+ | `results.maxStashBytes` | 8 MiB per `createConnecta` runtime |
211
+ | `results.maxStashEntries` | 64 per runtime |
212
+ | Top-level discovery result ceiling | 256,000 UTF-8 bytes |
213
+ | Downstream MCP `isError` text | 512 UTF-8 bytes plus an `…` marker |
214
+
215
+ The discovery ceiling counts text, `structuredContent`, and JSON escaping
216
+ together, because measuring one copy would advertise half the bytes the adapter
217
+ actually returns; error framing may shorten a bounded `isError` reason further to
218
+ fit the call's result cap on the same arithmetic. Both stash options accept
219
+ non-negative safe integers, zero disabling stashing, and are shared across all
220
+ subjects and pools; they count the stored ASCII paging envelope, base64 overhead
221
+ included, not only the result text. Capacity is reserved before each storage
222
+ write so concurrent requests cannot oversubscribe it, and a full stash refuses
223
+ new entries. A later attempt deletes expired entries before reusing their
224
+ capacity; a failed deletion keeps the charge. These bounds cover writes by this
225
+ runtime not other processes, Worker isolates, or entries a previous runtime
226
+ left behind.
227
+
228
+ Results belong to the authenticated subject whenever auth supplies a subject or
229
+ user id, independently of activity configuration, under the provider's namespace
230
+ when it has one and `connecta:auth:<provider kind>` otherwise. Keep subject ids
231
+ distinct within that namespace. An explicit principal is the fallback subject
232
+ when neither id is supplied, and open deployments and auth providers that supply
233
+ no identity share one partition.
234
+
235
+ New entries store UTF-8 bytes in a base64 envelope split across storage keys,
236
+ 48 KiB of result text per chunk — widening past roughly 1.5 MB so no result
237
+ occupies more than 33 keys, because every chunk is also a write. `get_result`
238
+ reads and decodes only the chunks a page covers plus a few boundary bytes, so
239
+ paging a 1.2 MB result costs the same per page as paging a 300 KB one; it
240
+ neither encodes nor reads the whole result per page. Pre-upgrade entries remain readable during their TTL — the earlier
241
+ single-key envelope reads one full value per page, and raw text before that
242
+ also pays one full encoding. Offsets and `totalBytes` always describe the
250
243
  original UTF-8 text, not the envelope. A supplied offset inside a character
251
244
  moves back to its start; page ends also align to character boundaries, and a
252
245
  page smaller than one character widens just enough to make progress.
253
246
 
254
- A successfully stashed `call_tool` truncation notice carries both the historical `resultId` and an
255
- exact `nextAction: { tool: "get_result", arguments: { id, offset: 0 } }`. The
256
- handle is therefore
257
- directly actionable without copying an identifier out of prose. Program results
258
- and oversized discovery responses carry no such route paging a program's
259
- return value is a refused shape, because a program can shrink anything before
260
- it returns.
261
-
262
- A refused stash or failed stash write cannot undo a downstream success. Both direct call tools
263
- return a truncated preview where usable, with a paging-unavailable notice and
264
- no `resultId` or paging action. Activity records success; the operator logger
265
- receives a fixed warning with the connector and tool, without storage error
266
- prose. For read-only work, reduce the result inside `execute_code`; repeating
267
- an approved write is not a way to recover its output. Other result-processing
268
- failures use a fixed `result_processing_failed` message and are never retryable.
269
-
270
- Downstream MCP `isError` text is bounded at its source to 512 UTF-8 bytes plus
271
- an `…` marker. Error framing may shorten it further to fit the call's result
272
- cap, counting JSON escaping and both copies in value mode.
273
-
274
- The top-level discovery ceiling is 256,000 UTF-8 bytes for the serialized tool
275
- result, including text, `structuredContent`, and JSON escaping. Measuring only
276
- one copy would advertise half the bytes the adapter actually returns.
247
+ A successfully stashed `call_tool` truncation notice carries both the historical
248
+ `resultId` and an exact
249
+ `nextAction: { tool: "get_result", arguments: { id, offset: 0 } }`, so the handle
250
+ is actionable without copying an identifier out of prose. Program results and
251
+ oversized discovery responses carry no such route: paging a program's return
252
+ value is a refused shape, because a program can shrink anything before it
253
+ returns.
254
+
255
+ A refused or failed stash write cannot undo a downstream success. Both call tools
256
+ then return a truncated preview where one is usable, with a paging-unavailable
257
+ notice and no `resultId` or paging action; an envelope carrying non-text blocks
258
+ gets the notice alone, because the head of a half-written base64 image helps
259
+ nobody. Activity records success, and the operator logger receives a fixed
260
+ warning naming the connector and tool without storage error prose. For read-only
261
+ work, reduce the result inside `execute_code` repeating an approved write is
262
+ not a way to recover its output. Other result-processing failures use a fixed
263
+ `result_processing_failed` message and are never retryable.
277
264
 
278
265
  A per-call `timeoutMs` covers catalog resolution, admission, and connector
279
- execution with one deadline. The admission queue's own timeout may expire
280
- sooner, but it cannot extend the call deadline. Result processing happens after
281
- that deadline ends, because a completed downstream call must not turn into a
266
+ execution under one deadline. The admission queue's own timeout may expire
267
+ sooner but cannot extend the call deadline. Result processing happens after that
268
+ deadline ends, because a completed downstream call must not turn into a
282
269
  retryable timeout while Connecta prepares its response.
283
270
 
284
271
  ## Lexical discovery
285
272
 
286
273
  `search_tools` tokenizes tool names and descriptions at punctuation and
287
- camel-case boundaries. Exact whole-token matches carry the most weight; a small
288
- set of inflectional variants preserves singular/plural and verb-form recall
289
- without allowing arbitrary mid-word substring matches. Ranking weights each
290
- query term by its document frequency across the available catalogs in that
291
- search, so a rare domain term outranks a ubiquitous action while action terms
292
- still distinguish `get`, `list`, `search`, and write operations. The scorer
293
- always evaluates useful near-matches instead of letting one broad all-term
294
- description hide them. Complete matches rank before ordinary partial matches;
295
- a partial candidate whose complete normalized tool name occurs in the
296
- normalized raw query competes with complete matches by score, and other
297
- candidates covering at least two terms fill the remaining page after them.
298
- Conversational cleanup applies only to scoring terms, never to the exact-name
274
+ camel-case boundaries. Exact whole-token matches carry the most weight, and a
275
+ small set of inflectional variants preserves singular/plural and verb-form
276
+ recall without admitting arbitrary mid-word substrings. Each query term is
277
+ weighted by its document frequency across the catalogs available to that search,
278
+ so a rare domain term outranks a ubiquitous action while action terms still
279
+ distinguish `get`, `list`, `search`, and write operations. Complete matches rank
280
+ before ordinary partial matches; a partial candidate whose complete normalized
281
+ tool name occurs in the normalized raw query competes with complete matches by
282
+ score, and other candidates covering at least two terms fill the remaining page.
283
+ Conversational cleanup applies to scoring terms only, never to the exact-name
299
284
  phrase check. If no tool covers every non-conversational term, the same scorer
300
285
  preserves the wider any-term fallback and marks the result
301
286
  `matchMode: "partial"`.
302
287
 
303
- Returned tool rows expose neither lexical scores nor per-result query coverage.
304
- The mixed complete/partial scorer still ranks rare domain terms, action terms,
305
- and exact tool-name phrases. Select from the returned purpose, address, schema,
306
- safety, and output shape. Page-level `queryAnalysis` remains the recovery path
307
- when no single result covers every term or no match exists.
308
-
309
- Only an empty or whitespace-only query browses. A non-empty query that
310
- normalizes to no ASCII lexical terms returns no tools instead of unrelated
311
- browse results. Its bounded `queryAnalysis.unmatchedTerms` contains the clipped
312
- raw query and guidance asks for ASCII action/object terms. A mixed query still
313
- searches with its ASCII terms; unsupported characters do not become false
314
- matches or per-tool coverage terms.
315
-
316
- Every partial or no-match lexical search also returns bounded page-level
317
- `queryAnalysis`; an all-term result needs no recovery advice.
318
- `representedTerms` occur in the current page, `otherResultTerms` occur only in
319
- another result, and
320
- `unmatchedTerms` have no lexical match in the catalogs that answered. Partial
321
- results explain that no single tool covered every term and recommend splitting
322
- distinct intents. A true negative says that no matching capability is
323
- configured and recommends refining, connector-scoping, or browsing; when a
324
- connector catalog was unavailable, the response includes
325
- `unavailableConnectorCount` instead of making that stronger claim. A no-match
326
- query whose terms name a configured connector's `id` or `title` never makes it
327
- either: connector identity is not in the lexical index — indexing it would move
328
- ranking for every query that already matches tools so instead the guidance on
329
- an unscoped miss names up to three such connectors by ID and sends the caller
330
- to a scoped browse. Identity affects that one sentence and nothing else: no
331
- ranking, no result, and no new field. A search
332
- explicitly scoped to that unavailable connector also receives `catalogError`
333
- the bounded classified failure (`code`, `message`, `retryable`, and any
334
- `retryAfterMs`) so the caller can tell a transient outage from one a deployment
335
- operator must clear. It carries nothing else the call-path classifier knows: a
336
- discovery read is not a call. Unscoped searches keep the count only — one
337
- connector's failure is not another search's context. An empty query browses
338
- rather than searches, so it reports no term analysis except when the scope
339
- itself failed, where the same fields apply. A browse scoped to an unavailable
340
- connector carries `unavailableConnectorCount`, `catalogError`, and guidance,
341
- and an unscoped browse again carries the count alone. A browse scoped to an ID
342
- that is not configured at all carries `connectorScope`, `unknownConnector`, and
343
- the same omit-the-connector guidance the term-bearing path gives — nothing was
344
- attempted, so there is no count and no `catalogError` — and it names no
345
- connector but the one the caller supplied. A connector that correctly exposes
346
- no tools still reports no analysis, so the two do not serialize alike. The
347
- advice to browse a connector with an empty query must not land in silence that
348
- reads like a connector with no tools. Analysis
349
- from a connector-filtered search includes `connectorScope` and speaks only
350
- about that connector; `unknownConnector` distinguishes an unconfigured ID from
351
- a known connector with no match. Analysis covers at most eight distinct terms
352
- of at most 64 displayed characters each, marks longer input `truncated`, and
353
- never changes lexical ranking.
288
+ Tool rows expose neither lexical scores nor per-result query coverage. Select
289
+ from the returned purpose, address, schema, safety, and output shape; page-level
290
+ `queryAnalysis` is the recovery path when no single result covers every term or
291
+ none exists. It reports `representedTerms` (in the current page),
292
+ `otherResultTerms` (only in another result), and `unmatchedTerms` (no lexical
293
+ match in the catalogs that answered), covers at most eight distinct terms of at
294
+ most 64 displayed characters each while marking longer input `truncated`, and
295
+ never changes lexical ranking. A non-empty query that normalizes to no ASCII
296
+ lexical terms returns no tools rather than unrelated browse results, with the
297
+ clipped raw query in `unmatchedTerms`; a mixed query searches with its ASCII
298
+ terms, and unsupported characters become neither false matches nor coverage
299
+ terms.
300
+
301
+ What the analysis says next depends on why the page is thin:
302
+
303
+ | Case | Fields and guidance |
304
+ | --- | --- |
305
+ | Partial match | no single tool covered every term; split distinct intents |
306
+ | True negative | no matching capability is configured; refine, connector-scope, or browse |
307
+ | True negative naming a configured connector | the same, plus up to three such connectors named by id and a pointer to a scoped browse |
308
+ | Unscoped search or browse with some connector unavailable | `unavailableConnectorCount` alone |
309
+ | Scoped to an unavailable connector | `unavailableConnectorCount`, `catalogError`, guidance |
310
+ | Scoped to an unconfigured id | `connectorScope`, `unknownConnector`, omit-the-connector guidance |
311
+ | Connector configured and genuinely exposing no tools | no analysis |
312
+
313
+ Three of those rows earn their asymmetry. An unavailable catalog downgrades the
314
+ claim, because a search may not report that nothing is configured when it does
315
+ not know. Connector identity is deliberately absent from the lexical index
316
+ indexing it would move ranking for every query that already matches tools — so a
317
+ query naming one gets exactly one extra sentence and nothing else: no ranking
318
+ change, no result, no new field. And a browse scoped to an unavailable connector
319
+ must not serialize like a connector that genuinely has no tools, or the advice to
320
+ browse lands in silence that reads like an answer.
321
+
322
+ `catalogError` is the bounded classified failure `code`, `message`,
323
+ `retryable`, and any `retryAfterMs` so a caller can tell a transient outage
324
+ from one an operator must clear, and nothing else the call-path classifier knows,
325
+ because a discovery read is not a call. Only an explicitly scoped search gets it:
326
+ one connector's failure is not another search's context, and a scope that was
327
+ never configured gets neither it nor a count, since nothing was attempted.
354
328
 
355
329
  ## Authorization recovery
356
330
 
@@ -391,113 +365,79 @@ The tool accepts no secret. `force` applies only to OAuth and may discard its
391
365
  stored grant before restarting consent. Static credential values are written
392
366
  only through the same-origin interactive-user credential route, and only for a
393
367
  connector visible to that user with the relevant shared or personal management
394
- permission. OAuth start, including `force`, requires that permission too. Core
395
- callbacks work without the UI for authorized interactive callers. After OAuth consent or a human update, retry
396
- the original operation; a static update is read from the vault on the next call
397
- and needs no redeploy.
368
+ permission; OAuth start, `force` included, requires that permission too. Core
369
+ callbacks work without the UI for authorized interactive callers. After OAuth
370
+ consent or a human update, retry the original operation; a static update is read
371
+ from the vault on the next call and needs no redeploy.
398
372
 
399
373
  ## Routing recovery
400
374
 
401
- Predictable local refusals carry structured recovery on both result modes.
402
- An unknown connector suggests an unscoped discovery query derived from the
375
+ Predictable local refusals carry structured recovery on both result modes. An
376
+ unknown connector suggests an unscoped discovery query derived from the
403
377
  attempted tool name; an unknown tool scopes the same query to the connector that
404
- answered. The suggested route follows the route the caller took: `tool:
405
- "search_tools"` for a top-level call, `function: "connecta.search"` with the same
406
- arguments when the miss happened inside `execute_code`, which has no way to call
407
- a tool. A read path that reaches an unannotated, write-capable, or destructive
408
- tool returns `nextAction` for `call_destructive_tool` with the canonical
409
- address. Nothing is executed by these records.
410
-
411
- `connecta.describe` keeps failures inline so one miss cannot discard the other
412
- schemas. Each failed entry keeps its human `error` and adds `errorDetails` with
413
- the equivalent invocation `code` and `retryable`. Address and tool misses use
414
- the same route-aware discovery action above. A close tool-name miss on a known
415
- connector may also carry `suggestions`: at most three deterministically ranked
416
- canonical addresses, with no scores or descriptions. An unknown connector
417
- stays unscoped and has no suggestions. A catalog-load failure carries only
418
- `code`, bounded `message`, `retryable`, and any `retryAfterMs`; discovery does
419
- not inherit later additions to the call-failure envelope.
420
-
421
- That route echoes the caller's own arguments back only while they fit a
422
- 512-byte budget, and then whole — never clipped. An error envelope is not
423
- size-guarded the way a result is, so an unbounded echo would let a large
424
- argument object produce a refusal many times the deployment's result cap, on
425
- both `call_tool` and program calls through `connecta.call`. Over budget, `args` is absent and the `purpose` says to
426
- re-send what was just sent: the agent already holds its own arguments, and half
427
- of them would describe a call nobody made.
428
-
429
- The address gets the same budget and the opposite rule: 512 bytes, clamped
430
- with a trailing `…` rather than dropped. It is caller-authored too — an
431
- invented one can be any length and it reaches the error message *and* the
432
- recovery query, each of which lands in both the text content and
433
- `structuredContent`; unbounded, a 50 KB typo produced a 200 KB refusal under a
434
- 1 KB result cap. Dropping it is not an option the way dropping arguments is:
435
- the address is the thing being corrected, a clipped one still identifies the
436
- mistake, and a short one every real one comes back exact and untagged.
437
-
438
- Unknown `get_result.id`, `authorize_connector.connector`, and `skills.name`
439
- echoes use the same 512-byte clamp. `search_tools.connector` instead rejects
440
- values over 512 UTF-8 bytes with `invalid_args` before catalog lookup, so a
441
- clipped scope can never select a different connector. A failed result-storage
442
- read returns typed `unavailable` without exposing backend error text.
443
-
444
- `call_destructive_tool` accepts an optional
445
- `reason` of at most 500 characters for the host's human approval view. It is
446
- outer-call context only: Connecta neither treats it as authority nor passes it
447
- to the downstream connector, and an empty or whitespace-only one is read as no
448
- reason rather than as a reason to refuse the call.
449
-
450
- Activity carries an optional coarse `friction` class: `tool_not_found`,
451
- `schema_retry`, `destructive_reroute`, `auth_required`, or `result_too_large`.
452
- It is derived from the typed error code, except on the one call that has no
453
- error code to derive from: a result too large to return inline is friction for
454
- the agent while remaining `outcome: "success"`. That applies to a `call_tool`
455
- result, the only source of `result_too_large` friction. (Activity stored by
456
- older releases may still carry the retired `batch_call` source; nothing writes
457
- it today.)
458
- An oversized *discovery* response and an oversized program return are shaped
459
- differently and produce none, and an `errorCode` is written only when the call
460
- actually failed. The category adds no arguments, results, search text,
461
- generated code, credentials, or raw errors.
462
-
463
- An address whose connector does not exist is recorded too, as written, provided
464
- it has the `<connectorId>.<toolName>` shape at all — a string that never split
465
- into the two fields activity keeps still records nothing. A hallucinated
466
- connector id is the most common address mistake, and an operator reading
467
- activity should see it; addresses are already a first-class activity field, so
468
- nothing new is retained.
469
-
470
- What *is* new is that those fields now hold caller-authored text, so the
471
- recording seam clamps them: `connectorId` and `toolName` at 128 UTF-8 bytes
472
- each, `address` at 257, with a `…` marker. Far past any real id or tool name,
473
- and far short of a 40 KB invented one. The clamp is structural rather than a
474
- policy the writer applies, because "payload-free by construction" has to mean
475
- the event type has nowhere to put a payload — a 40 KB connector id is a payload
476
- wearing an id's clothing. Clamped rather than skipped: the invented id is
477
- exactly what an operator needs to see, and its first 128 bytes say as much
478
- about the mistake as all 40,000 would.
378
+ answered. The suggested route follows the route the caller took:
379
+ `tool: "search_tools"` for a top-level call, `function: "connecta.search"` with
380
+ the same arguments when the miss happened inside `execute_code`, which has no
381
+ way to call a tool. A read path that reaches an unannotated, write-capable, or
382
+ destructive tool returns `nextAction` for `call_destructive_tool` with the
383
+ canonical address. Nothing is executed by these records.
384
+
385
+ `connecta.describe` keeps its failures inline instead, so one miss cannot
386
+ discard the other schemas; each failed entry carries a human `error`, typed
387
+ `errorDetails`, the same route-aware discovery action, and on a close
388
+ tool-name miss against a known connector at most three deterministically
389
+ ranked canonical `suggestions` with no scores and no descriptions. A
390
+ catalog-load failure carries only `code`, bounded `message`, `retryable`, and
391
+ any `retryAfterMs`: discovery does not inherit later additions to the
392
+ call-failure envelope. The per-entry rules are
393
+ [code mode](./code-mode.md#connectadescribe)'s `S4`.
394
+
395
+ ### Echo budgets
396
+
397
+ An error envelope is not size-guarded the way a result is, and every echoed byte
398
+ lands twice in the text content and in `structuredContent` so a 50 KB
399
+ invented address once produced a 200 KB refusal against a 1 KB result cap. Every
400
+ caller-authored string a refusal repeats therefore gets 512 UTF-8 bytes, and
401
+ what happens over budget differs by field.
402
+
403
+ | Field | Over 512 UTF-8 bytes |
404
+ | --- | --- |
405
+ | `args` on `call_tool` and `connecta.call` | dropped whole; `purpose` says to re-send what was just sent |
406
+ | the attempted address | clamped with a trailing `…` |
407
+ | unknown `get_result.id`, `authorize_connector.connector`, `skills.name` | clamped the same way |
408
+ | `search_tools.connector` | rejected with `invalid_args` before catalog lookup |
409
+
410
+ Arguments go all or nothing because the agent already holds what it sent, and
411
+ half of it would describe a call nobody made. The address gets the opposite rule
412
+ because it is the thing being corrected: a clipped one still identifies the
413
+ mistake, and a short one every real one — comes back exact and untagged. A
414
+ scope is rejected outright because a clipped one could select a different
415
+ connector. A failed result-storage read returns typed `unavailable` without
416
+ exposing backend error text.
417
+
418
+ Activity records each of these refusals with the coarse `friction` class derived
419
+ from its typed error code `tool_not_found`, `schema_retry`,
420
+ `destructive_reroute`, `auth_required` and clamps the caller-authored
421
+ `connectorId`, `toolName`, and `address` it keeps, both under
422
+ [code mode](./code-mode.md#activity)'s `V2` and `V3`. A `call_tool` result too
423
+ large to return inline is the one exception: it is friction
424
+ (`result_too_large`) on a call whose `outcome` is still `"success"`, so it
425
+ carries no `errorCode`.
479
426
 
480
427
  ## Argument recovery
481
428
 
482
- A remote MCP tool's advertised `inputSchema` is checked in the shared
483
- invocation path before admission and provider dispatch. A mismatch is the
484
- non-retryable `invalid_args`, consistently across `call_tool`,
485
- `call_destructive_tool`, generated-code failures, and rejected promises. The error
486
- names the connector and operation and carries bounded `validation.issues`:
487
- JSON Pointer `path`, schema-keyword `code`, and expected shape. Submitted
488
- values are never copied into those findings.
489
-
490
- At most three findings are returned; `validation.truncated` says when more
491
- exist. `nextAction` points to discovery scoped to the same connector and tool
492
- name when the compact schema is needed routed like any other miss, so a
493
- program is sent to `connecta.search` and a top-level call to `search_tools`
494
- while `retry` says to correct the listed arguments and reissue the original
495
- operation. A declared property reports only its failed schema keyword, while a
496
- truly undeclared property reports `additionalProperties`; validator-internal
497
- duplicate `additionalProperties` branches never reach the caller. A schema the local
498
- validator cannot evaluate passes through to the provider. Provider error prose
499
- is not parsed or guessed, so an unknown format remains
500
- `connector_call_failed`.
501
-
502
- Describe's nearby-address list uses the same three-item recovery bound. It
503
- contains addresses only; it never serializes ranking scores or result prose.
429
+ A remote MCP tool's advertised `inputSchema` is checked in the shared invocation
430
+ path before admission and provider dispatch, so a mismatch is the non-retryable
431
+ `invalid_args` identically on `call_tool`, `call_destructive_tool`,
432
+ generated-code failures, and rejected promises. The error names the connector and
433
+ operation and carries `validation.issues`: JSON Pointer `path`, schema-keyword
434
+ `code`, and expected shape, with submitted values never copied into a finding. At
435
+ most three are returned, and `validation.truncated` says when more exist — the
436
+ same three-item bound describe's nearby-address list uses.
437
+
438
+ `nextAction` points to discovery scoped to the same connector and tool name when
439
+ the compact schema is needed, routed like any other miss: a program to
440
+ `connecta.search`, a top-level call to `search_tools`. `retry` says to correct
441
+ the listed arguments and reissue the original operation. Which keyword a finding
442
+ names, and what the local validator declines to evaluate, is
443
+ [code mode](./code-mode.md#errors)'s `E8`.