@zackbart/connecta 0.24.2 → 0.24.3
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 +141 -0
- package/dist/auth/bearer.js +2 -0
- package/dist/auth/downstream-oauth.d.ts +12 -1
- package/dist/auth/downstream-oauth.js +147 -35
- package/dist/call-admission.d.ts +4 -0
- package/dist/call-admission.js +26 -0
- package/dist/catalog-drift.js +9 -4
- package/dist/catalog-service.d.ts +2 -0
- package/dist/catalog-service.js +25 -8
- package/dist/catalog.d.ts +2 -0
- package/dist/catalog.js +246 -121
- package/dist/connectors/api.js +11 -1
- package/dist/connectors/guarded-fetch.d.ts +1 -1
- package/dist/connectors/guarded-fetch.js +27 -20
- package/dist/connectors/remote-mcp.js +84 -53
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +58 -0
- package/dist/execute.js +85 -23
- package/dist/executor-result.js +3 -1
- package/dist/executors/quickjs-child.js +5 -1
- package/dist/executors/quickjs-protocol.d.ts +4 -0
- package/dist/executors/quickjs-runtime.d.ts +1 -1
- package/dist/executors/quickjs-runtime.js +38 -21
- package/dist/executors/quickjs.js +68 -27
- package/dist/index.d.ts +14 -0
- package/dist/index.js +24 -3
- package/dist/invocation.js +134 -93
- package/dist/mcp-result.js +3 -2
- package/dist/meta-tools.js +118 -39
- package/dist/registry.d.ts +14 -2
- package/dist/registry.js +87 -13
- package/dist/routes/mcp.d.ts +4 -1
- package/dist/routes/mcp.js +84 -13
- package/dist/routes/oauth.js +4 -0
- package/dist/routes/shared.d.ts +1 -0
- package/dist/routes/shared.js +4 -4
- package/dist/server.js +15 -3
- package/dist/skills.js +6 -5
- package/dist/storage/file.d.ts +6 -2
- package/dist/storage/file.js +312 -34
- package/dist/storage/memory.js +12 -1
- package/dist/validate.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +22 -6
- package/documentation/auth.md +42 -9
- package/documentation/call-admission.md +24 -8
- package/documentation/code-mode.md +34 -22
- package/documentation/connectors.md +47 -5
- package/documentation/meta-tools.md +74 -6
- package/documentation/operations.md +19 -19
- package/documentation/provider-conventions.md +7 -0
- package/documentation/request-admission.md +38 -4
- package/documentation/storage-and-credentials.md +54 -1
- package/documentation/upgrading.md +18 -4
- package/package.json +1 -1
- package/templates/node/package.json +1 -1
|
@@ -15,7 +15,7 @@ it.
|
|
|
15
15
|
|
|
16
16
|
## The pools
|
|
17
17
|
|
|
18
|
-
Every non-preflight `/mcp` request takes one permit from a deployment-wide FIFO
|
|
18
|
+
Every non-preflight `/mcp` or `/mcp/<pool>` request with an admitted Origin takes one permit from a deployment-wide FIFO
|
|
19
19
|
pool. Initialization, discovery, ordinary calls, and `execute_code` all pay it.
|
|
20
20
|
A program then takes a *second* permit from the deliberately smaller code pool,
|
|
21
21
|
so one request cannot trade ordinary capacity for an unbounded number of
|
|
@@ -54,6 +54,38 @@ identity rules give its principals different connector views
|
|
|
54
54
|
([`ethos.md`](../ethos.md)), so a global queue is not pretending to supply
|
|
55
55
|
something it does not.
|
|
56
56
|
|
|
57
|
+
## Origin before admission
|
|
58
|
+
|
|
59
|
+
MCP checks `Origin` before redirects, request admission, auth, and preflight.
|
|
60
|
+
A present, disallowed origin gets HTTP 403 with the exact body
|
|
61
|
+
`{"error":"origin not allowed"}` and `Cache-Control: no-store`. This local
|
|
62
|
+
check consumes no permit or auth lookup, even when the queue is full or closed.
|
|
63
|
+
Requests without Origin, including ordinary non-browser MCP clients, pass.
|
|
64
|
+
`/health`, OAuth callbacks, and auth metadata retain their existing behavior.
|
|
65
|
+
|
|
66
|
+
`allowedOrigins` accepts a list of exact HTTP(S) origins or `"*"`. An explicit
|
|
67
|
+
list replaces the defaults, including loopback; an empty list admits only
|
|
68
|
+
originless clients. By default the configured `publicUrl` origin and HTTP(S)
|
|
69
|
+
loopback origins at any port are admitted. Loopback means `localhost`,
|
|
70
|
+
`127.0.0.0/8`, or `[::1]`. Without `publicUrl`, only loopback is admitted.
|
|
71
|
+
The inbound Host header never chooses a trusted browser origin. Invalid list
|
|
72
|
+
entries, including paths, credentials, and opaque origins, refuse construction.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
createConnecta({
|
|
76
|
+
publicUrl: "https://connecta.example",
|
|
77
|
+
allowedOrigins: ["https://connecta.example", "https://client.example"],
|
|
78
|
+
// …
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For unrestricted browser access, set `allowedOrigins: "*"` explicitly. Otherwise
|
|
83
|
+
MCP responses reflect an admitted Origin and carry `Vary: Origin`; originless
|
|
84
|
+
and refused requests have no `Access-Control-Allow-Origin`. Allowed preflight
|
|
85
|
+
returns 204 without auth or admission. The SDK validates SEP-2243 parameter
|
|
86
|
+
headers, so preflight echoes valid requested `mcp-param-*` names alongside the
|
|
87
|
+
fixed MCP header list and varies by `Access-Control-Request-Headers` too.
|
|
88
|
+
|
|
57
89
|
## Admission before auth
|
|
58
90
|
|
|
59
91
|
`/mcp` acquires its permit *before* running the auth gate. This looks backwards
|
|
@@ -86,7 +118,7 @@ CORS headers, and a stable JSON-RPC error:
|
|
|
86
118
|
"jsonrpc": "2.0",
|
|
87
119
|
"id": null,
|
|
88
120
|
"error": {
|
|
89
|
-
"code": -
|
|
121
|
+
"code": -31001,
|
|
90
122
|
"message": "Server capacity is exhausted. Retry later.",
|
|
91
123
|
"data": { "code": "server_overloaded", "retryable": true, "retryAfterMs": 1000 }
|
|
92
124
|
}
|
|
@@ -94,8 +126,10 @@ CORS headers, and a stable JSON-RPC error:
|
|
|
94
126
|
```
|
|
95
127
|
|
|
96
128
|
`Retry-After` is that hint rounded up to at least one whole second. It is
|
|
97
|
-
advice, not a reservation. Shutdown uses `-
|
|
98
|
-
is not retryable.
|
|
129
|
+
advice, not a reservation. Shutdown uses `-31002` / `server_shutting_down` and
|
|
130
|
+
is not retryable. These application codes sit outside JSON-RPC's reserved
|
|
131
|
+
range: MCP forbids new allocations in the legacy `-32000..-32019` range.
|
|
132
|
+
Code-pool overload never reaches this layer: it surfaces as
|
|
99
133
|
an ordinary MCP tool error with `executor_overloaded`, `retryable: true`, and
|
|
100
134
|
the executor's own `retryAfterMs`.
|
|
101
135
|
|
|
@@ -43,6 +43,57 @@ permissions default to none. Saving, testing, replacing, or removing a value
|
|
|
43
43
|
never returns it. The vault is read for each call, so a saved replacement takes
|
|
44
44
|
effect without restarting the deployment.
|
|
45
45
|
|
|
46
|
+
## Result storage
|
|
47
|
+
|
|
48
|
+
Direct-call result paging uses the same KV interface with a 15-minute TTL.
|
|
49
|
+
`results.maxStashBytes` defaults to 8 MiB of stored paging envelopes, including
|
|
50
|
+
base64 overhead; `results.maxStashEntries` defaults to 64. Both are
|
|
51
|
+
non-negative safe integers, and zero disables stashing. One registry accounts
|
|
52
|
+
for all subjects and reserves capacity for pending writes. A full stash keeps
|
|
53
|
+
the successful call's preview and returns a paging-unavailable notice, without
|
|
54
|
+
a result id. Expired entries are deleted on later stash attempts before their
|
|
55
|
+
capacity is reused, even when the backend only expires entries on read.
|
|
56
|
+
A deletion failure keeps the reservation. Limits apply to writes by one
|
|
57
|
+
runtime; they do not coordinate other processes or Worker isolates, or count
|
|
58
|
+
entries left by a previous runtime.
|
|
59
|
+
|
|
60
|
+
The memory store also checks up to 16 existing keys on each `set`, rotating
|
|
61
|
+
through live keys so expired entries that nobody reads are eventually removed.
|
|
62
|
+
There are no timers or background sweeps. Paging values use an ASCII base64
|
|
63
|
+
envelope so only the requested bytes need decoding after the KV read. The
|
|
64
|
+
storage adapter's format and interface stay unchanged.
|
|
65
|
+
|
|
66
|
+
## File storage
|
|
67
|
+
|
|
68
|
+
`fileStorage` is a single-process development store. It loads one snapshot and
|
|
69
|
+
rewrites the whole state synchronously on each mutation, including result
|
|
70
|
+
stashes. It acquires an exclusive `<path>.lock` before loading, so a second
|
|
71
|
+
instance or process opening the same path fails with the holder's pid instead
|
|
72
|
+
of overwriting a stale snapshot. An unref'd timer refreshes the lock's mtime
|
|
73
|
+
every 15 seconds. A heartbeat older than 60 seconds expires regardless of pid,
|
|
74
|
+
so a container restart cannot leave a reused pid holding the file forever.
|
|
75
|
+
The lock records the host/PID namespace as well as the pid. Within that same
|
|
76
|
+
namespace, a dead pid permits immediate recovery; a matching current pid is
|
|
77
|
+
live only when the in-process registry owns that lock. Other namespaces and
|
|
78
|
+
older locks without namespace metadata rely on heartbeat expiry.
|
|
79
|
+
|
|
80
|
+
Recovery uses a serialized `.lock.reclaim` guard, which also expires after
|
|
81
|
+
60 seconds if its process crashes or pauses. An incomplete lock likewise
|
|
82
|
+
becomes recoverable after 60 seconds. A holder paused long enough to lose its
|
|
83
|
+
lock fails subsequent writes with "lock was lost". Writes check ownership
|
|
84
|
+
before changing state and again before rename; reads use the loaded snapshot
|
|
85
|
+
without filesystem lock checks. This remains an advisory development store
|
|
86
|
+
on a shared local filesystem, not a distributed storage adapter.
|
|
87
|
+
|
|
88
|
+
The returned store's `close()` releases the lock and refuses further operations.
|
|
89
|
+
Process exit also releases it, including Node `listen()`'s SIGTERM/SIGINT
|
|
90
|
+
shutdown. Each write uses a unique, exclusively created temp file, and the
|
|
91
|
+
state file's JSON format is unchanged.
|
|
92
|
+
|
|
93
|
+
Expired entries are removed before each write. Large direct-call results therefore increase both
|
|
94
|
+
retained state and write cost. Use `execute_code` to reduce read-only results
|
|
95
|
+
before returning them, and choose a storage adapter suited to the deployment.
|
|
96
|
+
|
|
46
97
|
## Storage continuity
|
|
47
98
|
|
|
48
99
|
This module extraction changes no encrypted record keys, owner partitions, or
|
|
@@ -74,7 +125,9 @@ Personal connectors disappear from a request that has no stable human
|
|
|
74
125
|
principal. For a principal that can see one, connecta partitions connector
|
|
75
126
|
storage, encrypted vault records, catalog caches, OAuth generations, and
|
|
76
127
|
observed result shapes under an opaque SHA-256 identity key. Results used by
|
|
77
|
-
`get_result` are partitioned by the authenticated subject
|
|
128
|
+
`get_result` are partitioned by the authenticated subject independently of
|
|
129
|
+
activity configuration. Open deployments and providers that supply no identity
|
|
130
|
+
share one results partition. See [the partition and paging contract](./meta-tools.md#result-representation).
|
|
78
131
|
|
|
79
132
|
Literal `auth: { type: "headers" }` cannot be personal because its secret lives
|
|
80
133
|
in deployment code. `remoteMcp()` refuses that combination at construction.
|
|
@@ -117,7 +117,7 @@ exist so far:
|
|
|
117
117
|
| --- | --- | --- |
|
|
118
118
|
| **pre-template** | before 0.10.2 | no `connecta init` existed; hand-written, or copied from the retired `examples/node` |
|
|
119
119
|
| **A** | 0.10.2 – 0.15.1 | `.env.example`, `.gitignore`, `AGENTS.md`, `CLAUDE.md`, `README.md`, `package.json`, `src/index.ts`, `tsconfig.json` |
|
|
120
|
-
| **B** | 0.16.0 – 0.24.
|
|
120
|
+
| **B** | 0.16.0 – 0.24.3 | adds `.dockerignore`, `Dockerfile`, `docker-compose.yml`, and `src/file-activity.ts`; `src/index.ts` grows the four commented operator blocks; `.env.example` ships `CONNECTA_TOKEN=` empty |
|
|
121
121
|
|
|
122
122
|
Generation A is a decade in template years and identifying it precisely does
|
|
123
123
|
not matter, because you are about to reconstruct it exactly rather than guess
|
|
@@ -190,7 +190,7 @@ Generate the *current* template beside the base you already made, into the same
|
|
|
190
190
|
`$SCRATCH`:
|
|
191
191
|
|
|
192
192
|
```sh
|
|
193
|
-
(cd "$SCRATCH" && npx @zackbart/connecta@0.24.
|
|
193
|
+
(cd "$SCRATCH" && npx @zackbart/connecta@0.24.3 init current)
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
You now have a three-way merge with a real base: `$SCRATCH/base` is what this
|
|
@@ -246,7 +246,7 @@ A deployment older than 0.10.2 has no base to diff against. Do not try to
|
|
|
246
246
|
manufacture one. Instead:
|
|
247
247
|
|
|
248
248
|
1. `SCRATCH=$(mktemp -d)`, then
|
|
249
|
-
`(cd "$SCRATCH" && npx @zackbart/connecta@0.24.
|
|
249
|
+
`(cd "$SCRATCH" && npx @zackbart/connecta@0.24.3 init current)` — there is no
|
|
250
250
|
`base` leg here, only the current template to read from.
|
|
251
251
|
2. Copy `$SCRATCH/current` into the deployment file by file, **skipping
|
|
252
252
|
`src/index.ts`**.
|
|
@@ -267,7 +267,21 @@ first, so cross them bottom-up: start at the oldest one still above this
|
|
|
267
267
|
deployment's pin and work back up the page, because each boundary assumes the
|
|
268
268
|
older ones are already done.
|
|
269
269
|
|
|
270
|
-
### 0.23.0 → 0.24.
|
|
270
|
+
### 0.23.0 → 0.24.3
|
|
271
|
+
|
|
272
|
+
0.24.3 validates the browser `Origin` header on `/mcp`. A browser MCP client
|
|
273
|
+
hosted on an origin other than `publicUrl` or loopback now gets a 403 until
|
|
274
|
+
the deployment lists it in `allowedOrigins` (or sets `"*"` to keep the old
|
|
275
|
+
open CORS); clients that send no `Origin`, which is every server-side and CLI
|
|
276
|
+
client, are unaffected. The overload and shutdown JSON-RPC error codes moved
|
|
277
|
+
from `-32001`/`-32002` to `-31001`/`-31002`; the `data.code` strings are
|
|
278
|
+
unchanged. See [request admission](./request-admission.md#origin-before-admission).
|
|
279
|
+
`fileStorage` now holds an exclusive lock on its state file: a second process
|
|
280
|
+
opening the same file fails at construction, so a deployment that shared one
|
|
281
|
+
file between two processes must give each its own. The returned store gained
|
|
282
|
+
`close()`. `get_result` stashes are bounded per runtime by the new `results`
|
|
283
|
+
config (8 MiB and 64 entries by default) and partitioned by any authenticated
|
|
284
|
+
subject, not only by providers that declare an activity namespace.
|
|
271
285
|
|
|
272
286
|
Use the [optional-module migration](./optional-modules-upgrade.md) to select
|
|
273
287
|
modules, grant auth-management permissions, and migrate issued-token clients.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zackbart/connecta",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of seven meta-tools.",
|