holon-sh 0.0.1-dev.16 → 0.0.1-dev.17
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/dist/context.d.ts +2 -5
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/ext/events.client.d.ts +4 -0
- package/dist/ext/events.client.d.ts.map +1 -0
- package/dist/ext/events.client.js +180 -0
- package/dist/ext/events.client.js.map +1 -0
- package/dist/ext/events.d.ts +6 -12
- package/dist/ext/events.d.ts.map +1 -1
- package/dist/ext/events.js +30 -51
- package/dist/ext/events.js.map +1 -1
- package/dist/ext/files.client.d.ts +2 -3
- package/dist/ext/files.client.d.ts.map +1 -1
- package/dist/ext/files.client.js +20 -22
- package/dist/ext/files.client.js.map +1 -1
- package/dist/ext/files.d.ts +1 -1
- package/dist/ext/files.d.ts.map +1 -1
- package/dist/link.d.ts +35 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +118 -0
- package/dist/link.js.map +1 -0
- package/dist/protocol.d.ts +6 -7
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +0 -1
- package/dist/protocol.js.map +1 -1
- package/dist/runtime.d.ts +0 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +129 -409
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
- package/src/context.ts +3 -1
- package/src/ext/events.client.ts +157 -0
- package/src/ext/events.ts +41 -83
- package/src/ext/files.client.ts +21 -20
- package/src/ext/files.ts +1 -1
- package/src/link.ts +136 -0
- package/src/protocol.ts +6 -9
- package/src/runtime.ts +120 -379
package/dist/context.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AuthInfo } from "@modelcontextprotocol/client";
|
|
1
2
|
/** Standard OpenID Connect address claims. */
|
|
2
3
|
export interface AddressClaims {
|
|
3
4
|
formatted?: string;
|
|
@@ -47,11 +48,7 @@ export interface RequestContext {
|
|
|
47
48
|
}
|
|
48
49
|
/** Read the caller and the app's origin from the context an MCP server receives: a tool callback's ctx.http, or the McpRequestContext of a createMcpHandler factory. */
|
|
49
50
|
export declare function getContext(context?: {
|
|
50
|
-
authInfo?:
|
|
51
|
-
clientId: string;
|
|
52
|
-
scopes: string[];
|
|
53
|
-
extra?: Record<string, unknown>;
|
|
54
|
-
};
|
|
51
|
+
authInfo?: Pick<AuthInfo, "clientId" | "scopes" | "extra">;
|
|
55
52
|
req?: Request;
|
|
56
53
|
requestInfo?: Request;
|
|
57
54
|
}): RequestContext;
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IACxH,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AACD,yHAAyH;AACzH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClG,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAClG,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAClG,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IACrG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AACD,yDAAyD;AACzD,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;AAC3J,sJAAsJ;AACtJ,MAAM,WAAW,cAAc;IAAG,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAMtF,wKAAwK;AACxK,wBAAgB,UAAU,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IACxH,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AACD,yHAAyH;AACzH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClG,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAClG,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAClG,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,aAAa,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IACrG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B;AACD,yDAAyD;AACzD,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;AAC3J,sJAAsJ;AACtJ,MAAM,WAAW,cAAc;IAAG,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE;AAMtF,wKAAwK;AACxK,wBAAgB,UAAU,CAAC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,cAAc,CAM7J"}
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAwBA,wKAAwK;AACxK,MAAM,UAAU,UAAU,CAAC,OAAO,GAAyG,EAAE;IAC3I,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC;IACxD,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAa,CAAC;IACtE,MAAM,SAAS,GAAG,CAAC,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI;QACtF,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAC1I,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACzD,CAAC;AAED,0HAA0H;AAC1H,WAAW;AACX,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,OAAO,EAAW;IACvC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,IAAI,EAAE,CAAC;AACrH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Link } from "../link.js";
|
|
2
|
+
import type { Contract, RuntimeClient } from "../runtime.js";
|
|
3
|
+
export declare function events(link: Link, contract: Contract | null, expect: (schema: object, label: string) => (value: unknown) => void): RuntimeClient["ext"]["events"];
|
|
4
|
+
//# sourceMappingURL=events.client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.client.d.ts","sourceRoot":"","sources":["../../src/ext/events.client.ts"],"names":[],"mappings":"AAGA,OAAO,EAA2C,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAEhF,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAW7D,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CA4IjK"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { patient, sleep, unchecked, untilAborted } from "../link.js";
|
|
2
|
+
// A stream that has said nothing for twice the longest heartbeat interval is dead: https://github.com/modelcontextprotocol/experimental-ext-triggers-events/blob/main/docs/design-sketch-proposal.md#lifecycle
|
|
3
|
+
const QUIET = 60_000;
|
|
4
|
+
// A server's refusal ends a subscription: a client error, or an answer that the event type is gone, that access was
|
|
5
|
+
// lost, or that the request is no longer valid. Anything else is tried again from the cursor.
|
|
6
|
+
const refused = (error) => error?.status >= 400 && error.status < 500 || error?.data?.status >= 400 && error.data.status < 500 || [-32602, -32023, -32024, -32026].includes(error?.code);
|
|
7
|
+
export function events(link, contract, expect) {
|
|
8
|
+
const { name } = link;
|
|
9
|
+
const connected = (options, run) => link.session(options, (_, connection) => {
|
|
10
|
+
if (!connection)
|
|
11
|
+
throw new Error(`MCP Events of ${name} require an endpoint binding.`);
|
|
12
|
+
return run(connection.client);
|
|
13
|
+
});
|
|
14
|
+
const list = (options) => connected(options, async (client) => {
|
|
15
|
+
const events = [];
|
|
16
|
+
for (let cursor;;) {
|
|
17
|
+
const page = await client.request({ method: "events/list", params: { ...(cursor && { cursor }) } }, unchecked(), patient(options));
|
|
18
|
+
events.push(...page.events ?? []);
|
|
19
|
+
if (!(cursor = page.nextCursor))
|
|
20
|
+
return events;
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const subscribe = async (event, args = {}, { signal, onGap, maxAgeMs, ...options } = {}) => {
|
|
24
|
+
const resolved = link.endpoint(`Subscribing to ${name} ${event}`);
|
|
25
|
+
const offered = (await list(signal && { signal })).find(type => type.name === event)?.delivery;
|
|
26
|
+
if (!offered)
|
|
27
|
+
throw new Error(`${name} has no event ${event}.`);
|
|
28
|
+
const delivery = options.delivery ?? ["push", "poll"].find(mode => offered.includes(mode));
|
|
29
|
+
// The sketch's NoCompatibleDeliveryMode: none of the modes the server offers is one this client uses.
|
|
30
|
+
if (!delivery || !offered.includes(delivery))
|
|
31
|
+
throw Object.assign(new Error(`${name} does not deliver ${event} by ${delivery ?? "poll or push"}.`), { name: "NoCompatibleDeliveryMode" });
|
|
32
|
+
const cancelled = new AbortController();
|
|
33
|
+
const ended = signal ? AbortSignal.any([signal, cancelled.signal]) : cancelled.signal;
|
|
34
|
+
let cursor = options.cursor ?? null;
|
|
35
|
+
const queue = [];
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
let wake = () => { };
|
|
38
|
+
let done = false;
|
|
39
|
+
let failure;
|
|
40
|
+
const reach = (next, truncated) => {
|
|
41
|
+
cursor = next ?? null;
|
|
42
|
+
if (truncated)
|
|
43
|
+
onGap?.();
|
|
44
|
+
};
|
|
45
|
+
const expected = contract?.events?.find(type => type.name === event)?.payloadSchema;
|
|
46
|
+
const check = expected && expect(expected, `data of ${name} ${event}`);
|
|
47
|
+
const deliver = ({ cursor: _, _meta, ...occurrence }) => {
|
|
48
|
+
if (seen.has(occurrence.eventId))
|
|
49
|
+
return;
|
|
50
|
+
seen.add(occurrence.eventId);
|
|
51
|
+
if (seen.size > 1024)
|
|
52
|
+
seen.delete(seen.values().next().value);
|
|
53
|
+
check?.(occurrence.data);
|
|
54
|
+
queue.push(occurrence);
|
|
55
|
+
wake();
|
|
56
|
+
};
|
|
57
|
+
const request = { name: event, arguments: args, ...(maxAgeMs !== undefined && { maxAgeMs }) };
|
|
58
|
+
let confirmed = false;
|
|
59
|
+
const established = Promise.withResolvers();
|
|
60
|
+
const confirm = () => { confirmed = true; established.resolve(); };
|
|
61
|
+
const poll = async () => {
|
|
62
|
+
while (!ended.aborted) {
|
|
63
|
+
let wait = 1000;
|
|
64
|
+
try {
|
|
65
|
+
const result = await connected({ signal: ended }, client => client.request({ method: "events/poll", params: { ...request, cursor } }, unchecked(), patient({ signal: ended })));
|
|
66
|
+
try {
|
|
67
|
+
for (const occurrence of result.events ?? [])
|
|
68
|
+
deliver(occurrence);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
failure = error;
|
|
72
|
+
return cancelled.abort();
|
|
73
|
+
}
|
|
74
|
+
reach(result.cursor, result.truncated);
|
|
75
|
+
confirm();
|
|
76
|
+
// A server that asks for a tight loop is not obeyed.
|
|
77
|
+
wait = result.hasMore ? 0 : Math.max(1000, result.nextPollMs ?? 0);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (ended.aborted)
|
|
81
|
+
return;
|
|
82
|
+
if (!confirmed || refused(error))
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
await sleep(wait, ended);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const stream = async () => {
|
|
89
|
+
while (!ended.aborted) {
|
|
90
|
+
let wait = 1000;
|
|
91
|
+
const quiet = new AbortController();
|
|
92
|
+
// The MCP client keeps waiting for the answer of a request whose stream has ended, so the end is watched for here.
|
|
93
|
+
// Only the stream's own response is watched: a 2025 server answers its initialization as a stream too.
|
|
94
|
+
const watched = async (input, init) => {
|
|
95
|
+
const response = await (resolved.fetch ?? fetch)(input, init);
|
|
96
|
+
const streaming = typeof init?.body === "string" && init.body.includes('"method":"events/stream"');
|
|
97
|
+
if (!streaming || !response.body || !response.headers.get("content-type")?.startsWith("text/event-stream"))
|
|
98
|
+
return response;
|
|
99
|
+
const reader = response.body.getReader();
|
|
100
|
+
const body = new ReadableStream({
|
|
101
|
+
pull: controller => reader.read().then(({ done, value }) => {
|
|
102
|
+
if (!done)
|
|
103
|
+
return controller.enqueue(value);
|
|
104
|
+
controller.close();
|
|
105
|
+
// The answer, when the stream ended with one, is handled first.
|
|
106
|
+
setTimeout(() => quiet.abort());
|
|
107
|
+
}, error => { controller.error(error); quiet.abort(); }),
|
|
108
|
+
cancel: reason => reader.cancel(reason),
|
|
109
|
+
});
|
|
110
|
+
return new Response(body, response);
|
|
111
|
+
};
|
|
112
|
+
// Each stream has its own connection, so every event notification on it is this subscription's.
|
|
113
|
+
const connection = link.connect({ ...resolved, fetch: watched });
|
|
114
|
+
let timer;
|
|
115
|
+
const heard = () => {
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
timer = setTimeout(() => quiet.abort(), QUIET);
|
|
118
|
+
};
|
|
119
|
+
let terminated;
|
|
120
|
+
try {
|
|
121
|
+
await untilAborted(connection.ready, ended);
|
|
122
|
+
const on = (method, handle) => connection.client.setNotificationHandler(`notifications/events/${method}`, { params: unchecked() }, params => { heard(); handle(params); });
|
|
123
|
+
on("active", ({ cursor: next, truncated }) => { reach(next, truncated); confirm(); });
|
|
124
|
+
on("event", occurrence => {
|
|
125
|
+
wait = 0;
|
|
126
|
+
try {
|
|
127
|
+
deliver(occurrence);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
failure = error;
|
|
131
|
+
return cancelled.abort();
|
|
132
|
+
}
|
|
133
|
+
reach(occurrence.cursor);
|
|
134
|
+
});
|
|
135
|
+
on("heartbeat", ({ cursor: next }) => reach(next));
|
|
136
|
+
// The server recovers from an error by itself, and the stream stays open.
|
|
137
|
+
on("error", () => { });
|
|
138
|
+
on("terminated", ({ error }) => { terminated = error; quiet.abort(); });
|
|
139
|
+
heard();
|
|
140
|
+
await connection.client.request({ method: "events/stream", params: { ...request, cursor } }, unchecked(), { signal: AbortSignal.any([ended, quiet.signal]), timeout: 2 ** 31 - 1 });
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (!terminated && !ended.aborted && (!confirmed || refused(error)))
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
await connection.close();
|
|
149
|
+
}
|
|
150
|
+
if (terminated)
|
|
151
|
+
throw Object.assign(new Error(`${name} ended the subscription to ${event}: ${terminated.message}`), { code: terminated.code, data: terminated.data });
|
|
152
|
+
await sleep(wait, ended);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
void (delivery === "push" ? stream() : poll())
|
|
156
|
+
.catch(error => { failure ??= error; })
|
|
157
|
+
.finally(() => { done = true; wake(); established.reject(failure ?? ended.reason); });
|
|
158
|
+
await established.promise;
|
|
159
|
+
return {
|
|
160
|
+
delivery,
|
|
161
|
+
get cursor() { return cursor; },
|
|
162
|
+
cancel: () => cancelled.abort(),
|
|
163
|
+
[Symbol.asyncIterator]: () => ({
|
|
164
|
+
next: async () => {
|
|
165
|
+
while (!queue.length && !done)
|
|
166
|
+
await new Promise(resolve => { wake = resolve; });
|
|
167
|
+
const value = queue.shift();
|
|
168
|
+
if (value)
|
|
169
|
+
return { value, done: false };
|
|
170
|
+
if (failure)
|
|
171
|
+
throw failure;
|
|
172
|
+
return { value: undefined, done: true };
|
|
173
|
+
},
|
|
174
|
+
return: async () => { cancelled.abort(); return { value: undefined, done: true }; },
|
|
175
|
+
}),
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
return Object.freeze({ list, subscribe: subscribe });
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=events.client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.client.js","sourceRoot":"","sources":["../../src/ext/events.client.ts"],"names":[],"mappings":"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"}
|
package/dist/ext/events.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JSONRPCErrorResponse, McpServer, Server, ServerContext, StandardSchemaWithJSON } from "@modelcontextprotocol/server";
|
|
2
|
+
import type { EventOccurrence as Occurrence } from "../protocol.js";
|
|
2
3
|
/** The request a subscription came in, as Holon admitted it: read its caller with getContext. */
|
|
3
|
-
export type SubscriptionContext =
|
|
4
|
-
authInfo?: AuthInfo | undefined;
|
|
5
|
-
} | undefined;
|
|
4
|
+
export type SubscriptionContext = ServerContext["http"];
|
|
6
5
|
/** How a subscription receives its events. */
|
|
7
6
|
export type DeliveryModeName = "poll" | "push";
|
|
8
7
|
/** A JSON-RPC error, as a subscription that is terminated or fails is told it. */
|
|
@@ -24,14 +23,9 @@ export interface Subscription<A> {
|
|
|
24
23
|
readonly context: SubscriptionContext;
|
|
25
24
|
}
|
|
26
25
|
/** An occurrence as subscriptions receive it: the sketch's EventOccurrence. `cursor` is the position after it. */
|
|
27
|
-
export
|
|
28
|
-
eventId: string;
|
|
29
|
-
name: string;
|
|
30
|
-
/** When it happened, in ISO 8601. */
|
|
31
|
-
timestamp: string;
|
|
32
|
-
data: D;
|
|
26
|
+
export type EventOccurrence<D = unknown> = Occurrence<D> & {
|
|
33
27
|
cursor?: string;
|
|
34
|
-
}
|
|
28
|
+
};
|
|
35
29
|
/** An event type: the arguments a subscription gives, and the data each occurrence carries. */
|
|
36
30
|
export interface EventDefinition<A, D> {
|
|
37
31
|
description?: string;
|
|
@@ -51,7 +45,7 @@ export interface EventDefinition<A, D> {
|
|
|
51
45
|
/** The data a subscription with these arguments receives of an emitted event. Without it, the data as emitted. */
|
|
52
46
|
transform?: (data: D, args: A, context: SubscriptionContext) => unknown;
|
|
53
47
|
/** The delivery modes this event type offers: poll and push unless given. */
|
|
54
|
-
delivery?: DeliveryModeName[];
|
|
48
|
+
delivery?: [DeliveryModeName, ...DeliveryModeName[]];
|
|
55
49
|
/**
|
|
56
50
|
* Whether the caller may subscribe with these arguments. A subscription is refused with Forbidden when it may not,
|
|
57
51
|
* and one already open is ended when it no longer may: each poll asks, and a stream asks at each heartbeat. A
|
package/dist/ext/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/ext/events.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/ext/events.ts"],"names":[],"mappings":"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"}
|
package/dist/ext/events.js
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
// The server side of MCP Events, as the Triggers & Events Working Group's design sketch defines it:
|
|
2
2
|
// https://github.com/modelcontextprotocol/experimental-ext-triggers-events/blob/main/docs/design-sketch-proposal.md
|
|
3
|
-
// The draft is experimental, so this lives under ext and may change with it. It serves the poll and push delivery
|
|
4
|
-
// modes. Names follow the sketch, and types are the MCP SDK's own wherever it has them.
|
|
5
3
|
import { ProtocolError, ProtocolErrorCode, SUBSCRIPTION_ID_META_KEY } from "@modelcontextprotocol/server";
|
|
4
|
+
import { getContext } from "../context.js";
|
|
6
5
|
/** The identifier a server declares the Events extension under, in capabilities.extensions. */
|
|
7
6
|
export const EVENTS_EXTENSION = "io.modelcontextprotocol/events";
|
|
8
|
-
const INVALID_PARAMS = ProtocolErrorCode.InvalidParams;
|
|
9
|
-
const INTERNAL_ERROR = ProtocolErrorCode.InternalError;
|
|
10
7
|
// The sketch's general-purpose codes, provisional until the specification allocates them.
|
|
11
8
|
const NOT_FOUND = -32023;
|
|
12
9
|
const FORBIDDEN = -32024;
|
|
13
10
|
const RESOURCE_EXHAUSTED = -32025;
|
|
14
11
|
const UNSUPPORTED = -32026;
|
|
15
|
-
|
|
16
|
-
const params = { "~standard": { version: 1, vendor: "holon-sh", validate: (value) => ({ value: value ?? {} }) } };
|
|
12
|
+
const params = { "~standard": { version: 1, vendor: "holon-sh", validate: value => ({ value: (value ?? {}) }) } };
|
|
17
13
|
const refusal = ({ code, message, data }) => new ProtocolError(code, message, data);
|
|
18
14
|
const removed = { code: NOT_FOUND, message: "NotFound", data: { kind: "event" } };
|
|
19
15
|
const forbidden = { code: FORBIDDEN, message: "Forbidden" };
|
|
20
|
-
// What a client is told of a failure: its code when it is a JSON-RPC one, and its message.
|
|
21
16
|
const described = (error) => ({
|
|
22
|
-
code: Number.isInteger(error?.code) ? error.code :
|
|
17
|
+
code: Number.isInteger(error?.code) ? error.code : ProtocolErrorCode.InternalError,
|
|
23
18
|
message: error?.message || "InternalError",
|
|
24
19
|
...(error?.data !== undefined && { data: error.data }),
|
|
25
20
|
});
|
|
@@ -27,14 +22,9 @@ const described = (error) => ({
|
|
|
27
22
|
const canonical = (value) => Array.isArray(value) ? value.map(canonical)
|
|
28
23
|
: value && typeof value === "object" ? Object.fromEntries(Object.keys(value).sort().map(key => [key, canonical(value[key])]))
|
|
29
24
|
: value;
|
|
30
|
-
// Who asked, as Holon admitted them: the user, the client acting for them, the agent when one acts for the user, and
|
|
31
|
-
// what the token allows. A caller without a subject has no identity: nobody can tell it from another such caller.
|
|
32
25
|
const caller = (context) => {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
if (!authInfo || typeof sub !== "string")
|
|
36
|
-
return null;
|
|
37
|
-
return JSON.stringify(canonical([iss, sub, authInfo.clientId ?? null, act, [...authInfo.scopes ?? []].sort()]));
|
|
26
|
+
const { principal } = getContext(context);
|
|
27
|
+
return principal && JSON.stringify(canonical([principal.issuer, principal.subject, principal.clientId, principal.actor ?? null, [...principal.scopes].sort()]));
|
|
38
28
|
};
|
|
39
29
|
const hex = (bytes) => [...bytes].map(byte => byte.toString(16).padStart(2, "0")).join("");
|
|
40
30
|
// A subscription's id is the same for the same caller, event type and arguments, so it survives reconnects and poll
|
|
@@ -43,11 +33,8 @@ const identify = async (who, name, args) => {
|
|
|
43
33
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify([who, name, canonical(args)])));
|
|
44
34
|
return `sub_${hex(new Uint8Array(digest, 0, 16))}`;
|
|
45
35
|
};
|
|
46
|
-
// A caller without an identity gets an id of its own for each stream.
|
|
47
36
|
const unique = () => `sub_${hex(crypto.getRandomValues(new Uint8Array(16)))}`;
|
|
48
|
-
// The subscriber a subscription's arguments name, whoever the caller.
|
|
49
37
|
const named = (args) => JSON.stringify(canonical(args));
|
|
50
|
-
// Counts up or down under a key, and forgets a key that reaches none.
|
|
51
38
|
const count = (counts, key, by) => {
|
|
52
39
|
const total = (counts.get(key) ?? 0) + by;
|
|
53
40
|
if (total)
|
|
@@ -55,8 +42,6 @@ const count = (counts, key, by) => {
|
|
|
55
42
|
else
|
|
56
43
|
counts.delete(key);
|
|
57
44
|
};
|
|
58
|
-
// A teardown that fails has nobody to tell, and must not take the process down.
|
|
59
|
-
const settle = (run) => { Promise.resolve().then(run).catch(() => { }); };
|
|
60
45
|
const integer = (value, minimum) => value === undefined || Number.isInteger(value) && value >= minimum;
|
|
61
46
|
const OFFERED = ["poll", "push"];
|
|
62
47
|
// What each type keeps of what it emits, for polls and reconnecting streams: the sketch's emit buffer.
|
|
@@ -72,7 +57,6 @@ const POLL_SUBSCRIPTION_TTL = 60_000;
|
|
|
72
57
|
// cannot be told apart, so they count together as one caller.
|
|
73
58
|
const MAX_SUBSCRIPTIONS = 10_000;
|
|
74
59
|
const MAX_SUBSCRIPTIONS_PER_CALLER = 1000;
|
|
75
|
-
// How many event types events/list returns at a time.
|
|
76
60
|
const PAGE_SIZE = 100;
|
|
77
61
|
/**
|
|
78
62
|
* What a stream sends before it ends and its client resumes from its cursor. The draft leaves flow control to the
|
|
@@ -95,7 +79,7 @@ export function createEvents() {
|
|
|
95
79
|
let declared = 0;
|
|
96
80
|
let subscribed = 0;
|
|
97
81
|
const callers = new Map();
|
|
98
|
-
const position = (
|
|
82
|
+
const position = (sequence) => `${epoch}.${sequence}`;
|
|
99
83
|
// What a subscription is owed after a cursor, up to `until`: the events for it, in order, where delivery starts,
|
|
100
84
|
// and where it now stands. A null cursor starts where the subscription was opened, and so does one from another
|
|
101
85
|
// process, which says that events were skipped. One past the events still kept starts from the oldest kept, and
|
|
@@ -111,16 +95,16 @@ export function createEvents() {
|
|
|
111
95
|
const events = [];
|
|
112
96
|
const failures = [];
|
|
113
97
|
let reached = recent.length ? recent[0].sequence - 1 : until;
|
|
114
|
-
const start = position(
|
|
98
|
+
const start = position(reached);
|
|
115
99
|
for (const kept of recent) {
|
|
116
100
|
if (events.length === limit)
|
|
117
|
-
return { events, failures, start, cursor: position(
|
|
101
|
+
return { events, failures, start, cursor: position(reached), truncated, hasMore: true };
|
|
118
102
|
reached = kept.sequence;
|
|
119
103
|
const event = shape(type, kept, subscription, error => failures.push(error));
|
|
120
104
|
if (event)
|
|
121
105
|
events.push(event);
|
|
122
106
|
}
|
|
123
|
-
return { events, failures, start, cursor: position(
|
|
107
|
+
return { events, failures, start, cursor: position(until), truncated, hasMore: false };
|
|
124
108
|
};
|
|
125
109
|
// The occurrence a subscription receives, or nothing when it is not for it. An event emitted to one subscription
|
|
126
110
|
// is for that one alone, and its arguments are not asked.
|
|
@@ -139,10 +123,10 @@ export function createEvents() {
|
|
|
139
123
|
catch (error) {
|
|
140
124
|
const { eventId } = occurrence;
|
|
141
125
|
console.error(`holon-sh/ext/events: event ${eventId} of ${type.name} was skipped for a subscription:`, error);
|
|
142
|
-
failed({ code:
|
|
126
|
+
failed({ code: ProtocolErrorCode.InternalError, message: `Event ${eventId} could not be delivered and was skipped`, data: { eventId, reason: String(error?.message ?? error) } });
|
|
143
127
|
return undefined;
|
|
144
128
|
}
|
|
145
|
-
return { ...occurrence, data, cursor: position(
|
|
129
|
+
return { ...occurrence, data, cursor: position(sequence) };
|
|
146
130
|
};
|
|
147
131
|
// The event type and arguments a request names, checked as the sketch's errors require, in its order: the type,
|
|
148
132
|
// the delivery mode, the request's own fields, the arguments, and then whether the caller may subscribe. A caller
|
|
@@ -154,14 +138,14 @@ export function createEvents() {
|
|
|
154
138
|
if (!type.delivery.includes(delivery))
|
|
155
139
|
throw refusal({ code: UNSUPPORTED, message: "Unsupported", data: { feature: "deliveryMode", value: delivery } });
|
|
156
140
|
if (cursor != null && typeof cursor !== "string")
|
|
157
|
-
throw new ProtocolError(
|
|
141
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "cursor must be a string or null");
|
|
158
142
|
if (!integer(maxAgeMs, 0))
|
|
159
|
-
throw new ProtocolError(
|
|
143
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "maxAgeMs must be a non-negative integer");
|
|
160
144
|
if (!integer(maxEvents, 1))
|
|
161
|
-
throw new ProtocolError(
|
|
145
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "maxEvents must be a positive integer");
|
|
162
146
|
const checked = await type.inputSchema["~standard"].validate(args);
|
|
163
147
|
if (checked.issues)
|
|
164
|
-
throw new ProtocolError(
|
|
148
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for ${name}: ${checked.issues.map(issue => issue.message).join("; ")}`);
|
|
165
149
|
const who = caller(context);
|
|
166
150
|
// Polls share a lease per caller and arguments, and callers without an identity share one per arguments.
|
|
167
151
|
const lease = await identify(who, type.name, checked.value);
|
|
@@ -171,7 +155,8 @@ export function createEvents() {
|
|
|
171
155
|
leases.delete(lease);
|
|
172
156
|
throw refusal(forbidden);
|
|
173
157
|
}
|
|
174
|
-
|
|
158
|
+
const subscription = { id: who === null ? unique() : lease, lease, arguments: checked.value, context };
|
|
159
|
+
return { type, subscription, cursor: cursor ?? null, maxAgeMs, maxEvents };
|
|
175
160
|
};
|
|
176
161
|
const endSubscription = (type, id, error) => {
|
|
177
162
|
for (const held of [...type.subscriptions.get(id)?.held ?? []])
|
|
@@ -184,7 +169,6 @@ export function createEvents() {
|
|
|
184
169
|
const open = async (type, { id, delivery, arguments: args, context }, end) => {
|
|
185
170
|
if (types.get(type.name) !== type)
|
|
186
171
|
throw refusal(removed);
|
|
187
|
-
// Callers without an identity cannot be told apart, so they count together as one.
|
|
188
172
|
const who = caller(context) ?? "anonymous";
|
|
189
173
|
if (subscribed >= MAX_SUBSCRIPTIONS)
|
|
190
174
|
throw refusal({ code: RESOURCE_EXHAUSTED, message: "ResourceExhausted", data: { limit: "subscriptions", max: MAX_SUBSCRIPTIONS } });
|
|
@@ -192,7 +176,6 @@ export function createEvents() {
|
|
|
192
176
|
throw refusal({ code: RESOURCE_EXHAUSTED, message: "ResourceExhausted", data: { limit: "subscriptionsPerCaller", max: MAX_SUBSCRIPTIONS_PER_CALLER } });
|
|
193
177
|
subscribed++;
|
|
194
178
|
count(callers, who, 1);
|
|
195
|
-
// A presence keeps a subscriber's value while any of its streams or leases is open.
|
|
196
179
|
const subscriber = named(args);
|
|
197
180
|
const hold = (by) => {
|
|
198
181
|
count(type.holds, subscriber, by);
|
|
@@ -222,9 +205,9 @@ export function createEvents() {
|
|
|
222
205
|
return;
|
|
223
206
|
if (type.subscriptions.get(id) === current)
|
|
224
207
|
type.subscriptions.delete(id);
|
|
225
|
-
// Only a subscription that was set up is told it ended.
|
|
226
|
-
|
|
227
|
-
|
|
208
|
+
// Only a subscription that was set up is told it ended. A teardown that fails has nobody to tell, and must not
|
|
209
|
+
// take the process down.
|
|
210
|
+
current.ready.then(subscription => type.onUnsubscribe?.(subscription)).catch(() => { });
|
|
228
211
|
} };
|
|
229
212
|
current.held.add(held);
|
|
230
213
|
try {
|
|
@@ -294,14 +277,11 @@ export function createEvents() {
|
|
|
294
277
|
* Declares an event type and returns the function that emits it. `match` decides which subscriptions receive an
|
|
295
278
|
* event from their arguments, and `transform` shapes its data for each.
|
|
296
279
|
*/
|
|
297
|
-
event(name,
|
|
280
|
+
event(name, definition) {
|
|
298
281
|
if (types.has(name))
|
|
299
282
|
throw new Error(`Event ${name} is already declared.`);
|
|
300
|
-
const unknown = delivery.filter(mode => !OFFERED.includes(mode));
|
|
301
|
-
if (unknown.length || !delivery.length)
|
|
302
|
-
throw new Error(`Event ${name} is delivered by ${delivery.join(", ") || "nothing"}, but only ${OFFERED.join(", ")} are served.`);
|
|
303
283
|
const type = {
|
|
304
|
-
name, order: ++declared,
|
|
284
|
+
...definition, name, order: ++declared, delivery: definition.delivery ?? OFFERED,
|
|
305
285
|
sequence: 0, dropped: 0, kept: [], sinks: new Set(), subscriptions: new Map(), holds: new Map(), present: new Set(),
|
|
306
286
|
};
|
|
307
287
|
types.set(name, type);
|
|
@@ -319,7 +299,7 @@ export function createEvents() {
|
|
|
319
299
|
sink();
|
|
320
300
|
},
|
|
321
301
|
cursor() {
|
|
322
|
-
return position(type);
|
|
302
|
+
return position(type.sequence);
|
|
323
303
|
},
|
|
324
304
|
terminate(id, error = forbidden) {
|
|
325
305
|
if (id !== undefined)
|
|
@@ -370,8 +350,7 @@ export function createEvents() {
|
|
|
370
350
|
},
|
|
371
351
|
/** Serves the declared event types on an MCP server: events/list, events/poll and events/stream. */
|
|
372
352
|
serve(mcp) {
|
|
373
|
-
|
|
374
|
-
const server = ("server" in mcp ? mcp.server : mcp);
|
|
353
|
+
const server = "server" in mcp ? mcp.server : mcp;
|
|
375
354
|
// The sketch declares capabilities.events. The MCP SDK's client drops top-level capabilities it does not know,
|
|
376
355
|
// so the extension is declared under capabilities.extensions too, where SDK clients read it.
|
|
377
356
|
// Clients are never told the event types changed: over Holon's stateless HTTP, the notification reaches none.
|
|
@@ -383,7 +362,7 @@ export function createEvents() {
|
|
|
383
362
|
if (request.cursor != null) {
|
|
384
363
|
const after = Number(request.cursor);
|
|
385
364
|
if (typeof request.cursor !== "string" || !Number.isInteger(after))
|
|
386
|
-
throw new ProtocolError(
|
|
365
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, "Invalid cursor for events/list");
|
|
387
366
|
start = all.findIndex(type => type.order > after);
|
|
388
367
|
if (start < 0)
|
|
389
368
|
start = all.length;
|
|
@@ -402,18 +381,18 @@ export function createEvents() {
|
|
|
402
381
|
};
|
|
403
382
|
});
|
|
404
383
|
server.setRequestHandler("events/poll", { params }, async (request, ctx) => {
|
|
405
|
-
const { type, subscription: subscribed } = await subscription(request, ctx.http, "poll");
|
|
384
|
+
const { type, subscription: subscribed, cursor, maxAgeMs, maxEvents } = await subscription(request, ctx.http, "poll");
|
|
406
385
|
// Taken before the lease, which waits for onSubscribe. The sketch's poll from null returns no events and a
|
|
407
386
|
// fresh cursor: this one is where the subscription opened, so the next poll has what onSubscribe emitted.
|
|
408
387
|
const opened = type.sequence;
|
|
409
388
|
const held = await lease(type, subscribed);
|
|
410
389
|
// Each poll is answered for its own caller, with the lease's id.
|
|
411
|
-
const { events, start, failures, ...rest } = since(type, { ...subscribed, id: held.id, untargetable: held.untargetable },
|
|
390
|
+
const { events, start, failures, ...rest } = since(type, { ...subscribed, id: held.id, untargetable: held.untargetable }, cursor, { maxAgeMs, maxEvents, opened, ...(cursor === null && { until: opened }) });
|
|
412
391
|
// A poll carries its cursor once, for the whole response.
|
|
413
392
|
return { events: events.map(({ cursor, ...event }) => event), ...rest, nextPollMs: INTERVAL };
|
|
414
393
|
});
|
|
415
394
|
server.setRequestHandler("events/stream", { params }, async (request, ctx) => {
|
|
416
|
-
const { type, subscription: subscribed } = await subscription(request, ctx.http, "push");
|
|
395
|
+
const { type, subscription: subscribed, cursor: from, maxAgeMs } = await subscription(request, ctx.http, "push");
|
|
417
396
|
const { signal } = ctx.mcpReq;
|
|
418
397
|
const meta = { [SUBSCRIPTION_ID_META_KEY]: ctx.mcpReq.id };
|
|
419
398
|
let done = false;
|
|
@@ -468,7 +447,7 @@ export function createEvents() {
|
|
|
468
447
|
// The stream follows its cursor, and emitted events wake it. What it is owed waits in the buffer, not here, so
|
|
469
448
|
// a slow client is not owed more memory than one batch; when what it is owed is no longer kept, it is told of
|
|
470
449
|
// the gap.
|
|
471
|
-
let cursor =
|
|
450
|
+
let cursor = from;
|
|
472
451
|
let behind = true;
|
|
473
452
|
const sink = () => {
|
|
474
453
|
// Nothing emitted after the app terminated the stream is for it.
|
|
@@ -502,7 +481,7 @@ export function createEvents() {
|
|
|
502
481
|
while (!done) {
|
|
503
482
|
if (behind) {
|
|
504
483
|
behind = false;
|
|
505
|
-
const result = since(type, held, cursor, { ...(first &&
|
|
484
|
+
const result = since(type, held, cursor, { ...(first && { maxAgeMs }), opened, until });
|
|
506
485
|
// The stream is confirmed before anything else is said on it.
|
|
507
486
|
if (first || result.truncated)
|
|
508
487
|
await notify("active", { cursor: result.start, truncated: result.truncated });
|