@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.
- package/CHANGELOG.md +252 -0
- package/README.md +72 -17
- package/SECURITY.md +10 -6
- package/dist/activity.d.ts +8 -0
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +1 -0
- package/dist/activity.js.map +1 -1
- package/dist/connectors/api.d.ts +23 -0
- package/dist/connectors/api.d.ts.map +1 -1
- package/dist/connectors/api.js +13 -1
- package/dist/connectors/api.js.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +27 -1
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +31 -0
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/credentials.d.ts +2 -1
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +4 -2
- package/dist/credentials.js.map +1 -1
- package/dist/execute.d.ts +4 -4
- package/dist/execute.d.ts.map +1 -1
- package/dist/execute.js.map +1 -1
- package/dist/executors/quickjs.d.ts.map +1 -1
- package/dist/executors/quickjs.js +32 -4
- package/dist/executors/quickjs.js.map +1 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -1
- package/dist/index.js.map +1 -1
- package/dist/meta-tools.d.ts +25 -4
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +138 -24
- package/dist/meta-tools.js.map +1 -1
- package/dist/registry.d.ts +183 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +293 -27
- package/dist/registry.js.map +1 -1
- package/dist/server.d.ts +9 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +96 -12
- package/dist/server.js.map +1 -1
- package/dist/skills.d.ts +52 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +161 -1
- package/dist/skills.js.map +1 -1
- package/dist/storage/file.d.ts.map +1 -1
- package/dist/storage/file.js +19 -3
- package/dist/storage/file.js.map +1 -1
- package/dist/toolkits.d.ts +44 -0
- package/dist/toolkits.d.ts.map +1 -0
- package/dist/toolkits.js +134 -0
- package/dist/toolkits.js.map +1 -0
- package/dist/types.d.ts +20 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +29 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +100 -15
- package/dist/ui.js.map +1 -1
- package/dist/validate.d.ts +33 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +32 -2
- package/dist/validate.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -2
- package/src/activity.ts +9 -0
- package/src/connectors/api.ts +36 -1
- package/src/connectors/remote-mcp.ts +67 -0
- package/src/credentials.ts +6 -3
- package/src/execute.ts +4 -4
- package/src/executors/quickjs.ts +32 -4
- package/src/index.ts +141 -2
- package/src/meta-tools.ts +215 -40
- package/src/registry.ts +416 -29
- package/src/server.ts +130 -12
- package/src/skills.ts +184 -1
- package/src/storage/file.ts +18 -2
- package/src/toolkits.ts +215 -0
- package/src/types.ts +20 -1
- package/src/ui.ts +103 -14
- package/src/validate.ts +60 -2
- 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`)
|
|
206
|
-
|
|
207
|
-
|
|
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
|
|
237
|
-
`attempts` count. Implementing `list` enables the
|
|
238
|
-
`/ui`; `activityReadGate` can narrow reads
|
|
239
|
-
never change a tool result. Clerk calls
|
|
240
|
-
calls are honestly labeled as bearer
|
|
241
|
-
`bearerToken(secret, { subjectId })` assigns that credential a stable
|
|
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
|
|
247
|
-
(Node / Workers / Docker), Clerk setup, testing,
|
|
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
|
-
- **
|
|
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.
|
|
16
|
-
are reviewed for reachability and recorded when an upstream package has
|
|
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
|
|
25
|
-
|
|
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.
|
package/dist/activity.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/activity.d.ts.map
CHANGED
|
@@ -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;
|
|
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);
|
package/dist/activity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity.js","sourceRoot":"","sources":["../src/activity.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/connectors/api.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/connectors/api.js
CHANGED
|
@@ -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;
|
|
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,
|
|
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"}
|