@zackbart/connecta 0.36.0 → 0.36.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +1 -1
  3. package/bin/version.mjs +1 -1
  4. package/dist/auth/downstream-oauth.js +3 -0
  5. package/dist/auth/oauth-authorization-params.js +52 -0
  6. package/dist/auth/static-oauth.js +1 -4
  7. package/dist/connectors/option-shapes.js +1 -0
  8. package/dist/connectors/remote-mcp.d.ts +4 -1
  9. package/dist/connectors/remote-mcp.js +12 -1
  10. package/dist/google.d.ts +13 -0
  11. package/dist/google.js +42 -0
  12. package/dist/http/hatch-paging.js +2 -2
  13. package/dist/http/paging.js +3 -88
  14. package/dist/http/provider.js +1 -2
  15. package/dist/http/tools.js +1 -23
  16. package/dist/program-writes.js +0 -1
  17. package/dist/providers/_shared/google/service-account.d.ts +1 -1
  18. package/dist/providers/_shared/google/service-account.js +20 -8
  19. package/dist/providers/_shared/sensitive-fields.js +0 -16
  20. package/dist/providers/breeze/api.js +2 -2
  21. package/dist/providers/ccb/api.js +1 -1
  22. package/dist/providers/cloudflare/api.js +8 -8
  23. package/dist/providers/cloudflare/lib/config.d.ts +2 -1
  24. package/dist/providers/docs/api.js +1 -1
  25. package/dist/providers/drive/api.js +1 -1
  26. package/dist/providers/drive/lib/hatch.js +1 -1
  27. package/dist/providers/drive/tools.js +1 -1
  28. package/dist/providers/forms/api.js +1 -1
  29. package/dist/providers/forms/lib/hatch.js +1 -1
  30. package/dist/providers/forms/tools.js +1 -1
  31. package/dist/providers/github/api.js +1 -1
  32. package/dist/providers/github/lib/discovery.js +2 -2
  33. package/dist/providers/gmail/api.js +1 -1
  34. package/dist/providers/granola/api.js +155 -0
  35. package/dist/providers/granola/index.d.ts +10 -0
  36. package/dist/providers/granola/index.js +68 -0
  37. package/dist/providers/granola/openapi.generated.js +30 -0
  38. package/dist/providers/granola/safety.js +37 -0
  39. package/dist/providers/granola/skill.generated.js +5 -0
  40. package/dist/providers/granola/tools.js +2 -0
  41. package/dist/providers/overflow/api.js +26 -26
  42. package/dist/providers/planning-center/api.js +4 -4
  43. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  44. package/dist/providers/planning-center/lib/json-api.js +2 -2
  45. package/dist/providers/quickbooks/lib/query.js +1 -1
  46. package/dist/providers/quickbooks/safety.js +1 -1
  47. package/dist/providers/sheets/api.js +1 -1
  48. package/dist/providers/sheets/lib/hatch.js +2 -2
  49. package/dist/providers/sheets/lib/requests.js +1 -1
  50. package/dist/providers/slides/api.js +1 -1
  51. package/dist/providers/stripe/api.js +7 -7
  52. package/dist/providers/tithely/api.js +2 -2
  53. package/dist/providers/vercel/api.js +12 -12
  54. package/dist/providers/vercel/lib/pin.js +1 -1
  55. package/dist/request-token.d.ts +7 -0
  56. package/dist/request-token.js +1 -0
  57. package/dist/routes/mcp.js +1 -1
  58. package/dist/runtime/admission.js +0 -12
  59. package/dist/storage/keys.js +2 -3
  60. package/dist/version.d.ts +1 -1
  61. package/dist/version.js +1 -1
  62. package/documentation/README.md +13 -12
  63. package/documentation/architecture.md +148 -76
  64. package/documentation/auth.md +198 -4
  65. package/documentation/code-mode.md +6 -6
  66. package/documentation/meta-tools.md +5 -2
  67. package/documentation/operator-tests.md +11 -2
  68. package/documentation/operator-ui.md +1 -2
  69. package/documentation/providers.md +110 -0
  70. package/package.json +10 -2
  71. package/templates/node/package.json +1 -1
@@ -14,7 +14,7 @@ credential contracts; use [deployment setup](./deploying.md) for runnable steps.
14
14
  | Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [Clerk Organizations](#one-clerk-instance-one-organization-per-deployment), [management](#human-authentication-management) |
15
15
  | Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting) |
16
16
  | Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say) |
17
- | Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens) |
17
+ | Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens), [Google hosted MCP](#google-service-account-tokens-and-hosted-mcp) |
18
18
  | Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant) |
19
19
 
