@zackbart/connecta 0.21.1 → 0.22.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.
Files changed (66) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +7 -0
  3. package/dist/access-tokens.d.ts +2 -2
  4. package/dist/access-tokens.js +14 -2
  5. package/dist/auth/downstream-oauth.d.ts +65 -2
  6. package/dist/auth/downstream-oauth.js +408 -20
  7. package/dist/connectors/api.d.ts +2 -0
  8. package/dist/connectors/api.js +1 -0
  9. package/dist/connectors/remote-mcp.d.ts +2 -0
  10. package/dist/connectors/remote-mcp.js +14 -4
  11. package/dist/credentials.d.ts +6 -6
  12. package/dist/credentials.js +25 -21
  13. package/dist/executors/quickjs.js +4 -0
  14. package/dist/identity.d.ts +4 -0
  15. package/dist/identity.js +17 -0
  16. package/dist/index.d.ts +16 -2
  17. package/dist/index.js +6 -1
  18. package/dist/meta-tools.js +7 -2
  19. package/dist/operator-ui/generated.js +1 -1
  20. package/dist/operator-ui/model.d.ts +4 -2
  21. package/dist/operator-ui/view.js +1 -1
  22. package/dist/providers/cloudflare.d.ts +2 -0
  23. package/dist/providers/cloudflare.js +1 -0
  24. package/dist/providers/linear.d.ts +2 -0
  25. package/dist/providers/linear.js +1 -0
  26. package/dist/providers/mixpanel.d.ts +2 -0
  27. package/dist/providers/mixpanel.js +1 -0
  28. package/dist/providers/notion.d.ts +2 -0
  29. package/dist/providers/notion.js +1 -0
  30. package/dist/providers/revenuecat.d.ts +2 -0
  31. package/dist/providers/revenuecat.js +1 -0
  32. package/dist/providers/stripe.d.ts +2 -0
  33. package/dist/providers/stripe.js +1 -0
  34. package/dist/registry.d.ts +25 -0
  35. package/dist/registry.js +200 -4
  36. package/dist/routes/access-tokens.js +2 -2
  37. package/dist/routes/activity.js +4 -1
  38. package/dist/routes/credentials.js +31 -12
  39. package/dist/routes/mcp.js +17 -2
  40. package/dist/routes/oauth.js +55 -11
  41. package/dist/routes/shared.d.ts +20 -4
  42. package/dist/routes/shared.js +92 -24
  43. package/dist/routes/ui.js +32 -13
  44. package/dist/types.d.ts +28 -2
  45. package/dist/ui.d.ts +3 -3
  46. package/dist/ui.js +18 -5
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/documentation/architecture.md +31 -8
  50. package/documentation/auth.md +90 -10
  51. package/documentation/code-mode.md +4 -4
  52. package/documentation/connectors.md +13 -0
  53. package/documentation/meta-tools.md +4 -3
  54. package/documentation/operations.md +5 -3
  55. package/documentation/operator-ui.md +13 -4
  56. package/documentation/request-admission.md +2 -1
  57. package/documentation/storage-and-credentials.md +77 -4
  58. package/documentation/upgrading.md +38 -7
  59. package/ethos.md +8 -8
  60. package/examples/worker/AGENTS.md +44 -0
  61. package/examples/worker/README.md +63 -14
  62. package/examples/worker/src/index.ts +26 -22
  63. package/package.json +1 -1
  64. package/templates/node/README.md +7 -0
  65. package/templates/node/package.json +1 -1
  66. package/templates/node/src/index.ts +13 -4
@@ -46,15 +46,56 @@ wrangler deploy
46
46
  Cloudflare Access to the Worker itself (the API destination type is `worker`,
47
47
  not a hostname application) and choose the account, email-domain, or
48
48
  advanced Zero Trust policy that owns admission. Enable **Managed OAuth** on
