@zackbart/connecta 0.4.0 → 0.5.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +252 -0
  2. package/README.md +72 -17
  3. package/SECURITY.md +10 -6
  4. package/dist/activity.d.ts +8 -0
  5. package/dist/activity.d.ts.map +1 -1
  6. package/dist/activity.js +1 -0
  7. package/dist/activity.js.map +1 -1
  8. package/dist/connectors/api.d.ts +23 -0
  9. package/dist/connectors/api.d.ts.map +1 -1
  10. package/dist/connectors/api.js +13 -1
  11. package/dist/connectors/api.js.map +1 -1
  12. package/dist/connectors/remote-mcp.d.ts +27 -1
  13. package/dist/connectors/remote-mcp.d.ts.map +1 -1
  14. package/dist/connectors/remote-mcp.js +31 -0
  15. package/dist/connectors/remote-mcp.js.map +1 -1
  16. package/dist/credentials.d.ts +2 -1
  17. package/dist/credentials.d.ts.map +1 -1
  18. package/dist/credentials.js +4 -2
  19. package/dist/credentials.js.map +1 -1
  20. package/dist/execute.d.ts +4 -4
  21. package/dist/execute.d.ts.map +1 -1
  22. package/dist/execute.js.map +1 -1
  23. package/dist/executors/quickjs.d.ts.map +1 -1
  24. package/dist/executors/quickjs.js +32 -4
  25. package/dist/executors/quickjs.js.map +1 -1
  26. package/dist/index.d.ts +51 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +67 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/meta-tools.d.ts +25 -4
  31. package/dist/meta-tools.d.ts.map +1 -1
  32. package/dist/meta-tools.js +138 -24
  33. package/dist/meta-tools.js.map +1 -1
  34. package/dist/registry.d.ts +183 -2
  35. package/dist/registry.d.ts.map +1 -1
  36. package/dist/registry.js +293 -27
  37. package/dist/registry.js.map +1 -1
  38. package/dist/server.d.ts +9 -1
  39. package/dist/server.d.ts.map +1 -1
  40. package/dist/server.js +96 -12
  41. package/dist/server.js.map +1 -1
  42. package/dist/skills.d.ts +52 -1
  43. package/dist/skills.d.ts.map +1 -1
  44. package/dist/skills.js +161 -1
  45. package/dist/skills.js.map +1 -1
  46. package/dist/storage/file.d.ts.map +1 -1
  47. package/dist/storage/file.js +19 -3
  48. package/dist/storage/file.js.map +1 -1
  49. package/dist/toolkits.d.ts +44 -0
  50. package/dist/toolkits.d.ts.map +1 -0
  51. package/dist/toolkits.js +134 -0
  52. package/dist/toolkits.js.map +1 -0
  53. package/dist/types.d.ts +20 -1
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/ui.d.ts +29 -1
  56. package/dist/ui.d.ts.map +1 -1
  57. package/dist/ui.js +100 -15
  58. package/dist/ui.js.map +1 -1
  59. package/dist/validate.d.ts +33 -1
  60. package/dist/validate.d.ts.map +1 -1
  61. package/dist/validate.js +32 -2
  62. package/dist/validate.js.map +1 -1
  63. package/dist/version.d.ts +1 -1
  64. package/dist/version.js +1 -1
  65. package/package.json +5 -2
  66. package/src/activity.ts +9 -0
  67. package/src/connectors/api.ts +36 -1
  68. package/src/connectors/remote-mcp.ts +67 -0
  69. package/src/credentials.ts +6 -3
  70. package/src/execute.ts +4 -4
  71. package/src/executors/quickjs.ts +32 -4
  72. package/src/index.ts +141 -2
  73. package/src/meta-tools.ts +215 -40
  74. package/src/registry.ts +416 -29
  75. package/src/server.ts +130 -12
  76. package/src/skills.ts +184 -1
  77. package/src/storage/file.ts +18 -2
  78. package/src/toolkits.ts +215 -0
  79. package/src/types.ts +20 -1
  80. package/src/ui.ts +103 -14
  81. package/src/validate.ts +60 -2
  82. package/src/version.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,258 @@
2
2
 
3
3
  All notable changes to this package are documented here.
4
4
 