20
20
  [Credential recognition and admission](#credential-recognition-and-admission)
@@ -412,6 +412,11 @@ agent. Enabling `preview` lets code on any pushed preview branch act as the agen
412
412
  Treat the project's OIDC token as a bearer credential for all of this agent's
413
413
  pools. Its default audience, `https://vercel.com/<team-slug>`, is shared by every
414
414
  service accepting Vercel's default OIDC tokens; it is not specific to Connecta.
415
+ Connecta accepts only that default audience. Vercel's custom-audience token
416
+ exchange can produce a validly signed token for another service; Connecta
417
+ refuses it. For example, a token exchanged for `https://tickets.example.com`
418
+ cannot authenticate to `/agent/assistant`. Resolve the ordinary workload token
419
+ with `getVercelOidcToken()` without an `audience` option.
415
420
  Anything holding the project's token can act as the agent:
416
421
  project code, builds (including build steps and dependency install scripts with
417
422
  access to `VERCEL_OIDC_TOKEN`), and services the project sends the token to,
@@ -2493,6 +2498,48 @@ stored static-client state and reselects the configured identity.
2493
2498
  Configured `scope` is a fallback; a downstream challenge or protected-resource
2494
2499
  declaration takes precedence, and the SDK adds `offline_access` when advertised.
2495
2500
 
2501
+ ### Browser authorization parameters
2502
+
2503
+ Set `auth.authorizationParams` on an OAuth `remoteMcp()` connector to add
2504
+ non-secret provider parameters to the browser consent URL. This works with
2505
+ pre-registered clients, CIMD, and DCR. For example:
2506
+
2507
+ ```ts
2508
+ remoteMcp("crm", {
2509
+ url: "https://mcp.example/mcp",
2510
+ auth: {
2511
+ type: "oauth",
2512
+ scope: "records.read",
2513
+ authorizationParams: { access_type: "offline", prompt: "consent" },
2514
+ },
2515
+ });
2516
+ ```
2517
+
2518
+ Each configured parameter occurs once, replacing any same-name value already
2519
+ on the authorization endpoint URL. These parameters are never added to token
2520
+ exchange, refresh, registration, revocation, metadata, or MCP requests.
2521
+ Descriptions expose parameter names only. Values are public configuration,
2522
+ visible in the consent URL and stored with it; never put secrets here.
2523
+
2524
+ The map must be a plain string record with at most 32 entries. Names contain
2525
+ 1–128 ASCII letters, digits, dots, underscores, or hyphens. Values must be
2526
+ nonempty (not whitespace-only), at most 2048 UTF-16 code units, and contain no
2527
+ ASCII control characters. Case-variant duplicate names are refused. The
2528
+ following names, including every case variant, fail construction with
2529
+ `ConfigError`: `response_type`, `response_mode`, `client_id`, `client_secret`,
2530
+ `redirect_uri`, `state`, `code_challenge`, `code_challenge_method`, `scope`,
2531
+ `resource`, `request`, and `request_uri`. Configure scopes through `auth.scope`.
2532
+ `response_mode` is reserved because Connecta accepts callbacks only as GET query
2533
+ parameters.
2534
+
2535
+ For remote MCP, the map joins the stored client configuration binding, like
2536
+ scope and client identity. Adding, removing, or changing a parameter requires
2537
+ Restart or Disconnect and reconnect to obtain a grant with the new consent
2538
+ settings. The map does not rewrite existing grants or pending consent URLs;
2539
+ as with scope, reconfiguring it alone does not obtain a new grant. A DCR
2540
+ registration created under different parameters cannot be carried through
2541
+ Restart. Key order is irrelevant; an empty map and omission are equivalent.
2542
+
2496
2543
  ## Downstream OAuth on `api()`
2497
2544
 
2498
2545
  `api()` takes a static authorization-code configuration for APIs without MCP
@@ -2527,7 +2574,7 @@ No discovery or registration occurs; all URLs are configuration, so the
2527
2574
  | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
2528
2575
  | Endpoints | Checked at construction: HTTPS, HTTP only on loopback, no credentials or fragment. |
2529
2576
  | `pkce` | S256 by default; `false` omits challenge and verifier. |
2530
- | `authorizationParams` | Adds provider parameters; cannot restate the grant's own. |
2577
+ | `authorizationParams` | Browser-only string parameters. Exact names `response_type`, `client_id`, `redirect_uri`, `state`, `scope`, `code_challenge`, `code_challenge_method` and `resource` are refused; other names and string values are accepted as before. Remote MCP's [stricter rules](#browser-authorization-parameters) do not apply here. |
2531
2578
  | `expectedCallbackParameters` | Optional `Readonly<Record<string, string>>` of fixed provider response values. Each configured parameter must occur exactly once with its exact decoded value, after pending-state verification and before issuer validation, flow claims, code exchange, or grant persistence. Missing, duplicate, or mismatched values refuse without exchanging a code or returning parameter values. Omission preserves existing behavior. |
2532
2579
  | `revocationEndpoint` | Optional trusted HTTPS revocation URL (loopback HTTP allowed), without credentials, query or fragment. Disconnect removes the local grant before one bounded attempt using the configured client. Failure reports `oauth_revocation_failed`; a repeated Disconnect never retries. |
2533
2580
  | `revocationRequestFormat` | `"form"` by default; `"json"` sends JSON without `token_type_hint`. Requires `revocationEndpoint` when set. |
@@ -2580,6 +2627,15 @@ owner's access token as `Authorization: Bearer` with these rules:
2580
2627
  - The answer's `.text()` and `.json()`, clones' too, decode its bytes as UTF-8, so
2581
2628
  workerd never quotes a downstream's Content-Type in its own log.
2582
2629
 
2630
+ `api()` already supports `oauth.authorizationParams` through the same OAuth
2631
+ provider and browser-only injection. It keeps its existing, more permissive
2632
+ validation (see the `api()` option table), so existing configurations still
2633
+ construct. Use only GET query response modes with it. Its existing binding remains
2634
+ unchanged: the token endpoint identifies the grant; scope, client identity,
2635
+ and authorization parameters do not add a stored client configuration binding.
2636
+ Changing authorization parameters affects new consents; reconnect to obtain a
2637
+ grant with different consent settings.
2638
+
2583
2639
  The grant record and consents, epoch fencing, vault sealing, shared/personal ownership,
2584
2640
  callback state/principal checks, `authorize_connector`, Connect, Restart, and
2585
2641
  Disconnect match `remoteMcp()`. Status calls report a stored grant for the
@@ -2598,6 +2654,14 @@ transport, are not stored or described, and a changed token replaces the client.
2598
2654
  This mode refuses cleartext non-loopback origins. Optional `auth.headers` carry
2599
2655
  static protocol/catalog controls and cannot set Authorization.
2600
2656
 
2657
+ A resolver may expose `token.invalidate(rejectedToken, ctx): Promise<void>` to
2658
+ remove a cached token after a downstream HTTP 401. The hook receives the exact
2659
+ token sent by that transport, so it must leave a newer cached token intact.
2660
+ Hook failures are withheld and preserve the original 401. Request auth never
2661
+ automatically repeats the operation or renews the grant inside the failed
2662
+ scope: authorization failure stays latched for that scope. A new request resolves
2663
+ the callback again. In particular, no dispatched write is replayed.
2664
+
2601
2665
  The GitHub provider validates each tool's owner/repo and scope access before
2602
2666
  resolving an installation token. It creates one token-bound MCP client for that
2603
2667
  operation and closes it before returning. Completed installation/token caches
@@ -2608,6 +2672,134 @@ is retained across requests, and rejected writes are never replayed. An omitted
2608
2672
  `describe()` includes neither the key nor the minted tokens. The maintained
2609
2673
  provider skill explains App permissions and installation prerequisites.
2610
2674
 
2675
+ ### Google service-account tokens and hosted MCP
2676
+
2677
+ Import `googleServiceAccountToken` from `@zackbart/connecta/google` to connect a
2678
+ Google hosted MCP server using a service-account JSON key on Node or Workers.
2679
+ This optional subpath is outside the root import graph. Supply deployment secrets
2680
+ through the environment or platform secret store:
2681
+
2682
+ ```ts
2683
+ import { remoteMcp } from "@zackbart/connecta";
2684
+ import { googleServiceAccountToken } from "@zackbart/connecta/google";
2685
+
2686
+ const warehouse = remoteMcp("warehouse", {
2687
+ url: "https://bigquery.googleapis.com/mcp",
2688
+ authScope: "shared",
2689
+ auth: {
2690
+ type: "request",
2691
+ token: googleServiceAccountToken({
2692
+ serviceAccount: env.GOOGLE_SERVICE_ACCOUNT_JSON,
2693
+ scopes: ["https://www.googleapis.com/auth/bigquery"],
2694
+ }),
2695
+ },
2696
+ });
2697
+
2698
+ const warehousePersonal = remoteMcp("warehouse_personal", {
2699
+ url: "https://bigquery.googleapis.com/mcp",
2700
+ authScope: "personal",
2701
+ auth: {
2702
+ type: "oauth",
2703
+ scope: "https://www.googleapis.com/auth/bigquery",
2704
+ authorizationParams: { access_type: "offline", prompt: "consent" },
2705
+ client: {
2706
+ issuer: "https://accounts.google.com",
2707
+ clientId: env.GOOGLE_CLIENT_ID,
2708
+ clientSecret: env.GOOGLE_CLIENT_SECRET,
2709
+ tokenEndpointAuthMethod: "client_secret_post",
2710
+ },
2711
+ },
2712
+ });
2713
+ ```
2714
+
2715
+ `serviceAccount` accepts downloaded JSON text or
2716
+ `{ clientEmail, privateKey, clientId? }`; `privateKey` is a PKCS#8 RSA PEM of at
2717
+ least 2048 bits. `scopes` must be a non-empty array of HTTPS URLs without
2718
+ credentials, whitespace, or fragments. Unknown options fail construction with
2719
+ `ConfigError`. An optional fixed `subject: "alice@example.com"` requests
2720
+ Workspace domain-wide delegation and requires its admin grant. Omit `subject`
2721
+ for BigQuery: the signed assertion then has no `sub` and acts as the service
2722
+ account itself. See [Google's service-account grant documentation](https://developers.google.com/identity/protocols/oauth2/service-account).
2723
+
2724
+ The helper signs RS256 JWT-bearer assertions and exchanges them at Google's fixed
2725
+ token endpoint. It keeps only bounded in-memory token entries, partitioned by
2726
+ account, key fingerprint, absent-or-present subject and scope set. Concurrent
2727
+ mints share a flight; tokens refresh with a one-minute margin. Keys, assertions
2728
+ and tokens are never logged, persisted or described. Bearer tokens use request
2729
+ auth's existing secret redaction. A 401 evicts only the rejected token; the next
2730
+ request mints again. The failed operation is never automatically repeated.
2731
+
2732
+ #### Separate agent and people connections
2733
+
2734
+ Grant the shared connection to agent pools (including pools used by Eve agents)
2735
+ and the personal connection to pools used by people. Add these entries to the
2736
+ existing deployment configuration, keeping its inbound auth, executor, vault and
2737
+ storage configuration:
2738
+
2739
+ ```ts
2740
+ connectors: [warehouse, warehousePersonal],
2741
+ agents: ["assistant"],
2742
+ pools: {
2743
+ warehouse_agents: { tools: ["warehouse"], members: ["agent:assistant"] },
2744
+ warehouse_people: { tools: ["warehouse_personal"], members: ["user_alice"] },
2745
+ },
2746
+ ```
2747
+
2748
+ Replace `user_alice` with Alice's admitted principal ID. Agents have no principal;
2749
+ `authScope: "personal"` connectors never appear for them, even if included in a
2750
+ pool's tools. The shared connection uses its fixed service account for every
2751
+ member; the personal connection uses each person's own encrypted OAuth grant.
2752
+ People connect through `authorize_connector` or their member page. Follow
2753
+ [Add a person](#add-a-person), [agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens)
2754
+ and [shared credential ownership](#connect-a-shared-credential) for admission and
2755
+ pool membership. The request-token connection gets its key from deployment
2756
+ configuration and does not need a credential-vault slot.
2757
+
2758
+ #### Google-side setup
2759
+
2760
+ 1. Enable the BigQuery API in the Google Cloud project and confirm that the
2761
+ BigQuery MCP service is available under the project's organization policies.
2762
+ Google's current setup uses `https://bigquery.googleapis.com/mcp` and API
2763
+ enablement; follow its current server setup if enablement requirements change.
2764
+ Grant the service account MCP Tool User (`roles/mcp.toolUser`) and BigQuery
2765
+ Job User (`roles/bigquery.jobUser`) on the project where queries run.
2766
+ [BigQuery MCP setup and required roles](https://docs.cloud.google.com/bigquery/docs/use-bigquery-mcp).
2767
+ 2. Grant BigQuery Data Viewer (`roles/bigquery.dataViewer`) on the datasets the
2768
+ service account may query. Dataset grants restrict data access separately
2769
+ from project-level job execution. Grant further permissions only for the
2770
+ operations you intend to expose; OAuth scopes do not replace IAM.
2771
+ [BigQuery IAM roles and grant levels](https://docs.cloud.google.com/bigquery/docs/access-control).
2772
+ Create a service-account JSON key and store it as
2773
+ `GOOGLE_SERVICE_ACCOUNT_JSON` in the deployment secret store; the helper
2774
+ does not use ADC, metadata-server credentials, or workload identity federation.
2775
+ [Service-account key creation](https://docs.cloud.google.com/iam/docs/keys-create-delete).
2776
+ 3. For the personal connection, configure Google Auth Platform branding,
2777
+ audience and data access. Choose **Internal** for an application restricted to
2778
+ people in your Workspace organization, using a project belonging to that
2779
+ organization. Internal consent does not grant dataset access; each person
2780
+ still needs the applicable IAM permissions.
2781
+ [Configure consent and audience](https://developers.google.com/workspace/guides/configure-oauth-consent).
2782
+ 4. Create a **Web application** OAuth client under Google Auth Platform → Clients.
2783
+ Register the exact Connecta callback, for example
2784
+ `https://connecta.example/oauth/callback/warehouse_personal`, using the
2785
+ deployment's configured public origin. Store its client ID and secret as
2786
+ deployment secrets. Google's hosted MCP servers do not support DCR or client
2787
+ metadata documents, so configure the static `auth.client` shown above.
2788
+ [Web OAuth client setup](https://docs.cloud.google.com/mcp/set-up-authentication-mcp-servers#web),
2789
+ [Google MCP registration limitations](https://docs.cloud.google.com/mcp/authenticate-mcp#limitations).
2790
+
2791
+ The pre-registered client path supports Google's exact issuer and
2792
+ `client_secret_post`; Google's [discovery document](https://accounts.google.com/.well-known/openid-configuration)
2793
+ advertises that method, PKCE S256, and `https://oauth2.googleapis.com/token`.
2794
+ The personal example requests `access_type=offline` to obtain a refresh token
2795
+ and `prompt=consent` to show consent again when reconnecting. See
2796
+ [Google's offline-access requirements](https://developers.google.com/identity/protocols/oauth2/web-server#offline).
2797
+ Connecta retains the stored refresh token when a successful refresh response
2798
+ omits `refresh_token`, allowing later refreshes with that same token. After
2799
+ adding these parameters to an existing connector, Disconnect and reconnect.
2800
+ Live Google consent and project-specific permissions still need verification
2801
+ against the operator's project.
2802
+
2611
2803
  ## Contributor reference
2612
2804
 
2613
2805
  ### INV-5 output redaction limits
@@ -2698,8 +2890,10 @@ The encoding lattice is exactly this, and anything outside it is out of scope:
2698
2890
 
2699
2891
  [Decision 0007](https://github.com/zackbart/connecta/blob/main/decisions/0007-agent-pool-layout.md)
2700
2892
  explains the pool layout; [decision 0006](https://github.com/zackbart/connecta/blob/main/decisions/0006-pools-are-the-access-model.md)
2701
- explains pools as the access model. `test/identity-scope.test.ts` checks identity
2702
- boundaries, and `src/storage/keys.ts` owns credential key families.
2893
+ explains pools as the access model. `test/identity-write-journey.node.test.ts`
2894
+ checks real HTTP identity, pool and personal credential isolation;
2895
+ `test/identity-scope.test.ts` retains handoff, callback and admission-revocation
2896
+ fault probes. `src/storage/keys.ts` owns credential key families.
2703
2897
 
2704
2898
  Inbound adapters live in [src/auth](https://github.com/zackbart/connecta/tree/main/src/auth);
2705
2899
  [auth routes](https://github.com/zackbart/connecta/blob/main/src/routes/oauth.ts)
@@ -1274,7 +1274,7 @@ rejection, and branded adapter acceptance across module copies.
1274
1274
  | `S9` | `test/result-shapes.test.ts` (value exclusion, bounds, merging, LRU and time expiry, runtime isolation, read-only admission, declared precedence, definition invalidation, unwrapped MCP results, discovery provenance, copy isolation, failure isolation) |
1275
1275
  | `S10` | `test/resource-read.test.ts` (qualified routing, whole-connector grants, personal credentials, redaction, admission, timeout, cancellation and downstream MCP reads), both guest-contract executors (successful resource reads and caught/uncaught typed failures) |
1276
1276
  | `E2`, `E8` | `test/guest-api-contract.test.ts` (code → `retryable`, caught, parallel, and uncaught validation recovery, a conflict's bounded `current`), `test/meta-tools-call.test.ts` (direct, destructive, provider fallback), `test/validate.test.ts` (bounded payload-free findings), `test/errors.test.ts` |
1277
- | `E3`, `E4` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`auth_required`, destructive reroute), `test/program-writes.test.ts` (the refusal's `nextAction`, nothing sent, a caught refusal) |
1277
+ | `E3`, `E4` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (`auth_required`, destructive reroute), `test/identity-write-journey.node.test.ts` (read-only refusals before validation or dispatch, actual QuickJS) |
1278
1278
  | `E5` | `test/guest-api-contract.test.ts` (execution-failure channel, in-flight `cancelled`), `test/execute.test.ts` (admission), `test/executor-admission.test.ts`, `test/quickjs-executor.node.test.ts` (mid-run shutdown) |
1279
1279
  | `E6`, `X8` | `test/guest-api-contract.test.ts` (unknown and inherited members, retained error identities), `test/quickjs-executor.node.test.ts` |
1280
1280
  | `E7` | `test/guest-api-contract.test.ts` (refusals about a `503`-named connector), `test/errors.test.ts` |
@@ -1288,10 +1288,10 @@ rejection, and branded adapter acceptance across module copies.
1288
1288
  | `L3`, `X1` | `test/guest-api-contract.test.ts` (short-deadline executors), `test/execute.test.ts` (the watchdog ends a never-settling executor, frees the default pool, spares a slow run, and falls back from an unusable value) |
1289
1289
  | `L4`, `L8` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (shared discovery/call budgets and terminal catch-and-continue loops and queued-write cancellation on both executors), `test/worker-budget-response.node.test.ts` (native handle disposal and admission recovery across completed HTTP responses) |
1290
1290
  | `L5`, `L7`, `X2` | `test/quickjs-executor.node.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue) |
1291
- | `L6`, `X10` | `test/guest-api-contract-quickjs.node.test.ts` and `test/guest-api-contract.test.ts` (whole oversized reads and one-write pageable handles), `test/sqlite-storage.node.test.ts` and `test/d1-storage.node.test.ts` (shared stash transfers), `test/quickjs-executor.node.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message), `test/quickjs-child-stderr.node.test.ts` (outer reply serialization failure settles the call) |
1292
- | `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no event for the execution itself), `test/activity.test.ts` (the shared code → friction table, the identity clamp, the one-attempt floor), `test/operator-view.test.ts`, `test/sql-storage-contract.ts`, run by `test/d1-storage.node.test.ts` and `test/sqlite-storage.node.test.ts` (historical pause and approval rows still render and round-trip) |
1293
- | `V5`, `W9` | `test/program-writes.test.ts` (an unawaited program write finished and recorded, its unknown outcome reported, counts on a failed program, the classification table), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
1294
- | `W10`, `W12` | `test/program-writes.test.ts` (a writable endpoint's program write runs, a read-only endpoint omits writes from search and describe and refuses every write call on every path, including a write its guide names, a required deployment `readOnly`, the write budget, `readOnly` inheritance and overrides, `call_tool` still refusing, search and describe verdicts, construction refusals including retired `trust`), `test/operator-ui-model.test.ts`, `test/browser/operator-ui.spec.ts` (the badge) |
1291
+ | `L6`, `X10` | `test/guest-api-contract-quickjs.node.test.ts` and `test/guest-api-contract.test.ts` (whole oversized reads and one-write pageable handles), `test/sqlite-storage.node.test.ts` and `test/kv-to-d1.test.ts` (SQL stash and native D1 fault probes), `test/quickjs-executor.node.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message), `test/quickjs-child-stderr.node.test.ts` (outer reply serialization failure settles the call) |
1292
+ | `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no event for the execution itself), `test/activity.test.ts` (the shared code → friction table, the identity clamp, the one-attempt floor), `test/browser/operator-ui.spec.ts` (identity changes), `test/sql-storage-contract.ts`, run by `test/sqlite-storage.node.test.ts` (historical activity rows still round-trip) |
1293
+ | `V5`, `W9` | `test/program-writes.test.ts` (unawaited program drain, failed-program accounting), `test/identity-write-journey.node.test.ts` (committed ambiguity, reconciliation and physical writes), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
1294
+ | `W10`, `W12` | `test/identity-write-journey.node.test.ts` (real HTTP/MCP and QuickJS: write budget, read-only discovery and refusal, named guide, pool inheritance/reopening and connector policy), `test/program-writes.test.ts` (construction refusals and classification drift), `test/operator-ui-model.test.ts`, `test/browser/operator-ui.spec.ts` (the badge) |
1295
1295
  | `M1` | `test/guest-api-contract.test.ts` (invalid emits throw catchably, accept nothing), `test/execute-emit.test.ts` (rejected shapes, bounded native snapshots, paging before metadata traversal, and cyclic presentation refusal) |
1296
1296
  | `M2`, `M3` | `test/guest-api-contract.test.ts` (delivery order, truncated return plus delivered blocks), `test/execute-emit.test.ts` (envelope, `structuredContent`, byte-for-byte no-emit path) |
1297
1297
  | `M4` | `test/guest-api-contract.test.ts` (discard is visible), `test/execute-emit.test.ts` (structured and plain paths), `test/quickjs-executor.node.test.ts` (mid-run shutdown) |
@@ -1304,4 +1304,4 @@ rejection, and branded adapter acceptance across module copies.
1304
1304
 
1305
1305
  The surface itself is checked by `test/server.test.ts` (the exact six-tool list),
1306
1306
  `test/code-first-surface.test.ts` (construction, executor, removed tools, copy, and size),
1307
- and `test/program-writes.test.ts` (the retired pause options refused at construction).
1307
+ and `test/program-writes.test.ts` (deployment policy and classification construction refusals).
@@ -1334,8 +1334,11 @@ Connector ids already appear in native tool names. The
1334
1334
  operator UI and `describeConfig()` show every pool's effective `surface` and
1335
1335
  `executeCode` as facts from deployment code. Change these settings in code.
1336
1336
 
1337
- Keep native pools bounded. Comparisons so far used 40 synthetic tools;
1338
- large production catalogs near the cap have not been evaluated.
1337
+ Keep native pools bounded. Local real-Eve validation uses seeded catalogs from
1338
+ 40 through 1,000 downstream tools across meta, native and native-plus-code
1339
+ surfaces, with a deterministic model transport and no paid model calls. These
1340
+ checks establish local runtime contracts; model task quality and large
1341
+ production catalogs near the cap remain unmeasured.
1339
1342
 
1340
1343
  ### Eve agents
1341
1344
 
@@ -190,8 +190,17 @@ OAuth provider validates the code exchange and PKCE. Credential Test calls a
190
190
  loopback downstream endpoint. Operator APIs, cookie/bearer admission, CSRF,
191
191
  assets, handoffs, callbacks, and encrypted storage use production code.
192
192
 
193
- The flows cover Clerk sign-in/reload/sign-out; OAuth connect/consent/callback/
194
- connected reread/reload; and credential save/test/replace/remove. They collect
193
+ The flows use a real SQLite file and cover Clerk sign-in/reload/sign-out,
194
+ actual Settings provenance and asset cache validators; OAuth
195
+ connect/consent/callback/connected reread/disconnect; and credential
196
+ save/test/replace/remove. They close and reopen the app, database and vault
197
+ before rereading OAuth and credential state. A rejected SQL replacement leaves
198
+ the previous secret usable downstream after restart. Trigger rows read from an
199
+ independent SQL connection require one save, one replacement and one removal;
200
+ reads and the rejected save commit no credential writes. Actual MCP calls
201
+ check that credential edits preserve a reader pool's read access and refusal. Activity renders
202
+ successful calls recorded before the SQLite restart without exposing either
203
+ credential value. They collect
195
204
  CSP violations across every document and popup, and fail on unexpected network
196
205
  traffic. No credentials enter visual fixtures or screenshot baselines.
197
206
 
@@ -644,8 +644,7 @@ and each personal connector's exact `manage`;
644
644
  The Overview's map and attention rows are pure functions of the contract in
645
645
  [connection-map.ts](https://github.com/zackbart/connecta/blob/main/src/operator-ui/connection-map.ts)
646
646
  and [overview.ts](https://github.com/zackbart/connecta/blob/main/src/operator-ui/overview.ts),
647
- covered by `test/operator-connection-map.test.ts` and
648
- `test/operator-overview.test.ts`; `src/operator-ui/styles/overview.css` holds
647
+ exercised in Chromium by `test/browser/operator-overview.spec.ts`; `src/operator-ui/styles/overview.css` holds
649
648
  the page's styles.
650
649
 
651
650
  `src/operator-ui/config-summary.ts` maps the snapshot and its provenance to
@@ -7,6 +7,95 @@ roster](./integrating.md#add-or-remove-a-connector) covers the general
7
7
  procedure and the providers not listed here. Agent guidance for each
8
8
  provider ships with its connection's usage guide, not here.
9
9
 
10
+ ## Granola
11
+
12
+ ```ts
13
+ import { granola } from "@zackbart/connecta/providers/granola";
14
+
15
+ const notes = granola("granola", { purpose: "Shared workspace meeting notes" });
16
+ ```
17
+
18
+ Use a **workspace API key**, created by a Business or Enterprise workspace
19
+ admin in Granola desktop Settings → Connectors → Workspace API keys → Create
20
+ new key. Save it in this connection's credential slot through the operator UI;
21
+ configure an encrypted vault as described in [shared credentials](./auth.md#connect-a-shared-credential).
22
+ The shared, single-secret field is `value`. The provider fixes the origin to
23
+ `https://public-api.granola.ai`, uses bearer authentication, and exposes reads
24
+ only. It accepts `purpose`, optional `title`, `instructions`, and
25
+ `maxResultBytes` and the common `readOnly` restriction. Even `readOnly: false`
26
+ exposes only the five reviewed reads; it accepts no inline secret, origin override, personal auth
27
+ scope, or option enabling writes.
28
+
29
+ Workspace keys belong to the workspace, survive their creator leaving and do
30
+ not expire; an admin can revoke them. They reach public notes and spaces with
31
+ Granola API access enabled. For an existing space an admin uses Integrations →
32
+ Granola API → Allow access with a workspace API key. Folders inherit the space
33
+ setting. Workspace ownership does not grant unrelated private notes or the
34
+ owner's private handwritten notes. Discover accessible IDs before filtering.
35
+ Only notes with a generated summary and transcript are returned; a 404 does
36
+ not prove absence. A successful credential test confirms accessible spaces,
37
+ not the key's ownership type or access to every meeting. Granola enforces
38
+ these permissions; Connecta cannot infer workspace versus personal key type
39
+ from the secret. Do not substitute a personal API key or MCP OAuth token.
40
+
41
+ The REST surface is `granola_api_search`, `granola_api_details`, and
42
+ `granola_api_read`. Search discovers API operations, not meeting content.
43
+ Reads cover `/v1/notes`, `/v1/notes/{note_id}`,
44
+ `/v1/notes/{note_id}/transcript`, `/v1/folders`, and `/v1/spaces`. Use API IDs
45
+ (`not_`, `fol_`, `spa_`), not UUIDs copied from web links. Date/folder/space
46
+ filters and response `fields` narrowing follow the reviewed operation details.
47
+ Audit, legal holds, webhook management and writes are refused, even if their
48
+ contracts appear in operation discovery.
49
+
50
+ Omit `query.cursor` on the first read. Continue with `page.nextCursor` as the
51
+ top-level `cursor`, retaining the path and query filters. Continuations are
52
+ bound to that request and are not raw Granola tokens. The vendor uses
53
+ `query.page_size`: notes/folders/spaces default to 10 with maximum 30;
54
+ transcripts default to 50 with maximum 100. Empty pages can have more data.
55
+ No total count is inferred. HTTP 413 when using `include: "transcript"`
56
+ requires a new note-detail read without include and paged transcript reads;
57
+ the provider sends no hidden fallback requests. Photo download URLs grant
58
+ unauthenticated access and are always withheld. Meeting text and sent
59
+ credentials cross the same value-safety boundary before selection or paging.
60
+
61
+ Granola documents 25 requests per 5-second burst and 5 requests/second
62
+ sustained, shared across the workspace key's scope. The default per-runtime
63
+ admission is 25 calls in a rolling 5-second window with concurrency 2. Other
64
+ processes and integrations share the vendor allowance; the local counter
65
+ cannot enforce a workspace-wide limit. A 429 returns `rate_limited` and
66
+ Retry-After when supplied (otherwise a 5-second hint); calls are not retried
67
+ automatically. Requests have a 30-second deadline and 4 MiB response ceiling.
68
+ Oversized responses fail without presenting a partial success; inline result
69
+ cuts retain the standard `truncated` paths.
70
+
71
+ For hosted Granola MCP, use a distinct generic `remoteMcp()` connection to
72
+ `https://mcp.granola.ai/mcp` with its user OAuth or enterprise-managed
73
+ authorization. Granola MCP does not accept workspace API keys or service
74
+ accounts. Its natural-language search is not a REST-provider capability.
75
+ See [Granola's workspace-key guide](https://docs.granola.ai/help-center/sharing/integrations/granola-api#workspace-api-keys)
76
+ and [API overview](https://docs.granola.ai/introduction).
77
+
78
+ ### Migrate a deployment-local Granola API adapter
79
+
80
+ Replace the local `api("granola", …)` construction with
81
+ `granola("granola", { purpose: "Shared workspace meeting notes" })`, retaining
82
+ the same encrypted vault, encryption key, connector ID, shared owner and
83
+ single-secret `value` field. The provider reuses that credential without
84
+ copying it. Existing addresses `granola.granola_api_search`,
85
+ `granola.granola_api_details`, and `granola.granola_api_read` remain stable.
86
+ Keep existing pool references to `granola`. A differently named or personal
87
+ credential field requires the operator to save a workspace key in the shared
88
+ slot; do not read out or copy stored secrets through an agent.
89
+
90
+ Compare local argument contracts before replacing the factory: the maintained
91
+ search is operation discovery, details uses an indexed operation, and read
92
+ uses a relative path and query. Replace raw `query.cursor` continuations with
93
+ the bound top-level `cursor`, and start new pagination sequences after the
94
+ upgrade. Responses use `{ status, data, page? }`. Remove local write tools or
95
+ natural-language-search assumptions; do not keep duplicate factories with the
96
+ same connector ID. If a local adapter used other tool names, update its
97
+ callers explicitly. No data migration or downstream write is required.
98
+
10
99
  ## API sources and tool inventory
11
100
 
12
101
  Maintained providers pair reviewed API contracts and indexes with Connecta's
@@ -143,6 +232,20 @@ discovery for your deployment's catalog.
143
232
 
144
233
  </details>
145
234
 
235
+ <details>
236
+ <summary>Granola (3 tools)</summary>
237
+
238
+ [Setup](#granola). **API source:** [Upstream OpenAPI](https://docs.granola.ai/api-reference/openapi.json).
239
+
240
+ - **Named reads:** None.
241
+ - **Named writes:** None.
242
+ - **API reads:** `granola_api_details`, `granola_api_read`, `granola_api_search`.
243
+ - **API writes:** None.
244
+
245
+ **Contributor evidence (optional):** [contracts, adapters and review](https://github.com/zackbart/connecta/tree/main/src/providers/granola).
246
+
247
+ </details>
248
+
146
249
  <details>
147
250
  <summary>Overflow (17 tools)</summary>
148
251
 
@@ -1318,6 +1421,13 @@ would change existing payment settings. Deletes, voids, delivery, payment
1318
1421
  execution, batch and other accounting writes remain unavailable. The API hatch
1319
1422
  refuses POSTs even when named writes are enabled.
1320
1423
 
1424
+ Sparse field preservation, a current SyncToken and requestid do not establish
1425
+ that an invoice update cannot send email. For example, changing only DueDate
1426
+ on an invoice with NeedToSend and enabled card/ACH payments retains those
1427
+ settings; omitting email fields does not disable them. Do not reset delivery or
1428
+ payment settings to work around the unavailable update, or replace the invoice
1429
+ with a new one. No unsent-only update tool is currently exposed either.
1430
+
1321
1431
  Each write requires `requestId`, a stable identifier for the exact intent,
1322
1432
  unique across all requests/tools for this company: 1–50 ASCII letters, digits,
1323
1433
  underscores or hyphens, beginning with a letter or digit. It is sent as Intuit's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.36.0",
3
+ "version": "0.36.2",
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 six meta-tools.",
@@ -107,6 +107,10 @@
107
107
  "types": "./dist/providers/gmail/index.d.ts",
108
108
  "import": "./dist/providers/gmail/index.js"
109
109
  },
110
+ "./providers/granola": {
111
+ "types": "./dist/providers/granola/index.d.ts",
112
+ "import": "./dist/providers/granola/index.js"
113
+ },
110
114
  "./providers/overflow": {
111
115
  "types": "./dist/providers/overflow/index.d.ts",
112
116
  "import": "./dist/providers/overflow/index.js"
@@ -158,6 +162,10 @@
158
162
  "./worker": {
159
163
  "types": "./dist/worker.d.ts",
160
164
  "import": "./dist/worker.js"
165
+ },
166
+ "./google": {
167
+ "types": "./dist/google.d.ts",
168
+ "import": "./dist/google.js"
161
169
  }
162
170
  },
163
171
  "scripts": {
@@ -191,7 +199,7 @@
191
199
  "preeval:outcomes": "node scripts/generate-version.mjs && pnpm run build:operator-ui",
192
200
  "eval:perf": "tsx eval/run-perf.ts",
193
201
  "eval:report": "tsx eval/report.ts",
194
- "check:static": "node --test scripts/check-provider-exemptions.test.mjs && pnpm run format:check && pnpm run check:providers-generated && pnpm run check:docs && pnpm run check:changes && pnpm run check:operator-ui && pnpm run check:clerk-sdk && pnpm run check:lint && pnpm run check:unused && pnpm run typecheck",
202
+ "check:static": "pnpm run format:check && pnpm run check:providers-generated && pnpm run check:docs && pnpm run check:changes && pnpm run check:operator-ui && pnpm run check:clerk-sdk && pnpm run check:lint && pnpm run check:unused && pnpm run typecheck",
195
203
  "check:build": "pnpm run build && pnpm run check:declarations && pnpm run check:bundle",
196
204
  "check:fast": "pnpm run check:static && vitest run --config vitest.config.node.ts --changed origin/main --passWithNoTests",
197
205
  "check": "pnpm run check:static && pnpm run test && pnpm run check:build",
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@clerk/backend": "^3.12.0",
21
- "@zackbart/connecta": "0.36.0",
21
+ "@zackbart/connecta": "0.36.2",
22
22
  "quickjs-emscripten": "0.32.0",
23
23
  "undici": "6.29.1"
24
24
  },