49
- that Access application for interactive MCP clients. Access then serves OAuth
50
- discovery and turns the client's opaque token into the trusted `ctx.access`
51
- identity connecta reads. A cron job or CI client uses an Access service token
52
- instead.
49
+ that Access application for interactive MCP clients, turn on Dynamic Client
50
+ Registration, and add these three entries under **Allowed redirect URIs**:
51
+
52
+ ```text
53
+ https://claude.ai/api/mcp/auth_callback
54
+ https://chatgpt.com/connector_platform_oauth_redirect
55
+ https://chatgpt.com/connector/oauth/*
56
+ ```
57
+
58
+ The Claude entry is its fixed hosted-MCP callback. ChatGPT may register either
59
+ its stable callback or a callback-id URL, so both forms are intentional. These
60
+ are Managed OAuth application settings, represented by
61
+ `oauth_configuration.dynamic_client_registration.allowed_uris` in the Access
62
+ API; they do not belong in the Access Allow policy that decides who may sign
63
+ in. Leaving the list empty is a footgun: discovery still works, then Dynamic
64
+ Client Registration fails because the callback is not allowed. If either
65
+ client presents a new redirect URI, copy that exact value from the registration
66
+ attempt and add the narrowest matching entry rather than allowing its entire
67
+ origin.
68
+
69
+ Access then serves OAuth discovery and turns the client's opaque token into the
70
+ trusted `ctx.access` identity connecta reads. A cron job or CI client uses an
71
+ Access service token instead.
72
+
73
+ Through the API, the relevant part of the application is:
74
+
75
+ ```json
76
+ {
77
+ "oauth_configuration": {
78
+ "enabled": true,
79
+ "dynamic_client_registration": {
80
+ "enabled": true,
81
+ "allowed_uris": [
82
+ "https://claude.ai/api/mcp/auth_callback",
83
+ "https://chatgpt.com/connector_platform_oauth_redirect",
84
+ "https://chatgpt.com/connector/oauth/*"
85
+ ]
86
+ }
87
+ }
88
+ }
89
+ ```
53
90
 
54
91
  Cloudflare's [Worker Access guide](https://developers.cloudflare.com/workers/configuration/cloudflare-access/)
55
92
  owns the dashboard/API steps; its [Managed OAuth guide](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/managed-oauth/)
56
93
  owns client registration, redirect allowlists, and token lifetimes.
57
94
 
95
+ [`AGENTS.md`](./AGENTS.md) repeats the callback invariant for coding agents
96
+ working in a copied deployment. Do not remove the entries there when changing
97
+ the Access policy or application.
98
+
58
99
  The checked-in `access.dev` block gives `wrangler dev` a local operator
59
100
  identity. Remove the block to test the missing-Access refusal. It has no effect
60
101
  on a deployed Worker's production identity.
@@ -75,13 +116,9 @@ connecta's manifest but never installed with it, and published as
75
116
  one outside and npm says so at install time instead of leaving a Worker to
