@zackbart/connecta 0.40.0 → 0.42.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/CHANGELOG.md +148 -0
- package/bin/connecta.mjs +26 -17
- package/bin/meta-tool-names.d.mts +3 -1
- package/bin/meta-tool-names.mjs +4 -1
- package/bin/version.mjs +1 -1
- package/dist/activity.d.ts +32 -5
- package/dist/activity.js +32 -6
- package/dist/advertised-schema.js +15 -0
- package/dist/agent-observation.js +24 -2
- package/dist/agent-routes.js +33 -44
- package/dist/auth/clerk.d.ts +6 -1
- package/dist/auth/clerk.js +16 -0
- package/dist/auth/cloudflare-access.js +49 -7
- package/dist/auth/downstream-oauth/consent.js +79 -29
- package/dist/auth/downstream-oauth/grant-store.js +58 -7
- package/dist/auth/downstream-oauth/refresh-coordinator.js +4 -1
- package/dist/auth/downstream-oauth/refresh-records.js +6 -5
- package/dist/auth/downstream-oauth.js +15 -4
- package/dist/auth/oauth-request-not-sent.js +4 -0
- package/dist/auth/static-oauth.js +77 -39
- package/dist/auth-elicitation.js +118 -61
- package/dist/authorization-handoff.js +36 -3
- package/dist/catalog-cache.js +352 -43
- package/dist/catalog-freshness.js +25 -0
- package/dist/catalog-service.js +408 -78
- package/dist/catalog.js +216 -62
- package/dist/config-defaults.d.ts +2 -1
- package/dist/config-defaults.js +4 -1
- package/dist/config.d.ts +53 -21
- package/dist/config.js +56 -17
- package/dist/connectors/remote-mcp/call.js +1 -1
- package/dist/connectors/remote-mcp/catalog.js +52 -28
- package/dist/connectors/remote-mcp/connection.js +113 -22
- package/dist/connectors/remote-mcp/oauth.js +2 -2
- package/dist/connectors/remote-mcp/resources.js +5 -2
- package/dist/connectors/remote-mcp/transport.js +24 -6
- package/dist/connectors/remote-mcp.js +14 -5
- package/dist/credential-contract.d.ts +3 -1
- package/dist/describe-config.d.ts +4 -2
- package/dist/describe-config.js +1 -0
- package/dist/discovery-health.js +26 -11
- package/dist/downstream-input.js +11 -2
- package/dist/errors.d.ts +60 -4
- package/dist/errors.js +88 -3
- package/dist/execute.js +491 -202
- package/dist/executor-admission.js +2 -0
- package/dist/executor-contract.js +6 -0
- package/dist/executor-result.js +3 -3
- package/dist/executors/quickjs-child.js +14 -5
- package/dist/executors/quickjs-runtime.d.ts +2 -0
- package/dist/executors/quickjs-runtime.js +37 -20
- package/dist/executors/quickjs.js +24 -7
- package/dist/guarded-transport.js +2 -0
- package/dist/guest-functions.js +5 -4
- package/dist/guest-runtime.js +37 -1
- package/dist/guest-types.d.ts +18 -0
- package/dist/http/client.js +14 -2
- package/dist/http/errors.js +9 -4
- package/dist/http/fields.js +17 -5
- package/dist/http/hatch.js +11 -3
- package/dist/http/node-transport.js +110 -4
- package/dist/http/oauth.js +17 -6
- package/dist/http/paging.js +1 -1
- package/dist/http/provider.js +2 -6
- package/dist/http/vendor.js +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -21
- package/dist/invocation-failure.js +28 -0
- package/dist/invocation.js +167 -64
- package/dist/membership.d.ts +1 -1
- package/dist/meta-output.js +24 -18
- package/dist/meta-tool-names.d.ts +1 -1
- package/dist/meta-tool-names.js +1 -1
- package/dist/meta-tools.js +286 -73
- package/dist/module-contracts.d.ts +3 -1
- package/dist/native-recovery.js +4 -1
- package/dist/native-tools.js +177 -55
- package/dist/node.d.ts +7 -0
- package/dist/node.js +85 -4
- package/dist/oauth-handoff.js +53 -43
- package/dist/operation-activity.js +104 -0
- package/dist/operation-vocabulary.js +6 -0
- package/dist/operator-record.d.ts +9 -1
- package/dist/operator-record.js +16 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/program-result.js +2 -0
- package/dist/program-source.js +1 -1
- package/dist/program-writes.js +112 -19
- package/dist/provider.d.ts +1 -14
- package/dist/provider.js +0 -3
- package/dist/providers/_shared/google/kit.js +2 -2
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/breeze/api.js +2 -2
- package/dist/providers/breeze/index.js +1 -1
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/ccb/api.js +2 -2
- package/dist/providers/ccb/index.js +1 -1
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/cloudflare/api.js +2 -2
- package/dist/providers/cloudflare/index.js +3 -1
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/index.js +1 -1
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/forms/index.js +2 -1
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/github/api.js +98 -6
- package/dist/providers/github/index.js +8 -3
- package/dist/providers/github/lib/auth.d.ts +3 -1
- package/dist/providers/github/lib/auth.js +43 -12
- package/dist/providers/github/lib/discovery.js +11 -1
- package/dist/providers/github/lib/operations.js +8 -0
- package/dist/providers/github/safety.js +39 -0
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +12 -5
- package/dist/providers/gmail/index.js +2 -1
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/granola/index.js +1 -1
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/index.js +3 -1
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/planning-center/index.js +12 -6
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/skill.generated.js +1 -1
- package/dist/providers/quickbooks/api.js +28 -25
- package/dist/providers/quickbooks/index.js +3 -6
- package/dist/providers/quickbooks/lib/writes.js +55 -0
- package/dist/providers/quickbooks/safety.js +17 -5
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/index.js +2 -1
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/slides/index.js +3 -3
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/stripe/index.js +2 -2
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/index.js +2 -1
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/vercel/api.js +3 -2
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/registry.d.ts +36 -4
- package/dist/registry.js +142 -30
- package/dist/resource-read.js +21 -2
- package/dist/result-preview.js +210 -0
- package/dist/result-seal.js +168 -0
- package/dist/route-names.js +53 -0
- package/dist/routes/activity-context.js +1 -0
- package/dist/routes/activity-disclosure.js +26 -4
- package/dist/routes/activity.js +2 -1
- package/dist/routes/connect.js +21 -13
- package/dist/routes/mcp.js +83 -35
- package/dist/routes/shared.d.ts +6 -0
- package/dist/routes/ui-config.js +1 -1
- package/dist/routes/ui-data.js +3 -2
- package/dist/runtime/call.js +15 -10
- package/dist/runtime/native-storage.js +12 -0
- package/dist/runtime/request-reads.js +66 -14
- package/dist/runtime/request-scope.js +38 -2
- package/dist/runtime/run.js +4 -1
- package/dist/sent-secrets.d.ts +20 -0
- package/dist/sent-secrets.js +49 -1
- package/dist/skill-origin.js +16 -0
- package/dist/skills.js +213 -57
- package/dist/storage/d1-driver.js +33 -5
- package/dist/storage/keys.js +16 -7
- package/dist/storage/sql-native.js +60 -3
- package/dist/storage/sql.js +6 -0
- package/dist/timing-trace.js +84 -0
- package/dist/tool-safety.d.ts +33 -0
- package/dist/tool-safety.js +51 -15
- package/dist/types.d.ts +36 -0
- package/dist/url-safety.js +38 -20
- package/dist/usage-guide.js +14 -13
- package/dist/utility-recovery.js +33 -0
- package/dist/validate.js +29 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +60 -15
- package/documentation/architecture.md +152 -43
- package/documentation/auth.md +160 -53
- package/documentation/code-mode.md +396 -119
- package/documentation/deploying.md +35 -10
- package/documentation/hosted-mcp-migration.md +9 -3
- package/documentation/integrating.md +27 -5
- package/documentation/meta-tools.md +761 -157
- package/documentation/operating.md +91 -4
- package/documentation/operator-ui.md +51 -2
- package/documentation/providers.md +34 -20
- package/examples/worker/README.md +3 -3
- package/guides/how-it-works.md +2 -1
- package/guides/safety-and-trust.md +1 -3
- package/package.json +10 -8
- package/templates/node/AGENTS.md +3 -3
- package/templates/node/README.md +1 -1
- package/templates/node/package.json +1 -1
|
@@ -18,29 +18,32 @@ Start with [Operating an endpoint](./operating.md) for a workflow, or
|
|
|
18
18
|
[the agent index](./README.md) for setup and repository tasks. This reference
|
|
19
19
|
owns the top-level MCP contract; it does not require reading the core internals.
|
|
20
20
|
|
|
21
|
-
| Task | Sections
|
|
22
|
-
| -------------------------------------------------- |
|
|
23
|
-
| Choose a tool or construct its arguments | [
|
|
24
|
-
| Find an operation and its schema | [Discovery context](#discovery-context), [TypeScript signatures](#typescript-signatures), [lexical discovery](#lexical-discovery)
|
|
25
|
-
| Load provider instructions or native skills | [Connector guides](#connector-guides-and-skills), [native extension](#native-skills-extension)
|
|
26
|
-
| Interpret a direct response or page a large result | [Representation](#result-representation), [truncation](#truncated-direct-call-results), [paging](#paging-with-connectaresult)
|
|
27
|
-
| Recover a refused or invalid call | [Authorization](#authorization-recovery), [routing](#routing-recovery), [arguments](#argument-recovery), [echo budgets](#echo-budgets)
|
|
28
|
-
| List downstream tools directly, or connect Eve | [Native tool surface](#native-tool-surface), [Eve agents](#eve-agents)
|
|
29
|
-
|
|
|
21
|
+
| Task | Sections |
|
|
22
|
+
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| Choose a tool or construct its arguments | [Meta-tool table](#the-meta-tools), [call routing](#routing-between-the-call-surfaces), [downstream input](#downstream-input-on-direct-calls) |
|
|
24
|
+
| Find an operation and its schema | [Discovery context](#discovery-context), [TypeScript signatures](#typescript-signatures), [lexical discovery](#lexical-discovery) |
|
|
25
|
+
| Load provider instructions or native skills | [Connector guides](#connector-guides-and-skills), [native extension](#native-skills-extension) |
|
|
26
|
+
| Interpret a direct response or page a large result | [Representation](#result-representation), [truncation](#truncated-direct-call-results), [paging](#paging-with-connectaresult) |
|
|
27
|
+
| Recover a refused or invalid call | [Authorization](#authorization-recovery), [routing](#routing-recovery), [arguments](#argument-recovery), [echo budgets](#echo-budgets) |
|
|
28
|
+
| List downstream tools directly, or connect Eve | [Native tool surface](#native-tool-surface), [Eve agents](#eve-agents) |
|
|
29
|
+
| Read an endpoint's surface and connector roster | [Endpoint facts](#endpoint-facts) |
|
|
30
|
+
| Change the contract | [Source and tests](#source-and-tests), [guest API](./code-mode.md#verification) |
|
|
30
31
|
|
|
31
32
|
## Tool presentation
|
|
32
33
|
|
|
33
|
-
The default surface is the
|
|
34
|
+
The default surface is the meta-tools below. From 0.35.0, pools can use
|
|
34
35
|
[native tool presentation](#native-tool-surface) for clients with their own
|
|
35
36
|
tool search. Both presentations use the same invocation enforcement; native
|
|
36
37
|
pools keep code available by default.
|
|
37
38
|
|
|
38
|
-
## The
|
|
39
|
+
## The meta-tools
|
|
39
40
|
|
|
40
|
-
Every deployment requires an executor. A meta endpoint's `tools/list` is
|
|
41
|
-
|
|
42
|
-
[
|
|
43
|
-
|
|
41
|
+
Every deployment requires an executor. A meta endpoint's `tools/list` is exactly
|
|
42
|
+
these six, plus `execute_read_code` on a writable `/mcp` or `/mcp/<pool>` unless
|
|
43
|
+
`execute.readRoute` is `false` ([read programs](#read-programs)). A read-only
|
|
44
|
+
endpoint and `/agent/<pool>` list six. This is the default; a pool may instead use the
|
|
45
|
+
[native tool surface](#native-tool-surface). No other configuration changes the
|
|
46
|
+
list, so a client's cached meta-tool list never depends on
|
|
44
47
|
the storage behind the deployment. `execute_code`
|
|
45
48
|
is annotated `readOnlyHint: true, destructiveHint: false` on a read-only
|
|
46
49
|
endpoint, and `readOnlyHint: false, destructiveHint: true` on a writable one.
|
|
@@ -48,13 +51,20 @@ Approval belongs to the client. A read-only endpoint keeps all six tools, omits
|
|
|
48
51
|
write tools from `search_tools` results and describe, and refuses every write
|
|
49
52
|
call. Connector `readOnly` applies the same restriction on every endpoint; effective read-only is endpoint OR connector. Neither setting changes connector visibility or read grants. Guides
|
|
50
53
|
(`queryAnalysis.guideSummary`, `skills`, `connecta.skill`, skill
|
|
51
|
-
`resources/read`) are documentation and may name tools the endpoint refuses. The
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
`resources/read`) are documentation and may name tools the endpoint refuses. The three tools with structured-only success results
|
|
55
|
+
(`execute_code`, `search_tools`, `authorize_connector`) advertise an open
|
|
56
|
+
`outputSchema`: required keys and top-level types, with every other field open.
|
|
57
|
+
The schemas declare no `$schema`, and validators for drafts 4, 7, 2019-09 and
|
|
58
|
+
2020-12 accept the same results. The server checks each successful result against
|
|
59
|
+
its full contract before sending it, so the shapes this guide states hold though
|
|
60
|
+
`tools/list` omits them. Both SDK edges exempt `isError: true` responses from
|
|
61
|
+
output-schema validation. Failures on a `search_tools` page are one such shape: each
|
|
62
|
+
`catalogErrors[]` entry and `queryAnalysis.catalogError` requires `code`, `message`
|
|
63
|
+
and `retryable`, may carry `retryAfterMs`, `retry`, `nextAction`, `stale`, `ageMs`
|
|
64
|
+
and the authorization-handoff fields (`recovery`, `status`, `authorizationUrl`,
|
|
65
|
+
`operatorUrl`, `instructions`, `credential`), and has no others; a `catalogErrors[]`
|
|
66
|
+
entry also requires `connector`, which `queryAnalysis.catalogError` may carry.
|
|
67
|
+
The two direct-call tools omit `outputSchema` because their default
|
|
58
68
|
mode returns native `content`. Default-mode successes declare
|
|
59
69
|
`_meta["dev.connecta/format"]: "json" | "text"`; value mode places the value in
|
|
60
70
|
`data` and declares `format` in its complete result envelope.
|
|
@@ -62,6 +72,7 @@ mode returns native `content`. Default-mode successes declare
|
|
|
62
72
|
| Tool | Arguments | Returns |
|
|
63
73
|
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
64
74
|
| `execute_code` | `code`, `diagnostics?` | `{ result?, error?, hostCalls }`, plus `logs`, `emitted`, and `emittedDiscarded` when present and diagnostics when asked |
|
|
75
|
+
| `execute_read_code` | the same | the same; [read programs](#read-programs) lists where it appears and what it refuses |
|
|
65
76
|
| `search_tools` | `query?`, `connector?`, `safety?`, `limit?`, `offset?`, `fullDescriptions?`, `includeSchemas?: "compact" \| "json" \| "typescript"` | `{ catalogErrors, tools, total, offset, limit, hasMore }`, plus `nextOffset` when more remain, `absence` when the query names a service with no configured connector, and `queryAnalysis` on a partial, failed, unsearchable, or absent-service search |
|
|
66
77
|
| `call_tool` | `address`, `args?`, `resultMode?: "mcp" \| "value"`, `timeoutMs?`, `diagnostics?` | the downstream result, bounded as [result representation](#result-representation) describes |
|
|
67
78
|
| `call_destructive_tool` | the same, plus `reason?` | the same |
|
|
@@ -96,10 +107,65 @@ endpoint, the `call_tool`, `call_destructive_tool`, and `search_tools`
|
|
|
96
107
|
descriptions say so too; `call_destructive_tool` still runs reads there. `call_destructive_tool.reason` is dropped before
|
|
97
108
|
anything runs.
|
|
98
109
|
|
|
110
|
+
## Read programs
|
|
111
|
+
|
|
112
|
+
`execute_read_code` runs a program with the same guest API, sandbox and budgets as
|
|
113
|
+
`execute_code`, annotated `readOnlyHint: true, destructiveHint: false`, so a host
|
|
114
|
+
that gates on annotations does not prompt for a read-only program. On a writable
|
|
115
|
+
endpoint `execute_code` stays annotated as a write.
|
|
116
|
+
|
|
117
|
+
| Endpoint | Lists `execute_read_code` |
|
|
118
|
+
| --------------------------------------- | --------------------------------------------- |
|
|
119
|
+
| Writable `/mcp`, writable `/mcp/<pool>` | Yes, unless `execute.readRoute: false` |
|
|
120
|
+
| Read-only endpoint | No: `execute_code` is already read-only there |
|
|
121
|
+
| `/agent/<pool>` | No, for now |
|
|
122
|
+
| Native pool | No |
|
|
123
|
+
|
|
124
|
+
**Refusal.** Every call whose effective or source class is write is refused below
|
|
125
|
+
the sandbox, before validation and dispatch, with `retryable: false` and a message
|
|
126
|
+
naming `call_destructive_tool` and `execute_code`. Its code is
|
|
127
|
+
`destructive_tool_requires_approval`, or `read_only_endpoint` for a read-only
|
|
128
|
+
connector, which has no write route. A program can read and then attempt a write; it
|
|
129
|
+
gets that refusal with zero writes sent, and catching it or using
|
|
130
|
+
`Promise.allSettled` changes nothing. A plain `"read"` override does not bring a source
|
|
131
|
+
write here: a tool it serves as a read over a silent source is refused, though `call_tool`
|
|
132
|
+
runs it. `"read-repeatable"` is the exception, because the deployment accepted that a host
|
|
133
|
+
may repeat the call, and a host can resend a whole read program: such a tool runs here
|
|
134
|
+
unmarked, and a resent program can reach the vendor twice.
|
|
135
|
+
|
|
136
|
+
**Discovery lists writes, marked.** `connecta.search` and `connecta.describe` return
|
|
137
|
+
them with `callable: false`, and no other entry carries the field. Writes are not
|
|
138
|
+
omitted because omission reads as absence, and a program that reduces a page keeps
|
|
139
|
+
the marker on the entry it selects where it would drop a page-level note.
|
|
140
|
+
`search_tools` remains the unprompted way to find a write's address outside a program.
|
|
141
|
+
|
|
142
|
+
**Handles.** `connecta.result` works with the existing stash bindings: a `resultId`
|
|
143
|
+
from a direct read or a read program pages, or reads whole with `{ all: true }`, from a
|
|
144
|
+
read program on the same endpoint.
|
|
145
|
+
Follow-up text from `call_tool`, `search_tools` and both program routes names this route
|
|
146
|
+
on endpoints that list it, including paging a writing program's oversized return, so
|
|
147
|
+
paging costs no write approval and cannot resend the write.
|
|
148
|
+
|
|
149
|
+
**Off switch.** `execute: { readRoute: false }` in deployment code stops listing it
|
|
150
|
+
on every writable endpoint. It is the deployment's choice alone, and suits a deployment
|
|
151
|
+
that wants a person to see every program, given the limit below.
|
|
152
|
+
|
|
153
|
+
**Known limit: a read program can still send data out through a read.** A read program runs
|
|
154
|
+
without a prompt on hosts that auto-approve reads. Sandboxes deny egress, but a read
|
|
155
|
+
tool that takes a URL or a query can carry data out in its arguments, so data a program
|
|
156
|
+
reads can instruct an agent to do that. Connecta enforces by classification and cannot
|
|
157
|
+
see intent. `call_tool` has the same exposure one call at a time; a program adds
|
|
158
|
+
loops and joins. Keep such tools out of the pool, or set them to `"write"` or `"hidden"`
|
|
159
|
+
in `classification`, or turn this route off. The `read-route-injection` eval task
|
|
160
|
+
measures whether agents follow such an instruction.
|
|
161
|
+
|
|
162
|
+
Activity records calls and programs on this route with source `execute_read_code`.
|
|
163
|
+
|
|
99
164
|
## Routing between the call surfaces
|
|
100
165
|
|
|
101
166
|
The route is chosen before discovery, and read-only work has exactly two:
|
|
102
|
-
`call_tool` for one known address,
|
|
167
|
+
`call_tool` for one known address, a program for everything wider —
|
|
168
|
+
`execute_read_code` where it is listed, otherwise `execute_code` — an
|
|
103
169
|
unknown address, a result that will be reduced, a call whose arguments depend on
|
|
104
170
|
an earlier result, or several operations. A program keeps discovery, calls, and
|
|
105
171
|
reduction together when the schemas and result shapes suffice, and gives each
|
|
@@ -120,13 +186,16 @@ read-only endpoint nothing writes: `search_tools`, `connecta.search`, and
|
|
|
120
186
|
`connecta.describe` omit write tools as if the connector lacked them, and
|
|
121
187
|
every write call through `call_tool`, `call_destructive_tool`, or a program is
|
|
122
188
|
refused before validation or dispatch with `read_only_endpoint`,
|
|
123
|
-
`retryable: false`, and
|
|
124
|
-
|
|
125
|
-
|
|
189
|
+
`retryable: false`, and an explicit statement that no write route exists here.
|
|
190
|
+
Bootstrap instructions state the serving endpoint’s actual write policy. Connector
|
|
191
|
+
guides preserve their authored Markdown and prepend the effective read-only
|
|
192
|
+
endpoint or connection restriction: steps that change data will be refused.
|
|
193
|
+
The agent reports the limitation and can ask the operator for a writable connection. Connecta
|
|
126
194
|
never pauses, replays, or approves a program.
|
|
127
195
|
|
|
128
196
|
Both search routes return the flat page specified under
|
|
129
|
-
[lexical discovery](#lexical-discovery),
|
|
197
|
+
[lexical discovery](#lexical-discovery), which the server validates against one
|
|
198
|
+
catalog page contract.
|
|
130
199
|
Describe returns an ordered tool list without pagination. Optional row metadata
|
|
131
200
|
is omitted in this example:
|
|
132
201
|
|
|
@@ -139,21 +208,28 @@ Live connector probing is not a fourth: the operator pages and `/health` own it.
|
|
|
139
208
|
|
|
140
209
|
## Discovery context
|
|
141
210
|
|
|
142
|
-
The deployment-derived `execute_code`
|
|
143
|
-
inventory before any catalog search — registry order and each canonical id,
|
|
211
|
+
The deployment-derived `execute_code` and `execute_read_code` descriptions carry
|
|
212
|
+
one connector inventory before any catalog search — registry order and each canonical id,
|
|
144
213
|
with no second name minted for programs. An entry adds the configured title
|
|
145
214
|
only when it says more than the id: a title whose words the id already spells,
|
|
146
|
-
such as `Google Calendar` for `google_calendar`, is dropped.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
215
|
+
such as `Google Calendar` for `google_calendar`, is dropped. It then adds the
|
|
216
|
+
connector's configured description as a one-line purpose, unless that too only
|
|
217
|
+
respells the id and title. Titles and purposes normalize whitespace; titles cap
|
|
218
|
+
at 48 UTF-8 bytes and purposes at 60, clipping on a whole token; text with no token
|
|
219
|
+
that fits is omitted. A connector whose guide is required
|
|
220
|
+
carries a `*`, and one legend follows when an entry shown has the mark. Optional
|
|
221
|
+
guides are not named here; search and describe name them. For example:
|
|
222
|
+
`Connectors: crm* (Customer CRM; Current accounts and contracts), tickets. *Read connecta.skill("connector:<id>") before its first call.`
|
|
223
|
+
|
|
224
|
+
Write each description so its first 60 bytes tell the connector apart from its
|
|
225
|
+
neighbours. Purposes stay in this inventory, never in the instructions, which
|
|
226
|
+
some hosts repeat on every tool.
|
|
227
|
+
|
|
228
|
+
The complete line, legend included, caps at 1,024 UTF-8 bytes, which names every
|
|
229
|
+
connector of a typical deployment. Past the cap, detail degrades before names do:
|
|
230
|
+
purposes are dropped first, from the end of registry order, then titles. Only
|
|
231
|
+
when bare ids still exceed it do whole entries give way, in registry order, to an
|
|
232
|
+
exact `+N more` count. The inventory reads only the configured registry: no
|
|
157
233
|
catalog load, no credential probe, no live status, no capability, and no
|
|
158
234
|
replacement for canonical discovery or addressing. Search pages carry the same
|
|
159
235
|
bounded title once per connector in `connectors[<id>].title`, so choosing an
|
|
@@ -170,7 +246,7 @@ discovery. Both take the same arguments.
|
|
|
170
246
|
| `safety` | `"readOnly"` selects stored `read` verdicts; `"approvalRequired"` selects stored `write` verdicts; omitted or `"all"` selects both. Each row carries `classification: "read" \| "write"`. These legacy filter names do not describe client approval policy; a read-only endpoint has no write rows. |
|
|
171
247
|
| `limit` / `offset` | page the ranked results; omit `limit` initially so the default eight-result page stays small |
|
|
172
248
|
| `includeSchemas` | `"compact"` for the rendered routing view, `"json"` for the exact schema, `"typescript"` for a function signature ([below](#typescript-signatures)) |
|
|
173
|
-
| `fullDescriptions` | unabridged tool purposes, at the obvious cost
|
|
249
|
+
| `fullDescriptions` | unabridged tool purposes instead of the one-sentence summary, at the obvious cost |
|
|
174
250
|
|
|
175
251
|
Neither `connector` nor `safety` grants authority or changes invocation
|
|
176
252
|
admission; they select what discovery shows and nothing else.
|
|
@@ -179,9 +255,10 @@ Main search also consults a standard hatch's construction-authored operation
|
|
|
179
255
|
vocabulary (path, operation id, vendor summary and tag), without calling a tool
|
|
180
256
|
or reading account data. A match returns the owning `_api_search` tool; call it
|
|
181
257
|
with the original query to retrieve operations. Exact tool/address and complete
|
|
182
|
-
authored-tool matches retain precedence
|
|
183
|
-
|
|
184
|
-
|
|
258
|
+
authored-tool matches retain precedence, and a vocabulary match never suppresses
|
|
259
|
+
lexical partial matches: both stay on the page, the hatch above partial tools.
|
|
260
|
+
A vocabulary match requires every term in one operation, and the operation's
|
|
261
|
+
hatch route must be visible and match the read/write filter. On `approvalRequired` searches the returned discovery tool
|
|
185
262
|
is still a read. Hidden tools, missing grants and read-only restrictions remain
|
|
186
263
|
in force. Browsing does not consult operation vocabulary.
|
|
187
264
|
|
|
@@ -274,9 +351,9 @@ directly; reserve `connecta.describe` for a search without schemas, an ambiguous
|
|
|
274
351
|
compact shape, or exact constraints that need `format: "json"`.
|
|
275
352
|
|
|
276
353
|
Compact search is a routing view, not a second copy of connector documentation,
|
|
277
|
-
so it spends bytes on shape and
|
|
278
|
-
|
|
279
|
-
input fields render before optional ones. Both surfaces share one renderer, so
|
|
354
|
+
so it spends bytes on shape and little on prose: a tool's purpose is its summary
|
|
355
|
+
([below](#lexical-discovery)), connector descriptions and property prose are
|
|
356
|
+
dropped, and required input fields render before optional ones. Both surfaces share one renderer, so
|
|
280
357
|
its byte and work budgets, its truncation renderings, and its handling of enums,
|
|
281
358
|
tuples, and conditional keywords are documented once under
|
|
282
359
|
[`connecta.search`](./code-mode.md#connectasearch) and
|
|
@@ -338,13 +415,28 @@ A connector may attach a deployment-owned guide as markdown, keeping the
|
|
|
338
415
|
original `usageGuide: string` configuration, or as
|
|
339
416
|
`{ content, summary?, required? }` — which registers no connector and creates no
|
|
340
417
|
shared runtime template. The skills catalog prepends Agent Skills frontmatter to
|
|
341
|
-
`content` and preserves the guide
|
|
418
|
+
`content` for native `skills/get` and `resources/read`, and preserves the guide
|
|
419
|
+
body, including its whitespace. The `skills` tool and `connecta.skill` return that
|
|
420
|
+
body without the frontmatter; a downstream skill's file keeps its exact bytes on
|
|
421
|
+
every reader. One read-only notice leads a guide: the endpoint's on a read-only
|
|
422
|
+
endpoint, otherwise the connection's on a read-only connector.
|
|
342
423
|
`summary` is normalized and refuses construction over 120 characters; absent,
|
|
343
424
|
Connecta derives the same bounded fallback the skills listing uses from the
|
|
344
425
|
guide's first meaningful body paragraph. `required: true` is reserved for
|
|
345
426
|
generic API wrappers and cross-operation conventions a complete downstream
|
|
346
427
|
schema cannot express.
|
|
347
428
|
|
|
429
|
+
A maintained provider's guide states only what no schema, description or typed
|
|
430
|
+
refusal already tells the agent where it acts. Generic rules live at the point of
|
|
431
|
+
use: `cursor` and hatch `page` schemas for continuation, `fields` for narrowing,
|
|
432
|
+
withheld values and cuts, hatch descriptions and `withheld` hints for operation
|
|
433
|
+
discovery, and the `write_outcome_unknown` error for write-once. A hatch `page`
|
|
434
|
+
with `hasMore: true` and no `nextCursor` also adds a `warnings` notice that the
|
|
435
|
+
result is incomplete. Money units, account or environment selection, sensitive
|
|
436
|
+
data reduction and "absence does not prove" rules stay in the guide, as does any
|
|
437
|
+
disqualifier that no refusal enforces. A read-only connection is not served its
|
|
438
|
+
guide's `writes` blocks.
|
|
439
|
+
|
|
348
440
|
Describe results carry a `guide: "connector:<id>"` pointer and a
|
|
349
441
|
`guideSummary`; a search page carries both once per connector in
|
|
350
442
|
`connectors[<id>]`. A matching tool row or description also carries
|
|
@@ -373,7 +465,7 @@ even when a connector has that id, whose guide stays at `connector:usage` or
|
|
|
373
465
|
`Connector "<id>" has no usage guide.`; any other unknown name fails as an
|
|
374
466
|
unknown skill. Names containing `:`, including URIs, never fall back to a bare
|
|
375
467
|
id. MCP `skills/get` resolves names the same way; `resources/read` accepts only
|
|
376
|
-
document URIs.
|
|
468
|
+
document URIs. Canonical names and document/root URIs take priority over compatibility aliases. An alias shared by multiple guides fails as ambiguous; use the canonical URI.
|
|
377
469
|
|
|
378
470
|
A connector-scoped lexical miss keeps that connector's guide metadata under
|
|
379
471
|
`queryAnalysis`. This matters for generic wrappers whose broad tool name
|
|
@@ -405,31 +497,66 @@ connector inventory, budgets, the endpoint's read-only setting, and whether any
|
|
|
405
497
|
visible connector has a guide. No catalog outcome, call, authorization state,
|
|
406
498
|
or other live status reaches `tools/list` or the instructions, so the
|
|
407
499
|
always-loaded surface stays byte-stable for a client's prompt cache.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
500
|
+
Instructions reflect the surface and endpoint write policy, subject to connector
|
|
501
|
+
restrictions. When all admitted connectors are read-only, instructions state that
|
|
502
|
+
writes are unavailable. On the meta surface they open by saying what the endpoint
|
|
503
|
+
reaches, for example `Gateway to connected services: crm, tickets.`, and then give
|
|
504
|
+
the routes, the write policy and recovery through `nextAction`. A native pool omits
|
|
505
|
+
the ids, because its tool names show them.
|
|
506
|
+
|
|
507
|
+
The ids come only from the caller's admitted view, the same view the `execute_code`
|
|
508
|
+
inventory reads: configured ids in registry order, never a value from the request,
|
|
509
|
+
a catalog or a credential. A maintainer at plain `/mcp` sees every id; a person
|
|
510
|
+
routed there sees only their pool's, and an id outside the admitted view never
|
|
511
|
+
appears. Past 120 UTF-8 bytes, ids give way, in registry order, to an exact
|
|
512
|
+
`+N more` count, and the inventory carries the fuller list.
|
|
513
|
+
|
|
514
|
+
They also include pool scope: a pool that sets a
|
|
515
|
+
[`description`](./auth.md#pools) has it placed first at `/mcp/<pool>` and
|
|
516
|
+
`/agent/<pool>`, and for a person routed to that pool at plain `/mcp`, so the model
|
|
517
|
+
learns that endpoint's scope, for example
|
|
518
|
+
`Team tier: shared tools only. For someone's own mail or files, ask them to message you one-on-one.`
|
|
519
|
+
A maintainer at plain `/mcp` receives none. The description changes no tool or tool
|
|
520
|
+
description. Plain `/mcp` can serve different ids and scope to different callers at
|
|
521
|
+
one URL, as its tool list and inventory already do, so `initialize` and
|
|
522
|
+
`server/discover` stay `cacheScope: "private"`: a client keeps what it learned per
|
|
523
|
+
caller and endpoint and never shares it. Connector
|
|
416
524
|
guides stay scoped to the deployment that listed them even when two deployments
|
|
417
525
|
use identical content, and a deployment with no connector guides receives none of
|
|
418
526
|
the short conditional guide pointers in its tool definitions.
|
|
419
527
|
|
|
420
528
|
### Native Skills extension
|
|
421
529
|
|
|
530
|
+
The [Skills extension](https://github.com/modelcontextprotocol/ext-skills) is Beta under
|
|
531
|
+
[SEP-3392](https://modelcontextprotocol.io/seps/3392-extension-lifecycle): breaking
|
|
532
|
+
revisions can reuse its ID. The released settings define no `revision`; Connecta
|
|
533
|
+
refuses downstream declarations with an explicit revision rather than enabling
|
|
534
|
+
unknown semantics. Upward handlers remain Connecta-owned to preserve caller views.
|
|
535
|
+
|
|
422
536
|
Connecta declares `resources: {}` and `io.modelcontextprotocol/skills`, then
|
|
423
537
|
serves `skills/list`, `skills/get`, `resources/list` and `resources/read` from
|
|
424
538
|
the same registry as `skills` and `connecta.skill`. These methods return
|
|
425
|
-
`resultType: "complete"
|
|
539
|
+
`resultType: "complete"` and `cacheScope: "private"`. Local-only caller views
|
|
540
|
+
advertise `ttlMs: 300000` (five minutes); any visible downstream skill source
|
|
541
|
+
keeps `ttlMs: 0`, including reads of local guides on that view. Clients must key
|
|
542
|
+
private caches by endpoint and authenticated caller. No request-bound catalog or
|
|
543
|
+
digest promise is retained across requests. Listing pages
|
|
426
544
|
contain at most five complete skills: usage first, required connector guides
|
|
427
545
|
next, other connector guides, downstream skills, then the investigation guide.
|
|
428
546
|
Subsequent pages use `cursor`/`nextCursor`. Local lookup works without listing.
|
|
429
547
|
|
|
430
548
|
The local document URIs are `skill://connecta/usage/SKILL.md` and
|
|
431
549
|
`skill://connecta/connectors/<slug>/SKILL.md`. A valid Agent Skills connector ID
|
|
432
|
-
is its slug; other IDs use
|
|
550
|
+
is its slug; other IDs use a readable normalized ID slug plus a 12-character
|
|
551
|
+
SHA-256 suffix. Generated slugs reserve every configured connector ID and append
|
|
552
|
+
a deterministic numeric suffix on collision. Avoid a connector ID equal to another
|
|
553
|
+
connector's generated name: allocation depends on the IDs a catalog can see, so
|
|
554
|
+
adding one moves the other guide to a suffixed name.
|
|
555
|
+
The former `connector-<48-character-hash>` names, roots and file URIs remain
|
|
556
|
+
resolvable aliases. Construction warns, without rejecting a guide, when its
|
|
557
|
+
summary has no English task verb or operation noun from the advisory heuristic
|
|
558
|
+
(for example, a credential-only scope statement). Set `usageGuide.summary`
|
|
559
|
+
to say when to load it; non-English prose may produce a false-positive warning.
|
|
433
560
|
The suffix makes each
|
|
434
561
|
document a valid Agent Skills file; the roots `skill://connecta/usage` and
|
|
435
562
|
`skill://connecta/connectors/<connector-id>` also resolve. Provider guides are
|
|
@@ -459,6 +586,19 @@ from skill manifests; neither skill catalogs nor file bodies enter its response
|
|
|
459
586
|
cache. A failed or malformed opted-in listing fails the entire skills catalog,
|
|
460
587
|
with no partial cache. Known local guides remain readable during an outage.
|
|
461
588
|
|
|
589
|
+
On `skills` and program `connecta.skill` reads, downstream text is prefixed with
|
|
590
|
+
a fixed third-party origin sentence naming the connector (bounded to 128 ID
|
|
591
|
+
characters). This prefix has no closing delimiter. `skills` listings carry the
|
|
592
|
+
same sentence before each downstream description, collapsed to one line, in text
|
|
593
|
+
and structured output. Program `connecta.read` applies it to text contents whose
|
|
594
|
+
URI uses the `skill:` scheme or ends in `SKILL.md`; a downstream's other resources
|
|
595
|
+
are returned as ordinary data, and binary contents are not labeled. Maintained and
|
|
596
|
+
deployment guides remain unlabeled. Native `skills/get` metadata and `resources/read`
|
|
597
|
+
contents stay unchanged so original manifest digests remain verifiable; adding a
|
|
598
|
+
label there would break integrity checks. A native host must attach its own origin
|
|
599
|
+
label when it loads verified content into model context, as the Skills extension
|
|
600
|
+
requires.
|
|
601
|
+
|
|
462
602
|
Downstream files are proxied byte-exact, including CRLF and binary supporting
|
|
463
603
|
files, within 512 files and 16 MiB per skill. Catalog metadata is bounded to
|
|
464
604
|
1,024 entries and 8 MiB. The existing request-scoped agent boundary redacts
|
|
@@ -486,7 +626,9 @@ dropping the text copy waits on host-forwarding measurements showing supported
|
|
|
486
626
|
clients do not need it. Direct-call failures set `isError: true` and carry the
|
|
487
627
|
same `{ ok: false, error, durationMs, attempts }` envelope in both forms, whether
|
|
488
628
|
`resultMode` is omitted, `"mcp"`, or `"value"`. Plain-text guidance stays
|
|
489
|
-
text-only
|
|
629
|
+
text-only: a `skills` fetch returns the guide once, in `content`, and the tool
|
|
630
|
+
advertises no `outputSchema`, while its listing and errors keep `structuredContent`.
|
|
631
|
+
A program's `connecta.skill` returns its own `{ name, format, text }` value. Successful direct calls with `resultMode` omitted or `"mcp"` return
|
|
490
632
|
their data only in `content`, with the downstream format in
|
|
491
633
|
`_meta["dev.connecta/format"]`. They omit `structuredContent`, including on
|
|
492
634
|
truncated results, so clients that prefer it cannot replace data or a preview
|
|
@@ -584,16 +726,106 @@ Completed writes remain successful and must not be repeated. JSON values still
|
|
|
584
726
|
round-trip such strings through their escaped JSON representation.
|
|
585
727
|
|
|
586
728
|
A per-call `timeoutMs` covers catalog resolution, admission, and connector
|
|
587
|
-
execution under one deadline
|
|
729
|
+
execution under one deadline, cut to the response ceiling, `execute.watchdogMs`
|
|
730
|
+
(55 s by default) from the request's arrival, so the client gets a typed result
|
|
731
|
+
rather than its own transport timeout. A call with no time left is refused before it
|
|
732
|
+
resolves, as a retryable `timeout` at `details.stage: "response ceiling"` that says it
|
|
733
|
+
was not sent. A call whose time runs out during resolution, admission or auth gets the
|
|
734
|
+
same refusal from a check that is the last step before its connector. See [`L3`](./code-mode.md#cancellation-and-limits). The admission queue's own timeout may expire
|
|
588
735
|
sooner but cannot extend the call deadline. Result processing happens after that
|
|
589
736
|
deadline ends, because a completed downstream call must not turn into a
|
|
590
737
|
retryable timeout while Connecta prepares its response.
|
|
591
738
|
|
|
739
|
+
### Previews of reads over the cap
|
|
740
|
+
|
|
741
|
+
A result at or under its cap always returns whole. A successful direct read over
|
|
742
|
+
its cap (the connector's `maxResultBytes`, else `calls.maxResultBytes`, else 24,000
|
|
743
|
+
bytes), on a surface that lists a program tool, may show a sample instead of its
|
|
744
|
+
first bytes: the structure and whole rows, or the head and tail of its text, with a
|
|
745
|
+
retained copy of the result and a ready-to-edit program. Such a read is already cut
|
|
746
|
+
and already stashed, so a preview adds no storage cost, and valid JSON with a notice
|
|
747
|
+
saying what it holds replaces raw JSON sliced mid-value. The rule is generic and
|
|
748
|
+
needs nothing from a provider. It reads the result after redaction and value safety,
|
|
749
|
+
for API values and for a lone MCP text block.
|
|
750
|
+
|
|
751
|
+
**Rows.** A preview fires when all of these hold:
|
|
752
|
+
|
|
753
|
+
- its largest list holds at least 3 records or arrays, sits within 3 keys of the
|
|
754
|
+
root, and holds at least half the bytes;
|
|
755
|
+
- everything outside that list fits in 1,024 bytes;
|
|
756
|
+
- at least the first row can be shown whole, within 3,000 bytes, and at least one
|
|
757
|
+
row is left out;
|
|
758
|
+
- every number in the result keeps its exact value through `JSON.parse`.
|
|
759
|
+
|
|
760
|
+
```text
|
|
761
|
+
{"truncated":true,"resultId":"…","totalBytes":48210,"rows":{"path":"items","count":200,"shown":18,"keys":["id","email","status"]},"hint":"Sample only: rows 1-18 of 200 at items follow, in the result's own shape, with every value outside the list; the other 182 are not shown. All of it is retained for 15 minutes: run nextAction in execute_code, editing its last line to filter, join or count the rows, instead of reading pages by hand. If execute_code is refused or unavailable, the rest can't be read here: narrow this read with its own filter, limit or fields arguments, or say what was not read.","nextOffset":0,"nextAction":{"tool":"execute_code","arguments":{"code":"async () => { let text = \"\", offset = 0, page; do { page = await connecta.result(\"…\", { offset }); text += page.text; offset = page.nextOffset; } while (page.hasMore); const rows = JSON.parse(text).items; return { count: rows.length, rows: rows.slice(0, 5) }; }"}}}
|
|
762
|
+
{"items":[{"id":"t_1","email":"alice@example.com","status":"open"},…],"has_more":true,"next_cursor":"c2"}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
The body after the first newline is valid JSON in the result's own shape: whole
|
|
766
|
+
rows from the start of the list up to 1,500 bytes, and every value outside it.
|
|
767
|
+
`rows.path` names the list as JavaScript would, so a key a dotted path cannot
|
|
768
|
+
spell reads `["a.b"].items`, and a root array reads `(root)`. `rows.keys` samples
|
|
769
|
+
up to 40 keys of at most 64 characters from the first 50 rows; `rows.count` and
|
|
770
|
+
`totalBytes` describe the whole result.
|
|
771
|
+
|
|
772
|
+
On an endpoint that lists `execute_read_code`, the hint and `nextAction` name it instead
|
|
773
|
+
of `execute_code`. A preview only ever follows a read, and its program reads only the
|
|
774
|
+
retained result, so it runs on either route.
|
|
775
|
+
|
|
776
|
+
**Text.** Text that is not JSON, or an API connector's text value, returns its line
|
|
777
|
+
count in `text.lines`, then its first 1,200 and last 800 bytes around a marker saying
|
|
778
|
+
how many bytes were left out, with `text.shownBytes` beside it. Cuts fall on
|
|
779
|
+
character boundaries. Its program reads the retained text and returns the lines
|
|
780
|
+
matching a pattern the agent can edit.
|
|
781
|
+
|
|
782
|
+
**Everything else keeps the route below:** results no program can reach whole within
|
|
783
|
+
`execute.maxHostCalls`, JSON objects with no such list, scalars, lists of scalars,
|
|
784
|
+
lists deeper than 3 keys or shorter than 3 rows, a first row over 3,000 bytes, text
|
|
785
|
+
with unpaired surrogates, several blocks, non-text blocks and value mode. So does any
|
|
786
|
+
number parsing would change: complete tokens are compared by exact value, including
|
|
787
|
+
fractions and exponents, so `9.007199254740993e15`, `1e999` and `-0` decline the view
|
|
788
|
+
wherever they appear, and `1e3` and `12.50` do not. Digits inside a string never count.
|
|
789
|
+
The offered program parses the same text, so a number past the sample declines too.
|
|
790
|
+
|
|
791
|
+
**A call that may have written never previews.** That covers every effective write
|
|
792
|
+
and a source write a deployment overrode to read: its result is its only copy, and
|
|
793
|
+
repeating it to look again is forbidden. It keeps its first bytes, and its notice says
|
|
794
|
+
the write already ran and offers no repeat.
|
|
795
|
+
|
|
796
|
+
A preview is presentation only. A program's value and the handle's pages are
|
|
797
|
+
byte-for-byte what they would have been: the retained text is the same redacted
|
|
798
|
+
text the read would have paged, and `format` keeps its meaning. A view drops no
|
|
799
|
+
continuation field, total, warning, status or `format`, shows no empty list in place
|
|
800
|
+
of a full one, keeps array positions, and shows `false` and `0` as they are.
|
|
801
|
+
`totalBytes` is the original size. Clients that parse a direct read should not assume
|
|
802
|
+
the body after a `truncated` notice is a prefix: `nextOffset` is 0 and the body is a
|
|
803
|
+
separate view. The per-connector and deployment `maxResultBytes` still bound what is
|
|
804
|
+
shown: a preview that does not fit the call's cap takes the route below, and a raised
|
|
805
|
+
cap returns more results whole. A preview counts as `result_too_large` friction, as
|
|
806
|
+
any result past its cap does. If the stash fails, the read keeps its first bytes and
|
|
807
|
+
its paging-unavailable notice, and the operator warning fires as usual.
|
|
808
|
+
|
|
809
|
+
**Recovery depends on the program tool.** `connecta.result` exists only inside
|
|
810
|
+
programs, and a host may prompt for or refuse `execute_code`, which is annotated as a
|
|
811
|
+
write on a writable endpoint. A refused program means the rows or text the preview left
|
|
812
|
+
out can't be read from this result, which is also true of the first-bytes route. So the
|
|
813
|
+
hint states the fallback: narrow the read with its own arguments, or say what was not
|
|
814
|
+
read. A view shows less of the result than first bytes would, 1,500 bytes of rows
|
|
815
|
+
against 24,000, so narrowing matters more on such a host.
|
|
816
|
+
|
|
817
|
+
Previews need a program tool, not only a paging tool. Page-first reading failed when an
|
|
818
|
+
agent read a 185 KB log in pages and missed the failing range, and a preview shows too
|
|
819
|
+
little to read by hand, so a native pool with `executeCode: false` keeps its first
|
|
820
|
+
bytes and its `read_result` notice. One value in `src/routes/mcp.ts` registers the
|
|
821
|
+
program tool for a request and names it in `nextAction` and the hint, so the text never
|
|
822
|
+
offers a tool the surface does not list.
|
|
823
|
+
|
|
592
824
|
### Truncated direct-call results
|
|
593
825
|
|
|
594
826
|
A `call_tool` or `call_destructive_tool` result over its cap — the connector's
|
|
595
|
-
`maxResultBytes`, else `calls.maxResultBytes`, else 24,000 bytes —
|
|
596
|
-
as one text block. Its first line is the truncation notice, one line of compact
|
|
827
|
+
`maxResultBytes`, else `calls.maxResultBytes`, else 24,000 bytes — that does not
|
|
828
|
+
get a [preview](#previews-of-reads-over-the-cap) comes back as one text block. Its first line is the truncation notice, one line of compact
|
|
597
829
|
JSON; everything after the first newline is the preview. `structuredContent`
|
|
598
830
|
is absent, so the notice cannot hide the preview in clients that prefer it:
|
|
599
831
|
|
|
@@ -647,16 +879,25 @@ notice is `data` itself, with no preview and a next action at offset 0.
|
|
|
647
879
|
The hint says what to do next. For a call not explicitly annotated read-only it
|
|
648
880
|
opens with “This write already ran: do not call it again to see its result.”
|
|
649
881
|
The approved write happened; repeating it to look again is how one export
|
|
650
|
-
becomes three. For an explicitly read-only call,
|
|
651
|
-
|
|
652
|
-
`execute_code`
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
882
|
+
becomes three. For an explicitly read-only call, the hint offers reduction first:
|
|
883
|
+
to find something specific, loop `connecta.result` over the retained result in
|
|
884
|
+
one `execute_code` program, filter there and return the matches; to read
|
|
885
|
+
on, run `nextAction`. A reduction must see the whole value at once, because
|
|
886
|
+
page-by-page reading in the model misses things: when the hint named paging
|
|
887
|
+
alone, the eval's weakest model read a 185 KB CI log in 24,000-byte pages, skipped
|
|
888
|
+
the range holding the real failure, and named the flaky test near the top. The
|
|
889
|
+
loop keeps that whole-value reduction without fetching from the downstream a
|
|
890
|
+
second time. The hint names the result ID and that the result is retained for 15
|
|
891
|
+
minutes.
|
|
892
|
+
|
|
893
|
+
The loop is offered only when one program can finish it: every page at the
|
|
894
|
+
connector's cap (at most the 32 KiB bridge ceiling), plus two host calls spare,
|
|
895
|
+
must fit `execute.maxHostCalls`. At the defaults that is about 432 KB. Past that
|
|
896
|
+
a loop would end in `budget_exceeded` with nothing returned, so the hint says to
|
|
897
|
+
repeat the read inside `execute_code` with `connecta.call` and reduce it there;
|
|
898
|
+
a host call's result is not bound by the inline cap, only what the program returns
|
|
899
|
+
(`L6` in [code mode](./code-mode.md)). A value over `L6`'s host-call bound returns
|
|
900
|
+
a paged handle instead, whose own hint is sized the same way.
|
|
660
901
|
When paging is available, `nextAction` is the page handle for either kind of call, because paging is
|
|
661
902
|
the one next step connecta can spell out exactly, while a reduction is a
|
|
662
903
|
program the agent has to write. `resultId` stays beside the exact `nextAction`,
|
|
@@ -671,17 +912,28 @@ no paging route.
|
|
|
671
912
|
|
|
672
913
|
Meta endpoints page inside a program. Native pools without code list `read_result` for the same bound handles. Program calls also
|
|
673
914
|
return handles for oversized values; see [program result limits](./code-mode.md#cancellation-and-limits).
|
|
915
|
+
A handle within the program's [read ceiling](./code-mode.md#connectaresult-and-connectaskill)
|
|
916
|
+
says to read it whole with `connecta.result(handle, { all: true })`, for one host call.
|
|
917
|
+
Past the ceiling, a read's handle says to loop `connecta.result(handle, { offset })`
|
|
918
|
+
until `!hasMore` only when every page at the connector's cap, plus two host calls
|
|
919
|
+
spare, fits the calls its program has left. At the default 20 calls and 24,000-byte
|
|
920
|
+
cap it doesn't: its hint then gives the pages
|
|
921
|
+
to read in this program and in each new one, and says to return the reduction so
|
|
922
|
+
far with `nextOffset` and continue from both, by `resultId`, inside the 15-minute
|
|
923
|
+
retention. A loop past the budget ends in `budget_exceeded` with nothing returned.
|
|
924
|
+
A write's handle says the write already ran and not to repeat it.
|
|
674
925
|
`connecta.result(handle, { page: 0 })` accepts those handles alongside direct
|
|
675
926
|
result IDs. Numbered pages are zero-based and mutually exclusive with offset.
|
|
676
927
|
Keep maxBytes fixed while incrementing pages. A direct call's
|
|
677
928
|
notice carries a `resultId`, `nextOffset`, and an executable `execute_code`
|
|
678
|
-
recovery action.
|
|
929
|
+
recovery action. When the hint offers the loop, read and reduce the result in one
|
|
930
|
+
program. Leave `maxBytes` unset, because a smaller page spends more host calls:
|
|
679
931
|
|
|
680
932
|
```js
|
|
681
933
|
async () => {
|
|
682
934
|
let text = "", offset = 0, page;
|
|
683
935
|
do {
|
|
684
|
-
page = await connecta.result("result-id", { offset
|
|
936
|
+
page = await connecta.result("result-id", { offset });
|
|
685
937
|
text += page.text;
|
|
686
938
|
offset = page.nextOffset;
|
|
687
939
|
} while (page.hasMore);
|
|
@@ -704,6 +956,13 @@ a paging-unavailable notice, without a handle or recovery action. A write's
|
|
|
704
956
|
notice still says it already ran. Activity records success; operator logs receive
|
|
705
957
|
a fixed warning without storage error prose. Never repeat a write to recover output.
|
|
706
958
|
|
|
959
|
+
A result a program retained is readable from later requests only once it has passed
|
|
960
|
+
that run's final secret set. Otherwise it stays unreadable: paging answers
|
|
961
|
+
`result_not_retained` with `retryable: false`, telling a read to rerun and a write
|
|
962
|
+
not to repeat ([code mode `R4a`](./code-mode.md#results-and-projection)).
|
|
963
|
+
A direct call's stash passes its own request's set when stored; a later direct call
|
|
964
|
+
in another request shares no set with it.
|
|
965
|
+
|
|
707
966
|
## Lexical discovery
|
|
708
967
|
|
|
709
968
|
`search_tools` and in-program `connecta.search` return the same flat discovery
|
|
@@ -752,9 +1011,87 @@ to 60 seconds: its entry keeps the recorded code with `retryable: true`,
|
|
|
752
1011
|
lookup failed recently (CODE). Search with connector "X" to try it now.` A
|
|
753
1012
|
scoped search always probes. Records are kept per owner, admitted endpoint and
|
|
754
1013
|
described connector configuration. A healthy connector whose catalog always
|
|
755
|
-
takes more than 5 seconds
|
|
756
|
-
`connector`, or let any longer-deadline operation
|
|
757
|
-
|
|
1014
|
+
takes more than 5 seconds and has no earlier complete catalog can stay out of
|
|
1015
|
+
unscoped search; search it with `connector`, or let any longer-deadline operation
|
|
1016
|
+
warm its complete catalog. See
|
|
1017
|
+
[Discovery health](./architecture.md#storage-credentials-and-connectors).
|
|
1018
|
+
|
|
1019
|
+
**Stale listings.** When a connector does not answer an unscoped search, because
|
|
1020
|
+
of a timeout, a skip after a recent one, the discovery budget, `unavailable`,
|
|
1021
|
+
`rate_limited` or a retryable downstream failure, the search lists that
|
|
1022
|
+
connector's last complete catalog if the same owner, credential and catalog
|
|
1023
|
+
generation have one no more than 24 hours past its expiry. Its `catalogErrors`
|
|
1024
|
+
entry then adds `stale: true` and `ageMs`, the milliseconds since the downstream
|
|
1025
|
+
last confirmed that catalog, and keeps `code`, `retryable` and any
|
|
1026
|
+
`retryAfterMs`. A downstream's own retry delay stays as it named it, and the
|
|
1027
|
+
message then says to wait rather than to search again. A skipped connector's
|
|
1028
|
+
`retryAfterMs` is when discovery next asks it, at most 60 seconds:
|
|
1029
|
+
|
|
1030
|
+
```json
|
|
1031
|
+
{
|
|
1032
|
+
"connector": "tickets",
|
|
1033
|
+
"code": "timeout",
|
|
1034
|
+
"message": "Connector \"tickets\" did not answer discovery (timeout), so its tools are listed from its last complete catalog. Calls still check the live catalog. Search with connector \"tickets\" to try it now.",
|
|
1035
|
+
"retryable": true,
|
|
1036
|
+
"stale": true,
|
|
1037
|
+
"ageMs": 412000
|
|
1038
|
+
}
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
A stale listing grants nothing. A call, `connecta.describe` and a search with
|
|
1042
|
+
`connector` read the live catalog, so a tool the downstream removed fails with
|
|
1043
|
+
`unknown_tool` and a reclassified one runs under its current verdict. Deployment
|
|
1044
|
+
classification, hidden tools, pool grants and read-only endpoints apply to a
|
|
1045
|
+
stale listing as they do to a live one. Authorization and permission failures,
|
|
1046
|
+
refusals and malformed catalogs never list a stale catalog, and a partial catalog
|
|
1047
|
+
is never stored or listed. An entry without `stale` lists no tools for that
|
|
1048
|
+
connector. `queryAnalysis.catalogError` holds failures only.
|
|
1049
|
+
|
|
1050
|
+
An authorization or permission refusal also retires that credential's catalog
|
|
1051
|
+
for later requests, in every isolate: it is neither listed nor reused, fresh or
|
|
1052
|
+
stale, by discovery or by a call's five-minute fallback, until a listing
|
|
1053
|
+
succeeds. On a connector with a shared credential this covers everyone using
|
|
1054
|
+
that credential; on any other connector, only the caller refused. A credential
|
|
1055
|
+
change, OAuth connect or disconnect, or operator invalidation has the same effect
|
|
1056
|
+
at once for every owner. A stale listing is read again after its probe fails, so
|
|
1057
|
+
any of these during the probe also takes the tools out of that search.
|
|
1058
|
+
|
|
1059
|
+
A refusal retires the catalog of the credential, OAuth identity and catalog
|
|
1060
|
+
generation its attempt was dispatched under, whichever are current when it
|
|
1061
|
+
answers, so another credential's catalog keeps working. A listing lifts only the
|
|
1062
|
+
retirement it read before it asked the downstream; a refusal that lands while it
|
|
1063
|
+
runs stays until a later listing succeeds. A custom connector's answer is
|
|
1064
|
+
retained only if its credential did not change while it was listing. Connecta
|
|
1065
|
+
cannot see which credential a custom connector sent, so a refusal whose
|
|
1066
|
+
credential changed while it was listing, or cannot be read afterwards, retires
|
|
1067
|
+
that connector's catalogs for every credential and owner.
|
|
1068
|
+
|
|
1069
|
+
Three limits follow:
|
|
1070
|
+
|
|
1071
|
+
- A custom connector's credential is compared when its listing begins and when
|
|
1072
|
+
it is refused. A change back to the same credential between them goes unseen,
|
|
1073
|
+
as does a connector sending a credential it kept from earlier. The credential
|
|
1074
|
+
actually refused is then not retired: its catalog can be listed stale if it
|
|
1075
|
+
becomes current again, until its first answer.
|
|
1076
|
+
- A retirement that cannot be written is logged as `catalog fence failed` and the
|
|
1077
|
+
refusal is still returned. The catalog stays reusable until a later refusal is
|
|
1078
|
+
recorded, a listing replaces it or its stale limit passes.
|
|
1079
|
+
- A connector whose `tools/list` keeps answering 401 or 403 has no catalog to
|
|
1080
|
+
fall back on, so every call that must resolve a tool through its catalog fails
|
|
1081
|
+
with that refusal until a listing succeeds. This includes a credential that may
|
|
1082
|
+
call tools but not list them, and the five-minute fallback does not apply while
|
|
1083
|
+
the catalog is retired.
|
|
1084
|
+
|
|
1085
|
+
`total` counts this response's matches. Offsets are stateless, so it stays the
|
|
1086
|
+
same across a browse's pages while each catalog is unchanged, including one
|
|
1087
|
+
listed stale, and can change if a catalog refreshes between pages or a connector
|
|
1088
|
+
with no stale catalog starts answering.
|
|
1089
|
+
|
|
1090
|
+
A cached or authored catalog does not wait for a probe slot, so
|
|
1091
|
+
`discovery.concurrency` bounds the connectors actually being asked. A probe that
|
|
1092
|
+
runs out of its own deadline also starts one refresh in the runtime's tail
|
|
1093
|
+
(`ctx.waitUntil`, or the Node listener's tracked equivalent) under the full probe
|
|
1094
|
+
deadline, at most 20 seconds, and that refresh warms the next search.
|
|
758
1095
|
|
|
759
1096
|
Tool rows carry the address, stored classification, and tool-specific
|
|
760
1097
|
`guideRequired` and `guideRequiredReasons`. Facts shared by every row of one
|
|
@@ -769,17 +1106,44 @@ compact schema strings and TypeScript signatures. Compact schemas are text;
|
|
|
769
1106
|
`inputSchema.properties` is meaningful only in JSON mode. Pagination counts
|
|
770
1107
|
tools across connectors: `nextOffset` is present only when another page remains.
|
|
771
1108
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1109
|
+
A row's `description` is a summary: the first complete sentence, joined to the
|
|
1110
|
+
following ones until it reaches 40 characters, within 160 UTF-16 code units. Text
|
|
1111
|
+
with no sentence end in that range is cut at a word and marked `…`. The row
|
|
1112
|
+
carries `descriptionTruncated: true` whenever text was left out;
|
|
1113
|
+
`fullDescriptions: true` returns the original text and no flag.
|
|
1114
|
+
|
|
1115
|
+
Search tokenizes tool names, titles and full descriptions at punctuation and
|
|
1116
|
+
camel-case boundaries. Whole-token matches, a small set of inflectional variants
|
|
1117
|
+
and a few unambiguous abbreviations (`config`/`configuration`, `db`/`database`,
|
|
1118
|
+
`doc`/`document`, `env`/`environment`, `org`/`organization`,
|
|
1119
|
+
`repo`/`repository`, `var`/`variable`) preserve recall without admitting
|
|
1120
|
+
arbitrary mid-word substrings. Document frequency weights rare domain terms above
|
|
1121
|
+
ubiquitous action terms. A term weighs most in the name, then the summary, then
|
|
1122
|
+
the title (`title` or `annotations.title`), and least in the rest of the
|
|
1123
|
+
description, which stays searchable but is weak evidence. A tool's score is then
|
|
1124
|
+
multiplied by the square of the share of the query's total rarity it covers in
|
|
1125
|
+
any zone, so rare words in a description's tail outrank a common verb in a name,
|
|
1126
|
+
and a tool covering every term is unaffected.
|
|
1127
|
+
|
|
1128
|
+
Exact tool names and canonical addresses rank first, so `get_issue` beats
|
|
1129
|
+
`get_issue_status` even when a connector is named `issue`. A tool named only by
|
|
1130
|
+
an action word, such as `search`, is exact when it is the whole query, not when a
|
|
1131
|
+
longer query contains that word. Exact configured connector IDs, full display
|
|
1132
|
+
titles, and recognized service names rank next. Three tiers follow:
|
|
1133
|
+
|
|
1134
|
+
1. Complete matches: every term is in the name, title or summary. A tool on the
|
|
1135
|
+
connector the query or scope names need not repeat that connector's name.
|
|
1136
|
+
2. A hatch whose operation vocabulary matched, and a tool whose name states a
|
|
1137
|
+
query word other than an action or the connector, with the remaining terms in
|
|
1138
|
+
the rest of its description.
|
|
1139
|
+
3. Every other match.
|
|
1140
|
+
|
|
1141
|
+
When a complete match exists, tier 3 keeps only tools covering every term
|
|
1142
|
+
somewhere, covering at least two terms, or whose name states a term at least as
|
|
1143
|
+
rare as the query's average. Otherwise it keeps any match. Within a tier, a query
|
|
1144
|
+
whose first word outside connector names is `list`, `get`, `find`, `search`,
|
|
1145
|
+
`show`, `read`, `fetch`, or `count` ranks read rows before write rows; writes stay
|
|
1146
|
+
on the page. Score and catalog order break the remaining ties.
|
|
783
1147
|
|
|
784
1148
|
A maintained provider may declare `searchSynonyms`: groups of single lowercase
|
|
785
1149
|
words its users and its vendor use for the same record, such as `household`
|
|
@@ -804,8 +1168,9 @@ catalog is a catalog failure, never an absence. Absence records and catalog
|
|
|
804
1168
|
errors use only the endpoint's tools; a connector the endpoint does not serve
|
|
805
1169
|
is indistinguishable from an unconfigured one.
|
|
806
1170
|
|
|
807
|
-
If no tool covers every search term,
|
|
808
|
-
|
|
1171
|
+
If no tool covers every search term, in any part of its text or through one
|
|
1172
|
+
operation of its vocabulary, wider any-term fallback remains available for
|
|
1173
|
+
ordinary action/object searches and the page carries `matchMode: "partial"`.
|
|
809
1174
|
A service absence suppresses that fallback, so `GitHub get issue` cannot return
|
|
810
1175
|
Linear issues or Supabase projects. Tool rows expose no scores or
|
|
811
1176
|
per-result query coverage. `queryAnalysis` partitions at most eight terms of at
|
|
@@ -946,11 +1311,41 @@ behavior is unchanged.
|
|
|
946
1311
|
The host echoes `requestState` and its bare `inputResponses.connecta_auth`
|
|
947
1312
|
response on the original `tools/call`. Accept re-runs the original call; it
|
|
948
1313
|
does not prove browser consent completed. Decline or cancel ends without
|
|
949
|
-
dispatch, with `auth_declined` or `auth_cancelled`.
|
|
1314
|
+
dispatch or another prompt, with `auth_declined` or `auth_cancelled`. A client
|
|
1315
|
+
can answer without showing anything, so neither code means a person refused, and
|
|
1316
|
+
Connecta cannot tell the difference. When this caller may still connect, the
|
|
1317
|
+
refusal carries the handoff this connector would get on a host without URL
|
|
1318
|
+
support (`connector`, `recovery`, `authorizationUrl` or `operatorUrl`,
|
|
1319
|
+
`instructions`), plus `nextAction: { url, purpose }`:
|
|
1320
|
+
|
|
1321
|
+
```json
|
|
1322
|
+
{
|
|
1323
|
+
"code": "auth_declined",
|
|
1324
|
+
"message": "The client declined the connection prompt, possibly automatically, so the user may not have seen it. Nothing was retried.",
|
|
1325
|
+
"retryable": false,
|
|
1326
|
+
"connector": "service",
|
|
1327
|
+
"recovery": "oauth",
|
|
1328
|
+
"authorizationUrl": "https://connecta.example/connect/service?h=...",
|
|
1329
|
+
"nextAction": { "url": "https://connecta.example/connect/service?h=...", "purpose": "..." }
|
|
1330
|
+
}
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
Give that link to the user once, then stop. Do not prompt again, retry, or reach
|
|
1334
|
+
the service another way until they confirm. If they already refused this
|
|
1335
|
+
connection in the conversation, only mention that the link is available.
|
|
1336
|
+
A failed signing or storage step leaves the refusal without a link. Its message
|
|
1337
|
+
says so: unless the user refused this connection, repeat the request once for a
|
|
1338
|
+
new link, then give that link to them, and stop if that repeat also fails. Connecta
|
|
1339
|
+
keeps no record of which clients refuse, so each new prompt to such a client
|
|
1340
|
+
ends the same way. Three prompts are allowed
|
|
950
1341
|
within one ten-minute retry window, then `auth_round_limit` ends the flow.
|
|
951
1342
|
Each state can be consumed once; concurrent or repeated retries are refused
|
|
952
|
-
before dispatch.
|
|
953
|
-
|
|
1343
|
+
before dispatch. Accept does not mean a browser has opened the link, so every link
|
|
1344
|
+
in a flow stays live. A `force: true` flow's links, including a refusal's, share one
|
|
1345
|
+
reset: the first verified visit performs it and later visits continue it. A visit
|
|
1346
|
+
during a running start returns 409 without spending its link, and says to start a new
|
|
1347
|
+
connection request if it persists, because a new call is a new flow. A failed start
|
|
1348
|
+
lets the flow's next visit begin again.
|
|
954
1349
|
Host-owned invocation facts record the auth failure's origin and entry into
|
|
955
1350
|
all calls, including each classification, catalog freshness and classification
|
|
956
1351
|
digest. Raw fetches and custom connector transports cannot establish recovery
|
|
@@ -1000,11 +1395,10 @@ An OAuth call failure uses this envelope:
|
|
|
1000
1395
|
"operation": "service.read",
|
|
1001
1396
|
"recovery": "oauth",
|
|
1002
1397
|
"authorizationUrl": "https://connecta.example/connect/service?h=...",
|
|
1003
|
-
"instructions": "
|
|
1398
|
+
"instructions": "For the person opening this link: sign in as the user who requested this connection; ...",
|
|
1004
1399
|
"nextAction": {
|
|
1005
|
-
"
|
|
1006
|
-
"
|
|
1007
|
-
"operatorHandoff": "Give the URL and instructions it returns to the operator."
|
|
1400
|
+
"url": "https://connecta.example/connect/service?h=...",
|
|
1401
|
+
"purpose": "Give this link to the user, unchanged, then stop. Retry after they confirm."
|
|
1008
1402
|
},
|
|
1009
1403
|
"retry": "Retry service.read after the operator completes recovery."
|
|
1010
1404
|
}
|
|
@@ -1016,7 +1410,26 @@ returns for the admitted user, connector, and pool. Direct calls include them in
|
|
|
1016
1410
|
`error`; program `connecta.call` failures include them in `error.data` and
|
|
1017
1411
|
`error.details`. Search `catalogErrors` and scoped `queryAnalysis.catalogError`,
|
|
1018
1412
|
and describe `errorDetails`, carry them too. When an auth error includes a
|
|
1019
|
-
handoff, give it to the user or operator.
|
|
1413
|
+
handoff, give it to the user or operator. An error that carries a handoff says
|
|
1414
|
+
so in `nextAction`, because calling `authorize_connector` would only return the
|
|
1415
|
+
same thing:
|
|
1416
|
+
|
|
1417
|
+
- a link: `{ url, purpose }`, with `url` equal to `authorizationUrl` or `operatorUrl`;
|
|
1418
|
+
- no link (`unavailable`): `{ action: "contact_operator", purpose }`, with the
|
|
1419
|
+
repair in `instructions`;
|
|
1420
|
+
- after a write of unknown outcome, either `purpose` says to re-read the write's
|
|
1421
|
+
target once they confirm, never to retry.
|
|
1422
|
+
|
|
1423
|
+
`message` drops its route text the same way. The typed fields are unchanged:
|
|
1424
|
+
`nextAction` repeats a link, it does not replace `authorizationUrl`, `operatorUrl`,
|
|
1425
|
+
`credential` or `instructions`. `nextAction` keeps
|
|
1426
|
+
`{ tool: "authorize_connector", arguments: { connector } }` in two cases: when no
|
|
1427
|
+
handoff could be attached, as for a request that has no handoff factory or whose
|
|
1428
|
+
signing failed, and in a program's terminal `write_outcome_unknown`-family result,
|
|
1429
|
+
which carries no handoff fields and so holds no link. A URL-elicitation host's
|
|
1430
|
+
call that falls back to a link gets the same `{ url, purpose }`.
|
|
1431
|
+
|
|
1432
|
+
The class-specific handoff is:
|
|
1020
1433
|
|
|
1021
1434
|
- `oauth`: an `authorizationUrl` to connecta's `/connect/<connector>?h=...` route
|
|
1022
1435
|
and instructions to sign in as the initiating user;
|
|
@@ -1066,7 +1479,9 @@ way to call a tool. On a writable endpoint, `call_tool` reaching an unannotated,
|
|
|
1066
1479
|
write-capable, or destructive tool returns `destructive_tool_requires_approval`
|
|
1067
1480
|
with `nextAction` for `call_destructive_tool` and the canonical address. On a
|
|
1068
1481
|
read-only endpoint every write path returns `read_only_endpoint` with no
|
|
1069
|
-
`nextAction`.
|
|
1482
|
+
`nextAction`. Its message says no write route exists, not to try another route,
|
|
1483
|
+
to tell the user, and that only the operator can provide a writable connection.
|
|
1484
|
+
Nothing is executed by these records.
|
|
1070
1485
|
|
|
1071
1486
|
`connecta.describe` keeps its failures inline instead, so one miss cannot
|
|
1072
1487
|
discard the other schemas; each failed entry carries a human `error`, typed
|
|
@@ -1129,11 +1544,47 @@ Activity records each of these refusals with the coarse `friction` class derived
|
|
|
1129
1544
|
from its typed error code — `tool_not_found`, `schema_retry`,
|
|
1130
1545
|
`destructive_reroute`, `auth_required` — and clamps the caller-authored
|
|
1131
1546
|
`connectorId`, `toolName`, and `address` it keeps, both under
|
|
1132
|
-
[code mode](./code-mode.md#activity)'s `V2` and `V3`.
|
|
1547
|
+
[code mode](./code-mode.md#activity)'s `V2` and `V3`. Optional
|
|
1548
|
+
[operation history](./operator-ui.md#opt-in-to-operation-history) records
|
|
1549
|
+
search, skill and result reads with fixed names, without arguments or payloads;
|
|
1550
|
+
it is off by default and separate from downstream-call counts. A `call_tool` result too
|
|
1133
1551
|
large to return inline is the one exception: it is friction
|
|
1134
1552
|
(`result_too_large`) on a call whose `outcome` is still `"success"`, so it
|
|
1135
1553
|
carries no `errorCode`.
|
|
1136
1554
|
|
|
1555
|
+
Recovery actions outside the tool namespace use `nextAction.action` and a
|
|
1556
|
+
`purpose`: `reconnect_host` repairs inbound HTTP authentication (separate from
|
|
1557
|
+
connector OAuth), `contact_operator` reports catalog or utility availability,
|
|
1558
|
+
`inspect_capabilities` requests a supporting client/operation, and
|
|
1559
|
+
`recover_result` requests a retained handle or an honest missing-output report.
|
|
1560
|
+
Unsupported task, Skills, and resource capabilities use the non-retryable
|
|
1561
|
+
`unsupported_capability` code with `inspect_capabilities`. Missing or expired handles have `code: not_found`; storage outages retain
|
|
1562
|
+
`code: unavailable`. Neither action authorizes anything or replays writes.
|
|
1563
|
+
Unknown skill names and connectors without a guide retain their local lookup
|
|
1564
|
+
diagnostic with `code: not_found` and a `nextAction` calling `skills` with no
|
|
1565
|
+
name to discover available names. They do not request service restoration.
|
|
1566
|
+
The server's search page contract states action discriminators compactly; action
|
|
1567
|
+
advice is additive, while catalog error and authorization handoff fields retain
|
|
1568
|
+
their detailed contracts.
|
|
1569
|
+
A spent read continuation's fresh-call action takes precedence over generic
|
|
1570
|
+
retry advice, while auth and provider permission repair prerequisites and unknown-write reconciliation remain
|
|
1571
|
+
in force. HTTP 401/403 responses preserve the adapter status and challenge. JSON responses
|
|
1572
|
+
carry the host recovery envelope. Custom non-JSON responses preserve their body
|
|
1573
|
+
and lifetime, with the same typed facts in `Connecta-Error-Code`,
|
|
1574
|
+
`Connecta-Recovery`, and JSON-encoded `Connecta-Next-Action` headers (also exposed
|
|
1575
|
+
to cross-origin hosts).
|
|
1576
|
+
|
|
1577
|
+
## Recovery limits
|
|
1578
|
+
|
|
1579
|
+
Typed `invalid_args` failures without validation findings point to scoped schema
|
|
1580
|
+
inspection. `not_found` asks the caller to verify the resource id, account and
|
|
1581
|
+
visibility. `unavailable` asks the operator to restore connector availability.
|
|
1582
|
+
These use `nextAction` for an executable action and `retry` for explicit limits.
|
|
1583
|
+
An uncertain write has no safe replay action: read its target to reconcile its
|
|
1584
|
+
outcome before deciding on any fresh operation. Host authentication requires
|
|
1585
|
+
reconnecting the host; downstream authorization handoffs repair credentials;
|
|
1586
|
+
provider permission denial requires the resource owner to grant access.
|
|
1587
|
+
|
|
1137
1588
|
## Argument recovery
|
|
1138
1589
|
|
|
1139
1590
|
Standard API hatches refuse marker containment in request paths and query
|
|
@@ -1220,9 +1671,16 @@ its received types and schema facts. A custom connector's own findings must
|
|
|
1220
1671
|
follow the same rule. Operator records contain only checked failure facts,
|
|
1221
1672
|
never findings or examples.
|
|
1222
1673
|
|
|
1223
|
-
`
|
|
1224
|
-
|
|
1225
|
-
`
|
|
1674
|
+
When `issues` and `acceptedKeys` are present and nothing was truncated, the
|
|
1675
|
+
findings name the fix, so the envelope has no `nextAction` and `retry` leads.
|
|
1676
|
+
Otherwise `nextAction` points to discovery scoped to the same connector and tool
|
|
1677
|
+
name, with the purpose "Only if the validation above is not enough: inspect the
|
|
1678
|
+
schema.", routed like any other miss: a program to `connecta.search`, a top-level
|
|
1679
|
+
call to `search_tools`. `example` is synthesized from the published schema and
|
|
1680
|
+
checked against its validator; a plain required string reads as a named
|
|
1681
|
+
placeholder such as `"<id>"` rather than an empty string, which would pass local
|
|
1682
|
+
validation and reach the downstream. A schema that refuses the placeholder, and a
|
|
1683
|
+
`writeOnly` position, keep the plain sample. `retry` says to correct
|
|
1226
1684
|
the listed arguments and reissue the original operation. What the local
|
|
1227
1685
|
validator declines to evaluate is [code mode](./code-mode.md#errors)'s `E8`.
|
|
1228
1686
|
|
|
@@ -1235,6 +1693,15 @@ Unknown-address errors list `configuredConnectors` from the current endpoint's
|
|
|
1235
1693
|
scoped registry, including an empty list when none are accessible. They never
|
|
1236
1694
|
list connectors from another pool or identity's grants.
|
|
1237
1695
|
|
|
1696
|
+
An `unknown_tool` error from a direct call or a program carries `suggestions`
|
|
1697
|
+
when the connector has a tool with a near name: canonical addresses ranked as
|
|
1698
|
+
describe ranks them, and only reads on a read-only endpoint or connector.
|
|
1699
|
+
|
|
1700
|
+
A direct call whose arguments do not match `call_tool` or `call_destructive_tool`
|
|
1701
|
+
fails input validation before anything is resolved. The message names the shape:
|
|
1702
|
+
`Send { address, args }`, with the tool's own arguments inside `args`. Neither
|
|
1703
|
+
tool accepts MCP's `name` or `arguments` keys.
|
|
1704
|
+
|
|
1238
1705
|
A write that times out after dispatch without complete auxiliary-only proof
|
|
1239
1706
|
returns `write_outcome_unknown` with
|
|
1240
1707
|
`retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted?, recovery? }`.
|
|
@@ -1357,7 +1824,7 @@ resent. It is never retryable and carries no wait. Until the credential is
|
|
|
1357
1824
|
renewed, the failure keeps its authorization code (`auth_required`, or
|
|
1358
1825
|
`downstream_oauth_required` for an OAuth connector) with
|
|
1359
1826
|
`writeOutcome: "unknown"`, `reconciliationRequired: true`, and the
|
|
1360
|
-
|
|
1827
|
+
authorization handoff, whose `instructions` and `nextAction` say to authorize and then
|
|
1361
1828
|
re-read the target before any fresh call. That holds whatever renewal meets: a
|
|
1362
1829
|
refused or unreachable token endpoint, a disconnect, or the caller leaving. It
|
|
1363
1830
|
also holds whatever the call met after the 401: a body past the response
|
|
@@ -1402,7 +1869,16 @@ in `writes.unknown` even when the program caught the failure. When that write
|
|
|
1402
1869
|
met an authorization verdict, the program's error is that verdict, as a direct
|
|
1403
1870
|
call reports it: its code, `connector`, `recovery`, and `nextAction`, with
|
|
1404
1871
|
`writeOutcome: "unknown"` and `reconciliationRequired: true`. Any other unknown
|
|
1405
|
-
write makes it `write_outcome_unknown`.
|
|
1872
|
+
write makes it `write_outcome_unknown`. Either error's `unknownWrites` lists at
|
|
1873
|
+
most ten such writes with their `address`, `code` and redacted `message`, and
|
|
1874
|
+
`unknownWritesTruncated` marks a write left out or a message dropped. A write whose
|
|
1875
|
+
tool answered `isError` carries `toolAnswered: true`, on a direct call too: its
|
|
1876
|
+
message is the tool's own words, up to 4,096 UTF-8 bytes, cut only between tokens
|
|
1877
|
+
and marked `… [truncated]`. Its outcome stays unknown because its handler ran before
|
|
1878
|
+
it answered, and `retry` says to check the target and to give the user any approval
|
|
1879
|
+
link or request id. A program's `write_outcome_unknown` leads its `message` with
|
|
1880
|
+
those words instead of repeating them in the entry. A later call the tool's words
|
|
1881
|
+
ask for, such as one carrying a provider's approval token, is a new deliberate call.
|
|
1406
1882
|
|
|
1407
1883
|
The classification served for the call decides this advice, including deployment
|
|
1408
1884
|
`classification` overrides. For example, `classification: { crm: { get_person:
|
|
@@ -1428,7 +1904,8 @@ at the downstream boundary, even when the agent may see the error's message.
|
|
|
1428
1904
|
A pool set to `surface: "native"` lists each downstream tool as its own MCP
|
|
1429
1905
|
tool instead of the six meta-tools, for clients that already defer tools behind
|
|
1430
1906
|
their own search (Eve, Claude Code with tool search). The meta surface stays
|
|
1431
|
-
the default everywhere
|
|
1907
|
+
the default everywhere. Plain `/mcp` is always meta for a maintainer; a person
|
|
1908
|
+
routed there by their one pool membership gets that pool's surface.
|
|
1432
1909
|
|
|
1433
1910
|
```ts
|
|
1434
1911
|
pools: {
|
|
@@ -1442,71 +1919,132 @@ refuses it otherwise, and refuses any `surface` other than `"meta"` or
|
|
|
1442
1919
|
`"native"`. Both pool endpoints, `/mcp/<pool>` and `/agent/<pool>`, serve the
|
|
1443
1920
|
pool's surface.
|
|
1444
1921
|
|
|
1445
|
-
| A native pool lists | Shape
|
|
1446
|
-
| ---------------------------- |
|
|
1447
|
-
| Each visible downstream tool |
|
|
1448
|
-
| `execute_code` | Unless `executeCode: false`. Each downstream tool's description then ends with `Program address: <connector>.<tool>`, the address `connecta.call` takes
|
|
1449
|
-
| `read_result` | Only with `executeCode: false`: `{ id, offset?, page?, maxBytes? }` pages a stashed result exactly as `connecta.result` does, with the same bindings and rechecks
|
|
1450
|
-
| `authorize_connector` | Always; requests the existing typed authorization handoff for a granted connector
|
|
1451
|
-
| `skills` | Always
|
|
1922
|
+
| A native pool lists | Shape |
|
|
1923
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1924
|
+
| Each visible downstream tool | A read is `<connector>__<tool>`, a write `<connector>__WRITE__<tool>`; object input constraints and local references preserved; no `outputSchema`; `_meta["dev.connecta/address"]` is its address |
|
|
1925
|
+
| `execute_code` | Unless `executeCode: false`. Each downstream tool's description then ends with `Program address: <connector>.<tool>`, the address `connecta.call` takes |
|
|
1926
|
+
| `read_result` | Only with `executeCode: false`: `{ id, offset?, page?, maxBytes? }` pages a stashed result exactly as `connecta.result` does, with the same bindings and rechecks |
|
|
1927
|
+
| `authorize_connector` | Always; requests the existing typed authorization handoff for a granted connector |
|
|
1928
|
+
| `skills` | Always |
|
|
1452
1929
|
|
|
1453
1930
|
`search_tools`, `call_tool`, and `call_destructive_tool` are not listed. Auth failures still carry their handoff in the error envelope.
|
|
1454
1931
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
`
|
|
1932
|
+
Native tool descriptions append `Guide: skills <name>` when their connector has
|
|
1933
|
+
guidance. Call `skills` with `{name:"<name>"}` to read it. The name is the same
|
|
1934
|
+
allocated readable name listed by `skills`, including collision suffixes; a
|
|
1935
|
+
name reserved for a built-in guide uses its canonical connector skill URI. This
|
|
1936
|
+
compact pointer repeats on each tool so host search results retain it; connectors
|
|
1937
|
+
without guides add nothing. It depends only on the admitted connector roster.
|
|
1938
|
+
|
|
1939
|
+
**Names.** A name is a pure function of the connector id, the downstream tool
|
|
1940
|
+
name and its served classification, so it survives catalog refreshes until the
|
|
1941
|
+
classification changes. A read is `<connector>__<tool>` and a write
|
|
1942
|
+
`<connector>__WRITE__<tool>` when the tool name uses only `[A-Za-z0-9_-]`,
|
|
1943
|
+
contains no `__`, does not start with `_`, and the whole name fits 48 characters.
|
|
1944
|
+
Otherwise a read is that name's head plus `_` and the first six hex digits of the
|
|
1945
|
+
SHA-256 of `<connector>.<tool>`, and a write is up to 24 characters of the
|
|
1946
|
+
connector, `__WRITE__`, the head of the tool name, `_w` and the same six hex
|
|
1947
|
+
digits. A write whose plain name would end in `_` and six lowercase hex digits,
|
|
1948
|
+
such as a tool named `get_373b87` or `facade`, also takes this hashed form. A
|
|
1949
|
+
connector id that does not start with a letter is prefixed with `C`. 48 characters
|
|
1950
|
+
leave room for a client's own prefix under common 64-character tool-name limits
|
|
1951
|
+
(Eve's `connecta__`, Claude Code's `mcp__connecta__`).
|
|
1952
|
+
|
|
1953
|
+
A name a client holds for a read can never select a write. Every write's name
|
|
1954
|
+
contains `__WRITE__` and never ends in `_` and six lowercase hex digits. A read's
|
|
1955
|
+
name contains `__WRITE__` only when the tool's own name supplies it, and such a read
|
|
1956
|
+
is always hashed, so it ends that way and takes the read route. A tool's read name
|
|
1957
|
+
does not depend on its classification or on this rule, so names clients cached from
|
|
1958
|
+
earlier versions stay read names. Two tools that would share either name are both
|
|
1959
|
+
left out of the list, with an operator warning, and a call by either of their names
|
|
1960
|
+
is refused; both stay callable by address in `execute_code`.
|
|
1961
|
+
|
|
1962
|
+
Read a tool's name from `tools/list` rather than building it. Host approval rules,
|
|
1963
|
+
allowlists, prompts and recipes that name a native write must use its `__WRITE__`
|
|
1964
|
+
name, and a tool that changes classification changes name. Programs keep using the
|
|
1965
|
+
address, which never changes.
|
|
1466
1966
|
|
|
1467
1967
|
**Annotations** come from the served classification only: a read is
|
|
1468
1968
|
`{ readOnlyHint: true, destructiveHint: false, openWorldHint: true }` and a
|
|
1469
1969
|
write `{ readOnlyHint: false, destructiveHint: true, openWorldHint: true }`,
|
|
1470
|
-
whatever hints the downstream declared. A read
|
|
1970
|
+
whatever hints the downstream declared. A deployment `"read"` override never
|
|
1971
|
+
produces read annotations for a tool whose source explicitly declares a write;
|
|
1972
|
+
`"read-repeatable"` does, and accepts that a host may repeat the call. See
|
|
1973
|
+
[deployment overrides](./integrating.md#another-remote-mcp-server). A read-only endpoint or connector
|
|
1471
1974
|
leaves write tools out of the list, by the same rule search and describe use.
|
|
1472
1975
|
|
|
1473
|
-
**Calls take the existing paths.** A
|
|
1474
|
-
`call_tool` call for its address
|
|
1475
|
-
`call_destructive_tool` call
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
names fail with `unknown_tool`; catalog failures retain their handoff. Success results are
|
|
1976
|
+
**Calls take the existing paths.** A name selects its route: a read's name is the
|
|
1977
|
+
`call_tool` call for its address, and a write's name is the
|
|
1978
|
+
`call_destructive_tool` call, run by the same handler. So classification,
|
|
1979
|
+
`read_only_endpoint` refusal, argument validation and repair, admission, redaction,
|
|
1980
|
+
single-attempt writes, auth elicitation, downstream input, result caps, and activity
|
|
1981
|
+
use the same enforcement. Activity records source `native` with the canonical
|
|
1982
|
+
address and served classification. Unknown or ambiguous names fail with
|
|
1983
|
+
`unknown_tool`; catalog failures retain their handoff. Success results are
|
|
1482
1984
|
`call_tool`'s default mode: native `content`, never `structuredContent`, which
|
|
1483
1985
|
is why no `outputSchema` is advertised. A truncated result's notice pages with
|
|
1484
1986
|
`connecta.result` inside `execute_code`, or with `read_result` on a pool without
|
|
1485
1987
|
`execute_code`.
|
|
1486
1988
|
|
|
1487
1989
|
**Approval is the host's, enforcement is Connecta's.** The host approves from
|
|
1488
|
-
the annotations of the list it cached
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1990
|
+
the annotations of the list it cached, and some hosts resend calls they believe
|
|
1991
|
+
are read-only when a connection fails. Those resends are new stateless requests:
|
|
1992
|
+
their JSON-RPC ids restart and they carry no session id, so Connecta cannot tell
|
|
1993
|
+
them from new calls. A name is therefore bound to its classification. If a tool
|
|
1994
|
+
changes from read to write between that list and a call (a downstream changes its
|
|
1995
|
+
hints, or the deployment changes its classification), the cached read name takes
|
|
1996
|
+
the `call_tool` route, which refuses before dispatch: `read_only_endpoint` on a
|
|
1997
|
+
read-only endpoint, otherwise `destructive_tool_requires_approval` with
|
|
1998
|
+
`nextAction.tool` set to the write's name. However often the host resends, nothing
|
|
1999
|
+
reaches the downstream. The write runs only by its own name, annotated destructive, after a refreshed
|
|
2000
|
+
list; Claude Code 2.1.318 and Codex 0.166.0 did not resend destructive calls when
|
|
2001
|
+
tested. In the other direction, a cached
|
|
2002
|
+
write name whose tool is now a read still runs, once, on the
|
|
2003
|
+
`call_destructive_tool` route. A native `tools/list` carries a 300-second private
|
|
2004
|
+
cache hint, against an hour on meta endpoints; hosts need not honor it. A listing
|
|
2005
|
+
that omits a connector or lists one stale carries 60 seconds instead, about when
|
|
2006
|
+
that connector is next asked.
|
|
2007
|
+
|
|
2008
|
+
The verdict is Connecta's current catalog: a downstream change takes effect when
|
|
2009
|
+
Connecta's catalog next loads it. A tool whose source declares a write and whose
|
|
2010
|
+
deployment classification overrides it to read has a read's name and takes the
|
|
2011
|
+
`call_tool` route, and a host may resend it; do not override a write to read unless
|
|
2012
|
+
repeating it is harmless. This also applies to `call_tool` on the meta surface.
|
|
2013
|
+
|
|
2014
|
+
**Known limit: a change Connecta has not seen.** Connecta enforces the catalog it
|
|
2015
|
+
holds. A vendor can change a tool from read to write before Connecta's catalog
|
|
2016
|
+
refreshes, and until then every read-annotated route treats that tool as a read:
|
|
2017
|
+
`call_tool`, a read-annotated native tool and programs on a read-only endpoint. A
|
|
2018
|
+
host may also resend such a call after a connection failure, so the vendor can
|
|
2019
|
+
receive it more than once. The catalog refresh closes this window, and nothing
|
|
2020
|
+
closes it sooner.
|
|
1496
2021
|
|
|
1497
2022
|
**Listing.** `tools/list` is one page with no cursor (Eve's MCP client reads only the
|
|
1498
2023
|
first page). A pool listing more than 1,000 tools fails `tools/list` with a
|
|
1499
2024
|
JSON-RPC error asking the operator to narrow the pool; nothing is silently
|
|
1500
|
-
dropped. A connector
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
2025
|
+
dropped. A connector that does not answer in time, is `unavailable` or
|
|
2026
|
+
`rate_limited`, or hits a retryable downstream failure stays listed from its last
|
|
2027
|
+
complete catalog, under the same limit and partitioning as
|
|
2028
|
+
[stale listings](#lexical-discovery). Such a connector gets the 5-second deadline, or
|
|
2029
|
+
`discovery.probeTimeoutMs` when shorter, and is skipped for up to 60 seconds after
|
|
2030
|
+
a failure; one with nothing to fall back on keeps the full deadline. Each of its
|
|
2031
|
+
tools carries `_meta["dev.connecta/stale"]` with `code`, `ageMs` and any
|
|
2032
|
+
`retryAfterMs`, and `readOnlyHint: false, destructiveHint: true` whatever its stored
|
|
2033
|
+
classification, because an old catalog cannot vouch for a read (INV-1). The `skills`
|
|
2034
|
+
tool's `dev.connecta/surface` metadata names these connectors in `staleCatalogs`. A
|
|
2035
|
+
`tools/call` always resolves a live catalog, under the verdict it finds.
|
|
2036
|
+
|
|
2037
|
+
A connector whose catalog cannot load and has no stale catalog, or that fails
|
|
2038
|
+
for authorization, permission or a malformed catalog, is left out of that listing
|
|
2039
|
+
whole, never partially, with an operator warning and a `catalogErrors` entry in the
|
|
2040
|
+
same metadata; its tools return with the next listing that loads it. Listing never
|
|
2041
|
+
starts authorization. The server still declares `tools.listChanged: false`.
|
|
1504
2042
|
|
|
1505
2043
|
**Instructions** replace the meta routing text: call one known operation
|
|
1506
|
-
directly; with `execute_code`,
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
`description` is
|
|
2044
|
+
directly; with `execute_code`, combine several using each tool's `Program address`;
|
|
2045
|
+
state the endpoint's write policy; on a writable endpoint, never resend a write to
|
|
2046
|
+
see its output; follow `nextAction` on errors and truncated results. A pool
|
|
2047
|
+
`description` is placed first, as on meta endpoints.
|
|
1510
2048
|
|
|
1511
2049
|
**Recovery.** `authorize_connector` remains listed even when a catalog fails.
|
|
1512
2050
|
Its description lists all granted connector ids independently of catalog health.
|
|
@@ -1519,12 +2057,14 @@ Repair and validation fields retain the exact constraint details.
|
|
|
1519
2057
|
Discovery `nextAction` points to the host's tool search (`host: "search"`), with
|
|
1520
2058
|
only a `query` argument. Retry actions and retry text name the native tool,
|
|
1521
2059
|
never a meta wrapper; a program on a native pool sees the same native wording
|
|
1522
|
-
in a failure it catches. An unknown name is reported as sent. Auth actions carry
|
|
1523
|
-
|
|
2060
|
+
in a failure it catches. An unknown name is reported as sent. Auth actions carry the link as `nextAction.url`,
|
|
2061
|
+
beside `authorizationUrl` or `operatorUrl`; give it and the instructions to the user or operator. Capable modern
|
|
1524
2062
|
clients may resume native auth and downstream form/URL input with the same
|
|
1525
2063
|
wire name, arguments and returned `requestState`. Changed names, targets or
|
|
1526
2064
|
arguments, expired state and spent retry nonces are refused. Never resend a
|
|
1527
|
-
write to recover output.
|
|
2065
|
+
write to recover output. A refusal of a stale read name says nothing was sent:
|
|
2066
|
+
refresh the host's tool list and call the write's own name only if the write is
|
|
2067
|
+
still wanted.
|
|
1528
2068
|
|
|
1529
2069
|
**Schemas.** Ordinary `type: "object"` schemas are byte-identical. Object
|
|
1530
2070
|
constraints without a top-level type, composed object schemas and local
|
|
@@ -1535,8 +2075,9 @@ connectors remain listed. Correct the schema or use the meta surface.
|
|
|
1535
2075
|
|
|
1536
2076
|
**Diagnostics.** `connecta doctor --pool assistant` recognizes the configured
|
|
1537
2077
|
surface and code setting from the local `skills` tool's
|
|
1538
|
-
`_meta["dev.connecta/surface"]`. It checks the required local tools,
|
|
1539
|
-
unavailable connector catalogs
|
|
2078
|
+
`_meta["dev.connecta/surface"]`. It checks the required local tools, fails on
|
|
2079
|
+
unavailable connector catalogs, listing-cap errors and connectors listed stale
|
|
2080
|
+
(`Native discovery degraded`), and runs only a constant
|
|
1540
2081
|
program when code is enabled. It never dispatches a downstream tool. The
|
|
1541
2082
|
metadata's `catalogErrors` exposes this request's failing connector ids,
|
|
1542
2083
|
including connectors with unrepresentable schemas, to every client of the pool.
|
|
@@ -1554,7 +2095,47 @@ production catalogs near the cap remain unmeasured.
|
|
|
1554
2095
|
|
|
1555
2096
|
Starting with 0.35.0, the Eve helper ships separately as `@zackbart/connecta-eve`.
|
|
1556
2097
|
Connecta core supplies the native pool surface and has no Eve dependency.
|
|
1557
|
-
Consult that package's documentation for its compatibility and setup.
|
|
2098
|
+
Consult that package's documentation for its compatibility and setup. The helper
|
|
2099
|
+
composes its Eve-specific guidance from [endpoint facts](#endpoint-facts) and
|
|
2100
|
+
places it before the server's own instructions; Connecta's text stays host-neutral.
|
|
2101
|
+
|
|
2102
|
+
## Endpoint facts
|
|
2103
|
+
|
|
2104
|
+
`server/discover` and `initialize` both advertise the endpoint in
|
|
2105
|
+
`capabilities.extensions["dev.connecta/endpoint"]`, so a client composing its
|
|
2106
|
+
own guidance can read what this request reached:
|
|
2107
|
+
|
|
2108
|
+
```json
|
|
2109
|
+
{
|
|
2110
|
+
"surface": "meta",
|
|
2111
|
+
"executeCode": true,
|
|
2112
|
+
"readOnly": false,
|
|
2113
|
+
"connectors": [
|
|
2114
|
+
{ "id": "crm", "title": "Customer records", "readFirst": "connector:crm" },
|
|
2115
|
+
{ "id": "tickets" }
|
|
2116
|
+
]
|
|
2117
|
+
}
|
|
2118
|
+
```
|
|
2119
|
+
|
|
2120
|
+
| Field | Meaning |
|
|
2121
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------ |
|
|
2122
|
+
| `surface` | `"meta"` or `"native"`: the surface this request is served |
|
|
2123
|
+
| `executeCode` | Whether `execute_code` is listed. Always `true` on meta |
|
|
2124
|
+
| `readOnly` | The effective policy: the endpoint is read-only, or every connector it reaches is |
|
|
2125
|
+
| `connectors[].id` | Each canonical connector id the caller's scope reaches, in registry order |
|
|
2126
|
+
| `connectors[].title` | Present only when the title says more than the id; normalized and capped as in the [inventory](#discovery-context) |
|
|
2127
|
+
| `connectors[].readFirst` | Present only for a required guide: the name to pass to `skills` |
|
|
2128
|
+
|
|
2129
|
+
The facts come from the admitted scope and deployment code, never from request
|
|
2130
|
+
arguments or `_meta`: a person on plain `/mcp` sees their one pool's connectors,
|
|
2131
|
+
a maintainer sees the whole catalog, and a refused caller gets the usual 404
|
|
2132
|
+
with no facts. The roster is the one `execute_code`'s inventory describes for
|
|
2133
|
+
the same caller, without its byte cap. Reading it loads no catalog, probes no
|
|
2134
|
+
credential, and records no activity. It says nothing about connector health or
|
|
2135
|
+
authorization, and grants nothing.
|
|
2136
|
+
|
|
2137
|
+
A client that does not recognize the extension ignores it. An older server omits
|
|
2138
|
+
it; treat the surface as unknown rather than assuming one.
|
|
1558
2139
|
|
|
1559
2140
|
## Contributor reference
|
|
1560
2141
|
|
|
@@ -1568,6 +2149,8 @@ owns the supplied usage instructions, each endpoint's instructions
|
|
|
1568
2149
|
(`endpointInstructions`), and connector guides. The native surface's names,
|
|
1569
2150
|
listing, and call translation live in
|
|
1570
2151
|
[native-tools.ts](https://github.com/zackbart/connecta/blob/main/src/native-tools.ts).
|
|
2152
|
+
[result-preview.ts](https://github.com/zackbart/connecta/blob/main/src/result-preview.ts)
|
|
2153
|
+
owns the rule behind previews of reads over the cap, as pure functions of redacted text.
|
|
1571
2154
|
|
|
1572
2155
|
[Schema tests](https://github.com/zackbart/connecta/blob/main/test/meta-tool-schemas.test.ts),
|
|
1573
2156
|
[search tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-search.test.ts),
|
|
@@ -1576,3 +2159,24 @@ listing, and call translation live in
|
|
|
1576
2159
|
and [native surface tests](https://github.com/zackbart/connecta/blob/main/test/native-tools.test.ts)
|
|
1577
2160
|
check the published contract. Guest parity and clause evidence are in
|
|
1578
2161
|
[code-mode verification](./code-mode.md#verification).
|
|
2162
|
+
|
|
2163
|
+
### Skills conformance (contributor)
|
|
2164
|
+
|
|
2165
|
+
On 2026-11-03, the official [conformance runner](https://github.com/modelcontextprotocol/conformance)
|
|
2166
|
+
at `c37eec888e1c6ff140af79987a40008548b7cc5f` passed enumeration (32 checks)
|
|
2167
|
+
and manifest (6 checks), with no warnings, against Connecta's HTTP handlers
|
|
2168
|
+
and a six-connector local fixture. Directory checks correctly skipped because
|
|
2169
|
+
`directoryRead` is not advertised; its wire-schema check passed. This is not
|
|
2170
|
+
full MCP conformance or a downstream-provider conformance claim.
|
|
2171
|
+
|
|
2172
|
+
To rerun, first read the runner's package scripts, CLI, connection code and
|
|
2173
|
+
`src/scenarios/server/skills/` as untrusted code in a temporary directory
|
|
2174
|
+
outside the checkout. Pin the commit above, install there with
|
|
2175
|
+
`pnpm install --ignore-scripts`, and keep its packages and outputs there.
|
|
2176
|
+
In the Connecta checkout, run `pnpm run build:operator-ui` and
|
|
2177
|
+
`pnpm exec tsx scripts/skills-conformance.ts`. From the runner directory, run
|
|
2178
|
+
`pnpm exec tsx src/index.ts server --url http://127.0.0.1:39743/mcp --scenario sep-2640-skills-enumeration --spec-version 2026-07-28 --force`;
|
|
2179
|
+
repeat with `sep-2640-skills-manifest` and `sep-2640-skills-directory`.
|
|
2180
|
+
Stop the fixture when done. `--force` selects extension scenarios outside the
|
|
2181
|
+
base protocol timeline. This remains an optional contributor check: the
|
|
2182
|
+
separately installed runner and dependency graph are not pinned in Connecta CI.
|