@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.
- package/AGENTS.md +18 -20
- package/CHANGELOG.md +64 -1
- package/README.md +5 -6
- package/dist/branding.d.ts +31 -2
- package/dist/branding.js +116 -8
- package/dist/connectors/api.d.ts +1 -1
- package/dist/connectors/api.js +10 -2
- package/dist/connectors/guarded-fetch.d.ts +5 -1
- package/dist/connectors/guarded-fetch.js +34 -4
- package/dist/connectors/remote-mcp.js +8 -4
- package/dist/errors.d.ts +11 -3
- package/dist/errors.js +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -1
- package/dist/meta-tools.js +105 -29
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/view.d.ts +38 -1
- package/dist/operator-ui/view.js +71 -0
- package/dist/providers/cloudflare.d.ts +14 -2
- package/dist/providers/cloudflare.js +107 -16
- package/dist/providers/linear.d.ts +26 -4
- package/dist/providers/linear.js +19 -4
- package/dist/providers/mixpanel.d.ts +16 -3
- package/dist/providers/mixpanel.js +13 -2
- package/dist/providers/notion.d.ts +8 -1
- package/dist/providers/notion.js +83 -10
- package/dist/providers/revenuecat.d.ts +30 -4
- package/dist/providers/revenuecat.js +42 -4
- package/dist/providers/stripe.d.ts +7 -1
- package/dist/providers/stripe.js +30 -4
- package/dist/providers/vercel.js +11 -1
- package/dist/registry.d.ts +12 -4
- package/dist/registry.js +22 -8
- package/dist/types.d.ts +37 -0
- package/dist/ui.js +18 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +193 -181
- package/documentation/auth.md +197 -176
- package/documentation/code-mode.md +426 -321
- package/documentation/meta-tools.md +356 -416
- package/examples/worker/AGENTS.md +2 -1
- package/examples/worker/README.md +12 -10
- package/examples/worker/src/index.ts +12 -15
- package/package.json +1 -2
- package/templates/node/.env.example +3 -3
- package/templates/node/AGENTS.md +5 -4
- package/templates/node/README.md +2 -1
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +23 -22
- package/documentation/call-admission.md +0 -158
- package/documentation/cloudflare.md +0 -471
- package/documentation/connector-guides.md +0 -176
- package/documentation/connectors.md +0 -431
- package/documentation/linear.md +0 -193
- package/documentation/mixpanel.md +0 -160
- package/documentation/notion.md +0 -308
- package/documentation/operations.md +0 -359
- package/documentation/operator-ui.md +0 -135
- package/documentation/optional-modules-upgrade.md +0 -243
- package/documentation/provider-conventions.md +0 -729
- package/documentation/request-admission.md +0 -204
- package/documentation/revenuecat.md +0 -305
- package/documentation/storage-and-credentials.md +0 -254
- package/documentation/stripe.md +0 -262
- package/documentation/upgrading.md +0 -768
- 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`
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
`
|
|
29
|
-
`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
and
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
`execute_code
|
|
40
|
-
a
|
|
41
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
The
|
|
54
|
-
|
|
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
|
|
68
|
-
inventory before any catalog search
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
and a truncated line ends with the exact `+N more` count
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
and
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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? }
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
`connector_required`
|
|
163
|
-
`approval_required`
|
|
164
|
-
`schema_truncated`
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
A refused
|
|
263
|
-
return a truncated preview where usable, with a paging-unavailable
|
|
264
|
-
no `resultId` or paging action
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
|
280
|
-
sooner
|
|
281
|
-
|
|
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
|
|
288
|
-
set of inflectional variants preserves singular/plural and verb-form
|
|
289
|
-
without
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
raw query
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
ranking
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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
|
|
395
|
-
callbacks work without the UI for authorized interactive callers. After OAuth
|
|
396
|
-
the original operation; a static update is read
|
|
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
|
-
|
|
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:
|
|
405
|
-
"search_tools"` for a top-level call, `function: "connecta.search"` with
|
|
406
|
-
arguments when the miss happened inside `execute_code`, which has no
|
|
407
|
-
a tool. A read path that reaches an unannotated, write-capable, or
|
|
408
|
-
tool returns `nextAction` for `call_destructive_tool` with the
|
|
409
|
-
address. Nothing is executed by these records.
|
|
410
|
-
|
|
411
|
-
`connecta.describe` keeps failures inline so one miss cannot
|
|
412
|
-
schemas
|
|
413
|
-
the
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
size-guarded the way a result is,
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
`
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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`.
|