@zackbart/connecta 0.21.2 → 0.22.1
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 +69 -0
- package/README.md +10 -3
- package/dist/access-tokens.d.ts +2 -2
- package/dist/access-tokens.js +14 -2
- package/dist/connectors/api.d.ts +2 -0
- package/dist/connectors/api.js +1 -0
- package/dist/connectors/remote-mcp.d.ts +2 -0
- package/dist/connectors/remote-mcp.js +6 -0
- package/dist/credentials.d.ts +6 -6
- package/dist/credentials.js +25 -21
- package/dist/identity.d.ts +4 -0
- package/dist/identity.js +17 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +6 -1
- package/dist/meta-tools.js +7 -2
- package/dist/operator-ui/generated.js +1 -1
- package/dist/operator-ui/model.d.ts +4 -2
- package/dist/operator-ui/view.js +1 -1
- package/dist/providers/cloudflare.d.ts +2 -0
- package/dist/providers/cloudflare.js +1 -0
- package/dist/providers/linear.d.ts +2 -0
- package/dist/providers/linear.js +1 -0
- package/dist/providers/mixpanel.d.ts +2 -0
- package/dist/providers/mixpanel.js +1 -0
- package/dist/providers/notion.d.ts +2 -0
- package/dist/providers/notion.js +1 -0
- package/dist/providers/revenuecat.d.ts +2 -0
- package/dist/providers/revenuecat.js +1 -0
- package/dist/providers/stripe.d.ts +2 -0
- package/dist/providers/stripe.js +1 -0
- package/dist/providers/vercel.d.ts +25 -0
- package/dist/providers/vercel.js +1132 -0
- package/dist/registry.d.ts +25 -0
- package/dist/registry.js +200 -4
- package/dist/routes/access-tokens.js +2 -2
- package/dist/routes/activity.js +4 -1
- package/dist/routes/credentials.js +31 -12
- package/dist/routes/mcp.js +17 -2
- package/dist/routes/oauth.js +55 -11
- package/dist/routes/shared.d.ts +20 -4
- package/dist/routes/shared.js +92 -24
- package/dist/routes/ui.js +32 -13
- package/dist/types.d.ts +28 -2
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +18 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +14 -8
- package/documentation/auth.md +89 -9
- package/documentation/code-mode.md +2 -2
- package/documentation/connector-guides.md +2 -2
- package/documentation/connectors.md +21 -6
- package/documentation/meta-tools.md +4 -3
- package/documentation/operations.md +5 -2
- package/documentation/operator-ui.md +13 -4
- package/documentation/provider-conventions.md +5 -5
- package/documentation/request-admission.md +2 -1
- package/documentation/storage-and-credentials.md +42 -4
- package/documentation/upgrading.md +35 -4
- package/documentation/vercel.md +194 -0
- package/ethos.md +8 -8
- package/examples/worker/AGENTS.md +44 -0
- package/examples/worker/README.md +63 -14
- package/examples/worker/src/index.ts +26 -22
- package/package.json +5 -1
- package/templates/node/README.md +7 -0
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +13 -4
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Connectors
|
|
2
2
|
|
|
3
|
+
Every connector may set `authScope: "shared" | "personal"`. Shared is the
|
|
4
|
+
default and keeps one deployment-wide downstream grant. Personal auth requires
|
|
5
|
+
a stable human principal and partitions connector state, credentials, OAuth,
|
|
6
|
+
catalogs, and observed shapes by that principal. Connector visibility is a
|
|
7
|
+
separate deployment rule under `identity.connectorAccess`; hiding a connector
|
|
8
|
+
does not change who owns its auth. See [shared and personal auth](./storage-and-credentials.md#shared-and-personal-auth).
|
|
9
|
+
|
|
10
|
+
`authScope` partitions connecta-owned context, not arbitrary variables captured
|
|
11
|
+
by connector code. A custom personal connector must read auth from
|
|
12
|
+
`ctx.credential` or `ctx.storage`; a secret closed over by its handler remains
|
|
13
|
+
shared JavaScript state. `remoteMcp()` rejects the equivalent mistake when
|
|
14
|
+
literal headers are combined with personal scope.
|
|
15
|
+
|
|
3
16
|
Connectors are the boundary between Connecta's fixed meta-tool surface and
|
|
4
17
|
downstream capabilities. Prefer a prebuilt connection when Connecta maintains
|
|
5
18
|
one for the provider. Use `api()` to define a deliberate HTTP API surface and
|
|
@@ -124,6 +137,7 @@ Maintained provider guides:
|
|
|
124
137
|
- [Notion](./notion.md)
|
|
125
138
|
- [RevenueCat](./revenuecat.md)
|
|
126
139
|
- [Stripe](./stripe.md)
|
|
140
|
+
- [Vercel](./vercel.md)
|
|
127
141
|
|
|
128
142
|
## The `api()` construction contract
|
|
129
143
|
|
|
@@ -213,12 +227,13 @@ into a result or a typed failure. That split is not fastidiousness. Notion's
|
|
|
213
227
|
cannot fix it — while Cloudflare's means a token scope, and the two want
|
|
214
228
|
opposite next moves. A helper that guessed would be wrong for one of them.
|
|
215
229
|
|
|
216
|
-
Cloudflare and
|
|
217
|
-
unchanged
|
|
218
|
-
not that nothing changed. Three things did,
|
|
219
|
-
3xx is refused where both providers used to
|
|
220
|
-
their byte ceiling, and `cloudflare()`'s
|
|
221
|
-
construction. Each suite gained one test for the
|
|
230
|
+
Cloudflare, Notion, and Vercel run on it. The first two existing suites carried
|
|
231
|
+
over unchanged when the helper was extracted, which proves that migration kept
|
|
232
|
+
the behavior those suites cover — not that nothing changed. Three things did,
|
|
233
|
+
and the changelog names them: a 3xx is refused where both providers used to
|
|
234
|
+
follow it, both now fail past their byte ceiling, and `cloudflare()`'s
|
|
235
|
+
`baseUrl` is validated at construction. Each suite gained one test for the
|
|
236
|
+
ceiling, because the one
|
|
222
237
|
guard the helper was written to add is the one a provider's own mapper can
|
|
223
238
|
most easily disarm: a bare `catch` around `response.json()` swallows the
|
|
224
239
|
transport's refusal along with a parse error, and turns a response nobody was
|
|
@@ -307,9 +307,10 @@ credential.
|
|
|
307
307
|
|
|
308
308
|
The tool accepts no secret. `force` applies only to OAuth and may discard its
|
|
309
309
|
stored grant before restarting consent. Static credential values are written
|
|
310
|
-
only through the same-origin
|
|
311
|
-
|
|
312
|
-
read from the vault on the next call
|
|
310
|
+
only through the same-origin interactive-user credential route, and only for a
|
|
311
|
+
connector visible to that user. After OAuth consent or a human update, retry
|
|
312
|
+
the original operation; a static update is read from the vault on the next call
|
|
313
|
+
and needs no redeploy.
|
|
313
314
|
|
|
314
315
|
## Routing recovery
|
|
315
316
|
|
|
@@ -84,6 +84,7 @@ optional.
|
|
|
84
84
|
| `connectors` | — (required) | the connector set ([connectors](./connectors.md)) |
|
|
85
85
|
| `executor` | — (required) | the sandbox `execute_code` runs in ([code mode](./code-mode.md#what-an-executor-must-implement)) |
|
|
86
86
|
| `auth?` | none ⇒ open (dev only) | one `InboundAuth` or an array; bearer providers are checked before interactive providers ([inbound auth](./auth.md)) |
|
|
87
|
+
| `identity?` | all connectors; every interactive human is an operator | `{ connectorAccess?, operatorAccess? }` derives the request's connector view and shared-auth authority from its authenticated identity ([principals](./auth.md#principals-visibility-and-operators)) |
|
|
87
88
|
| `storage?` | `memoryStorage()` | the one state seam for catalogs, result paging, credentials, and access tokens ([storage](./storage-and-credentials.md)) |
|
|
88
89
|
| `publicUrl?` | per-request origin | public base URL; an HTTPS value also redirects inbound HTTP |
|
|
89
90
|
| `logger?` | `console`, prefixed `[connecta]` | `{ debug, info, warn, error }` |
|
|
@@ -247,6 +248,7 @@ in.
|
|
|
247
248
|
| `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 |
|
|
248
249
|
| `guarded-fetch.test.ts` | the guarded transport — construction, request building, destination confinement, and response handling |
|
|
249
250
|
| `guest-api-contract.test.ts` | the shared guest contract on the Dynamic Worker, including caught call, typed inline describe recovery, discovery, utility, batch, and budget failure codes; plus the real authority boundary — local `data:` fetch, denied egress, unresolved DNS, empty environment paths, unavailable filesystem/HTTP builtins, and present runtime globals |
|
|
251
|
+
| `identity-scope.test.ts` | identity-derived connector visibility, personal credential isolation, shared-auth operator control, and personal OAuth callback ownership |
|
|
250
252
|
| `linear-provider.test.ts` | the Linear proxy's construction, guide, plan-aware catalog superset, and current workspace, template, and issue-sharing classifications |
|
|
251
253
|
| `meta-tools-call.test.ts` | registry-backed calls: structured errors, truncation and `get_result`, per-connector result bounds, JSON representation failures, MCP content bounds, and offset alignment |
|
|
252
254
|
| `meta-tools-search.test.ts` | registry-backed discovery: bounded search with page and address maxima, compact and JSON schemas with constraints, typed describe recovery and suggestions, and structured-result compatibility |
|
|
@@ -256,7 +258,7 @@ in.
|
|
|
256
258
|
| `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 |
|
|
257
259
|
| `operator-store.test.ts` | `src/operator-ui/app/store.ts` against a fake browser: the Clerk listener, ambient Access requests without a browser-readable token, `gate()`, the generation fence, and the request path |
|
|
258
260
|
| `provider-conventions.test.ts` | the conventions a test can hold: hand-written providers refusing schemas they cannot enforce (H5), their compact discovery schemas staying complete (H7), Cloudflare stating its second pagination convention in the schema (H10), and Notion saying it has no escape hatch (H14) |
|
|
259
|
-
| `provider-registry.test.ts` | all
|
|
261
|
+
| `provider-registry.test.ts` | all seven maintained providers inside real deployments: boot, description, address, catalog, storage, credential, admission, and activity isolation; plus provider-specific discovery and guide contracts |
|
|
260
262
|
| `registry.test.ts` | construction and id validation, startup warnings, address resolution, version 2 catalog TTL/persistence/completeness, agent-only stale-while-revalidate with cross-request single-flight shared with blocking reads in both start orders, owned teardown, invalidation/fingerprint guards, blocking diagnostics, and broken-connector isolation |
|
|
261
263
|
| `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 |
|
|
262
264
|
| `remote-mcp-credential.test.ts` | `remoteMcp()` drawing a static key from `/credentials`: the declared slot and its refusal of named fields and bad header names, header framing (bearer, bare, and the two `Basic` forms) observed on the wire, an empty slot failing as `auth_required` rather than reaching the downstream, a value carrying a control character refused before framing and absent from every surface — `call_tool`, `status`, the Test result, the payload-free activity event, and the thrown error — rotation replacing the cached client and a connect already in flight while a wiped value fails the next call, the Test action's catalog probe and scope close, the cleartext-destination warning, and the vault and `authorize_connector` handoff end to end |
|
|
@@ -272,6 +274,7 @@ in.
|
|
|
272
274
|
| `ui-credentials.test.ts` | credential-management routes: save, test, delete, validation, authentication, same-origin checks, and multi-field credential shapes |
|
|
273
275
|
| `ui.test.ts` | the server shell and remaining `/ui/*` routes: gated `/ui/data` with broken-connector isolation and registry-owned catalog-observation containment, plus the URL safety gates |
|
|
274
276
|
| `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 |
|
|
277
|
+
| `vercel-provider.test.ts` | `vercel()` construction, team scoping, project and deployment projections, finite build and runtime logs, value-safe environment variables, domains, lifecycle writes, REST hatches, typed failures, and credential test |
|
|
275
278
|
|
|
276
279
|
### Node-bound (`NODE_ONLY_SUITES`)
|
|
277
280
|
|
|
@@ -280,7 +283,7 @@ justification for *not* re-running it in workerd, so "it was easier" is not one.
|
|
|
280
283
|
|
|
281
284
|
| Suite | Covers | Why Node |
|
|
282
285
|
| --- | --- | --- |
|
|
283
|
-
| `deployment-shapes.test.ts` | the Worker as the only example with a loader-only sandbox, one Node template that is also its own container, the same source running locally and in the container, the Node template's pinned esbuild install-script approval, 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 |
|
|
286
|
+
| `deployment-shapes.test.ts` | the Worker as the only example with a loader-only sandbox, its agent instructions and setup guide pinning Claude and both ChatGPT Managed OAuth callback forms, one Node template that is also its own container, the same source running locally and in the container, the Node template's pinned esbuild install-script approval, 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 |
|
|
284
287
|
| `doc-links.test.ts` | the documentation checker itself — local file and fragment resolution, repository URLs resolved back to the checkout, duplicate heading slugs, fenced-code exclusion, and useful failures | spawns the Node checker against filesystem fixtures |
|
|
285
288
|
| `doctor-cli.test.ts` | `connecta doctor`'s executor line and credentials end to end — the sandbox the deployment reports is the one named, an unidentifiable executor gets an executor-neutral line, a hostile name is bounded, and a complete Cloudflare Access service-token pair is accepted while a partial pair is refused | spawns the CLI against a Node HTTP deployment over real sockets |
|
|
286
289
|
| `drift-check.test.ts` | the maintainer drift checker — hosted-provider credential framing, 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 |
|
|
@@ -5,10 +5,10 @@ the authentication material behind it. It is a small Preact app compiled by the
|
|
|
5
5
|
repository's own esbuild step and inlined into a data-free server shell.
|
|
6
6
|
|
|
7
7
|
Read [`ethos.md`](../ethos.md) first. The boundary this subsystem lives inside
|
|
8
|
-
is the
|
|
9
|
-
|
|
10
|
-
and may
|
|
11
|
-
OAuth scopes, admission policy,
|
|
8
|
+
is the human-management invariant: **members may manage authentication material
|
|
9
|
+
for every connector their code-derived view includes, operators may also manage
|
|
10
|
+
deployment tokens and global activity, and neither may change the connector set, tool catalog,
|
|
11
|
+
annotations, requested OAuth scopes, admission policy, or identity rules.**
|
|
12
12
|
`test/operator-boundary.test.ts` proves it after every mutation route.
|
|
13
13
|
|
|
14
14
|
Both deployment shapes ship the whole feature set behind it, because pages for
|
|
@@ -52,6 +52,15 @@ server reads the resulting runtime identity. Sign out navigates to
|
|
|
52
52
|
`/cdn-cgi/access/logout`. Mutations still require an exact same-origin
|
|
53
53
|
`Origin`; an ambient cookie does not weaken the CSRF boundary.
|
|
54
54
|
|
|
55
|
+
The shell is shared by members and operators. `/ui/data` uses the same
|
|
56
|
+
identity-scoped registry view as `/mcp`, so it cannot list a connector the
|
|
57
|
+
current caller cannot discover. A member sees credential and OAuth controls for
|
|
58
|
+
every visible connector. Personal actions resolve to that member's principal
|
|
59
|
+
partition; shared actions change the deployment-wide grant. The access-token
|
|
60
|
+
and global activity pages require `identity.operatorAccess`. Existing
|
|
61
|
+
deployments that omit that resolver keep every interactive human as an
|
|
62
|
+
operator.
|
|
63
|
+
|
|
55
64
|
This runtime selection is the Clerk migration seam. A deployment may contain
|
|
56
65
|
both providers: before Worker-level Access is attached, the data-free shell
|
|
57
66
|
selects Clerk; after Access supplies `ctx.access`, it selects ambient auth. That
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Provider conventions
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The seven maintained prebuilt connections grew one at a time, and until now
|
|
4
4
|
"excellent provider" meant whatever the last author thought. This document
|
|
5
5
|
writes the judgment down so it can be argued with, audited, and reused.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ There are two genuinely different provider shapes, and one convention set
|
|
|
8
8
|
cannot honestly cover both:
|
|
9
9
|
|
|
10
10
|
- **Hand-written HTTP providers** — `api()` surfaces where Connecta owns every
|
|
11
|
-
tool name, schema, projection, and error. Today: Cloudflare, Notion.
|
|
11
|
+
tool name, schema, projection, and error. Today: Cloudflare, Notion, Vercel.
|
|
12
12
|
- **Hosted-MCP proxies** — `remoteMcp()` wrappers around a server somebody else
|
|
13
13
|
operates, where the names, schemas, results, and error prose arrive as they
|
|
14
14
|
are. Today: Linear, Stripe, Mixpanel, RevenueCat.
|
|
@@ -85,7 +85,7 @@ other source with no description or no `inputSchema`.
|
|
|
85
85
|
## Hand-written HTTP providers
|
|
86
86
|
|
|
87
87
|
Connecta owns the whole surface here, which means every miss is ours. These
|
|
88
|
-
apply to `api()`-based prebuilt connections (Cloudflare, Notion) and are the
|
|
88
|
+
apply to `api()`-based prebuilt connections (Cloudflare, Notion, Vercel) and are the
|
|
89
89
|
bar any future one is written to.
|
|
90
90
|
|
|
91
91
|
None of them asks an author to re-derive transport safety. URL confinement,
|
|
@@ -646,11 +646,11 @@ evidence and nothing else: no tool is generated from one, which is the
|
|
|
646
646
|
## What the audit checks
|
|
647
647
|
|
|
648
648
|
The provider audit ([#342](https://github.com/zackbart/connecta/issues/342))
|
|
649
|
-
runs this document against each of the
|
|
649
|
+
runs this document against each of the seven providers and returns a verdict per
|
|
650
650
|
convention: **meets**, **misses** (with the fix), or **not applicable** (with
|
|
651
651
|
the reason). A convention is never quietly skipped, and an accepted miss is
|
|
652
652
|
recorded as a provider-specific exception with its argument, not left blank.
|
|
653
|
-
Its
|
|
653
|
+
Its seven reports live in [provider-audit.md](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md), and the
|
|
654
654
|
mechanically checkable half of the hand-written bar runs on every test run in
|
|
655
655
|
[`test/provider-conventions.test.ts`](https://github.com/zackbart/connecta/blob/main/test/provider-conventions.test.ts) —
|
|
656
656
|
so a convention that was met once stays met, or fails loudly.
|
|
@@ -49,7 +49,8 @@ which is also why `/health` always has a code-admission shape to report.
|
|
|
49
49
|
|
|
50
50
|
The request pool is global FIFO across identities. It is a capacity boundary,
|
|
51
51
|
not tenant fairness: one busy caller can occupy it. Per-tenant fairness needs a
|
|
52
|
-
policy above connecta, and one deployment serves one
|
|
52
|
+
policy above connecta, and one deployment still serves one tenant even when
|
|
53
|
+
identity rules give its principals different connector views
|
|
53
54
|
([`ethos.md`](../ethos.md)), so a global queue is not pretending to supply
|
|
54
55
|
something it does not.
|
|
55
56
|
|
|
@@ -6,7 +6,7 @@ token is an independent record rather than one shared, race-prone manifest.
|
|
|
6
6
|
The built-in memory and file adapters implement it, as does the Cloudflare KV
|
|
7
7
|
example.
|
|
8
8
|
|
|
9
|
-
Connectors may declare
|
|
9
|
+
Connectors may declare a human-managed `credential` slot. When
|
|
10
10
|
`credentials.encryptionKey` is configured, Connecta encrypts values in the
|
|
11
11
|
deployment storage and exposes read-only access only through that connector's
|
|
12
12
|
`ctx.credential`. Values, masked values, call arguments, and raw errors never
|
|
@@ -27,13 +27,40 @@ Credential mutation is intentionally narrower than MCP access:
|
|
|
27
27
|
|
|
28
28
|
- a static bearer may call tools and receive the operator handoff, but it
|
|
29
29
|
cannot write credentials;
|
|
30
|
-
-
|
|
31
|
-
|
|
30
|
+
- an admitted interactive human may mutate credentials for every visible
|
|
31
|
+
connector: their own partition for personal auth, or the deployment-wide
|
|
32
|
+
value for shared auth; and
|
|
32
33
|
- saving, replacing, testing, or removing a value never returns that value.
|
|
33
34
|
|
|
34
|
-
The vault is read for each call. Once
|
|
35
|
+
The vault is read for each call. Once a signed-in human saves a replacement,
|
|
35
36
|
the agent can retry immediately without restarting or redeploying Connecta.
|
|
36
37
|
|
|
38
|
+
## Shared and personal auth
|
|
39
|
+
|
|
40
|
+
Connector auth defaults to `authScope: "shared"`. Its credential, OAuth state,
|
|
41
|
+
tokens, catalog cache, and connector storage belong to the deployment. Set
|
|
42
|
+
`authScope: "personal"` when every human principal needs a separate downstream
|
|
43
|
+
account:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
remoteMcp("linear", {
|
|
47
|
+
url: "https://mcp.linear.app/mcp",
|
|
48
|
+
authScope: "personal",
|
|
49
|
+
auth: { type: "oauth" },
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Personal connectors disappear from a request that has no stable human
|
|
54
|
+
principal. For a principal that can see one, connecta partitions connector
|
|
55
|
+
storage, encrypted vault records, catalog caches, OAuth generations, and
|
|
56
|
+
observed result shapes under an opaque SHA-256 identity key. Results used by
|
|
57
|
+
`get_result` are partitioned by the authenticated subject, so one token cannot
|
|
58
|
+
page another token's call even when both tokens belong to the same principal.
|
|
59
|
+
|
|
60
|
+
Literal `auth: { type: "headers" }` cannot be personal because its secret lives
|
|
61
|
+
in deployment code. `remoteMcp()` refuses that combination at construction.
|
|
62
|
+
Use operator-managed credential auth or OAuth instead.
|
|
63
|
+
|
|
37
64
|
## A remote MCP connector's static credential
|
|
38
65
|
|
|
39
66
|
`remoteMcp()` accepts a third auth shape beside OAuth and literal headers:
|
|
@@ -97,6 +124,17 @@ Registration and token envelopes are bound to the validated authorization
|
|
|
97
124
|
server `issuer`. An unbound pre-0.9 envelope is upgraded in place on its first
|
|
98
125
|
issuer-aware read, preserving the existing grant.
|
|
99
126
|
|
|
127
|
+
For personal OAuth, the authorization handoff also stores a 15-minute mapping
|
|
128
|
+
from a SHA-256 digest of `state` to the principal partition. The public callback
|
|
129
|
+
uses that mapping before it verifies state or exchanges the code. Neither the
|
|
130
|
+
browser nor a callback parameter can select a principal. The callback deletes
|
|
131
|
+
the mapping before it exchanges the code, so a second callback cannot replay
|
|
132
|
+
the principal handoff in strongly consistent storage. Cloudflare KV deletion
|
|
133
|
+
is eventually consistent, so handoff consumption there is best-effort across
|
|
134
|
+
PoPs; the downstream authorization code remains single-use. If the callback
|
|
135
|
+
request also carries an interactive identity, Connecta refuses it when that
|
|
136
|
+
principal did not start the flow.
|
|
137
|
+
|
|
100
138
|
If later discovery resolves a different issuer, Connecta does not send the old
|
|
101
139
|
client identifier or tokens to it. The provider publishes a new generation
|
|
102
140
|
epoch, makes every older credential namespace unreadable, cleans up the retired
|
|
@@ -57,7 +57,7 @@ exist so far:
|
|
|
57
57
|
| --- | --- | --- |
|
|
58
58
|
| **pre-template** | before 0.10.2 | no `connecta init` existed; hand-written, or copied from the retired `examples/node` |
|
|
59
59
|
| **A** | 0.10.2 – 0.15.1 | `.env.example`, `.gitignore`, `AGENTS.md`, `CLAUDE.md`, `README.md`, `package.json`, `src/index.ts`, `tsconfig.json` |
|
|
60
|
-
| **B** | 0.16.0 – 0.
|
|
60
|
+
| **B** | 0.16.0 – 0.22.1 | adds `.dockerignore`, `Dockerfile`, `docker-compose.yml`, and `src/file-activity.ts`; `src/index.ts` grows the four commented operator blocks; `.env.example` ships `CONNECTA_TOKEN=` empty |
|
|
61
61
|
|
|
62
62
|
Generation A is a decade in template years and identifying it precisely does
|
|
63
63
|
not matter, because you are about to reconstruct it exactly rather than guess
|
|
@@ -106,7 +106,7 @@ know what to preserve, once to know what to re-verify at the end.
|
|
|
106
106
|
### Bump the pin and install
|
|
107
107
|
|
|
108
108
|
```sh
|
|
109
|
-
npm pkg set dependencies.@zackbart/connecta=0.
|
|
109
|
+
npm pkg set dependencies.@zackbart/connecta=0.22.1
|
|
110
110
|
npm install
|
|
111
111
|
```
|
|
112
112
|
|
|
@@ -130,7 +130,7 @@ Generate the *current* template beside the base you already made, into the same
|
|
|
130
130
|
`$SCRATCH`:
|
|
131
131
|
|
|
132
132
|
```sh
|
|
133
|
-
(cd "$SCRATCH" && npx @zackbart/connecta@0.
|
|
133
|
+
(cd "$SCRATCH" && npx @zackbart/connecta@0.22.1 init current)
|
|
134
134
|
```
|
|
135
135
|
|
|
136
136
|
You now have a three-way merge with a real base: `$SCRATCH/base` is what this
|
|
@@ -186,7 +186,7 @@ A deployment older than 0.10.2 has no base to diff against. Do not try to
|
|
|
186
186
|
manufacture one. Instead:
|
|
187
187
|
|
|
188
188
|
1. `SCRATCH=$(mktemp -d)`, then
|
|
189
|
-
`(cd "$SCRATCH" && npx @zackbart/connecta@0.
|
|
189
|
+
`(cd "$SCRATCH" && npx @zackbart/connecta@0.22.1 init current)` — there is no
|
|
190
190
|
`base` leg here, only the current template to read from.
|
|
191
191
|
2. Copy `$SCRATCH/current` into the deployment file by file, **skipping
|
|
192
192
|
`src/index.ts`**.
|
|
@@ -207,6 +207,22 @@ first, so cross them bottom-up: start at the oldest one still above this
|
|
|
207
207
|
deployment's pin and work back up the page, because each boundary assumes the
|
|
208
208
|
older ones are already done.
|
|
209
209
|
|
|
210
|
+
### 0.21.2 → 0.22.1
|
|
211
|
+
|
|
212
|
+
Connector and user policy remain config-as-code. If `identity.connectorAccess`
|
|
213
|
+
is configured, every interactive human may now manage the authentication of
|
|
214
|
+
each connector that resolver makes visible. A personal connector changes only
|
|
215
|
+
that principal's partition; a shared connector changes the deployment-wide
|
|
216
|
+
grant. Keep shared connectors out of a member's view, or change them to
|
|
217
|
+
`authScope: "personal"`, when that member must not rotate the shared grant.
|
|
218
|
+
`identity.operatorAccess` continues to govern deployment access tokens and
|
|
219
|
+
global activity.
|
|
220
|
+
|
|
221
|
+
Paged results also move under the authenticated subject's storage partition.
|
|
222
|
+
Finish any important in-flight `get_result` sequence before upgrading; its old
|
|
223
|
+
result id is not readable from the new partition after deployment. No persisted
|
|
224
|
+
connector catalog or credential migration is required.
|
|
225
|
+
|
|
210
226
|
### 0.20.0 → 0.21.2
|
|
211
227
|
|
|
212
228
|
0.21.2 adds no deployment migration beyond 0.21.0. The boundary is additive
|
|
@@ -244,6 +260,21 @@ For a Worker currently using Clerk, keep rollback live through the cutover:
|
|
|
244
260
|
tag>" }`, not a hostname application for the `workers.dev` URL: the latter
|
|
245
261
|
gates traffic but does not provide `ctx.access`. Create an Access service
|
|
246
262
|
token and a **Service Auth** policy for doctor and fully unattended clients.
|
|
263
|
+
In the application's Managed OAuth settings, enable Dynamic Client
|
|
264
|
+
Registration and add these three **Allowed redirect URIs**:
|
|
265
|
+
|
|
266
|
+
```text
|
|
267
|
+
https://claude.ai/api/mcp/auth_callback
|
|
268
|
+
https://chatgpt.com/connector_platform_oauth_redirect
|
|
269
|
+
https://chatgpt.com/connector/oauth/*
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
They map to
|
|
273
|
+
`oauth_configuration.dynamic_client_registration.allowed_uris` in the
|
|
274
|
+
Access API, not to the identity policy. The two ChatGPT entries cover its
|
|
275
|
+
stable and callback-id forms. An empty list fails client registration only
|
|
276
|
+
after discovery, so do not treat a working `/.well-known/*` response as
|
|
277
|
+
proof that this step is complete.
|
|
247
278
|
Do not create a bypass for `/.well-known/*`; Managed OAuth owns that
|
|
248
279
|
discovery surface.
|
|
249
280
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Vercel
|
|
2
|
+
|
|
3
|
+
Import `vercel()` independently from `@zackbart/connecta/providers/vercel`.
|
|
4
|
+
It is a hand-written `api()` connection over Vercel's public REST API. The
|
|
5
|
+
connection owns 18 named operations and three provider-relative REST hatches.
|
|
6
|
+
It adds no provider dependency, imports no `node:` builtin, and is not reachable
|
|
7
|
+
from Connecta's root entry.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { vercel } from "@zackbart/connecta/providers/vercel";
|
|
11
|
+
|
|
12
|
+
const hosting = vercel("hosting", {
|
|
13
|
+
purpose: "Production web applications for the product team",
|
|
14
|
+
teamId: "team_1a2b3c4d5e6f7g8h9i0j1k2l",
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The deployment stores one Vercel access token in Connecta's credential vault.
|
|
19
|
+
Create the token in Vercel Account Settings under Tokens. Scope it to the
|
|
20
|
+
personal account or team this connection needs and give it an expiration date.
|
|
21
|
+
The operator UI's Test action calls `GET /v2/user` and reports the authenticated
|
|
22
|
+
username, email, name, or id. Connecta never probes it in the background.
|
|
23
|
+
|
|
24
|
+
## Why this uses REST instead of Vercel MCP
|
|
25
|
+
|
|
26
|
+
Vercel MCP provides useful project, deployment, and log reads, but it does not
|
|
27
|
+
cover the public API. This connection keeps those common reads and adds project
|
|
28
|
+
domains, value-safe environment-variable management, deployment promotion and
|
|
29
|
+
deletion, and direct access to versioned REST endpoints. The three hatches mean
|
|
30
|
+
a newly published Vercel endpoint does not require a Connecta release before an
|
|
31
|
+
agent can use it.
|
|
32
|
+
|
|
33
|
+
This is still authored rather than generated. No OpenAPI document creates tools
|
|
34
|
+
at runtime. The named operations are reviewed, projected, classified, and
|
|
35
|
+
tested by hand. The published OpenAPI document is used only by
|
|
36
|
+
`npm run drift:check` to compare the 19 named endpoints this connection calls.
|
|
37
|
+
|
|
38
|
+
## Configuration
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
vercel("hosting", {
|
|
42
|
+
title: "Production hosting",
|
|
43
|
+
authScope: "shared",
|
|
44
|
+
purpose: "Customer-facing sites owned by Platform",
|
|
45
|
+
teamId: "team_...",
|
|
46
|
+
defaultPageSize: 20,
|
|
47
|
+
instructions: "Never promote the docs project from this connection.",
|
|
48
|
+
maxResultBytes: 512_000,
|
|
49
|
+
callAdmission: {
|
|
50
|
+
rules: [{ maxConcurrency: 6 }],
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`purpose` is required and blank text throws at construction. `teamId` is a
|
|
56
|
+
default, not a hidden lock. Named account-scoped tools accept a `teamId`
|
|
57
|
+
override; pass `null` to target the token owner's personal account explicitly.
|
|
58
|
+
Without a configured default or an override, Vercel uses that personal account.
|
|
59
|
+
`list_teams` returns the ids needed to reach a team.
|
|
60
|
+
|
|
61
|
+
`defaultPageSize` defaults to 20 and must be a whole number from 1 through 100.
|
|
62
|
+
Vercel meters endpoints separately, so the connection invents no global request
|
|
63
|
+
budget. A deployment may supply `callAdmission` when it has its own concurrency
|
|
64
|
+
or call-rate requirement.
|
|
65
|
+
|
|
66
|
+
`baseUrl` exists for a test double or an HTTPS proxy. The guarded transport
|
|
67
|
+
confines every path under that base, refuses redirects, prevents request headers
|
|
68
|
+
from replacing `Authorization`, passes `ctx.signal`, and stops reading at 8 MiB.
|
|
69
|
+
The runtime-log read also returns at most 500 rows and stops a stream that stays
|
|
70
|
+
open past 10 seconds. HTTP is accepted only for a loopback test double.
|
|
71
|
+
|
|
72
|
+
## Named tools
|
|
73
|
+
|
|
74
|
+
| Tool | What it does |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `list_teams` | Lists teams the token can reach. |
|
|
77
|
+
| `list_projects` | Searches or lists lean project summaries. |
|
|
78
|
+
| `get_project` | Reads build settings, Git identity, and the production deployment. |
|
|
79
|
+
| `list_deployments` | Filters deployments by project, target, state, branch, or SHA. |
|
|
80
|
+
| `get_deployment` | Reads one deployment by id or hostname. |
|
|
81
|
+
| `get_build_logs` | Reads at most 1,000 existing build events with live following disabled. |
|
|
82
|
+
| `get_runtime_logs` | Reads a 1–500 row runtime-log snapshot and stops a stream open past 10 seconds. |
|
|
83
|
+
| `list_project_domains` | Lists verification, redirect, branch, and custom-environment state. |
|
|
84
|
+
| `add_project_domain` | Adds a regular, redirect, branch, or custom-environment domain. |
|
|
85
|
+
| `verify_project_domain` | Rechecks a pending domain after its DNS challenge is complete. |
|
|
86
|
+
| `remove_project_domain` | Removes a project domain, optionally with domains redirecting to it. |
|
|
87
|
+
| `list_project_env_vars` | Lists metadata without asking Vercel to decrypt values. |
|
|
88
|
+
| `upsert_project_env_var` | Creates or replaces one variable. |
|
|
89
|
+
| `update_project_env_var` | Patches one variable by its id. |
|
|
90
|
+
| `delete_project_env_var` | Removes one variable from future deployments. |
|
|
91
|
+
| `promote_deployment` | Promotes an existing build to production without rebuilding. |
|
|
92
|
+
| `cancel_deployment` | Cancels work that is queued, initializing, or building. |
|
|
93
|
+
| `delete_deployment` | Permanently removes a deployment and its URL. |
|
|
94
|
+
|
|
95
|
+
Every read returns a lean projection by default. Projects drop security,
|
|
96
|
+
billing, and presentation settings. Deployments keep state, target, timestamps,
|
|
97
|
+
creator, and Git identity. Domain reads keep the verification challenge because
|
|
98
|
+
dropping it would make an unverified result unusable. The project, deployment,
|
|
99
|
+
domain, and build-log reads accept `raw: true` when a Vercel field omitted by
|
|
100
|
+
the projection matters.
|
|
101
|
+
|
|
102
|
+
`raw: true` preserves unprojected list items while keeping the named tool's
|
|
103
|
+
declared envelope: list calls still return their item key and `page`, and build
|
|
104
|
+
logs still return `{ events }`.
|
|
105
|
+
|
|
106
|
+
## Environment values are write-only here
|
|
107
|
+
|
|
108
|
+
`list_project_env_vars` sends `decrypt=false`, then drops `value` even if
|
|
109
|
+
Vercel returns one anyway. It returns the key, id, storage type, visibility,
|
|
110
|
+
targets, branch, custom-environment ids, comment, and timestamps. The create and
|
|
111
|
+
update tools accept a value as input, but their result projection drops it too.
|
|
112
|
+
|
|
113
|
+
That boundary is deliberate. An agent can audit placement and make a requested
|
|
114
|
+
change without filling its context with database URLs or API keys. Sensitive
|
|
115
|
+
values cannot be read back from Vercel in any case. The generic
|
|
116
|
+
`vercel_api_get` hatch returns the endpoint's untouched response, so a caller
|
|
117
|
+
that deliberately requests an endpoint capable of decrypting a non-sensitive
|
|
118
|
+
value has asked to cross the named tool's safer boundary.
|
|
119
|
+
|
|
120
|
+
Environment changes affect future deployments. They do not rewrite a value
|
|
121
|
+
already embedded in an existing deployment, and none of the environment tools
|
|
122
|
+
triggers a deployment on its own.
|
|
123
|
+
|
|
124
|
+
## REST hatches
|
|
125
|
+
|
|
126
|
+
Vercel's API is too large and changes too often for every operation to deserve
|
|
127
|
+
a permanent named tool.
|
|
128
|
+
|
|
129
|
+
- `vercel_api_get` accepts only GET and is explicitly read-only.
|
|
130
|
+
- `vercel_api_mutate` accepts JSON POST, PUT, PATCH, and DELETE. It always
|
|
131
|
+
crosses `call_destructive_tool`.
|
|
132
|
+
- `vercel_api_upload` accepts POST or PUT with exactly one explicit UTF-8 or
|
|
133
|
+
base64 body. It also crosses `call_destructive_tool`.
|
|
134
|
+
|
|
135
|
+
All three take a path beginning with `/` and including Vercel's version, such
|
|
136
|
+
as `/v1/edge-config`. Query parameters are name/value rows rather than a string
|
|
137
|
+
to parse. They use the configured default team unless the caller passes
|
|
138
|
+
`personalAccount: true`; that flag cannot be combined with a `teamId` or `slug`
|
|
139
|
+
query row. The upload hatch accepts endpoint-specific headers such as a digest,
|
|
140
|
+
but refuses credential, cookie, host, content-type, content-length, and
|
|
141
|
+
transfer-encoding headers. It reads no local file. The caller supplies the
|
|
142
|
+
bytes, content type, and any checksum the endpoint requires.
|
|
143
|
+
|
|
144
|
+
Use a named tool when one exists. A named tool wins on argument validation,
|
|
145
|
+
result size, or safety routing. The hatch is for products such as Edge Config,
|
|
146
|
+
feature flags, drains, checks, security, and team settings that are not worth a
|
|
147
|
+
large permanent catalog.
|
|
148
|
+
|
|
149
|
+
## Pagination
|
|
150
|
+
|
|
151
|
+
The four list families expose one connector-wide contract:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
{
|
|
155
|
+
items: [],
|
|
156
|
+
page: { hasMore: true, nextCursor: "opaque" }
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The item key is `teams`, `projects`, `deployments`, or `domains`. Pass
|
|
161
|
+
`nextCursor` back as `cursor` unchanged. Vercel uses different parameter names
|
|
162
|
+
and cursor types behind the four endpoints. The connector owns that mapping so
|
|
163
|
+
programs do not parse timestamps or branch on provider-specific pagination.
|
|
164
|
+
|
|
165
|
+
Environment-variable listing has no pagination in Vercel's published contract
|
|
166
|
+
and returns `{ variables }` without a false page object.
|
|
167
|
+
|
|
168
|
+
## Typed failures
|
|
169
|
+
|
|
170
|
+
- HTTP 401 and 403 become `auth_required`. Vercel uses 403 both for a bad token
|
|
171
|
+
and for a token outside the requested team or operation scope.
|
|
172
|
+
- HTTP 404 becomes `not_found`. Re-list the owning project, deployment, domain,
|
|
173
|
+
or environment variable before using the id again.
|
|
174
|
+
- HTTP 400, 409, and 422 become `invalid_args`.
|
|
175
|
+
- HTTP 429 becomes `rate_limited`. `Retry-After` wins; otherwise the connector
|
|
176
|
+
derives the delay from `X-RateLimit-Reset`.
|
|
177
|
+
- HTTP 5xx becomes `unavailable`.
|
|
178
|
+
|
|
179
|
+
The error text keeps Vercel's error code and message. It never parses prose to
|
|
180
|
+
invent a class.
|
|
181
|
+
|
|
182
|
+
## No SDK on purpose
|
|
183
|
+
|
|
184
|
+
The connection imports only Connecta modules and Web APIs. `@vercel/sdk` is not
|
|
185
|
+
a dependency or optional peer. Direct fetch keeps the root Workers-safe, avoids
|
|
186
|
+
shipping the generated model graph, and lets the reviewed named operations and
|
|
187
|
+
the REST hatches share one guarded transport.
|
|
188
|
+
|
|
189
|
+
The trade is API drift, handled explicitly. `scripts/drift/vercel-endpoints.json`
|
|
190
|
+
records the method, versioned path, specification revision, and request/response
|
|
191
|
+
digest for every fixed endpoint. Before a release, `npm run drift:check` compares
|
|
192
|
+
those rows with Vercel's published OpenAPI document at
|
|
193
|
+
`https://openapi.vercel.sh/`. The hatches are intentionally absent from that
|
|
194
|
+
list because their endpoint is chosen by deployment code at call time.
|
package/ethos.md
CHANGED
|
@@ -8,8 +8,8 @@ preserve. A contradiction needs a design decision, not a drive-by edit.
|
|
|
8
8
|
- **One MCP endpoint, one programmable surface.** Every integration you chose
|
|
9
9
|
sits behind a capability catalog that agents reach by writing JavaScript,
|
|
10
10
|
ringed by a few explicit tools for the boundaries code must not cross.
|
|
11
|
-
- **A deployment is
|
|
12
|
-
|
|
11
|
+
- **A deployment is config-as-code.** One tenant and connector set; principals
|
|
12
|
+
receive config-derived views.
|
|
13
13
|
- **Curated when available, open when not.** Prefer a maintained prebuilt
|
|
14
14
|
connection; `remoteMcp()` and `api()` stay first-class for everything else.
|
|
15
15
|
Every path yields the same `Connector` with the same rules.
|
|
@@ -30,8 +30,8 @@ preserve. A contradiction needs a design decision, not a drive-by edit.
|
|
|
30
30
|
- **Not a platform.** No runtime registration, admin-editable capability,
|
|
31
31
|
policy engine, approvals, or pauses.
|
|
32
32
|
- **Not a schema ingester.** No OpenAPI or GraphQL → tools.
|
|
33
|
-
- **Not multi-tenant.** No
|
|
34
|
-
|
|
33
|
+
- **Not multi-tenant.** No accounts, groups, or sessions. Inbound auth owns
|
|
34
|
+
identity; personal state stays within one tenant.
|
|
35
35
|
- **Not stateful.** No protocol sessions, no server push; scope resolves per
|
|
36
36
|
request.
|
|
37
37
|
- **Not a nanny.** Credentials fail loudly at use; nothing probes one.
|
|
@@ -47,7 +47,7 @@ CHANGELOG, not here.
|
|
|
47
47
|
| Decision | Verdict | Why |
|
|
48
48
|
| --- | --- | --- |
|
|
49
49
|
| OpenAPI / GraphQL ingestion | refused | the disease is a tool nobody chose — a document authored it; hand-written literals, even through a shared factory, are still authorship |
|
|
50
|
-
| Multi-tenancy / account model | refused | one deployment per tenant;
|
|
50
|
+
| Multi-tenancy / account model | refused | one deployment per tenant; inbound auth owns identity |
|
|
51
51
|
| Policy engine, approvals, pauses | refused | the host asks the human; connecta only annotates |
|
|
52
52
|
| Runtime connector registration | refused | config-as-code is the security model |
|
|
53
53
|
| Provider registry / marketplace | refused | prebuilt connections are imports; discovery happens in docs ([#297](https://github.com/zackbart/connecta/issues/297)) |
|
|
@@ -67,7 +67,7 @@ CHANGELOG, not here.
|
|
|
67
67
|
| Legacy embedded `UIResource` delivery | refused | superseded upstream, rendered by no client we face ([#266](https://github.com/zackbart/connecta/issues/266)) |
|
|
68
68
|
| Effect as the core effect system | refused | −4% of the core for +75 KB gzip and a second async paradigm; re-measure at v4 stable ([#470](https://github.com/zackbart/connecta/issues/470)) |
|
|
69
69
|
| Shared bounded queue under both admission controllers | refused | built and measured −17 lines for a hook-parameterised abstraction ([#453](https://github.com/zackbart/connecta/issues/453)) |
|
|
70
|
-
|
|
|
70
|
+
| Caller-selected toolkits | removed | only config may derive an identity's connector view ([#178](https://github.com/zackbart/connecta/issues/178)) |
|
|
71
71
|
| Proactive credential liveness | removed | fail-at-use is enough ([#179](https://github.com/zackbart/connecta/issues/179)) |
|
|
72
72
|
| Classic (executor-free) surface | removed | an executor is mandatory ([#273](https://github.com/zackbart/connecta/issues/273)) |
|
|
73
73
|
| Per-result lexical query coverage | removed | did not earn its response bytes in a precommitted gate ([#323](https://github.com/zackbart/connecta/issues/323)) |
|
|
@@ -91,10 +91,10 @@ Breaking one is a design change wearing a disguise.
|
|
|
91
91
|
- **A downstream catalog is complete or it is a failure.** A partial catalog is never cached, persisted, or served.
|
|
92
92
|
- **Activity is payload-free by construction.** The event type has nowhere to put arguments, results, code, or raw errors.
|
|
93
93
|
- **An observed shape is never a declaration.** Names and broad types only, labeled, and gone behind any declared schema.
|
|
94
|
-
- **Credentials never leave the host.** Encrypted at rest, readable only by the owning connector, rendered by nothing.
|
|
94
|
+
- **Credentials never leave the host.** Encrypted at rest, readable only by the owning connector and, for personal auth, its owning principal; rendered by nothing.
|
|
95
95
|
- **Import-graph purity.** Nothing reachable from the root entry imports a `node:` builtin.
|
|
96
96
|
- **The published surface is a boundary.** Heavyweight or platform-bound code goes behind an optional-peer subpath.
|
|
97
|
-
- **
|
|
97
|
+
- **Human routes manage auth, never capability.** Signed-in humans manage auth for visible connectors; operators also manage tokens and global activity.
|
|
98
98
|
- **Structural mistakes throw at construction.** Booting into the wrong shape is worse than not booting.
|
|
99
99
|
|
|
100
100
|
Connecta began as a radical simplification of
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Working on this Connecta Worker deployment
|
|
2
|
+
|
|
3
|
+
This repository is deployment configuration, not a copy of Connecta itself.
|
|
4
|
+
|
|
5
|
+
- Edit `src/index.ts` for connectors, authentication, storage, and public URL.
|
|
6
|
+
- Keep `cloudflareAccessAuth()` as the inbound auth provider. Cloudflare Access
|
|
7
|
+
authenticates the request before the Worker runs; do not add JWT parsing or a
|
|
8
|
+
second Worker-side identity gate.
|
|
9
|
+
- Attach Access to the Worker itself, not only its hostname. Enable Managed
|
|
10
|
+
OAuth and Dynamic Client Registration on that Access application.
|
|
11
|
+
- Managed OAuth's **Allowed redirect URIs** must contain all three entries
|
|
12
|
+
below. This is application configuration under
|
|
13
|
+
`oauth_configuration.dynamic_client_registration.allowed_uris`, not an
|
|
14
|
+
Access Allow policy:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
https://claude.ai/api/mcp/auth_callback
|
|
18
|
+
https://chatgpt.com/connector_platform_oauth_redirect
|
|
19
|
+
https://chatgpt.com/connector/oauth/*
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The first is Claude's hosted MCP callback. The two ChatGPT entries cover its
|
|
23
|
+
stable callback and its callback-id form. An empty allowlist lets Access
|
|
24
|
+
discovery work but makes client registration fail with `redirect_uri` not
|
|
25
|
+
allowed. If a client presents a different callback, copy that exact URI from
|
|
26
|
+
its registration attempt and add the narrowest matching entry rather than
|
|
27
|
+
broadening the allowlist to an entire origin.
|
|
28
|
+
- Keep `new DynamicWorkerExecutor({ loader: env.LOADER })` loader-only. Do not
|
|
29
|
+
add bindings, modules, or outbound access to generated code.
|
|
30
|
+
- Keep credentials in Worker secrets. Never commit credential values, Access
|
|
31
|
+
service-token secrets, or `CREDENTIAL_ENCRYPTION_KEY`.
|
|
32
|
+
- Add application logic only inside deliberate `api()` connector handlers.
|
|
33
|
+
Do not copy or modify Connecta package internals here.
|
|
34
|
+
- Prefer `api()` when the agent must see an exact reviewed capability set;
|
|
35
|
+
`remoteMcp()` follows the downstream server's evolving tool catalog.
|
|
36
|
+
- Use Access service credentials for `connecta doctor` and unattended clients.
|
|
37
|
+
A `cta_` token or static Connecta bearer cannot cross the Access edge alone.
|
|
38
|
+
- Run the repository's `npm run check:examples` after configuration changes.
|
|
39
|
+
After deployment, connect both Claude and ChatGPT to `<PUBLIC_URL>/mcp` and
|
|
40
|
+
complete their browser authorization flows before calling setup complete.
|
|
41
|
+
|
|
42
|
+
Do not add alternate entrypoints, policy layers, generated connector catalogs,
|
|
43
|
+
or runtime connector registration. Keep the deployment small enough to review
|
|
44
|
+
as configuration.
|