@zackbart/connecta 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +28 -15
- package/CHANGELOG.md +493 -0
- package/README.md +19 -4
- package/bin/connecta.mjs +44 -3
- package/dist/access-tokens.d.ts +0 -1
- package/dist/access-tokens.js +0 -1
- package/dist/activity.d.ts +50 -2
- package/dist/activity.js +34 -1
- package/dist/apps-shell.d.ts +0 -1
- package/dist/apps-shell.js +0 -1
- package/dist/auth/bearer.d.ts +0 -1
- package/dist/auth/bearer.js +0 -1
- package/dist/auth/clerk.d.ts +0 -1
- package/dist/auth/clerk.js +45 -9
- package/dist/auth/downstream-oauth.d.ts +0 -1
- package/dist/auth/downstream-oauth.js +0 -1
- package/dist/call-admission.d.ts +0 -1
- package/dist/call-admission.js +0 -1
- package/dist/catalog-drift.d.ts +99 -0
- package/dist/catalog-drift.js +251 -0
- package/dist/catalog-fingerprint.d.ts +0 -1
- package/dist/catalog-fingerprint.js +0 -1
- package/dist/catalog-limits.d.ts +0 -1
- package/dist/catalog-limits.js +0 -1
- package/dist/catalog-service.d.ts +0 -1
- package/dist/catalog-service.js +0 -1
- package/dist/catalog.d.ts +0 -1
- package/dist/catalog.js +0 -1
- package/dist/concurrency.d.ts +0 -1
- package/dist/concurrency.js +0 -1
- package/dist/connector-scope.d.ts +0 -1
- package/dist/connector-scope.js +0 -1
- package/dist/connectors/api.d.ts +29 -21
- package/dist/connectors/api.js +45 -19
- package/dist/connectors/guarded-fetch.d.ts +106 -0
- package/dist/connectors/guarded-fetch.js +289 -0
- package/dist/connectors/remote-mcp.d.ts +0 -1
- package/dist/connectors/remote-mcp.js +0 -1
- package/dist/credentials.d.ts +0 -1
- package/dist/credentials.js +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.js +0 -1
- package/dist/execute.d.ts +0 -1
- package/dist/execute.js +0 -1
- package/dist/executor-admission.d.ts +11 -1
- package/dist/executor-admission.js +28 -1
- package/dist/executor-result.d.ts +0 -1
- package/dist/executor-result.js +0 -1
- package/dist/executors/quickjs-child.d.ts +0 -1
- package/dist/executors/quickjs-child.js +0 -1
- package/dist/executors/quickjs-protocol.d.ts +0 -1
- package/dist/executors/quickjs-protocol.js +0 -1
- package/dist/executors/quickjs-runtime.d.ts +0 -1
- package/dist/executors/quickjs-runtime.js +0 -1
- package/dist/executors/quickjs.d.ts +0 -1
- package/dist/executors/quickjs.js +2 -1
- package/dist/favicon.d.ts +0 -1
- package/dist/favicon.js +0 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +24 -7
- package/dist/invocation.d.ts +0 -1
- package/dist/invocation.js +0 -1
- package/dist/json-schema.d.ts +0 -1
- package/dist/json-schema.js +0 -1
- package/dist/mcp-result.d.ts +0 -1
- package/dist/mcp-result.js +0 -1
- package/dist/meta-tools.d.ts +0 -1
- package/dist/meta-tools.js +0 -1
- package/dist/node.d.ts +0 -1
- package/dist/node.js +0 -1
- package/dist/operator-ui/generated.d.ts +2 -3
- package/dist/operator-ui/generated.js +3 -4
- package/dist/operator-ui/model.d.ts +10 -1
- package/dist/operator-ui/model.js +0 -1
- package/dist/operator-ui/view.d.ts +167 -0
- package/dist/operator-ui/view.js +270 -0
- package/dist/providers/cloudflare.d.ts +5 -2
- package/dist/providers/cloudflare.js +435 -328
- package/dist/providers/linear.d.ts +26 -5
- package/dist/providers/linear.js +32 -60
- package/dist/providers/mixpanel.d.ts +35 -4
- package/dist/providers/mixpanel.js +53 -78
- package/dist/providers/notion.d.ts +0 -1
- package/dist/providers/notion.js +87 -42
- package/dist/providers/stripe.d.ts +13 -1
- package/dist/providers/stripe.js +31 -59
- package/dist/registry.d.ts +34 -2
- package/dist/registry.js +85 -5
- package/dist/retired-toolkits.d.ts +0 -1
- package/dist/retired-toolkits.js +0 -1
- package/dist/routes/access-tokens.d.ts +0 -1
- package/dist/routes/access-tokens.js +0 -1
- package/dist/routes/activity.d.ts +0 -1
- package/dist/routes/activity.js +0 -1
- package/dist/routes/credentials.d.ts +0 -1
- package/dist/routes/credentials.js +0 -1
- package/dist/routes/mcp.d.ts +0 -1
- package/dist/routes/mcp.js +0 -1
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +0 -1
- package/dist/routes/shared.d.ts +2 -1
- package/dist/routes/shared.js +0 -1
- package/dist/routes/ui.d.ts +0 -1
- package/dist/routes/ui.js +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +11 -1
- package/dist/skills.d.ts +0 -1
- package/dist/skills.js +0 -1
- package/dist/storage/file.d.ts +0 -1
- package/dist/storage/file.js +0 -1
- package/dist/storage/memory.d.ts +0 -1
- package/dist/storage/memory.js +0 -1
- package/dist/timeout.d.ts +0 -1
- package/dist/timeout.js +0 -1
- package/dist/tool-safety.d.ts +0 -1
- package/dist/tool-safety.js +0 -1
- package/dist/types.d.ts +45 -1
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +0 -1
- package/dist/ui.js +13 -137
- package/dist/validate.d.ts +13 -15
- package/dist/validate.js +24 -11
- package/dist/version.d.ts +1 -2
- package/dist/version.js +1 -2
- package/documentation/architecture.md +186 -5
- package/documentation/auth.md +11 -0
- package/documentation/call-admission.md +142 -6
- package/documentation/cloudflare.md +95 -27
- package/documentation/code-mode.md +12 -12
- package/documentation/connector-guides.md +172 -5
- package/documentation/connectors.md +98 -7
- package/documentation/linear.md +26 -9
- package/documentation/mcp-ui-design.md +2 -2
- package/documentation/mixpanel.md +48 -16
- package/documentation/notion.md +16 -0
- package/documentation/operations.md +283 -5
- package/documentation/operator-ui.md +120 -5
- package/documentation/provider-audit.md +160 -0
- package/documentation/provider-conventions.md +631 -0
- package/documentation/request-admission.md +167 -5
- package/documentation/rich-output-design.md +1 -2
- package/documentation/stripe.md +27 -12
- package/ethos.md +14 -9
- package/examples/worker/README.md +96 -12
- package/examples/worker/src/index.ts +46 -1
- package/examples/worker/wrangler.jsonc +14 -1
- package/package.json +4 -5
- package/templates/node/.dockerignore +10 -0
- package/templates/node/.env.example +40 -3
- package/templates/node/AGENTS.md +11 -1
- package/templates/node/Dockerfile +49 -0
- package/templates/node/README.md +117 -2
- package/templates/node/docker-compose.yml +39 -0
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +182 -0
- package/templates/node/src/index.ts +80 -4
- package/assets/connecta-clay-hero.png +0 -0
- package/dist/access-tokens.d.ts.map +0 -1
- package/dist/access-tokens.js.map +0 -1
- package/dist/activity.d.ts.map +0 -1
- package/dist/activity.js.map +0 -1
- package/dist/apps-shell.d.ts.map +0 -1
- package/dist/apps-shell.js.map +0 -1
- package/dist/auth/bearer.d.ts.map +0 -1
- package/dist/auth/bearer.js.map +0 -1
- package/dist/auth/clerk.d.ts.map +0 -1
- package/dist/auth/clerk.js.map +0 -1
- package/dist/auth/downstream-oauth.d.ts.map +0 -1
- package/dist/auth/downstream-oauth.js.map +0 -1
- package/dist/call-admission.d.ts.map +0 -1
- package/dist/call-admission.js.map +0 -1
- package/dist/catalog-fingerprint.d.ts.map +0 -1
- package/dist/catalog-fingerprint.js.map +0 -1
- package/dist/catalog-limits.d.ts.map +0 -1
- package/dist/catalog-limits.js.map +0 -1
- package/dist/catalog-service.d.ts.map +0 -1
- package/dist/catalog-service.js.map +0 -1
- package/dist/catalog.d.ts.map +0 -1
- package/dist/catalog.js.map +0 -1
- package/dist/concurrency.d.ts.map +0 -1
- package/dist/concurrency.js.map +0 -1
- package/dist/connector-scope.d.ts.map +0 -1
- package/dist/connector-scope.js.map +0 -1
- package/dist/connectors/api.d.ts.map +0 -1
- package/dist/connectors/api.js.map +0 -1
- package/dist/connectors/remote-mcp.d.ts.map +0 -1
- package/dist/connectors/remote-mcp.js.map +0 -1
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/execute.d.ts.map +0 -1
- package/dist/execute.js.map +0 -1
- package/dist/executor-admission.d.ts.map +0 -1
- package/dist/executor-admission.js.map +0 -1
- package/dist/executor-result.d.ts.map +0 -1
- package/dist/executor-result.js.map +0 -1
- package/dist/executors/quickjs-child.d.ts.map +0 -1
- package/dist/executors/quickjs-child.js.map +0 -1
- package/dist/executors/quickjs-protocol.d.ts.map +0 -1
- package/dist/executors/quickjs-protocol.js.map +0 -1
- package/dist/executors/quickjs-runtime.d.ts.map +0 -1
- package/dist/executors/quickjs-runtime.js.map +0 -1
- package/dist/executors/quickjs.d.ts.map +0 -1
- package/dist/executors/quickjs.js.map +0 -1
- package/dist/favicon.d.ts.map +0 -1
- package/dist/favicon.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation.d.ts.map +0 -1
- package/dist/invocation.js.map +0 -1
- package/dist/json-schema.d.ts.map +0 -1
- package/dist/json-schema.js.map +0 -1
- package/dist/mcp-result.d.ts.map +0 -1
- package/dist/mcp-result.js.map +0 -1
- package/dist/meta-tools.d.ts.map +0 -1
- package/dist/meta-tools.js.map +0 -1
- package/dist/node.d.ts.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/operator-ui/generated.d.ts.map +0 -1
- package/dist/operator-ui/generated.js.map +0 -1
- package/dist/operator-ui/model.d.ts.map +0 -1
- package/dist/operator-ui/model.js.map +0 -1
- package/dist/providers/cloudflare.d.ts.map +0 -1
- package/dist/providers/cloudflare.js.map +0 -1
- package/dist/providers/linear.d.ts.map +0 -1
- package/dist/providers/linear.js.map +0 -1
- package/dist/providers/mixpanel.d.ts.map +0 -1
- package/dist/providers/mixpanel.js.map +0 -1
- package/dist/providers/notion.d.ts.map +0 -1
- package/dist/providers/notion.js.map +0 -1
- package/dist/providers/stripe.d.ts.map +0 -1
- package/dist/providers/stripe.js.map +0 -1
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js.map +0 -1
- package/dist/retired-toolkits.d.ts.map +0 -1
- package/dist/retired-toolkits.js.map +0 -1
- package/dist/routes/access-tokens.d.ts.map +0 -1
- package/dist/routes/access-tokens.js.map +0 -1
- package/dist/routes/activity.d.ts.map +0 -1
- package/dist/routes/activity.js.map +0 -1
- package/dist/routes/credentials.d.ts.map +0 -1
- package/dist/routes/credentials.js.map +0 -1
- package/dist/routes/mcp.d.ts.map +0 -1
- package/dist/routes/mcp.js.map +0 -1
- package/dist/routes/oauth.d.ts.map +0 -1
- package/dist/routes/oauth.js.map +0 -1
- package/dist/routes/shared.d.ts.map +0 -1
- package/dist/routes/shared.js.map +0 -1
- package/dist/routes/ui.d.ts.map +0 -1
- package/dist/routes/ui.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/skills.d.ts.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/storage/file.d.ts.map +0 -1
- package/dist/storage/file.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/timeout.d.ts.map +0 -1
- package/dist/timeout.js.map +0 -1
- package/dist/tool-safety.d.ts.map +0 -1
- package/dist/tool-safety.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/ui.d.ts.map +0 -1
- package/dist/ui.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/examples/node/README.md +0 -52
- package/examples/node/src/index.ts +0 -71
- package/src/access-tokens.ts +0 -289
- package/src/activity.ts +0 -234
- package/src/apps-shell.ts +0 -370
- package/src/auth/bearer.ts +0 -61
- package/src/auth/clerk.ts +0 -553
- package/src/auth/downstream-oauth.ts +0 -575
- package/src/call-admission.ts +0 -521
- package/src/catalog-fingerprint.ts +0 -52
- package/src/catalog-limits.ts +0 -11
- package/src/catalog-service.ts +0 -1153
- package/src/catalog.ts +0 -790
- package/src/concurrency.ts +0 -39
- package/src/connector-scope.ts +0 -66
- package/src/connectors/api.ts +0 -159
- package/src/connectors/remote-mcp.ts +0 -1090
- package/src/credentials.ts +0 -428
- package/src/errors.ts +0 -364
- package/src/execute.ts +0 -1406
- package/src/executor-admission.ts +0 -328
- package/src/executor-result.ts +0 -107
- package/src/executors/quickjs-child.ts +0 -172
- package/src/executors/quickjs-protocol.ts +0 -99
- package/src/executors/quickjs-runtime.ts +0 -470
- package/src/executors/quickjs.ts +0 -776
- package/src/favicon.ts +0 -53
- package/src/index.ts +0 -666
- package/src/invocation.ts +0 -655
- package/src/json-schema.ts +0 -11
- package/src/mcp-result.ts +0 -43
- package/src/meta-tools.ts +0 -1607
- package/src/node.ts +0 -206
- package/src/operator-ui/browser.css +0 -423
- package/src/operator-ui/browser.ts +0 -1281
- package/src/operator-ui/generated.ts +0 -4
- package/src/operator-ui/model.ts +0 -111
- package/src/providers/cloudflare.ts +0 -3803
- package/src/providers/linear.ts +0 -301
- package/src/providers/mixpanel.ts +0 -228
- package/src/providers/notion.ts +0 -1879
- package/src/providers/stripe.ts +0 -306
- package/src/registry.ts +0 -1085
- package/src/retired-toolkits.ts +0 -14
- package/src/routes/access-tokens.ts +0 -115
- package/src/routes/activity.ts +0 -215
- package/src/routes/credentials.ts +0 -254
- package/src/routes/mcp.ts +0 -429
- package/src/routes/oauth.ts +0 -322
- package/src/routes/shared.ts +0 -247
- package/src/routes/ui.ts +0 -152
- package/src/server.ts +0 -195
- package/src/skills.ts +0 -271
- package/src/storage/file.ts +0 -123
- package/src/storage/memory.ts +0 -44
- package/src/timeout.ts +0 -84
- package/src/tool-safety.ts +0 -15
- package/src/types.ts +0 -523
- package/src/ui.ts +0 -744
- package/src/validate.ts +0 -387
- package/src/version.ts +0 -7
|
@@ -47,13 +47,15 @@ choice does not grant the connection different runtime privileges. Two
|
|
|
47
47
|
instances of the same provider are isolated in exactly the same way as two
|
|
48
48
|
hand-written connectors with different ids.
|
|
49
49
|
|
|
50
|
-
A prebuilt connection's vetted annotations
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
A prebuilt connection's vetted annotations fill in downstream silence and
|
|
51
|
+
otherwise preserve explicit annotations. This includes an explicit
|
|
52
|
+
`destructiveHint: true` or `readOnlyHint: false` on a vetted read, and an
|
|
53
|
+
explicit `readOnlyHint: true` on a name no release has classified. One narrow
|
|
54
|
+
exception stays fail-closed: a release-reviewed destructive classification
|
|
55
|
+
overrides a contradictory `readOnlyHint: true`, because Connecta has
|
|
56
|
+
independently established that the tool mutates existing state. Silence on an
|
|
57
|
+
unclassified name still means not read-only. The authoring path never weakens
|
|
58
|
+
the fail-closed read-only invariant.
|
|
57
59
|
|
|
58
60
|
Prebuilt means preferred when available, not mandatory. A deployment may mix
|
|
59
61
|
prebuilt connections, custom `remoteMcp()` connections, and custom `api()`
|
|
@@ -109,6 +111,11 @@ export const connecta = createConnecta({
|
|
|
109
111
|
All three are ordinary `Connector` instances by the time the registry sees
|
|
110
112
|
them. Nothing in the list is privileged by how it was authored.
|
|
111
113
|
|
|
114
|
+
What a maintained connection owes an agent is written down rather than
|
|
115
|
+
inherited from whoever wrote it last:
|
|
116
|
+
[provider conventions](./provider-conventions.md), one set for hand-written
|
|
117
|
+
HTTP surfaces and one for hosted-MCP proxies.
|
|
118
|
+
|
|
112
119
|
Maintained provider guides:
|
|
113
120
|
|
|
114
121
|
- [Cloudflare](./cloudflare.md)
|
|
@@ -117,6 +124,90 @@ Maintained provider guides:
|
|
|
117
124
|
- [Notion](./notion.md)
|
|
118
125
|
- [Stripe](./stripe.md)
|
|
119
126
|
|
|
127
|
+
## The `api()` construction contract
|
|
128
|
+
|
|
129
|
+
`api()` is the path every custom integration takes, and whatever it accepts is
|
|
130
|
+
what an agent eventually reads. Three things are refused at construction rather
|
|
131
|
+
than discovered in production
|
|
132
|
+
([#340](https://github.com/zackbart/connecta/issues/340)):
|
|
133
|
+
|
|
134
|
+
- **A tool with no description.** Discovery has nothing else to route on, and a
|
|
135
|
+
guess costs a call.
|
|
136
|
+
- **A tool with no explicit boolean `annotations.readOnlyHint`.** `true`
|
|
137
|
+
declares a read and admits the tool to `call_tool` and `execute_code`;
|
|
138
|
+
`false` declares work that crosses `call_destructive_tool`, where the host
|
|
139
|
+
can ask a human. Connecta never infers the classification from a tool name,
|
|
140
|
+
description, schema, HTTP method, or the other annotations — an unclassified
|
|
141
|
+
tool is a bug in the deployment, not a puzzle to solve.
|
|
142
|
+
- **An `inputSchema` the validator cannot compile.** Declaring one is optional;
|
|
143
|
+
declaring one that cannot be enforced is not. A schema that only reveals
|
|
144
|
+
itself on first use — an unresolvable `$ref`, say — fails that call as
|
|
145
|
+
non-retryable `invalid_args` rather than forwarding raw arguments to the
|
|
146
|
+
handler. `validateArgs: false` still opts out of enforcement for deployments
|
|
147
|
+
that want loose coercion; it does not opt out of the schema being real.
|
|
148
|
+
|
|
149
|
+
None of this reaches a proxied catalog: hosted-MCP tools arrive as their
|
|
150
|
+
downstream wrote them, and an unannotated or contradictory one stays
|
|
151
|
+
fail-closed onto `call_destructive_tool`. The contract binds the surfaces we
|
|
152
|
+
write, not the catalogs we relay.
|
|
153
|
+
|
|
154
|
+
## The guarded fetch transport
|
|
155
|
+
|
|
156
|
+
Every hand-written HTTP surface re-derives the same safety machinery, and two
|
|
157
|
+
of them had already derived it slightly differently. `src/connectors/guarded-fetch.ts`
|
|
158
|
+
is that machinery extracted once ([#341](https://github.com/zackbart/connecta/issues/341)):
|
|
159
|
+
one `guardedFetch({ provider, baseUrl, headers, maxResponseBytes, authenticate })`
|
|
160
|
+
factory returning the transport a connector sends every request through.
|
|
161
|
+
|
|
162
|
+
What it owns is mechanical and provider-independent:
|
|
163
|
+
|
|
164
|
+
- **Confinement.** A request path is provider-relative, carries no query or
|
|
165
|
+
fragment, and is re-checked against the base origin and path prefix *after*
|
|
166
|
+
`new URL` normalization — because normalization is how a path escapes a
|
|
167
|
+
prefix, not something to trust before it.
|
|
168
|
+
- **Construction.** Query parameters are encoded rather than concatenated, a
|
|
169
|
+
JSON body is serialized with the `Content-Type` to match, and a pre-framed
|
|
170
|
+
body gets none, so `fetch` still picks the multipart boundary.
|
|
171
|
+
- **Credential forwarding.** `authenticate` is called once per request and its
|
|
172
|
+
headers are applied last; a request header wearing one of their names is
|
|
173
|
+
refused rather than allowed to shadow it. A 3xx is refused outright — a
|
|
174
|
+
redirect is an instruction to re-send the credential to whatever origin the
|
|
175
|
+
`Location` names, and a confinement a redirect can undo was never one.
|
|
176
|
+
- **Bounded reads.** `maxResponseBytes` is required, not defaulted: what counts
|
|
177
|
+
as an absurd response is a fact about the API, not about HTTP. A declared
|
|
178
|
+
`Content-Length` past the ceiling fails before a byte is read, and a
|
|
179
|
+
streaming body is abandoned at the ceiling rather than buffered past it.
|
|
180
|
+
- **Normalization.** An unreachable provider becomes a retryable `unavailable`
|
|
181
|
+
instead of whatever `TypeError` the runtime threw, and `ctx.signal` rides
|
|
182
|
+
every request.
|
|
183
|
+
|
|
184
|
+
What it deliberately does not own is meaning. It never reads a status code and
|
|
185
|
+
never invents an authentication scheme: the provider's `authenticate` callback
|
|
186
|
+
supplies the headers, and the provider's mapper turns one `GuardedResponse`
|
|
187
|
+
into a result or a typed failure. That split is not fastidiousness. Notion's
|
|
188
|
+
403 means a capability the integration was never granted — re-authorizing
|
|
189
|
+
cannot fix it — while Cloudflare's means a token scope, and the two want
|
|
190
|
+
opposite next moves. A helper that guessed would be wrong for one of them.
|
|
191
|
+
|
|
192
|
+
Cloudflare and Notion both run on it. Their existing suites carried over
|
|
193
|
+
unchanged, which proves the migration kept the behavior those suites cover —
|
|
194
|
+
not that nothing changed. Three things did, and the changelog names them: a
|
|
195
|
+
3xx is refused where both providers used to follow it, both now fail past
|
|
196
|
+
their byte ceiling, and `cloudflare()`'s `baseUrl` is validated at
|
|
197
|
+
construction. Each suite gained one test for the ceiling, because the one
|
|
198
|
+
guard the helper was written to add is the one a provider's own mapper can
|
|
199
|
+
most easily disarm: a bare `catch` around `response.json()` swallows the
|
|
200
|
+
transport's refusal along with a parse error, and turns a response nobody was
|
|
201
|
+
allowed to read into an empty success. A mapper re-throws
|
|
202
|
+
`ConnectorCallError` and swallows only what it recognizes.
|
|
203
|
+
|
|
204
|
+
It is **not exported this release**: the two migrations proved the shape
|
|
205
|
+
preserves behavior for connectors that already had this machinery, not that it
|
|
206
|
+
is the right shape for an author starting from nothing, and an unexported
|
|
207
|
+
symbol costs nothing to reshape while a published one is a promise.
|
|
208
|
+
The provider audit ([#342](https://github.com/zackbart/connecta/issues/342))
|
|
209
|
+
supplies the third caller that would settle it.
|
|
210
|
+
|
|
120
211
|
## MCP version skew
|
|
121
212
|
|
|
122
213
|
Connecta deliberately sits between protocol generations
|
package/documentation/linear.md
CHANGED
|
@@ -11,6 +11,7 @@ import { linear } from "@zackbart/connecta/providers/linear";
|
|
|
11
11
|
const tracker = linear("product_tracker", {
|
|
12
12
|
title: "Product issue tracking",
|
|
13
13
|
purpose: "Issue and project planning for the platform team",
|
|
14
|
+
access: "read-write",
|
|
14
15
|
instructions: "File bugs into the Platform team unless the request names another.",
|
|
15
16
|
});
|
|
16
17
|
```
|
|
@@ -23,13 +24,21 @@ cannot change the connector's safety classification.
|
|
|
23
24
|
|
|
24
25
|
## Access modes
|
|
25
26
|
|
|
26
|
-
Linear publishes two hosted endpoints, and `access` selects between them
|
|
27
|
+
Linear publishes two hosted endpoints, and `access` selects between them. It is
|
|
28
|
+
**required**, with no default:
|
|
27
29
|
|
|
28
30
|
| `access` | Endpoint | OAuth scopes |
|
|
29
31
|
| --- | --- | --- |
|
|
30
|
-
| `"read-write"`
|
|
32
|
+
| `"read-write"` | `https://mcp.linear.app/mcp` | `read`, `write` |
|
|
31
33
|
| `"read-only"` | `https://mcp.linear.app/mcp/readonly` | `read` |
|
|
32
34
|
|
|
35
|
+
There is no safe default to pick. Defaulting to `"read-write"` hands a
|
|
36
|
+
deployment write access it never asked for; defaulting to `"read-only"` turns a
|
|
37
|
+
deployment that does write into one whose every write fails at Linear — at
|
|
38
|
+
runtime, where no agent can repair it. So the operator declares which one this
|
|
39
|
+
is, and a deployment that forgot fails at construction with a message naming
|
|
40
|
+
both options.
|
|
41
|
+
|
|
33
42
|
Read-only is not a client-side filter. The endpoint advertises the `read` scope
|
|
34
43
|
alone, so the token minted for it cannot reach Linear's write APIs — a stronger
|
|
35
44
|
guarantee than any annotation Connecta applies. A deployment that only reports
|
|
@@ -63,6 +72,7 @@ also accepts a bearer token or a personal API key passed directly in the
|
|
|
63
72
|
```ts
|
|
64
73
|
linear("automation_tracker", {
|
|
65
74
|
purpose: "Headless release reporting",
|
|
75
|
+
access: "read-only",
|
|
66
76
|
auth: {
|
|
67
77
|
type: "headers",
|
|
68
78
|
headers: { Authorization: env.LINEAR_API_KEY },
|
|
@@ -83,18 +93,17 @@ The wrapper classifies Linear's documented `list_*`, `get_*`, and
|
|
|
83
93
|
downstream leaves unannotated fails closed onto `call_destructive_tool` until a
|
|
84
94
|
Connecta release reviews it.
|
|
85
95
|
|
|
86
|
-
That classification
|
|
87
|
-
|
|
88
|
-
annotation in neither direction. A tool on the read allowlist arriving with
|
|
96
|
+
That classification fills in downstream silence and otherwise preserves
|
|
97
|
+
explicit annotations. A tool on the read allowlist arriving with
|
|
89
98
|
`destructiveHint: true` or `readOnlyHint: false` keeps exactly what the
|
|
90
99
|
downstream said and stays behind `call_destructive_tool`. A tool on neither
|
|
91
100
|
maintained list arriving with `readOnlyHint: true` keeps that too, and stays
|
|
92
101
|
callable from `execute_code`. Both are the downstream telling you this
|
|
93
102
|
release's allowlist is stale, and on a name no release has reviewed its word is
|
|
94
|
-
the only evidence there is.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
the only evidence there is. One narrow fail-closed exception applies to a name
|
|
104
|
+
this release reviewed and filed destructive: a `save_*` tool claiming
|
|
105
|
+
`readOnlyHint: true` is a downstream bug rather than news, and stays on the
|
|
106
|
+
approval path.
|
|
98
107
|
|
|
99
108
|
One detail of Linear's own design shapes the classification: **`save_*` tools
|
|
100
109
|
are upserts.** Omitting a record id creates; supplying one updates in place.
|
|
@@ -131,6 +140,7 @@ one. An operator who knows their workspace can supply one explicitly:
|
|
|
131
140
|
```ts
|
|
132
141
|
linear("product_tracker", {
|
|
133
142
|
purpose: "Issue and project planning for the platform team",
|
|
143
|
+
access: "read-write",
|
|
134
144
|
callAdmission: {
|
|
135
145
|
rules: [
|
|
136
146
|
{ budget: { kind: "rolling-window", maxCalls: 1_000, windowMs: 3_600_000 } },
|
|
@@ -142,3 +152,10 @@ linear("product_tracker", {
|
|
|
142
152
|
A budget-only rule needs no queue. If you add `maxConcurrency` you are asking
|
|
143
153
|
for a queue, and the admission controller then requires the rest of the queue
|
|
144
154
|
settings at construction.
|
|
155
|
+
|
|
156
|
+
## Conventions
|
|
157
|
+
|
|
158
|
+
This connection is audited against
|
|
159
|
+
[the provider conventions](./provider-conventions.md). Its verdict per
|
|
160
|
+
convention, including every recorded exception, is the Linear section of
|
|
161
|
+
[the provider audit](./provider-audit.md).
|
|
@@ -375,8 +375,8 @@ buy a guarantee the protocol already gives away.
|
|
|
375
375
|
source carries the `srcdoc` and sandbox attributes and contains no
|
|
376
376
|
message-forwarding path from the inner frame to the host.
|
|
377
377
|
- `U1`–`U11` fold into `code-mode.md`, the `execute_code` description documents
|
|
378
|
-
`connecta.ui`, and the
|
|
379
|
-
[
|
|
378
|
+
`connecta.ui`, and the suite takes its row in the
|
|
379
|
+
[test map](./operations.md#the-test-map).
|
|
380
380
|
- `npm run check` passes.
|
|
381
381
|
|
|
382
382
|
The implementation issue (#277) carries these as acceptance criteria.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Import `mixpanel()` independently from
|
|
4
4
|
`@zackbart/connecta/providers/mixpanel`. It wraps Mixpanel's hosted MCP server
|
|
5
|
-
with regional endpoint selection, OAuth by default, a
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
with regional endpoint selection, OAuth by default, a task-oriented usage
|
|
6
|
+
guide, and a vetted safety classification. It adds no provider dependency and
|
|
7
|
+
is not reachable from Connecta's root entry.
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import { mixpanel } from "@zackbart/connecta/providers/mixpanel";
|
|
@@ -25,6 +25,12 @@ connector's safety classification.
|
|
|
25
25
|
|
|
26
26
|
`region` accepts `"us"` (the default), `"eu"`, or `"in"` and selects the
|
|
27
27
|
corresponding [official hosted endpoint](https://docs.mixpanel.com/docs/mcp#mcp-server-urls).
|
|
28
|
+
A project lives in exactly one residency, so the region also decides what this
|
|
29
|
+
connection can see at all: a question pointed at the wrong one comes back empty
|
|
30
|
+
rather than wrong, which reads as the project having no data. That makes it a
|
|
31
|
+
routing fact, so it rides the default `title` (`Mixpanel (us)`, `Mixpanel
|
|
32
|
+
(eu)`, `Mixpanel (in)`) and opens the usage guide — `search_tools` renders a
|
|
33
|
+
connector's title and guide summary and never its description.
|
|
28
34
|
OAuth is the recommended default and keeps each connector instance's flow and
|
|
29
35
|
tokens in its connector-scoped storage. Mixpanel service accounts are also
|
|
30
36
|
supported with an explicit header override:
|
|
@@ -49,18 +55,17 @@ documented create, update, edit, merge, dismiss, duplicate, and delete tools as
|
|
|
49
55
|
writes. An unfamiliar tool the downstream leaves unannotated fails closed onto
|
|
50
56
|
`call_destructive_tool` until a Connecta release reviews it.
|
|
51
57
|
|
|
52
|
-
That classification
|
|
53
|
-
|
|
54
|
-
annotation in neither direction. A tool on the read allowlist arriving with
|
|
58
|
+
That classification fills in downstream silence and otherwise preserves
|
|
59
|
+
explicit annotations. A tool on the read allowlist arriving with
|
|
55
60
|
`destructiveHint: true` or `readOnlyHint: false` keeps exactly what the
|
|
56
61
|
downstream said and stays behind `call_destructive_tool`. A tool on neither
|
|
57
62
|
maintained list arriving with `readOnlyHint: true` keeps that too, and stays
|
|
58
63
|
callable from `execute_code`. Both are the downstream telling you this
|
|
59
64
|
release's allowlist is stale, and on a name no release has reviewed its word is
|
|
60
|
-
the only evidence there is.
|
|
61
|
-
|
|
62
|
-
`
|
|
63
|
-
|
|
65
|
+
the only evidence there is. One narrow fail-closed exception applies to a name
|
|
66
|
+
this release reviewed and filed destructive: a `Delete-Dashboard` claiming
|
|
67
|
+
`readOnlyHint: true` is a downstream bug rather than news, and stays on the
|
|
68
|
+
approval path. Maintained writes that only create
|
|
64
69
|
something new (`Create-Dashboard`, `Create-Cohort`, `Create-Metric`, and the
|
|
65
70
|
rest) leave `destructiveHint` unset; `readOnlyHint: false` already routes them
|
|
66
71
|
through the destructive path, and asserting destruction only inflates the
|
|
@@ -69,9 +74,36 @@ approval copy the host shows a human.
|
|
|
69
74
|
Experiments and Feature Flags — 15 of the 63 classified tools — are Mixpanel
|
|
70
75
|
beta surfaces. Expect their names and schemas to move faster than the rest.
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
## Rate limits
|
|
78
|
+
|
|
79
|
+
Mixpanel meters its MCP server **per user per hour**, shared with everything
|
|
80
|
+
else that credential does. Connecta's counter is per runtime, not per user, and
|
|
81
|
+
the two cannot be reconciled in either direction: one runtime serving several
|
|
82
|
+
users under-counts, and several Worker isolates or Node processes sharing one
|
|
83
|
+
credential each admit a full budget. A hardcoded ceiling would therefore either
|
|
84
|
+
throttle a healthy deployment or fail to protect a busy one, so this connection
|
|
85
|
+
declares **no call-admission budget by default**. An operator who knows the
|
|
86
|
+
account can supply one explicitly:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
mixpanel("product_analytics", {
|
|
90
|
+
purpose: "Product and growth decisions for the production app",
|
|
91
|
+
callAdmission: {
|
|
92
|
+
rules: [
|
|
93
|
+
{ budget: { kind: "rolling-window", maxCalls: 300, windowMs: 3_600_000 } },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A budget-only rule needs no queue. If you add `maxConcurrency` you are asking
|
|
100
|
+
for a queue, and the admission controller then requires the rest of the queue
|
|
101
|
+
settings at construction. Discovery traffic is outside connector call admission
|
|
102
|
+
either way and still needs restrained use.
|
|
103
|
+
|
|
104
|
+
## Conventions
|
|
105
|
+
|
|
106
|
+
This connection is audited against
|
|
107
|
+
[the provider conventions](./provider-conventions.md). Its verdict per
|
|
108
|
+
convention, including every recorded exception, is the Mixpanel section of
|
|
109
|
+
[the provider audit](./provider-audit.md).
|
package/documentation/notion.md
CHANGED
|
@@ -231,3 +231,19 @@ block updates or deletes, no page moves. Those are all real Notion endpoints
|
|
|
231
231
|
and all deliberately absent: this is a deliberate tool surface, not a mirror of
|
|
232
232
|
the API. Anything missing is reachable through a custom `api()` connector
|
|
233
233
|
beside this one, which remains a first-class path.
|
|
234
|
+
|
|
235
|
+
There is also **no guarded raw-REST escape hatch** — no `notion_api_get`, no
|
|
236
|
+
`notion_api_mutate`. The convention that permits one
|
|
237
|
+
([H14](./provider-conventions.md#h14--a-named-tool-must-beat-the-escape-hatch-and-the-escape-hatch-splits-by-safety))
|
|
238
|
+
also permits a small provider to have none, provided it says so, and Notion's
|
|
239
|
+
public API is finite and slow-moving enough that a named surface can cover it.
|
|
240
|
+
The usage guide says it too, because an agent that assumes a hatch exists
|
|
241
|
+
spends a search proving it does not: absent from the tool list means absent
|
|
242
|
+
from this connection, not hidden behind a generic call.
|
|
243
|
+
|
|
244
|
+
## Conventions
|
|
245
|
+
|
|
246
|
+
This connection is audited against
|
|
247
|
+
[the provider conventions](./provider-conventions.md). Its verdict per
|
|
248
|
+
convention, including every recorded exception, is the Notion section of
|
|
249
|
+
[the provider audit](./provider-audit.md).
|
|
@@ -1,7 +1,285 @@
|
|
|
1
1
|
# Operations
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Configuring, running, verifying, and upgrading a deployment — and the map of
|
|
4
|
+
which suite proves what, which is the part an agent changing this repository
|
|
5
|
+
needs most.
|
|
6
|
+
|
|
7
|
+
## Running it
|
|
8
|
+
|
|
9
|
+
`createConnecta(config)` returns `{ fetch, registry, close }`. `fetch` takes
|
|
10
|
+
the Workers `(request, env, ctx)` signature; passing `ctx` through is what lets
|
|
11
|
+
connecta hand deferred work — best-effort activity writes — to `ctx.waitUntil`
|
|
12
|
+
instead of losing it when the response returns.
|
|
13
|
+
|
|
14
|
+
An `executor` is required. A deployment without one throws at construction
|
|
15
|
+
rather than serving a smaller surface
|
|
16
|
+
([#273](https://github.com/zackbart/connecta/issues/273)): Node uses
|
|
17
|
+
`quickJsExecutor()` from `@zackbart/connecta/quickjs`, Workers use
|
|
18
|
+
`new DynamicWorkerExecutor({ loader: env.LOADER })` from
|
|
19
|
+
`@cloudflare/codemode`.
|
|
20
|
+
|
|
21
|
+
There are exactly two deployment shapes.
|
|
22
|
+
[`templates/node/`](../templates/node/) is what `connecta init` copies — the
|
|
23
|
+
one standalone Node project, Docker-ready rather than Docker-only — and
|
|
24
|
+
[`examples/worker/`](../examples/worker/) is the Cloudflare shape. Both ship
|
|
25
|
+
the whole operator feature set; each README walks through its own enablement.
|
|
26
|
+
A third scaffold that is a diff away from either is the shape
|
|
27
|
+
[#344](https://github.com/zackbart/connecta/issues/344) deleted, so do not add
|
|
28
|
+
one.
|
|
29
|
+
|
|
30
|
+
### The CLI
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npx @zackbart/connecta init my-deployment
|
|
34
|
+
cd my-deployment && npm install && npm start
|
|
35
|
+
CONNECTA_TOKEN=… npx connecta doctor --url http://localhost:8787
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`init` copies the template, pins the generated deployment to the CLI package's
|
|
39
|
+
exact version, restores the template `.gitignore` (npm renames it in a
|
|
40
|
+
tarball), and refuses to merge into an existing path.
|
|
41
|
+
|
|
42
|
+
`doctor` verifies a *running* deployment: `/health` reports ok, `tools/list` is
|
|
43
|
+
exactly the seven prescribed names, and `execute_code` actually runs a trivial
|
|
44
|
+
program. The executor it names is the one the deployment reports on `/health`,
|
|
45
|
+
from that executor's own `name` or its constructor name, sanitized and bounded
|
|
46
|
+
on the way out: `QuickJS` on the Node template, `DynamicWorkerExecutor` on the
|
|
47
|
+
Worker example, and `code executed` when an executor identifies as nothing —
|
|
48
|
+
a checker that asserts a sandbox it never saw is worse than one that says it
|
|
49
|
+
does not know ([#368](https://github.com/zackbart/connecta/issues/368)). It
|
|
50
|
+
refuses to send a bearer token over remote plaintext HTTP, and it
|
|
51
|
+
*reports* catalog drift without failing on it — an unclassified downstream tool
|
|
52
|
+
already fails closed onto `call_destructive_tool`, so drift is a maintainer's
|
|
53
|
+
next task rather than a broken deployment
|
|
54
|
+
([#343](https://github.com/zackbart/connecta/issues/343)).
|
|
55
|
+
|
|
56
|
+
### Configuration
|
|
57
|
+
|
|
58
|
+
Structural seams stay top-level; tuning is grouped by subsystem. Every group is
|
|
59
|
+
optional.
|
|
60
|
+
|
|
61
|
+
| Option | Default | What it does |
|
|
62
|
+
| --- | --- | --- |
|
|
63
|
+
| `connectors` | — (required) | the connector set ([connectors](./connectors.md)) |
|
|
64
|
+
| `executor` | — (required) | the sandbox `execute_code` runs in ([code mode](./code-mode.md#what-an-executor-must-implement)) |
|
|
65
|
+
| `auth?` | none ⇒ open (dev only) | one `InboundAuth` or an array; bearer providers are checked before Clerk ([inbound auth](./auth.md)) |
|
|
66
|
+
| `storage?` | `memoryStorage()` | the one state seam ([storage](./storage-and-credentials.md)) |
|
|
67
|
+
| `publicUrl?` | per-request origin | public base URL; an HTTPS value also redirects inbound HTTP |
|
|
68
|
+
| `logger?` | `console`, prefixed `[connecta]` | `{ debug, info, warn, error }` |
|
|
69
|
+
| `branding?` | neutral Connecta defaults | operator-page and OAuth result-page labels and marks |
|
|
70
|
+
| `serverInfo?` | `connecta` / package version | `{ name, version, title?, websiteUrl?, icons? }` per the MCP icons spec |
|
|
71
|
+
| `deploymentInfo?` | unset | arbitrary metadata exposed by `/health` |
|
|
72
|
+
| `activity?` | unset | `{ store, readGate?, deploymentId? }` — payload-free activity storage, an optional operator-read gate, and a stable event label |
|
|
73
|
+
| `credentials.encryptionKey?` | unset | base64 32-byte AES key for the connector vault. Without it, connectors declaring `credential` warn and their slots stay unmanageable |
|
|
74
|
+
| `accessTokens?` | unset | `{ maxActive? }` (default 100) for operator-issued MCP bearer tokens. Requires a Clerk provider, or construction throws ([access tokens](./auth.md#operator-issued-access-tokens)) |
|
|
75
|
+
| `discovery.concurrency?` | 4 | connector catalogs/status probes in flight at once |
|
|
76
|
+
| `discovery.catalogTtlSeconds?` | 300 | fresh TTL for cached tool lists |
|
|
77
|
+
| `discovery.persistCatalog?` | true | persist serializable catalogs as a manifest plus revision-addressed chunks |
|
|
78
|
+
| `discovery.staleCatalogSeconds?` | 3600 | how long an expired catalog stays usable as a failure fallback |
|
|
79
|
+
| `discovery.probeTimeoutMs?` | 30_000 | per-connector deadline for catalog fan-out; a timed-out connector degrades alone. Not a tool-call deadline |
|
|
80
|
+
| `calls.defaultTimeoutMs?` | **unset (opt-in)** | deadline for calls that pass no `timeoutMs`. Bounds one attempt, so retries can still extend total duration |
|
|
81
|
+
| `calls.maxResultBytes?` | 50_000 | inline result cap before truncation and `get_result` paging; a connector may override it. Invalid values warn and fall back |
|
|
82
|
+
| `execute.maxEmittedBytes?` | 4_000_000 | aggregate `connecta.emit` bytes per run — a transport bound, not a context bound |
|
|
83
|
+
| `execute.maxEmittedBlocks?` | 32 | content blocks `connecta.emit` accepts per run |
|
|
84
|
+
| `admission.requests?` | 16 active / 32 queued / 5 s / 1 s | global FIFO `/mcp` capacity, taken before auth ([request admission](./request-admission.md)) |
|
|
85
|
+
| `admission.code?` | 2 active / 8 queued / 5 s / 1 s | fallback pool for an executor that owns no `acquire()`; ignored with a warning when it does |
|
|
86
|
+
|
|
87
|
+
Options removed in earlier releases throw with their migration named rather
|
|
88
|
+
than falling back to a default: `toolkits`
|
|
89
|
+
([#178](https://github.com/zackbart/connecta/issues/178)), `credentials.health`
|
|
90
|
+
([#179](https://github.com/zackbart/connecta/issues/179)), `surface` and
|
|
91
|
+
`calls.maxBatchResultBytes`
|
|
92
|
+
([#273](https://github.com/zackbart/connecta/issues/273)), and the flat v0.6
|
|
93
|
+
paths. Silently ignoring a removed option is how a deployment ends up running a
|
|
94
|
+
policy its config file says it has.
|
|
95
|
+
|
|
96
|
+
### Deployment as a release unit
|
|
97
|
+
|
|
98
|
+
Treat the package and each running instance as separate release units:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
@zackbart/connecta release
|
|
102
|
+
↓ exact version
|
|
103
|
+
deployment repository
|
|
104
|
+
src/index.ts connector and auth configuration
|
|
105
|
+
package-lock.json reproducible package graph
|
|
106
|
+
wrangler.jsonc (Worker) domain, bindings
|
|
107
|
+
migrations/ (Worker) deployment-owned D1 schema history
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
An upgrade is an intentional dependency change followed by a normal build.
|
|
111
|
+
Instances must not share KV namespaces, D1 databases, secrets, or encryption
|
|
112
|
+
keys. Keeping deployment configuration private is sensible even though this
|
|
113
|
+
package is public.
|
|
114
|
+
|
|
115
|
+
## Verification
|
|
116
|
+
|
|
117
|
+
`npm run check` must pass before anything is claimed done. In order:
|
|
118
|
+
|
|
119
|
+
| Script | What it gates |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `check:docs` | local Markdown targets and fragments, guide and ethos size caps, duplicate heading anchors, a resurrected `docs/`, stale manual references |
|
|
122
|
+
| `check:operator-ui` | the committed browser bundle matches its source, byte for byte |
|
|
123
|
+
| `check:lint` | Oxlint's correctness category only — style is authored, not enforced |
|
|
124
|
+
| `check:unused` | Knip's unused-export and dependency gate |
|
|
125
|
+
| `typecheck` | `tsc --noEmit` for the package and the separate DOM-lib browser project |
|
|
126
|
+
| `test` | both vitest projects |
|
|
127
|
+
| `build` | the operator bundle, then `tsc -p tsconfig.build.json` into `dist/` |
|
|
128
|
+
| `check:examples` | the Node template and the Worker example typecheck against the built package |
|
|
129
|
+
|
|
130
|
+
`npm run release:check` adds `check:security` (`npm audit --omit=dev
|
|
131
|
+
--audit-level=moderate`) and `check:package`, and is what CI runs on every push
|
|
132
|
+
and pull request. `check:package` packs the tarball, asserts the required files
|
|
133
|
+
are in it and that no platform-specific implementation or unshippable path
|
|
134
|
+
leaked in, derives the shipped guide list from which guides still carry a stub
|
|
135
|
+
marker, checks that every packed doc's `documentation/` link resolves to
|
|
136
|
+
something the tarball carries, and then runs `connecta init` and builds and
|
|
137
|
+
runs the generated deployment's own container.
|
|
138
|
+
|
|
139
|
+
Two more runners are deliberately outside `check`:
|
|
140
|
+
|
|
141
|
+
- `npm run test:browser` — Playwright against a real headless Chromium
|
|
142
|
+
(`npm run test:browser:install` once). It covers the embedded bundle without
|
|
143
|
+
adding a browser download to both CI Node-version jobs.
|
|
144
|
+
- `npm run drift:check` — the maintainer-run provider drift check, with local
|
|
145
|
+
provider credentials exported. No credential goes near CI and nothing files
|
|
146
|
+
itself; findings are read by a human and become issues
|
|
147
|
+
([provider conventions](./provider-conventions.md#the-maintainer-run-drift-check)).
|
|
148
|
+
- `npm run load:admission` — the opt-in capacity matrix and soak
|
|
149
|
+
([request admission](./request-admission.md#measuring-capacity)).
|
|
150
|
+
|
|
151
|
+
Releases: `npm run release:check`, tag `v<version>` matching `package.json`
|
|
152
|
+
exactly (the publish workflow verifies this and fails otherwise), and
|
|
153
|
+
publishing fires on GitHub **Release publication**, not on the tag push.
|
|
154
|
+
|
|
155
|
+
## The test map
|
|
156
|
+
|
|
157
|
+
Suites live in `test/` and run as two vitest projects. `WORKERS_SUITES` holds
|
|
158
|
+
runtime-portable suites; `NODE_ONLY_SUITES` holds Node-bound suites, each with
|
|
159
|
+
a stated reason. The `node` project runs their union; the `workers` project
|
|
160
|
+
re-runs the portable list inside workerd against the Worker example's
|
|
161
|
+
compatibility settings — so a Workers-only regression, the class of bug the
|
|
162
|
+
`CfWorkerJsonSchemaValidator` workaround exists for, fails CI instead of being
|
|
163
|
+
found by hand. `test/suite-partition.test.ts` walks the directory and refuses
|
|
164
|
+
an unclassified, double-classified, stale, or reasonless entry.
|
|
165
|
+
|
|
166
|
+
**New behavior gets a row here.** A suite that is not in this table is either
|
|
167
|
+
new and undocumented or dead, and neither is a state to leave the repository
|
|
168
|
+
in.
|
|
169
|
+
|
|
170
|
+
### Runtime-portable (`WORKERS_SUITES`)
|
|
171
|
+
|
|
172
|
+
| Suite | Covers |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `access-tokens.test.ts` | the `AccessTokenManager` — a one-time secret created, authenticated, renamed, and revoked, bounded names and active count, enumerable storage required, a deployment with no Clerk operator refused — and the Clerk-only routes, down to historical activity still resolving a revoked token's name |
|
|
175
|
+
| `activity.test.ts` | payload-free delivery: a rejected async write attaches to `waitUntil` instead of throwing, approved destructive calls record under their real entry point, result-size friction records without retaining the result, and a hallucinated connector id or invented identity is clamped so the event still cannot carry a payload |
|
|
176
|
+
| `api-connector.test.ts` | `api()` — kind, description, tool defs, dispatch, default args, unknown tools, handler throws, argument validation, and the construction contract |
|
|
177
|
+
| `bearer.test.ts` | constant-time bearer compare, case-insensitive scheme, 401 challenges, and the retired audience options refusing rather than silently unbinding |
|
|
178
|
+
| `branding.test.ts` | branding fallbacks and overrides across the operator shells, OAuth result pages, `/favicon.*`, page titles, and escaping — branding is not an injection vector |
|
|
179
|
+
| `call-admission.test.ts` | connector-scoped per-runtime downstream admission ([call admission](./call-admission.md)): independent partitions, exact rolling-window reset, cancellation that charges no budget, bounded partition state, local-refusal health isolation, one shared limiter across direct and program calls, and payload-free `/health` aggregates |
|
|
180
|
+
| `catalog-drift.test.ts` | `vettedCatalog()`, `detectCatalogDrift()`, and `withVettedCatalog()`; drift on the registry surface and on `/health`; the connector seam projected rather than echoed; and the drift types being public |
|
|
181
|
+
| `catalog.test.ts` | lexical ranking and the compact schema renderer — `const`, `allOf` beside siblings, `$ref`, the depth limit, per-schema caching, and 2020-12 keyword compatibility |
|
|
182
|
+
| `clerk.test.ts` | protected-resource metadata, the browser sign-in config, OAuth and session tokens, cached best-effort activity labels with their caps, the hand-applied `azp` rejection, and the `allowedDomains` allowlist including every lookalike that must not be repaired into a match |
|
|
183
|
+
| `cloudflare-provider.test.ts` | `cloudflare()` construction, tool surface, request building, projections, typed failures, and credential test |
|
|
184
|
+
| `cloudflare-registry.test.ts` | the same provider inside a real deployment: discovery, addressing, and admission through the registry |
|
|
185
|
+
| `code-first-surface.test.ts` | the seven-tool surface itself — an executor required and both runtime configurations named, every removed option and removed top-level tool refused, and `connecta.ui` findable before an agent chooses catalog search |
|
|
186
|
+
| `codemode-compat.test.ts` | the `Executor` seam staying structurally compatible with `@cloudflare/codemode`'s `DynamicWorkerExecutor`, enforced by `tsc` |
|
|
187
|
+
| `config.test.ts` | the grouped `ConnectaConfig` boundary — each group forwarding to its internals, malformed admission bounds failing construction, and one complete migration error for legacy own-properties |
|
|
188
|
+
| `credentials.test.ts` | the pure stored-shape classifier (containment, not equality) and the AES-GCM vault: round-trip, ciphertext bound to its connector id, named field sets, masked metadata, wrong-key rejection, deletion, coexistence with OAuth keys |
|
|
189
|
+
| `d1-activity-example.test.ts` | the Worker example's deployment-owned D1 activity store: actor namespace round-trip, payload-free friction reconstructed from the persisted code, and agreement with the package's friction table |
|
|
190
|
+
| `downstream-oauth.test.ts` | `KvOAuthProvider` round-trips and races, `auth_required` versus `error`, `startAuth`/`finishAuth`, callback refusal equality, bounded diagnostics, and HTML escaping |
|
|
191
|
+
| `errors.test.ts` | `ConnectorCallError` codes, retryable defaults and overrides, `retryAfterMs` round-trip, typed-over-heuristic classification, `AbortError` as a retryable timeout, and framing errors |
|
|
192
|
+
| `execute.test.ts` | the code-mode host bridge: identifier sanitization, MCP-result unwrapping, sandbox provider construction, fail-closed filtering of destructive and unannotated tools, and MCP/code-mode invocation parity |
|
|
193
|
+
| `execute-emit.test.ts` | `connecta.emit` (M1–M10) — block validation, budgets, the provider, delivery after the result envelope on success only, and the defaults |
|
|
194
|
+
| `execute-ui.test.ts` | `connecta.ui` (U1–U9) — validation, multiplicity and budget, the provider, `_meta` delivery, and the Apps shell |
|
|
195
|
+
| `executor-admission.test.ts` | the portable bounded FIFO both pools use: active and queue ceilings, stable retryable overload, queue timeout, cancellation removal, idempotent release, shutdown |
|
|
196
|
+
| `guarded-fetch.test.ts` | the guarded transport — construction, request building, destination confinement, and response handling |
|
|
197
|
+
| `guest-api-contract.test.ts` | the executor-independent half of the guest API contract cases, including the serialized truncation envelope, capped logs, truncation reported as success, and an in-flight host call failing when the run ends |
|
|
198
|
+
| `linear-provider.test.ts` / `linear-registry.test.ts` | the Linear proxy's construction, classification, and guide; then the same connector inside a real deployment |
|
|
199
|
+
| `meta-tools.test.ts` | the registry-backed meta-tools: bounded discovery with page and address maxima, concise and full descriptions, compact and JSON schemas, structured errors, `skills` and connector-guide selection, stored-credential drift, catalog-lookup health accounting, `fields` selection, truncation and `get_result` offset validation and character alignment, per-connector `maxResultBytes`, probe timeouts, and empty-query browse of an unavailable or unconfigured catalog |
|
|
200
|
+
| `mixpanel-provider.test.ts` / `mixpanel-registry.test.ts` | the Mixpanel proxy, then the same connector inside a real deployment |
|
|
201
|
+
| `notion-provider.test.ts` / `notion-registry.test.ts` | Notion's tool surface, request construction, lean projections, both pagination conventions, error mapping, and writes; then the connector in a real deployment |
|
|
202
|
+
| `operator-boundary.test.ts` | the operator row of the decisions table, after every mutation route: authentication material managed without moving a declared structure, and the one honest exception — a credential write making a remote catalog appear, which is discovery arriving, not an operator editing the deployment |
|
|
203
|
+
| `operator-store.test.ts` | `src/operator-ui/app/store.ts` against a fake browser: the Clerk listener, `gate()`, the generation fence, and the request path |
|
|
204
|
+
| `provider-conventions.test.ts` | the conventions a test can hold: hand-written providers refusing schemas they cannot enforce (H5), Cloudflare stating its second pagination convention in the schema (H10), and Notion saying it has no escape hatch (H14) |
|
|
205
|
+
| `registry.test.ts` | construction and id validation, startup convention and result-cap warnings, address resolution, tool-cache TTL, and broken-connector isolation |
|
|
206
|
+
| `remote-mcp.test.ts` | `remoteMcp()` against an in-process server through the `_transportFactory` seam: passthrough, downstream `isError`, Workers-safe output-schema validation, request-scoped client reuse and at-most-once scope close; plus the real transport's manual redirect policy, destination guard, credential containment, and downstream session termination |
|
|
207
|
+
| `remote-mcp-pagination.test.ts` | the `tools/list` cursor chain in both directions — exact cursor handoff, first-wins dedup, a failed later page rejecting rather than returning its prefix, the runaway backstops, the tool-metadata re-prime across pages, and paginated catalogs reaching the discovery path |
|
|
208
|
+
| `request-admission.test.ts` | `/mcp` bounded before auth, the stable 503 and `Retry-After`, health and operator responsiveness under saturation, payload-free counters, queued cancellation, shutdown rejection while active work drains, and the separate fallback code pool |
|
|
209
|
+
| `server.test.ts` | end-to-end `/mcp` (401 → initialize instructions → seven tools → usage skill → `call_tool`), the open routes, Clerk `.well-known` metadata with no network, and an end-to-end code-mode run |
|
|
210
|
+
| `server-route-contracts.test.ts` | the route contracts `server.ts` must keep byte-identical: every built-in answered ahead of connector routes inside the security wrapper, open data-free shells with framing denied, per-route auth and same-origin requirements with exact 401/403/405 bodies, and OAuth `verifyState`-before-`finishAuth` ordering |
|
|
211
|
+
| `startup-warnings.test.ts` | every construction-time `logger.warn` and, as importantly, the conditions that must *not* trigger one: open mode with a credential or OAuth connector, `publicUrl` unset beside OAuth, dropped branding and `uiAuth` URLs, a missing `verifyState`, a credential test-hook mismatch, and an unusable `calls.maxResultBytes` |
|
|
212
|
+
| `stripe-provider.test.ts` / `stripe-registry.test.ts` | the Stripe proxy's endpoint modes and admission, then the connector in a real deployment |
|
|
213
|
+
| `ui.test.ts` | the server shell and `/ui/*` routes and the app's pure state rules from `view.ts` — filtering, page routing and capability states, credential management, gated `/ui/data` with broken-connector isolation, and the URL safety gates |
|
|
214
|
+
| `validate.test.ts` | `validateToolInput()` — a returned (not thrown) `invalid_args` naming the path, `additionalProperties: false` enforcement, per-schema validator caching, and an unusable schema passed through with one warning |
|
|
215
|
+
|
|
216
|
+
### Node-bound (`NODE_ONLY_SUITES`)
|
|
217
|
+
|
|
218
|
+
Each entry carries its reason in `vitest.config.ts`; the reason is the
|
|
219
|
+
justification for *not* re-running it in workerd, so "it was easier" is not one.
|
|
220
|
+
|
|
221
|
+
| Suite | Covers | Why Node |
|
|
222
|
+
| --- | --- | --- |
|
|
223
|
+
| `deployment-shapes.test.ts` | the Worker as the only example, one Node template that is also its own container, the same source running locally and in the container, the full operator surface in both, a template that cannot start on its own `.env.example`, a Worker README naming every optional peer its entrypoint imports, and the initializer's `.gitignore` staying in step | walks the template and example trees with Node filesystem APIs |
|
|
224
|
+
| `doc-links.test.ts` | the documentation checker itself — local file and fragment resolution, duplicate heading slugs, fenced-code exclusion, and useful failures | spawns the Node checker against filesystem fixtures |
|
|
225
|
+
| `doctor-cli.test.ts` | `connecta doctor`'s executor line end to end — the sandbox the deployment reports is the one named, an unidentifiable executor gets an executor-neutral line, and a hostile name is bounded and stripped before it reaches a terminal | spawns the CLI against a Node HTTP deployment over real sockets |
|
|
226
|
+
| `drift-check.test.ts` | the maintainer drift checker — recorded touched endpoints, a quiet revision bump, clear failures for an unavailable spec/manifest/credential, `$ref` traversal, and one well-formed row per endpoint | spawns the Node checker against filesystem fixtures |
|
|
227
|
+
| `file-storage.test.ts` | `fileStorage()` across instances, logical TTL plus physical pruning without clobbering a newer value, and corrupt-file quarantine | exercises the Node filesystem storage adapter |
|
|
228
|
+
| `guest-api-contract-quickjs.test.ts` | the shared guest-contract cases on the real QuickJS executor | runs the contract cases on the Node QuickJS executor |
|
|
229
|
+
| `node.test.ts` | the `listen()` adapter propagating an HTTP client disconnect through the Web `Request` and the MCP handler into a program's connector call, releasing both admission permits | exercises the Node HTTP adapter over real TCP sockets |
|
|
230
|
+
| `package-surface.test.ts` | the published boundary — built output shipped, only generic factories, platform storage kept in examples, Clerk and QuickJS behind optional subpaths, every provider independently importable, and the Cloudflare provider free of bare specifiers | walks the package tree with Node filesystem APIs |
|
|
231
|
+
| `purity.test.ts` | the import-graph guardrail ([architecture](./architecture.md#import-graph-purity)) — the core stays Workers-clean | walks the source import graph with Node filesystem APIs |
|
|
232
|
+
| `quickjs-child-entry.test.ts` | a missing QuickJS child entry failing before `fork()`, with the expected path and the bundler-externalization constraint | mocks Node child-process and filesystem APIs |
|
|
233
|
+
| `quickjs-child-stderr.test.ts` | abnormal child exits retaining only an 8 KiB stderr tail, included in the parent-side diagnostic | mocks Node child-process streams |
|
|
234
|
+
| `quickjs-executor.test.ts` | the child-process sandbox — code normalization, lazy namespace proxies, bounded IPC, separate guest-CPU and wall budgets, saturation, cancellation and shutdown, crash and OOM recovery, host-call hangs, stalled-promise detection | runs the Node QuickJS child-process executor |
|
|
235
|
+
| `quickjs-log-limits.test.ts` | bounded `console.*` capture — per-entry cut, cumulative character and transport budgets, escape-heavy floods preserving the guest result | runs the Node QuickJS child-process executor |
|
|
236
|
+
| `suite-partition.test.ts` | this partition, including itself: every `*.test.ts` in exactly one list, stale entries and empty reasons refused | walks the test directory to guard the partition |
|
|
237
|
+
| `template-file-activity.test.ts` | the Node template's own activity store — persistence across restart, torn-line repair, newest-first paging, and compaction past the slack window | runs it against real files |
|
|
238
|
+
| `version.test.ts` | `CONNECTA_VERSION` matching `package.json` | reads `package.json` with Node filesystem APIs |
|
|
239
|
+
|
|
240
|
+
### Outside `npm run check`
|
|
241
|
+
|
|
242
|
+
| Suite | Covers |
|
|
243
|
+
| --- | --- |
|
|
244
|
+
| `browser/operator-ui.spec.ts` | the operator wiring in a real browser: the shell staying open until authentication, credential and access-token and OAuth flows end to end, drift shown without naming a tool, and every failure and empty state |
|
|
245
|
+
| `browser/program-ui.spec.ts` | the Apps shell in a real browser: a bound view merging fixed and declared arguments and correlating concurrent reads, and the one-string payload receiving no read bridge ([program UI read calls](./program-ui-read-calls.md)) |
|
|
246
|
+
|
|
247
|
+
**The `_transportFactory` seam.** `RemoteMcpOptions._transportFactory` is
|
|
248
|
+
internal, not public API: when set, `remoteMcp()` uses that `Transport` instead
|
|
249
|
+
of building an HTTP one. Tests link an in-memory transport to an in-process MCP
|
|
250
|
+
server, so remote-MCP behavior is exercised without a network or a real OAuth
|
|
251
|
+
server. Two consequences worth knowing before you use it: an in-memory
|
|
252
|
+
transport has no session semantics, so anything about `Mcp-Session-Id` needs
|
|
253
|
+
the real HTTP transport, and anything about redirects or destination
|
|
254
|
+
confinement does too.
|
|
255
|
+
|
|
256
|
+
## Troubleshooting
|
|
257
|
+
|
|
258
|
+
- **MCP clients cache the tool list.** After adding a connector or completing a
|
|
259
|
+
downstream OAuth flow, restart the client. It will not re-list on its own.
|
|
260
|
+
Connecta declares a one-hour private `tools/list` cache hint, which is a
|
|
261
|
+
ceiling on how long a well-behaved client may wait, not a promise it will.
|
|
262
|
+
- **`auth_required` that never clears.** Confirm `publicUrl` is set and
|
|
263
|
+
`GET <publicUrl>/oauth/callback/<connectorId>` is reachable from a browser,
|
|
264
|
+
and that storage is durable rather than `memoryStorage()` across restarts.
|
|
265
|
+
Then `authorize_connector` to restart the flow; `force: true` wipes stored
|
|
266
|
+
credentials for a clean retry.
|
|
267
|
+
- **A connector with no `verifyState` refuses every callback.** That is the
|
|
268
|
+
designed behavior, not a bug: handing an unverified code to `finishAuth` is
|
|
269
|
+
the vulnerability. The startup warning names the connector.
|
|
270
|
+
- **401 loops from a client that cannot discover auth.** The client must reach
|
|
271
|
+
the open `/.well-known/oauth-protected-resource` (and the `/mcp` variant);
|
|
272
|
+
confirm CORS and the Clerk keys, and that DCR is enabled on the Clerk
|
|
273
|
+
instance.
|
|
274
|
+
- **No sessions and no server push, by design.** The transport is stateless.
|
|
275
|
+
Scope resolves per request, which is also where the MCP spec has arrived.
|
|
276
|
+
- **A tool that should be callable from a program is not.** Only tools
|
|
277
|
+
explicitly annotated `readOnlyHint: true` are admissible inside the sandbox.
|
|
278
|
+
A missing, false, or contradictory annotation fails closed, every time, and
|
|
279
|
+
the recovery is `call_destructive_tool` — not a wider sandbox.
|
|
280
|
+
- **`check:operator-ui` fails after a UI change.** Run
|
|
281
|
+
`npm run build:operator-ui` and commit the regenerated
|
|
282
|
+
`src/operator-ui/generated.ts` ([operator UI](./operator-ui.md#why-the-bundle-is-committed)).
|
|
283
|
+
- **Upgrade the MCP SDK and Zod together**, then run `npm run release:check`.
|
|
284
|
+
The SDK packages are pinned exactly and paired with Zod 4 to keep the
|
|
285
|
+
optional code-mode peer graph valid.
|