@toolgrain/mcp 0.3.0-alpha.0 → 0.3.0-alpha.2
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 +19 -0
- package/README.md +17 -3
- package/dist/apps.d.ts +9 -0
- package/dist/apps.d.ts.map +1 -0
- package/dist/apps.js +21 -0
- package/dist/apps.js.map +1 -0
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +9 -1
- package/dist/connection.js.map +1 -1
- package/dist/dispatcher.d.ts +2 -1
- package/dist/dispatcher.d.ts.map +1 -1
- package/dist/dispatcher.js +3 -3
- package/dist/dispatcher.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/legacy/dispatcher.d.ts +2 -1
- package/dist/legacy/dispatcher.d.ts.map +1 -1
- package/dist/legacy/dispatcher.js +23 -8
- package/dist/legacy/dispatcher.js.map +1 -1
- package/dist/legacy/handlers.d.ts +1 -1
- package/dist/legacy/handlers.d.ts.map +1 -1
- package/dist/legacy/handlers.js +2 -2
- package/dist/legacy/handlers.js.map +1 -1
- package/dist/modern/dispatcher.d.ts +2 -1
- package/dist/modern/dispatcher.d.ts.map +1 -1
- package/dist/modern/dispatcher.js +22 -8
- package/dist/modern/dispatcher.js.map +1 -1
- package/dist/modern/handlers.d.ts +1 -1
- package/dist/modern/handlers.d.ts.map +1 -1
- package/dist/modern/handlers.js +2 -2
- package/dist/modern/handlers.js.map +1 -1
- package/dist/modern/results.d.ts +6 -0
- package/dist/modern/results.d.ts.map +1 -1
- package/dist/modern/results.js +9 -0
- package/dist/modern/results.js.map +1 -1
- package/dist/pending-requests.d.ts +2 -1
- package/dist/pending-requests.d.ts.map +1 -1
- package/dist/pending-requests.js +17 -0
- package/dist/pending-requests.js.map +1 -1
- package/dist/resources/definition.d.ts +45 -0
- package/dist/resources/definition.d.ts.map +1 -0
- package/dist/resources/definition.js +46 -0
- package/dist/resources/definition.js.map +1 -0
- package/dist/resources/registry.d.ts +13 -0
- package/dist/resources/registry.d.ts.map +1 -0
- package/dist/resources/registry.js +48 -0
- package/dist/resources/registry.js.map +1 -0
- package/dist/resources/requests.d.ts +13 -0
- package/dist/resources/requests.d.ts.map +1 -0
- package/dist/resources/requests.js +43 -0
- package/dist/resources/requests.js.map +1 -0
- package/dist/resources/validation.d.ts +33 -0
- package/dist/resources/validation.d.ts.map +1 -0
- package/dist/resources/validation.js +61 -0
- package/dist/resources/validation.js.map +1 -0
- package/dist/server.d.ts +7 -4
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +6 -3
- package/dist/server.js.map +1 -1
- package/dist/tools/context.d.ts +5 -4
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +1 -1
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/progress.d.ts +4 -2
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +40 -6
- package/dist/tools/progress.js.map +1 -1
- package/dist/tools/rich-result.d.ts +19 -19
- package/dist/tools/rich-result.d.ts.map +1 -1
- package/dist/tools/rich-result.js.map +1 -1
- package/package.json +2 -2
- package/src/apps.ts +23 -0
- package/src/connection.ts +15 -0
- package/src/dispatcher.ts +4 -0
- package/src/index.ts +9 -0
- package/src/legacy/dispatcher.ts +55 -13
- package/src/legacy/handlers.ts +2 -1
- package/src/modern/dispatcher.ts +57 -21
- package/src/modern/handlers.ts +2 -1
- package/src/modern/results.ts +10 -0
- package/src/pending-requests.ts +17 -1
- package/src/resources/definition.ts +92 -0
- package/src/resources/registry.ts +72 -0
- package/src/resources/requests.ts +59 -0
- package/src/resources/validation.ts +82 -0
- package/src/server.ts +11 -4
- package/src/tools/context.ts +5 -4
- package/src/tools/progress.ts +46 -8
- package/src/tools/rich-result.ts +53 -27
package/dist/tools/progress.js
CHANGED
|
@@ -5,15 +5,39 @@ const update = z.object({
|
|
|
5
5
|
total: z.number().finite().optional(),
|
|
6
6
|
message: z.string().optional(),
|
|
7
7
|
});
|
|
8
|
-
/**
|
|
9
|
-
export function
|
|
10
|
-
|
|
8
|
+
/** Tokens share the request-ID wire format, but have a separate lifetime. */
|
|
9
|
+
export function isProgressToken(token) {
|
|
10
|
+
return (typeof token === "string" ||
|
|
11
11
|
(typeof token === "number" &&
|
|
12
12
|
Number.isFinite(token) &&
|
|
13
|
-
Number.isInteger(token));
|
|
13
|
+
Number.isInteger(token)));
|
|
14
|
+
}
|
|
15
|
+
/** Cancellation must release the timer as well as the awaiting handler. */
|
|
16
|
+
function waitForProgress(milliseconds, signal) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const abort = () => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
reject(signal.reason);
|
|
21
|
+
};
|
|
22
|
+
const timer = setTimeout(() => {
|
|
23
|
+
signal.removeEventListener("abort", abort);
|
|
24
|
+
resolve();
|
|
25
|
+
}, milliseconds);
|
|
26
|
+
if (signal.aborted)
|
|
27
|
+
abort();
|
|
28
|
+
else
|
|
29
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** One invocation owns its reporter, ordering, and completion boundary. */
|
|
33
|
+
export function createProgressReporter(requestSignal, token, send) {
|
|
34
|
+
const lifetime = new AbortController();
|
|
35
|
+
const signal = AbortSignal.any([requestSignal, lifetime.signal]);
|
|
36
|
+
const validToken = isProgressToken(token);
|
|
14
37
|
let open = true;
|
|
15
38
|
let previous = Number.NEGATIVE_INFINITY;
|
|
16
39
|
let writes = Promise.resolve();
|
|
40
|
+
let lastSent = Number.NEGATIVE_INFINITY;
|
|
17
41
|
const report = (value) => {
|
|
18
42
|
if (!open || signal.aborted || !validToken || !send)
|
|
19
43
|
return Promise.resolve();
|
|
@@ -38,11 +62,20 @@ export function createProgressReporter(signal, token, send) {
|
|
|
38
62
|
},
|
|
39
63
|
};
|
|
40
64
|
const write = writes.then(async () => {
|
|
41
|
-
if (!signal.aborted)
|
|
65
|
+
if (!signal.aborted) {
|
|
66
|
+
// Pace each operation without dropping updates or bypassing backpressure.
|
|
67
|
+
let remaining = 100 - (performance.now() - lastSent);
|
|
68
|
+
while (remaining > 0) {
|
|
69
|
+
await waitForProgress(Math.ceil(remaining), signal);
|
|
70
|
+
remaining = 100 - (performance.now() - lastSent);
|
|
71
|
+
}
|
|
42
72
|
await abortable(Promise.resolve().then(() => {
|
|
43
|
-
if (!signal.aborted)
|
|
73
|
+
if (!signal.aborted) {
|
|
74
|
+
lastSent = performance.now();
|
|
44
75
|
return send(message);
|
|
76
|
+
}
|
|
45
77
|
}), signal);
|
|
78
|
+
}
|
|
46
79
|
});
|
|
47
80
|
// The awaiting handler observes failures; keep the queue usable for cleanup.
|
|
48
81
|
writes = write.catch(() => { });
|
|
@@ -56,6 +89,7 @@ export function createProgressReporter(signal, token, send) {
|
|
|
56
89
|
},
|
|
57
90
|
close() {
|
|
58
91
|
open = false;
|
|
92
|
+
lifetime.abort(new DOMException("Progress reporting closed", "AbortError"));
|
|
59
93
|
},
|
|
60
94
|
};
|
|
61
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAYxC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEH,
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ContentBlock, MetaObject } from "@toolgrain/protocol";
|
|
2
2
|
import type { DeepReadonly, JsonCompatible } from "./json-value.js";
|
|
3
3
|
/** Explicit MCP content and optional structured data, wrapped with `mcpResult()`. */
|
|
4
|
-
export interface McpResultOptions<Structured = unknown> {
|
|
4
|
+
export interface McpResultOptions<Structured = unknown, Meta extends MetaObject = MetaObject> {
|
|
5
5
|
/** Content blocks for the client; readonly authoring arrays are accepted. */
|
|
6
6
|
content: readonly DeepReadonly<ContentBlock>[];
|
|
7
7
|
/** JSON-compatible data; required for successful rich results with a declared output schema. */
|
|
@@ -9,35 +9,35 @@ export interface McpResultOptions<Structured = unknown> {
|
|
|
9
9
|
/** Marks an expected, client-visible tool failure; omitted or false indicates success. */
|
|
10
10
|
isError?: boolean;
|
|
11
11
|
/** Optional protocol result metadata, distinct from application structured data. */
|
|
12
|
-
_meta?:
|
|
12
|
+
_meta?: Meta;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Branded rich-result wrapper recognized by compatible Toolgrain package copies.
|
|
16
16
|
* Use `mcpResult()` to construct one; a plain object with the same fields remains application data.
|
|
17
17
|
*/
|
|
18
|
-
export declare class McpResult<Structured> {
|
|
18
|
+
export declare class McpResult<Structured, Meta extends MetaObject = MetaObject> {
|
|
19
19
|
/** Explicit MCP result envelope, before server validation and revision-specific conversion. */
|
|
20
|
-
readonly value: McpResultOptions<Structured>;
|
|
20
|
+
readonly value: McpResultOptions<Structured, Meta>;
|
|
21
21
|
constructor(
|
|
22
22
|
/** Explicit MCP result envelope, before server validation and revision-specific conversion. */
|
|
23
|
-
value: McpResultOptions<Structured>);
|
|
23
|
+
value: McpResultOptions<Structured, Meta>);
|
|
24
24
|
/** Structural brand shared by package copies; object spreads do not keep it. */
|
|
25
25
|
"~toolgrain"(): "result@1";
|
|
26
26
|
}
|
|
27
27
|
/** Recognize results created by any compatible copy of this package. */
|
|
28
28
|
export declare function isMcpResult(value: unknown): value is McpResult<unknown>;
|
|
29
29
|
/** Explicit, client-visible tool error; structured data may be omitted for text-only errors. */
|
|
30
|
-
export type McpErrorResult<Structured = never> = McpResult<Structured> & {
|
|
30
|
+
export type McpErrorResult<Structured = never, Meta extends MetaObject = MetaObject> = McpResult<Structured, Meta> & {
|
|
31
31
|
/** Envelope statically marked as an error. */
|
|
32
|
-
readonly value: McpResultOptions<Structured> & {
|
|
32
|
+
readonly value: McpResultOptions<Structured, Meta> & {
|
|
33
33
|
/** Explicit expected-failure marker, preserved in the MCP result. */
|
|
34
34
|
isError: true;
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
37
|
/** A result whose structured data is present, as required by an output schema. */
|
|
38
|
-
export type McpStructuredResult<Structured> = McpResult<Structured> & {
|
|
38
|
+
export type McpStructuredResult<Structured, Meta extends MetaObject = MetaObject> = McpResult<Structured, Meta> & {
|
|
39
39
|
/** Envelope with required application structured data. */
|
|
40
|
-
readonly value: McpResultOptions<Structured> & {
|
|
40
|
+
readonly value: McpResultOptions<Structured, Meta> & {
|
|
41
41
|
/** Structured value validated against the tool's output schema when one is declared. */
|
|
42
42
|
structuredContent: Structured;
|
|
43
43
|
};
|
|
@@ -48,45 +48,45 @@ export type McpStructuredResult<Structured> = McpResult<Structured> & {
|
|
|
48
48
|
* @param value - Error content and JSON-compatible data, parsed by any declared output schema.
|
|
49
49
|
* @returns A branded result typed as both an error and a structured result.
|
|
50
50
|
*/
|
|
51
|
-
export declare function mcpResult<Structured = never>(value: McpResultOptions<Structured> & {
|
|
51
|
+
export declare function mcpResult<Structured = never, const Meta extends MetaObject = MetaObject>(value: McpResultOptions<Structured, Meta> & {
|
|
52
52
|
/** Mark this result as an expected, client-visible tool failure. */
|
|
53
53
|
isError: true;
|
|
54
54
|
/** Structured error data, subject to output-schema validation when declared. */
|
|
55
55
|
structuredContent: Structured;
|
|
56
|
-
}): McpErrorResult<Structured> & McpStructuredResult<Structured>;
|
|
56
|
+
}): McpErrorResult<Structured, Meta> & McpStructuredResult<Structured, Meta>;
|
|
57
57
|
/**
|
|
58
58
|
* Wrap a text/content-only public tool error, allowed even when the tool declares output.
|
|
59
59
|
* @param value - Explicit error content without structured application data.
|
|
60
60
|
* @returns A branded error result whose `isError` flag is statically true.
|
|
61
61
|
*/
|
|
62
|
-
export declare function mcpResult(value: McpResultOptions<never> & {
|
|
62
|
+
export declare function mcpResult<Structured = never, const Meta extends MetaObject = MetaObject>(value: McpResultOptions<never, Meta> & {
|
|
63
63
|
/** Mark this result as an expected, client-visible tool failure. */
|
|
64
64
|
isError: true;
|
|
65
|
-
}): McpErrorResult<
|
|
65
|
+
}): McpErrorResult<Structured, Meta>;
|
|
66
66
|
/**
|
|
67
67
|
* Wrap a public tool error with an optional structured-data contract.
|
|
68
68
|
* @param value - Error content and any JSON-compatible structured data.
|
|
69
69
|
* @returns A branded error result; provided structured data still undergoes output validation.
|
|
70
70
|
*/
|
|
71
|
-
export declare function mcpResult<Structured>(value: McpResultOptions<Structured> & {
|
|
71
|
+
export declare function mcpResult<Structured, const Meta extends MetaObject = MetaObject>(value: McpResultOptions<Structured, Meta> & {
|
|
72
72
|
/** Mark this result as an expected, client-visible tool failure. */
|
|
73
73
|
isError: true;
|
|
74
|
-
}): McpErrorResult<Structured>;
|
|
74
|
+
}): McpErrorResult<Structured, Meta>;
|
|
75
75
|
/**
|
|
76
76
|
* Wrap rich content with required structured application data.
|
|
77
77
|
* @param value - Client content and JSON-compatible data; a successful output-schema result needs both.
|
|
78
78
|
* @returns A branded structured result; the server validates and converts it for the selected revision.
|
|
79
79
|
*/
|
|
80
|
-
export declare function mcpResult<Structured>(value: McpResultOptions<Structured> & {
|
|
80
|
+
export declare function mcpResult<Structured, const Meta extends MetaObject = MetaObject>(value: McpResultOptions<Structured, Meta> & {
|
|
81
81
|
/** Required application data, parsed by the declared output schema when present. */
|
|
82
82
|
structuredContent: Structured;
|
|
83
|
-
}): McpStructuredResult<Structured>;
|
|
83
|
+
}): McpStructuredResult<Structured, Meta>;
|
|
84
84
|
/**
|
|
85
85
|
* Wrap explicit client content without structured application data.
|
|
86
86
|
* @param value - Rich content; successful results of this form require a tool without an output schema.
|
|
87
87
|
* @returns A branded MCP result. Ordinary result-shaped objects are not treated as rich results.
|
|
88
88
|
*/
|
|
89
|
-
export declare function mcpResult(value: McpResultOptions<never>): McpResult<
|
|
89
|
+
export declare function mcpResult<Structured = never, const Meta extends MetaObject = MetaObject>(value: McpResultOptions<never, Meta>): McpResult<Structured, Meta>;
|
|
90
90
|
/**
|
|
91
91
|
* Explicitly return MCP content instead of ordinary application JSON.
|
|
92
92
|
* The server validates the envelope, parses supplied output data, and converts it for the protocol revision.
|
|
@@ -94,5 +94,5 @@ export declare function mcpResult(value: McpResultOptions<never>): McpResult<nev
|
|
|
94
94
|
* @param value - Content blocks, optional structured data, an error marker, and protocol metadata.
|
|
95
95
|
* @returns A frozen result wrapper; content is checked during server invocation.
|
|
96
96
|
*/
|
|
97
|
-
export declare function mcpResult<Structured = never>(value: McpResultOptions<Structured>): McpResult<Structured>;
|
|
97
|
+
export declare function mcpResult<Structured = never, const Meta extends MetaObject = MetaObject>(value: McpResultOptions<Structured, Meta>): McpResult<Structured, Meta>;
|
|
98
98
|
//# sourceMappingURL=rich-result.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-result.d.ts","sourceRoot":"","sources":["../../src/tools/rich-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEpE,qFAAqF;AACrF,MAAM,WAAW,gBAAgB,
|
|
1
|
+
{"version":3,"file":"rich-result.d.ts","sourceRoot":"","sources":["../../src/tools/rich-result.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-result.js","sourceRoot":"","sources":["../../src/tools/rich-result.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rich-result.js","sourceRoot":"","sources":["../../src/tools/rich-result.ts"],"names":[],"mappings":"AAkBA,kFAAkF;AAClF,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;AAE3D;;;GAGG;AACH,MAAM,OAAO,SAAS;IAGT,KAAK;IAFhB;IACE,+FAA+F;IACtF,KAAyC;qBAAzC,KAAK;QAEd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IAED,gFAAgF;IAChF,YAAY;QACV,OAAO,UAAU,CAAC;IACpB,CAAC;CACF;AACD,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAE1E,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAsC,CAAC,YAAY,CAAC,KAAK,IAAI,CAC/D,CAAC;AACJ,CAAC;AAyGD,MAAM,UAAU,SAAS,CAGvB,KAAyC;IACzC,OAAO,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolgrain/mcp",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.2",
|
|
4
4
|
"description": "Define reusable, typed MCP tools and servers with Zod validation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@toolgrain/repo-tools": "0.0.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@toolgrain/protocol": "0.3.0-alpha.
|
|
62
|
+
"@toolgrain/protocol": "0.3.0-alpha.2"
|
|
63
63
|
},
|
|
64
64
|
"engines": {
|
|
65
65
|
"node": "^24.20.0",
|
package/src/apps.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { APPS_EXTENSION_ID, APPS_MIME_TYPE } from "@toolgrain/protocol/apps";
|
|
2
|
+
import type { ToolContext } from "./tools/context.js";
|
|
3
|
+
|
|
4
|
+
/** Advisory client claim; never an authorization decision. */
|
|
5
|
+
export type AppSupport = "supported" | "not-advertised" | "unknown";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Read the current request's MCP Apps claim. Legacy independent HTTP has no
|
|
9
|
+
* retained client capabilities and returns unknown. A claim is not verified.
|
|
10
|
+
*/
|
|
11
|
+
export function getAppSupport(ctx: ToolContext): AppSupport {
|
|
12
|
+
const capabilities = ctx.request.client?.capabilities;
|
|
13
|
+
if (!capabilities) return "unknown";
|
|
14
|
+
const extensions = (capabilities as { extensions?: unknown }).extensions;
|
|
15
|
+
if (typeof extensions !== "object" || extensions === null)
|
|
16
|
+
return "not-advertised";
|
|
17
|
+
const ui = (extensions as Record<string, unknown>)[APPS_EXTENSION_ID];
|
|
18
|
+
if (typeof ui !== "object" || ui === null) return "not-advertised";
|
|
19
|
+
const mimeTypes = (ui as { mimeTypes?: unknown }).mimeTypes;
|
|
20
|
+
return Array.isArray(mimeTypes) && mimeTypes.includes(APPS_MIME_TYPE)
|
|
21
|
+
? "supported"
|
|
22
|
+
: "not-advertised";
|
|
23
|
+
}
|
package/src/connection.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
INVALID_PARAMS,
|
|
2
3
|
INVALID_REQUEST,
|
|
3
4
|
PARSE_ERROR,
|
|
4
5
|
type ProgressNotification,
|
|
@@ -13,6 +14,7 @@ import { decode } from "./protocol/decode.js";
|
|
|
13
14
|
import type { Request } from "./protocol/messages.js";
|
|
14
15
|
import { protocolError } from "./protocol/responses.js";
|
|
15
16
|
import type { Server } from "./server.js";
|
|
17
|
+
import { isProgressToken } from "./tools/progress.js";
|
|
16
18
|
import type {
|
|
17
19
|
RequestTransport,
|
|
18
20
|
Transport,
|
|
@@ -189,6 +191,19 @@ class Connection implements ConnectionLifecycle {
|
|
|
189
191
|
);
|
|
190
192
|
return;
|
|
191
193
|
}
|
|
194
|
+
const token = (request.params?._meta as Record<string, unknown> | undefined)
|
|
195
|
+
?.progressToken;
|
|
196
|
+
if (isProgressToken(token) && !pending.claimProgress(token)) {
|
|
197
|
+
pending.finish();
|
|
198
|
+
await this.send(
|
|
199
|
+
protocolError(
|
|
200
|
+
INVALID_PARAMS,
|
|
201
|
+
"Progress token is already in use",
|
|
202
|
+
request.id,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
192
207
|
const initialization = this.legacy.begin(request, prepared.protocolVersion);
|
|
193
208
|
try {
|
|
194
209
|
const response = await executeRequest(
|
package/src/dispatcher.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { errorResponse as legacyErrorResponse } from "./legacy/responses.js";
|
|
|
16
16
|
import { createModernDispatcher } from "./modern/dispatcher.js";
|
|
17
17
|
import type { Request } from "./protocol/messages.js";
|
|
18
18
|
import { validateVersionContext } from "./protocol/version-metadata.js";
|
|
19
|
+
import type { ResourceRegistry } from "./resources/registry.js";
|
|
19
20
|
import type { ToolAdapter } from "./tools/adapter.js";
|
|
20
21
|
import type { ToolClient, ToolContext } from "./tools/context.js";
|
|
21
22
|
|
|
@@ -41,18 +42,21 @@ export function createDispatcher(
|
|
|
41
42
|
identity: Implementation,
|
|
42
43
|
instructions?: string,
|
|
43
44
|
providers?: ContextRegistry,
|
|
45
|
+
resources?: ResourceRegistry,
|
|
44
46
|
): Dispatcher {
|
|
45
47
|
const legacy = createLegacyDispatcher(
|
|
46
48
|
tools,
|
|
47
49
|
identity,
|
|
48
50
|
instructions,
|
|
49
51
|
providers,
|
|
52
|
+
resources,
|
|
50
53
|
);
|
|
51
54
|
const modern = createModernDispatcher(
|
|
52
55
|
tools,
|
|
53
56
|
identity,
|
|
54
57
|
instructions,
|
|
55
58
|
providers,
|
|
59
|
+
resources,
|
|
56
60
|
);
|
|
57
61
|
return (request, scope, protocolVersion) => {
|
|
58
62
|
validateVersionContext(request, protocolVersion);
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export type { AppSupport } from "./apps.js";
|
|
2
|
+
export { getAppSupport } from "./apps.js";
|
|
1
3
|
export { validateTokenAudience } from "./auth/audience.js";
|
|
2
4
|
export { createAuthAccessor } from "./auth/context.js";
|
|
3
5
|
export type { AuthErrorCode } from "./auth/errors.js";
|
|
@@ -14,6 +16,13 @@ export { authUrl as validateAuthUrl } from "./auth/url.js";
|
|
|
14
16
|
export type { ContextProvider } from "./context/definition.js";
|
|
15
17
|
export { createContext } from "./context/definition.js";
|
|
16
18
|
export type { ToolErrorDetails } from "./diagnostics.js";
|
|
19
|
+
export type {
|
|
20
|
+
Resource,
|
|
21
|
+
ResourceDefinition,
|
|
22
|
+
ResourceMetadata,
|
|
23
|
+
ResourceReadResult,
|
|
24
|
+
} from "./resources/definition.js";
|
|
25
|
+
export { resource } from "./resources/definition.js";
|
|
17
26
|
export type { HandleOptions, Server, ServerOptions } from "./server.js";
|
|
18
27
|
export { createServer } from "./server.js";
|
|
19
28
|
export type {
|
package/src/legacy/dispatcher.ts
CHANGED
|
@@ -10,7 +10,15 @@ import type { RequestScope } from "../dispatcher.js";
|
|
|
10
10
|
import type { Request } from "../protocol/messages.js";
|
|
11
11
|
import { ProtocolError } from "../protocol/responses.js";
|
|
12
12
|
import { hasProtocolVersionMetadata } from "../protocol/version-metadata.js";
|
|
13
|
+
import type { ResourceRegistry } from "../resources/registry.js";
|
|
14
|
+
import {
|
|
15
|
+
listResources,
|
|
16
|
+
listResourceTemplates,
|
|
17
|
+
readResource,
|
|
18
|
+
requireResources,
|
|
19
|
+
} from "../resources/requests.js";
|
|
13
20
|
import type { ToolAdapter } from "../tools/adapter.js";
|
|
21
|
+
import type { ToolContext } from "../tools/context.js";
|
|
14
22
|
import { createToolContext } from "../tools/context.js";
|
|
15
23
|
import { callTool, initialize, listTools, ping } from "./handlers.js";
|
|
16
24
|
import { successResponse } from "./responses.js";
|
|
@@ -33,15 +41,36 @@ export function createLegacyDispatcher(
|
|
|
33
41
|
identity: Implementation,
|
|
34
42
|
instructions?: string,
|
|
35
43
|
providers?: ContextRegistry,
|
|
44
|
+
resources?: ResourceRegistry,
|
|
36
45
|
) {
|
|
37
46
|
return async (
|
|
38
47
|
request: Request,
|
|
39
48
|
scope: RequestScope,
|
|
40
49
|
): Promise<JSONRPCResponse | undefined> => {
|
|
50
|
+
const context = (params: {
|
|
51
|
+
_meta?: Record<string, unknown> | undefined;
|
|
52
|
+
}): ToolContext =>
|
|
53
|
+
createToolContext(
|
|
54
|
+
scope.signal,
|
|
55
|
+
{
|
|
56
|
+
id: request.id,
|
|
57
|
+
protocolVersion: LEGACY_PROTOCOL_VERSION,
|
|
58
|
+
...(scope.client === undefined ? {} : { client: scope.client }),
|
|
59
|
+
meta: params._meta ?? {},
|
|
60
|
+
},
|
|
61
|
+
scope.auth,
|
|
62
|
+
providers,
|
|
63
|
+
scope.reportProgress,
|
|
64
|
+
);
|
|
41
65
|
let result: Result | undefined;
|
|
42
66
|
switch (request.method) {
|
|
43
67
|
case "initialize":
|
|
44
|
-
result = initialize(
|
|
68
|
+
result = initialize(
|
|
69
|
+
request.params,
|
|
70
|
+
identity,
|
|
71
|
+
instructions,
|
|
72
|
+
resources !== undefined,
|
|
73
|
+
);
|
|
45
74
|
break;
|
|
46
75
|
case "ping":
|
|
47
76
|
result = ping(request.params);
|
|
@@ -50,19 +79,32 @@ export function createLegacyDispatcher(
|
|
|
50
79
|
result = listTools(request.params, tools);
|
|
51
80
|
break;
|
|
52
81
|
case "tools/call":
|
|
53
|
-
result = await callTool(request.params, tools,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
providers,
|
|
64
|
-
scope.reportProgress,
|
|
82
|
+
result = await callTool(request.params, tools, context);
|
|
83
|
+
break;
|
|
84
|
+
case "resources/list": {
|
|
85
|
+
const listed = listResources(
|
|
86
|
+
request.params,
|
|
87
|
+
requireResources(resources),
|
|
88
|
+
);
|
|
89
|
+
result = {
|
|
90
|
+
resources: listed.resources.map(
|
|
91
|
+
({ icons: _icons, ...entry }) => entry,
|
|
65
92
|
),
|
|
93
|
+
};
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case "resources/templates/list":
|
|
97
|
+
result = listResourceTemplates(
|
|
98
|
+
request.params,
|
|
99
|
+
requireResources(resources),
|
|
100
|
+
);
|
|
101
|
+
break;
|
|
102
|
+
case "resources/read":
|
|
103
|
+
result = await readResource(
|
|
104
|
+
request.params,
|
|
105
|
+
requireResources(resources),
|
|
106
|
+
context,
|
|
107
|
+
-32002,
|
|
66
108
|
);
|
|
67
109
|
break;
|
|
68
110
|
default:
|
package/src/legacy/handlers.ts
CHANGED
|
@@ -24,6 +24,7 @@ export function initialize(
|
|
|
24
24
|
params: unknown,
|
|
25
25
|
identity: Implementation,
|
|
26
26
|
instructions?: string,
|
|
27
|
+
resources = false,
|
|
27
28
|
): InitializeResult {
|
|
28
29
|
if (!initializeParams.safeParse(params).success)
|
|
29
30
|
throw new ProtocolError(
|
|
@@ -32,7 +33,7 @@ export function initialize(
|
|
|
32
33
|
);
|
|
33
34
|
return {
|
|
34
35
|
protocolVersion: LEGACY_PROTOCOL_VERSION,
|
|
35
|
-
capabilities: { tools: {} },
|
|
36
|
+
capabilities: { tools: {}, ...(resources ? { resources: {} } : {}) },
|
|
36
37
|
serverInfo: { ...identity },
|
|
37
38
|
...(instructions === undefined ? {} : { instructions }),
|
|
38
39
|
};
|
package/src/modern/dispatcher.ts
CHANGED
|
@@ -13,17 +13,27 @@ import type { ContextRegistry } from "../context/registry.js";
|
|
|
13
13
|
import type { RequestScope } from "../dispatcher.js";
|
|
14
14
|
import type { Request } from "../protocol/messages.js";
|
|
15
15
|
import { ProtocolError } from "../protocol/responses.js";
|
|
16
|
+
import type { ResourceRegistry } from "../resources/registry.js";
|
|
17
|
+
import {
|
|
18
|
+
listResources,
|
|
19
|
+
listResourceTemplates,
|
|
20
|
+
readResource,
|
|
21
|
+
requireResources,
|
|
22
|
+
} from "../resources/requests.js";
|
|
16
23
|
import type { ToolAdapter } from "../tools/adapter.js";
|
|
24
|
+
import type { ToolContext } from "../tools/context.js";
|
|
17
25
|
import { createToolContext, toolClient } from "../tools/context.js";
|
|
18
26
|
import { callTool, discover, listTools } from "./handlers.js";
|
|
19
27
|
import { metadata } from "./metadata.js";
|
|
20
28
|
import { successResponse } from "./responses.js";
|
|
29
|
+
import { resourceResult } from "./results.js";
|
|
21
30
|
|
|
22
31
|
export function createModernDispatcher(
|
|
23
32
|
tools: ToolAdapter,
|
|
24
33
|
identity: Implementation,
|
|
25
34
|
instructions?: string,
|
|
26
35
|
providers?: ContextRegistry,
|
|
36
|
+
resources?: ResourceRegistry,
|
|
27
37
|
) {
|
|
28
38
|
return async (
|
|
29
39
|
request: Request,
|
|
@@ -39,37 +49,63 @@ export function createModernDispatcher(
|
|
|
39
49
|
"Unsupported protocol version",
|
|
40
50
|
{ supported: [...SUPPORTED_PROTOCOL_VERSIONS], requested: revision },
|
|
41
51
|
);
|
|
52
|
+
const context = (params: {
|
|
53
|
+
_meta?: Record<string, unknown> | undefined;
|
|
54
|
+
}): ToolContext =>
|
|
55
|
+
createToolContext(
|
|
56
|
+
scope.signal,
|
|
57
|
+
{
|
|
58
|
+
id: request.id,
|
|
59
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
60
|
+
client: toolClient(
|
|
61
|
+
parsed.data["io.modelcontextprotocol/clientInfo"] as
|
|
62
|
+
| Implementation
|
|
63
|
+
| undefined,
|
|
64
|
+
parsed.data[
|
|
65
|
+
"io.modelcontextprotocol/clientCapabilities"
|
|
66
|
+
] as ClientCapabilities,
|
|
67
|
+
),
|
|
68
|
+
meta: params._meta ?? {},
|
|
69
|
+
},
|
|
70
|
+
scope.auth,
|
|
71
|
+
providers,
|
|
72
|
+
scope.reportProgress,
|
|
73
|
+
);
|
|
42
74
|
let result: Result | undefined;
|
|
43
75
|
switch (request.method) {
|
|
44
76
|
case "server/discover":
|
|
45
|
-
result = discover(
|
|
77
|
+
result = discover(
|
|
78
|
+
request.params,
|
|
79
|
+
instructions,
|
|
80
|
+
resources !== undefined,
|
|
81
|
+
);
|
|
46
82
|
break;
|
|
47
83
|
case "tools/list":
|
|
48
84
|
result = listTools(request.params, tools);
|
|
49
85
|
break;
|
|
50
86
|
case "tools/call":
|
|
51
|
-
result = await callTool(request.params, tools,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
scope.reportProgress,
|
|
70
|
-
),
|
|
87
|
+
result = await callTool(request.params, tools, context);
|
|
88
|
+
break;
|
|
89
|
+
case "resources/list":
|
|
90
|
+
result = resourceResult(
|
|
91
|
+
listResources(request.params, requireResources(resources)),
|
|
92
|
+
);
|
|
93
|
+
break;
|
|
94
|
+
case "resources/templates/list":
|
|
95
|
+
result = resourceResult(
|
|
96
|
+
listResourceTemplates(request.params, requireResources(resources)),
|
|
97
|
+
);
|
|
98
|
+
break;
|
|
99
|
+
case "resources/read": {
|
|
100
|
+
const value = await readResource(
|
|
101
|
+
request.params,
|
|
102
|
+
requireResources(resources),
|
|
103
|
+
context,
|
|
104
|
+
INVALID_PARAMS,
|
|
71
105
|
);
|
|
106
|
+
result = value === undefined ? undefined : resourceResult(value);
|
|
72
107
|
break;
|
|
108
|
+
}
|
|
73
109
|
default:
|
|
74
110
|
throw new ProtocolError(METHOD_NOT_FOUND, "Method not found");
|
|
75
111
|
}
|
package/src/modern/handlers.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { toolResult } from "./results.js";
|
|
|
14
14
|
export function discover(
|
|
15
15
|
params: unknown,
|
|
16
16
|
instructions?: string,
|
|
17
|
+
resources = false,
|
|
17
18
|
): DiscoverResult {
|
|
18
19
|
if (!commonParams.safeParse(params).success)
|
|
19
20
|
throw new ProtocolError(INVALID_PARAMS, "Invalid discovery parameters");
|
|
@@ -22,7 +23,7 @@ export function discover(
|
|
|
22
23
|
ttlMs: 0,
|
|
23
24
|
cacheScope: "private",
|
|
24
25
|
supportedVersions: [...SUPPORTED_PROTOCOL_VERSIONS],
|
|
25
|
-
capabilities: { tools: {} },
|
|
26
|
+
capabilities: { tools: {}, ...(resources ? { resources: {} } : {}) },
|
|
26
27
|
...(instructions === undefined ? {} : { instructions }),
|
|
27
28
|
};
|
|
28
29
|
}
|
package/src/modern/results.ts
CHANGED
|
@@ -4,3 +4,13 @@ import type { ToolExecutionResult } from "../tools/results.js";
|
|
|
4
4
|
export function toolResult(result: ToolExecutionResult): CallToolResult {
|
|
5
5
|
return { resultType: "complete", ...result };
|
|
6
6
|
}
|
|
7
|
+
|
|
8
|
+
/** Resource results do not enable caching until applications have a cache-policy API. */
|
|
9
|
+
export function resourceResult<Result extends object>(result: Result) {
|
|
10
|
+
return {
|
|
11
|
+
...result,
|
|
12
|
+
resultType: "complete" as const,
|
|
13
|
+
ttlMs: 0,
|
|
14
|
+
cacheScope: "private" as const,
|
|
15
|
+
};
|
|
16
|
+
}
|
package/src/pending-requests.ts
CHANGED
|
@@ -1,17 +1,33 @@
|
|
|
1
|
-
import type { RequestId } from "@toolgrain/protocol";
|
|
1
|
+
import type { ProgressToken, RequestId } from "@toolgrain/protocol";
|
|
2
2
|
|
|
3
3
|
/** Owns duplicate detection and cancellation signals for in-flight requests. */
|
|
4
4
|
export function createPendingRequests(isOpen: () => boolean) {
|
|
5
5
|
const pending = new Map<RequestId, AbortController>();
|
|
6
|
+
const progressTokens = new Set<ProgressToken>();
|
|
6
7
|
return {
|
|
7
8
|
begin(id: RequestId) {
|
|
8
9
|
if (pending.has(id)) return;
|
|
9
10
|
const controller = new AbortController();
|
|
10
11
|
pending.set(id, controller);
|
|
12
|
+
let releaseProgress: (() => void) | undefined;
|
|
11
13
|
return {
|
|
12
14
|
signal: controller.signal,
|
|
13
15
|
isActive: () => isOpen() && !controller.signal.aborted,
|
|
16
|
+
claimProgress(token: ProgressToken) {
|
|
17
|
+
if (progressTokens.has(token)) return false;
|
|
18
|
+
progressTokens.add(token);
|
|
19
|
+
releaseProgress = () => {
|
|
20
|
+
progressTokens.delete(token);
|
|
21
|
+
controller.signal.removeEventListener("abort", release);
|
|
22
|
+
releaseProgress = undefined;
|
|
23
|
+
};
|
|
24
|
+
// Aborting releases the token even if the cancelled handler keeps running.
|
|
25
|
+
const release = () => releaseProgress?.();
|
|
26
|
+
controller.signal.addEventListener("abort", release, { once: true });
|
|
27
|
+
return true;
|
|
28
|
+
},
|
|
14
29
|
finish: () => {
|
|
30
|
+
releaseProgress?.();
|
|
15
31
|
pending.delete(id);
|
|
16
32
|
},
|
|
17
33
|
};
|