@zackbart/connecta 0.38.0 → 0.40.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 +91 -0
- package/README.md +3 -2
- package/bin/connecta.mjs +4 -8
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.js +6 -4
- package/dist/agent-routes.js +231 -0
- package/dist/auth/downstream-oauth/consent.js +519 -0
- package/dist/auth/downstream-oauth/grant-store.js +439 -0
- package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
- package/dist/auth/downstream-oauth/refresh-records.js +447 -0
- package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
- package/dist/auth/downstream-oauth.js +66 -2382
- package/dist/auth/native.js +37 -0
- package/dist/auth/oauth-grant.js +13 -0
- package/dist/auth/static-oauth.js +153 -131
- package/dist/auth-elicitation.js +7 -1
- package/dist/authorization-handoff.js +11 -5
- package/dist/catalog-cache.js +219 -59
- package/dist/catalog-policy.js +18 -0
- package/dist/catalog-service.js +284 -47
- package/dist/catalog.js +18 -2
- package/dist/config.d.ts +16 -11
- package/dist/config.js +9 -6
- package/dist/connector-caller.d.ts +1 -1
- package/dist/connector-health.js +59 -0
- package/dist/connector-status.js +117 -0
- package/dist/connectors/api-connector.d.ts +7 -2
- package/dist/connectors/api-connector.js +7 -4
- package/dist/connectors/negotiation-cache.js +22 -4
- package/dist/connectors/remote-mcp/call.js +164 -0
- package/dist/connectors/remote-mcp/catalog.js +278 -0
- package/dist/connectors/remote-mcp/config.js +263 -0
- package/dist/connectors/remote-mcp/connection.js +682 -0
- package/dist/connectors/remote-mcp/errors.js +488 -0
- package/dist/connectors/remote-mcp/oauth.js +473 -0
- package/dist/connectors/remote-mcp/resources.js +473 -0
- package/dist/connectors/remote-mcp/runtime.js +1 -0
- package/dist/connectors/remote-mcp/transport.js +555 -0
- package/dist/connectors/remote-mcp.js +62 -2863
- package/dist/credential-contract.d.ts +23 -0
- package/dist/credential-reads.js +17 -0
- package/dist/credential-verification.js +53 -0
- package/dist/credentials.d.ts +7 -0
- package/dist/credentials.js +60 -25
- package/dist/describe-config.d.ts +12 -2
- package/dist/describe-config.js +2 -0
- package/dist/discovery-health.js +199 -0
- package/dist/downstream-input.js +2 -0
- package/dist/errors.d.ts +55 -17
- package/dist/errors.js +95 -41
- package/dist/execute.js +152 -114
- package/dist/executor-result.js +3 -1
- package/dist/guest-functions.js +83 -0
- package/dist/guest-runtime.js +60 -19
- package/dist/guest-types.d.ts +19 -5
- package/dist/http/argument-problems.js +35 -0
- package/dist/http/errors.js +5 -4
- package/dist/http/fields.js +192 -13
- package/dist/http/hatch-response.js +73 -0
- package/dist/http/hatch.js +88 -23
- package/dist/http/oauth.js +3 -2
- package/dist/http/provider.js +32 -4
- package/dist/http/safety.js +8 -0
- package/dist/http/tools.js +44 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -2
- package/dist/invocation.js +26 -8
- package/dist/membership.d.ts +13 -5
- package/dist/membership.js +34 -2
- package/dist/meta-tools.js +54 -31
- package/dist/native-recovery.js +4 -16
- package/dist/native-tools.js +5 -2
- package/dist/oauth-sealing.js +10 -0
- package/dist/operation-vocabulary.js +135 -13
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +1 -0
- package/dist/operator-ui/contract.d.ts +10 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +22 -1
- package/dist/provider-call-facts.js +6 -2
- package/dist/providers/_shared/rest/operation-index.js +48 -50
- package/dist/providers/_shared/rest/value-safety.js +80 -0
- package/dist/providers/breeze/api.js +7 -1
- package/dist/providers/breeze/skill.generated.js +1 -1
- package/dist/providers/breeze/tools.js +4 -3
- package/dist/providers/ccb/api.js +2 -1
- package/dist/providers/ccb/index.js +5 -0
- package/dist/providers/ccb/skill.generated.js +1 -1
- package/dist/providers/cloudflare/api.js +8 -1
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/lib/schemas.js +24 -22
- package/dist/providers/docs/safety.js +2 -0
- package/dist/providers/docs/skill.generated.js +1 -1
- package/dist/providers/docs/tools.js +2 -4
- package/dist/providers/drive/api.js +1 -0
- package/dist/providers/drive/lib/contracts.js +8 -5
- package/dist/providers/drive/skill.generated.js +1 -1
- package/dist/providers/drive/tools.js +11 -16
- package/dist/providers/forms/api.js +1 -0
- package/dist/providers/forms/skill.generated.js +1 -1
- package/dist/providers/forms/tools.js +10 -7
- package/dist/providers/github/api.js +1 -0
- package/dist/providers/github/lib/discovery.js +1 -1
- package/dist/providers/github/skill.generated.js +1 -1
- package/dist/providers/github/tools.js +61 -17
- package/dist/providers/gmail/lib/schemas.js +60 -50
- package/dist/providers/gmail/skill.generated.js +1 -1
- package/dist/providers/gmail/tools.js +7 -5
- package/dist/providers/granola/index.js +2 -0
- package/dist/providers/granola/skill.generated.js +1 -1
- package/dist/providers/overflow/api.js +7 -1
- package/dist/providers/overflow/skill.generated.js +1 -1
- package/dist/providers/planning-center/api.js +3 -1
- 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/safety.js +80 -39
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/api.js +1 -0
- package/dist/providers/sheets/skill.generated.js +1 -1
- package/dist/providers/sheets/tools.js +13 -14
- package/dist/providers/slides/skill.generated.js +1 -1
- package/dist/providers/slides/tools.js +15 -13
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/tithely/api.js +3 -1
- package/dist/providers/tithely/skill.generated.js +1 -1
- package/dist/providers/vercel/api.js +1 -1
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/providers/vercel/tools.js +4 -3
- package/dist/registry.d.ts +10 -4
- package/dist/registry.js +41 -13
- package/dist/routes/credentials.js +129 -60
- package/dist/routes/dispatch.js +5 -1
- package/dist/routes/mcp.js +30 -14
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +6 -1
- package/dist/routes/shared.js +7 -1
- package/dist/routes/ui-config.js +2 -2
- package/dist/routes/ui-data.js +126 -20
- package/dist/routes/ui.js +16 -0
- package/dist/runtime/call.js +7 -2
- package/dist/runtime/native-storage.js +12 -0
- package/dist/runtime/request-reads.js +207 -0
- package/dist/runtime/request-scope.js +16 -0
- package/dist/runtime/storage.js +12 -0
- package/dist/skills.js +37 -20
- package/dist/storage/keys.js +53 -3
- package/dist/storage/memory.js +8 -2
- package/dist/storage/sql-native.js +53 -1
- package/dist/storage/sql.js +12 -2
- package/dist/storage-digest.js +32 -0
- package/dist/timeout.js +8 -0
- package/dist/types.d.ts +33 -0
- package/dist/usage-guide.js +16 -35
- package/dist/validate.d.ts +3 -0
- package/dist/validate.js +220 -158
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/write-outcome.js +0 -3
- package/documentation/README.md +13 -13
- package/documentation/architecture.md +385 -20
- package/documentation/auth.md +233 -64
- package/documentation/code-mode.md +122 -39
- package/documentation/deploying.md +6 -3
- package/documentation/integrating.md +16 -2
- package/documentation/meta-tools.md +240 -49
- package/documentation/migration-0.33.md +5 -1
- package/documentation/migration-0.34.md +3 -1
- package/documentation/operator-tests.md +1 -1
- package/documentation/operator-ui.md +105 -12
- package/documentation/pool-membership-upgrade.md +44 -0
- package/documentation/providers.md +105 -0
- package/examples/worker/AGENTS.md +10 -10
- package/examples/worker/README.md +4 -3
- package/examples/worker/src/connecta.config.ts +1 -0
- package/guides/connecting-agents.md +2 -1
- package/guides/how-it-works.md +4 -2
- package/package.json +1 -1
- package/templates/node/AGENTS.md +13 -12
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +1 -0
|
@@ -47,7 +47,8 @@ as the isolate — on Workers a lazy module-scope singleton, which is why both
|
|
|
47
47
|
deployment shapes construct it outside the request handler.
|
|
48
48
|
|
|
49
49
|
An OAuth connector, `remoteMcp()` or `api()` with a static grant, owns a
|
|
50
|
-
runtime-local refresh completion gate in
|
|
50
|
+
runtime-local refresh completion gate in
|
|
51
|
+
`src/auth/downstream-oauth/refresh-coordinator.ts`.
|
|
51
52
|
Joined scopes share the outcome while keeping their own request I/O and
|
|
52
53
|
cancellation. Grant use and the caller's wait belong to the call. Renewal
|
|
53
54
|
preparation is call-owned only until the first durable coordinator write
|
|
@@ -99,6 +100,18 @@ never retried and requires re-consent. Valid rotations retry their grant commit
|
|
|
99
100
|
up to 32 times without another HTTP request; exhausted commits and all dispatched
|
|
100
101
|
provider failures require re-consent. [Auth](./auth.md#refresh-failures) describes the lease and failure
|
|
101
102
|
contracts.
|
|
103
|
+
The grant body stores `expiresAt` beside the tokens it describes; reads never
|
|
104
|
+
add it (INV-10). A call renews a token within `OAUTH_REFRESH_SKEW_MS` (60 s)
|
|
105
|
+
of it before sending: the invocation's authorization check (`registerInvocationAuth`
|
|
106
|
+
in `src/connectors/remote-mcp.ts` and `src/connectors/api-connector.ts`) runs
|
|
107
|
+
the 401 flow before entering the connector, so a failed renewal is a
|
|
108
|
+
pre-dispatch authorization failure, never an unknown write (INV-9).
|
|
109
|
+
`KvOAuthProvider.renewalDue` decides from the grant read that check already
|
|
110
|
+
made, in memory. The token request carries the access token it replaces
|
|
111
|
+
(`earlyRenewalFetch`), and the coordinator adopts any stored token that differs
|
|
112
|
+
instead of redeeming again. Status, listing, discovery and the connectionless
|
|
113
|
+
catalog read never renew. [Auth](./auth.md#refreshing-before-expiry) has the
|
|
114
|
+
operator contract.
|
|
102
115
|
|
|
103
116
|
**Per request, and no longer.** The MCP server, its transport, downstream MCP
|
|
104
117
|
clients, abort signals, and the connector scope a probe opens all belong to the
|
|
@@ -107,7 +120,10 @@ across requests on Workers is a cross-request capability leak, and a promise awa
|
|
|
107
120
|
work the runtime may already have torn down. Deferred work has one sanctioned
|
|
108
121
|
channel, `ctx.waitUntil`, threaded through `fetch(request, env, ctx)` — activity
|
|
109
122
|
writes and OAuth renewals from claim submission use it. Catalog listing and SQL cache
|
|
110
|
-
publication finish in the requesting scope.
|
|
123
|
+
publication finish in the requesting scope. The operator surface's Promise
|
|
124
|
+
edge (`ui.handle`) owns the request's signal and is awaited to its end even
|
|
125
|
+
when the caller leaves, so a storage write it has started, such as a stored
|
|
126
|
+
status observation, settles before the request does. A fresh
|
|
111
127
|
`McpServer` per request is what makes the deployment stateless: no sessions, no
|
|
112
128
|
server push, no stream resumability, scope resolved rather than remembered.
|
|
113
129
|
|
|
@@ -324,7 +340,7 @@ without storage reads, decryption or hashing. Catalog invalidation (including
|
|
|
324
340
|
operator refresh and OAuth connect/disconnect/restart) advances a counter for the
|
|
325
341
|
registry's cache store; built-in vault set/clear advances a counter for that vault.
|
|
326
342
|
These weakly held counters contain no catalogs, credentials or caller identity;
|
|
327
|
-
catalogs and derived views remain request-local. A moved fence replaces the
|
|
343
|
+
registry catalogs and derived views remain request-local. A moved fence replaces the
|
|
328
344
|
retained read and its search views, and prevents joining an older registry flight.
|
|
329
345
|
Changes made through another process/isolate or directly in storage need not be
|
|
330
346
|
observed mid-request; the next request uses the durable generation and auth checks.
|
|
@@ -603,6 +619,176 @@ Only complete, intake-redacted tools/list, resources/list and
|
|
|
603
619
|
resources/templates/list successes are stored. Resource payloads are not cached. SDK default
|
|
604
620
|
in-memory caching is replaced, so raw catalogs never enter it.
|
|
605
621
|
|
|
622
|
+
A warm isolate retains at most four validated stored JSON catalogs, with a
|
|
623
|
+
combined accounting budget of 8 MiB (twice serialized input bytes, key bytes and
|
|
624
|
+
64 bytes per object property/array slot; larger entries bypass reuse).
|
|
625
|
+
These deeply frozen facts contain no request context, transport, signal,
|
|
626
|
+
callback or promise, and require neither weak references nor cleanup timers.
|
|
627
|
+
Reuse follows the same manifest TTL checks, complete chunk reads, recomputed
|
|
628
|
+
SHA-256 fingerprint and fresh generation/credential acceptance fence as a cold
|
|
629
|
+
read. Its key binds the stored entry, current private owner/deployment partition
|
|
630
|
+
(even for public entries), and chunk fingerprint. Request-specific credential
|
|
631
|
+
redaction and TTL intake still run every time. Eviction affects performance
|
|
632
|
+
only; SQL remains authoritative.
|
|
633
|
+
|
|
634
|
+
Stored catalog and negotiation keys use deployment-keyed partitions. A remote
|
|
635
|
+
connector's whole cache configuration and negotiation input enter a stored key
|
|
636
|
+
only through HMAC-SHA-256 (`src/storage-digest.ts`) under a key that never
|
|
637
|
+
enters storage: every configured URL in full (the endpoint, a client metadata
|
|
638
|
+
document, a static client's issuer and the redirect URI), static auth header
|
|
639
|
+
values, resource headers, and a static OAuth client's secret. The
|
|
640
|
+
operator-managed or request-resolved credential (joined with the forwarded
|
|
641
|
+
principal), a custom connector's credential values, and the OAuth token
|
|
642
|
+
response that fences a listing are keyed the same way. The key is the vault's
|
|
643
|
+
`storageDigestKey()`, which `encryptedCredentialVault()` derives with HKDF from
|
|
644
|
+
its credential key. Without a vault, or with a custom vault that lacks the
|
|
645
|
+
method, a random key held only in the process is used: the partitions stay
|
|
646
|
+
exact, but another process or isolate addresses different entries and fills
|
|
647
|
+
its own cache, so cross-isolate reuse of remote catalogs, verdicts and health
|
|
648
|
+
records needs a vault. A different URL, header, credential, token response or
|
|
649
|
+
epoch addresses a different entry at once, including a value changed by
|
|
650
|
+
redeploy, and an earlier entry is never served under the new value; rotating
|
|
651
|
+
back to an earlier value within its TTL finds its entries again. A new
|
|
652
|
+
deployment key addresses new entries. Entries written by earlier releases are
|
|
653
|
+
not read and expire on their own TTLs (catalogs within `catalogMaxTtlSeconds`,
|
|
654
|
+
refresh baselines within 48 hours, verdicts within six hours), so each remote
|
|
655
|
+
connector's partitions refill once after the upgrade with one handshake and
|
|
656
|
+
listing.
|
|
657
|
+
|
|
658
|
+
Warm remote resolution batches the negotiation verdict and locating generation
|
|
659
|
+
when their physical keys share an observed SQL store. Private and public
|
|
660
|
+
manifest probes share a `getMany` statement; their normal precedence and public
|
|
661
|
+
scope check still apply. These batches contain only reads. Catalog addresses
|
|
662
|
+
use the listing's pinned OAuth identity and locating generation; they authorize
|
|
663
|
+
nothing. After chunk verification, acceptance re-reads the OAuth identity and
|
|
664
|
+
generation separately and refuses a changed epoch or token digest. The invocation
|
|
665
|
+
gate checks disconnect first, then reads tokens separately to refuse absent
|
|
666
|
+
credentials and observe current expiry and the invalidation fingerprint. Token
|
|
667
|
+
removal, expiry, scope and replacement can change without advancing the epoch;
|
|
668
|
+
epoch fences alone cannot protect these values. The SDK's later token read may
|
|
669
|
+
return no tokens and omit Authorization, so it does not replace invocation's
|
|
670
|
+
token-presence refusal. The transport also refuses tokenless MCP requests on
|
|
671
|
+
passive OAuth paths, regardless of provider history; an explicit authorization
|
|
672
|
+
start may probe for the SDK's challenge. A refused tool send binds its private
|
|
673
|
+
failure provenance to the undispatched physical request. Invocation accepts it
|
|
674
|
+
only for the owning boundary and whole logical call; error fields, copied
|
|
675
|
+
connectors, and refusals after a send cannot supply that proof.
|
|
676
|
+
Connection reuse and SDK dispatch retain their fresh
|
|
677
|
+
epoch or token reads; no credential read is prefetched.
|
|
678
|
+
Stores with replaced methods or separate backing stores retain individual reads.
|
|
679
|
+
|
|
680
|
+
A remote connector's `listTools` without a live connection in its request
|
|
681
|
+
reads this cache before connecting. It derives the same partition a connect
|
|
682
|
+
binds (the admitted principal, the caller's credential digest, or the owner's
|
|
683
|
+
OAuth epoch and token digest) from storage reads only, then makes the SDK's
|
|
684
|
+
two-probe read with the pre-connect empty server identity: the private slot,
|
|
685
|
+
then a shared connector's public slot, served only when written public. OAuth
|
|
686
|
+
grants are read by a non-mutating snapshot: no plaintext grant is sealed in
|
|
687
|
+
place, no epoch is bound, nothing is refreshed. Every identity check during
|
|
688
|
+
cache acceptance reads the grant again and refuses the entry unless the epoch
|
|
689
|
+
its partition was derived from and the token digest both still hold. A missing
|
|
690
|
+
or expired generation is a miss at every check and is never claimed or
|
|
691
|
+
recreated. An unexpired complete entry is returned with no transport,
|
|
692
|
+
handshake, session cleanup or storage write. A miss, an expired entry, an
|
|
693
|
+
explicit `ttlMs: 0`, an invalidated generation, a credential, token or epoch
|
|
694
|
+
change, an operator-disconnected or token-less grant, a cache read failure,
|
|
695
|
+
and a downstream whose stored negotiation verdict is modern all connect as
|
|
696
|
+
before. Modern downstreams connect without a handshake and read the cache
|
|
697
|
+
through the SDK. The connect reuses the credential and negotiation verdict that
|
|
698
|
+
read resolved. `list_changed` arrives only over a live connection, so an
|
|
699
|
+
unconnected catalog changes at its TTL or on invalidation.
|
|
700
|
+
|
|
701
|
+
A catalog served from this cache, before or after connecting, does not
|
|
702
|
+
validate downstream credential health. An expired or vendor-revoked credential
|
|
703
|
+
can still expose cached tools, and discovery reports no auth-related
|
|
704
|
+
`catalogErrors` entry for it until the cache misses. Tool calls and resource
|
|
705
|
+
reads reach the downstream and report auth failures with their handoffs. A
|
|
706
|
+
status probe connects: a legacy downstream answers a handshake, while a modern
|
|
707
|
+
downstream with a stored verdict connects without a request. A credential test
|
|
708
|
+
always sends a live `tools/list` that bypasses the cache in both directions.
|
|
709
|
+
|
|
710
|
+
In `"auto"` mode a remote connector stores its negotiation verdict, data only,
|
|
711
|
+
in the `negotiation` key family, partitioned by connector configuration,
|
|
712
|
+
credential and OAuth epoch, with secret-bearing inputs keyed as above. A later connect reuses it instead of probing with
|
|
713
|
+
`server/discover`. A modern verdict lasts five minutes. A legacy verdict lasts
|
|
714
|
+
six hours, because a server that adds the modern era still answers the legacy
|
|
715
|
+
handshake. Two signals end a verdict early:
|
|
716
|
+
|
|
717
|
+
- A legacy-verdict handshake refused with a 4xx other than 401, 403 or 429 (a
|
|
718
|
+
modern-only server answers 400 with its supported versions) is negotiated
|
|
719
|
+
again at once in the same connect. The new verdict replaces the old.
|
|
720
|
+
- On a modern connection (adopted from the verdict or just negotiated), an
|
|
721
|
+
HTTP 400 is read through a bounded clone of its body, as the transport's
|
|
722
|
+
backward-compatibility rule requires. A modern server also answers 400 with
|
|
723
|
+
`HeaderMismatch` (-32020) and `MissingRequiredClientCapability` (-32021),
|
|
724
|
+
which concern the request alone and keep the verdict and the connection.
|
|
725
|
+
`UnsupportedProtocolVersion` (-32022) keeps them only while its
|
|
726
|
+
`data.supported` list (at most 64 strings) still names the request's
|
|
727
|
+
`MCP-Protocol-Version`; a missing, malformed or other list makes the verdict
|
|
728
|
+
unusable. Any other body, empty or not a recognized modern error (a legacy
|
|
729
|
+
server's sessionless refusal, a gateway's page), is a legacy refusal. Either
|
|
730
|
+
way the verdict is deleted and that client retired. The refused request
|
|
731
|
+
fails and is never repeated. The next connect, in the same request or a
|
|
732
|
+
later one, probes again. The clone read ends with the call, the request or
|
|
733
|
+
the connection scope: an abort cancels it, cancels the unread original and
|
|
734
|
+
takes no evidence (INV-7).
|
|
735
|
+
|
|
736
|
+
An upgrade to a server that still answers the legacy handshake is therefore
|
|
737
|
+
adopted within six hours. Losing the legacy handshake costs one refused
|
|
738
|
+
request, and a downgrade costs the one request that met the refusal.
|
|
739
|
+
|
|
740
|
+
**Discovery health.** An unscoped search (`search_tools` or `connecta.search`
|
|
741
|
+
without `connector`) probes each connector for at most 5,000 ms, or
|
|
742
|
+
`discovery.probeTimeoutMs` when that is shorter, and its probes end within a
|
|
743
|
+
9,000 ms discovery budget. A tenth of the budget, at most 500 ms, is kept for
|
|
744
|
+
recording outcomes: a probe that starts late gets only what remains before
|
|
745
|
+
that slice, and one that cannot start reports `timeout`. Health reads, claims
|
|
746
|
+
and writes start only while budget remains and the caller is still waiting;
|
|
747
|
+
health is otherwise unknown. Once started, each is awaited to its end before
|
|
748
|
+
the search returns, never raced or detached, since a storage write cannot be
|
|
749
|
+
withdrawn once sent. The budget bounds when health work starts, not when it
|
|
750
|
+
finishes, so slow storage can carry a search past it by one operation's
|
|
751
|
+
latency. A scoped search,
|
|
752
|
+
`connecta.describe`, native `tools/list` and tool calls keep the full
|
|
753
|
+
`discovery.probeTimeoutMs`. A probe that times out on its own deadline, or a
|
|
754
|
+
retryable failure that itself took at least one second or named its own wait,
|
|
755
|
+
writes a record in the `connector-health:v1:` family: the connecta
|
|
756
|
+
classification code, observation time, retry time (60 seconds later, or the
|
|
757
|
+
downstream's shorter named wait, at least one second), and the connector's
|
|
758
|
+
catalog invalidation generation. Each probe's time is its own, measured as it
|
|
759
|
+
settles. Records hold no downstream text, tools, arguments or credentials.
|
|
760
|
+
Each is keyed by connector id and the deployment-keyed digest (as for the
|
|
761
|
+
catalog cache above) of the catalog owner partition (principal, subject,
|
|
762
|
+
caller identity and pool, origin and public URL), the admitted endpoint (plain
|
|
763
|
+
`/mcp`, `/mcp/<pool>` or `/agent/<pool>`), the connector's described
|
|
764
|
+
configuration, whose endpoint keeps its path, and the package version. One owner's
|
|
765
|
+
or endpoint's record never skips another's search. A change `describe()`
|
|
766
|
+
cannot see, such as a static header rotated in deployment code, takes effect
|
|
767
|
+
on redeploy; a record written before it keeps skipping for at most its retry
|
|
768
|
+
time, about 60 seconds. Until its retry time an unscoped search skips that connector
|
|
769
|
+
and reports it in `catalogErrors` with the recorded code, `retryable: true`,
|
|
770
|
+
`retryAfterMs`, and a fixed message that names the scoped search to try it
|
|
771
|
+
now; no tools from the failed walk are ever served. A scoped search still
|
|
772
|
+
probes and settles the record. A record whose generation no longer matches
|
|
773
|
+
lapses, so a vault credential change, OAuth connect or disconnect, or operator
|
|
774
|
+
invalidation clears every partition at once. A success, or an auth or
|
|
775
|
+
permission failure that shows the connector answers, clears the record this
|
|
776
|
+
request saw. Caller cancellation, a probe the budget cut short, a fast
|
|
777
|
+
failure, and a non-retryable failure record nothing. After its retry time a
|
|
778
|
+
record lingers for 60 seconds: the request that dispatches the next probe
|
|
779
|
+
claims it by compare-and-set, with a lease to an absolute time, the end of
|
|
780
|
+
that request's discovery budget plus two seconds, so a slow claim write cannot
|
|
781
|
+
shorten it; the others keep skipping until it settles or the lease lapses. Health reads are one `getMany` per unscoped search, made
|
|
782
|
+
once per request; a health storage failure is logged and never fails
|
|
783
|
+
discovery.
|
|
784
|
+
|
|
785
|
+
A healthy connector whose complete catalog always takes longer than 5 seconds
|
|
786
|
+
to list can therefore stay out of unscoped discovery: each unscoped probe
|
|
787
|
+
times out and records it. A scoped search, or any operation with the longer
|
|
788
|
+
`discovery.probeTimeoutMs` deadline that completes the listing (describe, a
|
|
789
|
+
tool call), warms a reusable complete catalog that later unscoped searches
|
|
790
|
+
read without the slow listing, and a success clears the record.
|
|
791
|
+
|
|
606
792
|
`discovery.catalogTtlSeconds` (300 by default) is the fallback for legacy
|
|
607
793
|
listings without `ttlMs`. Positive hints are bounded by
|
|
608
794
|
`catalogMinTtlSeconds` (0) and `catalogMaxTtlSeconds` (86400, also the SDK
|
|
@@ -653,7 +839,15 @@ activity write. The first complete catalog is a baseline.
|
|
|
653
839
|
Hash-only baselines publish bounded chunks before a manifest. They store the
|
|
654
840
|
catalog digest and hashes of each tool name and fact, so added, removed and changed counts require no persisted tool names or
|
|
655
841
|
schemas. They use the exact host auth/config cache partition, survive catalog
|
|
656
|
-
expiry and invalidation, and expire
|
|
842
|
+
expiry and invalidation, and expire 48 hours after they are written. A refresh
|
|
843
|
+
reads the manifest first. When its digest matches a baseline written within the
|
|
844
|
+
last hour, the refresh writes nothing; an older matching baseline is rewritten
|
|
845
|
+
with fresh chunks, so every refresh leaves a baseline for at least 47 hours.
|
|
846
|
+
A manifest's lifetime is never extended without new chunks, so its chunks
|
|
847
|
+
always outlive it. A changed digest writes the new baseline and reports drift
|
|
848
|
+
against the previous one. An unchanged zero-TTL listing therefore writes no
|
|
849
|
+
baseline; it still lists live every time and deletes its cache slots, since a
|
|
850
|
+
concurrent request may have published an older reusable catalog there. There is no connection-wide
|
|
657
851
|
fallback baseline across partitions. Private catalog events use the admitted
|
|
658
852
|
principal actor basis even for shared-auth connectors; activity disclosure
|
|
659
853
|
requires that same owner and the recorded pool's grants. Ownerless private
|
|
@@ -1128,7 +1322,11 @@ switched by the environment, and keep their entries under 30 lines.
|
|
|
1128
1322
|
A maintained provider is one `defineProvider()` call (`src/provider.ts`): a
|
|
1129
1323
|
name, title, `kind: "api"`, maintained skill, closed `options` shape and a
|
|
1130
1324
|
synchronous `create`. Common values are checked before construction and the
|
|
1131
|
-
factory stamps its provider name onto `describe().source.provider`.
|
|
1325
|
+
factory stamps its provider name onto `describe().source.provider`. The kit's
|
|
1326
|
+
`defineApiProvider` also takes optional `searchSynonyms` groups (single
|
|
1327
|
+
lowercase words, at most 32 groups of 2–8), checks them at definition and
|
|
1328
|
+
attaches them to each connector for
|
|
1329
|
+
[lexical discovery](./meta-tools.md#lexical-discovery). API
|
|
1132
1330
|
implementations and their contract evidence remain vendored. Hosted MCP servers
|
|
1133
1331
|
use generic `remoteMcp()` entries; provider reviews and hosted defaults are
|
|
1134
1332
|
removed. Legacy hosted selectors fail at construction with the generic migration
|
|
@@ -1543,7 +1741,10 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
|
|
|
1543
1741
|
Node/SQLite/QuickJS and native Workers/D1. It covers transcript-too-large
|
|
1544
1742
|
recovery, empty intermediate pages, selected fields, withheld photo/credential
|
|
1545
1743
|
values and shared-vault reuse after application reconstruction. No-op,
|
|
1546
|
-
fabricated-read and erased-delivery controls must fail.
|
|
1744
|
+
fabricated-read and erased-delivery controls must fail. Its fake, tasks and
|
|
1745
|
+
deployment wiring live in `src/providers/granola/eval/`, unshipped;
|
|
1746
|
+
`providers:generate` registers a provider's `eval/` wiring with the harness
|
|
1747
|
+
(`eval/README.md`, provider-local eval wiring).
|
|
1547
1748
|
- `test/identity-write-journey.node.test.ts` uses the existing Node deployment
|
|
1548
1749
|
adapter, stateful MCP fake, SQLite vault and QuickJS to grade admitted human
|
|
1549
1750
|
and machine identities, pool isolation, fail-closed discovery, endpoint and
|
|
@@ -1810,10 +2011,10 @@ storage is the root's namespaced to its principal.
|
|
|
1810
2011
|
| Admission (`executor-admission.ts`, `call-admission.ts`) | Queued waiters are Deferreds settled by whoever removes them from the queue; a wait is one flat race of grant, Clock timeout, and signal. The uncontended path stays synchronous. Two controllers, as [#453](https://github.com/zackbart/connecta/issues/453) requires. |
|
|
1811
2012
|
| One tool call (`invocation.ts`) | One fiber: resolution, the read-only and schema refusals, admission, and the downstream attempt sit under a single `withDeadlineEffect`, whose expiry interrupts the call wherever it is. The permit is an `acquireRelease`; the connector call is `Effect.tryPromise` over the unchanged `Connector`. |
|
|
1812
2013
|
| `execute_code` (`execute.ts`) | One fiber whose Scope owns the run's signal and executor lease, so a result, a throw, the watchdog, and cancellation all release the lease and abort the signal the same way. Public executor methods stay Promise-shaped; registered built-in execution composes directly as an Effect under the signal and `execute.watchdogMs`. Queued acquisition retains its Promise request handoff. Each guest host call is a fiber of its own. |
|
|
1813
|
-
| Discovery (`catalog-service.ts`) | A request-scoped cache: one shared read per connector (`runtime/shared-read.ts`), settled by the read itself and carrying its own signal and the probe timeout whichever asker starts it. Each asker waits under its own deadline and signal, so one that times out or is cancelled fails alone, and the read is cancelled only once every asker has gone. Fan-out is `Effect.forEach` under the discovery concurrency.
|
|
2014
|
+
| Discovery (`catalog-service.ts`) | A request-scoped cache: one shared read per connector (`runtime/shared-read.ts`), settled by the read itself and carrying its own signal and the probe timeout whichever asker starts it. Each asker waits under its own deadline and signal, so one that times out or is cancelled fails alone, and the read is cancelled only once every asker has gone. Fan-out is `Effect.forEach` under the discovery concurrency; unscoped search adds the discovery budget and health record (`discovery-health.ts`). |
|
|
1814
2015
|
| Registry (`registry.ts`) | The result stash is a program over `Storage`. Same-request catalog loads share one read the way discovery's do. Remote MCP caching uses the SDK ResponseCacheStore adapter over SQL-backed KV, with no cross-request catalog flight. |
|
|
1815
|
-
| Remote MCP (`connectors/remote-mcp.ts`)
|
|
1816
|
-
| Downstream OAuth (`auth/downstream-oauth
|
|
2016
|
+
| Remote MCP (`connectors/remote-mcp.ts`, `connectors/remote-mcp/`) | Each request scope's state holds a Scope, each connection is a lease forked from it, and a connect in flight is a Deferred carrying the client it connected. Closing a session and the transport are each bounded to a second. |
|
|
2017
|
+
| Downstream OAuth (`auth/downstream-oauth*`, `auth/static-oauth.ts`) | A refresh flight is a Deferred. Before the first durable coordinator write (claim CAS) is submitted, call/request closure stops preparation without a CAS, token request or state change. Submission transfers renewal to the storage coordinator under its own 35-second preparation deadline and 20-second token-request/body deadline, followed by the fenced rotation commit through the runtime deferred-work hook. A closed caller promptly fails without consuming the result, marking its ledger renewed or sending/resending a resource request. |
|
|
1817
2018
|
| Operator and activity data (`routes/operator.ts`) | Each JSON route is one program run by `serveOperator` behind the Promise `handle()`. Reads run under the request's signal; writes do not, so a vault write or OAuth disconnect that started reaches its cache invalidation. |
|
|
1818
2019
|
| QuickJS pool (`executors/quickjs.ts`, Node only) | Each child is a scoped resource whose release sends SIGTERM, then SIGKILL after a second; crash respawn backoff is a `Schedule`. |
|
|
1819
2020
|
|
|
@@ -1974,6 +2175,11 @@ value, matching that fallback. Interruption remains interruption. The lookup
|
|
|
1974
2175
|
module imports no SQL or platform implementation and does not change the
|
|
1975
2176
|
published storage interfaces.
|
|
1976
2177
|
|
|
2178
|
+
`NativeStorage.getMany` reads several keys in one statement, answering by
|
|
2179
|
+
position exactly as one `get` per key would; the SQL drivers share it, a
|
|
2180
|
+
replaced `get` answers each key itself, and other stores fall back to one `get`
|
|
2181
|
+
at a time. Catalog reads fetch up to eight chunks per round trip with it.
|
|
2182
|
+
|
|
1977
2183
|
`openSqlite` retains file modes, WAL, busy timeout,
|
|
1978
2184
|
and database ownership. `test/sql-effect-contract.ts` retains affected-row,
|
|
1979
2185
|
50-claim, typed-failure, cancellation, and tracing probes on SQLite and native
|
|
@@ -2133,6 +2339,93 @@ within five minutes of expiry in the same auth partition. Partial walks and
|
|
|
2133
2339
|
auth or permission failures refuse fallback; resources require unexpired
|
|
2134
2340
|
inventories, and stale fallback cannot authorize post-entry auth recovery.
|
|
2135
2341
|
|
|
2342
|
+
**Repeated reads share one request's memo.** `src/runtime/request-reads.ts`
|
|
2343
|
+
gives each request owner a memo of storage reads, emptied when the request
|
|
2344
|
+
closes (INV-7). It holds plain strings keyed by the physical key in the
|
|
2345
|
+
underlying store, so namespaced views of one partition share an entry and two
|
|
2346
|
+
principals' partitions never do. A read consults it only where the call site
|
|
2347
|
+
opts in and states why staleness within one request is safe; every other read
|
|
2348
|
+
goes to storage.
|
|
2349
|
+
|
|
2350
|
+
- Every write through the SQL drivers, `memoryStorage()` or a namespaced view
|
|
2351
|
+
over them drops that key from every open memo in the same isolate, before
|
|
2352
|
+
submission and again on settlement. A read that such a write overlapped is
|
|
2353
|
+
not recorded.
|
|
2354
|
+
- Only the latest read of a key that the request started records. An earlier
|
|
2355
|
+
read that settles later never replaces a newer fresh observation.
|
|
2356
|
+
- A fresh read records what it saw, so a memoized reader is never older than
|
|
2357
|
+
its request's latest fresh read of the key.
|
|
2358
|
+
- A custom store, or a built-in one with a replaced method, is never memoized.
|
|
2359
|
+
- A runtime without `WeakRef` and `FinalizationRegistry` memoizes nothing:
|
|
2360
|
+
workerd before compatibility date 2025-05-05 without `enable_weak_ref` reads
|
|
2361
|
+
storage every time.
|
|
2362
|
+
|
|
2363
|
+
The memo cannot see another isolate, another process sharing the database, or
|
|
2364
|
+
an entry expiring. After the first read, a memoized key can lag such a change
|
|
2365
|
+
for the rest of the request. So nothing that gates authority, sends a
|
|
2366
|
+
credential, or decides a write is memoized.
|
|
2367
|
+
|
|
2368
|
+
Memoized:
|
|
2369
|
+
|
|
2370
|
+
- the negotiation verdict, which only orders handshake versions;
|
|
2371
|
+
- catalog generation reads that only locate an entry, accepted or refused by a
|
|
2372
|
+
fresh fence;
|
|
2373
|
+
- the credential reads behind connector status, drift and the operator UI card,
|
|
2374
|
+
which only label a view. Only the built-in vault with its original `getAll`
|
|
2375
|
+
and `metadata` shares a read; a custom vault, a subclass, or an instance
|
|
2376
|
+
that overrides either method is called itself for every read, dispatch
|
|
2377
|
+
included.
|
|
2378
|
+
|
|
2379
|
+
Always fresh:
|
|
2380
|
+
|
|
2381
|
+
- every OAuth grant read, including the token and epoch check behind each SDK
|
|
2382
|
+
message (a disconnect need not revoke the vendor token), `peekGrant` and
|
|
2383
|
+
every cache-acceptance identity check, compare-and-set loops, refresh claims
|
|
2384
|
+
and leases, resets and disconnects;
|
|
2385
|
+
- the catalog generation fence before a cached entry is served or published;
|
|
2386
|
+
- the credential a call sends.
|
|
2387
|
+
|
|
2388
|
+
`test/request-reads.node.test.ts` holds the memo's contract, including a
|
|
2389
|
+
disconnect written by another isolate between paginated `tools/list` pages.
|
|
2390
|
+
|
|
2391
|
+
### Request budgets (contributor)
|
|
2392
|
+
|
|
2393
|
+
`test/request-reads.node.test.ts` also guards the common request shapes the way
|
|
2394
|
+
bundle caps guard size. Each case counts downstream HTTP requests, D1 round
|
|
2395
|
+
trips (`all`, `run` or `batch`) and SQL statements separately, since a batch is
|
|
2396
|
+
one round trip but several statements. The fixture is production-shaped: D1
|
|
2397
|
+
over `node:sqlite`, a sealed vault holding the API credential and the OAuth
|
|
2398
|
+
grant, Cloudflare Access with `accessTokens()`, a D1 activity sink settled
|
|
2399
|
+
through `waitUntil`, the operator UI and QuickJS standing in for Dynamic Worker
|
|
2400
|
+
startup. "Cold isolate" is a new deployment instance over the populated
|
|
2401
|
+
database. The ceilings, measured value plus about 10% D1 headroom and exact
|
|
2402
|
+
downstream counts, live in its `BUDGETS` table. Measured on main in October 2026:
|
|
2403
|
+
|
|
2404
|
+
| Request | Downstream | D1 round trips | SQL statements |
|
|
2405
|
+
| ---------------------------------------- | ---------: | -------------: | -------------: |
|
|
2406
|
+
| Legacy remote MCP call, warm | 5 | 6 | 7 |
|
|
2407
|
+
| Legacy remote MCP call, cold isolate | 5 | 9 | 12 |
|
|
2408
|
+
| Modern remote MCP call, warm | 1 | 7 | 8 |
|
|
2409
|
+
| Modern remote MCP call, cold isolate | 1 | 10 | 13 |
|
|
2410
|
+
| OAuth remote MCP call, warm | 5 | 19 | 20 |
|
|
2411
|
+
| OAuth remote MCP call, cold isolate | 5 | 22 | 25 |
|
|
2412
|
+
| OAuth call after a 401, with refresh | 9 | 39 | 48 |
|
|
2413
|
+
| OAuth call with proactive refresh | 6 | 35 | 43 |
|
|
2414
|
+
| `api()` credential call, warm | 1 | 3 | 4 |
|
|
2415
|
+
| `api()` credential call, cold isolate | 1 | 6 | 9 |
|
|
2416
|
+
| Unscoped search, warm (3 remote, 1 api) | 0 | 20 | 20 |
|
|
2417
|
+
| `execute_code`, 5 calls to one connector | 9 | 10 | 15 |
|
|
2418
|
+
| UI connector detail, `api()` credential | 0 | 2 | 2 |
|
|
2419
|
+
| UI connector detail, credential remote | 4 | 9 | 9 |
|
|
2420
|
+
| UI connector detail, OAuth remote | 4 | 18 | 18 |
|
|
2421
|
+
|
|
2422
|
+
Header-auth calls to the legacy (session) server handshake per request:
|
|
2423
|
+
`initialize`, `notifications/initialized`, the SSE `GET`, the call and the
|
|
2424
|
+
session `DELETE`. The modern server advertises a catalog TTL, so a warm call is
|
|
2425
|
+
the call alone. OAuth calls read the grant fresh before every SDK message.
|
|
2426
|
+
Run with `D1_BUDGET_REPORT=1` to print each measurement; a failing case prints
|
|
2427
|
+
its request and statement log.
|
|
2428
|
+
|
|
2136
2429
|
### Contributor provider kit
|
|
2137
2430
|
|
|
2138
2431
|
Provider declarations use `src/http/vendor.ts` for HttpApi operations and
|
|
@@ -2151,17 +2444,29 @@ exact vendor count as `page.total` from its page callback. The kit validates a
|
|
|
2151
2444
|
nonnegative safe integer and includes it on continuing and terminal pages;
|
|
2152
2445
|
without a vendor count the property is absent. Lists without the opt-in keep
|
|
2153
2446
|
their existing schema and result bytes. Never derive an exact total from a
|
|
2154
|
-
partial page or an estimate. `src/http/fields.ts`
|
|
2155
|
-
|
|
2156
|
-
|
|
2447
|
+
partial page or an estimate. `src/http/fields.ts` derives each `fields`
|
|
2448
|
+
description's anchor and example from the contract that validates it. It
|
|
2449
|
+
refuses before dispatch a path whose first segment is not a declared property,
|
|
2450
|
+
and a deeper segment only inside an object the schema closes with
|
|
2451
|
+
`additionalProperties: false`; a selected result renders against a sparse
|
|
2452
|
+
output schema. `src/http/hatch.ts` is the
|
|
2157
2453
|
standard hatch (`_api_search`, `_api_details`, `_api_read`, `_api_write`) over
|
|
2158
2454
|
an `OperationIndex` with explicit verdicts. It keeps the REST family's
|
|
2159
2455
|
argument and envelope names and validates `fields` against the operation's
|
|
2160
|
-
declared response roots before dispatch.
|
|
2456
|
+
declared response roots before dispatch. A deeper miss is reported in
|
|
2457
|
+
bounded `warnings` naming declared properties only, never refused, because the
|
|
2458
|
+
pinned index declares paths only to a bounded depth. Search answers stay within 16 KiB
|
|
2161
2459
|
(at most eight compact entries, long labels cut and marked, continuation by
|
|
2162
2460
|
cursor); details stay within 32 KiB, paging whole parameters and body
|
|
2163
2461
|
properties, and chunking any piece larger than one answer, behind a bound
|
|
2164
|
-
cursor.
|
|
2462
|
+
cursor. `src/http/hatch-response.ts` builds details' capped `response` section
|
|
2463
|
+
from the index's declared response paths in `fields` coordinates. It lists
|
|
2464
|
+
selectable paths and promises no readability. Its `withheld` hint comes from
|
|
2465
|
+
`ResultSafety.withholds`: `ValueSafety.withholds` reads the rules `redact`
|
|
2466
|
+
applies that change every non-null value they reach, matched with list
|
|
2467
|
+
markers. Custom redactions may declare their own hint. Rules that can pass a
|
|
2468
|
+
value through (`url:`, `origin:`, `[?…]`, `@keys`, string-only scrubbing) are
|
|
2469
|
+
never hints. Standard hatch search removes conversational framing; framing-only
|
|
2165
2470
|
queries return no matches. It admits partial lexical matches alongside full
|
|
2166
2471
|
matches. Action/read queries require all content nouns in the operation text,
|
|
2167
2472
|
or any noun in the path/id with at least half the nouns in the text. Their
|
|
@@ -2308,9 +2613,10 @@ A binding may opt into `truncated: true` to supply reviewed size-cut paths
|
|
|
2308
2613
|
(such as `data.content`) in its answer. The hatch then declares and propagates
|
|
2309
2614
|
an optional top-level `truncated: string[]` on reads and writes. Without the
|
|
2310
2615
|
opt-in, the schemas and results are unchanged; size-cut metadata is not inferred
|
|
2311
|
-
from vendor booleans or added to other providers.
|
|
2312
|
-
|
|
2313
|
-
|
|
2616
|
+
from vendor booleans or added to other providers. Every hatch read and write
|
|
2617
|
+
declares an optional `warnings: string[]` for `fields` entries that matched
|
|
2618
|
+
nothing in a nonempty body. A separate `warnings: true` opt-in also preserves
|
|
2619
|
+
reviewed vendor warnings there, even when `fields` narrows the vendor body.
|
|
2314
2620
|
|
|
2315
2621
|
A binding may expand deployment-owned path defaults (Cloudflare) or canonicalize
|
|
2316
2622
|
repository contents paths (GitHub) before indexed resolution, and may prepare a
|
|
@@ -2839,6 +3145,51 @@ Effect is pinned exactly; deployments using Effect must resolve the same version
|
|
|
2839
3145
|
Per-provider bundles are reported, not capped, because their standalone graphs
|
|
2840
3146
|
include the shared core again. Deployed non-provider shapes are capped by
|
|
2841
3147
|
`scripts/bundle-budget.json`.
|
|
3148
|
+
The always-loaded agent surface has fixed-cost caps of the same kind: the
|
|
3149
|
+
`always-loaded agent surface budgets` cases in
|
|
3150
|
+
`test/meta-tool-schemas.test.ts` cap the instructions plus `tools/list` total,
|
|
3151
|
+
each tool description and output schema, the `usage` skill, and one default
|
|
3152
|
+
and one compact search row, measured on a pinned four-connector fixture.
|
|
3153
|
+
|
|
3154
|
+
### Route names
|
|
3155
|
+
|
|
3156
|
+
Connecta-authored agent text names a route in the wording of the surface that
|
|
3157
|
+
serves it, resolved when the text is built (`src/agent-routes.ts`). Nothing
|
|
3158
|
+
renders afterwards: no code scans or rewrites replies, program values,
|
|
3159
|
+
downstream results, resources, guides or caller data, so paging, previews,
|
|
3160
|
+
totals and offsets measure the final text.
|
|
3161
|
+
|
|
3162
|
+
Two tables per surface. `AgentRouteNames` names the tools (`call_tool`,
|
|
3163
|
+
`call_destructive_tool`, `search_tools`, `execute_code`, `authorize_connector`,
|
|
3164
|
+
`skills` on the meta surface); a sentence uses a name only when written for a
|
|
3165
|
+
surface that lists that tool. `RoutePhrases` holds complete phrases for
|
|
3166
|
+
sentences every surface shares, such as "for a direct call_tool only" against
|
|
3167
|
+
"when calling this tool directly", "call directly (call_tool, connecta.result
|
|
3168
|
+
to page)" against "call this tool directly and page with read_result", and the
|
|
3169
|
+
authorization sentences, which on native pools tell the agent to give the
|
|
3170
|
+
error's nextAction to the user or operator. `program` is undefined on a native
|
|
3171
|
+
pool without `execute_code`, so text about program capacity branches on it.
|
|
3172
|
+
The eval code arm's phrases keep the text its comparator protocol recorded.
|
|
3173
|
+
|
|
3174
|
+
Where each text gets its wording:
|
|
3175
|
+
|
|
3176
|
+
| Text | Wording |
|
|
3177
|
+
| --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3178
|
+
| Invocation refusals, authorization handoffs, paging notices | The request's names, from the MCP route through meta-tools, programs and native pools |
|
|
3179
|
+
| Connector and provider call text | `routePhrases(ctx)`; inside a call's Effect, the `Routes` reference; vendor tables' `words(detail, routes)`; an `api()` connection's write-401 refusal, configured at construction, receives the call's `Routes` |
|
|
3180
|
+
| Contexts the registry builds | The deployment's names (meta, or the eval code arm's); a call carries its request's names |
|
|
3181
|
+
| Static provider schema descriptions | `routeDescription(build)`: the declared meta text; each request's catalog sets them on its own copy of each `api()` tool, which keeps the tool's source classification, built for its serving surface. Description paths are cached per connector and tool name, and rendered text per declared description and surface. Downstream catalogs are never touched |
|
|
3182
|
+
| Meta instructions, meta-only tool descriptions, built-in guides | Meta names, built once; built-in guides keep one digest across deployments |
|
|
3183
|
+
| Native instructions, the `execute_code` description | Built per surface |
|
|
3184
|
+
| Program-return and program-call paging notes | Meta names; they name only `execute_code`, which every surface running programs lists |
|
|
3185
|
+
| The OAuth adapter's `UnauthorizedError` | Meta names; only its class reaches callers, which word their own error from the call |
|
|
3186
|
+
|
|
3187
|
+
The provider standard's S7 check refuses a spelled-out meta-tool name in
|
|
3188
|
+
provider code. Remaining literals are not agent route text: `execute_code`'s own
|
|
3189
|
+
budget and timeout errors, operator log lines, construction errors, the
|
|
3190
|
+
configuration reference, the OAuth callback page and operator fix prompts. The
|
|
3191
|
+
eval code arm marks its configuration with `renderRoutesAsPrograms`, an internal
|
|
3192
|
+
hook no package entry exports; no configuration key selects it.
|
|
2842
3193
|
|
|
2843
3194
|
[Decision 0019](https://github.com/zackbart/connecta/blob/main/decisions/0019-core-validation-http-boundaries.md) records the rejected Schema/HttpApi measurements.
|
|
2844
3195
|
|
|
@@ -2973,7 +3324,10 @@ compiling and configuring the real thing.
|
|
|
2973
3324
|
is a known failure only on refusal provenance recorded where the evidence is
|
|
2974
3325
|
observed, never inferred from an error code: (a) the ledger proves it never
|
|
2975
3326
|
left, or (b) the downstream served an HTTP refusal, a 4xx other than 408
|
|
2976
|
-
(`refusedStatus` and `vendorRefusal` in `src/provider-call.ts`)
|
|
3327
|
+
(`refusedStatus` and `vendorRefusal` in `src/provider-call.ts`); for
|
|
3328
|
+
`remoteMcp()` only when that 4xx carries a JSON-RPC error body, since a bare
|
|
3329
|
+
one may be a gateway's answer after the server acted (`ambiguous` on the
|
|
3330
|
+
call's dispatch in `src/connectors/remote-mcp/transport.ts`), under the
|
|
2977
3331
|
provider's existing served classification (a write's HTTP 409 is a `conflict`). One helper, `servedRefusal` in `src/write-outcome.ts`, decides
|
|
2978
3332
|
(b) for the ledger and for a transport outside one: a refusing status the
|
|
2979
3333
|
provider reads as `unavailable` or a timeout (Stripe's 424) is not proof, so
|
|
@@ -3419,8 +3773,19 @@ change. All 88 declarations still exclude Effect and have complete references.
|
|
|
3419
3773
|
### Native remote MCP programs (contributor)
|
|
3420
3774
|
|
|
3421
3775
|
Remote MCP connection joins, credential and epoch checks, catalog intake,
|
|
3422
|
-
negotiation, calls, resources, Skills, auth and status compose native programs
|
|
3423
|
-
`connectors/remote-mcp.ts
|
|
3776
|
+
negotiation, calls, resources, Skills, auth and status compose native programs.
|
|
3777
|
+
`connectors/remote-mcp.ts` keeps the published options, the redirect guard and
|
|
3778
|
+
`remoteMcp()`, which builds one runtime from per-seam factories in
|
|
3779
|
+
`connectors/remote-mcp/`: `config.ts` (options and validation), `errors.ts`
|
|
3780
|
+
(boundary error mapping), `transport.ts` (transport and write-once dispatch),
|
|
3781
|
+
`connection.ts` (the per-scope connection state machine), `catalog.ts`
|
|
3782
|
+
(catalog intake), `resources.ts` (Skills and resource reads), `oauth.ts` (the
|
|
3783
|
+
OAuth surface) and `call.ts` (`callTool`); `runtime.ts` types what they share.
|
|
3784
|
+
`auth/downstream-oauth.ts` keeps `KvOAuthProvider`'s state and declared
|
|
3785
|
+
methods; `auth/downstream-oauth/` holds the refresh coordinator and, by seam,
|
|
3786
|
+
the provider's native method bodies (grant and epoch store, consent records,
|
|
3787
|
+
refresh lease and spent records, SDK hooks), installed once each by
|
|
3788
|
+
`authMethods`. `catalog-cache.ts` owns native SDK cache reads,
|
|
3424
3789
|
chunked publication, generation fencing, complete refresh acceptance and listing
|
|
3425
3790
|
serialization; `connectors/negotiation-cache.ts` owns native negotiation storage.
|
|
3426
3791
|
The published connector, cache and SDK callback methods remain Promise shells.
|