@zackbart/connecta 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -0
- package/README.md +95 -116
- package/SECURITY.md +1 -1
- package/bin/connecta.mjs +24 -6
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +167 -23
- package/dist/access-tokens.js +676 -140
- package/dist/activity.d.ts +3 -0
- package/dist/agent-identity.js +47 -0
- package/dist/auth/cloudflare-access.js +13 -5
- package/dist/branding.d.ts +7 -0
- package/dist/branding.js +76 -9
- package/dist/call-admission-context.js +15 -0
- package/dist/call-admission.js +30 -43
- package/dist/call-recovery.js +6 -2
- package/dist/catalog-service.js +15 -1
- package/dist/config-value-sources.js +16 -9
- package/dist/config.d.ts +48 -75
- package/dist/config.js +144 -36
- package/dist/connector-access.d.ts +4 -34
- package/dist/connector-access.js +15 -99
- package/dist/connector-caller.d.ts +7 -1
- package/dist/connector-caller.js +4 -2
- package/dist/connectors/api-connector.d.ts +27 -4
- package/dist/connectors/api-connector.js +2 -0
- package/dist/connectors/remote-mcp.js +2 -0
- package/dist/describe-config.d.ts +31 -7
- package/dist/describe-config.js +8 -17
- package/dist/execute.js +1 -11
- package/dist/fix-prompt.js +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +30 -10
- package/dist/invocation.js +5 -3
- package/dist/membership.d.ts +53 -0
- package/dist/membership.js +121 -0
- package/dist/module-contracts.d.ts +23 -6
- package/dist/oauth-callback-outcome.js +2 -2
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +2 -4
- package/dist/operator-ui/agents.d.ts +74 -0
- package/dist/operator-ui/agents.js +104 -0
- package/dist/operator-ui/config-summary.js +562 -0
- package/dist/operator-ui/connection-map.js +375 -0
- package/dist/operator-ui/contract.d.ts +149 -28
- package/dist/operator-ui/endpoints.js +221 -0
- package/dist/operator-ui/fix-prompts.js +5 -5
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/member-page.js +97 -0
- package/dist/operator-ui/model.d.ts +14 -6
- package/dist/operator-ui/overview.js +134 -0
- package/dist/operator-ui/view.d.ts +107 -37
- package/dist/operator-ui/view.js +163 -86
- package/dist/page-styles.js +5 -3
- package/dist/provider.d.ts +61 -8
- package/dist/provider.js +46 -7
- package/dist/providers/_shared/google/workspace.d.ts +56 -17
- package/dist/providers/_shared/google/workspace.js +34 -8
- package/dist/providers/_shared/rest/tools.js +2 -2
- package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
- package/dist/providers/_shared/rest/value-safety.js +699 -7
- package/dist/providers/ccb/index.js +2 -2
- package/dist/providers/cloudflare/index.js +3 -3
- package/dist/providers/cloudflare/rest.js +25 -3
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/cloudflare/value-safety.js +32 -0
- package/dist/providers/docs/index.js +4 -4
- package/dist/providers/docs/skill.generated.js +2 -2
- package/dist/providers/drive/index.js +4 -4
- package/dist/providers/drive/skill.generated.js +2 -2
- package/dist/providers/forms/index.js +4 -4
- package/dist/providers/forms/skill.generated.js +2 -2
- package/dist/providers/gmail/index.js +11 -9
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/mixpanel/index.js +1 -1
- package/dist/providers/mixpanel/skill.generated.js +3 -3
- package/dist/providers/notion/index.d.ts +7 -0
- package/dist/providers/notion/index.js +100 -11
- package/dist/providers/notion/rest.js +45 -5
- package/dist/providers/notion/skill.generated.js +2 -2
- package/dist/providers/notion/value-safety.js +30 -5
- package/dist/providers/overflow/index.js +2 -2
- package/dist/providers/overflow/skill.generated.js +2 -2
- package/dist/providers/planning-center/index.js +2 -2
- package/dist/providers/planning-center/skill.generated.js +4 -4
- package/dist/providers/revenuecat/skill.generated.js +2 -2
- package/dist/providers/sheets/index.d.ts +3 -0
- package/dist/providers/sheets/index.js +7 -4
- package/dist/providers/sheets/skill.generated.js +2 -2
- package/dist/providers/slides/index.js +4 -4
- package/dist/providers/slides/skill.generated.js +2 -2
- package/dist/providers/stripe/rest.js +9 -2
- package/dist/providers/stripe/skill.generated.js +1 -1
- package/dist/providers/stripe/value-safety.js +22 -0
- package/dist/providers/tithely/index.js +1 -1
- package/dist/providers/vercel/index.js +1 -1
- package/dist/providers/vercel/rest.js +20 -2
- package/dist/providers/vercel/skill.generated.js +2 -2
- package/dist/providers/vercel/value-safety.js +22 -0
- package/dist/registry.d.ts +2 -3
- package/dist/registry.js +23 -9
- package/dist/routes/access-tokens.js +82 -75
- package/dist/routes/activity-disclosure.js +17 -18
- package/dist/routes/activity.js +34 -54
- package/dist/routes/actor-labels.js +33 -4
- package/dist/routes/agent-path.js +43 -0
- package/dist/routes/connect.js +16 -12
- package/dist/routes/credentials.js +16 -40
- package/dist/routes/mcp.js +161 -104
- package/dist/routes/oauth-management.js +11 -5
- package/dist/routes/oauth.js +5 -11
- package/dist/routes/operator.js +24 -23
- package/dist/routes/shared.d.ts +60 -20
- package/dist/routes/shared.js +187 -96
- package/dist/routes/ui-config.js +437 -97
- package/dist/routes/ui-data.js +2 -2
- package/dist/routes/ui.js +9 -16
- package/dist/runtime/call-admission.js +9 -0
- package/dist/server.js +5 -0
- package/dist/skills.js +10 -0
- package/dist/storage/keys.js +9 -1
- package/dist/storage/sql.js +5 -1
- package/dist/types.d.ts +29 -7
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +23 -16
- package/dist/usage-guide.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace-address.js +9 -0
- package/dist/workspace-subject.js +23 -0
- package/documentation/README.md +47 -23
- package/documentation/architecture.md +371 -61
- package/documentation/auth.md +897 -249
- package/documentation/code-mode.md +37 -16
- package/documentation/deploying.md +126 -25
- package/documentation/integrating.md +97 -23
- package/documentation/meta-tools.md +50 -31
- package/documentation/migration-0.33.md +352 -0
- package/documentation/migration-0.34.md +67 -0
- package/documentation/operating.md +3 -1
- package/documentation/operator-tests.md +147 -34
- package/documentation/operator-ui.md +519 -64
- package/documentation/provider-auth-migration.md +37 -23
- package/documentation/provider-migration-0.29.md +2 -2
- package/examples/worker/AGENTS.md +37 -9
- package/examples/worker/README.md +127 -61
- package/examples/worker/src/connecta.config.ts +40 -23
- package/examples/worker/wrangler.jsonc +4 -1
- package/package.json +19 -18
- package/templates/node/.env.example +3 -0
- package/templates/node/AGENTS.md +33 -6
- package/templates/node/Dockerfile +2 -2
- package/templates/node/README.md +80 -52
- package/templates/node/docker-compose.yml +3 -2
- package/templates/node/package.json +4 -3
- package/templates/node/src/adopt-token.ts +23 -0
- package/templates/node/src/connecta.config.ts +28 -12
- package/templates/node/src/rotate-token.ts +17 -0
- package/templates/node/src/provision-token.ts +0 -17
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,104 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.34.0 — 2026-10-16
|
|
6
|
+
|
|
7
|
+
Connecta 0.34.0 rebuilds the operator UI around an Overview with connector health, endpoints, people and agents, a Needs attention list with fix prompts, and a connection map (#867). Maintainers now land on Overview at `/`; Endpoints moves to `/endpoints`, and `/access` redirects there. Update bookmarks, links and browser automation that expected Endpoints at `/`. Members still get their member page on every operator page path. The upgrade steps are in `documentation/migration-0.34.md`.
|
|
8
|
+
|
|
9
|
+
The redesign carries through Connectors, Endpoints, the access matrix, Tokens, Settings, Activity and the member page, with warm neutral surfaces, one accent, self-hosted Hanken Grotesk and JetBrains Mono, and light and dark themes. Configuration uses tiles, connector tools open in a schema drawer, Activity groups calls by request, and Account holds Sign out and Change token. Default browser titles now join the product and owner with `·`; set `branding.pageTitle` explicitly if an integration needs the old title. Custom titles stay unchanged. Activity outcome links can use `failed` or `succeeded`; older exact outcome values still filter by that value. Custom activity stores may optionally report `describe().optionSources.retentionDays` as `"config"` or `"default"` so Settings can distinguish an explicit retention setting; existing stores need no change.
|
|
10
|
+
|
|
11
|
+
Agents in a pool without an `owner` no longer discover Google Workspace connectors whose `subject` is a mapping function (#873). Those connectors previously appeared but failed calls with `auth_required`; now they are absent from discovery, search, describe, skills and code mode, and calls by address fail `unknown_address`. Saved program returns are rechecked too. To give an agent Workspace access, use a pool with the intended person's Workspace address as its `owner`, such as `alice@example.com`; do not add an owner to a shared pool just to restore discovery. People, agents in owned pools and fixed-subject connectors keep their behavior.
|
|
12
|
+
|
|
13
|
+
Pools can now take a one-line `description` of 1 to 500 characters, appended to that pool's MCP instructions after `This endpoint: ` (#872). Pools without a description and plain `/mcp` keep their instructions. The auth guide now documents group tiers: pick the most capable pool everyone present is cleared for, use the base pool when anyone is unknown, and build personal pools on that person's highest tier (#875). Shipped docs also correct operator, provider and deployment guidance (#866). Repository development and CI now use pnpm (#877); installed deployments, the Node template and npm publishing keep using npm. Upgrading from 0.33.0 needs no storage migration or token rotation. Deployments that do not use the operator UI or mapping-subject Workspace connectors in owner-less agent pools can ignore the UI and discovery migration steps; all new configuration fields are optional.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Add an Overview page as the maintainer's landing page at `/`: a status pill and stat cards for connector health, endpoints, and people and agents, a "Needs attention" list that links each problem to its existing fix with a copyable fix prompt, and a connection map of callers, endpoints and connectors that traces what a node reaches on hover or focus and opens its page on click. Endpoints moves to `/endpoints`, and `/access` now redirects there. Members still get their member page on every path.
|
|
18
|
+
- Pools take an optional `description`: one line of plain text, 1 to 500 characters, that `/mcp/<pool>` and `/agent/<pool>` append to their MCP instructions after `This endpoint: ` so the model learns that endpoint's scope. Plain `/mcp` and pools without one keep the shared instructions, and `describeConfig()` lists each pool's `description` (`null` when unset).
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- The operator UI's Activity page and the member page ("Your connections") follow the calm redesign (#867). Activity shows counts and a calls-over-time strip, filters with a search box, an All / Failed / Succeeded control and connector and source chips instead of select menus, and rows grouped by request with one status dot, the caller as a person or agent chip, a connector monogram, the tool, and duration and size; a row opens for the rest of what was recorded. A connector's Activity tab uses the same rows. The member page shows the signed-in email, endpoints as cards, accounts with distinct monograms and aligned status badges, and a compact shared list. The Activity outcome filter now takes `failed` or `succeeded`; an older exact outcome value in a link still filters by that value.
|
|
23
|
+
- An agent in a pool without an `owner` no longer sees Google Workspace connectors whose `subject` is a mapping function: they are absent from discovery, search, describe, skills and code mode, and a call by address fails `unknown_address`, as for a personal connector. Before, the agent saw them and every call failed `auth_required`. People in the same pool, agents in owned pools, and fixed-subject connectors are unchanged (#870).
|
|
24
|
+
|
|
25
|
+
Paging a saved program return rechecks every connector contributing to search counts, ranking, term analysis, or guidance, including matches outside the returned page and connector references when no tools matched. Removing a pool owner therefore also hides those saved contributions from agents.
|
|
26
|
+
- Document group tiers in the agent pool layout: a chat gets the most capable tier everyone present is cleared for, tiers nest by spreading tool lists, the base pool is the lowest tier and the fallback for anyone unknown, a tier can double as a team pool for the people cleared for it, and personal pools build on their person's highest tier. Shorten the README to the visual overview.
|
|
27
|
+
- The operator UI and the pages connecta renders for people (sign-in, no access, OAuth results, 404s) take a new look: warm neutral surfaces, one indigo accent, soft cards, status pills, and Hanken Grotesk with JetBrains Mono for values, in light and dark. The fonts stay self-hosted. The top bar puts the sections, Search, Appearance and a new Account button on one row; Sign out and Change token now live under Account. The drawn connecta mark shows only on an unbranded deployment; a configured `favicon.svg` shows instead. A custom accent derives readable button text and dark-mode link color from itself.
|
|
28
|
+
- The operator UI's Connectors page shows a card per connector with its status, Shared or Per person badge, a read/write tool bar and its last call, filtered by All, Needs attention or Healthy. A connector's page opens with its status and badges; Tools filters by Read or Write and opens a schema in a side drawer, and Connection shows authentication facts, a credential card with its masked last four, and diagnostics as tiles (#867).
|
|
29
|
+
- The operator UI's Endpoints, access matrix and Tokens pages adopt the calm design. Endpoint rows show who can use them as an avatar stack, their connectors as monograms and their tools over a read/write bar, with a read-only pill only where it applies; client setup switches between Claude Code, Codex and JSON over one snippet. Matrix cells show the share of a connector they grant as a pie, read-only columns are hatched, and the Status and Runs as columns give way to a status dot and each cell's popover, which marks every tool read or write. Tokens rows show the agent mark, pool chips, a state pill and dates, with one primary action at most, and stack into cards on a narrow screen. Matrix shares count only live granted tools, show unknown coverage when the catalog is unavailable, and name both granted and total live counts for assistive technology.
|
|
30
|
+
- Develop the repository with pnpm instead of npm: `pnpm-lock.yaml` replaces `package-lock.json`, `pnpm-workspace.yaml` allows only the esbuild and workerd build scripts, CI installs with `pnpm install --frozen-lockfile`, and `check:security` runs `pnpm audit`. The published package, its install instructions, and the Node template still use npm, and releases still publish through `npm publish` with trusted publishing.
|
|
31
|
+
- Settings and the connector Configuration card show configuration as tiles instead of a key/value tree. Deployment facts are chips (name with server version, Writes allowed or Read-only, and a warning chip for unconfigured memory storage); sign-in shows provider tiles, an agent count and one Maintainers tile; features show on or off; headline limits draw large with an accent dot when set in code, and the remaining defaults fold into one "17 defaults" disclosure of grouped cards. Sizes read in decimal units with the exact byte count as a tooltip, and a null tool timeout shows the host-call timeout it falls back to. A connector's card adds an auth row: mode, header names, OAuth scope, API origins and OAuth endpoints as compact badges. The D1 and SQLite activity stores report whether code set `retentionDays`, and Settings marks retention set only then. Snapshot keys the UI does not map still appear as labelled tiles.
|
|
32
|
+
|
|
33
|
+
Open deployments retain the Maintainers tile beside Open, with "None" when no maintainers are configured.
|
|
34
|
+
- The operator UI uses shorter copy throughout (#867): labels, empty and error states, confirmations, notices, gates and the OAuth callback and 404 pages, with no em dashes. Page titles separate with a middle dot. On the Overview, narrow stat tiles sit two up, the map draws a muted dash instead of "0 tools" for a connector whose catalog is unknown, and caller chips in the narrow list link like map nodes.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Correct inaccurate guidance across the shipped docs, provider skills, and the usage skill: token-route statuses, OAuth control labels, meta-tool paging and timing, host-call budgets, error codes, doctor output and credentials, local `.env` use, Worker secrets and copied-repo setup, the 0.33 Node template script upgrade, and provider argument names and write classifications. Rewrite the README to explain people, agents, pools, and account ownership with diagrams, and drop the hero image.
|
|
39
|
+
|
|
40
|
+
## 0.33.0 — 2026-10-16
|
|
41
|
+
|
|
42
|
+
Connecta 0.33.0 makes pools the access model, and every deployment with inbound auth must change its configuration (#837). Work through `documentation/migration-0.33.md` before upgrading; it maps every removed option to its replacement. A new top-level `maintainers` list names the people who reach plain `/mcp` and administer the deployment. Each pool now has a static `members` list and serves exactly its `tools` to those members. Agents are declared by name in `agents`, each with one rotatable `cta_` token. Each removed option fails at startup with a message naming its replacement, so a deployment that starts has nothing silently ignored: `identity` (even `{}`) and its `connectorAccess`, `credentialAdministration`, `personalConnection`, `accessTokenManagement` and `activityAccess`, plus the top-level `grant` and pool `grant`.
|
|
43
|
+
|
|
44
|
+
Agents move from `/mcp/<pool>` to `/agent/<pool>`, which admits only a declared agent's `cta_` token for a pool it belongs to. `/mcp`, `/mcp/<pool>` and the operator routes refuse `cta_` tokens, so point every agent at `/agent/<pool>`. Deployments behind Cloudflare Access add one Access application for `<host>/agent/*` with a single Bypass policy, after which agents send only their token. Tokens issued by earlier releases stop admitting until trusted provisioning adopts each one for an agent (`AccessTokenManager.adopt`, or `npm run adopt-token` in the Node template). Creating, renaming and human-bound tokens are gone, along with `maxActive`. Programmatic callers construct `AccessTokenManager` with `{ agents }` and call `rotate` where they called `create`, and `revoke(agent, options)`. Activity names agents by name. Rotation is an immediate cutover. HTTP issue, rotate and revoke requests must send the observed slot revision as `expect`; a stale revision gets 409 `slot_changed`. `AccessTokenManager.rotate`, `revoke` and `adopt` accept optional `expect` and reject a stale revision with `AccessTokenError.code: "slot_changed"`. A pool can set `owner`, the Google Workspace address its agents act as, so one Workspace connector serves a pool per person. An agent never reaches a `subject` mapping function, so a 0.32 mapping that answered for machine tokens must move to `owner`.
|
|
45
|
+
|
|
46
|
+
This release also defines how to organize an agent's pools. Each agent has one Connecta connection whose client picks the pool per turn. The base pool `<agent>` holds shared connectors and is used in group chats, scheduled runs and anything else without exactly one identified person. An `<agent>-<user>` pool adds that person's own connector ids, and its `owner` is their Workspace address; it is used only in a one-on-one with that person. Never mount a second Connecta connection beside the first. The rules and an example are in the auth guide, "Organizing an agent's pools".
|
|
47
|
+
|
|
48
|
+
The shipped documentation is now written first for the agent running a deployment from the installed package (#858). `documentation/README.md` has an operator task index: add or remove a connector, organize an agent's pools, add a person, connect a shared credential, issue, rotate or revoke an agent token, upgrade, and diagnose with `connecta doctor`. Each row links to a self-contained section, and the Node template and Worker example route agents through it. Existing deployments should add the pointer line from the migration guide to their own `AGENTS.md`.
|
|
49
|
+
|
|
50
|
+
The operator UI is rebuilt as an admin console and a member page (#834). Maintainers land on Endpoints, which lists `/mcp` and every pool with who can use it, its tools grouped by connector and who each connector runs as. They also get Connectors with a connector × endpoint matrix, Activity, Tokens with one row per agent and in-row issue, rotate and revoke, and Settings. Old URLs redirect (`/access` to `/`, `/tools` to `/connectors`, `/config` to `/settings`). Pool members get one page with their endpoints and client setup, their own accounts to connect or disconnect, and shared connectors by name only; anyone else sees a No access page. A saved dark mode no longer flashes light on load, and private browsing no longer breaks token sign-in. The UI reads operator contract v2 from `GET /ui/api/config`, which is now the page's only gate: 401 when signed out, 404 for anyone who is neither a maintainer nor a member, and 200 with `role` otherwise. Contract v1 and its types are removed, and `/ui/data` and `/ui/connectors/<id>` are maintainer-only. The UI also recovers from transient Cloudflare Access and auth failures without a reload (#830).
|
|
51
|
+
|
|
52
|
+
Security: 0.29.0 through 0.32.0 buffered an unauthenticated POST carrying `Mcp-Method: subscriptions/listen` with no size limit before authentication, so an anonymous client could exhaust memory on deployments without an edge gate such as Cloudflare Access. Every MCP endpoint now authenticates before reading any body and caps the pre-dispatch read at 64 KiB. Stripe, Vercel, Cloudflare and Notion REST responses now withhold signed URL parameters, and writes that carry the redaction placeholder are refused before sending (#812, #818).
|
|
53
|
+
|
|
54
|
+
The release also fixes Notion property filtering, Gmail's charset notice under truncation, recovery of long caller idempotency keys, Cloudflare ownership lookups against call admission, and `callAdmission` values that silently disabled rate limits. Notion token connectors accept `callAdmission`, and the fake-vendor agent evaluations now run.
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
|
|
58
|
+
- Add the operator UI contract v2 types (`OperatorUiContractV2` and its pool, member, agent, connector and matrix types) from the pools-are-the-access-model decision, with maintainer and member fixtures; `GET /ui/api/config` serves it (see Changed).
|
|
59
|
+
- **Breaking:** Pools take an optional `owner`, the Google Workspace address agents in that pool act as on a Workspace connector (`gmail()`, `drive()`, `sheets()`, `slides()`, `forms()`, `docs()`) whose `subject` is a mapping function, so one connector serves a pool per person instead of one connector per mailbox. People always act as themselves through the mapping, in any pool. An agent acts as the owner of the pool it came through, resolved before and instead of the mapping, which is never called for an agent. An agent in a pool without an owner fails `auth_required` before any request leaves, and the message names no address. A fixed `subject` address still applies to every caller, agents included, and ignores owners. Nothing in arguments, headers, or programs can choose the owner. `owner` must be a Workspace email address, checked at construction by the rule a fixed `subject` uses, and `describeConfig()` pools add `owner` (`null` when unset). `/ui/api/config` serves the address only to an authenticated maintainer; an open deployment's anonymous caller reads `null`. Breaking for agents only: people and fixed subjects behave as before. In 0.32 a `cta_` token reached the mapping function, which usually answered `undefined` for a caller with no principal (`auth_required`). Now an agent never reaches it, so a mapping that answered for machine tokens, by token subject or otherwise, no longer applies; set that address as the `owner` of the agent's pool.
|
|
60
|
+
- Operator contract v2 `runsAs` for shared connectors now says where the credential lives (`vault`, `missing`, `oauth`, `config` or `unauthenticated`), so a credential supplied in deployment config, or a connector that needs none, is not shown as unsaved.
|
|
61
|
+
- Activate ten deterministic agent evaluations for Stripe, Notion, Cloudflare, and Vercel REST connectors, with state, recovery, pin, paging, and value-safety graders and saved evidence for offline regrading.
|
|
62
|
+
|
|
63
|
+
Model Stripe idempotency by account and mode, reject refused named write attempts in read-only graders, bind analytics counts to their metrics, and align environment confirmation with its prompt. Replace fixture-invalid measurements with fresh live trials.
|
|
64
|
+
- Allow Notion token connectors to replace the default call-admission policy with `callAdmission`. The default remains 180 calls per minute and 3 concurrent. Malformed policies, including falsy values other than `undefined`, fail at construction for both token and OAuth connectors. Shared admission validation also rejects malformed nested budgets instead of silently disabling their limit.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Endpoints shows who can use each endpoint: `/mcp` lists its maintainers and each pool its members, people by name (or raw id) and agents as `agent:<name>`, with long lists collapsed behind "+N more". Tokens lists one row per agent declared in code with its pools, token state, date and last seen; issue, rotate and revoke keep their confirmations, a new secret appears once in a dialog, an unknown or aborted outcome is a failure notice that is never retried, and an agent missing from the live slot list cannot be acted on until a refresh lists it. Pool members get their own page with their endpoints and client setup, connect, reconnect or disconnect for their personal OAuth accounts, a credential form for personal credential slots whose Add, Replace, Test and Clear follow `manage.actions`, and the names of connectors shared with them. Every token and member action is checked again immediately before it is sent, so a slot list or contract refreshed meanwhile stops it, and a token operation with an unknown outcome disables that agent's controls until a refresh. Every token mutation, Issue included, sends the slot revision the operator confirmed against as `expect` (P3b of #837); a slot without a revision is never acted on, a contract revision that differs from the list's unresolves the slot, and a 409 `slot_changed` reads "Changed since you looked, refresh".
|
|
69
|
+
- The operator UI reads operator contract v2 and refuses v1. Endpoints lists `/mcp` and every pool, with an agent URL for pools that have agent members, and each expanded row shows who every connector runs as. Connectors adds a Matrix view of connectors by endpoint, where a cell opens its exact tools. Pool members see only their endpoints and client setup, and someone who is neither a maintainer nor a member sees a No access page. A shared connector's runs-as names where its credential comes from.
|
|
70
|
+
- **Breaking:** Agents move from `/mcp/<pool>` to `/agent/<pool>`. An agent endpoint admits only the built-in `cta_` adapter and a member agent of that pool, serves exactly the pool's tools with its `readOnly`, binds result handles to `/agent/<pool>`, answers a missing or invalid credential with a machine-only `WWW-Authenticate: Bearer` challenge, and publishes no protected-resource metadata. It never runs Clerk or Access and ignores cookies, Clerk handshake parameters, `ctx.access`, and Access headers. Only a raw pathname byte-equal to `/agent/` plus a pool name is an agent endpoint; every other path in the agent namespace (any case, escapes, doubled slashes, trailing slash, extra segments) is routed first and answers the pool 404 before auth, so no path Cloudflare Access bypasses as `/agent/*` reaches a human route. Breaking: `/mcp`, `/mcp/<pool>`, and the operator, connect, and callback routes refuse `cta_` tokens without reading token storage (401 on MCP and operator data routes, 403 on interactive-only routes), so point every agent at `/agent/<pool>`; `connecta doctor` checks `/agent/<pool>` for a `cta_` token and requires `--pool`, and the Node template's doctor checks `/agent/local-machine`. Deployments behind Cloudflare Access add one self-hosted Access application per agent hostname for `<host>/agent/*` with a single Bypass policy beside the Worker-level application, after which agents send only their `cta_` token (deploying guide, "Agent endpoints behind Cloudflare Access"). The `agent_not_maintainer` denial reason is removed.
|
|
71
|
+
- **Breaking:** Machine clients are agents declared by name in a new top-level `agents` option, each with one `cta_` token slot that operators issue, rotate, and revoke from `/ui/access-tokens`, the operator UI, or `AccessTokenManager.rotate` and `revoke`. Rotation is an immediate cutover: the old secret refuses on its next request, and of concurrent rotations exactly one returns a secret. The agent's slot is the only authority admission consults, and each operation commits by one compare-and-set on it and only moves forward: a rotation cancelled before its commit publishes nothing, and one cancelled after it moves the slot to revoked within one deadline that bounds every storage call of the operation, the commit's acknowledgement included (otherwise it reports the outcome unknown, never success; rotate again for a replacement secret). The request path deletes nothing it did not just write: superseded records and lookups stay behind, inert, so storage grows by one small record per rotation or adoption. A token admits only while it is bound to a currently declared agent, so removing a name stops its token on the next request. Admitted agents carry `identity.agent` (set only by the built-in adapter; any other provider returning `agent` is refused), have no principal and so no personal connectors, and are attributed in activity by agent name rather than token record id. Breaking: creating, renaming, and human-bound tokens are removed from the routes, manager, and UI; tokens issued by earlier releases stop admitting until trusted provisioning adopts each machine token for an agent with `AccessTokenManager.adopt(tokenId, agent)` (Node template: `npm run adopt-token`), which keeps its secret without rewriting the legacy record and takes a never-deleted claim so a legacy token is adopted by at most one agent, once, and human-bound tokens cannot be adopted; `accessTokens(storage, { maxActive })` throws; `describeConfig()` adds `agents` and drops `modules.accessTokens.maxActive`; grant callbacks that matched token record ids should match `identity.agent`. The Node template replaces `provision-token` with `npm run rotate-token -- <agent>` and declares `local-machine`.
|
|
72
|
+
- Record the decision that pools are the access model: maintainers alone use plain `/mcp`, pool members get exactly their pool's tools, agents are declared in code and reach `/agent/<pool>` with a `cta_` token, and a pool owner sets the Google Workspace account agents act as.
|
|
73
|
+
- **Breaking:** Management follows `maintainers` and pool `members` (#837). Shared credentials and OAuth grants, and agent token list, issue, rotate, and revoke, take an interactive maintainer; activity reads take a maintainer principal, interactive or not, within the optional `readGate`. A personal connector's own account may be connected and disconnected, always in the caller's own partition, by an interactive maintainer or by an interactive member of a pool containing that connector, through `authorize_connector` at their pool, the `/connect` link and callback, and `/ui/oauth/<id>` and `/ui/credentials/<id>`, which now admit such a member for their pools' personal connectors. Agents and other machine credentials manage nothing. Pool members get the private 404 on activity and token routes. A `cta_` credential is refused before token storage is read: 401 on activity data routes and 403 on interactive token-management routes. Breaking: `identity.credentialAdministration`, `identity.personalConnection`, `identity.accessTokenManagement`, and `identity.activityAccess` are removed and each fails construction naming its replacement; `describeConfig()` drops `identity`; the `ConnectaIdentityConfig` and `ConnectorPermission` types are no longer exported.
|
|
74
|
+
- **Breaking:** Add the 0.33 migration guide (`documentation/migration-0.33.md`), mapping every removed access option to `maintainers`, pool `members`, and `agents`, with agent endpoints, pool owners, token revisions, operator contract v2, and the 0.29.0–0.32.0 pre-auth listen fix. Add connecta's agent pool layout as a documented standard in the auth guide ("Organizing an agent's pools") and decision 0007: one Connecta connection per agent, a base pool `<agent>` with shared connectors and no `owner`, and an `<agent>-<user>` pool per person with that person's own connector ids and their Workspace address as `owner`. The Node template and Worker example agent guidance follow it. Breaking: the empty `identity` option is removed: `identity`, even `{}`, now fails construction, still naming each retired key's replacement. The Access bypass probe requires `--profile`.
|
|
75
|
+
- **Breaking:** `GET /ui/api/config` serves operator contract v2 and is the operator page's only gate: 401 with the auth challenge when signed out (an agent token gets the machine-only 401 every human route gives it), the private 404 for anyone who is neither a maintainer nor a pool member, and 200 with `role` otherwise. A maintainer gets the whole view: `root` and every pool with their members and directory labels, pool owners, every declared agent with its token slot and last activity, and a connector × endpoint matrix naming who each connector runs as. A pool member gets only their own pools and connectors, with their own connection state and the actions they may take. Contract v1, `you.grants`, `you.permissions`, `hasGrant`, and the `OperatorViewer` and `OperatorPool` types are removed; `you.interactive` remains, and `OperatorUiContract` now names v2. `/ui/data` and `/ui/connectors/<id>` stay maintainer-only. `/ui/oauth/<id>` gates each verb on its own hooks: connecting needs `startAuth`, `finishAuth` and `verifyState`, and disconnecting needs only `disconnectAuth`, so a connector provisioned out of band can be disconnected. `DELETE /ui/credentials/<id>` refuses to clear an empty partition or slot with 409 `credential_required`. `connecta doctor --config` reads v2 and refuses a member's view.
|
|
76
|
+
- **Breaking:** Pools are the access model (decision 0006). A new top-level `maintainers` list names the people who reach plain `/mcp`, which serves the whole catalog with the deployment `readOnly`, and the operator routes that administer the deployment (catalog, activity, shared connector auth, access tokens); it is required whenever an inbound provider other than the built-in agent-token adapter is configured. Each pool now requires a non-empty static `members` list and serves exactly its `tools` to those members, with no intersection against anything else. A reference is a bare principal id resolved in the namespace of the deployment's one interactive provider, `{ namespace, id }`, or `"agent:<name>"` for a declared agent; ambiguous, unknown, repeated, or misplaced references (an agent in `maintainers`) fail construction. A non-maintainer at `/mcp`, a non-member, and an undeclared pool get the identical 404 after auth, logged as `not_maintainer`, `not_pool_member`, or `pool_not_declared`; result handles stay bound to their endpoint and paging re-admits the caller. A pool member may connect their own personal connector in that pool through `/connect/<id>`, which starts directly for non-maintainers. Shared connector auth (issuing a link, starting, completing the callback, disconnecting, and writing a credential) requires a maintainer. Breaking: `identity.connectorAccess` (with its guarded `{ tool, requireReadOnly: true }` grants; use a `readOnly: true` pool), the top-level `grant` (use `maintainers`), and pool `grant` (use `members`) are removed and fail construction naming their replacement; `describeConfig()` drops `hasGrant` at the root and on pools and `identity.connectorAccess`, and adds `maintainers` and pool `members`; anyone neither a maintainer nor a pool member gets the private 404 on operator routes, and the member access the management and operator contract entries describe is all a member gets there. Agent members reach their pools at `/agent/<pool>` (agent endpoints entry) and are refused at plain `/mcp`; `connecta doctor --pool <name>` checks a pool, and the Node template (`CONNECTA_MAINTAINERS`, a `local-machine` pool for the `local-machine` agent, `npm run doctor`) and Worker example (`CONNECTA_MAINTAINERS`, a `ci` pool for the `ci` agent) are updated accordingly.
|
|
77
|
+
- **Breaking:** Agent token slots carry an opaque `revision` that changes on every commit to the slot (issue, rotate, revoke, adopt). `GET /ui/access-tokens`, the slot each mutation returns, and operator contract v2's `OperatorAgent.token` list it. Breaking: `POST` (issue or rotate) and `DELETE` (revoke) `/ui/access-tokens/<agent>` now require `{ "expect": "<revision>" }` as a JSON body (`DELETE` also takes `?expect=<revision>`); a missing or malformed `expect` is 400, and a slot whose revision differs is 409 `{ error, code: "slot_changed" }` before anything is minted or written. The slot's compare-and-set commits only from the slot with that revision, so a change landing between the read and the commit refuses the same way instead of being overwritten or revoked. `AccessTokenManager.rotate`, `revoke`, and `adopt` take the same optional `expect`; without it they behave as before. Each revocation stores a random, non-secret `revocationId`, so no two revocations store the same slot and a call recognizes only its own: of two conditional revocations of one slot, exactly one succeeds and the other is 409 `slot_changed`.
|
|
78
|
+
- Make the shipped documentation an operator entry point with self-contained connector, people, credential, token, pool, upgrade, and diagnosis procedures, and separate contributor references.
|
|
79
|
+
- Record a production Cloudflare probe showing that a path-scoped Access Bypass application for `<host>/agent/*` overrides a Worker-level Access application on that path only, with the edge's path matching and the pathnames the Worker receives, so `/agent/<pool>` can carry a single `cta_` credential behind Access.
|
|
80
|
+
- Rework the operator dashboard around Endpoints, Connectors, Activity, Tokens and Settings with a top bar, light and dark Geist typography, expandable endpoint tool lists, three connector tabs and catalog search. Preserve legacy links and existing permission gates.
|
|
81
|
+
|
|
82
|
+
### Fixed
|
|
83
|
+
|
|
84
|
+
- Operator pages apply a saved color scheme before first paint, through a small blocking hashed initializer, so a viewer who saved dark mode on a light system no longer sees a light frame. A deployment's pinned scheme still wins. Operator pages also load when browser storage throws, as in private browsing; a bearer token is then not remembered.
|
|
85
|
+
- Charge Cloudflare zone-ownership lookups for account pin enforcement to the
|
|
86
|
+
connector's shared call-admission budget. An exhausted budget refuses the lookup
|
|
87
|
+
and enclosing call before sending the lookup or target request. Keep the separate
|
|
88
|
+
limits of three ownership lookups per call and 60 per five minutes per connector.
|
|
89
|
+
- Keep Gmail's unsupported-charset UTF-8 fallback notice in a separate `bodyNote` field on message, thread, and draft reads so body truncation cannot remove it.
|
|
90
|
+
- Give load-sensitive test setup, static scans and child processes explicit deadline headroom while preserving correctness, CPU budget and heartbeat assertions.
|
|
91
|
+
- Send selected properties to Notion as repeated `filter_properties` URL parameters for named page reads and data source queries, reducing response size while preserving the projected page title. Page reads whose deduplicated selection plus title exceeds Notion's 100-filter limit fetch all properties and project locally instead.
|
|
92
|
+
- Review 22 additional Notion hosted MCP tools from a live catalog, preserving write enforcement for mutations and documenting signed download and upload credentials.
|
|
93
|
+
- Recover operator pages from transient Cloudflare Access and auth-state failures without reloading. Distinguish unavailable identity lookups from rejected identities, prevent caching refusals, reconcile catalog and auth refreshes, scope and cancel bounded OAuth status polling, prevent queued focus recovery after sign-out, preserve shared passive retry throttling and backoff across Access refusals, coalesce passive returns through slow catalog/auth recovery with a 15-second admission deadline that releases Retry without aborting healthy pending reads, and show unreadable credentials as unavailable.
|
|
94
|
+
- Preserve caller-supplied REST idempotency keys up to the schema's 255-character limit in `uncertainCall.recovery` after an ambiguous write. Versions 0.31.0 through 0.32.0 echoed caller-supplied keys only up to 128 characters. Other recovery values retain their 128-character cap, and keys longer than 255 characters are still rejected before dispatch.
|
|
95
|
+
- Refuse malformed `callAdmission` policies at construction across connector and maintained provider factories, including custom connectors registered by a deployment. Only omission or `undefined` selects the documented default; `false`, `null`, `0`, and `""` no longer silently disable admission or select a default.
|
|
96
|
+
|
|
97
|
+
### Security
|
|
98
|
+
|
|
99
|
+
- Affected versions: 0.29.0–0.32.0. An unauthenticated POST with `Mcp-Method: subscriptions/listen` to `/mcp` or `/mcp/<pool>` was buffered with no byte cap before authentication and request admission, so an anonymous client could hold unbounded memory per request without taking an admission permit. Every MCP endpoint (`/mcp`, `/mcp/<pool>`, and `/agent/<pool>`) now authenticates first, and endpoint admission follows, before any body is read. Every pre-dispatch body read is bounded: classification reads at most 64 KiB, and past that connecta answers 413 with JSON-RPC code `-33006` and stops reading. A confirmed modern listen still takes no permit and is refused as unsupported; any other body carrying that header takes a permit before dispatch.
|
|
100
|
+
- Withhold the whole query and fragment of canonical Notion file links embedded in every REST response and error string, including Markdown and async results. Match only each URL's actual authority, preserving external proxies and look-alike hosts with ordinary parameters; AWS and Google signing parameter values are still withheld at any host. Refuse writes carrying the exact plain or encoded `[redacted]` placeholder in a Notion file link's own query or fragment before dispatch. Admission checks every adjacent Markdown destination, recognizes leading spaces, tabs, or one line ending, and respects balanced or escaped parentheses. Both scans stay linear. Split rich-text runs, encoded hosts or signing names, bare hosts without `//`, and userinfo before a file host are outside this scan's threat model.
|
|
101
|
+
- Withhold AWS SigV4/Google V4 signing-parameter values in every decoded Stripe, Vercel, and Cloudflare REST string, and generic credential-named URL queries/fragments only on reviewed diagnostic paths and vendor error text. Preserve ordinary code/key/token URLs in customer descriptions, metadata, billing text, DNS TXT, KV values and operator HTML. Refuse withheld URL destinations before every supported write transport, including decoded raw/base64 and multipart uploads, documented base64 JSON fields, and JSON object keys; scan every JSON key and value iteratively within the host's request-size limit without separate depth/breadth ceilings. Decode documented base64 fields once within their encoded length, retaining Cloudflare KV's 25 MiB value limit instead of applying the response cap. Cover indexed Vercel deployment lists and Cloudflare telemetry source payloads and invocation maps, with a pinned-operation guard for embedded-URL rules. Scan original escaped text and relative Markdown destinations before projections. Binary/base64 response bodies remain unchanged; Cloudflare omits envelope cursors that sanitizing would change. Notion retains its reviewed file-host and write-admission policy.
|
|
102
|
+
|
|
5
103
|
## 0.32.0 — 2026-10-14
|
|
6
104
|
|
|
7
105
|
Connecta 0.32.0 makes each endpoint simply writable or read-only, and lets a deployment keep chosen callers, such as an agent's token, off plain `/mcp` (#820, #821). A deployment can then give an agent one token and per-person pools, and the agent sees only the pool its client picks for each turn.
|
package/README.md
CHANGED
|
@@ -1,149 +1,128 @@
|
|
|
1
1
|
# connecta
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**One connection between your agent and the services you choose.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Connecta puts your MCP servers and HTTP APIs behind one endpoint that you run
|
|
6
|
+
yourself. Connect your agent once; decide in code who can reach what.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
Connect your agent once, then choose which integrations it can use. You run
|
|
9
|
-
Connecta yourself, on your own machine or infrastructure.
|
|
10
|
-
|
|
11
|
-
## The mental model
|
|
12
|
-
|
|
13
|
-
Ask your agent to find open issues, compare them with customer feedback, or
|
|
14
|
-
prepare a report. Connecta gives it a small set of tools for finding and using
|
|
15
|
-
the services you connected, without loading every service's tool definitions
|
|
16
|
-
into the conversation.
|
|
17
|
-
|
|
18
|
-
The agent writes a short program that runs in a sandbox beside those
|
|
19
|
-
integrations. It can gather results, combine them, and return the parts that
|
|
20
|
-
answer your question. Fifty issues can become a short list grouped by owner
|
|
21
|
-
before the result reaches the conversation. The agent writes the code; you
|
|
22
|
-
ask for the work.
|
|
8
|
+
## How it works
|
|
23
9
|
|
|
24
10
|
```mermaid
|
|
25
11
|
flowchart LR
|
|
26
|
-
Agent["Your agent"] <-->|"One connection"| Connecta
|
|
27
|
-
|
|
28
|
-
Connecta
|
|
29
|
-
|
|
12
|
+
Agent["Your agent"] <-->|"One connection<br/>six small tools"| Connecta
|
|
13
|
+
|
|
14
|
+
subgraph Connecta["Your Connecta deployment"]
|
|
15
|
+
Sandbox["Sandbox<br/>runs the agent's short programs"]
|
|
16
|
+
Keys["Credentials<br/>stay on the server"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Connecta <--> Work["Linear, GitHub, Notion"]
|
|
20
|
+
Connecta <--> Business["Stripe, RevenueCat, Mixpanel"]
|
|
21
|
+
Connecta <--> Yours["Any remote MCP server<br/>or HTTP API"]
|
|
30
22
|
|
|
31
23
|
style Connecta fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b,stroke-width:2px
|
|
32
24
|
```
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
The agent doesn't load every service's tools into the conversation. It writes
|
|
27
|
+
a short program that gathers, joins, and trims results inside Connecta, so
|
|
28
|
+
only the answer comes back.
|
|
29
|
+
|
|
30
|
+
## What you can connect
|
|
31
|
+
|
|
32
|
+
Maintained connections: <!-- providers:start -->Basecamp, Breeze ChMS, Church Community Builder, Cloudflare, GitHub, Gmail, Google Docs, Google Drive, Google Forms, Google Sheets, Google Slides, Infisical, Linear, Mixpanel, Notion, Overflow, Planning Center, RevenueCat, Stripe, Tithe.ly, Vercel, and Wispr Flow<!-- providers:end -->.
|
|
33
|
+
Plus any remote MCP server or HTTP API you describe.
|
|
34
|
+
|
|
35
|
+
## Who can use it
|
|
36
|
+
|
|
37
|
+
| | People | Agents |
|
|
38
|
+
| ----------------- | ---------------------------------------------------- | ---------------------------------------- |
|
|
39
|
+
| What they are | You and your team | Bots, assistants, CI, scheduled jobs |
|
|
40
|
+
| How they prove it | Sign in: Clerk (Node) or Cloudflare Access (Workers) | One token Connecta issues to that agent |
|
|
41
|
+
| Their own logins | Yes, on personal connections | No. An agent uses accounts you assign it |
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
**Maintainers** are the people who run the deployment: they reach every tool
|
|
44
|
+
and manage credentials and agent tokens. Everyone else reaches only the pools
|
|
45
|
+
they are listed in.
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
### Pools
|
|
48
|
+
|
|
49
|
+
A **pool** is a named set of tools plus a list of who may use it. Each pool
|
|
50
|
+
has its own address, and every caller sees exactly that pool's tools.
|
|
42
51
|
|
|
43
52
|
```mermaid
|
|
44
|
-
flowchart
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
style Combine fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b,stroke-width:2px
|
|
54
|
-
style Answer fill:#dcfce7,stroke:#15803d,color:#14532d
|
|
53
|
+
flowchart LR
|
|
54
|
+
Maint["Maintainer<br/>(signs in)"] --> Main["/mcp<br/>every tool"]
|
|
55
|
+
Bob["Bob<br/>(signs in)"] --> Team["/mcp/team<br/>CRM + tickets"]
|
|
56
|
+
Bot["assistant agent<br/>(token)"] --> AgentPool["/agent/assistant<br/>CRM + tickets"]
|
|
57
|
+
|
|
58
|
+
style Main fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b
|
|
59
|
+
style Team fill:#dcfce7,stroke:#15803d,color:#14532d
|
|
60
|
+
style AgentPool fill:#fef3c7,stroke:#b45309,color:#78350f
|
|
55
61
|
```
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Connecta includes maintained connections for <!-- providers:start -->Basecamp, Breeze ChMS, Church Community Builder, Cloudflare, GitHub, Gmail, Google Docs, Google Drive, Google Forms, Google Sheets, Google Slides, Infisical, Linear, Mixpanel, Notion, Overflow, Planning Center, RevenueCat, Stripe, Tithe.ly, Vercel, and Wispr Flow<!-- providers:end -->.
|
|
69
|
-
Each connection has its own setup guide.
|
|
70
|
-
Stripe, Notion, Cloudflare, and Vercel use their hosted MCP servers when you
|
|
71
|
-
sign in with OAuth, and Connecta's own API connections when you use an API key,
|
|
72
|
-
a Cloudflare API token or Global API Key, a Notion integration token, or a
|
|
73
|
-
Vercel access token. You can also
|
|
74
|
-
connect other remote MCP servers or define the operations you need from an
|
|
75
|
-
HTTP API.
|
|
76
|
-
|
|
77
|
-
Google Workspace connections use the account your deployment assigns to each
|
|
78
|
-
signed-in person. A Workspace administrator authorizes them for your domain.
|
|
79
|
-
Gmail reads mail and attachments and writes drafts, but does not send mail.
|
|
80
|
-
Google Drive can read, write, and share files, but does not permanently delete
|
|
81
|
-
them. Available
|
|
82
|
-
operations and account access depend on the integration and your deployment.
|
|
83
|
-
|
|
84
|
-
Large results need not fill the conversation. The agent can filter and
|
|
85
|
-
summarize them in its program, or read a large result in smaller pieces.
|
|
86
|
-
|
|
87
|
-
## Writes, credentials, and access
|
|
88
|
-
|
|
89
|
-
Connecta distinguishes reads from writes, and each endpoint is either writable
|
|
90
|
-
or read-only. By default an endpoint is writable: your agent can make a change
|
|
91
|
-
with one visible tool call or inside a program, and your agent's host decides
|
|
92
|
-
whether to ask for approval; it sees the program tool as a write. You can mark
|
|
93
|
-
the whole deployment or a single endpoint read-only, and then nothing there can
|
|
94
|
-
change anything: search leaves write tools out and every write is refused. You
|
|
95
|
-
make this choice explicitly for each deployment. Neither choice gives a caller
|
|
96
|
-
access to additional services.
|
|
63
|
+
People use a pool at `/mcp/<pool>` and agents at `/agent/<pool>`. A caller
|
|
64
|
+
outside a pool gets the same "not found" as a pool that doesn't exist, and
|
|
65
|
+
someone in two pools uses two addresses: pools never merge.
|
|
66
|
+
|
|
67
|
+
### One agent, many people
|
|
68
|
+
|
|
69
|
+
An assistant that talks to several people keeps **one token and one
|
|
70
|
+
connection**. Its harness, not the model, picks the pool each turn: a chat
|
|
71
|
+
gets the most capable tier that everyone in it is cleared for, and a
|
|
72
|
+
one-on-one gets that person's own pool.
|
|
97
73
|
|
|
98
74
|
```mermaid
|
|
99
|
-
flowchart
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
style
|
|
75
|
+
flowchart LR
|
|
76
|
+
Guests["Chat with anyone unknown"] --> Base["/agent/assistant<br/>read-only basics"]
|
|
77
|
+
Group["Chat of team members"] --> TeamTier["/agent/assistant-team<br/>shared tools"]
|
|
78
|
+
Leads["Chat of leads only"] --> LeadsTier["/agent/assistant-leads<br/>shared tools + billing"]
|
|
79
|
+
Alice["One-on-one<br/>with Alice"] --> PA["/agent/assistant-alice<br/>her tier + her accounts"]
|
|
80
|
+
|
|
81
|
+
Base -.-> TeamTier -.-> LeadsTier
|
|
82
|
+
|
|
83
|
+
style Base fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b
|
|
84
|
+
style TeamTier fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b
|
|
85
|
+
style LeadsTier fill:#e0e7ff,stroke:#4338ca,color:#1e1b4b
|
|
86
|
+
style PA fill:#dcfce7,stroke:#15803d,color:#14532d
|
|
108
87
|
```
|
|
109
88
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
89
|
+
Each tier builds on the one below it. A person's own pool builds on the
|
|
90
|
+
highest tier they are cleared for and holds only the agent, since anyone else
|
|
91
|
+
in it would act as that person. A tier can double as a team's own pool for
|
|
92
|
+
the people cleared for it. [Organizing an agent's pools](./documentation/auth.md#organizing-an-agents-pools)
|
|
93
|
+
has the full layout.
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
clients use tokens issued by your Connecta deployment. A deployment can serve
|
|
118
|
-
several people within one tenant, each with the access you assign.
|
|
95
|
+
### Whose account a connection uses
|
|
119
96
|
|
|
120
|
-
|
|
97
|
+
- **Shared** (the default): the deployment's account, connected once by a
|
|
98
|
+
maintainer.
|
|
99
|
+
- **Personal**: each person's own account. Agents never see these.
|
|
100
|
+
- **Google Workspace**: the signed-in person's account; for an agent, the
|
|
101
|
+
owner of a person's pool, or one fixed shared account. Without an owner,
|
|
102
|
+
mapping connections are absent for agents.
|
|
121
103
|
|
|
122
|
-
|
|
123
|
-
and configuration. Authorized people can connect accounts, manage credentials,
|
|
124
|
-
and issue client tokens. Optional activity history shows who called what,
|
|
125
|
-
when, and whether it worked, without recording arguments, results, or program
|
|
126
|
-
code. The UI's controls follow the signed-in person's permissions.
|
|
104
|
+
## Reads and writes
|
|
127
105
|
|
|
128
|
-
|
|
129
|
-
|
|
106
|
+
Every endpoint is writable or read-only, and you choose explicitly. A
|
|
107
|
+
read-only endpoint hides write tools and refuses every write. On a writable
|
|
108
|
+
one, your agent's host decides what to approve.
|
|
130
109
|
|
|
131
110
|
## Where it runs
|
|
132
111
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
112
|
+
Node or Docker (Clerk sign-in, SQLite) or Cloudflare Workers (Cloudflare
|
|
113
|
+
Access, D1). Both serve the same agent tools. An optional operator UI shows
|
|
114
|
+
a connection map, endpoints, connectors, and activity; integrations and access
|
|
115
|
+
still change only in deployment code.
|
|
136
116
|
|
|
137
117
|
## Getting started
|
|
138
118
|
|
|
139
|
-
Give your agent the [agent documentation](./documentation/README.md) and ask
|
|
140
|
-
to set up Connecta with the integrations you want
|
|
141
|
-
|
|
142
|
-
Cloudflare Workers, including connecting your agent client.
|
|
119
|
+
Give your agent the [agent documentation](./documentation/README.md) and ask
|
|
120
|
+
it to set up Connecta with the integrations you want, where to run it, and who
|
|
121
|
+
should have access.
|
|
143
122
|
|
|
144
123
|
<a id="existing-client-tokens"></a>
|
|
145
124
|
|
|
146
|
-
|
|
125
|
+
Upgrading? Point your agent at the
|
|
147
126
|
[upgrade guidance](./documentation/deploying.md#upgrade-an-existing-deployment).
|
|
148
127
|
|
|
149
128
|
## Status
|
|
@@ -152,7 +131,7 @@ Built for its author's deployments first and published openly. Breaking
|
|
|
152
131
|
changes are expected before 1.0. See the [changelog](./CHANGELOG.md) and
|
|
153
132
|
[security policy](./SECURITY.md).
|
|
154
133
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
134
|
+
[Principles](https://github.com/zackbart/connecta/blob/main/PRINCIPLES.md) ·
|
|
135
|
+
[Decision records](https://github.com/zackbart/connecta/tree/main/decisions) ·
|
|
136
|
+
[Planned work](https://github.com/zackbart/connecta/issues) (a plan is not a
|
|
137
|
+
shipped feature)
|
package/SECURITY.md
CHANGED
|
@@ -20,5 +20,5 @@ The MCP TypeScript SDK 2.x packages and Effect are exact-pinned. Effect is the
|
|
|
20
20
|
runtime core, and its unstable modules can break in a minor release, so its pin
|
|
21
21
|
moves in a dedicated upgrade. The split client and server packages no longer
|
|
22
22
|
install Hono or `@hono/node-server`, so the temporary root-project override
|
|
23
|
-
used with SDK 1.x is gone. A dependency bump must pass `
|
|
23
|
+
used with SDK 1.x is gone. A dependency bump must pass `pnpm run release:check`
|
|
24
24
|
and the Node/Workers runtime suites before a pin moves.
|
package/bin/connecta.mjs
CHANGED
|
@@ -20,7 +20,7 @@ function usage() {
|
|
|
20
20
|
console.log(`Usage:
|
|
21
21
|
connecta init [directory]
|
|
22
22
|
connecta migrate-state <state.json> <connecta.sqlite>
|
|
23
|
-
CONNECTA_TOKEN=<bearer> connecta doctor [--config] [--url http://localhost:8787]
|
|
23
|
+
CONNECTA_TOKEN=<bearer> connecta doctor [--config] [--url http://localhost:8787] [--pool <name>]
|
|
24
24
|
CF_ACCESS_CLIENT_ID=<id> CF_ACCESS_CLIENT_SECRET=<secret> connecta doctor --url https://worker.example`);
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -78,7 +78,8 @@ async function init() {
|
|
|
78
78
|
console.log(` ${shellCd(target)}`);
|
|
79
79
|
console.log(" npm install");
|
|
80
80
|
console.log(" # Choose readOnly in src/connecta.config.ts: false writes with client approval, true refuses writes.");
|
|
81
|
-
console.log(
|
|
81
|
+
console.log(" # Declare each machine client in agents in src/connecta.config.ts; local-machine is the first.");
|
|
82
|
+
console.log(" npm run --silent rotate-token -- local-machine");
|
|
82
83
|
console.log(" # Save the returned cta_ token privately for your MCP client and doctor.");
|
|
83
84
|
console.log(" npm start");
|
|
84
85
|
console.log(" # README.md covers database paths and Docker provisioning.");
|
|
@@ -137,13 +138,19 @@ async function doctorFetch(url, init = {}) {
|
|
|
137
138
|
async function doctor() {
|
|
138
139
|
for (let index = 0; index < args.length; index++) {
|
|
139
140
|
if (args[index] === "--config") continue;
|
|
140
|
-
if (
|
|
141
|
+
if (!["--url", "--pool"].includes(args[index]) || !args[++index] || args[index].startsWith("--")) {
|
|
141
142
|
usage();
|
|
142
143
|
process.exitCode = 1;
|
|
143
144
|
return;
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
const requestedUrl = option("--url", "http://localhost:8787");
|
|
148
|
+
// An agent token reaches only /agent/<pool> for the pools that list the
|
|
149
|
+
// agent; /mcp and /mcp/<pool> admit people alone.
|
|
150
|
+
const pool = option("--pool", undefined);
|
|
151
|
+
if (pool !== undefined && !/^[a-z0-9_-]+$/.test(pool)) {
|
|
152
|
+
throw new Error("Doctor --pool must be a pool name: lowercase letters, digits, _ and -.");
|
|
153
|
+
}
|
|
147
154
|
const parsedUrl = new URL(requestedUrl);
|
|
148
155
|
if (parsedUrl.username || parsedUrl.password) {
|
|
149
156
|
throw new Error("Doctor URL must not contain credentials.");
|
|
@@ -167,6 +174,10 @@ async function doctor() {
|
|
|
167
174
|
"Set CONNECTA_TOKEN or a CF_ACCESS_CLIENT_ID/CF_ACCESS_CLIENT_SECRET pair so doctor can inspect the MCP surface.",
|
|
168
175
|
);
|
|
169
176
|
}
|
|
177
|
+
const agent = Boolean(token?.startsWith("cta_"));
|
|
178
|
+
if (agent && pool === undefined && !args.includes("--config")) {
|
|
179
|
+
throw new Error("An agent token reaches only /agent/<pool>: pass --pool with a pool that lists the agent.");
|
|
180
|
+
}
|
|
170
181
|
const authHeaders = {
|
|
171
182
|
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
172
183
|
...(accessClientId && accessClientSecret
|
|
@@ -189,7 +200,12 @@ async function doctor() {
|
|
|
189
200
|
} catch {
|
|
190
201
|
throw new Error("Deployment returned an invalid config contract.");
|
|
191
202
|
}
|
|
192
|
-
|
|
203
|
+
// Operator contract v2 (#837) serves a pool member their own view, which
|
|
204
|
+
// carries no config; only a maintainer's carries the snapshot.
|
|
205
|
+
if (contract?.schemaVersion === 2 && contract.role === "member") {
|
|
206
|
+
throw new Error("The config snapshot is for maintainers only.");
|
|
207
|
+
}
|
|
208
|
+
if (contract?.schemaVersion !== 2 || contract.role !== "maintainer" || contract.config?.schemaVersion !== 1) {
|
|
193
209
|
throw new Error("Deployment returned an unsupported config contract.");
|
|
194
210
|
}
|
|
195
211
|
// The server builds this with describeConfig's allowlist; do not print
|
|
@@ -221,8 +237,10 @@ async function doctor() {
|
|
|
221
237
|
{ name: "connecta-doctor", version: CONNECTA_VERSION },
|
|
222
238
|
{ versionNegotiation: { mode: "auto", probe: { timeoutMs: DOCTOR_TIMEOUT_MS } } },
|
|
223
239
|
);
|
|
224
|
-
|
|
225
|
-
|
|
240
|
+
// Agent endpoints bypass Access at the edge and read only the cta_ token.
|
|
241
|
+
const endpoint = agent ? `/agent/${pool}` : `/mcp${pool ? `/${pool}` : ""}`;
|
|
242
|
+
const transport = new StreamableHTTPClientTransport(new URL(`${baseUrl}${endpoint}`), {
|
|
243
|
+
requestInit: { headers: agent ? { Authorization: `Bearer ${token}` } : authHeaders },
|
|
226
244
|
fetch: doctorFetch,
|
|
227
245
|
});
|
|
228
246
|
let negotiatedVersion;
|
package/bin/version.mjs
CHANGED