76
117
  discover the skew in production ([#376](https://github.com/zackbart/connecta/issues/376)).
77
118
 
78
- `cloudflareAccessAuth()` has no dependency of its own. A deployment keeping
79
- Clerk for rollback still installs `@clerk/backend` and keeps the commented
80
- provider shape in `src/index.ts` until the migration is verified.
81
-
82
- ```sh
83
- npm install @clerk/backend # migration window only
84
- ```
119
+ `cloudflareAccessAuth()` has no dependency of its own. This Worker example has
120
+ no Clerk import, secret, package, or fallback provider. Docker deployments keep
121
+ the Clerk path in the Node template.
85
122
 
86
123
  Then point an MCP client at `<PUBLIC_URL>/mcp`, and open `<PUBLIC_URL>/` for
87
124
  Connections. Credentials is at `/credentials`, named MCP access tokens are at
@@ -96,9 +133,21 @@ as deployed; the fourth needs a database, so it is commented in place.
96
133
 
97
134
  **Operator sign-in** is the `cloudflareAccessAuth()` entry in `src/index.ts`.
98
135
  Access authenticates before the Worker runs. A human Access identity can use
99
- MCP and operator pages; a service-token identity can use MCP but cannot write a
100
- credential, run downstream OAuth, or issue a connecta token. Narrow admission
101
- in the Access policy rather than repeating email domains or groups in code.
136
+ MCP and human-management pages; a service-token identity can use MCP but cannot
137
+ write a credential, run downstream OAuth, or issue a connecta token. Cloudflare
138
+ still owns the outer application admission policy, but Connecta's user roster,
139
+ connector access, and deployment roles stay in `src/index.ts`.
140
+
141
+ **Several users** need no second auth system or Connecta account dashboard.
142
+ Uncomment the `identity` block in `src/index.ts` to derive connector ids and
143
+ deployment-operator membership from the Access principal. Connectors remain
144
+ visible to everyone and every human remains an operator when that block is
145
+ absent. A signed-in human may edit auth for every connector their view includes.
146
+ Add `authScope: "personal"` when each user should connect a different downstream
147
+ account; leave it shared only when any user with connector access may rotate the
148
+ deployment-wide grant. Static headers stay shared because their value lives in
149
+ deployment configuration. See [inbound identity](../../documentation/auth.md#principals-visibility-and-operators)
150
+ for the resolver contract.
102
151
 
103
152
  **The credential vault** is `credentials: { encryptionKey: … }`, backed by the
104
153
  same KV namespace as everything else and encrypted with the
@@ -17,15 +17,17 @@
17
17
  * 1. `npm install` in the connecta package root (../../ from here) so the
18
18
  * package import and wrangler resolve. A copy in its own repository
19
19
  * installs `@zackbart/connecta @cloudflare/codemode` instead. Codemode is
20
- * an optional peer; a migrating deployment also keeps `@clerk/backend`
21
- * until it removes the commented rollback provider below.
20
+ * an optional peer.
22
21
  * 2. Create a KV namespace and put its id in wrangler.jsonc under `kv_namespaces`.
23
22
  * 3. Set secrets:
24
23
  * wrangler secret put DOWNSTREAM_TOKEN
25
24
  * wrangler secret put CREDENTIAL_ENCRYPTION_KEY
26
25
  * and PUBLIC_URL as a plain var in wrangler.jsonc.
27
- * 4. Attach Cloudflare Access to this Worker. Enable Managed OAuth on the
28
- * Access application for interactive MCP clients.
26
+ * 4. Attach Cloudflare Access to this Worker. Enable Managed OAuth and
27
+ * Dynamic Client Registration. Its Allowed redirect URIs must include
28
+ * Claude's https://claude.ai/api/mcp/auth_callback plus ChatGPT's
29
+ * https://chatgpt.com/connector_platform_oauth_redirect and
30
+ * https://chatgpt.com/connector/oauth/* forms (see ../AGENTS.md).
29
31
  * 5. Use the Workers Paid plan required by the `worker_loaders` binding.
30
32
  * 6. `wrangler deploy` from this folder (examples/worker), where wrangler.jsonc
31
33
  * lives. Point your MCP client at `<PUBLIC_URL>/mcp`.
@@ -37,17 +39,12 @@ import {
37
39
  remoteMcp,
38
40
  } from "@zackbart/connecta";
39
41
  import { cloudflareAccessAuth } from "@zackbart/connecta/auth/cloudflare-access";
40
- // Rollback for a deployment migrating from Clerk:
41
- // import { clerkAuth } from "@zackbart/connecta/auth/clerk";
42
42
  import { cloudflareKvStorage } from "./cloudflare-kv.js";
43
43
  // Activity history, off by default because it needs a D1 database.
44
44
  // import { d1ActivityStore } from "./d1-activity.js";
45
45
 
46
46
  interface Env {
47
47
  CONNECTA_KV: KVNamespace;
48
- // Keep these during a Clerk migration until Access has been verified:
49
- // CLERK_PUBLISHABLE_KEY: string;
50
- // CLERK_SECRET_KEY: string;
51
48
  /**
52
49
  * Base64 32-byte AES key encrypting operator-managed credentials in KV.
53
50
  * Unset means no vault: /credentials stays read-only and connecta says so at
@@ -75,20 +72,24 @@ function build(env: Env) {
75
72
  // operator pages; a service token may use MCP but cannot mutate operator
76
73
  // state. Neither path asks connecta to parse a JWT.
77
74
  cloudflareAccessAuth(),
78
- // Leave the previous Clerk provider below this entry during migration.
79
- // It is a rollback path until Worker-level Access is detached; Access
80
- // itself decides whether a request reaches this array.
81
- // clerkAuth({
82
- // publishableKey: env.CLERK_PUBLISHABLE_KEY,
83
- // secretKey: env.CLERK_SECRET_KEY,
84
- // publicUrl: env.PUBLIC_URL,
85
- // allowedDomains: ["acme.com"],
86
- // }),
87
75
  ],
88
- // Connectors that declare a `credential` slot become editable at
89
- // /credentials, encrypted with this key before anything reaches KV. A
90
- // saved replacement takes effect on the next call no redeploy, and no
91
- // liveness probe: credentials fail at use.
76
+ // Optional code-owned roster. Access proves the identity; connecta derives
77
+ // connector visibility and deployment-operator status from the stable id
78
+ // it supplies. A signed-in human may manage auth for every connector this
79
+ // view includes. Omit the block to keep every connector visible and every
80
+ // human a deployment operator.
81
+ // identity: {
82
+ // connectorAccess: ({ principal }) =>
83
+ // principal?.id === "ACCESS_USER_UUID"
84
+ // ? ["notion", "echo"]
85
+ // : ["echo"],
86
+ // operatorAccess: ({ id }) => id === "ACCESS_USER_UUID",
87
+ // },
88
+ // Connectors that declare a `credential` slot become editable by every
89
+ // signed-in human who can see that connector at /credentials, encrypted
90
+ // with this key before anything reaches KV. A saved replacement takes
91
+ // effect on the next call — no redeploy, and no liveness probe:
92
+ // credentials fail at use.
92
93
  //
93
94
  // The key is the vault, not the page: /credentials is a list of connector
94
95
  // slots, so it stays hidden until a connector declares one. Neither
@@ -123,6 +124,9 @@ function build(env: Env) {
123
124
  // type: "credential",
124
125
  // credential: { label: "Notion internal integration token" },
125
126
  },
127
+ // Use `authScope: "personal"` with OAuth or credential auth when each
128
+ // Access user connects their own downstream account. Literal headers
129
+ // are deployment-owned and cannot be personal.
126
130
  }),
127
131
  api("echo", {
128
132
  description: "Echo — text transforms",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of seven meta-tools.",
@@ -68,6 +68,13 @@ import, the two `process.env.CLERK_*` reads, and the `clerkAuth({ … })` entry
68
68
  Applications → DCR) if MCP clients should sign in through it too, and set
69
69
  `PUBLIC_URL` first — Clerk redirects back to it.
70
70
 
71
+ Clerk remains the identity provider when several people share this Docker
72
+ deployment. Uncomment the `identity` block in `src/index.ts` to give each Clerk
73
+ principal a config-derived connector view and to choose operators. Add
74
+ `authScope: "personal"` to a connector when each person should supply their own
75
+ credential or finish their own downstream OAuth flow. Without those options,
76
+ all connectors and auth stay shared exactly as before.
77
+
71
78
  **2. Credential vault.** Uncomment `credentials` and set
72
79
  `CONNECTA_CREDENTIAL_KEY` to a base64 32-byte AES key:
73
80
 
@@ -15,7 +15,7 @@
15
15
  "typecheck": "tsc --noEmit"
16
16
  },
17
17
  "dependencies": {
18
- "@zackbart/connecta": "0.21.1",
18
+ "@zackbart/connecta": "0.22.0",
19
19
  "quickjs-emscripten": "0.32.0"
20
20
  },
21
21
  "devDependencies": {
@@ -42,10 +42,10 @@ const stateFile = process.env.CONNECTA_STATE_FILE || "./.connecta-state.json";
42
42
  const publicUrl = process.env.PUBLIC_URL || `http://localhost:${port}`;
43
43
 
44
44
  // Operator sign-in. A bearer token is a client key: it may call tools and read
45
- // connector status, but only a Clerk-authenticated operator may write a
46
- // credential or issue an access token. Without this block the operator pages
47
- // still render — an operator pastes the bearer to read them — and Credentials
48
- // and Tokens stay read-only.
45
+ // connector status, but only a Clerk-authenticated human may write a visible
46
+ // connector's credential, and only an operator may issue an access token.
47
+ // Without this block the operator pages still render — an operator pastes the
48
+ // bearer to read them — and Credentials and Tokens stay read-only.
49
49
  // const clerkPublishableKey = process.env.CLERK_PUBLISHABLE_KEY;
50
50
  // const clerkSecretKey = process.env.CLERK_SECRET_KEY;
51
51
  // if (!clerkPublishableKey || !clerkSecretKey) {
@@ -67,6 +67,15 @@ const connecta = createConnecta({
67
67
  // // allowedDomains: ["acme.com"],
68
68
  // }),
69
69
  ],
70
+ // Optional member/operator split for Clerk-backed Docker deployments.
71
+ // Connector access is derived from the authenticated identity and cannot be
72
+ // selected by an MCP argument. Omit this block for the legacy all-visible,
73
+ // all-interactive-users-are-operators behavior.
74
+ // identity: {
75
+ // connectorAccess: ({ principal }) =>
76
+ // principal?.id === "user_admin" ? "all" : ["time"],
77
+ // operatorAccess: ({ id }) => id === "user_admin",
78
+ // },
70
79
  publicUrl,
71
80
  // Required: model-written programs run in a bounded QuickJS child.
72
81
  executor: quickJsExecutor(),