@zackbart/connecta 0.29.0 → 0.30.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 +67 -0
- package/README.md +123 -134
- package/bin/connecta.mjs +13 -2
- package/bin/version.mjs +1 -1
- package/dist/argument-redaction.js +230 -0
- package/dist/auth-elicitation.js +14 -9
- package/dist/authorization-handoff.js +111 -0
- package/dist/catalog-cache.js +2 -2
- package/dist/catalog-service.js +103 -63
- package/dist/config.d.ts +2 -0
- package/dist/config.js +12 -7
- package/dist/connectors/guarded-fetch.js +3 -3
- package/dist/connectors/negotiation-cache.js +1 -1
- package/dist/downstream-input-context.js +9 -0
- package/dist/downstream-input.js +13 -11
- package/dist/errors.d.ts +36 -12
- package/dist/errors.js +19 -9
- package/dist/execute.js +375 -43
- package/dist/executor-result.js +11 -9
- package/dist/executors/quickjs-child.js +12 -5
- package/dist/executors/quickjs-protocol.js +5 -6
- package/dist/executors/quickjs-runtime.js +6 -1
- package/dist/executors/quickjs.js +20 -4
- package/dist/guest-runtime.js +52 -2
- package/dist/guest-types.d.ts +47 -5
- package/dist/index.d.ts +1 -1
- package/dist/invocation.js +40 -14
- package/dist/meta-output.js +0 -15
- package/dist/meta-tools.js +166 -95
- package/dist/oauth-handoff.js +88 -11
- package/dist/program-result.js +5 -0
- package/dist/program-writes.js +5 -1
- package/dist/provider.d.ts +1 -1
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/_shared/request-token.js +83 -0
- package/dist/providers/infisical/index.d.ts +7 -0
- package/dist/providers/infisical/index.js +713 -0
- package/dist/providers/infisical/skill.generated.js +8 -0
- package/dist/registry.d.ts +8 -6
- package/dist/registry.js +134 -61
- package/dist/resource-read.js +4 -1
- package/dist/routes/mcp.js +11 -2
- package/dist/sent-secrets.d.ts +54 -13
- package/dist/sent-secrets.js +770 -231
- package/dist/skills.js +26 -16
- package/dist/sqlite.js +21 -6
- package/dist/storage/keys.js +20 -5
- package/dist/storage/memory.js +9 -4
- package/dist/storage/sql.js +18 -9
- package/dist/types.d.ts +17 -3
- package/dist/usage-guide.js +49 -40
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +2 -1
- package/documentation/README.md +63 -0
- package/documentation/architecture.md +121 -89
- package/documentation/auth.md +138 -5
- package/documentation/code-mode.md +224 -107
- package/documentation/deploying.md +146 -0
- package/documentation/integrating.md +159 -0
- package/documentation/meta-tools.md +135 -30
- package/documentation/operating.md +135 -0
- package/documentation/operator-tests.md +4 -0
- package/documentation/operator-ui.md +132 -0
- package/documentation/provider-migration-0.29.md +5 -0
- package/examples/worker/README.md +7 -0
- package/package.json +7 -1
- package/templates/node/README.md +7 -0
- package/templates/node/package.json +1 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Deploying Connecta
|
|
2
|
+
|
|
3
|
+
Use this guide when creating or maintaining a deployment. For changes to the
|
|
4
|
+
Connecta package itself, follow [repository policy](https://github.com/zackbart/connecta/blob/main/AGENTS.md).
|
|
5
|
+
For work through an already connected endpoint, use [Operating an endpoint](./operating.md).
|
|
6
|
+
[All agent routes](./README.md) are in the index.
|
|
7
|
+
|
|
8
|
+
## Choose a deployment
|
|
9
|
+
|
|
10
|
+
| Platform | Runnable instructions | Required runtime and state |
|
|
11
|
+
| ------------------ | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
12
|
+
| Node | [Node template](../templates/node/README.md) | Node 22.13 or later, QuickJS peer, one SQLite file |
|
|
13
|
+
| Docker | [Container setup in the same Node template](../templates/node/README.md#run-it-in-docker) | Same source and config, persistent named volume |
|
|
14
|
+
| Cloudflare Workers | [Worker example](../examples/worker/README.md#deploy) | Workers Paid plan, Worker Loader binding, one D1 database, Cloudflare Access |
|
|
15
|
+
|
|
16
|
+
These are the two deployment shapes. Docker packages the Node project.
|
|
17
|
+
Templates and their configuration files own the full runnable steps and
|
|
18
|
+
environment-variable names; follow them rather than rebuilding their setup
|
|
19
|
+
from the contract reference.
|
|
20
|
+
|
|
21
|
+
## Start on Node
|
|
22
|
+
|
|
23
|
+
Create a new deployment directory with the CLI. `init` refuses an existing
|
|
24
|
+
path and pins the generated project's Connecta dependency to the CLI version:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npx @zackbart/connecta init my-connecta
|
|
28
|
+
cd my-connecta
|
|
29
|
+
npm install
|
|
30
|
+
npm run --silent provision-token -- "local-machine"
|
|
31
|
+
# Save the returned cta_ token privately. It is shown only once.
|
|
32
|
+
npm start
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The client connects to `http://localhost:8787/mcp` with
|
|
36
|
+
`Authorization: Bearer <returned-cta-token>`. The generated project always
|
|
37
|
+
installs `accessTokens(storage)`. An empty database admits no MCP caller even
|
|
38
|
+
though `/health` works. `CONNECTA_TOKEN` is only a client/doctor variable;
|
|
39
|
+
it never configures server authentication. The local commands read the process
|
|
40
|
+
environment and do not load `.env`; Docker Compose does.
|
|
41
|
+
|
|
42
|
+
Edit `src/connecta.config.ts` for connectors, public URL, auth, identity rules,
|
|
43
|
+
and optional modules. `src/index.ts` only constructs and starts the deployment.
|
|
44
|
+
Follow the [template README](../templates/node/README.md) for storage paths,
|
|
45
|
+
backups, Docker provisioning, secrets, and optional modules. Run the deployment's
|
|
46
|
+
`npm run typecheck` after configuration changes.
|
|
47
|
+
|
|
48
|
+
## Start on Workers
|
|
49
|
+
|
|
50
|
+
Follow the [Worker example](../examples/worker/README.md#deploy) to create D1,
|
|
51
|
+
set secrets, configure `PUBLIC_URL` and `LOADER`, and deploy with Wrangler.
|
|
52
|
+
When copying it into a separate repository, follow
|
|
53
|
+
[the separate-project installation](../examples/worker/README.md#copied-into-its-own-repository)
|
|
54
|
+
and pin Connecta's exact version. The optional `@cloudflare/codemode` peer
|
|
55
|
+
must be installed for the Worker executor.
|
|
56
|
+
|
|
57
|
+
Attach Cloudflare Access to the Worker destination. Interactive MCP clients
|
|
58
|
+
need Managed OAuth, Dynamic Client Registration, and the documented redirect
|
|
59
|
+
allowlist. Machines need Access service headers at the edge plus a stored
|
|
60
|
+
`cta_` bearer inside Connecta; service identity alone is refused. Keep
|
|
61
|
+
`enable_request_signal`, construct Connecta once per isolate, and pass `ctx`
|
|
62
|
+
through to `fetch`. The example owns those details and
|
|
63
|
+
[the machine-token procedure](../examples/worker/README.md#machine-tokens).
|
|
64
|
+
|
|
65
|
+
## Compose capabilities and access
|
|
66
|
+
|
|
67
|
+
Both configurations use `defineConfig((env) => …)` and a required executor.
|
|
68
|
+
[Integrating services](./integrating.md) explains the `connectors` array.
|
|
69
|
+
Configuration selects optional factories through explicit slots:
|
|
70
|
+
|
|
71
|
+
| Slot | Factory/import | Purpose |
|
|
72
|
+
| -------------- | -------------------------------------------------------------------- | -------------------------------------------------- |
|
|
73
|
+
| `ui` | `operatorUi()` from `@zackbart/connecta/ui` | Operator pages and scoped config inspection |
|
|
74
|
+
| `vault` | `encryptedCredentialVault()` from `@zackbart/connecta/credentials` | Encrypted operator-managed credentials |
|
|
75
|
+
| `activity` | `activityHistory()` from `@zackbart/connecta/activity` | Payload-free history over the platform's SQL store |
|
|
76
|
+
| `auth` | Clerk or Cloudflare Access from `@zackbart/connecta/auth/*` | Interactive inbound authentication |
|
|
77
|
+
| `accessTokens` | `accessTokens(storage)` from `@zackbart/connecta/auth/access-tokens` | Stored machine-token authentication |
|
|
78
|
+
|
|
79
|
+
Omitting an optional module leaves its implementation inactive. Invalid or
|
|
80
|
+
unknown configuration refuses construction; do not catch it and silently
|
|
81
|
+
fall back. [Configuration](./architecture.md#configuration) owns validation
|
|
82
|
+
and `describeConfig()`, and [optional modules](./architecture.md#optional-deployment-modules)
|
|
83
|
+
owns their composition contracts.
|
|
84
|
+
|
|
85
|
+
Enable `encryptedCredentialVault(storage, key)` before persisting downstream
|
|
86
|
+
OAuth credentials. Without a sealing vault, OAuth grants are stored as plaintext.
|
|
87
|
+
The platform templates describe the base64 32-byte encryption key. Keep it
|
|
88
|
+
outside the database and source control, and preserve it across upgrades.
|
|
89
|
+
[OAuth state at rest](./auth.md#downstream-oauth-state-at-rest) owns sealing and
|
|
90
|
+
existing-record migration.
|
|
91
|
+
|
|
92
|
+
One deployment serves one tenant and may admit several people. Node uses Clerk
|
|
93
|
+
for human auth; Access is Workers-only. Keep credential administration,
|
|
94
|
+
personal connection, token management, and activity access explicit.
|
|
95
|
+
[Auth](./auth.md#principals-visibility-and-operators) owns identity and grants;
|
|
96
|
+
[Clerk setup](./auth.md#clerk-oauth-tokens-and-operator-sessions) owns resource-bound
|
|
97
|
+
tokens and `aud_claim_enabled`. A person's operator session is not an MCP token.
|
|
98
|
+
|
|
99
|
+
Root and named pools default to `read-only`. A `trusted` pool allows writes
|
|
100
|
+
inside programs and marks `execute_code` as a write. Host approval remains the
|
|
101
|
+
host's decision. Pool membership intersects the caller's grants; it never
|
|
102
|
+
widens them. See [pools](./auth.md#pools) before changing trust.
|
|
103
|
+
|
|
104
|
+
## Verify and diagnose
|
|
105
|
+
|
|
106
|
+
With the deployment running, replace the placeholder with its saved client
|
|
107
|
+
token and run doctor:
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
CONNECTA_TOKEN='<stored-cta-token>' npx connecta doctor --url http://localhost:8787
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Doctor checks health, the required executor, and the current six-tool set.
|
|
114
|
+
For a Worker protected by Access, follow the example's doctor credentials
|
|
115
|
+
and service-header setup. If the operator UI is enabled, authenticated
|
|
116
|
+
`connecta doctor --config --url <public-url>` prints the caller-scoped
|
|
117
|
+
configuration snapshot as JSON. It runs no diagnostic program and prints no
|
|
118
|
+
raw HTTP failure body. [Operator UI](./operator-ui.md) describes the returned
|
|
119
|
+
configuration, live facts, and permission rules.
|
|
120
|
+
|
|
121
|
+
Health success alone does not prove a client can authenticate or reach an
|
|
122
|
+
integration. Check client connection and a bounded read through a chosen
|
|
123
|
+
connector; resolve credentials using [authorization recovery](./meta-tools.md#authorization-recovery).
|
|
124
|
+
|
|
125
|
+
## Upgrade an existing deployment
|
|
126
|
+
|
|
127
|
+
Bump the exact package pin, read the [changelog](../CHANGELOG.md) for every
|
|
128
|
+
intervening release, and apply the relevant migration before startup. `init`
|
|
129
|
+
does not merge changes into an existing deployment.
|
|
130
|
+
|
|
131
|
+
- [Managed tokens from v0.23](./auth.md#managed-client-tokens-and-upgrading-from-v023) keep their secrets when storage, namespaces, and grants are retained. Replace the old boolean config with `accessTokens(storage)`; machine tokens gain no operator authority.
|
|
132
|
+
- [Node storage from 0.28](../templates/node/README.md#storage) uses `connecta migrate-state` to copy the JSON state into SQLite. Activity JSONL is not imported.
|
|
133
|
+
- [Worker storage from 0.28](../examples/worker/README.md#upgrading-from-028) copies KV into D1 with a one-shot migration.
|
|
134
|
+
- [Notion, Vercel, and Cloudflare for 0.29](./provider-migration-0.29.md) change defaults and retained API tool names.
|
|
135
|
+
- [Executor wiring](./code-mode.md#deploy-time-capability) requires branded shipped executors or a self-managed custom executor.
|
|
136
|
+
|
|
137
|
+
## Implementation and evidence
|
|
138
|
+
|
|
139
|
+
Check the [CLI](https://github.com/zackbart/connecta/blob/main/bin/connecta.mjs),
|
|
140
|
+
[Node configuration](../templates/node/src/connecta.config.ts), and
|
|
141
|
+
[Worker configuration](../examples/worker/src/connecta.config.ts) when changing
|
|
142
|
+
setup. [Deployment-shape tests](https://github.com/zackbart/connecta/blob/main/test/deployment-shapes.node.test.ts),
|
|
143
|
+
[config tests](https://github.com/zackbart/connecta/blob/main/test/config.test.ts),
|
|
144
|
+
and [package smoke](https://github.com/zackbart/connecta/blob/main/scripts/check-package.mjs)
|
|
145
|
+
check the deployment and published package boundaries. Repository verification
|
|
146
|
+
requirements remain in AGENTS.md.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Integrating services
|
|
2
|
+
|
|
3
|
+
Use this guide to add a connector to deployment configuration. To implement a
|
|
4
|
+
new maintained provider in Connecta itself, follow the repository's
|
|
5
|
+
[add-provider skill](https://github.com/zackbart/connecta/blob/main/.claude/skills/add-provider/SKILL.md)
|
|
6
|
+
and [AGENTS.md](https://github.com/zackbart/connecta/blob/main/AGENTS.md).
|
|
7
|
+
[All agent routes](./README.md) are in the index.
|
|
8
|
+
|
|
9
|
+
## Choose a connector
|
|
10
|
+
|
|
11
|
+
| Need | Configuration path | Check before using |
|
|
12
|
+
| -------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
13
|
+
| A maintained integration | Its `@zackbart/connecta/providers/<name>` factory | Provider `SKILL.md`, options, auth ownership, supported tools |
|
|
14
|
+
| Another hosted remote MCP server | `remoteMcp()` from the root package | Endpoint, authentication, current catalog, and read/write annotations |
|
|
15
|
+
| A selected HTTP API capability | `api()` from the root package | Input/output schemas, explicit read/write hints, bounded handler, credential handling |
|
|
16
|
+
|
|
17
|
+
All produce connectors in the deployment's `connectors` array and use the same
|
|
18
|
+
registry enforcement, caller grants, result bounds, and activity paths. There
|
|
19
|
+
is no runtime installation or registration. Keep connector IDs unique and
|
|
20
|
+
stable because they prefix canonical `<connectorId>.<toolName>` addresses.
|
|
21
|
+
|
|
22
|
+
## Maintained integrations
|
|
23
|
+
|
|
24
|
+
Import a provider individually. For example, Linear requires an explicit
|
|
25
|
+
choice of its downstream endpoint:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { linear } from "@zackbart/connecta/providers/linear";
|
|
29
|
+
|
|
30
|
+
const tracker = linear("tracker", {
|
|
31
|
+
purpose: "Engineering issues in our workspace",
|
|
32
|
+
access: "read-only",
|
|
33
|
+
});
|
|
34
|
+
// Add tracker to the deployment's connectors array.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Linear defaults to OAuth; its read-only endpoint requests only the downstream
|
|
38
|
+
read scope. That is separate from Connecta pool trust. Read the
|
|
39
|
+
[Linear skill](https://github.com/zackbart/connecta/blob/main/src/providers/linear/SKILL.md)
|
|
40
|
+
and [factory options](https://github.com/zackbart/connecta/blob/main/src/providers/linear/index.ts)
|
|
41
|
+
for API-key framing, workspace instructions, and admission choices.
|
|
42
|
+
|
|
43
|
+
The [human README](../README.md#what-you-can-do-with-it) lists available
|
|
44
|
+
integrations. Each [provider folder](https://github.com/zackbart/connecta/tree/main/src/providers)
|
|
45
|
+
contains its scoped guide, options, tests, fixtures, and review data. Prefer
|
|
46
|
+
that guide to general assumptions about a service's API. Google Workspace
|
|
47
|
+
providers require administrator-managed domain-wide delegation and a deployment
|
|
48
|
+
mapping from admitted identity to the downstream account; a request must not
|
|
49
|
+
choose its own account. Gmail never sends mail, and Drive never permanently
|
|
50
|
+
deletes files.
|
|
51
|
+
|
|
52
|
+
Notion, Vercel, and Cloudflare default to hosted MCP. Their explicit
|
|
53
|
+
`surface: "api"` choices retain selected REST capabilities, with Notion's
|
|
54
|
+
internal-integration identity preserved. To use both, configure distinct IDs
|
|
55
|
+
and independent credentials. Read the [0.29 migration guide](./provider-migration-0.29.md)
|
|
56
|
+
and its provider reconciliation tables for retained and removed names.
|
|
57
|
+
|
|
58
|
+
## Another remote MCP server
|
|
59
|
+
|
|
60
|
+
Declare its endpoint and intended auth mode. This fragment uses a server-stored
|
|
61
|
+
credential slot; replace the illustrative URL with the real endpoint:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { remoteMcp } from "@zackbart/connecta";
|
|
65
|
+
|
|
66
|
+
const service = remoteMcp("service", {
|
|
67
|
+
url: "https://mcp.example.com/mcp",
|
|
68
|
+
description: "Our team's hosted service",
|
|
69
|
+
auth: { type: "credential" },
|
|
70
|
+
});
|
|
71
|
+
// Add service to connectors. Configure the vault and permitted operators
|
|
72
|
+
// before supplying its credential through the operator UI.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Credential auth defaults to an `Authorization: Bearer` header. Choose framing
|
|
76
|
+
from the downstream contract, not from its HTTP API's convention. Remote auth
|
|
77
|
+
also supports OAuth, static headers, and request-local token resolution. Use
|
|
78
|
+
[shared and personal auth](./auth.md#shared-and-personal-auth),
|
|
79
|
+
[client registration](./auth.md#downstream-oauth-client-registration), and
|
|
80
|
+
[request-local tokens](./auth.md#request-local-downstream-bearer-tokens)
|
|
81
|
+
for those cases. Secrets belong in deployment secret storage or the vault,
|
|
82
|
+
not source literals.
|
|
83
|
+
|
|
84
|
+
Remote catalogs evolve. Discovery, direct calls, and program calls all enforce
|
|
85
|
+
classification against the current catalog. Missing or contradictory read hints
|
|
86
|
+
fail closed as writes. `classify` can supply a reviewed per-tool verdict;
|
|
87
|
+
schema changes can invalidate a reviewed read. Exact deployment classification
|
|
88
|
+
overrides take precedence. See
|
|
89
|
+
[reviewed classification](./architecture.md#providers-and-reviewed-classification)
|
|
90
|
+
for precedence, schema digests, and `unlisted: "hide"`. Review the behavior
|
|
91
|
+
before declaring a read, and do not use classification to bypass provider target
|
|
92
|
+
scopes or caller grants.
|
|
93
|
+
|
|
94
|
+
## A hand-written HTTP capability
|
|
95
|
+
|
|
96
|
+
Define only the operations the deployment needs. Every `api()` tool needs a
|
|
97
|
+
nonempty description and explicit `readOnlyHint`. This local time tool matches
|
|
98
|
+
the shape used by the deployment templates:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { api } from "@zackbart/connecta";
|
|
102
|
+
|
|
103
|
+
const time = api("time", {
|
|
104
|
+
description: "Time, current timestamp",
|
|
105
|
+
tools: [{
|
|
106
|
+
name: "get_now",
|
|
107
|
+
description: "Return the current time as an ISO 8601 timestamp.",
|
|
108
|
+
inputSchema: { type: "object", properties: {} },
|
|
109
|
+
outputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: { now: { type: "string" } },
|
|
112
|
+
required: ["now"],
|
|
113
|
+
},
|
|
114
|
+
annotations: { readOnlyHint: true },
|
|
115
|
+
handler: async () => ({ now: new Date().toISOString() }),
|
|
116
|
+
}],
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
For an HTTP handler, use its supplied `ctx.fetch` to register outgoing secrets
|
|
121
|
+
for redaction, pass the request signal, and bound any pagination or fanout.
|
|
122
|
+
Read declared credential slots through `ctx.credential`; do not return their
|
|
123
|
+
values. With a declared static OAuth configuration, use `ctx.oauth.fetch`,
|
|
124
|
+
which binds the grant to configured API origins and manages authentication.
|
|
125
|
+
`api()` refuses simultaneous `oauth` and `credential` declarations.
|
|
126
|
+
[Downstream OAuth on api()](./auth.md#downstream-oauth-on-api) owns a concrete
|
|
127
|
+
HTTP example and the client setup contract.
|
|
128
|
+
|
|
129
|
+
Mark every side-effecting tool `readOnlyHint: false`. On read-only endpoints
|
|
130
|
+
writes use `call_destructive_tool`; trusted programs may write within their
|
|
131
|
+
grants. Host approval is independent of tool definition. Use declared output
|
|
132
|
+
schemas when known; runtime observations cannot replace them.
|
|
133
|
+
|
|
134
|
+
## Separate ownership from visibility
|
|
135
|
+
|
|
136
|
+
`authScope: "shared"` uses deployment-owned downstream auth; `"personal"` uses
|
|
137
|
+
the admitted principal's partition. Visibility is a different decision,
|
|
138
|
+
controlled by identity and pool grants. A personal connector must not close
|
|
139
|
+
over a shared secret or derive its owner from arguments. Remote MCP refuses
|
|
140
|
+
literal header credentials with personal scope. Review
|
|
141
|
+
[principals and grants](./auth.md#principals-visibility-and-operators) and
|
|
142
|
+
[management permissions](./auth.md#management-permissions) before exposing a
|
|
143
|
+
new integration or its authentication controls.
|
|
144
|
+
|
|
145
|
+
After editing configuration, run the deployment's typecheck and doctor, then
|
|
146
|
+
perform a bounded read. Add negative checks for target restrictions and writes
|
|
147
|
+
when authoring a handler or provider. Follow repository verification policy
|
|
148
|
+
when changing package code.
|
|
149
|
+
|
|
150
|
+
## Implementation and evidence
|
|
151
|
+
|
|
152
|
+
Factory contracts live in [remote MCP](https://github.com/zackbart/connecta/blob/main/src/connectors/remote-mcp.ts),
|
|
153
|
+
[API connector](https://github.com/zackbart/connecta/blob/main/src/connectors/api-connector.ts),
|
|
154
|
+
and [provider construction](https://github.com/zackbart/connecta/blob/main/src/provider.ts).
|
|
155
|
+
[Remote tests](https://github.com/zackbart/connecta/blob/main/test/remote-mcp.test.ts),
|
|
156
|
+
[API tests](https://github.com/zackbart/connecta/blob/main/test/api-connector.test.ts),
|
|
157
|
+
[provider contracts](https://github.com/zackbart/connecta/blob/main/test/provider-contracts.test.ts),
|
|
158
|
+
and [classification tests](https://github.com/zackbart/connecta/blob/main/test/classified-decorators.test.ts)
|
|
159
|
+
check shared behavior. Provider-specific tests remain beside each provider.
|
|
@@ -9,6 +9,21 @@ This guide is the contract an MCP client sees. The in-program `connecta.*` API
|
|
|
9
9
|
those tools imply belongs to [code mode](./code-mode.md); inbound identity and
|
|
10
10
|
credential administration belong to [auth](./auth.md).
|
|
11
11
|
|
|
12
|
+
## Task routes
|
|
13
|
+
|
|
14
|
+
Start with [Operating an endpoint](./operating.md) for a workflow, or
|
|
15
|
+
[the agent index](./README.md) for setup and repository tasks. This reference
|
|
16
|
+
owns the top-level MCP contract; it does not require reading the core internals.
|
|
17
|
+
|
|
18
|
+
| Task | Sections |
|
|
19
|
+
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| Choose a tool or construct its arguments | [Six-tool table](#the-six-tools), [call routing](#routing-between-the-call-surfaces), [downstream input](#downstream-input-on-direct-calls) |
|
|
21
|
+
| Find an operation and its schema | [Discovery context](#discovery-context), [TypeScript signatures](#typescript-signatures), [lexical discovery](#lexical-discovery) |
|
|
22
|
+
| Load provider instructions or native skills | [Connector guides](#connector-guides-and-skills), [native extension](#native-skills-extension) |
|
|
23
|
+
| Interpret a direct response or page a large result | [Representation](#result-representation), [truncation](#truncated-direct-call-results), [paging](#paging-with-connectaresult) |
|
|
24
|
+
| Recover a refused or invalid call | [Authorization](#authorization-recovery), [routing](#routing-recovery), [arguments](#argument-recovery), [echo budgets](#echo-budgets) |
|
|
25
|
+
| Change the contract | [Source and tests](#source-and-tests), [guest API](./code-mode.md#verification) |
|
|
26
|
+
|
|
12
27
|
## The six tools
|
|
13
28
|
|
|
14
29
|
Every deployment requires an executor, so `tools/list` is exactly six. No
|
|
@@ -19,9 +34,11 @@ never depends on the storage behind the deployment. There was an eighth,
|
|
|
19
34
|
is annotated `readOnlyHint: true, destructiveHint: false` on a `read-only`
|
|
20
35
|
endpoint, and `readOnlyHint: false, destructiveHint: true` on a `trusted`
|
|
21
36
|
endpoint. Approval belongs to the host. Pool trust never changes connector
|
|
22
|
-
visibility or tool grants.
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
visibility or tool grants. The four tools with structured-only success results
|
|
38
|
+
advertise `outputSchema`. The two direct-call tools omit it because their default
|
|
39
|
+
mode returns native `content`. Default-mode successes declare
|
|
40
|
+
`_meta["dev.connecta/format"]: "json" | "text"`; value mode places the value in
|
|
41
|
+
`data` and declares `format` in its complete result envelope.
|
|
25
42
|
|
|
26
43
|
| Tool | Arguments | Returns |
|
|
27
44
|
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
@@ -72,9 +89,9 @@ a small sample for inspection before continuing in another call, which avoids
|
|
|
72
89
|
repeated guesses at text formats and collection roots without restoring a
|
|
73
90
|
mandatory discovery-only round trip. A one-time write can return the only copy
|
|
74
91
|
of its result. Inspect and reduce the full value before a program returns, or
|
|
75
|
-
|
|
76
|
-
the write runs once.
|
|
77
|
-
|
|
92
|
+
page the completed direct-call result or oversized program return with
|
|
93
|
+
`connecta.result` after the write runs once. Sampling or slicing a write's output
|
|
94
|
+
inside the program can still discard the answer.
|
|
78
95
|
|
|
79
96
|
A `trusted` pool lets the same program discover and dispatch writes.
|
|
80
97
|
A `read-only` pool refuses writes before validation or dispatch with
|
|
@@ -302,16 +319,25 @@ and [2026-07-28 Resources](https://modelcontextprotocol.io/specification/2026-07
|
|
|
302
319
|
|
|
303
320
|
## Result representation
|
|
304
321
|
|
|
305
|
-
For
|
|
322
|
+
For structured envelopes, `structuredContent` is the canonical full-fidelity value and
|
|
306
323
|
`content` carries the same complete value as compact JSON for clients that only
|
|
307
324
|
consume text. Keeping both follows MCP's backwards-compatibility guidance;
|
|
308
325
|
dropping the text copy waits on host-forwarding measurements showing supported
|
|
309
326
|
clients do not need it. Direct-call failures set `isError: true` and carry the
|
|
310
327
|
same `{ ok: false, error, durationMs, attempts }` envelope in both forms, whether
|
|
311
328
|
`resultMode` is omitted, `"mcp"`, or `"value"`. Plain-text guidance stays
|
|
312
|
-
text-only.
|
|
313
|
-
in
|
|
314
|
-
|
|
329
|
+
text-only. Successful direct calls with `resultMode` omitted or `"mcp"` return
|
|
330
|
+
their data only in `content`, with the downstream format in
|
|
331
|
+
`_meta["dev.connecta/format"]`. They omit `structuredContent`, including on
|
|
332
|
+
truncated results, so clients that prefer it cannot replace data or a preview
|
|
333
|
+
with metadata. API results keep their serialized value; downstream MCP native
|
|
334
|
+
content blocks pass through. Value-mode successes retain
|
|
335
|
+
`{ ok: true, data, format, durationMs, attempts }` in both forms, including when
|
|
336
|
+
`data` is a truncation notice. Direct-call tools advertise no `outputSchema`:
|
|
337
|
+
MCP requires a tool declaring one to supply conforming structured results, which
|
|
338
|
+
default native content does not promise.
|
|
339
|
+
When downstream MCP content has no text block and downstream `structuredContent`
|
|
340
|
+
is present, Connecta appends a
|
|
315
341
|
text block carrying its compact JSON and then applies the same content size
|
|
316
342
|
guard, which preserves structured-only results including `null`, arrays, and
|
|
317
343
|
scalars. An existing text mirror stays unchanged; Connecta adds no second copy.
|
|
@@ -346,7 +372,14 @@ when it has one and `connecta:auth:<provider kind>` otherwise. Keep subject ids
|
|
|
346
372
|
distinct within that namespace. An explicit principal is the fallback subject
|
|
347
373
|
when neither id is supplied, and open deployments and auth providers that supply
|
|
348
374
|
no identity share one partition. Each new stash also binds the principal,
|
|
349
|
-
endpoint/pool, request origin, connector, tool, and classification.
|
|
375
|
+
endpoint/pool, request origin, connector, tool, and classification. Program-return
|
|
376
|
+
stashes bind every contributing call, resource or skill read, and catalog lookup,
|
|
377
|
+
including dependencies inherited from pages read during the run. Downstream skill and
|
|
378
|
+
resource content requires a whole-connector grant on every page. Local guides and catalog
|
|
379
|
+
metadata require the contributing connector or tool to remain visible, including
|
|
380
|
+
caught catalog-load errors, unknown-tool failures, and recovery messages that
|
|
381
|
+
list visible connectors or local guides. Returns with no downstream calls still
|
|
382
|
+
require matching identity, origin and endpoint, current auth, and pool membership. A page must
|
|
350
383
|
match those bindings and pass current auth, connector/tool grants, pool membership,
|
|
351
384
|
and trust checks. Old entries lacking bindings fail closed. A random UUID is a
|
|
352
385
|
handle, never an access grant.
|
|
@@ -361,6 +394,14 @@ including pre-upgrade single-key envelopes and raw-text entries. Offsets and `to
|
|
|
361
394
|
original UTF-8 text, not the envelope. A supplied offset inside a character
|
|
362
395
|
moves back to its start; page ends also align to character boundaries, and a
|
|
363
396
|
page smaller than one character widens just enough to make progress.
|
|
397
|
+
Decoding preserves every payload code point, including U+FEFF at the start of
|
|
398
|
+
the result or any page, so joining pages reproduces the original UTF-8 text.
|
|
399
|
+
Oversized raw text containing unpaired UTF-16 surrogates cannot use this UTF-8
|
|
400
|
+
page/offset contract. It returns a successful truncation notice explaining that
|
|
401
|
+
the text cannot be paged and suggesting a JSON value request for recovery,
|
|
402
|
+
without a stash id, paging action, or altered preview.
|
|
403
|
+
Completed writes remain successful and must not be repeated. JSON values still
|
|
404
|
+
round-trip such strings through their escaped JSON representation.
|
|
364
405
|
|
|
365
406
|
A per-call `timeoutMs` covers catalog resolution, admission, and connector
|
|
366
407
|
execution under one deadline. The admission queue's own timeout may expire
|
|
@@ -373,7 +414,8 @@ retryable timeout while Connecta prepares its response.
|
|
|
373
414
|
A `call_tool` or `call_destructive_tool` result over its cap — the connector's
|
|
374
415
|
`maxResultBytes`, else `calls.maxResultBytes`, else 24,000 bytes — comes back
|
|
375
416
|
as one text block. Its first line is the truncation notice, one line of compact
|
|
376
|
-
JSON; everything after the first newline is the preview
|
|
417
|
+
JSON; everything after the first newline is the preview. `structuredContent`
|
|
418
|
+
is absent, so the notice cannot hide the preview in clients that prefer it:
|
|
377
419
|
|
|
378
420
|
```text
|
|
379
421
|
{"truncated":true,"resultId":"…","totalBytes":161420,"hint":"This write already ran: do not call it again to see its result. Bytes 0-24000 of 161420 follow; page the rest with connecta.result using nextAction.","nextOffset":24000,"nextAction":{"tool":"execute_code","arguments":{"code":"async () => await connecta.result(\"…\", { offset: 24000 })"}}}
|
|
@@ -437,13 +479,19 @@ When paging is available, `nextAction` is the page handle for either kind of cal
|
|
|
437
479
|
the one next step connecta can spell out exactly, while a reduction is a
|
|
438
480
|
program the agent has to write. `resultId` stays beside the exact `nextAction`,
|
|
439
481
|
so the handle is actionable without copying an identifier out of prose.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
482
|
+
Oversized program returns carry the same route, with a bounded preview after
|
|
483
|
+
its notice fields. They stash the compact JSON return and page from offset 0;
|
|
484
|
+
see [program returns](./code-mode.md#results-and-projection). A run that completed
|
|
485
|
+
any write gets the same no-repeat warning. Oversized discovery responses carry
|
|
486
|
+
no paging route.
|
|
443
487
|
|
|
444
488
|
### Paging with connecta.result
|
|
445
489
|
|
|
446
|
-
Paging is a guest operation, never another top-level tool.
|
|
490
|
+
Paging is a guest operation, never another top-level tool. Program calls also
|
|
491
|
+
return handles for oversized values; see [program result limits](./code-mode.md#cancellation-and-limits).
|
|
492
|
+
`connecta.result(handle, { page: 0 })` accepts those handles alongside direct
|
|
493
|
+
result IDs. Numbered pages are zero-based and mutually exclusive with offset.
|
|
494
|
+
Keep maxBytes fixed while incrementing pages. A direct call's
|
|
447
495
|
notice carries a `resultId`, `nextOffset`, and an executable `execute_code`
|
|
448
496
|
recovery action. Read and reduce the result in one program:
|
|
449
497
|
|
|
@@ -461,7 +509,7 @@ async () => {
|
|
|
461
509
|
|
|
462
510
|
A page is `{ resultId, offset, bytes, totalBytes, hasMore, nextOffset?,
|
|
463
511
|
format: "text", text }`. The page size is clamped to the original call's inline
|
|
464
|
-
cap, with UTF-8 alignment and forward progress. Reassemble inside the sandbox;
|
|
512
|
+
cap and a 32 KiB program bridge ceiling, with UTF-8 alignment and forward progress. Reassemble inside the sandbox;
|
|
465
513
|
returning pages unchanged can hit the program result cap. The admitted subject
|
|
466
514
|
and principal, endpoint/pool, request origin, connector, and tool bindings must
|
|
467
515
|
match. The host rechecks current access and trust before returning each page; a
|
|
@@ -472,7 +520,7 @@ usable bounded preview. It says paging is unavailable and the write already
|
|
|
472
520
|
ran; it carries no result handle or recovery action and stores no unreachable
|
|
473
521
|
stash. Write paging remains available on trusted endpoints.
|
|
474
522
|
|
|
475
|
-
A refused stash write cannot undo a downstream success.
|
|
523
|
+
A refused stash write cannot undo a downstream success. Direct calls and program returns produce
|
|
476
524
|
a paging-unavailable notice, without a handle or recovery action. A write's
|
|
477
525
|
notice still says it already ran. Activity records success; operator logs receive
|
|
478
526
|
a fixed warning without storage error prose. Never repeat a write to recover output.
|
|
@@ -709,6 +757,8 @@ An OAuth call failure uses this envelope:
|
|
|
709
757
|
"connector": "service",
|
|
710
758
|
"operation": "service.read",
|
|
711
759
|
"recovery": "oauth",
|
|
760
|
+
"authorizationUrl": "https://connecta.example/connect/service?h=...",
|
|
761
|
+
"instructions": "Copy authorizationUrl exactly as returned and sign in as the initiating user.",
|
|
712
762
|
"nextAction": {
|
|
713
763
|
"tool": "authorize_connector",
|
|
714
764
|
"arguments": { "connector": "service" },
|
|
@@ -718,9 +768,13 @@ An OAuth call failure uses this envelope:
|
|
|
718
768
|
}
|
|
719
769
|
```
|
|
720
770
|
|
|
721
|
-
`recovery` is `oauth`, `operator_config`, or `unavailable`.
|
|
722
|
-
|
|
723
|
-
|
|
771
|
+
`recovery` is `oauth`, `operator_config`, or `unavailable`. On hosts without URL
|
|
772
|
+
elicitation, auth errors carry the same handoff fields that `authorize_connector`
|
|
773
|
+
returns for the admitted user, connector, and pool. Direct calls include them in
|
|
774
|
+
`error`; program `connecta.call` failures include them in `error.data` and
|
|
775
|
+
`error.details`. Search `catalogErrors` and scoped `queryAnalysis.catalogError`,
|
|
776
|
+
and describe `errorDetails`, carry them too. When an auth error includes a
|
|
777
|
+
handoff, give it to the user or operator. The class-specific handoff is:
|
|
724
778
|
|
|
725
779
|
- `oauth`: an `authorizationUrl` to connecta's `/connect/<connector>?h=...` route
|
|
726
780
|
and instructions to sign in as the initiating user;
|
|
@@ -733,6 +787,13 @@ The class follows what the connector declares, not how it was authored: a
|
|
|
733
787
|
and no OAuth flow, so it uses `operator_config` when both vault and UI are
|
|
734
788
|
configured. Without either it returns `unavailable`, never a dead UI link.
|
|
735
789
|
|
|
790
|
+
Errors mint at most one handoff per connector per direct call or program run,
|
|
791
|
+
including concurrent and repeated call or catalog errors. The request reuses the
|
|
792
|
+
same signed URL; a later request can issue a fresh one. Issuing a link is
|
|
793
|
+
stateless and starts no consent. Errors never request `force`; use an explicit
|
|
794
|
+
`authorize_connector` call for a restart or a fresh expired link. URL-elicitation
|
|
795
|
+
hosts keep the existing prompt and retry flow.
|
|
796
|
+
|
|
736
797
|
The tool accepts no secret and starts no downstream consent. `force` applies
|
|
737
798
|
only to OAuth and requests a restart when the verified browser opens the signed
|
|
738
799
|
link. That visit may discard the stored grant before restarting consent. Static credential values are written
|
|
@@ -740,9 +801,12 @@ only through the same-origin interactive-user credential route, and only for a
|
|
|
740
801
|
connector visible to that user with the relevant shared or personal management
|
|
741
802
|
permission; Issuing an OAuth link, `force` included, requires that permission too. The
|
|
742
803
|
`/connect` visit and callback both verify the same initiating user under Clerk or
|
|
743
|
-
Cloudflare Access, including for shared connectors.
|
|
744
|
-
|
|
745
|
-
|
|
804
|
+
Cloudflare Access, including for shared connectors.
|
|
805
|
+
|
|
806
|
+
Copy `authorizationUrl` exactly as returned. Do not decode or re-encode its opaque,
|
|
807
|
+
unpadded base64url handoff token. If the link is invalid or expired, request a fresh link with
|
|
808
|
+
`authorize_connector`. The URL expires after fifteen minutes and carries no browser authentication.
|
|
809
|
+
A signing credential vault is required; deployments without either interactive provider return `unavailable`
|
|
746
810
|
with a clear configuration message. Status reads stay passive and never expose
|
|
747
811
|
a downstream authorization-server URL. These routes work without the UI. After OAuth
|
|
748
812
|
consent or a human update, retry the original operation; a static update is read
|
|
@@ -786,13 +850,36 @@ what happens over budget differs by field.
|
|
|
786
850
|
| unknown `connecta.result(id)`, `authorize_connector.connector`, `skills.name` | clamped the same way |
|
|
787
851
|
| `search_tools.connector` | rejected with `invalid_args` before catalog lookup |
|
|
788
852
|
|
|
789
|
-
Arguments
|
|
790
|
-
|
|
853
|
+
Arguments are schema-filtered before budgeting. `writeOnly: true` values are
|
|
854
|
+
omitted at any depth, including array items and prefixes, local `$ref`/`$defs`,
|
|
855
|
+
and `allOf`. `oneOf`/`anyOf` alternatives must agree on sensitivity. Unresolved
|
|
856
|
+
or remote references, sensitive `patternProperties`/`additionalProperties`
|
|
857
|
+
schemas, unsupported sensitivity rules, and traversal limits omit the entire
|
|
858
|
+
echo. A private array element omits its containing array to keep indices intact.
|
|
859
|
+
`argsRedacted: true` marks partial or withheld echoes. Use them only to identify
|
|
860
|
+
the target for reconciliation; any new call requires the original arguments.
|
|
861
|
+
The filtered snapshot is never clipped: over-budget arguments are dropped whole.
|
|
862
|
+
The address gets the opposite rule
|
|
791
863
|
because it is the thing being corrected: a clipped one still identifies the
|
|
792
864
|
mistake, and a short one — every real one — comes back exact and untagged. A
|
|
793
865
|
scope is rejected outright because a clipped one could select a different
|
|
794
866
|
connector. A failed result-storage read returns typed `unavailable` without
|
|
795
867
|
exposing backend error text.
|
|
868
|
+
Submitted `writeOnly` values also enter the call's sent-secret output redaction
|
|
869
|
+
set, including property names inside private object subtrees. Downstream
|
|
870
|
+
result and error text quoting them, in raw or credential-supported encoded
|
|
871
|
+
forms, is redacted before direct-call output, program returns, logs, and emits,
|
|
872
|
+
or result stash persistence; paging in a later request reads already-redacted
|
|
873
|
+
bytes. A program's `connecta.read` resources, connector usage guides, and
|
|
874
|
+
downstream `connecta.skill` files take the same rules before the program sees
|
|
875
|
+
them. JSON stays valid: a matching number or boolean becomes `"[redacted]"`.
|
|
876
|
+
Empty private strings are exempt. A private value shorter than eight characters
|
|
877
|
+
redacts only structured fields that equal it, so identifiers and approval state
|
|
878
|
+
remain, and withholds prose that contains it, including error messages, while
|
|
879
|
+
typed classification, retry facts, and write outcome remain.
|
|
880
|
+
Work limits withhold the affected text without failing the call. See
|
|
881
|
+
[output redaction](./auth.md#what-a-servers-errors-may-say)
|
|
882
|
+
for the shared encoding, budget, and argument-filter contract.
|
|
796
883
|
|
|
797
884
|
Activity records each of these refusals with the coarse `friction` class derived
|
|
798
885
|
from its typed error code — `tool_not_found`, `schema_retry`,
|
|
@@ -837,10 +924,12 @@ scoped registry, including an empty list when none are accessible. They never
|
|
|
837
924
|
list connectors from another pool or identity's grants.
|
|
838
925
|
|
|
839
926
|
A write that times out after dispatch returns `write_outcome_unknown` with
|
|
840
|
-
`retryable: false` and an agent-only `uncertainCall: { address, args }`.
|
|
841
|
-
is automatically replayed.
|
|
842
|
-
|
|
843
|
-
|
|
927
|
+
`retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted? }`.
|
|
928
|
+
No write is automatically replayed. `args` may be partial reconciliation context
|
|
929
|
+
with `argsRedacted: true`. A withheld echo or arguments over the 512-byte budget
|
|
930
|
+
carry `argsOmitted: true`; sensitive or unresolved schemas also carry
|
|
931
|
+
`argsRedacted: true`. When `args` is absent or redacted, use the original
|
|
932
|
+
arguments if reconciliation requires another call. A timeout before dispatch remains `timeout`. Trusted
|
|
844
933
|
program write accounting keeps this detail even if the guest catches the
|
|
845
934
|
failure or returns early. Multiple uncertain writes use `uncertainCalls`,
|
|
846
935
|
bounded to ten calls with `uncertainCallsTruncated` when more exist.
|
|
@@ -850,3 +939,19 @@ SDK timeout classes, and runtime network codes. Untyped prose is non-retryable:
|
|
|
850
939
|
"timeout", "temporarily unavailable", "rate limit", or "503" in a body, tool
|
|
851
940
|
name, or URL never changes the verdict. JSON-RPC `ProtocolError.data` is dropped
|
|
852
941
|
at the downstream boundary, even when the agent may see the error's message.
|
|
942
|
+
|
|
943
|
+
## Source and tests
|
|
944
|
+
|
|
945
|
+
Handlers and schemas live in [meta-tools.ts](https://github.com/zackbart/connecta/blob/main/src/meta-tools.ts).
|
|
946
|
+
[Catalog service](https://github.com/zackbart/connecta/blob/main/src/catalog-service.ts)
|
|
947
|
+
and [invocation](https://github.com/zackbart/connecta/blob/main/src/invocation.ts)
|
|
948
|
+
serve both direct tools and programs. [Skills](https://github.com/zackbart/connecta/blob/main/src/skills.ts)
|
|
949
|
+
owns the supplied usage instructions and connector guides.
|
|
950
|
+
|
|
951
|
+
[Schema tests](https://github.com/zackbart/connecta/blob/main/test/meta-tool-schemas.test.ts),
|
|
952
|
+
[search tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-search.test.ts),
|
|
953
|
+
[call tests](https://github.com/zackbart/connecta/blob/main/test/meta-tools-call.test.ts),
|
|
954
|
+
[skills tests](https://github.com/zackbart/connecta/blob/main/test/skills-extension.test.ts),
|
|
955
|
+
and [surface tests](https://github.com/zackbart/connecta/blob/main/test/code-first-surface.test.ts)
|
|
956
|
+
check the published contract. Guest parity and clause evidence are in
|
|
957
|
+
[code-mode verification](./code-mode.md#verification).
|