@zackbart/connecta 0.6.0 → 0.7.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 +403 -0
- package/README.md +163 -308
- package/dist/auth/bearer.d.ts +4 -3
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +10 -8
- package/dist/auth/bearer.js.map +1 -1
- package/dist/auth/clerk.d.ts +8 -7
- package/dist/auth/clerk.d.ts.map +1 -1
- package/dist/auth/clerk.js +27 -8
- package/dist/auth/clerk.js.map +1 -1
- package/dist/connector-scope.d.ts +13 -0
- package/dist/connector-scope.d.ts.map +1 -0
- package/dist/connector-scope.js +35 -0
- package/dist/connector-scope.js.map +1 -0
- package/dist/connectors/api.d.ts +5 -5
- package/dist/connectors/api.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.d.ts +3 -3
- package/dist/connectors/remote-mcp.d.ts.map +1 -1
- package/dist/connectors/remote-mcp.js +309 -10
- package/dist/connectors/remote-mcp.js.map +1 -1
- package/dist/credential-health.d.ts +20 -9
- package/dist/credential-health.d.ts.map +1 -1
- package/dist/credential-health.js +127 -63
- package/dist/credential-health.js.map +1 -1
- package/dist/credentials.d.ts +84 -1
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +109 -2
- package/dist/credentials.js.map +1 -1
- package/dist/index.d.ts +83 -82
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +101 -31
- package/dist/index.js.map +1 -1
- package/dist/meta-tools.d.ts +3 -3
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +16 -7
- package/dist/meta-tools.js.map +1 -1
- package/dist/registry.d.ts +3 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +4 -3
- package/dist/registry.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +154 -52
- package/dist/server.js.map +1 -1
- package/dist/skills.js +2 -2
- package/dist/skills.js.map +1 -1
- package/dist/toolkits.js +1 -1
- package/dist/types.d.ts +51 -26
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +52 -21
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +665 -196
- package/dist/ui.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/src/auth/bearer.ts +10 -8
- package/src/auth/clerk.ts +28 -9
- package/src/connector-scope.ts +41 -0
- package/src/connectors/api.ts +5 -5
- package/src/connectors/remote-mcp.ts +348 -25
- package/src/credential-health.ts +151 -71
- package/src/credentials.ts +166 -3
- package/src/index.ts +202 -113
- package/src/meta-tools.ts +22 -7
- package/src/registry.ts +4 -3
- package/src/server.ts +197 -71
- package/src/skills.ts +2 -2
- package/src/toolkits.ts +1 -1
- package/src/types.ts +51 -26
- package/src/ui.ts +703 -195
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,409 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.7.0 — 2026-07-27
|
|
6
|
+
|
|
7
|
+
0.7.0 is the surface settlement: one release that finishes moving connecta's
|
|
8
|
+
public shape so that it can stop moving. It arrives from two directions at once.
|
|
9
|
+
A controlled qualification pass against 0.6.1 (issue #86) measured four ways a
|
|
10
|
+
deployment could be worn down — an unbounded cache, an enumerable callback, a
|
|
11
|
+
session leak, and a credential that reports itself usable and then isn't — and
|
|
12
|
+
those are fixed here. Meanwhile the config surface had accumulated eleven flat
|
|
13
|
+
tuning fields, the reference manual had reached 2,600 lines, and the operator UI
|
|
14
|
+
was one page pretending to be three. Those are settled here too, and settling
|
|
15
|
+
them breaks things, which is the argument for doing it in one release instead of
|
|
16
|
+
spreading it across three.
|
|
17
|
+
|
|
18
|
+
**What breaks.** Three things, and each is a compile error or a loud refusal
|
|
19
|
+
rather than a behavior change you have to notice on your own. Every flat 0.6.x
|
|
20
|
+
tuning option is removed in favour of four grouped ones. A connector that
|
|
21
|
+
implements `finishAuth` without `verifyState` can no longer complete OAuth. And
|
|
22
|
+
`/`, `/credentials`, and `/activity` are now core-owned routes, so a connector
|
|
23
|
+
`handleRequest` that served any of them is shadowed. The first is mechanical:
|
|
24
|
+
`createConnecta` throws once, before it reads anything else, naming every legacy
|
|
25
|
+
path it found and its replacement.
|
|
26
|
+
|
|
27
|
+
**What a deployment can ignore.** The defaults and runtime behavior of every
|
|
28
|
+
config option are unchanged — only the paths moved. `/ui` bookmarks keep working
|
|
29
|
+
through a permanent redirect, and all 17 documentation anchors still resolve
|
|
30
|
+
even though the manual is now nine files. If you configure connecta with a
|
|
31
|
+
literal object, do not paginate a downstream MCP server, and write your own
|
|
32
|
+
connectors against `remoteMcp`/`api` rather than hand-rolling `finishAuth`, the
|
|
33
|
+
whole migration is a mechanical rewrite of one config block.
|
|
34
|
+
|
|
35
|
+
### Breaking
|
|
36
|
+
|
|
37
|
+
- **`ConnectaConfig` tuning is grouped by subsystem for 0.7.0** (issue #28).
|
|
38
|
+
The flat 0.6.x options are removed rather than deprecated: TypeScript rejects
|
|
39
|
+
them, and JavaScript callers get one fail-fast error from `createConnecta`
|
|
40
|
+
listing every legacy own property it finds and its replacement. Supplying
|
|
41
|
+
both an old and a new path is still an error; there is no precedence rule or
|
|
42
|
+
compatibility alias. Migrate configuration as follows:
|
|
43
|
+
|
|
44
|
+
| 0.6.x | 0.7.0 |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `activity` | `activity.store` |
|
|
47
|
+
| `activityReadGate` | `activity.readGate` |
|
|
48
|
+
| `activityDeploymentId` | `activity.deploymentId` |
|
|
49
|
+
| `credentialEncryptionKey` | `credentials.encryptionKey` |
|
|
50
|
+
| `credentialHealth` | `credentials.health` |
|
|
51
|
+
| `toolCacheTtlSeconds` | `discovery.catalogTtlSeconds` |
|
|
52
|
+
| `persistToolCatalog` | `discovery.persistCatalog` |
|
|
53
|
+
| `toolCatalogStaleSeconds` | `discovery.staleCatalogSeconds` |
|
|
54
|
+
| `probeTimeoutMs` | `discovery.probeTimeoutMs` |
|
|
55
|
+
| `defaultToolTimeoutMs` | `calls.defaultTimeoutMs` |
|
|
56
|
+
| `maxResultBytes` | `calls.maxResultBytes` |
|
|
57
|
+
|
|
58
|
+
For example:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// 0.6.x
|
|
62
|
+
createConnecta({
|
|
63
|
+
connectors,
|
|
64
|
+
storage,
|
|
65
|
+
activity,
|
|
66
|
+
credentialEncryptionKey: env.CONNECTA_CREDENTIAL_KEY,
|
|
67
|
+
toolCacheTtlSeconds: 300,
|
|
68
|
+
defaultToolTimeoutMs: 30_000,
|
|
69
|
+
maxResultBytes: 50_000,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// 0.7.0
|
|
73
|
+
createConnecta({
|
|
74
|
+
connectors,
|
|
75
|
+
storage,
|
|
76
|
+
activity: { store: activity },
|
|
77
|
+
credentials: { encryptionKey: env.CONNECTA_CREDENTIAL_KEY },
|
|
78
|
+
discovery: { catalogTtlSeconds: 300 },
|
|
79
|
+
calls: { defaultTimeoutMs: 30_000, maxResultBytes: 50_000 },
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The defaults and runtime behavior of each option are unchanged. Connector
|
|
84
|
+
definitions keep their per-connector `maxResultBytes`, and structural seams
|
|
85
|
+
such as `storage`, `auth`, `connectors`, `toolkits`, and `executor` remain at
|
|
86
|
+
the top level.
|
|
87
|
+
- **A connector implementing `finishAuth` without `verifyState` can no longer
|
|
88
|
+
complete OAuth** (issue #62). Such a connector previously exchanged the
|
|
89
|
+
authorization code with no CSRF guard at all — connecta had no way to
|
|
90
|
+
establish that it had started the flow being completed. The callback now
|
|
91
|
+
refuses with the same opaque 400 as every other refusal, exchanges no code,
|
|
92
|
+
and logs one operator-grade line naming the connector and the missing hook.
|
|
93
|
+
`verifyState` stays optional in the type system and is required in practice
|
|
94
|
+
wherever `finishAuth` is present. The shipped `remoteMcp` OAuth provider has
|
|
95
|
+
always implemented it, so this reaches hand-written connectors only.
|
|
96
|
+
- **`/`, `/credentials`, and `/activity` are now core-owned routes** (issue
|
|
97
|
+
#57). They previously fell through to connector `handleRequest` and then to a
|
|
98
|
+
404, so a connector serving any of the three is now shadowed without warning.
|
|
99
|
+
`GET /` returns the operator shell where 0.6.1 returned 404, and non-GET on
|
|
100
|
+
those routes and on `/ui` returns 405 rather than falling through.
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
|
|
104
|
+
- **`Connector.closeScope?(ctx)`** (issue #66) — an optional, best-effort seam
|
|
105
|
+
for releasing whatever a connector opened for one scope. Called at most once,
|
|
106
|
+
inside a fixed 100 ms completion window: a hook that is absent, throws, or
|
|
107
|
+
never settles cannot replace, corrupt, or delay the result of the operation
|
|
108
|
+
that triggered it. `remoteMcp` implements it, tearing down cached and
|
|
109
|
+
half-open sessions and fencing late connection races. Connectors that do not
|
|
110
|
+
implement it are unaffected.
|
|
111
|
+
|
|
112
|
+
### Changed
|
|
113
|
+
|
|
114
|
+
- **The operator UI is now a direct-linkable three-page set** (issue #57):
|
|
115
|
+
Connections at `/`, Credentials at `/credentials`, and Activity at
|
|
116
|
+
`/activity`. `GET /ui` remains as a permanent `308` compatibility redirect to
|
|
117
|
+
`/`. The pages use semantic navigation, path-specific titles, direct
|
|
118
|
+
load/refresh/bookmark and Back/Forward behavior, and the same restrained
|
|
119
|
+
responsive connector-ledger design. They are three routes over one
|
|
120
|
+
no-build-step client implementation, not copied applications.
|
|
121
|
+
- **Every canonical operator route serves the same open, data-free shell.** The
|
|
122
|
+
nonce CSP, framing denial, referrer policy, HSTS behavior, content-type
|
|
123
|
+
protection, URL gates, and escaping guarantees formerly attached to `/ui`
|
|
124
|
+
apply equally to `/`, `/credentials`, and `/activity`. Deployment data remains
|
|
125
|
+
behind the unchanged private APIs: `/ui/data`, `/ui/activity`, and
|
|
126
|
+
`/ui/credentials/<connectorId>[/test]`.
|
|
127
|
+
- **Vault controls moved out of connector cards and into Credentials.**
|
|
128
|
+
Connections remains read-only status; Credentials preserves Add/Replace/Test/
|
|
129
|
+
Remove, masked metadata, stored-shape recovery, and live feedback without ever
|
|
130
|
+
returning a secret. Its explicit capability states cover eligible Clerk
|
|
131
|
+
operators, bearer-only sessions, missing vaults, and deployments with no
|
|
132
|
+
credential slots without broadening the existing Clerk/user-id/toolkit/
|
|
133
|
+
same-origin mutation boundary. Activity likewise renders an explicit
|
|
134
|
+
not-configured state when `activity.store.list` is absent. OAuth result pages
|
|
135
|
+
now return to `/`.
|
|
136
|
+
- **The reference manual is nine documents instead of one** (issue #61).
|
|
137
|
+
`docs/documentation.md` had reached 2,600 lines; it is now a compatibility
|
|
138
|
+
index that preserves all 17 `#N-…` anchors, so every existing deep link from
|
|
139
|
+
the README, the examples, and source comments still resolves. The prose moved
|
|
140
|
+
to `architecture`, `meta-tools`, `connectors`, `auth`,
|
|
141
|
+
`storage-and-credentials`, `operations`, `code-mode`, `operator-ui`, and
|
|
142
|
+
`toolkits`. A new `npm run check:docs` validates local links, fragments,
|
|
143
|
+
heading slugs, and the legacy-anchor manifest, and runs first in
|
|
144
|
+
`npm run check`. Contributor-facing only: the published package is unchanged
|
|
145
|
+
and docs still ship in no tarball.
|
|
146
|
+
|
|
147
|
+
### Fixed
|
|
148
|
+
|
|
149
|
+
- **`clerkAuth`'s identity-decision cache is bounded at 1,024 identities per
|
|
150
|
+
instance** (issue #70). It never evicted: every distinct identity that reached
|
|
151
|
+
the gate was retained for the lifetime of the isolate. `allowedDomains` made
|
|
152
|
+
that attacker-reachable on an open-signup Clerk instance, since an
|
|
153
|
+
authenticated-but-denied identity is still a cache entry — 100,000 denied
|
|
154
|
+
identities measured at roughly 11.96 MiB retained, with the oldest entry still
|
|
155
|
+
present. Eviction is LRU, so a small active set stays hot, and the only thing
|
|
156
|
+
evicting an entry can cause is a fresh check against Clerk: it can never turn
|
|
157
|
+
a denial into an admission, and it cannot extend an allow past its TTL, since
|
|
158
|
+
a cache hit re-inserts the same record rather than restamping it. The bound is
|
|
159
|
+
deliberately fixed rather than an operator knob, and the ~60 s allow / ~30 s
|
|
160
|
+
deny windows are unchanged.
|
|
161
|
+
- **`/oauth/callback/<id>` no longer lets an unauthenticated caller enumerate
|
|
162
|
+
configured connector ids** (issue #62). 0.6.1 answered `404 Unknown connector
|
|
163
|
+
"<id>"` for an id it did not recognise and a distinct 400 for a state
|
|
164
|
+
mismatch, which made the callback a free directory of every connector a
|
|
165
|
+
deployment had configured. Unknown ids, non-OAuth connectors, missing or
|
|
166
|
+
mismatched state, an absent verifier, and a throwing verifier now return one
|
|
167
|
+
byte-identical 400 — same status, same body, same headers. They also cost the
|
|
168
|
+
same single storage read, so the clock cannot sort the ids the body refuses to
|
|
169
|
+
name. That is cost equalisation rather than constant time, and the
|
|
170
|
+
documentation says so and enumerates what stays distinguishable. The precise
|
|
171
|
+
diagnosis moved to the operator log, bounded and escaped.
|
|
172
|
+
- **Probe-opened downstream MCP sessions are closed instead of abandoned**
|
|
173
|
+
(issue #66). Credential-health sweeps, `list_connectors({ probe: true })`, and
|
|
174
|
+
`/ui/data` each opened downstream sessions and left them for the provider to
|
|
175
|
+
age out — measured at 200 probes opening 200 sessions and explicitly closing
|
|
176
|
+
zero, made continuous by the periodic sweep. Closing now ends the session
|
|
177
|
+
rather than merely dropping the local client: `remoteMcp` issues the
|
|
178
|
+
specification's `DELETE` carrying `Mcp-Session-Id` *before* the close that
|
|
179
|
+
would otherwise abort it, feature-detected and best-effort, while a stateless
|
|
180
|
+
downstream issues none. A downstream that refuses, errors, or never answers is
|
|
181
|
+
closed anyway. One consequence worth knowing: a probing `list_connectors` now
|
|
182
|
+
runs on its own scope rather than the request's — it has to, since a request
|
|
183
|
+
scope cannot be closed — so a request that probes *and* then calls the same
|
|
184
|
+
connector opens two downstream sessions where it opened one. Per-request
|
|
185
|
+
`/mcp` scopes are unchanged.
|
|
186
|
+
- **A credential stored under an older declaration no longer reports itself
|
|
187
|
+
usable and then fails** (issue #69). `/ui`, the credential Test route, and
|
|
188
|
+
credential health each interpreted the stored shape slightly differently, so a
|
|
189
|
+
credential could render as configured and testable and then answer 409 with a
|
|
190
|
+
misleading configure-first message. One pure classifier now answers for all
|
|
191
|
+
three, and it asks about *containment* rather than equality: a stored set is
|
|
192
|
+
fine as long as it holds every field the declaration currently names. A
|
|
193
|
+
missing declared field is drift — which covers every case issue #69 reported,
|
|
194
|
+
including a rename and a swap between the single-value and named shapes in
|
|
195
|
+
either direction — and drift is never auto-migrated. Credentials keeps Replace
|
|
196
|
+
and Remove and hides Test, the test route answers 409 without calling a hook,
|
|
197
|
+
and health records an explicit error rather than a fabricated verdict, checked
|
|
198
|
+
before the freshness gate so a stale `ok` cannot mask a redeploy. Leftover
|
|
199
|
+
keys are explicitly *not* drift: dropping a field from a declaration leaves
|
|
200
|
+
its secret in the vault, but every accessor keeps returning the right value
|
|
201
|
+
and every call keeps working, so calling that drift would tell an operator to
|
|
202
|
+
re-enter a credential that works and that many providers will not reissue in
|
|
203
|
+
readable form. Credentials prints one non-blocking line naming the leftovers
|
|
204
|
+
instead. A repeated drift verdict is charged to the freshness budget rather
|
|
205
|
+
than rewritten on every sweep in every isolate.
|
|
206
|
+
- **A paginated downstream MCP server no longer loses everything after its
|
|
207
|
+
first `tools/list` page** (issue #77). `tools/list` is cursor-paginated in the
|
|
208
|
+
MCP specification and the SDK's `Client.listTools()` returns one page,
|
|
209
|
+
`nextCursor` included, without following it. `remoteMcp()` called it once, so
|
|
210
|
+
a downstream that paged its catalog was split in half without complaint:
|
|
211
|
+
page-one tools were counted, searchable, describable and callable, and every
|
|
212
|
+
tool after them appeared not to exist — missing from `list_connectors` counts,
|
|
213
|
+
`search_tools`, `describe_tools`, and address resolution alike, with nothing
|
|
214
|
+
in any log saying a page had been left behind. That is the wrong failure for
|
|
215
|
+
a product whose premise is progressive discovery over large tool catalogs,
|
|
216
|
+
since large catalogs are exactly the ones that paginate. A catalog refresh now
|
|
217
|
+
walks the whole cursor chain on the same request-scoped client before the
|
|
218
|
+
result is indexed. Cursors are handed back byte-for-byte and never parsed,
|
|
219
|
+
rewritten, or persisted; an empty-string `nextCursor` is treated as *present*
|
|
220
|
+
(pagination ends on an absent one, and a truthiness check there would truncate
|
|
221
|
+
the catalog silently). A downstream that advertises no `nextCursor` is
|
|
222
|
+
unchanged in both directions: one request, sent with no `cursor` param, and
|
|
223
|
+
byte-identical tool definitions. Tools are deduplicated by name, first page
|
|
224
|
+
wins, so an unstable cursor that overlaps pages cannot inflate `toolCount`,
|
|
225
|
+
double a `search_tools` row, or churn the registry into a persistence write
|
|
226
|
+
per refresh. A `nextCursor: null` — a common JSON idiom for end-of-pagination
|
|
227
|
+
that the MCP result schema does not accept — is now reported as a named
|
|
228
|
+
nonconformance instead of a raw Zod dump. Any page that fails fails the whole
|
|
229
|
+
refresh rather than publishing a prefix, so the registry keeps serving the
|
|
230
|
+
last complete catalog through its existing stale fallback.
|
|
231
|
+
|
|
232
|
+
The walk is bounded on **tools**, not pages. A page ceiling is the wrong
|
|
233
|
+
dimension — the server picks the page size, so N pages is N × a number
|
|
234
|
+
connecta cannot observe, and the common conformant idiom of "advertise a
|
|
235
|
+
cursor whenever the page came back full, then serve one empty page to
|
|
236
|
+
terminate" means a well-behaved 10,000-tool server paging at 100 spends 101
|
|
237
|
+
requests. So a refresh now fails immediately on a cursor handed back twice,
|
|
238
|
+
fails on two consecutive pages that add nothing while still promising more
|
|
239
|
+
(one is legal — that empty terminator), caps what it accumulates at
|
|
240
|
+
`MAX_TOOLS` (100,000, the top of the catalog envelope issue #82 benchmarks),
|
|
241
|
+
and keeps `MAX_TOOL_PAGES` (raised to 10,000) only as an unreachable runaway
|
|
242
|
+
backstop. These bounds make an unterminating walk finite and report it as a
|
|
243
|
+
connector error; they do not cancel one that a probe deadline has already
|
|
244
|
+
abandoned, because `withTimeout` bounds the caller's wait rather than the
|
|
245
|
+
work.
|
|
246
|
+
|
|
247
|
+
- **Downstream output-schema validation now covers every page of a paginated
|
|
248
|
+
catalog, not just the last one** (issue #77). The MCP SDK's
|
|
249
|
+
`Client.listTools()` rebuilds its output-schema validators and task-support
|
|
250
|
+
sets from each page it receives, clearing them first — so walking the cursor
|
|
251
|
+
chain left the request-scoped client validating the final page alone. A
|
|
252
|
+
`call_tool` against any earlier-page tool then found no validator and
|
|
253
|
+
silently skipped both the "declared an `outputSchema` but returned no
|
|
254
|
+
`structuredContent`" check and the structured-content validation, and lost
|
|
255
|
+
the required-task guard with them. Enforcement depended on which page a tool
|
|
256
|
+
happened to land on. A completed walk now re-primes that cache once from the
|
|
257
|
+
full aggregated catalog; a test asserts the pinned SDK still provides the
|
|
258
|
+
method it reaches for, so a future bump fails CI rather than quietly
|
|
259
|
+
restoring the gap.
|
|
260
|
+
|
|
261
|
+
## 0.6.1 — 2026-07-26
|
|
262
|
+
|
|
263
|
+
A patch release: three bug fixes and a documentation overhaul. No new
|
|
264
|
+
configuration, no new exported types, no change to the published API surface —
|
|
265
|
+
`ConnectaConfig` and every type are byte-identical to 0.6.0. Nothing here is
|
|
266
|
+
breaking. But two of the fixes *withdraw* behavior a deployment could have been
|
|
267
|
+
relying on by accident, and in both cases withdrawing it is what the fix is, not
|
|
268
|
+
a decision taken alongside it:
|
|
269
|
+
|
|
270
|
+
- **A `uiAuth.signInUrl` or `signUpUrl` that is not an absolute `https:` URL is
|
|
271
|
+
now dropped** rather than handed to `Clerk.load` (issue #56). Relative paths,
|
|
272
|
+
`http:`, protocol-relative, `javascript:` and `data:` values all fall together,
|
|
273
|
+
under the same gate `frontendApiUrl` has always passed. A rejected value reaches no part of the rendered page: the key is
|
|
274
|
+
simply absent from `/ui`'s inline `AUTH` object, so Clerk falls back to its own
|
|
275
|
+
default exactly as it does for an unset field, and `/ui` still renders and
|
|
276
|
+
still signs operators in. Construction logs one warning naming each dropped
|
|
277
|
+
field. What belongs in these fields is a hosted Account Portal address
|
|
278
|
+
(`https://accounts.<domain>`, `https://<slug>.accounts.dev`), so a deployment
|
|
279
|
+
that noticed this at all was pointing operators at a sign-in page connecta does
|
|
280
|
+
not host.
|
|
281
|
+
- **The declared credential shape now picks the credential test hook, on every
|
|
282
|
+
surface** (issue #55). Two of the four shape/hook combinations change behavior.
|
|
283
|
+
A **single-value `credential` declaring both hooks** now runs
|
|
284
|
+
`testCredential(value)` with the raw string, where the test route previously
|
|
285
|
+
preferred `testCredentials` and handed it the vault's reserved `{ value }` map
|
|
286
|
+
— the single-value hook now receives the string it was written to expect
|
|
287
|
+
instead of a one-entry map named after a storage detail. A **single-value
|
|
288
|
+
`credential` declaring only `testCredentials`** is no longer tested or probed
|
|
289
|
+
at all: /ui renders no Test button, a direct POST answers 400 naming the
|
|
290
|
+
mismatch, `createConnecta` warns at construction, and the liveness sweep added
|
|
291
|
+
in 0.6.0 reports it `not_checkable` instead of calling a hook with a shape its
|
|
292
|
+
connector never declared. Both of those are the fallback order that *was* the
|
|
293
|
+
bug, so there is no version of this fix that preserves them. Named `fields`
|
|
294
|
+
with `testCredentials`, or with both hooks, behave exactly as they did.
|
|
295
|
+
|
|
296
|
+
One textual delta reaches every deployment and no configuration turns it off, so
|
|
297
|
+
anything snapshotting agent-facing error text will diff: `resolveSkill`'s two
|
|
298
|
+
`Available:` branches now say `Available skills:` like the other three (issue
|
|
299
|
+
#50).
|
|
300
|
+
|
|
301
|
+
The docs are substantially reorganized, and one of the changes ships to npm:
|
|
302
|
+
readers of the package page get a completely different README. See Changed. The
|
|
303
|
+
next intentional breaking release stays reserved for issue #28.
|
|
304
|
+
|
|
305
|
+
### Changed
|
|
306
|
+
|
|
307
|
+
- **The README is rewritten around the problem connecta solves** (345 lines to
|
|
308
|
+
196). It had become a condensed reference manual — roughly 60% code blocks and
|
|
309
|
+
option tables, every one of them duplicated in `docs/documentation.md` or
|
|
310
|
+
`examples/` — with a single paragraph explaining why connecta exists. npm
|
|
311
|
+
readers see only this file, so it was the worst place for the duplication and
|
|
312
|
+
the best place for the argument. It now runs as prose: the context-window and
|
|
313
|
+
per-client configuration costs of connecting an agent to N MCP servers, what
|
|
314
|
+
connecta does about them, why it is shaped the way it is (config as code, a
|
|
315
|
+
server-side credential vault with liveness checks, fail-closed read-only,
|
|
316
|
+
toolkits and who is admitted to them, payload-free activity, a read-only
|
|
317
|
+
operator dashboard), when *not* to use it, one minimal Node quickstart, and
|
|
318
|
+
links out. Removed from it and unchanged in `docs/` and `examples/`: the
|
|
319
|
+
nine-row signature-level meta-tool table (the nine names now appear in prose
|
|
320
|
+
with their purposes), the Worker quickstart, and the toolkits, code-mode,
|
|
321
|
+
credentials, activity and branding blocks with their option semantics.
|
|
322
|
+
**Links out of the README are now absolute GitHub URLs**, because `docs/` does
|
|
323
|
+
not ship in the package and relative links are dead on npmjs.com. This is the
|
|
324
|
+
only change in the release that reaches npm as content rather than code.
|
|
325
|
+
- **`docs/design.md` is retired into a new `docs/decisions.md`.** design.md was
|
|
326
|
+
mostly a worse copy of documentation.md, and it had gone stale in at least one
|
|
327
|
+
load-bearing way: it claimed the Clerk adapter passes `authorizedParties:
|
|
328
|
+
[connectaOrigin]` to `authenticateRequest`. It does not, and must not — OAuth
|
|
329
|
+
access tokens may be JWTs with no `azp` claim, and Clerk rejects `azp ===
|
|
330
|
+
undefined` when that option is set, so passing it would reject every MCP
|
|
331
|
+
client; the `azp` pin is applied by hand after verification, for session tokens
|
|
332
|
+
only. decisions.md records that as a **rejected alternative** rather than
|
|
333
|
+
repeating the wrong version. It answers two questions documentation.md does
|
|
334
|
+
not: "may I build X?" (non-goals, rejected alternatives) and "must my change
|
|
335
|
+
preserve Y?" (invariants) — collecting the invariants documentation.md states
|
|
336
|
+
but never gathers as pointers rather than duplicated prose, alongside the four
|
|
337
|
+
that lived only in design.md: no runtime admin, nothing request-bound surviving
|
|
338
|
+
a request, single tenant, and fail-closed read-only. Two facts moved into
|
|
339
|
+
documentation.md §13 instead, next to the code they explain. Inbound references
|
|
340
|
+
in the README, documentation.md and the Docker example follow; **any external
|
|
341
|
+
link to `docs/design.md` is now dead**, and the 0.x CHANGELOG mentions of it
|
|
342
|
+
stay as historical record. The credential-test invariant added by issue #55
|
|
343
|
+
also gained the qualifier §17 already carried: a connector whose shape and hook
|
|
344
|
+
mismatch carries no verdict *from a credential hook*, but is still probed
|
|
345
|
+
through `status()` if it declares one, since that question never involves the
|
|
346
|
+
shape.
|
|
347
|
+
- **CLAUDE.md is an agent brief rather than a policy stub.** It now states what
|
|
348
|
+
gates "done" (`npm run check`, and what that runs), the two-document map
|
|
349
|
+
(documentation.md as reference manual, decisions.md as non-goals and
|
|
350
|
+
invariants) with the warning that its section numbers are linked from source
|
|
351
|
+
comments, the two CI-enforced boundaries that are invisible from inside a
|
|
352
|
+
single file (import-graph purity, the published surface) as where-new-code-goes
|
|
353
|
+
guidance, the `WORKERS_SUITES` allowlist trap that silently skips a portable
|
|
354
|
+
suite left out of it, and the commit, CHANGELOG and release conventions. The
|
|
355
|
+
roadmap-lives-in-GitHub-issues policy is unchanged, verbatim.
|
|
356
|
+
|
|
357
|
+
### Fixed
|
|
358
|
+
|
|
359
|
+
- **One rule decides how a credential is tested, and three copies of it no
|
|
360
|
+
longer disagree** (issue #55). `/ui` offered a Test button from the mere
|
|
361
|
+
presence of a hook (`testCredential || testCredentials`); the test route made
|
|
362
|
+
its own different choice — prefer `testCredentials`, else `testCredential` on
|
|
363
|
+
the vault's reserved `value` field; and 0.6.0's credential-health prober added
|
|
364
|
+
a third copy of that same preference order. They disagreed in both mismatch
|
|
365
|
+
shapes, and the visible bug was the ugliest one: a connector declaring named
|
|
366
|
+
`credential.fields` with only `testCredential` answered **409 "configure the
|
|
367
|
+
credential before testing it" on a fully configured credential**, blaming the
|
|
368
|
+
operator for connecta's own hook selection. `credentialTestRule` in
|
|
369
|
+
`src/credentials.ts` is now the single source of truth — `buildUiData`'s
|
|
370
|
+
`testable` flag, the test route's hook selection, the credential-health
|
|
371
|
+
prober's `testHookFor`/`isCheckableConnector`, and a new construction-time
|
|
372
|
+
warning all read it, so they cannot drift apart again. **The declared shape
|
|
373
|
+
picks the hook and the other one is never substituted**: named fields are
|
|
374
|
+
tested by `testCredentials` with the whole set, a single value by
|
|
375
|
+
`testCredential` with the raw string. A connector implementing only the
|
|
376
|
+
mismatched hook is not testable rather than testable-by-accident — no button, a
|
|
377
|
+
400 sharing its wording with the boot warning, and `not_checkable` to the
|
|
378
|
+
prober, which carries no verdict rather than an invented one (handing
|
|
379
|
+
`testCredential` a `value` that named fields never wrote would test the empty
|
|
380
|
+
string and record a confident `auth_required` about a credential nothing
|
|
381
|
+
examined). `status()` is unaffected: a mismatched connector that implements it
|
|
382
|
+
is still probed through it. The two behavior deltas this produces are stated
|
|
383
|
+
above. Recorded as an invariant in `docs/decisions.md`, and stated in
|
|
384
|
+
documentation.md §4, §7, §14 and §17.
|
|
385
|
+
- **`uiAuth.signInUrl` and `signUpUrl` are gated like every other URL that
|
|
386
|
+
reaches the browser** (issue #56). They were the residual — operator config
|
|
387
|
+
that arrives in the page as a *navigation target* rather than an attribute, so
|
|
388
|
+
no gate covered them and §14's URL-position invariant had to be read with an
|
|
389
|
+
exception beside it. Both are serialized into `/ui`'s inline `AUTH` object and
|
|
390
|
+
handed to `Clerk.load`, which navigates to them when an operator signs in. They
|
|
391
|
+
now pass the same gate `frontendApiUrl` does, so `isSafeScriptSrcUrl` — named
|
|
392
|
+
for the one position it used to guard — becomes `isSafeHttpsUrl`, one predicate
|
|
393
|
+
for all three `uiAuth` URLs. Absolute `https:` only, for the typed fields as
|
|
394
|
+
for the derived one: the loose carve-outs buy nothing real, since `http:` would
|
|
395
|
+
carry a sign-in over cleartext and a path relative to this origin is
|
|
396
|
+
meaningless because connecta hosts no sign-in page of its own. The drop warning
|
|
397
|
+
decides "did the operator mean to supply this?" through the same `isSetUrlValue`
|
|
398
|
+
helper `branding` uses, so a blank-string or falsy non-string value cannot warn
|
|
399
|
+
on one path and stay silent on the other. **§14 now states the invariant over
|
|
400
|
+
every URL position — attribute and navigation target — with no carve-out left.**
|
|
401
|
+
- **`resolveSkill` enumerates its list under one label** (issue #50). Three
|
|
402
|
+
branches said `Available skills:` and two said `Available:`, so which one an
|
|
403
|
+
agent saw depended on the branch it hit rather than on any difference in
|
|
404
|
+
meaning. Agent-facing error text is interface, and two labels read as two
|
|
405
|
+
concepts where there is one. Both stragglers now say `Available skills:`, and a
|
|
406
|
+
test walks all five error branches so reverting any one of them fails.
|
|
407
|
+
|
|
5
408
|
## 0.6.0 — 2026-07-26
|
|
6
409
|
|
|
7
410
|
A feature release that makes two of 0.5.0's mechanisms protective rather than
|