@zackbart/connecta 0.18.0 → 0.18.2

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 CHANGED
@@ -2,6 +2,114 @@
2
2
 
3
3
  All notable changes to this package are documented here.
4
4
 
5
+ ## 0.18.2 — 2026-08-18
6
+
7
+ This patch closes the gap the RevenueCat rollout exposed on the same day 0.18.1
8
+ shipped: a hosted-MCP connection could authenticate with OAuth from the
9
+ Connections page or with a header literal baked into the deployment file, but
10
+ never with a key an operator pastes on `/credentials` the way every `api()`
11
+ connector already can. Now it can. Nothing breaks, no constructor changes, and a
12
+ deployment that keeps its static keys in runtime secrets can ignore this release.
13
+ Two things are worth reading before adopting the new shape: the operator vault
14
+ (`credentials.encryptionKey`) must be configured, and a key pasted with a
15
+ wrapped newline is refused before it is framed rather than echoed back by the
16
+ runtime that rejects it.
17
+
18
+ ### Added
19
+
20
+ - **`auth: { type: "credential" }` for `remoteMcp()` and every maintained
21
+ hosted connection.** The connector declares an operator credential slot on
22
+ `/credentials` (label and guidance from the provider, overridable), reads the
23
+ stored value on every request before trusting a cached client, and frames it
24
+ as `Authorization: Bearer <value>` by default — `header` and `scheme` are
25
+ configurable, `scheme: null` sends the value bare, and a scheme ending in
26
+ `Basic` base64-encodes it, which is how Mixpanel's documented
27
+ `Bearer Basic <base64(user:secret)>` is spelled. A missing or empty value is
28
+ `auth_required`, and `authorize_connector` returns the operator handoff to
29
+ `/credentials`; a deployment without a vault warns at construction and answers
30
+ `recovery: "unavailable"` at use. Rotation on `/credentials` takes effect on
31
+ the next call: a SHA-256 digest of the connected value is compared per
32
+ request (including against an in-flight connect) and a differing digest
33
+ closes the old client. The `/credentials` Test action connects with the
34
+ candidate and counts the catalog, and only when an operator presses it — the
35
+ #179 refusal of proactive probing stands, now written into P10. Provider
36
+ defaults: Stripe (requires `mode`, refuses `connectedAccount`), Linear
37
+ (Bearer, per Linear's MCP docs), Mixpanel (`Bearer Basic`, service account),
38
+ RevenueCat (single-project branch, "API v2 secret key") (#439).
39
+
40
+ ### Changed
41
+
42
+ - **A stored credential never reaches an agent or operator surface, even
43
+ malformed.** The value is refused before framing if it carries a control
44
+ character (the wrapped-newline paste), with a message that names the problem
45
+ and not the value; behind that, any error whose message or `cause` chain
46
+ quotes the raw or framed value is replaced whole rather than redacted. Both
47
+ layers are tested independently (#439).
48
+
49
+ - **Provider conventions P9 and P10 say what is now true.** P9 names both
50
+ headless shapes and requires the framing to match the provider's published
51
+ MCP contract; P10 is retitled to allow an operator-requested credential test
52
+ while forbidding any unasked probe (#439).
53
+
54
+ - **Linear's headers example uses `Bearer`.** Linear's MCP server documents
55
+ `Authorization: Bearer <token>` for API keys; the bare form is its GraphQL
56
+ convention. Existing `headers` connectors are untouched (#439).
57
+
58
+ ## 0.18.1 — 2026-08-18
59
+
60
+ This patch is the response to one long investigation run against a live
61
+ deployment: a Mixpanel-and-Stripe user-journey reconstruction that rated the
62
+ connectors trustworthy and the ceremony too high. Nothing breaks and no
63
+ constructor changes. The two maintained guides now say the things that
64
+ investigation had to discover the hard way — which questions Insights cannot
65
+ answer, when Mixpanel's `false` is an absent property, which Stripe reads must
66
+ be projected inside the sandbox and why that is a privacy boundary as much as
67
+ a size one, and which fields Stripe search will not filter on. Beside them, a
68
+ new maintained RevenueCat connection lands, with the fact that decides its
69
+ deployment shape stated up front: a RevenueCat secret key reaches exactly one
70
+ project, so several projects are several connectors. A deployment that
71
+ constructs none of the three connections can ignore this release entirely.
72
+
73
+ ### Added
74
+
75
+ - **`revenuecat()` — a maintained RevenueCat hosted-MCP connection** at
76
+ `@zackbart/connecta/providers/revenuecat`. OAuth by default (account-scoped:
77
+ the guide requires `list-projects` first and carries `project_id` into every
78
+ project-scoped call, stopping on ambiguity); an API v2 secret key as the
79
+ headless alternative, which binds the connector to one project — the guide's
80
+ first line names it, and a second project is a second connector with its own
81
+ key. All 95 documented tools are classified from RevenueCat's tools reference
82
+ (50 read-only, 15 additive, 29 destructive); `render-paywall-screenshot`
83
+ carries no access column there and fails closed. Borderline verdicts are
84
+ argued beside the rows they decide: `create-webhook-integration`,
85
+ `grant-customer-entitlement`, and `assign-customer-offering` are destructive
86
+ on consequence, not on verb. RevenueCat publishes six per-domain rate limits
87
+ and one admission rule cannot honestly cover 95 tools, so `callAdmission`
88
+ stays with the operator and the guide states the numbers. The maintainer-run
89
+ drift check accepts `revenuecat` behind `CONNECTA_DRIFT_REVENUECAT_KEY`; no
90
+ schema digests are recorded until a maintainer runs `--record` against a live
91
+ project (#433).
92
+
93
+ ### Changed
94
+
95
+ - **The Mixpanel guide says what Insights cannot answer.** One analysis is one
96
+ `execute_code` program with one `Get-Query-Schema` fetch; per-user event
97
+ timelines and sequence questions are out of reach through `Run-Query`
98
+ (`Get-User-Replays-Data` covers one user only where session replay is on);
99
+ `false` on a boolean property may be an absent one and must be confirmed with
100
+ `List-Properties` before it becomes a signal; breakdown responses are
101
+ flattened one row per combination inside the program with `$overall`
102
+ dropped. Convention P7 gains the rendering-rule clause this rests on (#430).
103
+
104
+ - **The Stripe guide names the reads that must be projected and the fields
105
+ search cannot filter.** Any `stripe_api_read` list or `stripe_api_search`
106
+ that returns full objects belongs inside `execute_code`, projected before
107
+ `return` — the reduction keeps customer PII out of the transcript, not only
108
+ bytes. Charges search takes a documented field set with no `payment_intent`;
109
+ follow the PaymentIntent's `latest_charge` instead. The account → search →
110
+ details → read sequence is one program, and decline outcomes are `outcome`,
111
+ `failure_code`, `failure_message` on the charge (#431).
112
+
5
113
  ## 0.18.0 — 2026-08-13
6
114
 
7
115
  This minor release spends fewer tokens before the first call and answers
package/README.md CHANGED
@@ -2,133 +2,94 @@
2
2
 
3
3
  ![A monochrome clay Connecta hub joining many tools](https://raw.githubusercontent.com/zackbart/connecta/main/assets/connecta-clay-hero.png)
4
4
 
5
- One place for AI agents to connect to the tools you choose.
5
+ One MCP endpoint. The integrations you chose. Your agent reaches them by
6
+ writing code instead of loading a thousand tool definitions.
6
7
 
7
- Connecta gives an agent a single MCP endpoint instead of making it connect to
8
- every service separately. You decide which integrations are available and
9
- Connecta holds their credentials, and the agent mostly works by writing
10
- ordinary JavaScript that Connecta runs in a sandbox next to them — with a few
11
- explicit tools for the jobs a program is the wrong shape for, destructive calls
12
- among them.
8
+ ## The mental model
13
9
 
14
- ```mermaid
15
- flowchart LR
16
- Agent["AI agent"]
17
- Integrations["The integrations you chose"]
18
-
19
- subgraph Connecta["Connecta: one MCP endpoint; credentials stay here"]
20
- Sandbox["execute_code<br/>server-side sandbox"]
21
- Explicit["Explicit tools<br/>destructive calls, search, auth"]
22
- end
23
-
24
- Agent -->|"writes a program"| Sandbox
25
- Agent -->|"one deliberate call"| Explicit
26
- Sandbox --> Integrations
27
- Explicit --> Integrations
28
- ```
10
+ You ask your agent a question that touches a service — Linear, Stripe, an
11
+ internal API, anything you have connected. Here is what happens:
29
12
 
30
- ## Why Connecta
13
+ 1. The agent talks to one endpoint, yours, and sees seven tools. Always seven,
14
+ no matter how many services sit behind it.
15
+ 2. It writes a short JavaScript program. Connecta runs it in a sandbox next to
16
+ your integrations. The program can search for tools, call them, chain the
17
+ calls, and shape the result.
18
+ 3. Only the answer comes back into the agent's context — not raw pages of
19
+ API output.
20
+ 4. If the agent wants to change something — create, update, delete — it
21
+ cannot do that from a program. It makes one explicit call, and your MCP
22
+ client can put that call in front of you first.
31
23
 
32
- - **One connection.** Configure clients once, even as integrations change.
33
- - **Seven tools, not seven hundred.** A program can search the catalog, chain
34
- calls, and trim the results before the agent ever sees them — so nothing has
35
- to be loaded up front.
36
- - **Safer access.** Credentials stay server-side — the program never sees them
37
- — and consequential actions remain explicit and individual.
38
- - **Named client access.** A Clerk operator can issue and revoke one-time,
39
- hashed Bearer tokens for MCP clients that support header authentication.
40
- - **Your deployment.** Connecta runs on Node, Docker, or Cloudflare Workers,
41
- with configuration you can review and version.
24
+ Credentials never leave the server. The program never sees them, and neither
25
+ does the agent.
42
26
 
43
- ## Start here
27
+ ```mermaid
28
+ flowchart TB
29
+ Client["Your MCP client<br/>Claude, Cursor, …"]
44
30
 
45
- Create the prescribed Node deployment:
31
+ subgraph Connecta["Connecta one endpoint, seven tools, your credentials"]
32
+ Sandbox["execute_code<br/>the agent's program runs here<br/>read-only tools only"]
33
+ Explicit["call_destructive_tool<br/>one visible call per write<br/>your client can ask you first"]
34
+ end
46
35
 
47
- ```sh
48
- npx @zackbart/connecta init my-connecta
49
- cd my-connecta
50
- npm install
51
- CONNECTA_TOKEN=dev-token npm start
52
- ```
36
+ Integrations["The integrations you chose<br/>Linear · Stripe · Notion · your HTTP API · any MCP server"]
53
37
 
54
- Point an MCP client at `http://localhost:8787/mcp` with
55
- `Authorization: Bearer dev-token`. The generated project is deliberately small:
56
-
57
- ```text
58
- my-connecta/
59
- ├── src/index.ts # connectors, auth, storage, public URL
60
- ├── src/file-activity.ts # deployment-owned activity store, wired on request
61
- ├── package.json # exact Connecta and QuickJS versions
62
- ├── tsconfig.json
63
- ├── Dockerfile # the same source, containerized
64
- ├── docker-compose.yml # one service, state on a named volume
65
- ├── .dockerignore
66
- ├── .env.example
67
- ├── .gitignore
68
- ├── AGENTS.md
69
- ├── CLAUDE.md -> AGENTS.md
70
- └── README.md
38
+ Client -->|"one connection"| Sandbox
39
+ Client --> Explicit
40
+ Sandbox -->|"reads"| Integrations
41
+ Explicit -->|"writes"| Integrations
71
42
  ```
72
43
 
73
- The same project runs as a long-lived container without changing a line of it:
74
-
75
- ```sh
76
- cp .env.example .env # set CONNECTA_TOKEN
77
- docker compose up -d --build
44
+ This is the kind of thing the agent writes, not you:
45
+
46
+ ```js
47
+ async () => {
48
+ const { nodes } = await tracker.list_issues({ state: "started" });
49
+ const byOwner = {};
50
+ for (const issue of nodes) {
51
+ (byOwner[issue.assignee?.name ?? "unassigned"] ??= []).push(issue.identifier);
52
+ }
53
+ return byOwner;
54
+ }
78
55
  ```
79
56
 
80
- For an agent setting this up, the contract is:
81
-
82
- 1. Edit `src/index.ts`; do not copy Connecta internals into the deployment.
83
- 2. Keep the required `executor: quickJsExecutor()` configuration; without an
84
- executor the deployment refuses to boot.
85
- 3. Keep secrets in environment variables or a secret store, never source.
86
- 4. Add connectors explicitly: import a maintained prebuilt provider
87
- constructor when one exists, otherwise write a deliberate `remoteMcp()` or
88
- `api()` connector. There is no registry to browse and nothing registers
89
- itself.
90
- 5. Run `npm run typecheck`, start the server, and run
91
- `CONNECTA_TOKEN=... npm run doctor`. Doctor checks health, the executor, and
92
- the exact seven-tool model-facing surface, then executes a harmless sandbox
93
- program. The bearer stays in the environment rather than command history.
94
-
95
- The operator surface is the same in both shapes and off until you say so:
96
- Clerk sign-in, the credential vault, access-token issuance, and payload-free
97
- activity ship as commented configuration, each one a variable and an
98
- uncommented block away. The generated `README.md` walks through all four, and
99
- the [Worker example](./examples/worker/) does the same for KV and D1.
100
-
101
- Already have a deployment on an older version? `init` deliberately refuses to
102
- merge into it, so bringing one current is its own procedure:
103
- [Upgrading an existing deployment](./documentation/upgrading.md) is the
104
- runbook, written for the agent working inside that project.
105
-
106
- The template refuses to merge into an existing directory, so initialization
107
- cannot overwrite another project. Its generated programs have no filesystem,
108
- environment, arbitrary network, imports, or timers; only explicitly read-only
109
- connector tools are reachable. Unannotated or write-capable calls stay
110
- individual and cross `call_destructive_tool`, where the MCP host can ask the
111
- operator for approval.
112
-
113
- The Node template also pins its one approved dependency install script:
114
- esbuild, which `tsx` needs to run the deployment source. A dependency update
115
- that adds or changes an install script fails the package smoke until reviewed.
116
-
117
- There are two deployment shapes and no others:
118
-
119
- - [Node, local or Docker](./templates/node/) — what `init` copies
120
- - [Cloudflare Worker deployment](./examples/worker/)
121
- - [Subsystem documentation](./documentation/)
122
-
123
- Every deployment configures a sandbox: QuickJS on Node or a Dynamic Worker on
124
- Cloudflare. Construction fails with an actionable error when the executor is
125
- missing, so the model-facing interface is always the same seven tools.
126
-
127
- ## Project status
128
-
129
- Connecta is built for its author's deployments first and is still evolving.
130
- Breaking changes are expected before 1.0.
131
-
132
- Read the [ethos](./ethos.md) for the product's principles, the
133
- [changelog](./CHANGELOG.md) for releases, and [security policy](./SECURITY.md)
134
- for vulnerability reporting.
57
+ Fifty issues in, one small object out. Your context window notices.
58
+
59
+ ## What you can do with it
60
+
61
+ - **Put every MCP server you use behind one connection.** Add or remove
62
+ services in a config file; your client never changes.
63
+ - **Wrap any HTTP API by hand.** A few lines per tool. No OpenAPI conversion —
64
+ generated tool sprawl is the problem, not the fix.
65
+ - **Use maintained connections** for Cloudflare, Linear, Mixpanel, Notion,
66
+ RevenueCat, and Stripe — known endpoints, auth defaults, and vetted read/write
67
+ classifications, imported one at a time.
68
+ - **Let the agent work in code.** Search, chain, filter, join, and reduce
69
+ inside the sandbox instead of round-tripping every call through the model.
70
+ - **Keep writes deliberate.** Only tools marked read-only run in a program.
71
+ Everything else is a separate, visible call your client can gate.
72
+ - **Run it where you like.** Node, a Docker container, or a Cloudflare Worker,
73
+ from the same small deployment file.
74
+
75
+ There is also an operator surface, off until you turn it on: sign-in, an
76
+ encrypted credential vault with rotation, revocable per-client tokens, and a
77
+ payload-free activity log.
78
+
79
+ Connecta is not a platform, a marketplace, a policy engine, or a multi-tenant
80
+ service. Those are decisions, and the [ethos](./ethos.md) records each one
81
+ and why.
82
+
83
+ ## Getting started
84
+
85
+ Setup is written for an agent. Point yours at [`AGENTS.md`](./AGENTS.md) and
86
+ ask it to set up a Connecta deployment; the
87
+ [documentation](./documentation/) covers every subsystem if you want to go
88
+ deeper, and [upgrading](./documentation/upgrading.md) an existing deployment
89
+ is its own runbook.
90
+
91
+ ## Status
92
+
93
+ Built for its author's deployments first and published openly. Breaking
94
+ changes are expected before 1.0. See the [changelog](./CHANGELOG.md) and
95
+ [security policy](./SECURITY.md).
@@ -1,12 +1,58 @@
1
1
  import type { FetchLike, Transport } from "@modelcontextprotocol/client";
2
2
  import { ConnectorCallError } from "../errors.js";
3
- import type { Connector, ConnectorCallAdmissionPolicy, ConnectorContext, ConnectorUsageGuide, Logger } from "../types.js";
3
+ import type { Connector, ConnectorCallAdmissionPolicy, ConnectorContext, ConnectorCredentialConfig, ConnectorUsageGuide, Logger } from "../types.js";
4
+ /**
5
+ * A static downstream credential the operator supplies at `/credentials`
6
+ * rather than the deployment baking into its source.
7
+ *
8
+ * The connector, its endpoint, and the credential *slot* stay declared in
9
+ * code; only the secret arrives through the operator route, exactly as for
10
+ * `api()`. One reserved `value` field, deliberately: a header is assembled
11
+ * from a name, a framing scheme, and one secret, and anything that needs two
12
+ * secrets composed into one header is a provider integration, not a proxy
13
+ * config ([#439](https://github.com/zackbart/connecta/issues/439)).
14
+ */
15
+ interface RemoteMcpCredentialAuth {
16
+ type: "credential";
17
+ /**
18
+ * Operator-facing slot description rendered on `/credentials`. Defaults to
19
+ * `{ label: "API key" }`; a maintained provider passes the name the provider
20
+ * itself uses. Named `fields` are refused — this shape reads the reserved
21
+ * `value` field only.
22
+ */
23
+ credential?: ConnectorCredentialConfig;
24
+ /** Header the credential rides. Defaults to `Authorization`. */
25
+ header?: string;
26
+ /**
27
+ * Framing token placed before the value. Defaults to `"Bearer"`. `null` (or
28
+ * an empty string) sends the stored value verbatim, for an endpoint that
29
+ * reads a bare key. A scheme whose last token is `Basic` declares
30
+ * HTTP Basic credentials: the stored `user:secret` is base64-encoded first,
31
+ * so `"Basic"` produces `Basic <base64>` and Mixpanel's documented
32
+ * `"Bearer Basic"` produces `Bearer Basic <base64>`.
33
+ */
34
+ scheme?: string | null;
35
+ }
4
36
  export type RemoteMcpAuth = {
5
37
  type: "headers";
6
38
  headers: Record<string, string>;
7
- } | {
39
+ } | RemoteMcpCredentialAuth | {
8
40
  type: "oauth";
9
41
  };
42
+ /**
43
+ * Apply a maintained provider's slot copy and header framing to credential
44
+ * auth the deployment left bare.
45
+ *
46
+ * A provider knows what its own key is called and how the endpoint expects it
47
+ * framed; a deployment that states either one keeps its answer. Every other
48
+ * auth shape passes through untouched, so a provider can hand this its whole
49
+ * `auth` option without branching first.
50
+ */
51
+ export declare function withCredentialDefaults(auth: RemoteMcpAuth, defaults: {
52
+ credential: ConnectorCredentialConfig;
53
+ /** Provider framing; omit to leave the `Bearer` default in place. */
54
+ scheme?: string | null;
55
+ }): RemoteMcpAuth;
10
56
  export type RemoteMcpRedirectPolicy = "none" | "same-origin";
11
57
  export interface RemoteMcpOptions {
12
58
  url: string;
@@ -90,3 +136,4 @@ export declare function redirectSafeFetch(connectorId: string, policy?: RemoteMc
90
136
  * server or hide other connectors).
91
137
  */
92
138
  export declare function remoteMcp(id: string, opts: RemoteMcpOptions): Connector;
139
+ export {};