@zackbart/connecta 0.24.3 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +58 -30
- package/CHANGELOG.md +317 -1
- package/README.md +5 -6
- package/dist/advertised-schema.js +53 -0
- package/dist/auth/downstream-oauth.js +490 -106
- package/dist/branding.d.ts +31 -2
- package/dist/branding.js +116 -8
- package/dist/call-admission.js +139 -113
- package/dist/catalog-service.js +141 -105
- package/dist/catalog.js +258 -33
- package/dist/concurrency.js +24 -23
- package/dist/connector-scope.js +6 -37
- package/dist/connectors/api.d.ts +3 -3
- package/dist/connectors/api.js +10 -2
- package/dist/connectors/guarded-fetch.js +34 -4
- package/dist/connectors/remote-mcp.d.ts +6 -5
- package/dist/connectors/remote-mcp.js +325 -245
- package/dist/credential-contract.d.ts +9 -0
- package/dist/credential-rules.js +2 -2
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +45 -3
- package/dist/errors.d.ts +11 -3
- package/dist/errors.js +2 -1
- package/dist/execute.js +363 -257
- package/dist/executor-admission.js +67 -50
- package/dist/executors/quickjs-runtime.js +4 -2
- package/dist/executors/quickjs.js +251 -210
- package/dist/fix-prompt.js +37 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +63 -21
- package/dist/invocation.js +324 -285
- package/dist/meta-tools.js +304 -114
- package/dist/oauth-callback-outcome.js +94 -0
- package/dist/oauth-sealing.js +29 -0
- package/dist/operator-ui/fix-prompts.js +87 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +46 -1
- package/dist/operator-ui/setup-commands.js +53 -0
- package/dist/operator-ui/view.js +109 -2
- package/dist/program-source.js +205 -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 +85 -12
- package/dist/providers/revenuecat.d.ts +30 -4
- package/dist/providers/revenuecat.js +70 -5
- package/dist/providers/stripe.d.ts +10 -3
- package/dist/providers/stripe.js +30 -4
- package/dist/providers/vercel.js +12 -2
- package/dist/registry.d.ts +13 -5
- package/dist/registry.js +317 -258
- package/dist/routes/activity.js +120 -137
- package/dist/routes/credentials.js +174 -163
- package/dist/routes/mcp.js +129 -70
- package/dist/routes/oauth-management.js +60 -71
- package/dist/routes/oauth.js +44 -16
- package/dist/routes/operator.js +82 -0
- package/dist/routes/shared.d.ts +2 -0
- package/dist/routes/ui-data.js +210 -0
- package/dist/routes/ui.js +64 -32
- package/dist/runtime/admission.js +43 -0
- package/dist/runtime/call-admission.js +67 -0
- package/dist/runtime/connector-scope.js +72 -0
- package/dist/runtime/run.js +174 -0
- package/dist/runtime/services.js +130 -0
- package/dist/runtime/storage.js +66 -0
- package/dist/server.js +111 -89
- package/dist/skills.js +1 -1
- package/dist/storage/file.js +39 -2
- package/dist/storage/memory.js +28 -16
- package/dist/timeout.js +17 -47
- package/dist/types.d.ts +64 -7
- package/dist/ui.d.ts +16 -2
- package/dist/ui.js +68 -176
- package/dist/url-safety.js +157 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +368 -182
- package/documentation/auth.md +277 -176
- package/documentation/code-mode.md +479 -329
- package/documentation/meta-tools.md +494 -417
- package/ethos.md +44 -47
- package/examples/worker/AGENTS.md +2 -1
- package/examples/worker/README.md +62 -14
- package/examples/worker/src/d1-storage.ts +116 -0
- package/examples/worker/src/index.ts +18 -21
- package/examples/worker/wrangler.jsonc +16 -4
- package/package.json +11 -4
- 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/dist/auth/downstream-oauth.d.ts +0 -200
- package/dist/catalog-fingerprint.d.ts +0 -19
- package/dist/catalog-limits.d.ts +0 -9
- package/dist/catalog-service.d.ts +0 -263
- package/dist/catalog.d.ts +0 -87
- package/dist/concurrency.d.ts +0 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -92
- package/dist/credential-rules.d.ts +0 -85
- package/dist/execute.d.ts +0 -154
- package/dist/executor-result.d.ts +0 -10
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -67
- package/dist/favicon.d.ts +0 -1
- package/dist/identity.d.ts +0 -4
- package/dist/invocation.d.ts +0 -74
- package/dist/mcp-result.d.ts +0 -8
- package/dist/meta-tools.d.ts +0 -129
- package/dist/operator-ui/generated.d.ts +0 -2
- package/dist/operator-ui/view.d.ts +0 -151
- package/dist/result-shapes.d.ts +0 -13
- package/dist/retired-toolkits.d.ts +0 -2
- package/dist/routes/activity.d.ts +0 -2
- package/dist/routes/credentials.d.ts +0 -2
- package/dist/routes/mcp.d.ts +0 -10
- package/dist/routes/oauth-management.d.ts +0 -2
- package/dist/routes/oauth.d.ts +0 -2
- package/dist/routes/ui.d.ts +0 -2
- package/dist/server.d.ts +0 -9
- package/dist/skills.d.ts +0 -44
- package/dist/timeout.d.ts +0 -21
- package/dist/tool-safety.d.ts +0 -9
- 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,79 @@
|
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
([
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
a
|
|
41
|
-
|
|
42
|
-
|
|
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. Resumable writes plan an
|
|
16
|
+
unconditional eighth, the destructive-annotated `resume_execution`
|
|
17
|
+
([ethos](../ethos.md#decisions)); `execute_code` keeps its read-only hint,
|
|
18
|
+
because writes will run only there or for config-exempt tools.
|
|
19
|
+
|
|
20
|
+
| Tool | Arguments | Returns |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| `execute_code` | `code`, `diagnostics?` | the program's reduced return value, plus a `diagnostics` block when asked |
|
|
23
|
+
| `search_tools` | `query?`, `connector?`, `safety?`, `limit?`, `offset?`, `fullDescriptions?`, `includeSchemas?: "compact" \| "json" \| "typescript"` | `{ connectors: [{ id, tools }], total, offset, limit, hasMore }`, plus `queryAnalysis` on a partial or failed search |
|
|
24
|
+
| `call_tool` | `address`, `args?`, `resultMode?: "mcp" \| "value"`, `timeoutMs?`, `diagnostics?` | the downstream result, bounded as [result representation](#result-representation) describes |
|
|
25
|
+
| `call_destructive_tool` | the same, plus `reason?` | the same |
|
|
26
|
+
| `authorize_connector` | `connector`, `force?` | the class-specific handoff in [authorization recovery](#authorization-recovery) |
|
|
27
|
+
| `get_result` | `id`, `offset?`, `maxBytes?` | a one-line JSON header `{ resultId, offset, bytes, totalBytes, hasMore, nextAction? }`, a newline, then the page as raw text ([paging](#paging-with-get_result)) |
|
|
28
|
+
| `skills` | `name?` | the listing when `name` is absent, that skill's markdown when it is present |
|
|
29
|
+
|
|
30
|
+
`limit` defaults to 8 and is capped at 100, as is one `connecta.describe` batch.
|
|
31
|
+
`get_result.offset` is a whole number of bytes ≥ 0 defaulting to 0 and
|
|
32
|
+
`maxBytes` a whole number ≥ 1 that defaults to, and is clamped to, the inline
|
|
33
|
+
cap of the call that stashed the result — 24,000 bytes unless
|
|
34
|
+
`calls.maxResultBytes` or a per-connector override says otherwise
|
|
35
|
+
([why 24,000](#truncated-direct-call-results)). A value outside those domains is
|
|
36
|
+
an input error; a valid `maxBytes` above the cap is an upper bound, not an
|
|
37
|
+
error ([why](#paging-with-get_result)). `reason` is at most 500 characters of context for the host's human
|
|
38
|
+
approval view; Connecta neither treats it as authority nor sends it downstream,
|
|
39
|
+
and an empty or whitespace-only one reads as no reason rather than as grounds to
|
|
40
|
+
refuse a consequential call.
|
|
41
|
+
|
|
42
|
+
`diagnostics: true` adds compact request-local timing and serialized-size
|
|
43
|
+
aggregates for a caller measuring a workflow: a `diagnostics` block from
|
|
44
|
+
`execute_code`, a `timing` block on a call response that is already structured
|
|
45
|
+
(value mode, or a failure carrying recovery). Normal calls pay nothing for it,
|
|
46
|
+
and the measurements never contain program source, arguments, values, addresses,
|
|
43
47
|
credentials, logs, or raw error text.
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
The
|
|
54
|
-
|
|
49
|
+
Connecta's own tools carry the annotations it demands of downstream tools:
|
|
50
|
+
read-only hints on all but `authorize_connector`, which mutates stored auth
|
|
51
|
+
state, and `call_destructive_tool`. Otherwise a host that gates on annotations
|
|
52
|
+
would prompt for every search, and a connecta aggregated behind another connecta
|
|
53
|
+
would be refused by its own policy.
|
|
54
|
+
|
|
55
|
+
## Routing between the call surfaces
|
|
56
|
+
|
|
57
|
+
The route is chosen before discovery, and read-only work has exactly two:
|
|
58
|
+
`call_tool` for one known address, `execute_code` for everything wider — an
|
|
59
|
+
unknown address, a result that will be reduced, a call whose arguments depend on
|
|
60
|
+
an earlier result, or several operations. A program keeps discovery, calls, and
|
|
61
|
+
reduction together when the schemas and result shapes suffice, and gives each
|
|
62
|
+
distinct operation its own short `connecta.search` query. That is cheaper than it
|
|
63
|
+
looks: discovery inside the program returns no candidate schema to the model and
|
|
64
|
+
costs no round trip. One exception — an unfamiliar provider result may come back
|
|
65
|
+
as a small sample for inspection before continuing in another call, which avoids
|
|
66
|
+
repeated guesses at text formats and collection roots without restoring a
|
|
67
|
+
mandatory discovery-only round trip.
|
|
68
|
+
|
|
69
|
+
That covers read-only work, because that is the only work a program can do.
|
|
70
|
+
Anything unannotated, write-capable, or destructive is inadmissible inside the
|
|
71
|
+
sandbox, so multi-step destructive work discovers at the top level and runs each
|
|
72
|
+
step through `call_destructive_tool`, where the host can put the question to a
|
|
73
|
+
human. Telling an agent never to search at the top level for multiple calls
|
|
74
|
+
would close the only route that work has.
|
|
75
|
+
|
|
76
|
+
The three discovery routes use deliberately different envelopes. These are their
|
|
77
|
+
smallest successful one-tool shapes:
|
|
55
78
|
|
|
56
79
|
```js
|
|
57
80
|
// Top-level search_tools
|
|
@@ -62,295 +85,383 @@ their smallest successful one-tool shapes:
|
|
|
62
85
|
{ tools: [{ name: "get_run", address: "ci.get_run", inputSchema: "{ runId: integer }" }] } // connecta.describe
|
|
63
86
|
```
|
|
64
87
|
|
|
88
|
+
Live connector probing is not a fourth: the operator pages and `/health` own it.
|
|
89
|
+
|
|
65
90
|
## Discovery context
|
|
66
91
|
|
|
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
|
-
|
|
92
|
+
The deployment-derived `execute_code` description carries a live connector
|
|
93
|
+
inventory before any catalog search — registry order, each canonical id, and a
|
|
94
|
+
distinct configured title, with no second name minted for programs. Titles
|
|
95
|
+
normalize whitespace and cap at 48 UTF-8 bytes, the complete line caps at 256,
|
|
96
|
+
entries stay whole, and a truncated line ends with the exact `+N more` count, so
|
|
97
|
+
account and environment hints cannot eat the inventory. It reads only the
|
|
98
|
+
configured registry: no catalog load, no credential probe, no capability, and no
|
|
99
|
+
replacement for canonical discovery or addressing. Program search results carry
|
|
100
|
+
the same bounded `connectorTitle` per tool, so choosing an account or
|
|
101
|
+
environment costs no provider read. Context, not a ranking input and not proof
|
|
102
|
+
of live access.
|
|
103
|
+
|
|
104
|
+
Read-only lookup belongs in `connecta.search` inside the program; top-level
|
|
105
|
+
`search_tools` stays for explicit catalog inspection and approval-required
|
|
106
|
+
discovery. Both take the same arguments.
|
|
107
|
+
|
|
108
|
+
| Argument | What it does |
|
|
109
|
+
| --- | --- |
|
|
110
|
+
| `query` | two to four action/object terms; empty or whitespace-only browses |
|
|
111
|
+
| `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 |
|
|
112
|
+
| `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 |
|
|
113
|
+
| `limit` / `offset` | page the ranked results; omit `limit` initially so the default eight-result page stays small |
|
|
114
|
+
| `includeSchemas` | `"compact"` for the rendered routing view, `"json"` for the exact schema, `"typescript"` for a function signature ([below](#typescript-signatures)) |
|
|
115
|
+
| `fullDescriptions` | unabridged tool purposes, at the obvious cost |
|
|
116
|
+
|
|
117
|
+
Neither `connector` nor `safety` grants authority or changes invocation
|
|
118
|
+
admission; they select what discovery shows and nothing else.
|
|
119
|
+
|
|
87
120
|
`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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
`
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
121
|
+
output shape, plus `inputKeys`, `requiredInputKeys`, and `outputKeys` for
|
|
122
|
+
bounded plain objects. Where the provider declared no output shape but an
|
|
123
|
+
earlier successful call learned one, the same field carries that open observed
|
|
124
|
+
schema beside `outputSchemaSource: "observed"`. The marker is load-bearing:
|
|
125
|
+
observed names and broad types are routing evidence, never a provider contract,
|
|
126
|
+
and a provider declaration always wins. Observation originates no provider
|
|
127
|
+
traffic and cannot fail a call; the mechanism and its bounds are
|
|
128
|
+
[code mode](./code-mode.md#connectasearch)'s `S9`, and this surface only labels
|
|
129
|
+
what it returns.
|
|
130
|
+
|
|
131
|
+
Lexical rank is one signal among several: pick a candidate whose required inputs
|
|
132
|
+
are available, whose schema is complete enough for the call, and whose safety
|
|
133
|
+
and outputs fit the work. When that shape suffices, call the returned address
|
|
134
|
+
directly; reserve `connecta.describe` for a search without schemas, an ambiguous
|
|
135
|
+
compact shape, or exact constraints that need `format: "json"`.
|
|
136
|
+
|
|
137
|
+
Compact search is a routing view, not a second copy of connector documentation,
|
|
138
|
+
so it spends bytes on shape and none on prose: tool purposes cap at 160
|
|
139
|
+
characters, connector descriptions and property prose are dropped, and required
|
|
140
|
+
input fields render before optional ones. Both surfaces share one renderer, so
|
|
141
|
+
its byte and work budgets, its truncation renderings, and its handling of enums,
|
|
142
|
+
tuples, and conditional keywords are documented once under
|
|
143
|
+
[`connecta.search`](./code-mode.md#connectasearch) and
|
|
144
|
+
[`connecta.describe`](./code-mode.md#connectadescribe). The part a client must
|
|
145
|
+
act on is the flag: any cap sets `inputSchemaTruncated` or
|
|
146
|
+
`outputSchemaTruncated`, and that means repeat with `includeSchemas: "json"`, or
|
|
147
|
+
describe, when the exact constraints matter.
|
|
148
|
+
|
|
149
|
+
### TypeScript signatures
|
|
150
|
+
|
|
151
|
+
`includeSchemas: "typescript"` replaces both schema fields with one
|
|
152
|
+
`signature`: the function `connecta.call(address, args)` resolves to, written
|
|
153
|
+
as a TypeScript type because agents write code against types more reliably
|
|
154
|
+
than against JSON Schema ([executor](https://github.com/UsefulSoftwareCo/executor)'s
|
|
155
|
+
discovery made the same bet).
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
(args: { team: string; limit?: number /* <= 250 */ }) => Promise<unknown>
|
|
159
|
+
(args?: {}) => Promise<{ accounts: { stripe_context: string; livemode: boolean; name?: string }[] }>
|
|
160
|
+
(args: { team: string }) => Promise</* observed, not declared */ { cursor?: string; issues?: { id?: string }[] }>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
It is something to read, not something that runs: programs stay JavaScript,
|
|
164
|
+
and the refusal of erasable TypeScript in `execute_code` stands
|
|
165
|
+
([ethos](../ethos.md#decisions)). The output half is the provider's declared
|
|
166
|
+
schema, else an `S9` observation that opens with the
|
|
167
|
+
`/* observed, not declared */` marker and keeps the `outputSchemaSource`
|
|
168
|
+
field, else `unknown` — runtime evidence never reads as a contract, even when
|
|
169
|
+
the signature is lifted out of its entry. A tool without parameters takes
|
|
170
|
+
`args?: {}`.
|
|
171
|
+
|
|
172
|
+
The walk, byte budgets, and truncation flags are compact's, each half
|
|
173
|
+
separately, and search, describe, and every discovery route share them, so the
|
|
174
|
+
same tool renders the same signature from `search_tools` and `connecta.search`.
|
|
175
|
+
What changes is the dialect: `number` for `integer`, `;` between members,
|
|
176
|
+
quoted non-identifier keys, `T[]` over a parenthesized union, `| null` for
|
|
177
|
+
OpenAPI `nullable` and type lists, index signatures for schema-valued
|
|
178
|
+
`additionalProperties`, `Record<string, unknown>` for an open object with no
|
|
179
|
+
declared properties, and JSDoc for property prose where describe keeps it.
|
|
180
|
+
Where compact would print a bare definition name or raw JSON, the signature
|
|
181
|
+
prints `unknown` with a marker and sets the flag: `/* recursive */` for a
|
|
182
|
+
`$ref` cycle, `/* unresolved */` for a missing target, `/* truncated */` past
|
|
183
|
+
depth four or an exhausted budget. `inputKeys` and friends come from the
|
|
184
|
+
declared schema exactly as they do for compact.
|
|
185
|
+
|
|
186
|
+
## Connector guides and skills
|
|
187
|
+
|
|
188
|
+
A connector may attach a deployment-owned guide as markdown, keeping the
|
|
147
189
|
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.
|
|
190
|
+
`{ content, summary?, required? }` — which registers no connector and creates no
|
|
191
|
+
shared runtime template. `content` is the markdown `skills` returns verbatim.
|
|
192
|
+
`summary` is normalized and refuses construction over 120 characters; absent,
|
|
193
|
+
Connecta derives the same bounded fallback the skills listing uses from the
|
|
194
|
+
guide's first meaningful body paragraph. `required: true` is reserved for
|
|
195
|
+
generic API wrappers and cross-operation conventions a complete downstream
|
|
196
|
+
schema cannot express.
|
|
197
|
+
|
|
198
|
+
Search and describe results carry a `guide: "connector:<id>"` pointer and a
|
|
199
|
+
`guideSummary`. A matching tool also carries `guideRequired: true` and
|
|
200
|
+
`guideRequiredReasons` when Connecta can prove review is necessary:
|
|
201
|
+
|
|
202
|
+
| Reason | Raised by | Survives exact schema expansion |
|
|
203
|
+
| --- | --- | --- |
|
|
204
|
+
| `connector_required` | the explicit `required: true` above | yes |
|
|
205
|
+
| `approval_required` | an unannotated or write-capable tool | yes |
|
|
206
|
+
| `schema_truncated` | a requested compact shape was capped | no — the describe that returns the exact shape clears it |
|
|
207
|
+
|
|
208
|
+
Describe reports whatever reasons remain in the same two fields. The boolean is
|
|
209
|
+
an instruction, not a server-side gate: nothing refuses the call, so the agent
|
|
210
|
+
is merely told to fetch the guide first, for any reason listed. Otherwise the
|
|
211
|
+
bounded summary decides — connector-specific sequencing, units, pagination,
|
|
212
|
+
aliases, and generic API conventions still need the guide when they affect the
|
|
213
|
+
task, while a complete and unambiguous one-read schema proceeds directly. Guide
|
|
214
|
+
lookup always uses an exact name returned by `skills({})`, search, or describe;
|
|
215
|
+
callers never manufacture `connector:<id>` from an unmarked connector.
|
|
216
|
+
|
|
217
|
+
A connector-scoped lexical miss keeps that connector's guide metadata under
|
|
218
|
+
`queryAnalysis`. This matters for generic wrappers whose broad tool name
|
|
219
|
+
contains no endpoint vocabulary: a required guide stays discoverable instead of
|
|
220
|
+
disappearing with the zero-tool page.
|
|
221
|
+
|
|
222
|
+
Two built-in skills are byte-identical across deployments. `usage` says to read
|
|
223
|
+
it at most once per task and owns program selection detail, examples, runtime
|
|
224
|
+
differences, and repair guidance; `investigate` is on-demand guidance for
|
|
225
|
+
purchase verification, experiment checks, and customer or deployment
|
|
226
|
+
investigations — shared guidance, not a saved workflow and not a source of
|
|
227
|
+
deployment-specific ids. The always-loaded instructions and tool definitions keep
|
|
228
|
+
route selection, the fail-closed boundary, and the minimum guest syntax, so a
|
|
229
|
+
client that never fetches a skill can still write a valid first program. Connector
|
|
230
|
+
guides stay scoped to the deployment that listed them even when two deployments
|
|
231
|
+
use identical content, and a deployment with no connector guides receives none of
|
|
232
|
+
the short conditional guide pointers in its tool definitions.
|
|
209
233
|
|
|
210
234
|
## Result representation
|
|
211
235
|
|
|
212
|
-
For object results, `structuredContent` is the canonical full-fidelity value
|
|
236
|
+
For object results, `structuredContent` is the canonical full-fidelity value and
|
|
213
237
|
`content` carries the same complete value as compact JSON for clients that only
|
|
214
238
|
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
|
-
|
|
239
|
+
dropping the text copy waits on host-forwarding measurements showing supported
|
|
240
|
+
clients do not need it. Plain-text guidance and errors stay text-only, and a
|
|
241
|
+
downstream MCP tool's native content blocks pass through in MCP result mode.
|
|
242
|
+
When no text block exists and `structuredContent` is present, Connecta appends a
|
|
243
|
+
text block carrying its compact JSON and then applies the same content size
|
|
244
|
+
guard, which preserves structured-only results including `null`, arrays, and
|
|
245
|
+
scalars. An existing text mirror stays unchanged; Connecta adds no second copy.
|
|
246
|
+
Newly stashed JSON and downstream content envelopes use compact serialization,
|
|
247
|
+
and a lone text block stashes as its own text, so `get_result` offsets and
|
|
248
|
+
totals describe exactly that text.
|
|
249
|
+
|
|
250
|
+
| Bound | Value |
|
|
251
|
+
| --- | --- |
|
|
252
|
+
| Stashed result TTL | 15 minutes |
|
|
253
|
+
| `results.maxStashBytes` | 8 MiB per `createConnecta` runtime |
|
|
254
|
+
| `results.maxStashEntries` | 64 per runtime |
|
|
255
|
+
| Top-level discovery result ceiling | 256,000 UTF-8 bytes |
|
|
256
|
+
| Downstream MCP `isError` text | 512 UTF-8 bytes plus an `…` marker |
|
|
257
|
+
|
|
258
|
+
The discovery ceiling counts text, `structuredContent`, and JSON escaping
|
|
259
|
+
together, because measuring one copy would advertise half the bytes the adapter
|
|
260
|
+
actually returns; error framing may shorten a bounded `isError` reason further to
|
|
261
|
+
fit the call's result cap on the same arithmetic. Both stash options accept
|
|
262
|
+
non-negative safe integers, zero disabling stashing, and are shared across all
|
|
263
|
+
subjects and pools; they count the stored ASCII paging envelope, base64 overhead
|
|
264
|
+
included, not only the result text. Capacity is reserved before each storage
|
|
265
|
+
write so concurrent requests cannot oversubscribe it, and a full stash refuses
|
|
266
|
+
new entries. A later attempt deletes expired entries before reusing their
|
|
267
|
+
capacity; a failed deletion keeps the charge. These bounds cover writes by this
|
|
268
|
+
runtime — not other processes, Worker isolates, or entries a previous runtime
|
|
269
|
+
left behind.
|
|
270
|
+
|
|
271
|
+
Results belong to the authenticated subject whenever auth supplies a subject or
|
|
272
|
+
user id, independently of activity configuration, under the provider's namespace
|
|
273
|
+
when it has one and `connecta:auth:<provider kind>` otherwise. Keep subject ids
|
|
274
|
+
distinct within that namespace. An explicit principal is the fallback subject
|
|
275
|
+
when neither id is supplied, and open deployments and auth providers that supply
|
|
276
|
+
no identity share one partition.
|
|
277
|
+
|
|
278
|
+
New entries store UTF-8 bytes in a base64 envelope split across storage keys,
|
|
279
|
+
48 KiB of result text per chunk — widening past roughly 1.5 MB so no result
|
|
280
|
+
occupies more than 33 keys, because every chunk is also a write. `get_result`
|
|
281
|
+
reads and decodes only the chunks a page covers plus a few boundary bytes, so
|
|
282
|
+
paging a 1.2 MB result costs the same per page as paging a 300 KB one; it
|
|
283
|
+
neither encodes nor reads the whole result per page. Pre-upgrade entries remain readable during their TTL — the earlier
|
|
284
|
+
single-key envelope reads one full value per page, and raw text before that
|
|
285
|
+
also pays one full encoding. Offsets and `totalBytes` always describe the
|
|
250
286
|
original UTF-8 text, not the envelope. A supplied offset inside a character
|
|
251
287
|
moves back to its start; page ends also align to character boundaries, and a
|
|
252
288
|
page smaller than one character widens just enough to make progress.
|
|
253
289
|
|
|
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.
|
|
277
|
-
|
|
278
290
|
A per-call `timeoutMs` covers catalog resolution, admission, and connector
|
|
279
|
-
execution
|
|
280
|
-
sooner
|
|
281
|
-
|
|
291
|
+
execution under one deadline. The admission queue's own timeout may expire
|
|
292
|
+
sooner but cannot extend the call deadline. Result processing happens after that
|
|
293
|
+
deadline ends, because a completed downstream call must not turn into a
|
|
282
294
|
retryable timeout while Connecta prepares its response.
|
|
283
295
|
|
|
296
|
+
### Truncated direct-call results
|
|
297
|
+
|
|
298
|
+
A `call_tool` or `call_destructive_tool` result over its cap — the connector's
|
|
299
|
+
`maxResultBytes`, else `calls.maxResultBytes`, else 24,000 bytes — comes back
|
|
300
|
+
as one text block. Its first line is the truncation notice, one line of compact
|
|
301
|
+
JSON; everything after the first newline is the preview:
|
|
302
|
+
|
|
303
|
+
```text
|
|
304
|
+
{"truncated":true,"resultId":"…","totalBytes":161420,"hint":"This write already ran: do not call it again to see its result. Bytes 0-24000 of 161420 follow; page the rest with get_result using nextAction.","nextAction":{"tool":"get_result","arguments":{"id":"…","offset":24000}}}
|
|
305
|
+
{"ts":"2026-09-16T21:31:11.759Z","actor":"sam.ortiz@example.com",…
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The notice leads because clients cut oversized results from the end, and a
|
|
309
|
+
notice at the tail is the part they drop. Claude Code (measured on 2.1.280, from
|
|
310
|
+
its bundled source and with `claude -p` against a probe server) passes an MCP
|
|
311
|
+
result through untouched while its text blocks total at most 50,000 characters.
|
|
312
|
+
One character more and it replaces the whole result with a pointer to a spill
|
|
313
|
+
file plus the first 2,000 characters of `JSON.stringify(content, null, 2)`,
|
|
314
|
+
whatever `MAX_MCP_OUTPUT_TOKENS` says. Once the characters exceed twice
|
|
315
|
+
`MAX_MCP_OUTPUT_TOKENS` (25,000 by default, so again 50,000) it also counts real
|
|
316
|
+
tokens against that limit, and a result over it arrives as an error carrying no
|
|
317
|
+
content at all — no preview, no notice. The old layout — a
|
|
318
|
+
50,000-byte preview, then the notice — crossed the first line every time, so an
|
|
319
|
+
agent without file tools never saw the handle; in the eval's write task two of
|
|
320
|
+
three models re-ran a billable export three times trying to read its result.
|
|
321
|
+
|
|
322
|
+
The default cap sits under both lines with room to spare. Preview plus notice
|
|
323
|
+
stays under 25,000 bytes, which is under 50,000 characters for any text, because
|
|
324
|
+
a character is at least one UTF-8 byte, and under 25,000 tokens for any text,
|
|
325
|
+
because a token covers at least one byte. A `get_result` page obeys the same
|
|
326
|
+
cap, and 24,000 matches the program result boundary in
|
|
327
|
+
[code mode](./code-mode.md). A deployment whose
|
|
328
|
+
clients take more can raise the cap, and one whose clients take less can lower
|
|
329
|
+
it, per connector if need be; a result between 24,000 and 50,000 bytes that
|
|
330
|
+
used to arrive whole now pages. A client that spills rather than rejects keeps
|
|
331
|
+
the notice whatever its threshold, since the notice is the first few hundred
|
|
332
|
+
characters. One that rejects outright — Claude Code with
|
|
333
|
+
`MAX_MCP_OUTPUT_TOKENS` set below 12,500 — needs a cap under twice its limit.
|
|
334
|
+
|
|
335
|
+
The preview is the head of what `get_result` pages, cut on a character
|
|
336
|
+
boundary, so `nextAction.arguments.offset` continues exactly where it stops. A
|
|
337
|
+
lone text block — including the one synthesized from `structuredContent` — is
|
|
338
|
+
measured, stashed, previewed, and paged as its text: wrapping it in a content
|
|
339
|
+
envelope added nothing but a second layer of JSON escaping, so a JSON payload
|
|
340
|
+
used to preview as `[{"type":"text","text":"{\"ts\":…` and every page carried
|
|
341
|
+
the escaping twice. Several text blocks keep the serialized envelope as the one
|
|
342
|
+
string measured, stashed, and paged, because their boundaries live there; they
|
|
343
|
+
preview as their text joined by newlines, and their next action starts at
|
|
344
|
+
offset 0. A content array carrying non-text blocks returns the notice alone,
|
|
345
|
+
because the head of a half-written base64 image helps nobody. In value mode the
|
|
346
|
+
notice is `data` itself, with no preview and a next action at offset 0.
|
|
347
|
+
|
|
348
|
+
The hint says what to do next. For a call not explicitly annotated read-only it
|
|
349
|
+
opens with “This write already ran: do not call it again to see its result.”
|
|
350
|
+
The approved write happened; repeating it to look again is how one export
|
|
351
|
+
becomes three. For an explicitly read-only call, repeating is the better route,
|
|
352
|
+
so the hint offers it first: to find something specific, repeat the read inside
|
|
353
|
+
`execute_code` with `connecta.call` and filter or search the result there; to
|
|
354
|
+
read it in full, page with `get_result`. Inside a program the inline cap does
|
|
355
|
+
not apply to a host call's result, only to what the program returns (`L6` in
|
|
356
|
+
[code mode](./code-mode.md) bounds a host call far higher), so the reduction
|
|
357
|
+
sees the whole result at once. When the hint named paging alone, the eval's
|
|
358
|
+
weakest model read a 185 KB CI log in 24,000-byte pages, skipped the range
|
|
359
|
+
holding the real failure, and named the flaky test near the top; before the
|
|
360
|
+
notice was visible, it had reduced the log in a program and found the failure.
|
|
361
|
+
`nextAction` stays the page handle for either kind of call, because paging is
|
|
362
|
+
the one next step connecta can spell out exactly, while a reduction is a
|
|
363
|
+
program the agent has to write. `resultId` stays beside the exact `nextAction`,
|
|
364
|
+
so the handle is actionable without copying an identifier out of prose.
|
|
365
|
+
Program results and oversized discovery responses carry no such route: paging a
|
|
366
|
+
program's return value is a refused shape, because a program can shrink
|
|
367
|
+
anything before it returns.
|
|
368
|
+
|
|
369
|
+
### Paging with get_result
|
|
370
|
+
|
|
371
|
+
A page is the truncated result's shape again: one text block whose first line
|
|
372
|
+
is a JSON header and whose remainder is the page, raw.
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
{"resultId":"…","offset":24000,"bytes":24000,"totalBytes":161420,"hasMore":true,"nextAction":{"tool":"get_result","arguments":{"id":"…","offset":48000}}}
|
|
376
|
+
{"ts":"2026-09-17T23:42:55.357Z","actor":"noor.haddad@example.com",…
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
`bytes` is what this page returned; `hasMore` is false on the last page, which
|
|
380
|
+
carries no `nextAction`. The page is the stashed text as-is — a lone text
|
|
381
|
+
block's own text, a value's compact JSON, several blocks' serialized content
|
|
382
|
+
array — and never a JSON string holding it. Pages used to arrive as
|
|
383
|
+
`{ offset, nextOffset?, totalBytes, text }`, which escaped every quote and
|
|
384
|
+
newline in the page: a second layer over JSON payloads that made them hard to
|
|
385
|
+
read and swelled a 20,000-byte page of JSON lines to 25,000 characters or more.
|
|
386
|
+
|
|
387
|
+
`maxBytes` is an upper bound clamped to the inline cap of the call that stashed
|
|
388
|
+
the result — the connector's override when it had one, recorded in the stash
|
|
389
|
+
entry — and a clamped request is answered, not refused, with `bytes` saying how
|
|
390
|
+
much came back. Clients cut an oversized page exactly as they cut the original
|
|
391
|
+
result. In the eval, agents that could finally see the handle asked for pages of
|
|
392
|
+
50,000 bytes and more, and Claude Code rejected every one of those answers
|
|
393
|
+
outright, leaving an agent that had just been told not to repeat a write with
|
|
394
|
+
nothing to read. So no response to a truncated call or to a page request
|
|
395
|
+
exceeds the cap plus a header of a few hundred bytes. Value mode's unpageable
|
|
396
|
+
preview is cut short enough that its JSON escaping still fits. Entries stashed
|
|
397
|
+
before the cap was recorded page at the deployment cap.
|
|
398
|
+
|
|
399
|
+
A refused or failed stash write cannot undo a downstream success. Both call tools
|
|
400
|
+
then return the same layout with a paging-unavailable notice and no `resultId`
|
|
401
|
+
or paging action — the notice first, then the preview where one is usable, and
|
|
402
|
+
for a write the same warning not to repeat it. Activity records success, and
|
|
403
|
+
the operator logger receives a fixed warning naming the connector and tool
|
|
404
|
+
without storage error prose. For read-only work, reduce the result inside
|
|
405
|
+
`execute_code` — repeating an approved write is not a way to recover its output. Other result-processing failures use a fixed
|
|
406
|
+
`result_processing_failed` message and are never retryable.
|
|
407
|
+
|
|
284
408
|
## Lexical discovery
|
|
285
409
|
|
|
286
410
|
`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
|
|
411
|
+
camel-case boundaries. Exact whole-token matches carry the most weight, and a
|
|
412
|
+
small set of inflectional variants preserves singular/plural and verb-form
|
|
413
|
+
recall without admitting arbitrary mid-word substrings. Each query term is
|
|
414
|
+
weighted by its document frequency across the catalogs available to that search,
|
|
415
|
+
so a rare domain term outranks a ubiquitous action while action terms still
|
|
416
|
+
distinguish `get`, `list`, `search`, and write operations. Complete matches rank
|
|
417
|
+
before ordinary partial matches; a partial candidate whose complete normalized
|
|
418
|
+
tool name occurs in the normalized raw query competes with complete matches by
|
|
419
|
+
score, and other candidates covering at least two terms fill the remaining page.
|
|
420
|
+
Conversational cleanup applies to scoring terms only, never to the exact-name
|
|
299
421
|
phrase check. If no tool covers every non-conversational term, the same scorer
|
|
300
422
|
preserves the wider any-term fallback and marks the result
|
|
301
423
|
`matchMode: "partial"`.
|
|
302
424
|
|
|
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.
|
|
425
|
+
Tool rows expose neither lexical scores nor per-result query coverage. Select
|
|
426
|
+
from the returned purpose, address, schema, safety, and output shape; page-level
|
|
427
|
+
`queryAnalysis` is the recovery path when no single result covers every term or
|
|
428
|
+
none exists. It reports `representedTerms` (in the current page),
|
|
429
|
+
`otherResultTerms` (only in another result), and `unmatchedTerms` (no lexical
|
|
430
|
+
match in the catalogs that answered), covers at most eight distinct terms of at
|
|
431
|
+
most 64 displayed characters each while marking longer input `truncated`, and
|
|
432
|
+
never changes lexical ranking. A non-empty query that normalizes to no ASCII
|
|
433
|
+
lexical terms returns no tools rather than unrelated browse results, with the
|
|
434
|
+
clipped raw query in `unmatchedTerms`; a mixed query searches with its ASCII
|
|
435
|
+
terms, and unsupported characters become neither false matches nor coverage
|
|
436
|
+
terms.
|
|
437
|
+
|
|
438
|
+
What the analysis says next depends on why the page is thin:
|
|
439
|
+
|
|
440
|
+
| Case | Fields and guidance |
|
|
441
|
+
| --- | --- |
|
|
442
|
+
| Partial match | no single tool covered every term; split distinct intents |
|
|
443
|
+
| True negative | no matching capability is configured; refine, connector-scope, or browse |
|
|
444
|
+
| True negative naming a configured connector | the same, plus up to three such connectors named by id and a pointer to a scoped browse |
|
|
445
|
+
| Unscoped search or browse with some connector unavailable | `unavailableConnectorCount` alone |
|
|
446
|
+
| Scoped to an unavailable connector | `unavailableConnectorCount`, `catalogError`, guidance |
|
|
447
|
+
| Scoped to an unconfigured id | `connectorScope`, `unknownConnector`, omit-the-connector guidance |
|
|
448
|
+
| Connector configured and genuinely exposing no tools | no analysis |
|
|
449
|
+
|
|
450
|
+
Three of those rows earn their asymmetry. An unavailable catalog downgrades the
|
|
451
|
+
claim, because a search may not report that nothing is configured when it does
|
|
452
|
+
not know. Connector identity is deliberately absent from the lexical index —
|
|
453
|
+
indexing it would move ranking for every query that already matches tools — so a
|
|
454
|
+
query naming one gets exactly one extra sentence and nothing else: no ranking
|
|
455
|
+
change, no result, no new field. And a browse scoped to an unavailable connector
|
|
456
|
+
must not serialize like a connector that genuinely has no tools, or the advice to
|
|
457
|
+
browse lands in silence that reads like an answer.
|
|
458
|
+
|
|
459
|
+
`catalogError` is the bounded classified failure — `code`, `message`,
|
|
460
|
+
`retryable`, and any `retryAfterMs` — so a caller can tell a transient outage
|
|
461
|
+
from one an operator must clear, and nothing else the call-path classifier knows,
|
|
462
|
+
because a discovery read is not a call. Only an explicitly scoped search gets it:
|
|
463
|
+
one connector's failure is not another search's context, and a scope that was
|
|
464
|
+
never configured gets neither it nor a count, since nothing was attempted.
|
|
354
465
|
|
|
355
466
|
## Authorization recovery
|
|
356
467
|
|
|
@@ -391,113 +502,79 @@ The tool accepts no secret. `force` applies only to OAuth and may discard its
|
|
|
391
502
|
stored grant before restarting consent. Static credential values are written
|
|
392
503
|
only through the same-origin interactive-user credential route, and only for a
|
|
393
504
|
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.
|
|
505
|
+
permission; OAuth start, `force` included, requires that permission too. Core
|
|
506
|
+
callbacks work without the UI for authorized interactive callers. After OAuth
|
|
507
|
+
consent or a human update, retry the original operation; a static update is read
|
|
508
|
+
from the vault on the next call and needs no redeploy.
|
|
398
509
|
|
|
399
510
|
## Routing recovery
|
|
400
511
|
|
|
401
|
-
Predictable local refusals carry structured recovery on both result modes.
|
|
402
|
-
|
|
512
|
+
Predictable local refusals carry structured recovery on both result modes. An
|
|
513
|
+
unknown connector suggests an unscoped discovery query derived from the
|
|
403
514
|
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.
|
|
515
|
+
answered. The suggested route follows the route the caller took:
|
|
516
|
+
`tool: "search_tools"` for a top-level call, `function: "connecta.search"` with
|
|
517
|
+
the same arguments when the miss happened inside `execute_code`, which has no
|
|
518
|
+
way to call a tool. A read path that reaches an unannotated, write-capable, or
|
|
519
|
+
destructive tool returns `nextAction` for `call_destructive_tool` with the
|
|
520
|
+
canonical address. Nothing is executed by these records.
|
|
521
|
+
|
|
522
|
+
`connecta.describe` keeps its failures inline instead, so one miss cannot
|
|
523
|
+
discard the other schemas; each failed entry carries a human `error`, typed
|
|
524
|
+
`errorDetails`, the same route-aware discovery action, and — on a close
|
|
525
|
+
tool-name miss against a known connector — at most three deterministically
|
|
526
|
+
ranked canonical `suggestions` with no scores and no descriptions. A
|
|
527
|
+
catalog-load failure carries only `code`, bounded `message`, `retryable`, and
|
|
528
|
+
any `retryAfterMs`: discovery does not inherit later additions to the
|
|
529
|
+
call-failure envelope. The per-entry rules are
|
|
530
|
+
[code mode](./code-mode.md#connectadescribe)'s `S4`.
|
|
531
|
+
|
|
532
|
+
### Echo budgets
|
|
533
|
+
|
|
534
|
+
An error envelope is not size-guarded the way a result is, and every echoed byte
|
|
535
|
+
lands twice — in the text content and in `structuredContent` — so a 50 KB
|
|
536
|
+
invented address once produced a 200 KB refusal against a 1 KB result cap. Every
|
|
537
|
+
caller-authored string a refusal repeats therefore gets 512 UTF-8 bytes, and
|
|
538
|
+
what happens over budget differs by field.
|
|
539
|
+
|
|
540
|
+
| Field | Over 512 UTF-8 bytes |
|
|
541
|
+
| --- | --- |
|
|
542
|
+
| `args` on `call_tool` and `connecta.call` | dropped whole; `purpose` says to re-send what was just sent |
|
|
543
|
+
| the attempted address | clamped with a trailing `…` |
|
|
544
|
+
| unknown `get_result.id`, `authorize_connector.connector`, `skills.name` | clamped the same way |
|
|
545
|
+
| `search_tools.connector` | rejected with `invalid_args` before catalog lookup |
|
|
546
|
+
|
|
547
|
+
Arguments go all or nothing because the agent already holds what it sent, and
|
|
548
|
+
half of it would describe a call nobody made. The address gets the opposite rule
|
|
549
|
+
because it is the thing being corrected: a clipped one still identifies the
|
|
550
|
+
mistake, and a short one — every real one — comes back exact and untagged. A
|
|
551
|
+
scope is rejected outright because a clipped one could select a different
|
|
552
|
+
connector. A failed result-storage read returns typed `unavailable` without
|
|
553
|
+
exposing backend error text.
|
|
554
|
+
|
|
555
|
+
Activity records each of these refusals with the coarse `friction` class derived
|
|
556
|
+
from its typed error code — `tool_not_found`, `schema_retry`,
|
|
557
|
+
`destructive_reroute`, `auth_required` — and clamps the caller-authored
|
|
558
|
+
`connectorId`, `toolName`, and `address` it keeps, both under
|
|
559
|
+
[code mode](./code-mode.md#activity)'s `V2` and `V3`. A `call_tool` result too
|
|
560
|
+
large to return inline is the one exception: it is friction
|
|
561
|
+
(`result_too_large`) on a call whose `outcome` is still `"success"`, so it
|
|
562
|
+
carries no `errorCode`.
|
|
479
563
|
|
|
480
564
|
## Argument recovery
|
|
481
565
|
|
|
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.
|
|
566
|
+
A remote MCP tool's advertised `inputSchema` is checked in the shared invocation
|
|
567
|
+
path before admission and provider dispatch, so a mismatch is the non-retryable
|
|
568
|
+
`invalid_args` identically on `call_tool`, `call_destructive_tool`,
|
|
569
|
+
generated-code failures, and rejected promises. The error names the connector and
|
|
570
|
+
operation and carries `validation.issues`: JSON Pointer `path`, schema-keyword
|
|
571
|
+
`code`, and expected shape, with submitted values never copied into a finding. At
|
|
572
|
+
most three are returned, and `validation.truncated` says when more exist — the
|
|
573
|
+
same three-item bound describe's nearby-address list uses.
|
|
574
|
+
|
|
575
|
+
`nextAction` points to discovery scoped to the same connector and tool name when
|
|
576
|
+
the compact schema is needed, routed like any other miss: a program to
|
|
577
|
+
`connecta.search`, a top-level call to `search_tools`. `retry` says to correct
|
|
578
|
+
the listed arguments and reissue the original operation. Which keyword a finding
|
|
579
|
+
names, and what the local validator declines to evaluate, is
|
|
580
|
+
[code mode](./code-mode.md#errors)'s `E8`.
|