@astral-mcp/projection 0.8.0 → 0.9.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/README.md +58 -7
- package/dist/index.d.mts +1430 -426
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +8558 -2134
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -13,24 +13,45 @@ Both SDK packages are pinned at `2.0.0` in Projection's own dependencies, so
|
|
|
13
13
|
install the same version to match the pin.
|
|
14
14
|
|
|
15
15
|
Projection wraps an existing MCP server and exposes two projections of it —
|
|
16
|
-
five tools in all, never the wrapped server's own:
|
|
16
|
+
five tools in all, never a pass-through to the wrapped server's own:
|
|
17
17
|
|
|
18
18
|
- **Code mode** — `execute_machine` runs an LLM-authored XState v5 state
|
|
19
|
-
machine in a QuickJS WebAssembly sandbox (read-only calls
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
machine in a QuickJS WebAssembly sandbox (read-only calls, or, under
|
|
20
|
+
`destructive-only`, a non-destructive write, run inline; any other write
|
|
21
|
+
suspends the machine into a sealed, encrypted blob and asks the human to
|
|
22
|
+
approve that exact call via MRTR elicitation); `discover_apis` returns
|
|
22
23
|
the generated TypeScript definitions and the authoring contract.
|
|
23
24
|
- **Catalog** — `search_tools` lists the wrapped tools with their approval
|
|
24
|
-
posture, `describe_tool` returns one tool's schemas (and its generated
|
|
25
|
+
posture (a query's top three hits with their arguments), `describe_tool` returns one tool's schemas (and its generated
|
|
25
26
|
types on request, `types: true`), and `call_tool` runs one tool by name
|
|
26
27
|
under the same approval gate. The instructions route the model: one step
|
|
27
28
|
→ `call_tool`; a workflow → `execute_machine`. Either projection can be
|
|
28
29
|
disabled (`projections: { codeMode: false }` or `{ catalog: false }`; not
|
|
29
30
|
both).
|
|
31
|
+
- **Direct listing** (default `projections.direct: 'auto'`) — when the
|
|
32
|
+
wrapped tools' estimated definitions fit `directBudgetTokens` (8 000),
|
|
33
|
+
each is also listed by name (`<box>__<tool>` on a multi-server facade) as
|
|
34
|
+
a mediated alias of `call_tool` under the same gate, and `search_tools`
|
|
35
|
+
is dropped. `projections: { direct: false }` keeps the five tools.
|
|
30
36
|
|
|
31
37
|
Every tool surface uses one posture vocabulary: a tool `runs immediately`
|
|
32
|
-
(read-only,
|
|
33
|
-
policy`.
|
|
38
|
+
(read-only, `auto` by policy, or, under `destructive-only`, a non-destructive
|
|
39
|
+
write), `asks for approval`, or is `blocked by policy`.
|
|
40
|
+
|
|
41
|
+
`approvals` sets the approvals mode: `'every-write'` (default) asks before
|
|
42
|
+
every tool that is not read-only; `'destructive-only'` also runs, without
|
|
43
|
+
approval, a tool whose annotations declare `destructiveHint: false` exactly
|
|
44
|
+
(a missing or any other hint still asks). A per-tool `policy` override wins
|
|
45
|
+
over the mode. The mode extends the trust Projection places in
|
|
46
|
+
`readOnlyHint` to `destructiveHint: false`, and a hostile server can
|
|
47
|
+
mislabel a damaging tool — enable it only for servers you trust.
|
|
48
|
+
|
|
49
|
+
`upstreamElicitation: 'forward'` lets a trusted wrapped server ask the user
|
|
50
|
+
questions mid-call (a form, or a link to open): each is relayed over MRTR as
|
|
51
|
+
`Question from <box> (not an approval)` with the server's words quoted and
|
|
52
|
+
its form allowlisted, after the call itself passed the approval gate. The
|
|
53
|
+
default, `'refuse'`, fails such a call with a result telling the model the
|
|
54
|
+
tool needs the user's input. Per box on `wrapMcpServers`.
|
|
34
55
|
|
|
35
56
|
All suspension state rides the MRTR `requestState` payload, so Projection is
|
|
36
57
|
fully stateless: any server instance can resume any workflow.
|
|
@@ -44,6 +65,8 @@ import { wrapMcpServer } from '@astral-mcp/projection';
|
|
|
44
65
|
const facade = await wrapMcpServer(myMcpServer, {
|
|
45
66
|
blob: { key: myThirtyTwoByteKey }, // shared across instances
|
|
46
67
|
policy: { query: 'ask' }, // override annotations per tool
|
|
68
|
+
// approvals: 'destructive-only', // skip approval for destructiveHint: false writes (trusted servers only)
|
|
69
|
+
// upstreamElicitation: 'forward', // pass the server's own questions on to the user (trusted servers only)
|
|
47
70
|
redact: (tool, args) => ({ ...args, token: '[redacted]' }),
|
|
48
71
|
});
|
|
49
72
|
|
|
@@ -56,6 +79,31 @@ still run read-only machines and calls, and get a structured error the
|
|
|
56
79
|
moment a call needs approval. You can also wrap a connected SDK `Client` to put
|
|
57
80
|
Projection in front of a server you don't own.
|
|
58
81
|
|
|
82
|
+
### Several servers (preview)
|
|
83
|
+
|
|
84
|
+
`wrapMcpServers({ fs: filesServer, github: { source: githubClient } }, options)`
|
|
85
|
+
fronts several servers as tool boxes: tools are addressed as
|
|
86
|
+
`<alias>.<name>` (`fs.write_file`), policy keys are qualified names, each
|
|
87
|
+
box has its own `search_<alias>` tool (whose first page shows the server's
|
|
88
|
+
guidance), and `search_tools` searches every box (`catalog.globalSearch:
|
|
89
|
+
false` turns it off). Code mode works across boxes: the generated types have
|
|
90
|
+
one `tools.<alias>` property per box, and machines call
|
|
91
|
+
`toolCall('fs.read_file', args)`. Optional sources are not available yet.
|
|
92
|
+
See `docs/server-authors.md`.
|
|
93
|
+
|
|
94
|
+
### Remote servers by URL (preview)
|
|
95
|
+
|
|
96
|
+
Any source can be `{ url, oauth?: { clientId, scopes } }`: Projection builds
|
|
97
|
+
the Streamable HTTP connection itself, uses and refreshes tokens from a
|
|
98
|
+
token store (the OS credential store through the optional
|
|
99
|
+
`@napi-rs/keyring` dependency by default, or `WrapOptions.tokenStore`, a
|
|
100
|
+
plain string key-value store), and `facade.signOut()` clears them. Never put
|
|
101
|
+
a token in an MCP configuration file. Just-in-time sign-in (asking the user
|
|
102
|
+
on first use) is not supported in this version: a public server works, and
|
|
103
|
+
so does one whose token is already in your `tokenStore`; otherwise the wrap
|
|
104
|
+
fails with an `auth` error. See "Upstream authentication" in
|
|
105
|
+
`docs/server-authors.md`.
|
|
106
|
+
|
|
59
107
|
## Security model
|
|
60
108
|
|
|
61
109
|
| Invariant | How it holds |
|
|
@@ -71,6 +119,9 @@ Projection in front of a server you don't own.
|
|
|
71
119
|
Known limitation: unless you supply a shared `replayCache` (R38), an approved
|
|
72
120
|
retry can be replayed until its blob expires (by default 5 min for
|
|
73
121
|
`call_tool`, 15 for machines; `blob.callTtlMs` and `blob.ttlMs` override).
|
|
122
|
+
A forwarded server question's blob keeps the expiry of the approval it
|
|
123
|
+
follows. With `upstreamElicitation: 'forward'` on, a `replayCache` is
|
|
124
|
+
recommended.
|
|
74
125
|
See
|
|
75
126
|
[Lifecycle](https://github.com/astralMCP/projection/blob/main/docs/server-authors.md#lifecycle-and-operations).
|
|
76
127
|
|