toolcraft 0.0.231 → 0.0.233
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/composition.json +3 -3
- package/dist/applied-default.d.ts +3 -0
- package/dist/applied-default.js +16 -0
- package/dist/cli.js +508 -204
- package/dist/composition.json +3 -3
- package/dist/discriminator.d.ts +9 -0
- package/dist/discriminator.js +32 -0
- package/dist/error-report.d.ts +2 -18
- package/dist/error-report.js +12 -14
- package/dist/http.js +8 -1
- package/dist/human-in-loop/gate.js +6 -0
- package/dist/index.compile-check.js +22 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.js +50 -0
- package/dist/json-schema-converter.d.ts +14 -6
- package/dist/json-schema-converter.js +150 -20
- package/dist/mcp-proxy-metadata.d.ts +2 -0
- package/dist/mcp-proxy-metadata.js +13 -0
- package/dist/mcp-proxy.d.ts +1 -0
- package/dist/mcp-proxy.js +65 -47
- package/dist/mcp-result.d.ts +3 -0
- package/dist/mcp-result.js +10 -0
- package/dist/mcp.js +211 -153
- package/dist/redaction.js +26 -17
- package/dist/renderer.js +40 -29
- package/dist/schema-member-names.d.ts +2 -0
- package/dist/schema-member-names.js +39 -0
- package/dist/schema-scope.d.ts +1 -0
- package/dist/schema-scope.js +29 -16
- package/dist/schema.d.ts +1 -1
- package/dist/sdk.d.ts +2 -1
- package/dist/sdk.js +164 -50
- package/dist/stream.js +24 -23
- package/dist/testing/diagnostic-value.d.ts +1 -0
- package/dist/testing/diagnostic-value.js +57 -0
- package/dist/testing/fakes.js +2 -1
- package/dist/testing/harness.js +1 -0
- package/dist/testing/memory-fs.js +42 -1
- package/dist/testing/parity.js +50 -6
- package/dist/union-validation.d.ts +5 -0
- package/dist/union-validation.js +36 -0
- package/node_modules/@poe-code/process-runner/dist/types.d.ts +2 -0
- package/node_modules/mcp-oauth-server/dist/index.js +47 -6
- package/node_modules/tiny-http-mcp-server/dist/cli.js +14 -2
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/dist/http-server.d.ts +7 -5
- package/node_modules/tiny-http-mcp-server/dist/http-server.js +3 -11
- package/node_modules/tiny-http-mcp-server/dist/http-transport.d.ts +6 -1
- package/node_modules/tiny-http-mcp-server/dist/http-transport.js +196 -15
- package/node_modules/tiny-http-mcp-server/dist/index.d.ts +2 -2
- package/node_modules/tiny-http-mcp-server/dist/modern-headers.d.ts +3 -0
- package/node_modules/tiny-http-mcp-server/dist/modern-headers.js +35 -0
- package/node_modules/tiny-http-mcp-server/dist/parse-body.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/dist/parse-body.js +9 -5
- package/node_modules/tiny-http-mcp-server/dist/test-support.js +17 -0
- package/node_modules/tiny-http-mcp-server/dist/testing.d.ts +1 -1
- package/node_modules/tiny-http-mcp-server/dist/testing.js +3 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +21 -4
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +14 -6
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-fetch.js +8 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-response.d.ts +1 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/http-response.js +50 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.d.ts +2 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/index.js +2 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +21 -3
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/README.md +1 -0
- package/node_modules/tiny-mcp-client/dist/index.d.ts +178 -101
- package/node_modules/tiny-mcp-client/dist/index.js +4696 -291
- package/node_modules/tiny-mcp-client/package.json +2 -1
- package/node_modules/tiny-stdio-mcp-server/dist/base64.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/base64.js +5 -0
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/content/audio.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/content/convert.d.ts +5 -1
- package/node_modules/tiny-stdio-mcp-server/dist/content/convert.js +41 -6
- package/node_modules/tiny-stdio-mcp-server/dist/content/file.d.ts +5 -0
- package/node_modules/tiny-stdio-mcp-server/dist/content/image.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/headers.d.ts +10 -0
- package/node_modules/tiny-stdio-mcp-server/dist/headers.js +180 -0
- package/node_modules/tiny-stdio-mcp-server/dist/index.d.ts +5 -5
- package/node_modules/tiny-stdio-mcp-server/dist/index.js +1 -1
- package/node_modules/tiny-stdio-mcp-server/dist/input-request-schema.d.ts +220 -0
- package/node_modules/tiny-stdio-mcp-server/dist/input-request-schema.js +1995 -0
- package/node_modules/tiny-stdio-mcp-server/dist/jsonrpc.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/jsonrpc.js +38 -25
- package/node_modules/tiny-stdio-mcp-server/dist/metadata.d.ts +2 -0
- package/node_modules/tiny-stdio-mcp-server/dist/metadata.js +37 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol-validation.d.ts +4 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol-validation.js +53 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol.d.ts +17 -0
- package/node_modules/tiny-stdio-mcp-server/dist/protocol.js +228 -0
- package/node_modules/tiny-stdio-mcp-server/dist/request-cancellation.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/request-cancellation.js +18 -0
- package/node_modules/tiny-stdio-mcp-server/dist/sampling-messages.d.ts +18 -0
- package/node_modules/tiny-stdio-mcp-server/dist/sampling-messages.js +34 -0
- package/node_modules/tiny-stdio-mcp-server/dist/schema.d.ts +4 -1
- package/node_modules/tiny-stdio-mcp-server/dist/server.d.ts +9 -4
- package/node_modules/tiny-stdio-mcp-server/dist/server.js +334 -135
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-input.d.ts +3 -0
- package/node_modules/tiny-stdio-mcp-server/dist/stdio-input.js +50 -28
- package/node_modules/tiny-stdio-mcp-server/dist/subscriptions.d.ts +11 -0
- package/node_modules/tiny-stdio-mcp-server/dist/subscriptions.js +128 -0
- package/node_modules/tiny-stdio-mcp-server/dist/types.d.ts +58 -18
- package/node_modules/tiny-stdio-mcp-server/dist/types.js +2 -1
- package/node_modules/tiny-stdio-mcp-server/dist/uri.d.ts +1 -0
- package/node_modules/tiny-stdio-mcp-server/dist/uri.js +41 -0
- package/node_modules/tiny-stdio-mcp-server/package.json +8 -0
- package/node_modules/toolcraft-design/dist/command-registry.d.ts +11 -0
- package/node_modules/toolcraft-design/dist/command-registry.js +24 -0
- package/node_modules/toolcraft-design/dist/dashboard/ansi.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/ansi.js +81 -27
- package/node_modules/toolcraft-design/dist/dashboard/buffer.js +39 -25
- package/node_modules/toolcraft-design/dist/dashboard/components/border.js +6 -5
- package/node_modules/toolcraft-design/dist/dashboard/components/footer.js +26 -21
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.d.ts +1 -1
- package/node_modules/toolcraft-design/dist/dashboard/components/output-pane.js +12 -6
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.d.ts +2 -1
- package/node_modules/toolcraft-design/dist/dashboard/components/stats-pane.js +51 -10
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.d.ts +4 -0
- package/node_modules/toolcraft-design/dist/dashboard/dashboard.js +77 -4
- package/node_modules/toolcraft-design/dist/dashboard/index.d.ts +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/index.js +2 -0
- package/node_modules/toolcraft-design/dist/dashboard/keymap.js +26 -6
- package/node_modules/toolcraft-design/dist/dashboard/layout.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/layout.js +19 -3
- package/node_modules/toolcraft-design/dist/dashboard/line-buffer.d.ts +9 -0
- package/node_modules/toolcraft-design/dist/dashboard/line-buffer.js +63 -0
- package/node_modules/toolcraft-design/dist/dashboard/output-preview.d.ts +9 -0
- package/node_modules/toolcraft-design/dist/dashboard/output-preview.js +70 -0
- package/node_modules/toolcraft-design/dist/dashboard/snapshot.js +3 -1
- package/node_modules/toolcraft-design/dist/dashboard/store.js +9 -3
- package/node_modules/toolcraft-design/dist/dashboard/terminal-strings.d.ts +6 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-strings.js +108 -0
- package/node_modules/toolcraft-design/dist/dashboard/terminal-width.js +3 -1
- package/node_modules/toolcraft-design/dist/dashboard/terminal.js +11 -1
- package/node_modules/toolcraft-design/dist/dashboard/testing/pipeline-scenario.d.ts +1 -0
- package/node_modules/toolcraft-design/dist/dashboard/testing/pipeline-scenario.js +126 -0
- package/node_modules/toolcraft-design/dist/dashboard/types.d.ts +3 -1
- package/node_modules/toolcraft-design/dist/event-groups.d.ts +19 -0
- package/node_modules/toolcraft-design/dist/event-groups.js +49 -0
- package/node_modules/toolcraft-design/dist/index.d.ts +6 -1
- package/node_modules/toolcraft-design/dist/index.js +5 -1
- package/node_modules/toolcraft-design/dist/inline-notice.d.ts +14 -0
- package/node_modules/toolcraft-design/dist/inline-notice.js +28 -0
- package/node_modules/toolcraft-design/dist/metric.d.ts +8 -0
- package/node_modules/toolcraft-design/dist/metric.js +21 -0
- package/node_modules/toolcraft-design/dist/overlay-manager.d.ts +6 -0
- package/node_modules/toolcraft-design/dist/overlay-manager.js +10 -0
- package/node_modules/toolcraft-design/dist/primitives.d.ts +12 -0
- package/node_modules/toolcraft-design/dist/primitives.js +7 -0
- package/node_modules/toolcraft-design/dist/progress-group.d.ts +7 -0
- package/node_modules/toolcraft-design/dist/progress-group.js +10 -0
- package/node_modules/toolcraft-design/dist/render-performance.d.ts +33 -0
- package/node_modules/toolcraft-design/dist/render-performance.js +71 -0
- package/node_modules/toolcraft-design/dist/task-tree.d.ts +22 -0
- package/node_modules/toolcraft-design/dist/task-tree.js +79 -0
- package/node_modules/toolcraft-design/dist/viewport.d.ts +18 -0
- package/node_modules/toolcraft-design/dist/viewport.js +56 -0
- package/node_modules/toolcraft-schema/dist/clone-default.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/clone-default.js +26 -0
- package/node_modules/toolcraft-schema/dist/index.compile-check.js +1 -0
- package/node_modules/toolcraft-schema/dist/index.d.ts +18 -9
- package/node_modules/toolcraft-schema/dist/index.js +46 -8
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/compiler.js +6 -0
- package/node_modules/toolcraft-schema/dist/json-schema/evaluate.js +6 -3
- package/node_modules/toolcraft-schema/dist/json-schema/normalize-nullability.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json-schema/normalize-nullability.js +69 -0
- package/node_modules/toolcraft-schema/dist/json-schema/types.d.ts +1 -0
- package/node_modules/toolcraft-schema/dist/json.d.ts +4 -1
- package/node_modules/toolcraft-schema/dist/json.js +57 -1
- package/node_modules/toolcraft-schema/dist/native-json-schema.d.ts +10 -0
- package/node_modules/toolcraft-schema/dist/native-json-schema.js +11 -0
- package/node_modules/toolcraft-schema/dist/validate.d.ts +5 -1
- package/node_modules/toolcraft-schema/dist/validate.js +61 -47
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +6 -3
|
@@ -1,14 +1,46 @@
|
|
|
1
1
|
import * as readline from "readline";
|
|
2
|
-
import { compileJsonSchema, formatIssues } from "toolcraft-schema";
|
|
2
|
+
import { compileJsonSchema, formatIssues, normalizeLegacyNullability } from "toolcraft-schema";
|
|
3
3
|
import { JSON_RPC_ERROR_CODES, ToolError } from "./types.js";
|
|
4
|
-
import { parseMessage, formatSuccessResponse, formatErrorResponse } from "./jsonrpc.js";
|
|
4
|
+
import { parseMessage, formatSuccessResponse, formatErrorResponse, isRequestId } from "./jsonrpc.js";
|
|
5
5
|
import { parseUriTemplate } from "./uri-template.js";
|
|
6
6
|
import { toContentBlocks } from "./content/convert.js";
|
|
7
7
|
import { ToolCallAdmission } from "./tool-call-admission.js";
|
|
8
8
|
import { StdioOutput } from "./stdio-output.js";
|
|
9
9
|
import { StdioInput } from "./stdio-input.js";
|
|
10
|
+
import { isBase64 } from "./base64.js";
|
|
11
|
+
import { selectRequestProtocol, decorateModernResult, MODERN_PROTOCOL_VERSION } from "./protocol.js";
|
|
12
|
+
import { SubscriptionRegistry } from "./subscriptions.js";
|
|
13
|
+
import { waitForRequest } from "./request-cancellation.js";
|
|
14
|
+
import { isJsonValue } from "toolcraft-schema";
|
|
15
|
+
import { isValidUri } from "./uri.js";
|
|
16
|
+
import { getParameterHeaders, validateParameterHeaders } from "./headers.js";
|
|
10
17
|
const PROTOCOL_VERSION = "2025-11-25";
|
|
11
|
-
const SUPPORTED_PROTOCOL_VERSIONS = new Set(["2025-03-26", "2025-06-18", PROTOCOL_VERSION]);
|
|
18
|
+
const SUPPORTED_PROTOCOL_VERSIONS = new Set(["2025-03-26", "2025-06-18", PROTOCOL_VERSION, MODERN_PROTOCOL_VERSION]);
|
|
19
|
+
function createLifecycleState(listener, supportNotifications, supportResourceSubscriptions) {
|
|
20
|
+
const abortController = new AbortController();
|
|
21
|
+
const admissions = new Set();
|
|
22
|
+
const requests = new Map();
|
|
23
|
+
abortController.signal.addEventListener("abort", () => {
|
|
24
|
+
for (const admission of admissions)
|
|
25
|
+
admission.abort(abortController.signal.reason);
|
|
26
|
+
admissions.clear();
|
|
27
|
+
for (const request of requests.values())
|
|
28
|
+
request.abort(abortController.signal.reason);
|
|
29
|
+
requests.clear();
|
|
30
|
+
}, { once: true });
|
|
31
|
+
return {
|
|
32
|
+
initialized: false,
|
|
33
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
34
|
+
initializeAccepted: false,
|
|
35
|
+
notificationReady: false,
|
|
36
|
+
resourceSubscriptions: new Set(),
|
|
37
|
+
abortController,
|
|
38
|
+
admissions,
|
|
39
|
+
requests,
|
|
40
|
+
subscriptions: new SubscriptionRegistry(listener ?? (() => { }), supportNotifications, supportResourceSubscriptions),
|
|
41
|
+
listener
|
|
42
|
+
};
|
|
43
|
+
}
|
|
12
44
|
function compileToolSchema(schema) {
|
|
13
45
|
try {
|
|
14
46
|
return compileJsonSchema(schema);
|
|
@@ -27,12 +59,14 @@ export function createServer(options) {
|
|
|
27
59
|
const maxQueuedToolCalls = options.maxQueuedToolCalls ?? 64;
|
|
28
60
|
const maxStdioOutputBytes = options.maxStdioOutputBytes ?? 1024 * 1024;
|
|
29
61
|
const maxPendingStdioMessages = options.maxPendingStdioMessages ?? 128;
|
|
62
|
+
const maxActiveRequests = options.maxActiveRequests ?? 128;
|
|
30
63
|
const maxStdioLineBytes = options.maxStdioLineBytes ?? 1024 * 1024;
|
|
31
64
|
for (const [name, value, minimum] of [
|
|
32
65
|
["maxConcurrentToolCalls", maxConcurrentToolCalls, 1],
|
|
33
66
|
["maxQueuedToolCalls", maxQueuedToolCalls, 0],
|
|
34
67
|
["maxStdioOutputBytes", maxStdioOutputBytes, 1],
|
|
35
68
|
["maxPendingStdioMessages", maxPendingStdioMessages, 1],
|
|
69
|
+
["maxActiveRequests", maxActiveRequests, 1],
|
|
36
70
|
["maxStdioLineBytes", maxStdioLineBytes, 1]
|
|
37
71
|
]) {
|
|
38
72
|
if (!Number.isSafeInteger(value) || value < minimum) {
|
|
@@ -49,15 +83,50 @@ export function createServer(options) {
|
|
|
49
83
|
const methods = new Map();
|
|
50
84
|
const notificationListeners = new Set();
|
|
51
85
|
const connectionNotificationListeners = new Map();
|
|
52
|
-
const defaultLifecycle = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
resourceSubscriptions: new Set(),
|
|
57
|
-
abortController: new AbortController()
|
|
58
|
-
};
|
|
86
|
+
const defaultLifecycle = createLifecycleState((notification) => {
|
|
87
|
+
for (const listener of notificationListeners)
|
|
88
|
+
listener(notification);
|
|
89
|
+
}, supportNotifications, supportResourceSubscriptions);
|
|
59
90
|
const messageLifecycles = new Set([defaultLifecycle]);
|
|
60
|
-
|
|
91
|
+
let activeRequests = 0;
|
|
92
|
+
const describeCapabilities = () => ({
|
|
93
|
+
tools: {
|
|
94
|
+
...(supportNotifications ? { listChanged: true } : {})
|
|
95
|
+
},
|
|
96
|
+
prompts: {
|
|
97
|
+
...(supportNotifications ? { listChanged: true } : {})
|
|
98
|
+
},
|
|
99
|
+
resources: {
|
|
100
|
+
...(supportNotifications ? { listChanged: true } : {}),
|
|
101
|
+
...(supportResourceSubscriptions ? { subscribe: true } : {})
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const dispatchMessage = async (method, lifecycle, params, modern = false, requestSignal = lifecycle.abortController.signal, parameterHeaders) => {
|
|
105
|
+
const handlerContext = {
|
|
106
|
+
signal: requestSignal,
|
|
107
|
+
...(modern && params?.requestState !== undefined
|
|
108
|
+
? { requestState: params.requestState }
|
|
109
|
+
: {}),
|
|
110
|
+
...(modern && params?.inputResponses !== undefined
|
|
111
|
+
? { inputResponses: params.inputResponses }
|
|
112
|
+
: {}),
|
|
113
|
+
clientCapabilities: modern
|
|
114
|
+
? (params?._meta)["io.modelcontextprotocol/clientCapabilities"]
|
|
115
|
+
: {}
|
|
116
|
+
};
|
|
117
|
+
if (method === "server/discover") {
|
|
118
|
+
const result = {
|
|
119
|
+
resultType: "complete",
|
|
120
|
+
supportedVersions: [...SUPPORTED_PROTOCOL_VERSIONS].reverse(),
|
|
121
|
+
capabilities: describeCapabilities(),
|
|
122
|
+
_meta: {
|
|
123
|
+
"io.modelcontextprotocol/serverInfo": { name: options.name, version: options.version }
|
|
124
|
+
},
|
|
125
|
+
ttlMs: 0,
|
|
126
|
+
cacheScope: "private"
|
|
127
|
+
};
|
|
128
|
+
return { result };
|
|
129
|
+
}
|
|
61
130
|
// Allow ping and initialize before initialization
|
|
62
131
|
if (method === "ping") {
|
|
63
132
|
return { result: {} };
|
|
@@ -73,26 +142,17 @@ export function createServer(options) {
|
|
|
73
142
|
lifecycle.notificationReady = false;
|
|
74
143
|
const requestedProtocol = typeof params?.protocolVersion === "string" ? params.protocolVersion : undefined;
|
|
75
144
|
const result = {
|
|
76
|
-
protocolVersion: requestedProtocol !== undefined &&
|
|
145
|
+
protocolVersion: requestedProtocol !== undefined && requestedProtocol !== MODERN_PROTOCOL_VERSION &&
|
|
146
|
+
SUPPORTED_PROTOCOL_VERSIONS.has(requestedProtocol)
|
|
77
147
|
? requestedProtocol
|
|
78
148
|
: PROTOCOL_VERSION,
|
|
79
|
-
capabilities:
|
|
80
|
-
tools: {
|
|
81
|
-
...(supportNotifications ? { listChanged: true } : {})
|
|
82
|
-
},
|
|
83
|
-
prompts: {
|
|
84
|
-
...(supportNotifications ? { listChanged: true } : {})
|
|
85
|
-
},
|
|
86
|
-
resources: {
|
|
87
|
-
...(supportNotifications ? { listChanged: true } : {}),
|
|
88
|
-
...(supportResourceSubscriptions ? { subscribe: true } : {})
|
|
89
|
-
}
|
|
90
|
-
},
|
|
149
|
+
capabilities: describeCapabilities(),
|
|
91
150
|
serverInfo: {
|
|
92
151
|
name: options.name,
|
|
93
152
|
version: options.version
|
|
94
153
|
}
|
|
95
154
|
};
|
|
155
|
+
lifecycle.protocolVersion = result.protocolVersion;
|
|
96
156
|
return { result };
|
|
97
157
|
}
|
|
98
158
|
if (method === "notifications/initialized") {
|
|
@@ -108,7 +168,7 @@ export function createServer(options) {
|
|
|
108
168
|
return { result: undefined };
|
|
109
169
|
}
|
|
110
170
|
// All other methods require initialization
|
|
111
|
-
if (!lifecycle.initialized) {
|
|
171
|
+
if (!modern && !lifecycle.initialized) {
|
|
112
172
|
return {
|
|
113
173
|
error: {
|
|
114
174
|
code: JSON_RPC_ERROR_CODES.INVALID_REQUEST,
|
|
@@ -123,9 +183,10 @@ export function createServer(options) {
|
|
|
123
183
|
delete descriptor.handler;
|
|
124
184
|
delete descriptor.inputValidator;
|
|
125
185
|
delete descriptor.outputValidator;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
186
|
+
delete descriptor.parameterHeaders;
|
|
187
|
+
if (!modern && descriptor.outputSchema?.type !== "object")
|
|
188
|
+
delete descriptor.outputSchema;
|
|
189
|
+
toolList.push(structuredClone(descriptor));
|
|
129
190
|
}
|
|
130
191
|
return { result: { tools: toolList } };
|
|
131
192
|
}
|
|
@@ -149,7 +210,14 @@ export function createServer(options) {
|
|
|
149
210
|
}
|
|
150
211
|
};
|
|
151
212
|
}
|
|
213
|
+
if (params?.arguments !== undefined && !isJsonObject(params.arguments))
|
|
214
|
+
return invalidParams("Tool arguments must be an object");
|
|
152
215
|
const toolArgs = (params?.arguments ?? {});
|
|
216
|
+
if (modern && parameterHeaders !== undefined) {
|
|
217
|
+
const headerError = validateParameterHeaders(tool.parameterHeaders, toolArgs, parameterHeaders);
|
|
218
|
+
if (headerError !== undefined)
|
|
219
|
+
return { error: { code: -32020, message: headerError } };
|
|
220
|
+
}
|
|
153
221
|
const inputValidation = tool.inputValidator.validate(toolArgs);
|
|
154
222
|
if (options.validateToolArguments !== false && !inputValidation.ok) {
|
|
155
223
|
return {
|
|
@@ -162,43 +230,58 @@ export function createServer(options) {
|
|
|
162
230
|
}
|
|
163
231
|
try {
|
|
164
232
|
let handlerResult;
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
233
|
+
const admissionController = new AbortController();
|
|
234
|
+
const sessionSignal = requestSignal;
|
|
235
|
+
const admissionSignal = admissionController.signal;
|
|
236
|
+
const cancelAdmission = () => admissionController.abort(sessionSignal.reason);
|
|
237
|
+
if (sessionSignal.aborted)
|
|
238
|
+
admissionController.abort(sessionSignal.reason);
|
|
239
|
+
else {
|
|
240
|
+
lifecycle.admissions.add(admissionController);
|
|
241
|
+
if (sessionSignal !== lifecycle.abortController.signal)
|
|
242
|
+
sessionSignal.addEventListener("abort", cancelAdmission, { once: true });
|
|
243
|
+
}
|
|
170
244
|
const handlerPromise = (async () => {
|
|
171
245
|
const release = await toolAdmission.acquire(admissionSignal);
|
|
172
246
|
try {
|
|
173
247
|
admissionSignal.throwIfAborted();
|
|
174
|
-
return await tool.handler(toolArgs);
|
|
248
|
+
return await tool.handler(toolArgs, { ...handlerContext, signal: admissionSignal });
|
|
175
249
|
}
|
|
176
250
|
finally {
|
|
177
251
|
release();
|
|
178
252
|
}
|
|
179
253
|
})();
|
|
180
|
-
|
|
181
|
-
|
|
254
|
+
try {
|
|
255
|
+
if (options.toolCallTimeoutMs === undefined) {
|
|
256
|
+
handlerResult = await handlerPromise;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
let timeout;
|
|
260
|
+
handlerResult = await Promise.race([
|
|
261
|
+
handlerPromise,
|
|
262
|
+
new Promise((_resolve, reject) => {
|
|
263
|
+
timeout = setTimeout(() => {
|
|
264
|
+
const error = new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, `Tool call timed out: ${toolName}`);
|
|
265
|
+
admissionController.abort(error);
|
|
266
|
+
reject(error);
|
|
267
|
+
}, options.toolCallTimeoutMs);
|
|
268
|
+
})
|
|
269
|
+
]).finally(() => {
|
|
270
|
+
if (timeout !== undefined) {
|
|
271
|
+
clearTimeout(timeout);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
}
|
|
182
275
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
handlerPromise,
|
|
187
|
-
new Promise((_resolve, reject) => {
|
|
188
|
-
timeout = setTimeout(() => {
|
|
189
|
-
const error = new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, `Tool call timed out: ${toolName}`);
|
|
190
|
-
admissionTimeout.abort(error);
|
|
191
|
-
reject(error);
|
|
192
|
-
}, options.toolCallTimeoutMs);
|
|
193
|
-
})
|
|
194
|
-
]).finally(() => {
|
|
195
|
-
if (timeout !== undefined) {
|
|
196
|
-
clearTimeout(timeout);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
276
|
+
finally {
|
|
277
|
+
lifecycle.admissions.delete(admissionController);
|
|
278
|
+
sessionSignal.removeEventListener("abort", cancelAdmission);
|
|
199
279
|
}
|
|
200
|
-
|
|
201
|
-
|
|
280
|
+
if (modern && isInputRequiredResult(handlerResult))
|
|
281
|
+
return { result: handlerResult };
|
|
282
|
+
const outputSchema = modern || tool.outputSchema?.type === "object" ? tool.outputSchema : undefined;
|
|
283
|
+
const result = normalizeToolResult(handlerResult, tool.outputSchema, modern);
|
|
284
|
+
const outputValidation = outputSchema === undefined ? undefined : tool.outputValidator?.validate(result.structuredContent);
|
|
202
285
|
if (result.isError !== true && outputValidation !== undefined && !outputValidation.ok) {
|
|
203
286
|
throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, `Invalid structured tool result: ${formatIssues(outputValidation.issues)}`, outputValidation.issues);
|
|
204
287
|
}
|
|
@@ -225,7 +308,7 @@ export function createServer(options) {
|
|
|
225
308
|
if (method === "prompts/list") {
|
|
226
309
|
return {
|
|
227
310
|
result: {
|
|
228
|
-
prompts: [...prompts.values()].map(({ handler: _handler, ...prompt }) => prompt)
|
|
311
|
+
prompts: [...prompts.values()].map(({ handler: _handler, ...prompt }) => structuredClone(prompt))
|
|
229
312
|
}
|
|
230
313
|
};
|
|
231
314
|
}
|
|
@@ -243,8 +326,10 @@ export function createServer(options) {
|
|
|
243
326
|
return invalidParams("Invalid prompt arguments");
|
|
244
327
|
}
|
|
245
328
|
try {
|
|
246
|
-
const result = await prompt.handler(args);
|
|
247
|
-
if (
|
|
329
|
+
const result = await prompt.handler(args, handlerContext);
|
|
330
|
+
if (modern && isInputRequiredResult(result))
|
|
331
|
+
return { result };
|
|
332
|
+
if (!isGetPromptResult(result, modern || lifecycle.protocolVersion === PROTOCOL_VERSION)) {
|
|
248
333
|
return internalError("Invalid prompt result");
|
|
249
334
|
}
|
|
250
335
|
return { result };
|
|
@@ -256,14 +341,14 @@ export function createServer(options) {
|
|
|
256
341
|
if (method === "resources/list") {
|
|
257
342
|
return {
|
|
258
343
|
result: {
|
|
259
|
-
resources: [...resources.values()].map(({ handler: _handler, ...resource }) => resource)
|
|
344
|
+
resources: [...resources.values()].map(({ handler: _handler, ...resource }) => structuredClone(resource))
|
|
260
345
|
}
|
|
261
346
|
};
|
|
262
347
|
}
|
|
263
348
|
if (method === "resources/templates/list") {
|
|
264
349
|
return {
|
|
265
350
|
result: {
|
|
266
|
-
resourceTemplates: [...resourceTemplates.values()].map(({ handler: _handler, template: _template, ...resourceTemplate }) => resourceTemplate)
|
|
351
|
+
resourceTemplates: [...resourceTemplates.values()].map(({ handler: _handler, template: _template, ...resourceTemplate }) => structuredClone(resourceTemplate))
|
|
267
352
|
}
|
|
268
353
|
};
|
|
269
354
|
}
|
|
@@ -277,7 +362,9 @@ export function createServer(options) {
|
|
|
277
362
|
return resourceNotFound(uri);
|
|
278
363
|
}
|
|
279
364
|
try {
|
|
280
|
-
const result = await resource.handler(uri);
|
|
365
|
+
const result = await resource.handler(uri, handlerContext);
|
|
366
|
+
if (modern && isInputRequiredResult(result))
|
|
367
|
+
return { result };
|
|
281
368
|
if (!isReadResourceResult(result)) {
|
|
282
369
|
return internalError("Invalid resource result");
|
|
283
370
|
}
|
|
@@ -316,9 +403,11 @@ export function createServer(options) {
|
|
|
316
403
|
if (customMethod !== undefined) {
|
|
317
404
|
try {
|
|
318
405
|
const result = await customMethod(params, {
|
|
319
|
-
signal:
|
|
406
|
+
signal: requestSignal,
|
|
320
407
|
async notify(notificationMethod, notificationParams) {
|
|
321
|
-
if (
|
|
408
|
+
if (requestSignal.aborted ||
|
|
409
|
+
(!modern && !lifecycle.notificationReady) ||
|
|
410
|
+
lifecycle.listener === undefined) {
|
|
322
411
|
return;
|
|
323
412
|
}
|
|
324
413
|
await lifecycle.listener({
|
|
@@ -331,6 +420,15 @@ export function createServer(options) {
|
|
|
331
420
|
return { result };
|
|
332
421
|
}
|
|
333
422
|
catch (error) {
|
|
423
|
+
if (error instanceof ToolError) {
|
|
424
|
+
return {
|
|
425
|
+
error: {
|
|
426
|
+
code: error.code,
|
|
427
|
+
message: error.message,
|
|
428
|
+
...(error.data === undefined ? {} : { data: error.data })
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
334
432
|
return internalError(toErrorMessage(error));
|
|
335
433
|
}
|
|
336
434
|
}
|
|
@@ -341,21 +439,90 @@ export function createServer(options) {
|
|
|
341
439
|
}
|
|
342
440
|
};
|
|
343
441
|
};
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
442
|
+
const handleMessageWithLifecycle = async (method, lifecycle, params, context) => {
|
|
443
|
+
if (method === "notifications/cancelled") {
|
|
444
|
+
const id = params?.requestId;
|
|
445
|
+
if (typeof id === "string" || typeof id === "number") {
|
|
446
|
+
lifecycle.requests.get(id)?.abort(new Error("Request cancelled"));
|
|
447
|
+
}
|
|
448
|
+
return { result: undefined };
|
|
449
|
+
}
|
|
450
|
+
const protocol = selectRequestProtocol(method, params, SUPPORTED_PROTOCOL_VERSIONS);
|
|
451
|
+
if (protocol.error !== undefined)
|
|
452
|
+
return { error: protocol.error };
|
|
453
|
+
const directLegacy = !protocol.modern && context === undefined;
|
|
454
|
+
if (protocol.modern &&
|
|
455
|
+
context !== undefined &&
|
|
456
|
+
context.requestId !== undefined &&
|
|
457
|
+
!isRequestId(context.requestId)) {
|
|
458
|
+
return {
|
|
459
|
+
error: { code: JSON_RPC_ERROR_CODES.INVALID_REQUEST, message: "Invalid Request ID" }
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (activeRequests >= maxActiveRequests) {
|
|
463
|
+
return { error: { code: -32000, message: "Too many active requests" } };
|
|
464
|
+
}
|
|
465
|
+
const key = context?.requestId ?? Symbol("request");
|
|
466
|
+
if (lifecycle.requests.has(key)) {
|
|
467
|
+
return {
|
|
468
|
+
error: {
|
|
469
|
+
code: JSON_RPC_ERROR_CODES.INVALID_REQUEST,
|
|
470
|
+
message: "Request ID is already active"
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
const controller = new AbortController();
|
|
475
|
+
const parents = context?.signal === undefined ? [] : [context.signal];
|
|
476
|
+
const signal = controller.signal;
|
|
477
|
+
if (lifecycle.abortController.signal.aborted)
|
|
478
|
+
controller.abort(lifecycle.abortController.signal.reason);
|
|
479
|
+
const abort = (event) => controller.abort(event.target.reason);
|
|
480
|
+
for (const parent of parents) {
|
|
481
|
+
if (parent.aborted)
|
|
482
|
+
controller.abort(parent.reason);
|
|
483
|
+
else
|
|
484
|
+
parent.addEventListener("abort", abort, { once: true });
|
|
485
|
+
}
|
|
486
|
+
const cleanup = () => {
|
|
487
|
+
for (const parent of parents)
|
|
488
|
+
parent.removeEventListener("abort", abort);
|
|
489
|
+
};
|
|
490
|
+
if (signal.aborted) {
|
|
491
|
+
cleanup();
|
|
492
|
+
return { result: undefined };
|
|
493
|
+
}
|
|
494
|
+
lifecycle.requests.set(key, controller);
|
|
495
|
+
activeRequests += 1;
|
|
496
|
+
const operation = protocol.modern && method === "subscriptions/listen"
|
|
497
|
+
? lifecycle.subscriptions.listen(context?.requestId, params?.notifications, signal)
|
|
498
|
+
: dispatchMessage(method, lifecycle, params, protocol.modern, directLegacy ? lifecycle.abortController.signal : signal, context?.parameterHeaders);
|
|
499
|
+
const release = () => {
|
|
500
|
+
activeRequests -= 1;
|
|
501
|
+
if (lifecycle.requests.get(key) === controller)
|
|
502
|
+
lifecycle.requests.delete(key);
|
|
352
503
|
};
|
|
504
|
+
void operation.then(release, release);
|
|
505
|
+
try {
|
|
506
|
+
const handled = directLegacy ? await operation : await waitForRequest(operation, signal);
|
|
507
|
+
if (handled === undefined || (!directLegacy && signal.aborted))
|
|
508
|
+
return { result: undefined };
|
|
509
|
+
return protocol.modern
|
|
510
|
+
? decorateModernResult(method, handled, { name: options.name, version: options.version }, (params?._meta)["io.modelcontextprotocol/clientCapabilities"])
|
|
511
|
+
: handled;
|
|
512
|
+
}
|
|
513
|
+
finally {
|
|
514
|
+
cleanup();
|
|
515
|
+
controller.abort();
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
const createMessageSession = (listener) => {
|
|
519
|
+
const lifecycle = createLifecycleState(listener, supportNotifications, supportResourceSubscriptions);
|
|
353
520
|
messageLifecycles.add(lifecycle);
|
|
354
521
|
if (listener !== undefined) {
|
|
355
522
|
connectionNotificationListeners.set(listener, lifecycle);
|
|
356
523
|
}
|
|
357
524
|
return {
|
|
358
|
-
handleMessage: (method, params) => handleMessageWithLifecycle(method, lifecycle, params),
|
|
525
|
+
handleMessage: (method, params, context) => handleMessageWithLifecycle(method, lifecycle, params, context),
|
|
359
526
|
close: () => {
|
|
360
527
|
lifecycle.abortController.abort();
|
|
361
528
|
if (listener !== undefined) {
|
|
@@ -365,14 +532,18 @@ export function createServer(options) {
|
|
|
365
532
|
}
|
|
366
533
|
};
|
|
367
534
|
};
|
|
368
|
-
const handleMessage = (method, params) => handleMessageWithLifecycle(method, defaultLifecycle, params);
|
|
369
|
-
const processLine = async (line, write, messageHandler) => {
|
|
535
|
+
const handleMessage = (method, params, context) => handleMessageWithLifecycle(method, defaultLifecycle, params, context);
|
|
536
|
+
const processLine = async (line, write, messageHandler, subscriptionSignal) => {
|
|
370
537
|
const parsed = parseMessage(line);
|
|
371
538
|
if (!parsed.success) {
|
|
372
539
|
await write(formatErrorResponse(parsed.id, parsed.error) + "\n");
|
|
373
540
|
return;
|
|
374
541
|
}
|
|
375
542
|
const { request, isNotification } = parsed;
|
|
543
|
+
if (isNotification &&
|
|
544
|
+
!request.method.startsWith("notifications/") &&
|
|
545
|
+
selectRequestProtocol(request.method, request.params, SUPPORTED_PROTOCOL_VERSIONS).modern)
|
|
546
|
+
return;
|
|
376
547
|
if (isNotification && request.method === "initialize") {
|
|
377
548
|
return;
|
|
378
549
|
}
|
|
@@ -386,7 +557,12 @@ export function createServer(options) {
|
|
|
386
557
|
}
|
|
387
558
|
let handled;
|
|
388
559
|
try {
|
|
389
|
-
handled = await messageHandler(request.method, request.params
|
|
560
|
+
handled = await messageHandler(request.method, request.params, !isNotification && request.id !== null
|
|
561
|
+
? {
|
|
562
|
+
requestId: request.id,
|
|
563
|
+
...(request.method === "subscriptions/listen" ? { signal: subscriptionSignal } : {})
|
|
564
|
+
}
|
|
565
|
+
: undefined);
|
|
390
566
|
}
|
|
391
567
|
catch {
|
|
392
568
|
if (!isNotification) {
|
|
@@ -419,6 +595,7 @@ export function createServer(options) {
|
|
|
419
595
|
for (const listener of notificationListeners) {
|
|
420
596
|
listener(notification);
|
|
421
597
|
}
|
|
598
|
+
await Promise.all([...messageLifecycles].map((lifecycle) => lifecycle.subscriptions.emit(method, params)));
|
|
422
599
|
await Promise.all([...connectionNotificationListeners].map(async ([listener, lifecycle]) => {
|
|
423
600
|
if (lifecycle.notificationReady && canSend(lifecycle)) {
|
|
424
601
|
await listener(notification);
|
|
@@ -431,19 +608,23 @@ export function createServer(options) {
|
|
|
431
608
|
if (tools.has(name)) {
|
|
432
609
|
throw new Error(`Tool already registered: ${name}`);
|
|
433
610
|
}
|
|
434
|
-
const
|
|
611
|
+
const inputSchemaSnapshot = normalizeLegacyNullability(inputSchema);
|
|
612
|
+
const outputSchemaSnapshot = outputSchema === undefined ? undefined : normalizeLegacyNullability(outputSchema);
|
|
613
|
+
const inputValidator = compileToolSchema(inputSchemaSnapshot);
|
|
614
|
+
assertObjectRootSchema(inputSchemaSnapshot, "inputSchema");
|
|
435
615
|
let outputValidator;
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
outputValidator = compileToolSchema(
|
|
616
|
+
if (outputSchemaSnapshot !== undefined) {
|
|
617
|
+
assertOutputSchema(outputSchemaSnapshot);
|
|
618
|
+
outputValidator = compileToolSchema(outputSchemaSnapshot);
|
|
439
619
|
}
|
|
440
620
|
tools.set(name, {
|
|
441
621
|
name,
|
|
442
622
|
description,
|
|
443
|
-
inputSchema:
|
|
444
|
-
...(
|
|
623
|
+
inputSchema: inputSchemaSnapshot,
|
|
624
|
+
...(outputSchemaSnapshot === undefined ? {} : { outputSchema: outputSchemaSnapshot }),
|
|
445
625
|
handler: handler,
|
|
446
626
|
inputValidator,
|
|
627
|
+
parameterHeaders: getParameterHeaders(inputSchemaSnapshot),
|
|
447
628
|
...(outputValidator === undefined ? {} : { outputValidator })
|
|
448
629
|
});
|
|
449
630
|
return server;
|
|
@@ -453,16 +634,22 @@ export function createServer(options) {
|
|
|
453
634
|
if (tools.has(definition.name)) {
|
|
454
635
|
throw new Error(`Tool already registered: ${definition.name}`);
|
|
455
636
|
}
|
|
456
|
-
const
|
|
637
|
+
const descriptor = structuredClone(definition);
|
|
638
|
+
const inputSchema = normalizeLegacyNullability(descriptor.inputSchema);
|
|
639
|
+
const inputValidator = compileToolSchema(inputSchema);
|
|
640
|
+
assertObjectRootSchema(inputSchema, "inputSchema");
|
|
457
641
|
let outputValidator;
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
642
|
+
if (descriptor.outputSchema !== undefined) {
|
|
643
|
+
descriptor.outputSchema = normalizeLegacyNullability(descriptor.outputSchema);
|
|
644
|
+
assertOutputSchema(descriptor.outputSchema);
|
|
645
|
+
outputValidator = compileToolSchema(descriptor.outputSchema);
|
|
461
646
|
}
|
|
462
647
|
tools.set(definition.name, {
|
|
463
|
-
...
|
|
648
|
+
...descriptor,
|
|
649
|
+
inputSchema,
|
|
464
650
|
handler: handler,
|
|
465
651
|
inputValidator,
|
|
652
|
+
parameterHeaders: getParameterHeaders(inputSchema),
|
|
466
653
|
...(outputValidator === undefined ? {} : { outputValidator })
|
|
467
654
|
});
|
|
468
655
|
return server;
|
|
@@ -472,7 +659,7 @@ export function createServer(options) {
|
|
|
472
659
|
if (prompts.has(definition.name)) {
|
|
473
660
|
throw new Error(`Prompt already registered: ${definition.name}`);
|
|
474
661
|
}
|
|
475
|
-
prompts.set(definition.name, { ...definition, handler });
|
|
662
|
+
prompts.set(definition.name, { ...structuredClone(definition), handler });
|
|
476
663
|
return server;
|
|
477
664
|
},
|
|
478
665
|
resource(definition, handler) {
|
|
@@ -482,7 +669,7 @@ export function createServer(options) {
|
|
|
482
669
|
if (resources.has(definition.uri)) {
|
|
483
670
|
throw new Error(`Resource already registered: ${definition.uri}`);
|
|
484
671
|
}
|
|
485
|
-
resources.set(definition.uri, { ...definition, handler });
|
|
672
|
+
resources.set(definition.uri, { ...structuredClone(definition), handler });
|
|
486
673
|
return server;
|
|
487
674
|
},
|
|
488
675
|
resourceTemplate(definition, handler) {
|
|
@@ -490,7 +677,7 @@ export function createServer(options) {
|
|
|
490
677
|
if (resourceTemplates.has(definition.uriTemplate)) {
|
|
491
678
|
throw new Error(`Resource template already registered: ${definition.uriTemplate}`);
|
|
492
679
|
}
|
|
493
|
-
resourceTemplates.set(definition.uriTemplate, { ...definition, handler, template });
|
|
680
|
+
resourceTemplates.set(definition.uriTemplate, { ...structuredClone(definition), handler, template });
|
|
494
681
|
return server;
|
|
495
682
|
},
|
|
496
683
|
method(name, handler) {
|
|
@@ -518,19 +705,19 @@ export function createServer(options) {
|
|
|
518
705
|
},
|
|
519
706
|
async notifyToolsChanged() {
|
|
520
707
|
if (supportNotifications &&
|
|
521
|
-
[...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
|
|
708
|
+
[...messageLifecycles].some((lifecycle) => lifecycle.notificationReady || lifecycle.subscriptions.size > 0)) {
|
|
522
709
|
await broadcastNotification("notifications/tools/list_changed");
|
|
523
710
|
}
|
|
524
711
|
},
|
|
525
712
|
async notifyPromptsChanged() {
|
|
526
713
|
if (supportNotifications &&
|
|
527
|
-
[...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
|
|
714
|
+
[...messageLifecycles].some((lifecycle) => lifecycle.notificationReady || lifecycle.subscriptions.size > 0)) {
|
|
528
715
|
await broadcastNotification("notifications/prompts/list_changed");
|
|
529
716
|
}
|
|
530
717
|
},
|
|
531
718
|
async notifyResourcesChanged() {
|
|
532
719
|
if (supportNotifications &&
|
|
533
|
-
[...messageLifecycles].some((lifecycle) => lifecycle.notificationReady)) {
|
|
720
|
+
[...messageLifecycles].some((lifecycle) => lifecycle.notificationReady || lifecycle.subscriptions.size > 0)) {
|
|
534
721
|
await broadcastNotification("notifications/resources/list_changed");
|
|
535
722
|
}
|
|
536
723
|
},
|
|
@@ -553,6 +740,7 @@ export function createServer(options) {
|
|
|
553
740
|
let inputClosed = false;
|
|
554
741
|
let settled = false;
|
|
555
742
|
const pendingMessages = new Set();
|
|
743
|
+
const subscriptions = new AbortController();
|
|
556
744
|
const output = new StdioOutput(transport.writable, maxStdioOutputBytes, fail, finish);
|
|
557
745
|
const listener = (notification) => output.write(`${JSON.stringify(notification)}\n`);
|
|
558
746
|
const session = server.createMessageSession(listener);
|
|
@@ -596,7 +784,7 @@ export function createServer(options) {
|
|
|
596
784
|
fail(new Error("Stdio pending message limit exceeded"));
|
|
597
785
|
return;
|
|
598
786
|
}
|
|
599
|
-
const message = processLine(line, data => output.write(data), session.handleMessage);
|
|
787
|
+
const message = processLine(line, (data) => output.write(data), session.handleMessage, subscriptions.signal);
|
|
600
788
|
if (!settled)
|
|
601
789
|
pendingMessages.add(message);
|
|
602
790
|
void message.then(() => {
|
|
@@ -606,6 +794,7 @@ export function createServer(options) {
|
|
|
606
794
|
});
|
|
607
795
|
rl.on("close", () => {
|
|
608
796
|
inputClosed = true;
|
|
797
|
+
subscriptions.abort();
|
|
609
798
|
finish();
|
|
610
799
|
});
|
|
611
800
|
transport.readable.pipe(input);
|
|
@@ -622,6 +811,10 @@ export function createServer(options) {
|
|
|
622
811
|
}
|
|
623
812
|
// Handle notifications (no id) - don't respond
|
|
624
813
|
if (!("id" in message) || message.id === undefined) {
|
|
814
|
+
if (!message.method.startsWith("notifications/") &&
|
|
815
|
+
selectRequestProtocol(message.method, message.params, SUPPORTED_PROTOCOL_VERSIONS)
|
|
816
|
+
.modern)
|
|
817
|
+
return;
|
|
625
818
|
if (message.method === "initialize") {
|
|
626
819
|
return;
|
|
627
820
|
}
|
|
@@ -645,9 +838,19 @@ export function createServer(options) {
|
|
|
645
838
|
return;
|
|
646
839
|
}
|
|
647
840
|
const request = message;
|
|
841
|
+
if (selectRequestProtocol(request.method, request.params, SUPPORTED_PROTOCOL_VERSIONS)
|
|
842
|
+
.modern &&
|
|
843
|
+
!isRequestId(request.id)) {
|
|
844
|
+
await transport.send({
|
|
845
|
+
jsonrpc: "2.0",
|
|
846
|
+
id: null,
|
|
847
|
+
error: { code: JSON_RPC_ERROR_CODES.INVALID_REQUEST, message: "Invalid Request ID" }
|
|
848
|
+
});
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
648
851
|
let handled;
|
|
649
852
|
try {
|
|
650
|
-
handled = await session.handleMessage(request.method, request.params);
|
|
853
|
+
handled = await session.handleMessage(request.method, request.params, request.id === null ? undefined : { requestId: request.id });
|
|
651
854
|
}
|
|
652
855
|
catch {
|
|
653
856
|
await transport.send({
|
|
@@ -718,15 +921,6 @@ function resourceNotFound(uri) {
|
|
|
718
921
|
function toErrorMessage(error) {
|
|
719
922
|
return error instanceof Error ? error.message : String(error);
|
|
720
923
|
}
|
|
721
|
-
function isValidUri(uri) {
|
|
722
|
-
try {
|
|
723
|
-
new URL(uri);
|
|
724
|
-
return true;
|
|
725
|
-
}
|
|
726
|
-
catch {
|
|
727
|
-
return false;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
924
|
function assertNonEmptyName(name, message) {
|
|
731
925
|
if (name.length === 0) {
|
|
732
926
|
throw new Error(message);
|
|
@@ -765,57 +959,81 @@ function findReadableResource(uri, resources, resourceTemplates) {
|
|
|
765
959
|
}
|
|
766
960
|
return [...resourceTemplates.values()].find((template) => template.template.match(uri) !== null);
|
|
767
961
|
}
|
|
768
|
-
function
|
|
962
|
+
function isInputRequiredResult(value) {
|
|
963
|
+
return (typeof value === "object" &&
|
|
964
|
+
value !== null &&
|
|
965
|
+
!Array.isArray(value) &&
|
|
966
|
+
value.resultType === "input_required");
|
|
967
|
+
}
|
|
968
|
+
function isCallToolResult(value, modern) {
|
|
769
969
|
if (!hasContentArray(value) || !value.content.every(isContentItem)) {
|
|
770
970
|
return false;
|
|
771
971
|
}
|
|
772
972
|
if (hasOwnProperty(value, "structuredContent") &&
|
|
773
973
|
value.structuredContent !== undefined &&
|
|
774
|
-
!isJsonObject(value.structuredContent)) {
|
|
974
|
+
!(isJsonValue(value.structuredContent) && (modern || isJsonObject(value.structuredContent)))) {
|
|
775
975
|
return false;
|
|
776
976
|
}
|
|
777
977
|
return !(hasOwnProperty(value, "isError") &&
|
|
778
978
|
value.isError !== undefined &&
|
|
779
979
|
typeof value.isError !== "boolean");
|
|
780
980
|
}
|
|
781
|
-
function normalizeToolResult(handlerResult, outputSchema) {
|
|
782
|
-
if (
|
|
981
|
+
function normalizeToolResult(handlerResult, outputSchema, modern) {
|
|
982
|
+
if (!modern && outputSchema !== undefined && outputSchema.type !== "object") {
|
|
983
|
+
const result = normalizeToolResult(handlerResult, outputSchema, true);
|
|
984
|
+
const { structuredContent, ...legacyResult } = result;
|
|
985
|
+
return {
|
|
986
|
+
...legacyResult,
|
|
987
|
+
content: typeof handlerResult === "string"
|
|
988
|
+
? [{ type: "text", text: handlerResult }]
|
|
989
|
+
: legacyResult.content.length > 0 || structuredContent === undefined
|
|
990
|
+
? legacyResult.content
|
|
991
|
+
: [{ type: "text", text: JSON.stringify(structuredContent) }]
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
if (hasContentArray(handlerResult) && !isCallToolResult(handlerResult, modern)) {
|
|
995
|
+
if (outputSchema !== undefined)
|
|
996
|
+
throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, "Invalid tool result");
|
|
783
997
|
throw new Error("Invalid tool result");
|
|
784
998
|
}
|
|
785
999
|
if (outputSchema === undefined) {
|
|
786
|
-
const result = isCallToolResult(handlerResult)
|
|
1000
|
+
const result = isCallToolResult(handlerResult, modern)
|
|
787
1001
|
? handlerResult
|
|
788
1002
|
: { content: toContentBlocks(handlerResult) };
|
|
789
|
-
if (!isCallToolResult(result)) {
|
|
1003
|
+
if (!isCallToolResult(result, modern)) {
|
|
790
1004
|
throw new Error("Invalid tool result");
|
|
791
1005
|
}
|
|
792
1006
|
return result;
|
|
793
1007
|
}
|
|
794
|
-
if (isCallToolResult(handlerResult) && handlerResult.isError === true) {
|
|
1008
|
+
if (isCallToolResult(handlerResult, modern) && handlerResult.isError === true) {
|
|
795
1009
|
return handlerResult;
|
|
796
1010
|
}
|
|
797
|
-
const callToolResult = isCallToolResult(handlerResult) ? handlerResult : undefined;
|
|
1011
|
+
const callToolResult = isCallToolResult(handlerResult, modern) ? handlerResult : undefined;
|
|
798
1012
|
const structuredContent = callToolResult ? callToolResult.structuredContent : handlerResult;
|
|
799
|
-
if (!isJsonObject(structuredContent)) {
|
|
800
|
-
throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, "Structured tool result must be
|
|
1013
|
+
if (!(isJsonValue(structuredContent) && (modern || isJsonObject(structuredContent)))) {
|
|
1014
|
+
throw new ToolError(JSON_RPC_ERROR_CODES.INTERNAL_ERROR, modern ? "Structured tool result must be JSON" : "Structured tool result must be a JSON object");
|
|
801
1015
|
}
|
|
802
1016
|
return {
|
|
1017
|
+
...callToolResult,
|
|
803
1018
|
content: callToolResult !== undefined && callToolResult.content.length > 0
|
|
804
1019
|
? callToolResult.content
|
|
805
1020
|
: [{ type: "text", text: JSON.stringify(structuredContent) }],
|
|
806
|
-
...(callToolResult?.isError !== undefined ? { isError: callToolResult.isError } : {}),
|
|
807
1021
|
structuredContent
|
|
808
1022
|
};
|
|
809
1023
|
}
|
|
810
1024
|
function assertObjectRootSchema(schema, path) {
|
|
811
|
-
if (schema.type !== "object") {
|
|
1025
|
+
if (typeof schema !== "object" || schema === null || !("type" in schema) || schema.type !== "object") {
|
|
812
1026
|
throw new Error(`${path} root type must be "object"`);
|
|
813
1027
|
}
|
|
814
1028
|
}
|
|
1029
|
+
function assertOutputSchema(schema) {
|
|
1030
|
+
if (typeof schema !== "object" || schema === null || Array.isArray(schema))
|
|
1031
|
+
throw new Error("outputSchema must be a JSON Schema object");
|
|
1032
|
+
}
|
|
815
1033
|
function isJsonObject(value) {
|
|
816
1034
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
817
1035
|
}
|
|
818
|
-
function isGetPromptResult(value) {
|
|
1036
|
+
function isGetPromptResult(value, modern = false) {
|
|
819
1037
|
if (typeof value !== "object" || value === null || !hasOwnProperty(value, "messages")) {
|
|
820
1038
|
return false;
|
|
821
1039
|
}
|
|
@@ -828,7 +1046,7 @@ function isGetPromptResult(value) {
|
|
|
828
1046
|
hasOwnProperty(message, "role") &&
|
|
829
1047
|
(message.role === "user" || message.role === "assistant") &&
|
|
830
1048
|
hasOwnProperty(message, "content") &&
|
|
831
|
-
isPromptContentItem(message.content)));
|
|
1049
|
+
(modern ? isContentItem(message.content) : isPromptContentItem(message.content))));
|
|
832
1050
|
}
|
|
833
1051
|
function isReadResourceResult(value) {
|
|
834
1052
|
if (typeof value !== "object" || value === null || !hasOwnProperty(value, "contents")) {
|
|
@@ -915,25 +1133,6 @@ function hasValidContentAnnotations(value) {
|
|
|
915
1133
|
(priority === undefined || typeof priority === "number") &&
|
|
916
1134
|
(lastModified === undefined || typeof lastModified === "string"));
|
|
917
1135
|
}
|
|
918
|
-
function isBase64(value) {
|
|
919
|
-
if (value.length === 0) {
|
|
920
|
-
return true;
|
|
921
|
-
}
|
|
922
|
-
if (value.length % 4 !== 0) {
|
|
923
|
-
return false;
|
|
924
|
-
}
|
|
925
|
-
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
926
|
-
const paddingStart = value.indexOf("=");
|
|
927
|
-
const encoded = paddingStart === -1 ? value : value.slice(0, paddingStart);
|
|
928
|
-
const padding = paddingStart === -1 ? "" : value.slice(paddingStart);
|
|
929
|
-
if (padding.length > 2 || [...padding].some((character) => character !== "=")) {
|
|
930
|
-
return false;
|
|
931
|
-
}
|
|
932
|
-
if ([...encoded].some((character) => !alphabet.includes(character))) {
|
|
933
|
-
return false;
|
|
934
|
-
}
|
|
935
|
-
return Buffer.from(value, "base64").toString("base64") === value;
|
|
936
|
-
}
|
|
937
1136
|
function isPromptContentItem(value) {
|
|
938
1137
|
if (!isContentItem(value)) {
|
|
939
1138
|
return false;
|