@zackbart/connecta 0.30.0 → 0.31.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 +32 -0
- package/README.md +6 -3
- package/bin/version.mjs +1 -1
- package/dist/call-recovery.js +25 -0
- package/dist/config-schema.d.ts +14 -7
- package/dist/config-schema.js +29 -10
- package/dist/connectors/guarded-fetch.d.ts +9 -1
- package/dist/connectors/guarded-fetch.js +14 -2
- package/dist/errors.d.ts +2 -0
- package/dist/invocation.js +32 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/view.d.ts +9 -0
- package/dist/operator-ui/view.js +11 -0
- package/dist/program-writes.js +2 -47
- package/dist/provider.d.ts +7 -5
- package/dist/provider.js +3 -4
- package/dist/providers/_shared/rest/dispatch.js +41 -0
- package/dist/providers/_shared/rest/operation-index.d.ts +133 -0
- package/dist/providers/_shared/rest/operation-index.js +527 -0
- package/dist/providers/_shared/rest/tools.d.ts +141 -0
- package/dist/providers/_shared/rest/tools.js +569 -0
- package/dist/providers/_shared/rest/value-safety.d.ts +151 -0
- package/dist/providers/_shared/rest/value-safety.js +580 -0
- package/dist/providers/cloudflare/graphql.js +385 -0
- package/dist/providers/cloudflare/index.d.ts +47 -51
- package/dist/providers/cloudflare/index.js +188 -1997
- package/dist/providers/cloudflare/openapi.generated.js +3347 -0
- package/dist/providers/cloudflare/rest.d.ts +37 -0
- package/dist/providers/cloudflare/rest.js +1056 -0
- package/dist/providers/cloudflare/skill.generated.js +7 -4
- package/dist/providers/cloudflare/value-safety.js +1247 -0
- package/dist/providers/notion/index.d.ts +35 -31
- package/dist/providers/notion/index.js +60 -1499
- package/dist/providers/notion/openapi.generated.js +75 -0
- package/dist/providers/notion/rest.d.ts +24 -0
- package/dist/providers/notion/rest.js +1067 -0
- package/dist/providers/notion/skill.generated.js +5 -4
- package/dist/providers/notion/value-safety.js +31 -0
- package/dist/providers/stripe/index.d.ts +26 -20
- package/dist/providers/stripe/index.js +103 -130
- package/dist/providers/stripe/openapi.generated.js +649 -0
- package/dist/providers/stripe/rest.d.ts +14 -0
- package/dist/providers/stripe/rest.js +395 -0
- package/dist/providers/stripe/skill.generated.js +7 -5
- package/dist/providers/stripe/value-safety.js +705 -0
- package/dist/providers/vercel/index.d.ts +31 -22
- package/dist/providers/vercel/index.js +96 -853
- package/dist/providers/vercel/openapi.generated.js +456 -0
- package/dist/providers/vercel/rest.d.ts +20 -0
- package/dist/providers/vercel/rest.js +1002 -0
- package/dist/providers/vercel/skill.generated.js +4 -5
- package/dist/providers/vercel/value-safety.js +328 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/write-outcome.js +47 -0
- package/documentation/README.md +1 -0
- package/documentation/architecture.md +120 -8
- package/documentation/integrating.md +16 -5
- package/documentation/meta-tools.md +9 -2
- package/documentation/provider-auth-migration.md +307 -0
- package/documentation/provider-migration-0.29.md +17 -5
- package/examples/worker/README.md +2 -2
- package/package.json +3 -2
- package/templates/node/README.md +2 -2
- package/templates/node/package.json +1 -1
- package/dist/providers/vercel/mcp-ownership.js +0 -212
|
@@ -646,7 +646,7 @@ only: an array or a class instance there is refused by path, unread. String
|
|
|
646
646
|
maps (static `headers`, `api()`'s `authorizationParams` and
|
|
647
647
|
`tokenRequestHeaders`) are copied the same way and each value must be a
|
|
648
648
|
string. A discriminated union (`remoteMcp()`'s `auth.type`,
|
|
649
|
-
a provider's `
|
|
649
|
+
a provider's `auth.type`) with a missing or unrecognized discriminant throws
|
|
650
650
|
with the valid values. Only plain data is copied. Connectors, modules,
|
|
651
651
|
executors, storage, loggers, handlers, and schemas are opaque and never
|
|
652
652
|
entered. An `api()` tool carries its handler, so it is checked in place (own
|
|
@@ -712,7 +712,7 @@ call. The [provider guide](https://github.com/zackbart/connecta/blob/main/src/pr
|
|
|
712
712
|
value-read conventions.
|
|
713
713
|
|
|
714
714
|
A maintained provider is one `defineProvider()` call (`src/provider.ts`): a
|
|
715
|
-
name, title, kind (`"mcp"`, `"api"`, or `"
|
|
715
|
+
name, title, kind (`"mcp"`, `"api"`, `"composed"`, or `"dual"`), a maintained skill, an
|
|
716
716
|
optional reviewed classification, a closed `options` shape declared with
|
|
717
717
|
`optionsOf<O>()`, and a synchronous `create`. The root exports the option-shape
|
|
718
718
|
combinators and `PROVIDER_COMMON` so provider authors use the same validation
|
|
@@ -728,12 +728,124 @@ live in their own folders. The eight hosted implementations, including the
|
|
|
728
728
|
MCP branches of Notion, Vercel, and Cloudflare, use reviewed presets over
|
|
729
729
|
`remoteMcp({ classify })`. The eleven API-only factories retain an internal
|
|
730
730
|
adapter to the same definition and description-stamping path. GitHub composes hosted tools with a scope-enforced REST complement.
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
731
|
+
A `"dual"` provider lets `auth` select the implementation
|
|
732
|
+
([decision 0005](https://github.com/zackbart/connecta/blob/main/decisions/0005-auth-selects-implementation.md)):
|
|
733
|
+
`variants(["auth", "type"], …)` closes each case's options, and
|
|
734
|
+
`byAuth()` in `src/providers/_shared/rest/dispatch.ts` sends OAuth to
|
|
735
|
+
`hostedOAuth()` (`remoteMcp()`, OAuth only, the reviewed classification) and a
|
|
736
|
+
key to Connecta's REST connector. Stripe, Notion, Cloudflare, and Vercel are dual; `auth` is required.
|
|
737
|
+
The REST connector (`src/providers/_shared/rest/`) serves
|
|
738
|
+
`<vendor>_api_search`, `_api_details`, `_api_read`, and `_api_write` over an
|
|
739
|
+
`OperationIndex` that `npm run providers:spec` compiles offline from the
|
|
740
|
+
provider's pinned `openapi.source.json` into `openapi.generated.ts`.
|
|
741
|
+
Each call is matched to an operation and checked against its parameters before
|
|
742
|
+
transport; `_api_read` admits GET and the vendor's reviewed read-only POSTs in
|
|
743
|
+
its handler, since classification is per tool name. Vendors configure only a
|
|
744
|
+
transport, failure mapper, scope, framing, cursor extraction, a reviewed
|
|
745
|
+
value-safety table (required; see below), argument-dependent refusals, and an
|
|
746
|
+
idempotency header, plus optional hooks: `path` (fill default ids before
|
|
747
|
+
matching), `admit` (an awaited check in `callRest` every tool passes, such as
|
|
748
|
+
Cloudflare's account and zone pin), `result` (unwrap a vendor envelope;
|
|
749
|
+
redaction runs after it and before cursors and `select`), a reviewed
|
|
750
|
+
request-header allowlist, text bodies, and index `slashParams` that admit an
|
|
751
|
+
encoded `/` in a final key segment (`test/rest-connector.test.ts`).
|
|
752
|
+
Large documents opt into generator options that drop restated operation ids
|
|
753
|
+
and plain path parameters and cap one operation's details (`opBudget`).
|
|
754
|
+
`check:providers-generated` checks each index header against its source record
|
|
755
|
+
offline; `providers:check` reports a pin that differs from the live document.
|
|
756
|
+
No maintained provider selects an implementation with `surface` any more. See
|
|
757
|
+
the
|
|
758
|
+
[provider auth migration](./provider-auth-migration.md). Notion's token
|
|
759
|
+
connector keeps its `integration_*` projections and authoring helpers beside
|
|
760
|
+
the generic REST tools.
|
|
761
|
+
|
|
762
|
+
#### Value safety for REST vendors
|
|
763
|
+
|
|
764
|
+
The bar ([decision 0005](https://github.com/zackbart/connecta/blob/main/decisions/0005-auth-selects-implementation.md)):
|
|
765
|
+
no REST tool response, success or error, returns a credential that grants
|
|
766
|
+
access to the vendor or a third party, or a stored secret value, unless a
|
|
767
|
+
reviewed named tool exists to return it. The threat model is Infisical's
|
|
768
|
+
above: an honest server, with secrets people type into free text out of
|
|
769
|
+
scope. Field-by-field redaction over a whole vendor API does not converge, so
|
|
770
|
+
every vendor ships one reviewed table through one mechanism
|
|
771
|
+
(`src/providers/_shared/rest/value-safety.ts`):
|
|
772
|
+
|
|
773
|
+
1. **Candidates from the pinned spec.** `scripts/value-safety.mjs` flags every
|
|
774
|
+
operation whose success response names or describes a credential, keyed
|
|
775
|
+
value, header, environment, or binding container, or whose path, id,
|
|
776
|
+
summary, or description names a secret family (tokens, secrets, keys,
|
|
777
|
+
signing, webhooks, drains, transfer, claim, and invite codes). It is the
|
|
778
|
+
union of the detectors Cloudflare and Vercel shipped first, and
|
|
779
|
+
deliberately over-inclusive. `providers:spec` writes
|
|
780
|
+
`value-safety.candidates.json` beside the index when the source record sets
|
|
781
|
+
`options.valueSafety` (vendor `operationWords`; Stripe's `expansions: false`
|
|
782
|
+
skips id-or-object expansions, which are their own operations' candidates),
|
|
783
|
+
stamped with the pinned digest, the detector format, and its options. The
|
|
784
|
+
file is test evidence and never ships.
|
|
785
|
+
2. **One reviewed verdict per candidate**, in the vendor's `value-safety.ts`:
|
|
786
|
+
`refuse` (the generic tools refuse it before transport, naming the
|
|
787
|
+
operation and the reason), `redact` (reviewed field paths), or `safe`, each
|
|
788
|
+
with a reason. Paths are dot paths into the data a tool returns: a list on
|
|
789
|
+
the way is traversed, `[]` and `{}`/`*` address list items and map values,
|
|
790
|
+
`@keys` replaces secret map keys, `[?env]`, `[?credential]`, and
|
|
791
|
+
`[?header]` filter list items, `url:` sanitizes a URL, and `origin:` (or a
|
|
792
|
+
`#url` suffix) keeps only scheme and host. `keep` names reviewed metadata
|
|
793
|
+
the heuristic leaves (children are still checked). A vendor whose objects
|
|
794
|
+
repeat across operations (Stripe) reviews a field once in `fields`,
|
|
795
|
+
`redact` or `keep`, wherever it appears, and states `resources`: rules
|
|
796
|
+
keyed on the object's type discriminator (Stripe's `object`) that apply to
|
|
797
|
+
every such object wherever a response embeds it (an expansion, a list, an
|
|
798
|
+
event's `data.object` and its `previous_attributes`). A resource rule can
|
|
799
|
+
return a reviewed payer-facing URL verbatim only while a declarative guest
|
|
800
|
+
condition holds (deny by default: a Checkout page only when every field
|
|
801
|
+
that can bind the session to a Customer, an Account, or saved payment
|
|
802
|
+
methods is empty and the session only takes a payment) and withhold it
|
|
803
|
+
otherwise. `providers:spec` derives the binding fields from the pinned
|
|
804
|
+
schema, and the harness fails until the condition lists each one. Argument-dependent refusals (Stripe expansions,
|
|
805
|
+
Vercel `decrypt`) stay in the vendor's `refuse` hook.
|
|
806
|
+
3. **Redaction on every success body**, in `callRest` after `result` and
|
|
807
|
+
before cursors and `select`, so named tools, HEAD data, and uploads pass
|
|
808
|
+
through it too: the operation's reviewed paths, then the key-name
|
|
809
|
+
heuristic as defense in depth. The heuristic is the union of the former
|
|
810
|
+
vendor engines: a credential word anywhere in a non-metadata name removes
|
|
811
|
+
the value whole, a credential suffix removes the strings below it,
|
|
812
|
+
labelled or typed secret records lose their value, environment containers
|
|
813
|
+
lose their values, header, cookie, and query rules lose theirs, external
|
|
814
|
+
destinations keep their origin, and every URL loses userinfo and
|
|
815
|
+
credential-named query parameters or fragments. Exemptions are never
|
|
816
|
+
shared; each vendor states its own.
|
|
817
|
+
4. **Errors.** A reviewed operation, or an unreviewed one whose request
|
|
818
|
+
accepts a credential-named field, answers failures with the vendor's codes
|
|
819
|
+
and status but not its text, unless its verdict is wrapped in
|
|
820
|
+
`vendorErrors()` because a review found its errors cannot echo a stored
|
|
821
|
+
secret. Credentials Connecta sent are always removed by the sent-secrets
|
|
822
|
+
matcher.
|
|
823
|
+
|
|
824
|
+
`providers:spec` (run under tsx) also checks the table against the pinned
|
|
825
|
+
spec: every `redact` path and `keep` must name something the operation's
|
|
826
|
+
response schema can return (through `options.valueSafety.dataRoot` for an
|
|
827
|
+
envelope such as Cloudflare's `result`), and every resource rule path
|
|
828
|
+
something the resource's schema can return. It stamps the paths that resolve
|
|
829
|
+
into the candidates file and fails on the rest, unless
|
|
830
|
+
`value-safety.absent.json` acknowledges them (shared verdicts applied to
|
|
831
|
+
operations whose schema lacks a path, or fields a schema leaves undeclared;
|
|
832
|
+
such paths still apply at runtime).
|
|
833
|
+
|
|
834
|
+
`test/fixtures/value-safety.ts` is the shared harness each vendor's
|
|
835
|
+
`value-safety.node.test.ts` runs; `reviewProblems` is pure, and
|
|
836
|
+
`test/value-safety-harness.node.test.ts` proves wrong paths fail it. It fails
|
|
837
|
+
on candidates from another pin, format, or option set; an unreviewed
|
|
838
|
+
candidate; a verdict for an operation the index lacks; a table path the stamp
|
|
839
|
+
lacks (unresolved, or edited since `providers:spec`); a flagged field no path,
|
|
840
|
+
keep, field review, or scope map covers; a flagged field, built in the shape
|
|
841
|
+
the pinned schema gives it, that survives redaction or that no redact path
|
|
842
|
+
reaches; a keep that exempts anything below it or a vendor-wide keep of a
|
|
843
|
+
credential name; a redact path or resource rule that leaves its value at the
|
|
844
|
+
top level, in a list, an expansion, or an event; a refusal that reaches
|
|
845
|
+
transport; a reviewed operation that echoes vendor error text; and a moved
|
|
846
|
+
verdict count.
|
|
847
|
+
`RestVendor.valueSafety` is required, so a new REST vendor cannot ship without
|
|
848
|
+
a table.
|
|
737
849
|
|
|
738
850
|
`remoteMcp({ classify })` is the public way to declare what a downstream's
|
|
739
851
|
tools do: `{ tools: { name: "read" | "write" | "destructive" | { verdict,
|
|
@@ -49,11 +49,22 @@ mapping from admitted identity to the downstream account; a request must not
|
|
|
49
49
|
choose its own account. Gmail never sends mail, and Drive never permanently
|
|
50
50
|
deletes files.
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
`
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
Stripe requires `auth`: `{ type: "oauth" }` reaches Stripe's hosted MCP
|
|
53
|
+
server, and `{ type: "apiKey" }` with `mode` reaches Connecta's REST connector
|
|
54
|
+
with an operator-managed key. Notion requires `auth` the same way:
|
|
55
|
+
`{ type: "oauth" }` acts as the signed-in user over Notion's hosted MCP, and
|
|
56
|
+
`{ type: "token" }` acts as an internal integration over Connecta's REST
|
|
57
|
+
connector. Cloudflare requires `auth` too: `{ type: "oauth" }` reaches its
|
|
58
|
+
hosted MCP server, and `{ type: "apiToken" }` or `{ type: "globalApiKey" }`
|
|
59
|
+
reaches Connecta's REST connector, with an optional `pin` of accounts and zones
|
|
60
|
+
(required for a Global API Key unless `unpinned: true`). Vercel requires `auth`
|
|
61
|
+
as well: `{ type: "oauth" }` reaches Vercel's hosted MCP server, and
|
|
62
|
+
`{ type: "token" }` with an optional `teamId` reaches Connecta's REST connector
|
|
63
|
+
with an operator-managed access token. Vercel admits only reviewed and approved
|
|
64
|
+
MCP clients to its hosted server, an operational gate outside Connecta. See the
|
|
65
|
+
[provider auth migration](./provider-auth-migration.md).
|
|
66
|
+
To use both implementations of one provider, configure two connector ids with
|
|
67
|
+
independent credentials.
|
|
57
68
|
|
|
58
69
|
## Another remote MCP server
|
|
59
70
|
|
|
@@ -924,8 +924,15 @@ scoped registry, including an empty list when none are accessible. They never
|
|
|
924
924
|
list connectors from another pool or identity's grants.
|
|
925
925
|
|
|
926
926
|
A write that times out after dispatch returns `write_outcome_unknown` with
|
|
927
|
-
`retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted? }`.
|
|
928
|
-
No write is automatically replayed. `
|
|
927
|
+
`retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted?, recovery? }`.
|
|
928
|
+
No write is automatically replayed. `recovery` carries facts the connector
|
|
929
|
+
recorded before dispatch (`src/call-recovery.ts`), such as the `idempotencyKey`
|
|
930
|
+
a REST connector generated; a deliberate retry that reuses it with the exact
|
|
931
|
+
original arguments cannot repeat the write while the vendor retains the key
|
|
932
|
+
(Stripe may prune v1 keys after 24 hours). After that, look the object up before
|
|
933
|
+
retrying. When a connector recorded such facts, any other dispatched write whose
|
|
934
|
+
outcome is unknown (a lost response body, a 5xx) also carries `uncertainCall`
|
|
935
|
+
with them under its own code, and trusted-program write accounting keeps it. `args` may be partial reconciliation context
|
|
929
936
|
with `argsRedacted: true`. A withheld echo or arguments over the 512-byte budget
|
|
930
937
|
carry `argsOmitted: true`; sensitive or unresolved schemas also carry
|
|
931
938
|
`argsRedacted: true`. When `args` is absent or redacted, use the original
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# Provider auth migration
|
|
2
|
+
|
|
3
|
+
Use this upgrade guide when moving a Stripe, Notion, Vercel, or Cloudflare
|
|
4
|
+
deployment from 0.30 to 0.31, where `auth` selects each provider's
|
|
5
|
+
implementation ([#801](https://github.com/zackbart/connecta/issues/801)).
|
|
6
|
+
The [0.31.0 release notes](https://github.com/zackbart/connecta/blob/main/release-notes/0.31.0.md) summarize the change.
|
|
7
|
+
[Deployment upgrades](./deploying.md#upgrade-an-existing-deployment) cover other
|
|
8
|
+
version changes; [integrating services](./integrating.md) covers new connectors.
|
|
9
|
+
|
|
10
|
+
A dual provider requires `auth`. `{ type: "oauth" }` connects the vendor's
|
|
11
|
+
hosted MCP server, OAuth only. A key type connects Connecta's REST connector
|
|
12
|
+
with an operator-managed credential. There is no default, and one connector id
|
|
13
|
+
uses one implementation: configure two ids for both. The
|
|
14
|
+
[decision record](https://github.com/zackbart/connecta/blob/main/decisions/0005-auth-selects-implementation.md)
|
|
15
|
+
explains why.
|
|
16
|
+
|
|
17
|
+
## Stripe
|
|
18
|
+
|
|
19
|
+
Stripe's hosted MCP server stops accepting secret keys and untagged
|
|
20
|
+
restricted keys on 2026-10-31; Stripe still documents agent-tagged restricted
|
|
21
|
+
keys for it. Connecta's hosted Stripe connection is OAuth only either way, and
|
|
22
|
+
every key, tagged or not, uses the REST connector. Every `stripe()` call must
|
|
23
|
+
now name `auth`:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
connectors: [
|
|
27
|
+
stripe("stripe", {
|
|
28
|
+
purpose: "Organization billing",
|
|
29
|
+
auth: { type: "oauth" },
|
|
30
|
+
}),
|
|
31
|
+
stripe("stripe_live", {
|
|
32
|
+
purpose: "Live billing",
|
|
33
|
+
auth: { type: "apiKey" },
|
|
34
|
+
mode: "production",
|
|
35
|
+
}),
|
|
36
|
+
stripe("stripe_sandbox", {
|
|
37
|
+
purpose: "Rehearsal",
|
|
38
|
+
auth: { type: "apiKey" },
|
|
39
|
+
mode: "sandbox",
|
|
40
|
+
}),
|
|
41
|
+
stripe("stripe_merchant", {
|
|
42
|
+
purpose: "One connected merchant",
|
|
43
|
+
auth: { type: "apiKey" },
|
|
44
|
+
mode: "production",
|
|
45
|
+
connectedAccount: "acct_…",
|
|
46
|
+
}),
|
|
47
|
+
];
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| 0.30 configuration | Now |
|
|
51
|
+
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| No `auth`, or `auth: { type: "oauth" }` | `auth: { type: "oauth" }`. Same endpoint, reviewed catalog, guide conventions, and grant. |
|
|
53
|
+
| `auth: { type: "headers", headers }` with `mode` | `auth: { type: "apiKey" }` with `mode`. Paste the key into the connection in the operator UI; literal keys are gone. |
|
|
54
|
+
| `auth: { type: "credential" }` with `mode` | `auth: { type: "apiKey" }` with `mode`. A key stored for the same connector id carries over; run its Test action. |
|
|
55
|
+
| `connectedAccount` with `headers` auth | `connectedAccount` with `auth: { type: "apiKey" }`; it now works with an operator-managed key. |
|
|
56
|
+
| `mode` or `connectedAccount` with OAuth | Refused by name at construction, as before. |
|
|
57
|
+
|
|
58
|
+
The key connector keeps Stripe's tool names (`stripe_api_search`,
|
|
59
|
+
`stripe_api_details`, `stripe_api_read`, `stripe_api_write`,
|
|
60
|
+
`get_stripe_account_info`, and `get_balance_summary`) with Connecta's own
|
|
61
|
+
contracts: search finds operations in a pinned API index, every call is
|
|
62
|
+
checked against that index before it is sent, results arrive as
|
|
63
|
+
`{ status, data, page? }`, and POST writes send the caller's `idempotencyKey`
|
|
64
|
+
or a generated one as `Idempotency-Key` and return it (DELETE sends none).
|
|
65
|
+
Every request, v1 and v2, sends `Stripe-Version: 2026-09-30.endive`, the
|
|
66
|
+
version of the pinned index, whatever the account's default API version, so
|
|
67
|
+
response shapes and accepted parameters follow that version. Check fields
|
|
68
|
+
your programs rely on with `stripe_api_details`.
|
|
69
|
+
A key whose `sk_live_`/`rk_live_` or `sk_test_`/`rk_test_` prefix contradicts
|
|
70
|
+
`mode` is refused before any request. Organization keys (`sk_org_…`) need a
|
|
71
|
+
`Stripe-Context` header the connector does not send; use an account key.
|
|
72
|
+
|
|
73
|
+
Lost on the key path: natural-language Sigma (`stripe_analytics`), metrics,
|
|
74
|
+
documentation search, the implementation planner, reports, and feedback exist
|
|
75
|
+
only on the hosted OAuth connection. Programs calling those tools on a key
|
|
76
|
+
connector need an OAuth connector beside it. The
|
|
77
|
+
[Stripe skill](https://github.com/zackbart/connecta/blob/main/src/providers/stripe/SKILL.md)
|
|
78
|
+
owns the conventions for both.
|
|
79
|
+
|
|
80
|
+
## Notion
|
|
81
|
+
|
|
82
|
+
Every `notion()` call must now name `auth`. 0.30 defaulted to hosted MCP and
|
|
83
|
+
selected the REST interface with `surface: "api"`; `surface` is gone:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
connectors: [
|
|
87
|
+
notion("notion", { purpose: "Workspace content as the signed-in user", auth: { type: "oauth" } }),
|
|
88
|
+
notion("notion_bot", { purpose: "Internal integration content", auth: { type: "token" } }),
|
|
89
|
+
]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| 0.30 configuration | Now |
|
|
93
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| No `surface`, or `surface: "mcp"` | `auth: { type: "oauth" }`. Same endpoint, reviewed catalog, grant, and optional `callAdmission`. |
|
|
95
|
+
| `surface: "api"` | `auth: { type: "token" }`. The integration token stored for the same connector id carries over; run its Test action. |
|
|
96
|
+
| `credentialLabel` or `defaultPageSize` | Token only, as before. Under OAuth they are refused by name at construction. |
|
|
97
|
+
| `integration_search`, `_get_page`, `_get_page_content`, `_get_data_source_schema`, `_query_data_source`, `_create_page`, `_append_blocks` | Unchanged names and contracts. `integration_append_blocks` also takes `checklist`. |
|
|
98
|
+
| `integration_get_database` | `notion_api_read` `GET /v1/databases/{database_id}`. |
|
|
99
|
+
| `integration_get_page_property` | `notion_api_read` `GET /v1/pages/{page_id}/properties/{property_id}`; `truncated_properties` names it. |
|
|
100
|
+
| `integration_list_users`, `integration_get_self` | `notion_api_read` `GET /v1/users` or `GET /v1/users/me`. |
|
|
101
|
+
| `integration_list_comments`, `integration_add_comment` | `notion_api_read` `GET /v1/comments`; `notion_api_write` `POST /v1/comments`. |
|
|
102
|
+
| `integration_update_page_properties`, `integration_trash_page` | `notion_api_write` `PATCH /v1/pages/{page_id}` with `properties`, or `in_trash` to trash or restore. |
|
|
103
|
+
|
|
104
|
+
The token connector gains Connecta's generic REST tools over Notion's pinned
|
|
105
|
+
API index (`notion_api_search`, `notion_api_details`, `notion_api_read`,
|
|
106
|
+
`notion_api_write`), so every public operation is reachable with the bot's
|
|
107
|
+
token: views, page moves and Markdown, meeting notes, custom agents and
|
|
108
|
+
sessions, file uploads by `external_url`, and data source changes. Every call
|
|
109
|
+
is checked against the index before it is sent, and results arrive as
|
|
110
|
+
`{ status, data, page? }`. The read-only POST queries (search, data source,
|
|
111
|
+
meeting notes, agents, sessions, and session events) use `notion_api_read`;
|
|
112
|
+
`page.in: "body"` says their cursor goes back in the body. Notion's OAuth token
|
|
113
|
+
endpoints and multipart file sends are refused. The 0.29 reconciliation table
|
|
114
|
+
and the `surface: "api"` complement rules no longer apply to Notion.
|
|
115
|
+
|
|
116
|
+
Lost per mode. A token connector acts as the integration bot, never as a
|
|
117
|
+
person, and has none of the hosted MCP's search across connected apps, page
|
|
118
|
+
duplication, team and skill search, attachment downloads, or page-to-skill
|
|
119
|
+
conversion. An OAuth connector acts as the user, and has none of the generic
|
|
120
|
+
REST tools or the `integration_*` projections and authoring helpers. Configure
|
|
121
|
+
both ids when a deployment needs both; a failed call is never retried on the
|
|
122
|
+
other. The [Notion skill](https://github.com/zackbart/connecta/blob/main/src/providers/notion/SKILL.md)
|
|
123
|
+
owns the conventions for both.
|
|
124
|
+
|
|
125
|
+
## Vercel
|
|
126
|
+
|
|
127
|
+
Every `vercel()` call must now name `auth`. `surface` is gone, and so are the
|
|
128
|
+
0.29 ownership rules that made the REST complement refuse whatever the hosted
|
|
129
|
+
server also covered: each implementation is complete on its own.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
connectors: [
|
|
133
|
+
vercel("vercel", { purpose: "Deployment diagnosis", auth: { type: "oauth" } }),
|
|
134
|
+
vercel("vercel_rest", { purpose: "Production apps", auth: { type: "token" }, teamId: "team_…" }),
|
|
135
|
+
]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| 0.30 configuration | Now |
|
|
139
|
+
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
140
|
+
| No `surface`, or `surface: "mcp"` | `auth: { type: "oauth" }`. Same endpoint, reviewed catalog, and grant. |
|
|
141
|
+
| `surface: "api"` with `teamId` or `baseUrl` | `auth: { type: "token" }` with the same options. A token stored for the same connector id carries over; run its Test action. |
|
|
142
|
+
| `defaultPageSize` | Removed (it already had no effect). Pass `limit` in `query`, and follow `page.next` as `page.param`. |
|
|
143
|
+
| `teamId` or `baseUrl` with OAuth | Refused by name at construction. |
|
|
144
|
+
| `vercel_api_get`, `vercel_api_mutate` | `vercel_api_read` and `vercel_api_write`: `query` is a JSON object, not name/value pairs, and `teamId: null` replaces `personalAccount: true`. |
|
|
145
|
+
| `verify_project_domain`, `remove_project_domain`, `delete_deployment` | `vercel_api_write` with the operation from `vercel_api_search`, such as `DELETE /v13/deployments/{id}`. |
|
|
146
|
+
| `vercel_api_upload` with any path and headers | `vercel_api_upload` on octet-stream operations in the index (`POST /v2/files`, project avatars, Remote Cache artifacts); Connecta computes `x-vercel-digest`. |
|
|
147
|
+
|
|
148
|
+
The token connector serves `vercel_api_search`, `vercel_api_details`,
|
|
149
|
+
`vercel_api_read`, and `vercel_api_write` over Vercel's OpenAPI document,
|
|
150
|
+
pinned by content hash because Vercel does not version it. Every call is
|
|
151
|
+
checked against the pinned index before it is sent, and results arrive as
|
|
152
|
+
`{ status, data, page? }`. It adds `list_teams`, the value-safe environment
|
|
153
|
+
variable tools (`list_project_env_vars`, `upsert_project_env_var`,
|
|
154
|
+
`update_project_env_var`, `delete_project_env_var`),
|
|
155
|
+
`get_deployment_build_logs` (build events, never following), `get_runtime_logs`
|
|
156
|
+
(the runtime stream, stopped after `waitMs` or `maxRows`), and
|
|
157
|
+
`vercel_api_upload`.
|
|
158
|
+
|
|
159
|
+
Value safety follows a reviewed table (`src/providers/vercel/value-safety.ts`).
|
|
160
|
+
Every operation whose response schema in the pinned spec may carry a credential
|
|
161
|
+
or a stored secret has a verdict: refused (credential minting, Connect
|
|
162
|
+
authorization, decrypted environment values, Global Config items and tokens,
|
|
163
|
+
domain transfer codes, KMS signing), redacted at reviewed field paths
|
|
164
|
+
(environment values, protection-bypass secrets, deploy hook URLs, drain and
|
|
165
|
+
webhook headers and destination URLs beyond their origin, external route and
|
|
166
|
+
redirect destinations beyond their origin, the args and values of every header,
|
|
167
|
+
cookie, and query rule (route transforms and conditions, firewall conditions), team invite codes, synced Global Config items, URL credentials, and signing
|
|
168
|
+
secrets, including the one a new webhook or drain returns at creation), or
|
|
169
|
+
safe with a reason. Detection reads field names, field descriptions, and
|
|
170
|
+
operation descriptions, so a project transfer request (whose code lets another
|
|
171
|
+
team claim the project) is refused too. A test fails when
|
|
172
|
+
a newly flagged operation has no verdict. A key-name heuristic covers every
|
|
173
|
+
other body as defense in depth. Secret-family failures carry fixed messages
|
|
174
|
+
instead of Vercel's text, and every other reviewed operation also withholds that
|
|
175
|
+
text ([value safety](#value-safety-on-every-key-connector)). Read or rotate withheld secrets in the Vercel
|
|
176
|
+
dashboard. A domain move-out is refused because its answer is a transfer token.
|
|
177
|
+
|
|
178
|
+
Lost on the token path: documentation search, runtime error clusters, toolbar
|
|
179
|
+
threads, agent runs, web analytics summaries, purchase quotes and guided
|
|
180
|
+
purchases, `deploy_to_vercel`, and access grants for protected URLs exist only
|
|
181
|
+
on the hosted OAuth connection. Programs calling those tools need an OAuth
|
|
182
|
+
connector beside the token connector.
|
|
183
|
+
|
|
184
|
+
The hosted connection now lists `filter_project_envs`, `get_project_env`,
|
|
185
|
+
`create_project_env`, and `edit_project_env`, which 0.29 hid. The two reads can
|
|
186
|
+
return decrypted values, so they are reviewed as writes and the pool trust
|
|
187
|
+
policy gates them. Vercel admits only reviewed and approved MCP clients to its
|
|
188
|
+
hosted server; if authorization is refused for that reason, the deployment
|
|
189
|
+
needs Vercel's approval or a token connector. The
|
|
190
|
+
[Vercel skill](https://github.com/zackbart/connecta/blob/main/src/providers/vercel/SKILL.md)
|
|
191
|
+
owns the conventions for both.
|
|
192
|
+
|
|
193
|
+
## Cloudflare
|
|
194
|
+
|
|
195
|
+
Every `cloudflare()` call must now name `auth`. 0.30 defaulted to hosted MCP
|
|
196
|
+
when `surface` was omitted; that default is gone with `surface` itself:
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
connectors: [
|
|
200
|
+
cloudflare("cloudflare", {
|
|
201
|
+
purpose: "Estate changes",
|
|
202
|
+
auth: { type: "oauth" },
|
|
203
|
+
}),
|
|
204
|
+
cloudflare("cloudflare_api", {
|
|
205
|
+
purpose: "Production DNS and Workers",
|
|
206
|
+
auth: { type: "apiToken" },
|
|
207
|
+
accountId: "<account id>",
|
|
208
|
+
zoneId: "<zone id>",
|
|
209
|
+
pin: { accountIds: ["<account id>"] },
|
|
210
|
+
}),
|
|
211
|
+
cloudflare("cloudflare_legacy", {
|
|
212
|
+
purpose: "Legacy zones behind a user key",
|
|
213
|
+
auth: { type: "globalApiKey" },
|
|
214
|
+
pin: { zoneIds: ["<zone id>"] },
|
|
215
|
+
}),
|
|
216
|
+
];
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
| 0.30 configuration | Now |
|
|
220
|
+
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
221
|
+
| No `surface`, or `surface: "mcp"`, with OAuth | `auth: { type: "oauth" }`. Same endpoint, catalog, and grant. |
|
|
222
|
+
| `surface: "mcp"` with `auth: { type: "credential" }` | `auth: { type: "apiToken" }`: bearer tokens no longer reach `mcp.cloudflare.com`. A token stored for the same connector id carries over. |
|
|
223
|
+
| `surface: "api"` (API token) | `auth: { type: "apiToken" }` with the same `accountId`, `zoneId`, `baseUrl`, and `maxConcurrency`. |
|
|
224
|
+
| `surface: "api"`, `authentication: "globalApiKey"` | `auth: { type: "globalApiKey" }` plus `pin: { accountIds, zoneIds }`, or `unpinned: true` to accept the user's whole estate. |
|
|
225
|
+
| `credential` override | Removed; the credential slot's copy is fixed. |
|
|
226
|
+
| `callAdmission` with hosted MCP | Unchanged under `auth: { type: "oauth" }`. |
|
|
227
|
+
|
|
228
|
+
The key connector is complete on its own. JSON writes that 0.30 refused on an
|
|
229
|
+
API token (they belonged to hosted `execute`) go through
|
|
230
|
+
`cloudflare_api_write`, checked against a pinned API index before they are
|
|
231
|
+
sent. The 23 hand-written reads (`get_zone`, `list_dns_records`,
|
|
232
|
+
`list_r2_objects`, `list_pages_deployments`, and the rest) are replaced by
|
|
233
|
+
`cloudflare_api_read` on the same paths; results are Cloudflare's own
|
|
234
|
+
`result`, unprojected, so programs pass `select` instead of reading the old
|
|
235
|
+
projections. `list_accounts`, `list_zones`, and `cloudflare_api_upload` keep
|
|
236
|
+
their names (upload now takes `parts` for multipart); `verify_api_token` and
|
|
237
|
+
`verify_global_api_key` become `verify_credential`; `cloudflare_api_get` and
|
|
238
|
+
`cloudflare_api_mutate` become `cloudflare_api_read` and `cloudflare_api_write`.
|
|
239
|
+
`graphql_query` is new.
|
|
240
|
+
|
|
241
|
+
Credentials stay in the dashboard. The key connector refuses every operation
|
|
242
|
+
that mints, rotates, or returns a credential (API tokens, Access service
|
|
243
|
+
tokens, R2 temporary credentials, signing keys, direct-upload URLs, deploy
|
|
244
|
+
hooks) and redacts stored secrets on every result. A pinned connector refuses
|
|
245
|
+
operations that name no account or zone unless they are reviewed safe, such
|
|
246
|
+
as `/certificates` and `/memberships/{id}`.
|
|
247
|
+
|
|
248
|
+
Read-only pools: hosted `execute` is always a write, even for a program that
|
|
249
|
+
only reads, so a read-only pool on an OAuth connector reaches only `search`.
|
|
250
|
+
Use an API-token connector for read-only API access.
|
|
251
|
+
|
|
252
|
+
Lost on each path. OAuth has no index validation, named tools, GraphQL tool,
|
|
253
|
+
upload tool, or pin; Cloudflare's OAuth grant is the only boundary. A key has
|
|
254
|
+
no hosted `search` over Cloudflare's live OpenAPI document; the pinned index
|
|
255
|
+
replaces it and `providers:check` reports when it falls behind. A Global API
|
|
256
|
+
Key cannot reach R2, which accepts API tokens only. The
|
|
257
|
+
[Cloudflare skill](https://github.com/zackbart/connecta/blob/main/src/providers/cloudflare/SKILL.md)
|
|
258
|
+
owns the conventions for both.
|
|
259
|
+
|
|
260
|
+
## Value safety on every key connector
|
|
261
|
+
|
|
262
|
+
All four key connectors now share one value-safety mechanism: a reviewed
|
|
263
|
+
table per vendor over every operation the detector flags in the pinned spec,
|
|
264
|
+
one redaction pass on every result, and one error policy
|
|
265
|
+
([architecture](./architecture.md#value-safety-for-rest-vendors)). Results
|
|
266
|
+
lose fields they returned before. A `"[redacted]"` value is never the real
|
|
267
|
+
one; read or rotate withheld secrets in the vendor's dashboard.
|
|
268
|
+
|
|
269
|
+
- **Stripe** refuses operations that hand out a credential: ephemeral keys,
|
|
270
|
+
Terminal connection tokens, account links, Express login links, account and
|
|
271
|
+
customer sessions, billing portal sessions, file link creation, Financial
|
|
272
|
+
Connections session creation, and the meter event session. Results redact
|
|
273
|
+
client secrets (PaymentIntents, SetupIntents, Checkout and Identity
|
|
274
|
+
sessions, Sources, Financial Connections, invoice confirmation secrets),
|
|
275
|
+
webhook and event destination signing secrets, Apps secret payloads,
|
|
276
|
+
app-install authorization codes, Terminal Wi-Fi passwords, forwarded
|
|
277
|
+
request header values, and Issuing card numbers and CVCs; webhook, file
|
|
278
|
+
link, and pre-signed import URLs keep only their scheme and host, and the
|
|
279
|
+
rules follow each object into expansions, lists, and events (an expanded
|
|
280
|
+
File's links, an event's `previous_attributes`). Identity verification
|
|
281
|
+
URLs are unchanged. A Checkout Session's URL is returned whole only for a
|
|
282
|
+
guest payment session (`mode: "payment"`, no `customer` or
|
|
283
|
+
`customer_account`, no customer creation, no saved payment method options);
|
|
284
|
+
any other session returns only its scheme and host, because its page can
|
|
285
|
+
show, reuse, or remove a customer's saved payment methods. v2 account
|
|
286
|
+
links and Terminal onboarding links are refused too. Issuing `number` and
|
|
287
|
+
`cvc` expansions are refused on every Issuing path, not only cards.
|
|
288
|
+
Failures of credential-bearing operations keep Stripe's type, code, and
|
|
289
|
+
param but not its message. Create client-side sessions and links from your
|
|
290
|
+
own server code.
|
|
291
|
+
- **Notion** results withhold the signature, credential, and security-token
|
|
292
|
+
parameters of pre-signed file URLs, so a returned file URL no longer
|
|
293
|
+
downloads; credential-named fields, including database properties named
|
|
294
|
+
like credentials, read as `"[redacted]"`. Failures of the OAuth, file
|
|
295
|
+
upload, bot user, and agent operations keep Notion's code but not its
|
|
296
|
+
message.
|
|
297
|
+
- **Vercel** replaces environment values with `"[redacted]"` instead of
|
|
298
|
+
dropping the field, and withholds Vercel's error text for every reviewed
|
|
299
|
+
operation and every operation whose request takes a credential-named
|
|
300
|
+
field, not only secret families. Audit event invite codes, integration
|
|
301
|
+
drain headers, and route header values are redacted too.
|
|
302
|
+
- **Cloudflare** redacts Hyperdrive database passwords, RealtimeKit storage
|
|
303
|
+
credentials, transform-rule header values, and dispatch binding values,
|
|
304
|
+
and reduces notification, RealtimeKit, and data security webhook
|
|
305
|
+
destinations to their scheme and host. A credential-named field now loses
|
|
306
|
+
its whole value, not only its strings (a `cookies` list, a variable named
|
|
307
|
+
`API_KEY`).
|
|
@@ -11,6 +11,11 @@ BePresent and One&Many use Notion and Cloudflare; their actual grants and
|
|
|
11
11
|
connector ids must be checked in deployment configuration before that release.
|
|
12
12
|
The provider audit establishes available auth modes, not production OAuth grants.
|
|
13
13
|
|
|
14
|
+
Cloudflare's part of this guide is superseded: `cloudflare()` now selects its
|
|
15
|
+
implementation by `auth` and its key connector is complete on its own. Follow
|
|
16
|
+
the [provider auth migration](./provider-auth-migration.md#cloudflare) for
|
|
17
|
+
Cloudflare; the Cloudflare lines below record the 0.29 transition only.
|
|
18
|
+
|
|
14
19
|
All three providers now default to hosted MCP. `surface: "api"` explicitly
|
|
15
20
|
selects a REST complement and accepts only its API-specific options. The old
|
|
16
21
|
Vercel/Cloudflare API modes are no longer complete alternatives; Notion retains
|
|
@@ -27,6 +32,11 @@ connectors: [
|
|
|
27
32
|
]
|
|
28
33
|
```
|
|
29
34
|
|
|
35
|
+
Notion has since moved to auth-selected implementations: `surface` is gone,
|
|
36
|
+
and `auth: { type: "oauth" }` or `{ type: "token" }` selects hosted MCP or the
|
|
37
|
+
REST connector. Upgrade with the
|
|
38
|
+
[provider auth migration](./provider-auth-migration.md#notion); the rest of
|
|
39
|
+
this paragraph records the 0.29 rules.
|
|
30
40
|
Notion preserves headless/internal-integration capabilities. Add explicit
|
|
31
41
|
`surface: "api"` to retain that identity and migrate old REST names to
|
|
32
42
|
`integration_*`, for example `search` to `integration_search`, `create_page`
|
|
@@ -50,7 +60,9 @@ include `cf-r2-jurisdiction` only when the bucket needs it. This remains a write
|
|
|
50
60
|
`execute` remains a write even when its program only calls GET.
|
|
51
61
|
Trusted pools may execute writes; read-only pools use direct host-approved writes.
|
|
52
62
|
|
|
53
|
-
|
|
63
|
+
Vercel's `surface` and ownership rules were later replaced: `auth` now selects
|
|
64
|
+
the implementation. See the [provider auth migration](./provider-auth-migration.md#vercel).
|
|
65
|
+
The 0.29 Vercel guidance follows for history. For Vercel, authorize the owning teams in hosted OAuth, migrate discovery/logs,
|
|
54
66
|
domain listing/adding, deployment cancellation and file uploads to live MCP
|
|
55
67
|
schemas, and keep the independently authorized REST complement for value-safe
|
|
56
68
|
project environment variables (including `upsert: false` create-only writes), domain verification/removal and deployment
|
|
@@ -59,7 +71,7 @@ category. Raw hatches refuse all published REST counterparts, including project
|
|
|
59
71
|
creation/update. Do not copy REST argument
|
|
60
72
|
shapes into vendor tools, even when a tool name is unchanged.
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
[Notion](
|
|
64
|
-
[
|
|
65
|
-
[
|
|
74
|
+
Every provider's table is retired with `surface`; the provider auth migration
|
|
75
|
+
maps their names ([Notion](./provider-auth-migration.md#notion),
|
|
76
|
+
[Cloudflare](./provider-auth-migration.md#cloudflare),
|
|
77
|
+
[Vercel](./provider-auth-migration.md#vercel)).
|
|
@@ -316,8 +316,8 @@ Rolling back to 0.24.x then requires reauthorizing those OAuth connectors.
|
|
|
316
316
|
|
|
317
317
|
The shipped Notion connector uses a deployment-owned static header and echo
|
|
318
318
|
needs no secret. To exercise vault controls, declare a `credential` slot on an
|
|
319
|
-
`api()` connector or use Notion
|
|
320
|
-
|
|
319
|
+
`api()` connector or use a Notion connector with `auth: { type: "token" }`,
|
|
320
|
+
which declares its own slot.
|
|
321
321
|
Authorized users manage that slot inside the connection. Configuring a vault
|
|
322
322
|
does not create credentials or permissions by itself.
|
|
323
323
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of six meta-tools.",
|
|
@@ -227,7 +227,8 @@
|
|
|
227
227
|
"eval:regrade": "tsx eval/regrade.ts",
|
|
228
228
|
"eval:compare": "tsx eval/compare.ts",
|
|
229
229
|
"format": "oxfmt --write .",
|
|
230
|
-
"format:check": "oxfmt --check ."
|
|
230
|
+
"format:check": "oxfmt --check .",
|
|
231
|
+
"providers:spec": "tsx scripts/generate-openapi.mjs"
|
|
231
232
|
},
|
|
232
233
|
"dependencies": {
|
|
233
234
|
"@cfworker/json-schema": "^4.1.1",
|
package/templates/node/README.md
CHANGED
|
@@ -188,8 +188,8 @@ node -e "console.log(crypto.randomBytes(32).toString('base64'))"
|
|
|
188
188
|
Keep this key outside the database. Losing it makes saved values unreadable;
|
|
189
189
|
upgrades must reuse it. The shipped `time` connector declares no credential
|
|
190
190
|
slot. Add `credential: { label: "API token" }` to an `api()` connector and read
|
|
191
|
-
it through `await ctx.credential?.get()`, or use Notion
|
|
192
|
-
`
|
|
191
|
+
it through `await ctx.credential?.get()`, or use a Notion
|
|
192
|
+
connector with `auth: { type: "token" }`, which declares its own slot. Authorized humans manage the slot inside that
|
|
193
193
|
connection on `/`; there is no separate Credentials tab.
|
|
194
194
|
|
|
195
195
|
In 0.25.0, this vault also seals downstream OAuth tokens on their first read.
|