@kya-os/mcp 1.17.0 → 1.20.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 +398 -0
- package/CONFORMANCE.md +18 -6
- package/README.md +59 -0
- package/SPEC.md +57 -4
- package/dist/card/cimd.js +6 -2
- package/dist/card/cimd.js.map +1 -1
- package/dist/card/delegation.d.ts +12 -3
- package/dist/card/delegation.js +13 -7
- package/dist/card/delegation.js.map +1 -1
- package/dist/card/index.d.ts +1 -0
- package/dist/card/index.js +1 -0
- package/dist/card/index.js.map +1 -1
- package/dist/card/revocation.d.ts +15 -4
- package/dist/card/revocation.js +9 -4
- package/dist/card/revocation.js.map +1 -1
- package/dist/card/status-list-proof.d.ts +32 -0
- package/dist/card/status-list-proof.js +102 -0
- package/dist/card/status-list-proof.js.map +1 -0
- package/dist/delegation/chain-enforcement.d.ts +16 -0
- package/dist/delegation/chain-enforcement.js +17 -0
- package/dist/delegation/chain-enforcement.js.map +1 -1
- package/dist/delegation/verification-method-key.d.ts +4 -1
- package/dist/delegation/verification-method-key.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/index.d.ts +3 -1
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/kya-os-transport.d.ts +43 -12
- package/dist/middleware/kya-os-transport.js +151 -28
- package/dist/middleware/kya-os-transport.js.map +1 -1
- package/dist/middleware/mcp-tool-callback.d.ts +48 -0
- package/dist/middleware/mcp-tool-callback.js +38 -0
- package/dist/middleware/mcp-tool-callback.js.map +1 -0
- package/dist/middleware/serve-in-process.d.ts +41 -0
- package/dist/middleware/serve-in-process.js +86 -0
- package/dist/middleware/serve-in-process.js.map +1 -0
- package/dist/middleware/with-kya-os-server.d.ts +20 -3
- package/dist/middleware/with-kya-os-server.js +16 -1
- package/dist/middleware/with-kya-os-server.js.map +1 -1
- package/dist/middleware/with-kya-os.config-types.d.ts +34 -0
- package/dist/middleware/with-kya-os.d.ts +4 -0
- package/dist/middleware/with-kya-os.delegation-gate.d.ts +2 -1
- package/dist/middleware/with-kya-os.delegation-gate.js +12 -18
- package/dist/middleware/with-kya-os.delegation-gate.js.map +1 -1
- package/dist/middleware/with-kya-os.delegation-verify.d.ts +7 -12
- package/dist/middleware/with-kya-os.delegation-verify.js +44 -9
- package/dist/middleware/with-kya-os.delegation-verify.js.map +1 -1
- package/dist/middleware/with-kya-os.deps.d.ts +15 -4
- package/dist/middleware/with-kya-os.helpers.d.ts +5 -0
- package/dist/middleware/with-kya-os.helpers.js +13 -0
- package/dist/middleware/with-kya-os.helpers.js.map +1 -1
- package/dist/middleware/with-kya-os.js +23 -0
- package/dist/middleware/with-kya-os.js.map +1 -1
- package/dist/middleware/with-kya-os.outcome.d.ts +18 -0
- package/dist/middleware/with-kya-os.outcome.js +36 -0
- package/dist/middleware/with-kya-os.outcome.js.map +1 -0
- package/dist/middleware/with-kya-os.policy-gate.d.ts +2 -1
- package/dist/middleware/with-kya-os.policy-gate.js +2 -1
- package/dist/middleware/with-kya-os.policy-gate.js.map +1 -1
- package/dist/middleware/with-kya-os.session.d.ts +76 -10
- package/dist/middleware/with-kya-os.session.js +739 -164
- package/dist/middleware/with-kya-os.session.js.map +1 -1
- package/dist/middleware/with-kya-os.types.d.ts +102 -8
- package/dist/proof/generator.d.ts +13 -0
- package/dist/proof/generator.js +15 -0
- package/dist/proof/generator.js.map +1 -1
- package/dist/proof/verifier.js +14 -9
- package/dist/proof/verifier.js.map +1 -1
- package/package.json +18 -14
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,404 @@ Versioning: https://semver.org/spec/v2.0.0.html
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.20.0] - 2026-10-13
|
|
11
|
+
|
|
12
|
+
1.19.0 was tagged but never published to npm; its changes (listed under
|
|
13
|
+
1.19.0 below) ship to npm for the first time in this release.
|
|
14
|
+
|
|
15
|
+
### Upgrading — BREAKING without an open KYA-OS transport
|
|
16
|
+
|
|
17
|
+
- **Every response proof is now made where the KYA-OS transport sends the
|
|
18
|
+
result, under the default body profile as under the envelope profile, and
|
|
19
|
+
the wrappers refuse to run without one.** While no KYA-OS transport is open
|
|
20
|
+
for the middleware, `wrapWithProof`, `wrapWithDelegation`, `withPolicyGate`
|
|
21
|
+
and `proveOutcome` throw on entry, before they record or run anything, with
|
|
22
|
+
an error that names `withKyaOs` and `createKyaOsTransport`. A transport
|
|
23
|
+
counts as open from `start()` until it closes, by `close()` or by its own
|
|
24
|
+
`onclose`. Once a KYA-OS transport has been created for a middleware, every
|
|
25
|
+
wrapper result must go out through one. Before, the proofs were signed in
|
|
26
|
+
the handler, and they did not verify on 2026-07-28 (envelope) or after an
|
|
27
|
+
SDK reshaped `content` (body). Find your setup:
|
|
28
|
+
- **`withKyaOs(server, …)` before `server.connect()`, `proofAllTools` left
|
|
29
|
+
on:** nothing to change.
|
|
30
|
+
- **The low-level `Server` API with `createKyaOsMiddleware`:** connect
|
|
31
|
+
through the KYA-OS transport, as this repository's examples now do:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
await server.connect(createKyaOsTransport(transport, kyaos)); // was server.connect(transport)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The transport proves and audits every tool's result, as `withKyaOs`
|
|
38
|
+
does, including tools you registered without a wrapper, which went out
|
|
39
|
+
unproven on 1.19.0. List any to leave unproven in its third argument,
|
|
40
|
+
which adds to the always-excluded `_kyaos` and `_kyaos_handshake`.
|
|
41
|
+
- **A script, walkthrough or test that calls a wrapped handler directly:**
|
|
42
|
+
serve it in process, and read the result a client would receive:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const local = serveInProcess(kyaos);
|
|
46
|
+
const checkout = local.tool('checkout', kyaos.wrapWithDelegation('checkout', consent, handler));
|
|
47
|
+
const result = await checkout({ item: 'laptop' }); // proved
|
|
48
|
+
await local.close();
|
|
49
|
+
```
|
|
50
|
+
- **`withKyaOs` with `proofAllTools: false`:** it wraps no transport, so
|
|
51
|
+
the wrappers refuse. Leave it on and list the tools to leave unproven in
|
|
52
|
+
`excludeTools`.
|
|
53
|
+
- **`withKyaOs` called after `connect()`:** the connected transport is not
|
|
54
|
+
wrapped; call `withKyaOs` first.
|
|
55
|
+
- **Code that reads the proof inside the handler:** the result
|
|
56
|
+
`proveOutcome` or a wrapper returns no longer carries one, under either
|
|
57
|
+
profile. Return it from the handler; the transport adds the proof.
|
|
58
|
+
- **Results delivered later, such as MCP task results:** a result is
|
|
59
|
+
proved the first time the same middleware sends it, within
|
|
60
|
+
`pendingProofTtlMs` (default five minutes, at most 2147483647) of the
|
|
61
|
+
handler returning. Raise it for task results fetched later; a later or
|
|
62
|
+
repeated send goes out with `proofError` (see below).
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
|
|
66
|
+
- **The Entity Card revocation checker can verify a status list's proof.**
|
|
67
|
+
`createRevocationChecker` fetched a Bitstring status list and read its bits
|
|
68
|
+
without checking the list's proof, which Bitstring Status List v1.0 requires
|
|
69
|
+
(§3.2, step 4). Whoever could serve or alter the list could clear a
|
|
70
|
+
revocation. It now takes an optional `verifyStatusList`: a list it rejects,
|
|
71
|
+
by returning anything but `true` or by throwing, resolves to revoked and not
|
|
72
|
+
fresh, like any other unreadable list. `createStatusListProofVerifier({
|
|
73
|
+
didResolver })` is a ready verifier for `DataIntegrityProof` /
|
|
74
|
+
`eddsa-jcs-2022` lists. It finds the key that `proof.verificationMethod`
|
|
75
|
+
names in the issuer's DID document, requires that method to belong to the
|
|
76
|
+
list's `issuer`, and accepts relative method ids and `publicKeyJwk` or
|
|
77
|
+
`publicKeyMultibase` keys. It verifies a status list published by
|
|
78
|
+
builders.kya-os.org, signed by an independent implementation. Without
|
|
79
|
+
`verifyStatusList` the checker behaves as before. The option becomes
|
|
80
|
+
required in 2.0, and SPEC.md §6.10 states the requirement.
|
|
81
|
+
- **`createKyaOsTransport` is exported from `@kya-os/mcp`,** next to
|
|
82
|
+
`createKyaOsMiddleware`, for servers on the low-level `Server` API, which
|
|
83
|
+
now connect through it. It was only on `@kya-os/mcp/middleware`. Its
|
|
84
|
+
`Transport` type declares the callbacks as methods, so an SDK 1 or SDK 2
|
|
85
|
+
transport is accepted, and the wrapper connects to either SDK's server,
|
|
86
|
+
without a cast.
|
|
87
|
+
- **`serveInProcess(kyaos)` calls wrapped handlers outside an MCP server.**
|
|
88
|
+
`local.tool(name, handler)` answers as a server would, through a KYA-OS
|
|
89
|
+
transport, so the result carries its proof; `local.close()` closes the
|
|
90
|
+
transport. It replaces the examples' own helper.
|
|
91
|
+
- **`pendingProofTtlMs`** on `KyaOsConfig` and `WithKyaOsOptions`: how long a
|
|
92
|
+
wrapper's result may wait to be sent before its proof is given up (default
|
|
93
|
+
300000, five minutes; at most 2147483647, about 24.8 days, the longest timer
|
|
94
|
+
a JavaScript runtime keeps). Memory stays bounded by the caps on waiting
|
|
95
|
+
proofs.
|
|
96
|
+
|
|
97
|
+
### Changed
|
|
98
|
+
|
|
99
|
+
- **Envelope-profile proofs are made at the transport, over the result as it
|
|
100
|
+
is sent.** On the 2026-07-28 revision, MCP SDK 2 adds `resultType:
|
|
101
|
+
'complete'` to a tool result after the handler returns it. An
|
|
102
|
+
envelope-profile proof made in the handler, by `proveOutcome`,
|
|
103
|
+
`wrapWithProof` or either gate, did not cover that member, so the client's
|
|
104
|
+
check of the result it received failed. Under the envelope profile these
|
|
105
|
+
paths now leave the proof to the `withKyaOs` transport, or one built with
|
|
106
|
+
`createKyaOsTransport`, which makes it in `send()`, after the SDK has
|
|
107
|
+
finished the result. The proof covers exactly the result the client
|
|
108
|
+
receives, on 2025-11-25 and on 2026-07-28, with SDK 1 or SDK 2. This
|
|
109
|
+
supersedes the 1.19.0 advice to set `resultType: 'complete'` on a result
|
|
110
|
+
before proving it under the envelope profile: that is no longer needed, and
|
|
111
|
+
a result that sets it is proved as sent.
|
|
112
|
+
- What a proof claims stays in the middleware. The result carries only a
|
|
113
|
+
private one-time id, redeemed only on a result that also carries the
|
|
114
|
+
middleware's stamp, and the session is read again when the proof is made,
|
|
115
|
+
which takes its audience from it. A result naming no waiting proof, or
|
|
116
|
+
one whose session is gone by then, goes out without a proof and with
|
|
117
|
+
`_meta.proofError`, as does an outcome whose proof fails at send.
|
|
118
|
+
- A waiting proof holds the request's hash, computed when the call was
|
|
119
|
+
recorded, never the arguments, so a waiting proof is small however large
|
|
120
|
+
they were, and later changes to the arguments cannot reach what is
|
|
121
|
+
signed. Arguments that cannot be hashed fail the proof in the handler
|
|
122
|
+
(`proofError`, `proof.rejected` with `PROOF_GENERATION_FAILED`).
|
|
123
|
+
- The claims, `_meta` keys and audit events are the ones the handler-side
|
|
124
|
+
proof had. For a result that is sent, the audit events are recorded in
|
|
125
|
+
the same order as before: an allowed call's `tool.call.started` in the
|
|
126
|
+
handler, then `proof.generated` and `tool.call.completed` at send.
|
|
127
|
+
- A result the SDK never sends (a cancelled call, or an SDK 1 result
|
|
128
|
+
replaced after an `outputSchema` check) is closed in the audit trail with
|
|
129
|
+
`proof.rejected` under the new code `PROOF_NOT_SENT`. An allowed call
|
|
130
|
+
then records `tool.call.failed` with the reason `RESULT_NOT_SENT`, never
|
|
131
|
+
`tool.call.completed`, since its client did not get the result. An
|
|
132
|
+
outcome's terminal events were recorded in the handler. This happens five
|
|
133
|
+
minutes after the handler returned; when the last KYA-OS transport for
|
|
134
|
+
the middleware closes; or, oldest first, when 10,000 proofs are waiting
|
|
135
|
+
or they hold about 16 MB of audit context.
|
|
136
|
+
- **Body-profile proofs are made at the transport too, so neither profile
|
|
137
|
+
signs in the handler.** The MCP SDK also changes `content` after the
|
|
138
|
+
handler returns. SDK 2, on both revisions, appends a text item when
|
|
139
|
+
`structuredContent` is not an object and `content` has none. Both SDKs fill
|
|
140
|
+
in `content: []` when a handler leaves it out. A body-profile proof made in
|
|
141
|
+
the handler did not cover the appended item, so it failed verification, and
|
|
142
|
+
over a missing `content` it could not be made at all. The body profile now
|
|
143
|
+
takes the same path as the envelope profile above: the claims stay in the
|
|
144
|
+
middleware by request hash, and the transport makes the proof in `send()`
|
|
145
|
+
over the result as sent, with the same claims, `_meta` keys and audit
|
|
146
|
+
events, recorded in the same order. `_meta` keeps its place among the
|
|
147
|
+
result's members. Where the SDK does not reshape the result, the output is
|
|
148
|
+
byte-identical to 1.19.0. An outcome proof that fails now also says so in
|
|
149
|
+
`_meta.proofError`, as an allowed call's already did.
|
|
150
|
+
- **A result is proved once, within the window.** A stamped result whose
|
|
151
|
+
pending proof is no longer waiting, because its window passed, it was
|
|
152
|
+
evicted, or it was already sent (a task result fetched again), goes out
|
|
153
|
+
with `proofError`, and the audit trail records `proof.rejected` with the
|
|
154
|
+
new code `PROOF_EXPIRED`, with the call's audit context when it is still
|
|
155
|
+
known. That record of closed proofs is bounded too: 10,000 entries and
|
|
156
|
+
about 4 MB, oldest first, holding no context over 16 KB.
|
|
157
|
+
- **`createKyaOsTransport`'s `exclude` adds to the protocol tools.** The
|
|
158
|
+
third argument replaced the default `["_kyaos", "_kyaos_handshake"]`, so a
|
|
159
|
+
caller passing its own list had the protocol tools' results proved. It now
|
|
160
|
+
adds to them, as `excludeTools` does in `withKyaOs`; the protocol tools
|
|
161
|
+
are always excluded.
|
|
162
|
+
|
|
163
|
+
### Security
|
|
164
|
+
|
|
165
|
+
- **SDK 2's per-tool OAuth scope challenges were bypassed under `withKyaOs`
|
|
166
|
+
in earlier versions.** SDK 2's `McpServer.connect()` installs
|
|
167
|
+
the `scopeChallenge` enforcement of `registerTool` only on a transport
|
|
168
|
+
that has `setScopeChallengeResolver`, and the KYA-OS transport did not
|
|
169
|
+
pass that member through. A tool registered with, for example,
|
|
170
|
+
`scopeChallenge: requireScopes('payments:write')` therefore ran, and was
|
|
171
|
+
even proved, for a token without that scope. The caller should have
|
|
172
|
+
received a 403. This affected 1.19.0 and earlier:
|
|
173
|
+
- `withKyaOs` with a server's own `WebStandardStreamableHTTPServerTransport`;
|
|
174
|
+
- `createMcpHandler` serving a client that negotiates 2025-11-25.
|
|
175
|
+
|
|
176
|
+
At 2026-07-28, `createMcpHandler` enforces the challenge at its entry, before
|
|
177
|
+
the transport. The transport now passes the resolver through, and every
|
|
178
|
+
other transport member either SDK feature-detects or reads is passed
|
|
179
|
+
through too.
|
|
180
|
+
- **The private members could leave in a message that is not a result, and
|
|
181
|
+
a copied stamp and id could make the transport sign chosen content.** The
|
|
182
|
+
transport removed the lifecycle stamp and pending-proof id only from
|
|
183
|
+
`result`. A tool that logged a wrapped result in a notification therefore
|
|
184
|
+
sent both to the client. A tool that relays caller-controlled `_meta` into
|
|
185
|
+
its own result could then redeem them, and the client got a server-signed
|
|
186
|
+
proof over content of its choosing, verifying as the wrapped call's
|
|
187
|
+
response. With envelope-profile proofs at the transport this was possible
|
|
188
|
+
before this release; with the body profile there too, in this release
|
|
189
|
+
before the fix. Now:
|
|
190
|
+
- every outgoing message is cleaned at any depth: its `params`,
|
|
191
|
+
`result` and `error.data`;
|
|
192
|
+
- a waiting proof whose id is found anywhere other than its own result is
|
|
193
|
+
closed, so nothing can redeem it;
|
|
194
|
+
- on the answer to a `tools/call` the transport tracked, a pending proof
|
|
195
|
+
left for a different tool is not redeemed. The answer goes out unproven,
|
|
196
|
+
and the audit trail records `proof.rejected` with the new code
|
|
197
|
+
`PROOF_TOOL_MISMATCH`.
|
|
198
|
+
|
|
199
|
+
### Fixed
|
|
200
|
+
|
|
201
|
+
- **The proof wrappers no longer change the result a handler returns.**
|
|
202
|
+
`wrapWithProof` set its `_meta` members (the pending-proof id, the
|
|
203
|
+
lifecycle stamp, `proofError`, the audit marker) on the handler's own
|
|
204
|
+
object, and its audit-degraded path set `isError: true` on it. A handler
|
|
205
|
+
that returned one object from every call, a module-level constant or a
|
|
206
|
+
cached result, found those members on it, and a later call could go out
|
|
207
|
+
with an earlier call's `isError` and audit marker; a frozen result made the
|
|
208
|
+
wrapper throw. Every member now goes on a shallow copy of the result with a
|
|
209
|
+
new `_meta`, in `wrapWithProof` and in the outcome path behind the gates
|
|
210
|
+
and `proveOutcome`, and the handler's object and its `_meta` are never
|
|
211
|
+
changed. What the client receives is otherwise the same. Closes #249.
|
|
212
|
+
|
|
213
|
+
- **The KYA-OS transport passes the SDK's request context through.** It
|
|
214
|
+
dropped the second argument of `onmessage` and of `send`, and did not
|
|
215
|
+
expose the inner transport's `sessionId`, `setProtocolVersion`,
|
|
216
|
+
`setSupportedProtocolVersions` or `hasPerRequestStream`. Under `withKyaOs`,
|
|
217
|
+
tools lost the request's `sessionId`, `authInfo` and `requestInfo` (SDK 1)
|
|
218
|
+
and `ctx.http.authInfo` (SDK 2, both revisions); SDK 1 task stores stopped
|
|
219
|
+
separating tasks by session; and notifications a tool sent during a call
|
|
220
|
+
lost their `relatedRequestId`, so on Streamable HTTP they missed the
|
|
221
|
+
request's stream. All of these now pass through unchanged. Closes #248.
|
|
222
|
+
- **The proof's scope is the call's, as the wrapper received it.**
|
|
223
|
+
`wrapWithProof` read `context.scopeId` and the audit context after the
|
|
224
|
+
handler ran, so a handler could change the scope a proof claims by changing
|
|
225
|
+
the context object it was handed. They are now read before the handler
|
|
226
|
+
runs.
|
|
227
|
+
- **Private members never reach the wire from a nested result.** A wrapped
|
|
228
|
+
result a handler placed inside its own result, in `structuredContent` for
|
|
229
|
+
example, carried the lifecycle stamp and the pending-proof id to the
|
|
230
|
+
client. They are now removed at any depth: the walk is iterative, with no
|
|
231
|
+
depth limit. A wrapped result serialized into a string, a text item for
|
|
232
|
+
example, cannot be cleaned that way. Such a result is sent without a
|
|
233
|
+
proof and with `proofError`, recorded as `proof.rejected` with the new
|
|
234
|
+
code `PRIVATE_MEMBER_IN_RESULT`. Every waiting proof whose id appears in
|
|
235
|
+
that string is closed, so it cannot be redeemed.
|
|
236
|
+
|
|
237
|
+
## [1.19.0] - 2026-10-09
|
|
238
|
+
|
|
239
|
+
### Added
|
|
240
|
+
|
|
241
|
+
- **`proveOutcome` proves the authorization outcomes application code
|
|
242
|
+
decides.** A server that enforces OAuth scopes in its tool handlers
|
|
243
|
+
returns its own challenges and denials as tool results, and the
|
|
244
|
+
`withKyaOs` transport sent such an error result unproven. The middleware
|
|
245
|
+
now has `proveOutcome({ toolName, args, outcome, reason, result,
|
|
246
|
+
sessionId? })`, which returns a copy of `result` carrying the proof the
|
|
247
|
+
gates attach to their outcomes: the same claims, the same `_meta` keys,
|
|
248
|
+
the configured response-proof profile and the same audit events. It runs
|
|
249
|
+
the gates' own code, and the transport passes its result through as
|
|
250
|
+
proven. A `needs_authorization` proof binds the result and a denial or
|
|
251
|
+
step-up proof binds none, as SPEC.md §7.4 has it. The `_kyaos*` control
|
|
252
|
+
arguments are left out of the signed request, `_meta` members only the
|
|
253
|
+
middleware may set are dropped, and no session needs to be opened. Under
|
|
254
|
+
the envelope profile, set `resultType: 'complete'` on the result first: on
|
|
255
|
+
the 2026-07-28 revision the MCP SDK adds it after the handler returns,
|
|
256
|
+
outside a proof made there. It is
|
|
257
|
+
optional on `KyaOsMiddleware`, as `withPolicyGate` is, so structural
|
|
258
|
+
implementers still type-check; the new `KyaOsOutcomeProver` role requires
|
|
259
|
+
it. New types: `KyaOsOutcomeProofRequest`, `KyaOsAuthorizationOutcome`,
|
|
260
|
+
`KyaOsToolResult`.
|
|
261
|
+
- **`formatChallenge` may return the whole challenge result.** Besides the
|
|
262
|
+
content array, the delegation gate's hook may return `{ content,
|
|
263
|
+
structuredContent?, isError?, _meta? }` (`KyaOsChallengeResult`; the hook
|
|
264
|
+
type is `KyaOsChallengeFormatter`). The gate sets all of it before it
|
|
265
|
+
signs the challenge, so the envelope profile covers `isError` and
|
|
266
|
+
`structuredContent`; the body profile still covers `content` only.
|
|
267
|
+
`_meta` members the middleware owns are dropped from what the hook
|
|
268
|
+
returns. The array form and the default challenge are unchanged; the
|
|
269
|
+
default challenge still carries no `isError`, unlike the policy gate's
|
|
270
|
+
step-up.
|
|
271
|
+
- **`toMcpToolCallback` registers a wrapped handler with `registerTool`
|
|
272
|
+
without a cast.** Passed straight to `McpServer.registerTool`, a
|
|
273
|
+
`KyaOsToolHandler` needed `as never`, and the SDK's request context then
|
|
274
|
+
arrived as the KYA-OS session id, so `wrapWithProof` found no session and
|
|
275
|
+
sent the result unproven. The adapter passes the arguments alone. Types:
|
|
276
|
+
`McpToolCallback`, `McpToolCallbackResult`.
|
|
277
|
+
|
|
278
|
+
### Changed
|
|
279
|
+
|
|
280
|
+
- **A `formatChallenge` hook that returns neither form gets the default
|
|
281
|
+
challenge**, as a throwing hook does. Before, the value was emitted as
|
|
282
|
+
`content`, which `McpServer` then rejected as an invalid result.
|
|
283
|
+
- **`wrapWithProof` keeps an outcome proof it is handed.** A result
|
|
284
|
+
produced by the outcome-proof path, recognized by identity, is returned
|
|
285
|
+
as it is. Before, a challenge that was not an error result was signed
|
|
286
|
+
again there as an allowed call. No gate composition in this repository
|
|
287
|
+
reached that path.
|
|
288
|
+
|
|
289
|
+
### Fixed
|
|
290
|
+
|
|
291
|
+
- **An allow result the session fallback cannot attribute says so.** A
|
|
292
|
+
call that threads no session, which is every call on the `withKyaOs`
|
|
293
|
+
transport path, is proved under the single established session. With
|
|
294
|
+
more than one session live the middleware refuses to pick one and the
|
|
295
|
+
result went out unproven, with only a log line; opening a session in the
|
|
296
|
+
middleware's `SessionManager` from application code did this to every
|
|
297
|
+
later allow result. Such a result now carries `_meta.proofError`, the
|
|
298
|
+
marker a failed proof generation already sets. `isError`, the content and
|
|
299
|
+
the audit events are unchanged, and a server with no session at all
|
|
300
|
+
emits what it did before.
|
|
301
|
+
- **Three DID-document readers accept a key published as
|
|
302
|
+
`publicKeyMultibase`.** `ProofVerifier.fetchPublicKeyFromDID`, the Entity
|
|
303
|
+
Card `didKeyedJwks` projection, and the conformance adapter's DID-resolution
|
|
304
|
+
check read only `publicKeyJwk`. A verification method that published its
|
|
305
|
+
Ed25519 key as `publicKeyMultibase`, the material property of
|
|
306
|
+
`Ed25519VerificationKey2020` and the form did:cheqd documents use, failed
|
|
307
|
+
with `PUBLIC_KEY_NOT_FOUND`, projected no key, or failed the check. They now
|
|
308
|
+
read the key through `verificationMethodJwk`, as the credential verifiers
|
|
309
|
+
already do: a `publicKeyJwk` is used unchanged, otherwise the key is taken
|
|
310
|
+
from `publicKeyMultibase` or `publicKeyBase58`, and anything that is not a
|
|
311
|
+
32-byte Ed25519 key is still refused. The `PUBLIC_KEY_NOT_FOUND` message from
|
|
312
|
+
`fetchPublicKeyFromDID` now names all three properties. The did:key resolver's
|
|
313
|
+
output is unchanged.
|
|
314
|
+
- **Conformance suite 1.2.0: the status-list vectors' list verifies, and a
|
|
315
|
+
list altered after signing must be rejected.** The StatusList2021Credential
|
|
316
|
+
in `status-list.json` carried a proof that did not verify, and the reference
|
|
317
|
+
adapter read its bits without checking it. The suite therefore passed an
|
|
318
|
+
implementation that never checked a list's proof and failed one that did.
|
|
319
|
+
The list is re-signed, the reference adapter verifies its proof on the
|
|
320
|
+
delegation credentials' path before reading a bit, and a new negative
|
|
321
|
+
vector, `status-list/tampered-list`, clears the revoked bit after signing.
|
|
322
|
+
SPEC.md §6.6 and CONFORMANCE.md L3.11 state the check. The other eight
|
|
323
|
+
vector files change only their `version`, the suite has 49 vectors, and the
|
|
324
|
+
library is unchanged.
|
|
325
|
+
|
|
326
|
+
### Documentation
|
|
327
|
+
|
|
328
|
+
- **The README says what a response proof does not cover.** `_meta` is
|
|
329
|
+
outside every response proof (SPEC.md §7.6), so a server should repeat
|
|
330
|
+
security-relevant hints it carries there, such as an OAuth
|
|
331
|
+
`resource_metadata`, `scope` or `error`, in `content` or
|
|
332
|
+
`structuredContent`. The new section on proving application outcomes says
|
|
333
|
+
so, and says which profile covers which members.
|
|
334
|
+
- **SPEC.md §6.2 states how a base-profile credential is signed.** Its
|
|
335
|
+
example labels the proof `Ed25519Signature2020`, and CONFORMANCE.md L3.1
|
|
336
|
+
asked for "Ed25519Signature2020 or equivalent", but the profile has never
|
|
337
|
+
used the W3C suite of that name. §6.2 now specifies the construction the
|
|
338
|
+
reference implementation and the vectors use: Ed25519 over the JCS
|
|
339
|
+
canonicalization of the credential without `proof`, `proofValue` as
|
|
340
|
+
unpadded base64url, the key found by `proof.verificationMethod`, and the
|
|
341
|
+
proof options outside the signature. L3.1 and L3.5 point to it. A new
|
|
342
|
+
conformance test checks the vectors' delegation credentials against that
|
|
343
|
+
text with `node:crypto` rather than the library's verifier. Moving to a
|
|
344
|
+
standard cryptosuite stays open in #184.
|
|
345
|
+
|
|
346
|
+
## [1.18.0] - 2026-10-08
|
|
347
|
+
|
|
348
|
+
### Added
|
|
349
|
+
|
|
350
|
+
- **`maxChainLength` for delegation chains.** `validateDelegationChain` and
|
|
351
|
+
the middleware's `delegation` config take an optional cap on how many
|
|
352
|
+
credentials one chain may hold, checked before any of them is verified. A
|
|
353
|
+
resolver could return a chain of any length, and each credential costs a
|
|
354
|
+
signature check and possibly a DID resolution. It is a local cost limit, not
|
|
355
|
+
a protocol rule about how far authority may be delegated: it is unset by
|
|
356
|
+
default, and SPEC.md §6.4 records that the base profile leaves chain length
|
|
357
|
+
unbounded. The middleware refuses a value that is not a positive integer at
|
|
358
|
+
startup rather than rejecting every call. The cap applies to the chain
|
|
359
|
+
`resolveDelegationChain` returns, not to the resolver's own fetches.
|
|
360
|
+
|
|
361
|
+
### Changed
|
|
362
|
+
|
|
363
|
+
- **The Entity Card chain limit is opt-in, as on the base profile.**
|
|
364
|
+
`validateDelegationChain` and `evaluateDelegationChain` in
|
|
365
|
+
`@kya-os/mcp/card` rejected any chain of more than `MAX_DELEGATION_DEPTH`
|
|
366
|
+
(10) credentials unless the caller passed a larger `maxDepth`. They now
|
|
367
|
+
accept any length unless the caller sets `maxChainLength`, the option's name
|
|
368
|
+
on both profiles. **A verifier that relied on the default now accepts chains
|
|
369
|
+
of 11 credentials or more; pass `maxChainLength: MAX_DELEGATION_DEPTH` to
|
|
370
|
+
keep the previous behavior.** A longer chain broadens nothing: every hop
|
|
371
|
+
still attenuates its parent, so the cap bounded only the verifier's work.
|
|
372
|
+
SPEC.md §6.10 drops the cap from the attenuation invariants and states it,
|
|
373
|
+
non-normatively, as a local limit, as §6.4 does for the base profile. A
|
|
374
|
+
limit that is not a positive integer rejects every chain; previously `NaN`
|
|
375
|
+
accepted every length.
|
|
376
|
+
|
|
377
|
+
### Deprecated
|
|
378
|
+
|
|
379
|
+
- **`maxDepth` on the Entity Card `DelegationChainContext`.** Use
|
|
380
|
+
`maxChainLength`, which wins when both are set.
|
|
381
|
+
|
|
382
|
+
### Fixed
|
|
383
|
+
|
|
384
|
+
- **The `example:*` scripts run from a fresh clone.** They resolved
|
|
385
|
+
`@kya-os/mcp` to `dist/`, which a clone does not have until `npm run build`,
|
|
386
|
+
and the node-server and authz-inspector examples, being packages of their
|
|
387
|
+
own, could not resolve it at all without a per-example install. The scripts
|
|
388
|
+
and `scripts/demo.sh` now run the examples from `src/` through
|
|
389
|
+
`tsconfig.examples.json`, so the root `npm install` is all they need. Two
|
|
390
|
+
scripts are added for the consent-persistence scenarios.
|
|
391
|
+
- **`withKyaOs()` warns when the server is already connected.** It wraps
|
|
392
|
+
transports by patching `connect()`, so a transport connected before it ran
|
|
393
|
+
was never wrapped, and its tool results went out without a KYA-OS proof or
|
|
394
|
+
a transport audit record, silently. It now logs a warning saying so, on MCP
|
|
395
|
+
SDK 1.3 and later. When the server had no tools before connecting, the
|
|
396
|
+
warning also explains the SDK's refusal to register `_kyaos`.
|
|
397
|
+
|
|
398
|
+
### Documentation
|
|
399
|
+
|
|
400
|
+
- **CONFORMANCE.md L2.17 names the current proof keys.** It told verifiers to
|
|
401
|
+
read `org.kya-os/proof`, a key no server writes since the role-named keys
|
|
402
|
+
of SPEC.md §7.6. It now names `org.kya-os/response-proof`, keeps the prior
|
|
403
|
+
keys as 1.x fallbacks, in that order, and separates the request proof under
|
|
404
|
+
`org.kya-os/request-proof`.
|
|
405
|
+
- **SPEC.md §6.10 says how to tell the two delegation profiles apart.** The
|
|
406
|
+
first `@context` entry selects the rule set.
|
|
407
|
+
|
|
10
408
|
## [1.17.0] - 2026-10-05
|
|
11
409
|
|
|
12
410
|
### Added
|
package/CONFORMANCE.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Compliance levels for KYA-OS implementations**
|
|
4
4
|
|
|
5
|
-
Version: 1.
|
|
5
|
+
Version: 1.2.0
|
|
6
6
|
Status: Stable
|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -161,8 +161,14 @@ intact while swapping the `authorizationUrl`, and only the recompute detects it.
|
|
|
161
161
|
|
|
162
162
|
`_meta` is the MCP per-request metadata channel and is shared with reserved
|
|
163
163
|
reverse-DNS keys. A conformant verifier MUST:
|
|
164
|
-
- Read the
|
|
165
|
-
|
|
164
|
+
- Read the detached response proof from `org.kya-os/response-proof`. Through
|
|
165
|
+
1.x, also accept the prior keys `org.kya-os/proof` and bare `proof` (SPEC
|
|
166
|
+
§7.6), in that order: the first key present wins. Servers write `org.kya-os/response-proof` and, unless configured with
|
|
167
|
+
`emitLegacyProofKey: false`, mirror it under bare `proof`; nothing writes
|
|
168
|
+
`org.kya-os/proof`.
|
|
169
|
+
- Treat the holder-of-key request proof, under `org.kya-os/request-proof`
|
|
170
|
+
(SPEC-ENTITY-CARD §8.1), as a separate profile: never read one in place of
|
|
171
|
+
the other.
|
|
166
172
|
- Process **only** the KYA-OS proof key; ignore every other `_meta` key (never
|
|
167
173
|
hash it, never trust it).
|
|
168
174
|
- **Never reject** a response merely because `_meta` also carries non-KYA-OS
|
|
@@ -225,7 +231,7 @@ Implementation MUST:
|
|
|
225
231
|
- Include `issuer` as DID string or object with `id`
|
|
226
232
|
- Include `issuanceDate` in ISO 8601 format
|
|
227
233
|
- Include `credentialSubject` with delegation details
|
|
228
|
-
- Include `proof`
|
|
234
|
+
- Include `proof` as SPEC.md §6.2 specifies: an Ed25519 signature over the JCS canonicalization of the credential without `proof`, in `proofValue` as unpadded base64url. This is not the W3C `Ed25519Signature2020` suite, despite the `proof.type` value ([#184](https://github.com/decentralized-identity/kya-os-mcp/issues/184))
|
|
229
235
|
|
|
230
236
|
#### L3.5 — VC Verification
|
|
231
237
|
|
|
@@ -236,12 +242,13 @@ Implementation MUST validate:
|
|
|
236
242
|
- `issuanceDate` is present and in the past
|
|
237
243
|
- `expirationDate` (if present) is in the future
|
|
238
244
|
- `credentialSubject.delegation` has required fields
|
|
239
|
-
- `proof` is present
|
|
245
|
+
- `proof` is present and verifies as SPEC.md §6.2 specifies
|
|
240
246
|
|
|
241
247
|
#### L3.11 — StatusList2021 Checking
|
|
242
248
|
|
|
243
249
|
Implementation MUST:
|
|
244
250
|
- Fetch StatusList2021 credential from `credentialStatus.statusListCredential`
|
|
251
|
+
- Verify the list's `proof` as SPEC.md §6.2 specifies, and reject the credential when it does not verify
|
|
245
252
|
- Decompress and decode the bitstring
|
|
246
253
|
- Check bit at `statusListIndex`
|
|
247
254
|
- Return revoked status if bit is 1
|
|
@@ -424,7 +431,7 @@ so it is reproducible against any implementation without re-signing.
|
|
|
424
431
|
|------|----------|----------|----------|
|
|
425
432
|
| `vectors/signed-proof.json` | Detached proof verification | valid signature + in-window ts; envelope content binding (profile `org.kya-os/response-proof.envelope`) | tampered signature, tampered meta, wrong key, timestamp skew exceeded; tampered `structuredContent` under envelope coverage, `prf` stripped (downgrade), unknown profile |
|
|
426
433
|
| `vectors/delegation-chain.json` | Delegation chain verification | single-hop, two-hop attenuated | broken issuer↔subject linkage, scope widening, tampered signature, audience mismatch |
|
|
427
|
-
| `vectors/status-list.json` | StatusList2021 revocation | active (bit unset) | revoked (bit set) |
|
|
434
|
+
| `vectors/status-list.json` | StatusList2021 revocation | active (bit unset) | revoked (bit set), list altered after signing |
|
|
428
435
|
| `vectors/did-key-resolution.json` | did:key resolution | valid Ed25519 | malformed multibase, wrong method |
|
|
429
436
|
| `vectors/did-web-resolution.json` | did:web resolution | well-formed id-matched document | document id mismatch, not found |
|
|
430
437
|
| `vectors/card-proof.json` | `org.kya-os/proof.v1` holder-of-key proof | valid signed proof, in-window, audience-bound | tampered body, tampered signature, wrong audience, expired, kid⇄did forgery |
|
|
@@ -452,6 +459,11 @@ This vector leaves the JWS byte-identical to `valid-basic` and alters only the m
|
|
|
452
459
|
A conformant verifier MUST reconcile `meta` against the decoded JWS payload and reject on any mismatch.
|
|
453
460
|
Signature validity alone does not pass this vector: it is the `requestHash` counterpart of the `responseHash` recompute rule in L2.11.
|
|
454
461
|
|
|
462
|
+
**On `status-list/tampered-list` (suite ≥ 1.2.0).**
|
|
463
|
+
This vector carries the revoked credential and its signed list with the revoked bit cleared and the list's proof left as signed.
|
|
464
|
+
A verifier that reads the bits without verifying the list's proof reports the credential active and passes a vector it must reject (L3.11).
|
|
465
|
+
Suites before 1.2.0 shipped a list whose proof did not verify, so an implementation that checked it failed `status-list/active-credential`.
|
|
466
|
+
|
|
455
467
|
### Running the reference implementation
|
|
456
468
|
|
|
457
469
|
```bash
|
package/README.md
CHANGED
|
@@ -54,6 +54,24 @@ npm install @kya-os/mcp
|
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
+
## Upgrading to 1.20: proofs need a KYA-OS transport
|
|
58
|
+
|
|
59
|
+
> **Breaking for servers without a KYA-OS transport.** From 1.20, every response proof (under the default body profile as under the envelope profile) is made where the KYA-OS transport sends the result, after the MCP SDK has finished it. While no KYA-OS transport is open for the middleware, `wrapWithProof`, `wrapWithDelegation`, `withPolicyGate` and `proveOutcome` throw before they run anything.
|
|
60
|
+
|
|
61
|
+
| Your setup | What to do |
|
|
62
|
+
|---|---|
|
|
63
|
+
| `withKyaOs(server, …)` before `server.connect()` | Nothing. |
|
|
64
|
+
| Low-level `Server` with `createKyaOsMiddleware` | `await server.connect(createKyaOsTransport(transport, kyaos))`. It proves every tool's result, unwrapped tools included (unproven on 1.19.0); its third argument adds tools to leave unproven. |
|
|
65
|
+
| A script or test that calls a wrapped handler directly | `const local = serveInProcess(kyaos); const call = local.tool('name', handler); await call(args); await local.close();` |
|
|
66
|
+
| `withKyaOs` with `proofAllTools: false` | Leave it on; list tools to leave unproven in `excludeTools`. |
|
|
67
|
+
| `withKyaOs` after `connect()` | Call it before `connect()`. |
|
|
68
|
+
| Reading the proof inside the handler | The transport adds it when the result is sent; read it from what the client receives. |
|
|
69
|
+
| MCP task results fetched later | A result is proved the first time the same middleware sends it, within `pendingProofTtlMs` (default five minutes, at most 2147483647 ms); raise it for later fetches. |
|
|
70
|
+
|
|
71
|
+
See the [changelog](./CHANGELOG.md) for the details.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
57
75
|
## Migrate any MCP server in 2 lines
|
|
58
76
|
|
|
59
77
|
**Before**, a standard MCP server with no identity or proofs:
|
|
@@ -106,6 +124,45 @@ When an agent calls `checkout` without a delegation credential, it gets back a `
|
|
|
106
124
|
|
|
107
125
|
> Try it yourself: [examples/consent-basic](./examples/consent-basic/) walks through the full consent flow end-to-end.
|
|
108
126
|
|
|
127
|
+
### Prove the outcomes your own code decides
|
|
128
|
+
|
|
129
|
+
A server that enforces authorization itself, checking OAuth scopes in the tool handler for example, returns its own challenges and denials as tool results. `withKyaOs` proves the results it can attribute, but it sends an error result its own gates did not produce as the handler returned it, without a proof. `proveOutcome` gives that result the proof the gates attach to theirs, with the same claims, `_meta` keys and audit events:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { toMcpToolCallback } from '@kya-os/mcp';
|
|
133
|
+
|
|
134
|
+
server.registerTool('get_report', { inputSchema }, toMcpToolCallback(async (args) => {
|
|
135
|
+
if (!grantedScopes.includes('reports:read')) {
|
|
136
|
+
return kyaos.proveOutcome!({
|
|
137
|
+
toolName: 'get_report',
|
|
138
|
+
args,
|
|
139
|
+
outcome: 'needs_authorization', // or 'step_up_required', 'denied'
|
|
140
|
+
reason: 'insufficient_scope',
|
|
141
|
+
result: {
|
|
142
|
+
content: [{ type: 'text', text: 'Sign in to see this report.' }],
|
|
143
|
+
structuredContent: { error: 'insufficient_scope', scope: 'reports:read', resource_metadata },
|
|
144
|
+
isError: true,
|
|
145
|
+
_meta: { 'mcp/www_authenticate': [`Bearer resource_metadata="${resource_metadata}"`] },
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
return { content: [{ type: 'text', text: report }] };
|
|
150
|
+
}));
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- **Build the whole result before proving it**, and return what `proveOutcome` gives you from the handler.
|
|
154
|
+
- **Every proof is made where the result is sent.** The `withKyaOs` transport makes it when it sends the result, after the MCP SDK has finished the result, so it covers what the client receives. Under the default body profile it covers `content`, including a text item SDK 2 appends when `structuredContent` is not an object, and the `content: []` either SDK fills in when a handler leaves it out. Under the envelope response-proof profile (`responseProofProfile: RESPONSE_PROOF_PROFILE_ENVELOPE`) it covers every member except `_meta`, including the `resultType` that SDK 2 adds on the 2026-07-28 revision. This applies to `proveOutcome`, `wrapWithProof` and both gates.
|
|
155
|
+
- **Every wrapper needs an open KYA-OS transport.** `withKyaOs` wraps the transport you connect, unless `proofAllTools` is `false`; for a transport you connect yourself (the low-level `Server` API with `createKyaOsMiddleware`), wrap it with `createKyaOsTransport`, exported from `@kya-os/mcp`. Once a KYA-OS transport has been created for a middleware, every wrapper result must go out through one. While none is open (none was started, or all have closed), the wrappers and `proveOutcome` throw before they run anything, instead of sending a result they cannot prove.
|
|
156
|
+
- **`_meta` is outside every response proof** (SPEC §7.6). Repeat anything a client must be able to trust, such as `resource_metadata`, `scope` and `error`, in `content` or `structuredContent`.
|
|
157
|
+
- A `needs_authorization` proof binds the result; a denial or step-up proof binds none (SPEC §7.4).
|
|
158
|
+
- The result carries private members until the transport sends it, and the transport removes them. One of them is a one-time id: what the proof claims (by request hash) and its session stay in the middleware, and the transport redeems the id once. A result the SDK never sends, such as a cancelled call's, is closed in the audit trail with `proof.rejected` (`PROOF_NOT_SENT`) and, for an allowed call, `tool.call.failed` (`RESULT_NOT_SENT`).
|
|
159
|
+
- **A result is proved once, by the middleware that sends it, within `pendingProofTtlMs`** (default five minutes, at most 2147483647 ms). A task result fetched again, or later than the window, goes out with `proofError`, recorded as `PROOF_EXPIRED`. What the proof claims lives in that middleware's memory, so only the process that recorded the call can make the proof; [#250](https://github.com/decentralized-identity/kya-os-mcp/issues/250) proposes sealed claims so that any process holding the identity could.
|
|
160
|
+
- **Do not serialize a wrapped result into a string.** Its private members cannot be removed from inside the string, so the result that carries it goes out unproven (`PRIVATE_MEMBER_IN_RESULT`), and the serialized result's own proof is voided. Nest the result as an object instead: private members are removed at any depth from every outgoing message (results, notification and request `params`, and error `data`), and a wrapped result that goes out anywhere but as its own answer has its proof closed. On the answer to a `tools/call`, only a proof left for that tool is made.
|
|
161
|
+
- **Connect every server that shares a middleware through a KYA-OS transport.** The refusal checks only that some KYA-OS transport is open for the middleware. A second server sharing it over a plain transport therefore runs its wrappers, and its results leave with the lifecycle stamp and the pending-proof id in `_meta` and no proof. Those proofs are later closed as `PROOF_NOT_SENT`, although the results were sent.
|
|
162
|
+
- Leave `kyaos.sessionManager` alone. A second live session makes the fallback session ambiguous, and allow results then go out unproven, marked with `_meta.proofError`.
|
|
163
|
+
- `wrapWithDelegation`'s `formatChallenge` can return the same `{ content, structuredContent, isError, _meta }` shape for the library's own challenge, and all of it is set before signing.
|
|
164
|
+
- `toMcpToolCallback` adapts any wrapped handler to `registerTool` without a cast. The SDK's request context never reaches the handler as its session id.
|
|
165
|
+
|
|
109
166
|
---
|
|
110
167
|
|
|
111
168
|
## Turn proofs into a verifiable audit trail
|
|
@@ -217,6 +274,8 @@ This starts all example servers and opens [MCP Inspector](https://github.com/mod
|
|
|
217
274
|
|
|
218
275
|
Also available: [outbound-delegation](./examples/outbound-delegation/) (gateway pattern), [verify-proof](./examples/verify-proof/) (standalone verification), [statuslist](./examples/statuslist/) (revocation lifecycle), [cheqd-dlr](./examples/cheqd-dlr/) (operator DID linkage + DLR publishing).
|
|
219
276
|
|
|
277
|
+
To run one example on its own, use its `npm run example:*` script, for example `npm run example:server`. The scripts and `scripts/demo.sh` run the examples from `src/` through `tsconfig.examples.json`, so they need no build step. The scripts also need nothing beyond the root `npm install`; `demo.sh` additionally installs each example's own dependencies.
|
|
278
|
+
|
|
220
279
|
## Try it against the live server
|
|
221
280
|
|
|
222
281
|
[](https://github.com/decentralized-identity/kya-os-mcp/actions/workflows/live-probe.yml)
|