@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.
- package/CHANGELOG.md +55 -0
- package/README.md +1 -1
- package/bin/version.mjs +1 -1
- package/dist/auth/downstream-oauth.js +3 -0
- package/dist/auth/oauth-authorization-params.js +52 -0
- package/dist/auth/static-oauth.js +1 -4
- package/dist/connectors/option-shapes.js +1 -0
- package/dist/connectors/remote-mcp.d.ts +4 -1
- package/dist/connectors/remote-mcp.js +12 -1
- package/dist/google.d.ts +13 -0
- package/dist/google.js +42 -0
- package/dist/http/hatch-paging.js +2 -2
- package/dist/http/paging.js +3 -88
- package/dist/http/provider.js +1 -2
- package/dist/http/tools.js +1 -23
- package/dist/program-writes.js +0 -1
- package/dist/providers/_shared/google/service-account.d.ts +1 -1
- package/dist/providers/_shared/google/service-account.js +20 -8
- package/dist/providers/_shared/sensitive-fields.js +0 -16
- package/dist/providers/breeze/api.js +2 -2
- package/dist/providers/ccb/api.js +1 -1
- package/dist/providers/cloudflare/api.js +8 -8
- package/dist/providers/cloudflare/lib/config.d.ts +2 -1
- package/dist/providers/docs/api.js +1 -1
- package/dist/providers/drive/api.js +1 -1
- package/dist/providers/drive/lib/hatch.js +1 -1
- package/dist/providers/drive/tools.js +1 -1
- package/dist/providers/forms/api.js +1 -1
- package/dist/providers/forms/lib/hatch.js +1 -1
- package/dist/providers/forms/tools.js +1 -1
- package/dist/providers/github/api.js +1 -1
- package/dist/providers/github/lib/discovery.js +2 -2
- package/dist/providers/gmail/api.js +1 -1
- package/dist/providers/granola/api.js +155 -0
- package/dist/providers/granola/index.d.ts +10 -0
- package/dist/providers/granola/index.js +68 -0
- package/dist/providers/granola/openapi.generated.js +30 -0
- package/dist/providers/granola/safety.js +37 -0
- package/dist/providers/granola/skill.generated.js +5 -0
- package/dist/providers/granola/tools.js +2 -0
- package/dist/providers/overflow/api.js +26 -26
- package/dist/providers/planning-center/api.js +4 -4
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/lib/json-api.js +2 -2
- package/dist/providers/quickbooks/lib/query.js +1 -1
- package/dist/providers/quickbooks/safety.js +1 -1
- package/dist/providers/sheets/api.js +1 -1
- package/dist/providers/sheets/lib/hatch.js +2 -2
- package/dist/providers/sheets/lib/requests.js +1 -1
- package/dist/providers/slides/api.js +1 -1
- package/dist/providers/stripe/api.js +7 -7
- package/dist/providers/tithely/api.js +2 -2
- package/dist/providers/vercel/api.js +12 -12
- package/dist/providers/vercel/lib/pin.js +1 -1
- package/dist/request-token.d.ts +7 -0
- package/dist/request-token.js +1 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/runtime/admission.js +0 -12
- package/dist/storage/keys.js +2 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/README.md +13 -12
- package/documentation/architecture.md +148 -76
- package/documentation/auth.md +198 -4
- package/documentation/code-mode.md +6 -6
- package/documentation/meta-tools.md +5 -2
- package/documentation/operator-tests.md +11 -2
- package/documentation/operator-ui.md +1 -2
- package/documentation/providers.md +110 -0
- package/package.json +10 -2
- package/templates/node/package.json +1 -1
package/documentation/auth.md
CHANGED
|
@@ -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` |
|
|
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-
|
|
2702
|
-
|
|
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/
|
|
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
|
|
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-
|
|
1293
|
-
| `V5`, `W9` | `test/program-writes.test.ts` (
|
|
1294
|
-
| `W10`, `W12` | `test/
|
|
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` (
|
|
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.
|
|
1338
|
-
|
|
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
|
|
194
|
-
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
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",
|