5
+ ## 0.5.0 — 2026-07-26
6
+
7
+ A feature release. Toolkits, per-connector usage guides, and per-connector
8
+ result caps are the substance; a paging fix, a docs coherence pass, and a
9
+ cleared audit advisory round it out. Nothing here is breaking, and a deployment
10
+ that declares none of the new options keeps its 0.4.1 *runtime* behavior on
11
+ every path that matters: an unscoped connection still sees the whole registry,
12
+ a zero-guide catalog gains no discovery text, and an unset cap truncates where
13
+ it always did. One narrow exception — a request carrying `?toolkit=` against a
14
+ deployment that configures no toolkits is now a 404, where 0.4.1 never read the
15
+ parameter and served the request; no 0.4.1 client had reason to send it. Three
16
+ textual changes are unconditional and reach every deployment, so anything that
17
+ snapshots them will diff: the `get_result` tool description now documents the
18
+ accepted `maxBytes` range, its `maxBytes` JSON Schema tightens from
19
+ `exclusiveMinimum: 0` to `minimum: 1`, and the matching validation error
20
+ message is reworded. The next intentional breaking release stays reserved for
21
+ issue #28.
22
+
23
+ ### Added
24
+
25
+ - **Toolkits — named scoped views over one deployment's registry.** A connecta
26
+ deployment belongs to an org; a toolkit is the slice one team group sees.
27
+ Operators declare them in the new `ConnectaConfig.toolkits` (`{ support: {
28
+ connectors: [...], includeTools?, excludeTools? } }`, with per-tool address
29
+ grain; `ToolkitConfig` and `ToolkitDefinition` are exported), and a client
30
+ selects one with `?toolkit=<name>` on the MCP URL. The boundary is a single
31
+ enforcement point: a `ScopedRegistry` built once per HTTP request — the
32
+ transport is stateless, so there is no longer-lived connection to hang it on
33
+ — with every meta-tool and the `execute_code` sandbox bridge typed against a
34
+ narrow `RegistryView` rather than the full `Registry`, so reaching for an
35
+ unfiltered method is a compile error instead of a silent leak. All nine
36
+ meta-tools are scoped; an out-of-scope address fails byte-identically to a
37
+ nonexistent one; `get_result` stashes are namespaced per toolkit so a scoped
38
+ session cannot page out a sibling's results; and health observations are
39
+ per-toolkit so one scope's `lastError` never names another's tools. A
40
+ monotonic has-ever-succeeded flag is the one deployment-wide *observation* a
41
+ scoped view can read back, so scoped sessions don't misreport live remote
42
+ connectors as `unknown`. Plenty of other state is shared by design and is not
43
+ scoped at all — the tool-catalog cache, and, worth saying out loud, the
44
+ `conn:<id>:` storage namespace and the credential vault: **a downstream OAuth
45
+ token obtained under one toolkit is usable from another.** `/ui`, `/health`,
46
+ and the OAuth pages stay unscoped operator surfaces. **Selection is
47
+ self-service: any caller who can reach the endpoint may name any toolkit, or
48
+ omit the parameter and see the whole registry.** A toolkit scopes visibility,
49
+ not identity — binding a team member to a toolkit belongs in `auth`, and
50
+ enforcing that binding is a deliberate follow-up (issue #37). Until it lands,
51
+ treat toolkits as an ergonomics and context-budget feature rather than access
52
+ control; connecta warns at startup when toolkits are configured without
53
+ inbound `auth`. `InboundAuth.authorize` already receives the full request, so
54
+ an adapter can refuse a mismatched `?toolkit=` today.
55
+ - **Per-connector usage guides, served by the existing `skills` meta-tool.** A
56
+ connector declaration can carry `usageGuide` — agent-facing markdown,
57
+ config-as-code, and a new pass-through option on both `ApiOptions` and
58
+ `RemoteMcpOptions`. `skills({})` then lists the built-in `usage` guide
59
+ alongside one summarized entry per guided connector, with `skills({ name:
60
+ "connector:<id>" })` returning the guide verbatim. Collision with a built-in
61
+ skill name is structurally impossible because the built-in names are bare
62
+ identifiers that never contain `:`, so no `connector:<id>` can ever match one
63
+ — not even when a connector's id is literally `usage`. The prefixed form is
64
+ also the only way to reach a guide: a bare connector id is never resolved,
65
+ and errors with a pointer to the prefixed name rather than shadowing
66
+ anything. Discovery is conditional: the extra `usage` section and the `guide`
67
+ hints on `search_tools`/`describe_tools`/`skills` appear only once some
68
+ connector declares a guide, so a zero-guide deployment's catalog is
69
+ byte-identical to before — with one exception: the `skills({ name })` error
70
+ wording for connector-shaped names. Those branches are gated on the name
71
+ matching a connector id, not on any guide existing, so asking for a bare
72
+ connector id now reports that the connector has no usage guide where 0.4.1
73
+ said `Unknown skill`, and `connector:<unknown>` reports an unknown connector.
74
+ Guides follow the connection's toolkit scope — a scoped session sees only
75
+ in-scope guides.
76
+ - **Per-connector `maxResultBytes` override.** The inline-result byte cap now
77
+ resolves per call as connector value → global `ConnectaConfig.maxResultBytes`
78
+ → the built-in 50 000 default, so a connector that habitually returns large
79
+ payloads can be capped without shrinking everything else. It is a new
80
+ `maxResultBytes` field on the `Connector` interface with pass-through options
81
+ on both `ApiOptions` and `RemoteMcpOptions`. Resolution happens after address
82
+ resolution inside the call path, so `call_tool`, `call_destructive_tool`, and
83
+ each leg of a `batch_call` use their own connector's cap. `get_result`'s
84
+ default page size deliberately stays on the global cap — a stashed result
85
+ carries no connector identity — and `execute_code` host-call results never
86
+ flowed through this truncation path at all (they have a separate guard on the
87
+ final sandbox return), so they're unaffected. A toolkit can neither raise nor
88
+ lower a cap.
89
+ - **`ToolCallActivityEvent.toolkitId`** — an optional field on the exported
90
+ activity event, set to the toolkit a call arrived through and absent on
91
+ unscoped calls. Every custom `ActivityStore` now receives it, so a sink that
92
+ persists into a typed or column-bound schema needs a migration to keep the
93
+ value rather than drop it on the floor; sinks that pass the event through
94
+ untouched need no change.
95
+
96
+ ### Fixed
97
+
98
+ - **`maxResultBytes` is validated at all three intake points, and `get_result`
99
+ paging can never fail to advance** (issue #32). The cap is now a whole number
100
+ of bytes >= 1, enforced from one shared definition. An unusable operator
101
+ value (zero, negative, `NaN`, non-integer, `Infinity`) emits a startup
102
+ warning through the existing insensible-config channel — naming the connector
103
+ for a per-connector override, and quoting the exact value the runtime falls
104
+ back to — instead of silently misbehaving: previously `0`/`NaN` served a
105
+ 0-byte head, and a *negative* cap served a **larger** head than the default
106
+ (`Uint8Array.slice` counts from the end) while still claiming truncation. A
107
+ bad `get_result` `maxBytes` argument is now an ordinary input-validation
108
+ error. On top of intake validation, `alignEndToCharBoundary` widens any
109
+ window that would yield no bytes, so a page inside the payload always
110
+ advances; the end of the payload is handled separately, by only reporting a
111
+ `nextOffset` while the window stops short of the total. Between them, paging
112
+ terminates whatever effective page size reaches it. Worth recording so the
113
+ issue isn't closed on a false premise: the *client*-triggerable hang the
114
+ issue suspected was confirmed never reachable — the wire schema already
115
+ rejected non-positive `maxBytes` before dispatch, verified empirically
116
+ against the previous release. The hang was real but operator-triggered only;
117
+ the in-handler check is defense in depth for in-process callers. Valid values
118
+ behave byte-identically to 0.4.1.
119
+ - **Documentation coherence pass across README, `docs/`, and `examples/`.** The
120
+ changes above each documented themselves; nobody had reconciled the set.
121
+ Fixed: a TOC link to an anchor that never existed, a toolkits section that
122
+ opened by calling a toolkit "an access boundary" in direct contradiction of
123
+ the "what toolkits are *not*" text below it, a `ConnectaConfig` table missing
124
+ `toolkits` and `probeTimeoutMs`, design.md's silence on guides and toolkits,
125
+ and a `serveMcp` misattribution of where the scope is actually built.
126
+ **Operators running the worker example should note a schema migration**: its
127
+ `ActivityStore` dropped `toolkitId`, the field that shows which team's view a
128
+ call came through. The example now binds and reads back a `toolkit_id`
129
+ column, and its README carries an `ALTER TABLE` note for pre-existing D1
130
+ tables — without it, every insert fails with `no such column`, and because
131
+ activity writes are best-effort the failure is invisible to the caller (it
132
+ surfaces only as a warning per write in the operator log) while the activity
133
+ table stops filling.
134
+
135
+ ### Security
136
+
137
+ - **`branding.favicon.href` is now scheme-gated**, completing the branding-URL
138
+ invariant begun in 0.4.1 (issue #29). All three branding URLs are validated
139
+ in one resolver shared by both HTML surfaces (`/ui` and the OAuth result
140
+ pages). A favicon href must be an absolute http(s) URL or a genuinely
141
+ root-relative path — enforced structurally, after stripping the tab/LF/CR
142
+ characters URL parsers ignore, with an origin comparison kept as defense in
143
+ depth. Root-relative only, because `/ui` and `/oauth/callback/<id>` sit at
144
+ different depths and a document-relative path would resolve differently on
145
+ each. A rejected value falls back silently to the default `/favicon.svg` and
146
+ is named in a startup warning — which also names a dropped `productUrl` or
147
+ `ownerUrl`, so a deployment that has been quietly carrying one of those since
148
+ 0.4.1 will start seeing a warning line it did not see before.
149
+ `resolveBranding` also reads every field through a string guard, so malformed
150
+ branding from an untyped JavaScript caller degrades to defaults instead of
151
+ throwing at construction.
152
+ - **GHSA-frvp-7c67-39w9 cleared, and `check:security` tightened from `high` to
153
+ `moderate`.** The advisory (a Windows-only path traversal in
154
+ `@hono/node-server`'s `serve-static`) is resolved by a root `overrides` entry
155
+ pinning `@hono/node-server@^2.0.12`. No *declared* dependency version changes
156
+ — the MCP SDK stays where it was — though the override does of course move
157
+ the resolved transitive package across a major, 1.19.14 to 2.0.12. The
158
+ planned SDK bump could not work: every SDK release from 1.25.0 onward pins a
159
+ `@hono/node-server@^1.19.x` range that can never resolve to the patched 2.x
160
+ line, and it is those same releases that pull the package in at all. This is
161
+ audit-noise remediation rather than live vulnerability remediation — a
162
+ module-graph trace from every SDK entrypoint connecta uses found no reachable
163
+ `@hono/node-server` import, and the vulnerable `serve-static` subpath is
164
+ imported by nothing in the tree — which is what makes the forced major safe.
165
+ The old `high` gate caught high and critical findings correctly, but by
166
+ construction could never surface this moderate one; `SECURITY.md` is updated
167
+ to match, having still recorded the advisory as an accepted open finding
168
+ under the old threshold. **Residual, stated plainly:** npm applies
169
+ `overrides` only at the root project, so consumers who install this package
170
+ still resolve the `1.19.x` range and will keep seeing the advisory in their
171
+ own audits until the SDK moves to `@hono/node-server@^2`. Tracked as issue
172
+ #40, which also retires the override.
173
+
174
+ ## 0.4.1 — 2026-07-25
175
+
176
+ A security-hardening release from a full audit of the codebase. Every change is
177
+ backward-compatible: new safety that could alter behavior is opt-in or a
178
+ generous default, and existing well-behaved deployments are unaffected. No
179
+ sandbox-escape, RCE, or XSS was found; the items below are the real gaps.
180
+
181
+ ### Security
182
+
183
+ - **QuickJS sandbox log memory is now bounded.** `execute_code`'s `console.log`
184
+ capped only the *number* of retained entries (200), not their size, so
185
+ untrusted guest code — the sandbox's explicit threat model — could
186
+ `console.log("x".repeat(25_000_000))` two hundred times and retain multiple
187
+ GB of host memory, OOMing the Node process. Each entry is now truncated to 8k
188
+ chars and the cumulative buffer to 256k, both at capture time. Small logs are
189
+ byte-for-byte unchanged.
190
+
191
+ - **The `fileStorage` state file is now owner-only.** It holds downstream OAuth
192
+ access/refresh tokens in cleartext on the Node backend and was written with
193
+ no mode (world-readable `0644`), so any local user could read long-lived
194
+ tokens. The directory is now `0700`, the temp file `0600` (the atomic rename
195
+ preserves it), and an existing loose-mode file is repaired to `0600` on load.
196
+ Repair is best-effort so a non-POSIX filesystem can't block startup.
197
+
198
+ - **Cleartext-credential and destination guard on `remoteMcp`.** Static
199
+ `headers` credentials were attached with no scheme check, so an `http://`
200
+ `url` sent bearer tokens / API keys in cleartext. `remoteMcp` now warns at
201
+ construction when headers-auth targets a non-https, non-loopback URL, and a
202
+ new opt-in `requireHttps` makes that a hard error. A `NOTE` documents the
203
+ residual redirect-following SSRF in the SDK's fetch transport (a malicious
204
+ downstream could 3xx-redirect to an internal address); a full guard needs
205
+ manual redirect handling in the SDK and is deferred to a non-patch release.
206
+
207
+ - **Construction-time warnings for insecure deployment shapes.**
208
+ `createConnecta` now emits a one-time `logger.warn` when: no inbound `auth`
209
+ is configured while credential/OAuth connectors are present (any caller
210
+ reaches the credential vault — connecta's trust model is single-domain, so an
211
+ open or open-signup deployment grants every caller full access); `publicUrl`
212
+ is unset while OAuth connectors exist (the downstream `redirect_uri` is
213
+ derived from the attacker-influenced inbound `Host` header — set `publicUrl`
214
+ to a fixed https origin); or a connector exposes `finishAuth` but not
215
+ `verifyState` (its `/oauth/callback` would exchange any delivered `code`).
216
+ The shipped `remoteMcp` implements `verifyState`, so the last fires only for
217
+ hand-rolled connectors. Full per-actor authorization remains an architectural
218
+ change for a future release.
219
+
220
+ - **The discovery meta-tools no longer hang on a stuck downstream.**
221
+ `list_connectors`, `search_tools`, and `describe_tools` fanned out live
222
+ probes to every connector with no deadline, so one hung downstream stalled
223
+ the whole call. Bounded by the new `probeTimeoutMs` (below); a timed-out
224
+ connector degrades to an unavailable/errored entry. The bound is
225
+ caller-facing — the registry takes no `AbortSignal` yet, so the underlying
226
+ fetch is not cancelled; real cancellation is a documented follow-up.
227
+
228
+ - **`/ui` now ships a nonce-based script CSP.** The operator page carried no
229
+ script-restricting CSP, so any future escaping regression would be directly
230
+ exploitable. It now sends a per-request `script-src 'nonce-…' 'strict-dynamic'
231
+ https: 'unsafe-inline'; object-src 'none'; base-uri 'none'; frame-ancestors
232
+ 'none'`. `'strict-dynamic'` keeps Clerk's runtime-injected scripts working;
233
+ the `https:`/`'unsafe-inline'` fallbacks are ignored by modern browsers and
234
+ only cover legacy ones; and no `default-src` is set so Clerk's network/fonts
235
+ and the inline styles stay unrestricted. Operator `branding` `productUrl`/
236
+ `ownerUrl` values are now scheme-gated (a `javascript:` URL is dropped rather
237
+ than rendered as a link), and credential metadata omits `lastFour` for values
238
+ shorter than 12 chars so a short secret doesn't leak half of itself.
239
+
240
+ ### Added
241
+
242
+ - `RemoteMcpOptions.requireHttps` (default `false`) — reject a non-`https://`
243
+ (non-loopback) `url` at construction, and `RemoteMcpOptions.logger` for the
244
+ cleartext-credential warning.
245
+ - `ApiOptions.strictValidation` (default `false`) and
246
+ `ValidateToolInputOptions.failClosed` (default `false`) — reject a call whose
247
+ `inputSchema` the validator cannot evaluate instead of passing the raw
248
+ arguments through. The default remains fail-open (a broken schema does not
249
+ break an otherwise working tool); `api()` also now eagerly compiles each tool
250
+ schema at construction so a bad one warns once at startup rather than
251
+ silently on first use.
252
+ - `ConnectaConfig.probeTimeoutMs` (default `30_000`) — per-connector deadline
253
+ for the `list_connectors`/`search_tools`/`describe_tools` fan-out. Generous
254
+ by default so it trips only on a pathological hang; does not apply to
255
+ `call_tool`/`batch_call`, which carry `defaultToolTimeoutMs`.
256
+
5
257
  ## 0.4.0 — 2026-07-25
6
258
 
7
259
  ### Added
package/README.md CHANGED
@@ -44,13 +44,13 @@ A tool **address** is `<connectorId>.<toolName>` (e.g. `notion.search`).
44
44
  | Tool | Input | Returns |
45
45
  | --- | --- | --- |
46
46
  | `list_connectors` | `{ probe? }` | live (`probe: true`, default) or cached health, tool count, and recent real-call observations |
47
- | `skills` | `{ name? }` | lists or fetches the concise `usage` guide for choosing among the meta-tools |
47
+ | `skills` | `{ name? }` | lists or fetches the concise `usage` guide for choosing among the meta-tools, plus any operator-authored per-connector guide (`connector:<connectorId>`) |
48
48
  | `search_tools` | `{ query?, connector?, limit?, offset?, fullDescriptions?, includeSchemas? }` | ranked, paginated matches; optionally includes compact/raw schemas to remove a round trip |
49
49
  | `describe_tools` | `{ addresses[], format?, fullDescriptions? }` | names, descriptions, input/output schemas, and behavior annotations |
50
50
  | `call_tool` | `{ address, args?, fields?, resultMode?, timeoutMs?, maxRetries?, diagnostics? }` | invokes only tools explicitly annotated `readOnlyHint: true` |
51
51
  | `call_destructive_tool` | same as `call_tool` | invokes unannotated, write-capable, or destructive tools through a host-visible approval boundary |
52
52
  | `authorize_connector` | `{ connector, force? }` | starts (or with `force`, restarts) the downstream OAuth flow; returns the `authorizationUrl` to open |
53
- | `get_result` | `{ id, offset?, maxBytes? }` | a byte-slice page of a truncated result — `{ text, offset, nextOffset?, totalBytes }` |
53
+ | `get_result` | `{ id, offset?, maxBytes? }` | a byte-slice page of a truncated result — `{ text, offset, nextOffset?, totalBytes }`; `maxBytes` is a whole number of bytes >= 1 |
54
54
  | `batch_call` | `{ calls, resultMode?, timeoutMs?, maxRetries? }` | 1–10 parallel calls sharing request-scoped clients, with attempts/timing/errors |
55
55
  | `execute_code` *(optional)* | `{ code }` | result + logs of bounded async JS orchestration over explicitly read-only tools; registered only when an `executor` is configured |
56
56
 
@@ -65,9 +65,7 @@ connector configuration, auth policy, domain, bindings, migrations, and secrets.
65
65
 
66
66
  ```ts
67
67
  import { DynamicWorkerExecutor } from "@cloudflare/codemode";
68
- import {
69
- api, bearerToken, createConnecta, remoteMcp,
70
- } from "@zackbart/connecta";
68
+ import { bearerToken, createConnecta, remoteMcp } from "@zackbart/connecta";
71
69
  import { clerkAuth } from "@zackbart/connecta/auth/clerk";
72
70
  import { cloudflareKvStorage } from "./cloudflare-kv.js";
73
71
 
@@ -143,6 +141,41 @@ listen(connecta, 8787); // http://localhost:8787/mcp
143
141
  Example: [`examples/node/`](./examples/node/). Docker (single-service compose
144
142
  stack): [`examples/docker/`](./examples/docker/).
145
143
 
144
+ ## Toolkits — one deployment, many teams
145
+
146
+ A deployment belongs to an org. Optional **toolkits** give each group of team
147
+ members its own scoped view of the same registry, so different teams no longer
148
+ need separate deployments:
149
+
150
+ ```ts
151
+ createConnecta({
152
+ connectors: [zendesk, notion, gmail],
153
+ auth: bearerToken(env.CONNECTA_TOKEN),
154
+ toolkits: {
155
+ support: { connectors: ["zendesk", "notion"] },
156
+ exec: {
157
+ connectors: ["zendesk", "notion", "gmail"],
158
+ excludeTools: ["gmail.send_message"], // finer grain than a connector id
159
+ },
160
+ },
161
+ });
162
+ ```
163
+
164
+ A client picks one at connect time: `https://…/mcp?toolkit=support`. Inside a
165
+ scoped session **every** meta-tool — search, describe, call, batch, skills,
166
+ authorize, `get_result`, and `execute_code` host calls — behaves as if
167
+ out-of-scope connectors and tools do not exist, and an out-of-scope address
168
+ fails identically to a nonexistent one. No `?toolkit=` ⇒ the full registry, as
169
+ before; an unknown name is an error, never a silent fallback.
170
+
171
+ A toolkit scopes **visibility, not identity**: any authenticated client may
172
+ select any toolkit, or omit the parameter and see everything, so a credential
173
+ shared by two teams gives both teams every view. Binding a member to a toolkit
174
+ belongs in `auth`, which stays the access check; enforcing that binding is a
175
+ deliberate follow-up
176
+ ([issue #37](https://github.com/zackbart/connecta/issues/37)). Details:
177
+ [docs §16](./docs/documentation.md#16-toolkits-scoped-views).
178
+
146
179
  ## Code mode
147
180
 
148
181
  With an `executor` configured, the model can write an async function instead of
@@ -202,9 +235,11 @@ api("example", {
202
235
  ```
203
236
 
204
237
  Set `credentialEncryptionKey` on `createConnecta` to a base64-encoded 32-byte
205
- key held in the runtime's secret store (`openssl rand -base64 32`). Credential
206
- mutation routes require the configured Clerk provider and a same-origin browser
207
- request; the static inbound bearer cannot administer the vault.
238
+ key held in the runtime's secret store (`openssl rand -base64 32`) — a connector
239
+ that declares a credential without one fails at construction rather than booting
240
+ with an unusable vault. Credential mutation routes require the configured Clerk
241
+ provider and a same-origin browser request; the static inbound bearer cannot
242
+ administer the vault.
208
243
 
209
244
  Connecta does not bundle service-specific HTTP API connectors. Package consumers
210
245
  define them with `api()` (or implement `Connector` directly), keeping endpoint,
@@ -233,17 +268,37 @@ const connecta = createConnecta({
233
268
  ```
234
269
 
235
270
  One final event is emitted for each resolved connector call made through
236
- `call_tool`, `batch_call`, or `execute_code`; retries remain one event with an
237
- `attempts` count. Implementing `list` enables the authenticated Activity tab in
238
- `/ui`; `activityReadGate` can narrow reads further. Writes are best-effort and
239
- never change a tool result. Clerk calls carry the Clerk user ID; shared bearer
240
- calls are honestly labeled as bearer unless
241
- `bearerToken(secret, { subjectId })` assigns that credential a stable subject.
271
+ `call_tool`, `call_destructive_tool`, `batch_call`, or `execute_code`; retries
272
+ remain one event with an `attempts` count. Implementing `list` enables the
273
+ authenticated Activity tab in `/ui`; `activityReadGate` can narrow reads
274
+ further. Writes are best-effort and never change a tool result. Clerk calls
275
+ carry the Clerk user ID; shared bearer calls are honestly labeled as bearer
276
+ unless `bearerToken(secret, { subjectId })` assigns that credential a stable
277
+ subject. On Workers, pass `ctx` through to `connecta.fetch(request, env, ctx)`
278
+ so async writes settle on `waitUntil`.
279
+ [`examples/worker/src/d1-activity.ts`](./examples/worker/src/d1-activity.ts) is
280
+ a complete D1 implementation with keyset paging and a retention pass.
281
+
282
+ ## Operator dashboard
283
+
284
+ `GET /ui` is a read-only dashboard with no build step: connector health, tool
285
+ counts and descriptions with a client-side filter, downstream authorization
286
+ links, the credential controls above, and an Activity tab when an activity store
287
+ is configured. The shell is open because it carries no data; everything it shows
288
+ comes from `/ui/data`, behind the same auth gate as `/mcp`. With Clerk
289
+ configured it signs operators in through Clerk's hosted portal; a bearer-only
290
+ deployment falls back to a pasted token.
291
+
292
+ Every deployment-facing label and mark on `/ui` and the OAuth result pages comes
293
+ from `ConnectaConfig.branding` — `productName`, `ownerName`, their URLs,
294
+ `description`, `pageTitle`, `themeColor`, and `favicon` — each falling back to a
295
+ neutral Connecta default. Nothing about the operator is baked into the package.
242
296
 
243
297
  ## Learn more
244
298
 
245
299
  - **[`docs/documentation.md`](./docs/documentation.md)** — how everything works: architecture,
246
- the meta-tools, connectors, inbound auth, downstream OAuth, storage, running it
247
- (Node / Workers / Docker), Clerk setup, testing, troubleshooting.
300
+ the meta-tools, connectors and their usage guides, inbound auth, downstream
301
+ OAuth, storage, running it (Node / Workers / Docker), Clerk setup, testing,
302
+ troubleshooting, code mode, the status UI, activity history, and toolkits.
248
303
  - **[`docs/design.md`](./docs/design.md)** — why it's built this way, and the non-goals.
249
- - **Status UI** — a read-only operator dashboard at `GET /ui` (open shell; data from the bearer/Clerk-gated `/ui/data`). See [§14](./docs/documentation.md#14-status-ui).
304
+ - **[`CHANGELOG.md`](./CHANGELOG.md)** what changed in each release.
package/SECURITY.md CHANGED
@@ -12,14 +12,18 @@ sensitive payloads in a public issue.
12
12
 
13
13
  ## Dependency policy
14
14
 
15
- CI blocks high and critical production dependency advisories. Moderate findings
16
- are reviewed for reachability and recorded when an upstream package has not yet
17
- released a compatible fix.
15
+ CI blocks moderate, high, and critical production dependency advisories. Lower
16
+ findings are reviewed for reachability and recorded when an upstream package has
17
+ not yet released a compatible fix.
18
18
 
19
- The initial release has one such upstream finding:
20
19
  `@modelcontextprotocol/sdk@1.29.0` declares `@hono/node-server` 1.x, which is
21
20
  covered by `GHSA-frvp-7c67-39w9`. The advisory affects Hono's Windows
22
21
  `serve-static` implementation. Connecta does not import that adapter or serve
23
22
  static files through Hono. npm's suggested fix downgrades the MCP SDK below
24
- security behavior Connecta relies on, so the package retains SDK 1.29.0 while
25
- tracking an upstream compatible release.
23
+ security behavior Connecta relies on, so the package instead pins
24
+ `@hono/node-server` to 2.x through an `overrides` entry and keeps SDK 1.29.0.
25
+
26
+ Because npm only applies `overrides` to the root project, that pin governs this
27
+ repository's own installs rather than trees built by consumers of the published
28
+ package. The override is removed once the SDK declares `@hono/node-server` ^2
29
+ upstream.
@@ -34,6 +34,12 @@ export interface ToolCallActivityEvent {
34
34
  serverName: string;
35
35
  serverVersion: string;
36
36
  deploymentId?: string;
37
+ /**
38
+ * Name of the toolkit this connection selected with `?toolkit=`, when it
39
+ * selected one. Calls are recorded the same way with or without it; this only
40
+ * says which scoped view the call came through.
41
+ */
42
+ toolkitId?: string;
37
43
  }
38
44
  export interface ActivityPage {
39
45
  events: ToolCallActivityEvent[];
@@ -69,6 +75,8 @@ export interface ActivityRequestContext {
69
75
  version: string;
70
76
  };
71
77
  deploymentId?: string;
78
+ /** Toolkit this connection is scoped to, when `?toolkit=` selected one. */
79
+ toolkitId?: string;
72
80
  defer?: (promise: Promise<unknown>) => void;
73
81
  logger: Logger;
74
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,uBAAuB,GACvB,YAAY,GACZ,cAAc,CAAC;AAEnB,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,CAAC,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,iFAAiF;AACjF,qBAAa,0BAA2B,SAAQ,KAAK;IAC1C,IAAI,SAAgC;;CAK9C;AAED,MAAM,MAAM,gBAAgB,GAAG,CAC7B,KAAK,EAAE,aAAa,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,qBAAqB,EACnB,aAAa,GACb,UAAU,GACV,SAAS,GACT,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,UAAU,GACV,WAAW,CACd,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,sBAAsB,GAAG,SAAS,EAC3C,KAAK,EAAE,kBAAkB,GACxB,IAAI,CAkCN"}
1
+ {"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,uBAAuB,GACvB,YAAY,GACZ,cAAc,CAAC;AAEnB,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAE9D;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,CAAC,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,OAAO,EAAE,eAAe,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,IAAI,CAAC,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,iFAAiF;AACjF,qBAAa,0BAA2B,SAAQ,KAAK;IAC1C,IAAI,SAAgC;;CAK9C;AAED,MAAM,MAAM,gBAAgB,GAAG,CAC7B,KAAK,EAAE,aAAa,KACjB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC,gFAAgF;AAChF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAC5C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,qBAAqB,EACnB,aAAa,GACb,UAAU,GACV,SAAS,GACT,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,UAAU,GACV,WAAW,CACd,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,sBAAsB,GAAG,SAAS,EAC3C,KAAK,EAAE,kBAAkB,GACxB,IAAI,CAmCN"}
package/dist/activity.js CHANGED
@@ -32,6 +32,7 @@ export function recordToolActivity(context, input) {
32
32
  ...(context.deploymentId
33
33
  ? { deploymentId: context.deploymentId }
34
34
  : {}),
35
+ ...(context.toolkitId ? { toolkitId: context.toolkitId } : {}),
35
36
  };
36
37
  try {
37
38
  const result = context.sink.record(event);
@@ -1 +1 @@
1
- {"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AAiEA,iFAAiF;AACjF,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAC1C,IAAI,GAAG,4BAA4B,CAAC;IAE7C;QACE,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACnC,CAAC;CACF;AA6BD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA2C,EAC3C,KAAyB;IAEzB,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,MAAM,KAAK,GAA0B;QACnC,aAAa,EAAE,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;QACvB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrD,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACjD,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI;QACnC,aAAa,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO;QACzC,GAAG,CAAC,OAAO,CAAC,YAAY;YACtB,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE;YACxC,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;IACF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,IAAI,OAAQ,MAA2B,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACvE,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACtD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"AAuEA,iFAAiF;AACjF,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAC1C,IAAI,GAAG,4BAA4B,CAAC;IAE7C;QACE,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACnC,CAAC;CACF;AA+BD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA2C,EAC3C,KAAyB;IAEzB,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,MAAM,KAAK,GAA0B;QACnC,aAAa,EAAE,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;QACvB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrD,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACjD,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI;QACnC,aAAa,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO;QACzC,GAAG,CAAC,OAAO,CAAC,YAAY;YACtB,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,YAAY,EAAE;YACxC,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC/D,CAAC;IACF,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,IAAI,OAAQ,MAA2B,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACvE,OAAO;QACT,CAAC;QACD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACtD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,KAAK;YAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
@@ -17,6 +17,19 @@ export interface ApiOptions {
17
17
  /** Human-readable display name; the connector id remains the address prefix. */
18
18
  title?: string;
19
19
  description?: string;
20
+ /**
21
+ * Max inline result size (bytes) for this connector's tools before
22
+ * call_tool/batch_call truncate and stash the full text for get_result
23
+ * paging. Overrides the deployment's `maxResultBytes`; omit to inherit it.
24
+ * Must be a whole number of bytes >= 1; anything else warns at startup and
25
+ * is ignored.
26
+ */
27
+ maxResultBytes?: number;
28
+ /**
29
+ * Optional agent-facing usage guide (markdown) served by the `skills`
30
+ * meta-tool as `connector:<id>`. See `Connector.usageGuide`.
31
+ */
32
+ usageGuide?: string;
20
33
  /** Optional operator-managed credential exposed through ctx.credential and /ui. */
21
34
  credential?: ConnectorCredentialConfig;
22
35
  /** Optional validation behind /ui's Test action. */
@@ -31,6 +44,16 @@ export interface ApiOptions {
31
44
  * on loose coercion.
32
45
  */
33
46
  validateArgs?: boolean;
47
+ /**
48
+ * Fail-closed on a tool whose `inputSchema` the validator cannot evaluate
49
+ * (default false). The default surfaces such a schema as a one-time warning
50
+ * and then passes the raw arguments through, so a broken schema never breaks
51
+ * an otherwise working tool. Set true to instead reject those calls with a
52
+ * non-retryable `invalid_args` ConnectorCallError, so a schema that cannot be
53
+ * enforced never silently admits unvalidated input. Only consulted when
54
+ * `validateArgs` is not false.
55
+ */
56
+ strictValidation?: boolean;
34
57
  tools: ApiTool[];
35
58
  }
36
59
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/connectors/api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,EACV,eAAe,EAEhB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC3E;AAED,MAAM,WAAW,UAAU;IACzB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,oDAAoD;IACpD,cAAc,CAAC,EAAE,CACf,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,6DAA6D;IAC7D,eAAe,CAAC,EAAE,CAChB,MAAM,EAAE,yBAAyB,EACjC,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAsC3D"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/connectors/api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,yBAAyB,EACzB,yBAAyB,EACzB,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,EACV,eAAe,EAEhB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC3E;AAED,MAAM,WAAW,UAAU;IACzB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,oDAAoD;IACpD,cAAc,CAAC,EAAE,CACf,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,6DAA6D;IAC7D,eAAe,CAAC,EAAE,CAChB,MAAM,EAAE,yBAAyB,EACjC,GAAG,EAAE,gBAAgB,KAClB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS,CAkD3D"}
@@ -1,4 +1,4 @@
1
- import { validateToolInput } from "../validate.js";
1
+ import { precompileValidator, validateToolInput } from "../validate.js";
2
2
  /**
3
3
  * A connector defined entirely in code: static tool defs + fetch handlers.
4
4
  * Tool inputs are plain JSON Schema objects (bring your own zod-to-json-schema
@@ -20,11 +20,22 @@ export function api(id, opts) {
20
20
  }));
21
21
  const byName = new Map(opts.tools.map((t) => [t.name, t]));
22
22
  const validateArgs = opts.validateArgs ?? true;
23
+ const strictValidation = opts.strictValidation ?? false;
24
+ if (validateArgs) {
25
+ // Compile each schema now so a validator-hostile inputSchema surfaces once
26
+ // here rather than silently on its first call. Warning-only; never throws.
27
+ for (const t of opts.tools) {
28
+ if (t.inputSchema)
29
+ precompileValidator(t.inputSchema, { address: `${id}.${t.name}` });
30
+ }
31
+ }
23
32
  return {
24
33
  id,
25
34
  title: opts.title,
26
35
  kind: "api",
27
36
  description: opts.description,
37
+ maxResultBytes: opts.maxResultBytes,
38
+ usageGuide: opts.usageGuide,
28
39
  credential: opts.credential,
29
40
  testCredential: opts.testCredential,
30
41
  testCredentials: opts.testCredentials,
@@ -42,6 +53,7 @@ export function api(id, opts) {
42
53
  const invalid = validateToolInput(tool.inputSchema, input, {
43
54
  address: `${id}.${name}`,
44
55
  logger: ctx.logger,
56
+ failClosed: strictValidation,
45
57
  });
46
58
  if (invalid)
47
59
  throw invalid;
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/connectors/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAsDnD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,GAAG,CAAC,EAAU,EAAE,IAAgB;IAC9C,MAAM,IAAI,GAAc,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,WAAW,EAAE,CAAC,CAAC,WAAW;KAC3B,CAAC,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;IAC/C,OAAO;QACL,EAAE;QACF,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,WAAW,EAAE,IAAI;QACjB,KAAK,CAAC,SAAS;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG;YAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,mBAAmB,EAAE,GAAG,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;YACzB,IAAI,YAAY,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrC,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE;oBACzD,OAAO,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE;oBACxB,MAAM,EAAE,GAAG,CAAC,MAAM;iBACnB,CAAC,CAAC;gBACH,IAAI,OAAO;oBAAE,MAAM,OAAO,CAAC;YAC7B,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/connectors/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AA6ExE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,GAAG,CAAC,EAAU,EAAE,IAAgB;IAC9C,MAAM,IAAI,GAAc,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,WAAW,EAAE,CAAC,CAAC,WAAW;KAC3B,CAAC,CAAC,CAAC;IACJ,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;IAC/C,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,IAAI,KAAK,CAAC;IACxD,IAAI,YAAY,EAAE,CAAC;QACjB,2EAA2E;QAC3E,2EAA2E;QAC3E,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,WAAW;gBACf,mBAAmB,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IACD,OAAO;QACL,EAAE;QACF,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,KAAK;QACX,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,WAAW,EAAE,IAAI;QACjB,KAAK,CAAC,SAAS;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG;YAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,mBAAmB,EAAE,GAAG,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;YACzB,IAAI,YAAY,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBACrC,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE;oBACzD,OAAO,EAAE,GAAG,EAAE,IAAI,IAAI,EAAE;oBACxB,MAAM,EAAE,GAAG,CAAC,MAAM;oBAClB,UAAU,EAAE,gBAAgB;iBAC7B,CAAC,CAAC;gBACH,IAAI,OAAO;oBAAE,MAAM,OAAO,CAAC;YAC7B,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
2
- import type { Connector, ConnectorContext } from "../types.js";
2
+ import type { Connector, ConnectorContext, Logger } from "../types.js";
3
3
  export type RemoteMcpAuth = {
4
4
  type: "headers";
5
5
  headers: Record<string, string>;
@@ -11,7 +11,33 @@ export interface RemoteMcpOptions {
11
11
  /** Human-readable display name; the connector id remains the address prefix. */
12
12
  title?: string;
13
13
  description?: string;
14
+ /**
15
+ * Max inline result size (bytes) for this connector's tools before
16
+ * call_tool/batch_call truncate and stash the full text for get_result
17
+ * paging. Overrides the deployment's `maxResultBytes`; omit to inherit it.
18
+ * Must be a whole number of bytes >= 1; anything else warns at startup and
19
+ * is ignored.
20
+ */
21
+ maxResultBytes?: number;
22
+ /**
23
+ * Optional agent-facing usage guide (markdown) served by the `skills`
24
+ * meta-tool as `connector:<id>`. See `Connector.usageGuide`.
25
+ */
26
+ usageGuide?: string;
14
27
  auth?: RemoteMcpAuth;
28
+ /**
29
+ * Refuse to connect to a non-`https://` `url` at construction (default
30
+ * false). Loopback hosts (`localhost`, `127.0.0.1`, `[::1]`) are always
31
+ * allowed for local development. Off by default, static `headers` credentials
32
+ * over a cleartext connection are warned about but permitted; set this true
33
+ * to make that misconfiguration a hard error instead.
34
+ */
35
+ requireHttps?: boolean;
36
+ /**
37
+ * Destination for the cleartext-credential warning emitted at construction.
38
+ * Default console.
39
+ */
40
+ logger?: Logger;
15
41
  /**
16
42
  * @internal Testing seam. When set, this transport is used instead of the
17
43
  * HTTP transport, letting tests point the connector at an in-process MCP
@@ -1 +1 @@
1
- {"version":3,"file":"remote-mcp.d.ts","sourceRoot":"","sources":["../../src/connectors/remote-mcp.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAK/E,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAGjB,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,SAAS,CAAC;CAC1D;AAeD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,SAAS,CA+RvE"}
1
+ {"version":3,"file":"remote-mcp.d.ts","sourceRoot":"","sources":["../../src/connectors/remote-mcp.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAC;AAK/E,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAEhB,MAAM,EAEP,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,SAAS,CAAC;CAC1D;AAwBD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,SAAS,CA8TvE"}