@introspection-ai/recipes 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -58
- package/dist/{agent-tool.d.ts → agents.d.ts} +4 -1
- package/dist/agents.d.ts.map +1 -0
- package/dist/{agent-tool.js → agents.js} +1 -1
- package/dist/agents.js.map +1 -0
- package/dist/api/extensions.d.ts +3 -0
- package/dist/api/extensions.d.ts.map +1 -0
- package/dist/api/extensions.js +2 -0
- package/dist/api/extensions.js.map +1 -0
- package/dist/api/mcp.d.ts +4 -0
- package/dist/api/mcp.d.ts.map +1 -0
- package/dist/api/mcp.js +2 -0
- package/dist/api/mcp.js.map +1 -0
- package/dist/api/session.d.ts +3 -0
- package/dist/api/session.d.ts.map +1 -0
- package/dist/api/session.js +2 -0
- package/dist/api/session.js.map +1 -0
- package/dist/child-agent.d.ts +5 -4
- package/dist/child-agent.d.ts.map +1 -1
- package/dist/child-agent.js +26 -117
- package/dist/child-agent.js.map +1 -1
- package/dist/child-session.d.ts +23 -0
- package/dist/child-session.d.ts.map +1 -0
- package/dist/child-session.js +48 -0
- package/dist/child-session.js.map +1 -0
- package/dist/extensions.d.ts +29 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +119 -0
- package/dist/extensions.js.map +1 -0
- package/dist/index.d.ts +4 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -13
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +38 -5
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +92 -41
- package/dist/inspect.js.map +1 -1
- package/dist/interactions.d.ts +18 -2
- package/dist/interactions.d.ts.map +1 -1
- package/dist/interactions.js +41 -10
- package/dist/interactions.js.map +1 -1
- package/dist/mcp-catalog.d.ts +1 -0
- package/dist/mcp-catalog.d.ts.map +1 -1
- package/dist/mcp-catalog.js +14 -6
- package/dist/mcp-catalog.js.map +1 -1
- package/dist/mcp-chunks/auth-command-ZLGYIEUY.js +273 -0
- package/dist/mcp-chunks/call-arguments-LU3W6D3K.js +13 -0
- package/dist/mcp-chunks/call-command-LKTKSYTH.js +612 -0
- package/dist/mcp-chunks/chunk-2U4BSDC4.js +3951 -0
- package/dist/mcp-chunks/chunk-2XL7MG7R.js +272 -0
- package/dist/mcp-chunks/chunk-3BJA27PY.js +2321 -0
- package/dist/mcp-chunks/chunk-4SQKWYFF.js +15 -0
- package/dist/mcp-chunks/chunk-5QOSCSQB.js +290 -0
- package/dist/mcp-chunks/chunk-6W5QFASN.js +77 -0
- package/dist/mcp-chunks/chunk-6XCZGU3G.js +1004 -0
- package/dist/mcp-chunks/chunk-7FHEBT5G.js +149 -0
- package/dist/mcp-chunks/chunk-7KA2VJVZ.js +114 -0
- package/dist/mcp-chunks/chunk-7UDVSOOF.js +138 -0
- package/dist/mcp-chunks/chunk-BNV3TPE4.js +66 -0
- package/dist/mcp-chunks/chunk-BTLAWHTA.js +129 -0
- package/dist/mcp-chunks/chunk-BV56DXPW.js +13 -0
- package/dist/mcp-chunks/chunk-BWHIA4PC.js +90 -0
- package/dist/mcp-chunks/chunk-BZIQOWE6.js +267 -0
- package/dist/mcp-chunks/chunk-D3MRAZ6H.js +16140 -0
- package/dist/mcp-chunks/chunk-DHBE7UBH.js +750 -0
- package/dist/mcp-chunks/chunk-E22DYOW5.js +251 -0
- package/dist/mcp-chunks/chunk-ECIVB7P4.js +139 -0
- package/dist/mcp-chunks/chunk-HKRJ6O36.js +1076 -0
- package/dist/mcp-chunks/chunk-IABRYROW.js +44 -0
- package/dist/mcp-chunks/chunk-J6LYIZXN.js +45 -0
- package/dist/mcp-chunks/chunk-L7NTQVYN.js +161 -0
- package/dist/mcp-chunks/chunk-LEZSACDE.js +141 -0
- package/dist/mcp-chunks/chunk-LKIBAYKU.js +208 -0
- package/dist/mcp-chunks/chunk-LNH5LAJD.js +152 -0
- package/dist/mcp-chunks/chunk-LQQEYD76.js +124 -0
- package/dist/mcp-chunks/chunk-LRKZP7DJ.js +47 -0
- package/dist/mcp-chunks/chunk-LS7PMWSR.js +109 -0
- package/dist/mcp-chunks/chunk-M3TPODXI.js +95 -0
- package/dist/mcp-chunks/chunk-MZ2NARG7.js +400 -0
- package/dist/mcp-chunks/chunk-N46NEFAF.js +45 -0
- package/dist/mcp-chunks/chunk-NNOEGEUZ.js +17273 -0
- package/dist/mcp-chunks/chunk-NRAJX5E4.js +69 -0
- package/dist/mcp-chunks/chunk-P3PSC54P.js +869 -0
- package/dist/mcp-chunks/chunk-PIIYJR44.js +6341 -0
- package/dist/mcp-chunks/chunk-PWYFKGKG.js +385 -0
- package/dist/mcp-chunks/chunk-SJCCBNZ4.js +45 -0
- package/dist/mcp-chunks/chunk-TATVT5IT.js +194 -0
- package/dist/mcp-chunks/chunk-TEUKFFQL.js +29 -0
- package/dist/mcp-chunks/chunk-UKYNQGNE.js +150 -0
- package/dist/mcp-chunks/chunk-UNKB4SBV.js +37 -0
- package/dist/mcp-chunks/chunk-WD6BOD24.js +138 -0
- package/dist/mcp-chunks/chunk-WQJZMCJN.js +73 -0
- package/dist/mcp-chunks/chunk-XANPGTLI.js +364 -0
- package/dist/mcp-chunks/chunk-XQK2JSW4.js +35 -0
- package/dist/mcp-chunks/chunk-YHQUTU6L.js +571 -0
- package/dist/mcp-chunks/chunk-YVZKMV5H.js +57 -0
- package/dist/mcp-chunks/cli-UV4QJZZM.js +929 -0
- package/dist/mcp-chunks/client-S7RQUG5U.js +13 -0
- package/dist/mcp-chunks/config-command-NLAGKID5.js +1101 -0
- package/dist/mcp-chunks/daemon-command-U242Z6VY.js +26 -0
- package/dist/mcp-chunks/dist-LK3SHAMX.js +6478 -0
- package/dist/mcp-chunks/emit-ts-command-VBXVXO6V.js +386 -0
- package/dist/mcp-chunks/generate-cli-runner-6CAZZA3P.js +1426 -0
- package/dist/mcp-chunks/inspect-cli-command-3ZORTOR7.js +111 -0
- package/dist/mcp-chunks/launch-YKRXHXJI.js +10 -0
- package/dist/mcp-chunks/lifecycle-XVKRHPH3.js +16 -0
- package/dist/mcp-chunks/list-command-2MEGGNFQ.js +4190 -0
- package/dist/mcp-chunks/output-utils-NKESLAJ6.js +12 -0
- package/dist/mcp-chunks/prompt-B1Yc1NPt-VGOTWJMN.js +918 -0
- package/dist/mcp-chunks/record-command-YXHTTTT5.js +17 -0
- package/dist/mcp-chunks/replay-command-25HJ6AQU.js +85 -0
- package/dist/mcp-chunks/resource-command-OO4NYF5C.js +97 -0
- package/dist/mcp-chunks/result-utils-MOHHLNM7.js +15 -0
- package/dist/mcp-chunks/runtime-5WJ3G25Y.js +33 -0
- package/dist/mcp-chunks/runtime-wrapper-RPWDIDBF.js +10 -0
- package/dist/mcp-chunks/serve-command-DHXJZMMN.js +3302 -0
- package/dist/mcp-chunks/timeouts-W2XWCCXR.js +18 -0
- package/dist/mcp-chunks/vault-command-PIYIUEZL.js +154 -0
- package/dist/mcp-daemon-client.d.ts +6 -1
- package/dist/mcp-daemon-client.d.ts.map +1 -1
- package/dist/mcp-daemon-client.js +119 -2
- package/dist/mcp-daemon-client.js.map +1 -1
- package/dist/mcp-daemon-protocol.d.ts +18 -1
- package/dist/mcp-daemon-protocol.d.ts.map +1 -1
- package/dist/mcp-daemon-protocol.js +31 -0
- package/dist/mcp-daemon-protocol.js.map +1 -1
- package/dist/mcp-daemon.js +169 -76602
- package/dist/mcp-daemon.js.map +1 -1
- package/dist/mcp-policy.d.ts +11 -0
- package/dist/mcp-policy.d.ts.map +1 -0
- package/dist/mcp-policy.js +27 -0
- package/dist/mcp-policy.js.map +1 -0
- package/dist/mcp-run-worker.js +38 -76540
- package/dist/mcp-tools.d.ts +39 -0
- package/dist/mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools.js +493 -0
- package/dist/mcp-tools.js.map +1 -0
- package/dist/mcp.d.ts +21 -9
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +101 -31
- package/dist/mcp.js.map +1 -1
- package/dist/model-binding.d.ts +39 -0
- package/dist/model-binding.d.ts.map +1 -0
- package/dist/model-binding.js +103 -0
- package/dist/model-binding.js.map +1 -0
- package/dist/pi-extension.d.ts +2 -22
- package/dist/pi-extension.d.ts.map +1 -1
- package/dist/pi-extension.js +296 -71
- package/dist/pi-extension.js.map +1 -1
- package/dist/recipe/resolve.d.ts +43 -20
- package/dist/recipe/resolve.d.ts.map +1 -1
- package/dist/recipe/resolve.js +215 -70
- package/dist/recipe/resolve.js.map +1 -1
- package/dist/recipe-agent.d.ts +24 -27
- package/dist/recipe-agent.d.ts.map +1 -1
- package/dist/recipe-agent.js +278 -230
- package/dist/recipe-agent.js.map +1 -1
- package/dist/recipe-check.d.ts +17 -0
- package/dist/recipe-check.d.ts.map +1 -0
- package/dist/recipe-check.js +258 -0
- package/dist/recipe-check.js.map +1 -0
- package/dist/recipe-extensions.d.ts.map +1 -1
- package/dist/recipe-extensions.js +0 -3
- package/dist/recipe-extensions.js.map +1 -1
- package/dist/recipe-model.d.ts +3 -1
- package/dist/recipe-model.d.ts.map +1 -1
- package/dist/recipe-model.js +15 -12
- package/dist/recipe-model.js.map +1 -1
- package/dist/recipe-package.d.ts +6 -28
- package/dist/recipe-package.d.ts.map +1 -1
- package/dist/recipe-package.js +248 -191
- package/dist/recipe-package.js.map +1 -1
- package/dist/recipe-skills.d.ts.map +1 -1
- package/dist/recipe-skills.js +42 -29
- package/dist/recipe-skills.js.map +1 -1
- package/dist/run-controller.d.ts +10 -9
- package/dist/run-controller.d.ts.map +1 -1
- package/dist/run-controller.js +28 -21
- package/dist/run-controller.js.map +1 -1
- package/dist/session.d.ts +40 -53
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +312 -173
- package/dist/session.js.map +1 -1
- package/dist/test-utils.d.ts +7 -7
- package/dist/test-utils.d.ts.map +1 -1
- package/dist/test-utils.js +32 -15
- package/dist/test-utils.js.map +1 -1
- package/docs/agent-composition.md +41 -35
- package/docs/host-api.md +201 -0
- package/docs/index.md +8 -14
- package/docs/interactions.md +12 -11
- package/docs/mcp-auth.md +6 -6
- package/docs/mcp-configuration.md +72 -24
- package/docs/pi-extension.md +39 -11
- package/docs/recipe-flow.md +9 -9
- package/docs/recipe-format.md +90 -41
- package/package.json +23 -17
- package/vendor/mcp-client/darwin-arm64/mcp-client +0 -0
- package/vendor/mcp-client/darwin-x64/mcp-client +0 -0
- package/vendor/mcp-client/linux-arm64/mcp-client +0 -0
- package/vendor/recipe-check/darwin-arm64/recipe-check +0 -0
- package/vendor/recipe-check/darwin-x64/recipe-check +0 -0
- package/vendor/recipe-check/linux-arm64/recipe-check +0 -0
- package/vendor/recipe-check/linux-x64/recipe-check +0 -0
- package/vendor/recipe-check/linux-x64-musl/recipe-check +0 -0
- package/vendor/recipe-check/win32-x64/recipe-check.exe +0 -0
- package/dist/agent-tool.d.ts.map +0 -1
- package/dist/agent-tool.js.map +0 -1
- package/dist/run.d.ts +0 -28
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -84
- package/dist/run.js.map +0 -1
- package/dist/testing.d.ts +0 -25
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -96
- package/dist/testing.js.map +0 -1
- package/docs/deployment-configuration.md +0 -65
- package/docs/migration.md +0 -70
- package/docs/python-bindings-release.md +0 -47
- package/docs/recipe-evals.md +0 -70
- package/docs/recipe-judges.md +0 -152
- package/docs/runtime-library.md +0 -171
|
@@ -0,0 +1,3302 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
AjvJsonSchemaValidator,
|
|
4
|
+
Protocol,
|
|
5
|
+
assertClientRequestTaskCapability,
|
|
6
|
+
assertToolsCallTaskCapability,
|
|
7
|
+
createRuntime,
|
|
8
|
+
getLiteralValue,
|
|
9
|
+
getObjectShape,
|
|
10
|
+
getParseErrorMessage,
|
|
11
|
+
getSchemaDescription,
|
|
12
|
+
isSchemaOptional,
|
|
13
|
+
isZ4Schema,
|
|
14
|
+
mergeCapabilities,
|
|
15
|
+
normalizeObjectSchema,
|
|
16
|
+
objectFromShape,
|
|
17
|
+
safeParse,
|
|
18
|
+
safeParseAsync,
|
|
19
|
+
toJsonSchemaCompat
|
|
20
|
+
} from "./chunk-NNOEGEUZ.js";
|
|
21
|
+
import "./chunk-7FHEBT5G.js";
|
|
22
|
+
import "./chunk-BZIQOWE6.js";
|
|
23
|
+
import "./chunk-YHQUTU6L.js";
|
|
24
|
+
import "./chunk-TATVT5IT.js";
|
|
25
|
+
import {
|
|
26
|
+
ReadBuffer,
|
|
27
|
+
serializeMessage
|
|
28
|
+
} from "./chunk-HKRJ6O36.js";
|
|
29
|
+
import {
|
|
30
|
+
MCPORTER_VERSION
|
|
31
|
+
} from "./chunk-4SQKWYFF.js";
|
|
32
|
+
import "./chunk-6W5QFASN.js";
|
|
33
|
+
import "./chunk-2U4BSDC4.js";
|
|
34
|
+
import "./chunk-UKYNQGNE.js";
|
|
35
|
+
import "./chunk-P3PSC54P.js";
|
|
36
|
+
import {
|
|
37
|
+
DaemonClient
|
|
38
|
+
} from "./chunk-PWYFKGKG.js";
|
|
39
|
+
import "./chunk-2XL7MG7R.js";
|
|
40
|
+
import "./chunk-WQJZMCJN.js";
|
|
41
|
+
import "./chunk-ECIVB7P4.js";
|
|
42
|
+
import {
|
|
43
|
+
createKeepAliveRuntime
|
|
44
|
+
} from "./chunk-L7NTQVYN.js";
|
|
45
|
+
import {
|
|
46
|
+
isKeepAliveServer
|
|
47
|
+
} from "./chunk-LS7PMWSR.js";
|
|
48
|
+
import {
|
|
49
|
+
CallToolRequestSchema,
|
|
50
|
+
CallToolResultSchema,
|
|
51
|
+
CompleteRequestSchema,
|
|
52
|
+
CreateMessageResultSchema,
|
|
53
|
+
CreateMessageResultWithToolsSchema,
|
|
54
|
+
CreateTaskResultSchema,
|
|
55
|
+
DEFAULT_NEGOTIATED_PROTOCOL_VERSION,
|
|
56
|
+
ElicitResultSchema,
|
|
57
|
+
EmptyResultSchema,
|
|
58
|
+
ErrorCode,
|
|
59
|
+
GetPromptRequestSchema,
|
|
60
|
+
InitializeRequestSchema,
|
|
61
|
+
InitializedNotificationSchema,
|
|
62
|
+
JSONRPCMessageSchema,
|
|
63
|
+
LATEST_PROTOCOL_VERSION,
|
|
64
|
+
ListPromptsRequestSchema,
|
|
65
|
+
ListResourceTemplatesRequestSchema,
|
|
66
|
+
ListResourcesRequestSchema,
|
|
67
|
+
ListRootsResultSchema,
|
|
68
|
+
ListToolsRequestSchema,
|
|
69
|
+
LoggingLevelSchema,
|
|
70
|
+
McpError,
|
|
71
|
+
ReadResourceRequestSchema,
|
|
72
|
+
SUPPORTED_PROTOCOL_VERSIONS,
|
|
73
|
+
SetLevelRequestSchema,
|
|
74
|
+
ZodOptional,
|
|
75
|
+
assertCompleteRequestPrompt,
|
|
76
|
+
assertCompleteRequestResourceTemplate,
|
|
77
|
+
isInitializeRequest,
|
|
78
|
+
isJSONRPCErrorResponse,
|
|
79
|
+
isJSONRPCRequest,
|
|
80
|
+
isJSONRPCResultResponse
|
|
81
|
+
} from "./chunk-D3MRAZ6H.js";
|
|
82
|
+
import "./chunk-LRKZP7DJ.js";
|
|
83
|
+
|
|
84
|
+
// node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/serve.js
|
|
85
|
+
import http from "node:http";
|
|
86
|
+
|
|
87
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
|
|
88
|
+
var ExperimentalServerTasks = class {
|
|
89
|
+
constructor(_server) {
|
|
90
|
+
this._server = _server;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Sends a request and returns an AsyncGenerator that yields response messages.
|
|
94
|
+
* The generator is guaranteed to end with either a 'result' or 'error' message.
|
|
95
|
+
*
|
|
96
|
+
* This method provides streaming access to request processing, allowing you to
|
|
97
|
+
* observe intermediate task status updates for task-augmented requests.
|
|
98
|
+
*
|
|
99
|
+
* @param request - The request to send
|
|
100
|
+
* @param resultSchema - Zod schema for validating the result
|
|
101
|
+
* @param options - Optional request options (timeout, signal, task creation params, etc.)
|
|
102
|
+
* @returns AsyncGenerator that yields ResponseMessage objects
|
|
103
|
+
*
|
|
104
|
+
* @experimental
|
|
105
|
+
*/
|
|
106
|
+
requestStream(request, resultSchema, options) {
|
|
107
|
+
return this._server.requestStream(request, resultSchema, options);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Sends a sampling request and returns an AsyncGenerator that yields response messages.
|
|
111
|
+
* The generator is guaranteed to end with either a 'result' or 'error' message.
|
|
112
|
+
*
|
|
113
|
+
* For task-augmented requests, yields 'taskCreated' and 'taskStatus' messages
|
|
114
|
+
* before the final result.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```typescript
|
|
118
|
+
* const stream = server.experimental.tasks.createMessageStream({
|
|
119
|
+
* messages: [{ role: 'user', content: { type: 'text', text: 'Hello' } }],
|
|
120
|
+
* maxTokens: 100
|
|
121
|
+
* }, {
|
|
122
|
+
* onprogress: (progress) => {
|
|
123
|
+
* // Handle streaming tokens via progress notifications
|
|
124
|
+
* console.log('Progress:', progress.message);
|
|
125
|
+
* }
|
|
126
|
+
* });
|
|
127
|
+
*
|
|
128
|
+
* for await (const message of stream) {
|
|
129
|
+
* switch (message.type) {
|
|
130
|
+
* case 'taskCreated':
|
|
131
|
+
* console.log('Task created:', message.task.taskId);
|
|
132
|
+
* break;
|
|
133
|
+
* case 'taskStatus':
|
|
134
|
+
* console.log('Task status:', message.task.status);
|
|
135
|
+
* break;
|
|
136
|
+
* case 'result':
|
|
137
|
+
* console.log('Final result:', message.result);
|
|
138
|
+
* break;
|
|
139
|
+
* case 'error':
|
|
140
|
+
* console.error('Error:', message.error);
|
|
141
|
+
* break;
|
|
142
|
+
* }
|
|
143
|
+
* }
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* @param params - The sampling request parameters
|
|
147
|
+
* @param options - Optional request options (timeout, signal, task creation params, onprogress, etc.)
|
|
148
|
+
* @returns AsyncGenerator that yields ResponseMessage objects
|
|
149
|
+
*
|
|
150
|
+
* @experimental
|
|
151
|
+
*/
|
|
152
|
+
createMessageStream(params, options) {
|
|
153
|
+
const clientCapabilities = this._server.getClientCapabilities();
|
|
154
|
+
if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
|
|
155
|
+
throw new Error("Client does not support sampling tools capability.");
|
|
156
|
+
}
|
|
157
|
+
if (params.messages.length > 0) {
|
|
158
|
+
const lastMessage = params.messages[params.messages.length - 1];
|
|
159
|
+
const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
|
|
160
|
+
const hasToolResults = lastContent.some((c) => c.type === "tool_result");
|
|
161
|
+
const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : void 0;
|
|
162
|
+
const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
|
|
163
|
+
const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
|
|
164
|
+
if (hasToolResults) {
|
|
165
|
+
if (lastContent.some((c) => c.type !== "tool_result")) {
|
|
166
|
+
throw new Error("The last message must contain only tool_result content if any is present");
|
|
167
|
+
}
|
|
168
|
+
if (!hasPreviousToolUse) {
|
|
169
|
+
throw new Error("tool_result blocks are not matching any tool_use from the previous message");
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (hasPreviousToolUse) {
|
|
173
|
+
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
174
|
+
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
175
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
|
|
176
|
+
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return this.requestStream({
|
|
181
|
+
method: "sampling/createMessage",
|
|
182
|
+
params
|
|
183
|
+
}, CreateMessageResultSchema, options);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Sends an elicitation request and returns an AsyncGenerator that yields response messages.
|
|
187
|
+
* The generator is guaranteed to end with either a 'result' or 'error' message.
|
|
188
|
+
*
|
|
189
|
+
* For task-augmented requests (especially URL-based elicitation), yields 'taskCreated'
|
|
190
|
+
* and 'taskStatus' messages before the final result.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* ```typescript
|
|
194
|
+
* const stream = server.experimental.tasks.elicitInputStream({
|
|
195
|
+
* mode: 'url',
|
|
196
|
+
* message: 'Please authenticate',
|
|
197
|
+
* elicitationId: 'auth-123',
|
|
198
|
+
* url: 'https://example.com/auth'
|
|
199
|
+
* }, {
|
|
200
|
+
* task: { ttl: 300000 } // Task-augmented for long-running auth flow
|
|
201
|
+
* });
|
|
202
|
+
*
|
|
203
|
+
* for await (const message of stream) {
|
|
204
|
+
* switch (message.type) {
|
|
205
|
+
* case 'taskCreated':
|
|
206
|
+
* console.log('Task created:', message.task.taskId);
|
|
207
|
+
* break;
|
|
208
|
+
* case 'taskStatus':
|
|
209
|
+
* console.log('Task status:', message.task.status);
|
|
210
|
+
* break;
|
|
211
|
+
* case 'result':
|
|
212
|
+
* console.log('User action:', message.result.action);
|
|
213
|
+
* break;
|
|
214
|
+
* case 'error':
|
|
215
|
+
* console.error('Error:', message.error);
|
|
216
|
+
* break;
|
|
217
|
+
* }
|
|
218
|
+
* }
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* @param params - The elicitation request parameters
|
|
222
|
+
* @param options - Optional request options (timeout, signal, task creation params, etc.)
|
|
223
|
+
* @returns AsyncGenerator that yields ResponseMessage objects
|
|
224
|
+
*
|
|
225
|
+
* @experimental
|
|
226
|
+
*/
|
|
227
|
+
elicitInputStream(params, options) {
|
|
228
|
+
const clientCapabilities = this._server.getClientCapabilities();
|
|
229
|
+
const mode = params.mode ?? "form";
|
|
230
|
+
switch (mode) {
|
|
231
|
+
case "url": {
|
|
232
|
+
if (!clientCapabilities?.elicitation?.url) {
|
|
233
|
+
throw new Error("Client does not support url elicitation.");
|
|
234
|
+
}
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case "form": {
|
|
238
|
+
if (!clientCapabilities?.elicitation?.form) {
|
|
239
|
+
throw new Error("Client does not support form elicitation.");
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const normalizedParams = mode === "form" && params.mode === void 0 ? { ...params, mode: "form" } : params;
|
|
245
|
+
return this.requestStream({
|
|
246
|
+
method: "elicitation/create",
|
|
247
|
+
params: normalizedParams
|
|
248
|
+
}, ElicitResultSchema, options);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Gets the current status of a task.
|
|
252
|
+
*
|
|
253
|
+
* @param taskId - The task identifier
|
|
254
|
+
* @param options - Optional request options
|
|
255
|
+
* @returns The task status
|
|
256
|
+
*
|
|
257
|
+
* @experimental
|
|
258
|
+
*/
|
|
259
|
+
async getTask(taskId, options) {
|
|
260
|
+
return this._server.getTask({ taskId }, options);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Retrieves the result of a completed task.
|
|
264
|
+
*
|
|
265
|
+
* @param taskId - The task identifier
|
|
266
|
+
* @param resultSchema - Zod schema for validating the result
|
|
267
|
+
* @param options - Optional request options
|
|
268
|
+
* @returns The task result
|
|
269
|
+
*
|
|
270
|
+
* @experimental
|
|
271
|
+
*/
|
|
272
|
+
async getTaskResult(taskId, resultSchema, options) {
|
|
273
|
+
return this._server.getTaskResult({ taskId }, resultSchema, options);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Lists tasks with optional pagination.
|
|
277
|
+
*
|
|
278
|
+
* @param cursor - Optional pagination cursor
|
|
279
|
+
* @param options - Optional request options
|
|
280
|
+
* @returns List of tasks with optional next cursor
|
|
281
|
+
*
|
|
282
|
+
* @experimental
|
|
283
|
+
*/
|
|
284
|
+
async listTasks(cursor, options) {
|
|
285
|
+
return this._server.listTasks(cursor ? { cursor } : void 0, options);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Cancels a running task.
|
|
289
|
+
*
|
|
290
|
+
* @param taskId - The task identifier
|
|
291
|
+
* @param options - Optional request options
|
|
292
|
+
*
|
|
293
|
+
* @experimental
|
|
294
|
+
*/
|
|
295
|
+
async cancelTask(taskId, options) {
|
|
296
|
+
return this._server.cancelTask({ taskId }, options);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
|
|
301
|
+
var Server = class extends Protocol {
|
|
302
|
+
/**
|
|
303
|
+
* Initializes this server with the given name and version information.
|
|
304
|
+
*/
|
|
305
|
+
constructor(_serverInfo, options) {
|
|
306
|
+
super(options);
|
|
307
|
+
this._serverInfo = _serverInfo;
|
|
308
|
+
this._loggingLevels = /* @__PURE__ */ new Map();
|
|
309
|
+
this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
|
|
310
|
+
this.isMessageIgnored = (level, sessionId) => {
|
|
311
|
+
const currentLevel = this._loggingLevels.get(sessionId);
|
|
312
|
+
return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
|
|
313
|
+
};
|
|
314
|
+
this._capabilities = options?.capabilities ?? {};
|
|
315
|
+
this._instructions = options?.instructions;
|
|
316
|
+
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator();
|
|
317
|
+
this.setRequestHandler(InitializeRequestSchema, (request) => this._oninitialize(request));
|
|
318
|
+
this.setNotificationHandler(InitializedNotificationSchema, () => this.oninitialized?.());
|
|
319
|
+
if (this._capabilities.logging) {
|
|
320
|
+
this.setRequestHandler(SetLevelRequestSchema, async (request, extra) => {
|
|
321
|
+
const transportSessionId = extra.sessionId || extra.requestInfo?.headers["mcp-session-id"] || void 0;
|
|
322
|
+
const { level } = request.params;
|
|
323
|
+
const parseResult = LoggingLevelSchema.safeParse(level);
|
|
324
|
+
if (parseResult.success) {
|
|
325
|
+
this._loggingLevels.set(transportSessionId, parseResult.data);
|
|
326
|
+
}
|
|
327
|
+
return {};
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Access experimental features.
|
|
333
|
+
*
|
|
334
|
+
* WARNING: These APIs are experimental and may change without notice.
|
|
335
|
+
*
|
|
336
|
+
* @experimental
|
|
337
|
+
*/
|
|
338
|
+
get experimental() {
|
|
339
|
+
if (!this._experimental) {
|
|
340
|
+
this._experimental = {
|
|
341
|
+
tasks: new ExperimentalServerTasks(this)
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
return this._experimental;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Registers new capabilities. This can only be called before connecting to a transport.
|
|
348
|
+
*
|
|
349
|
+
* The new capabilities will be merged with any existing capabilities previously given (e.g., at initialization).
|
|
350
|
+
*/
|
|
351
|
+
registerCapabilities(capabilities) {
|
|
352
|
+
if (this.transport) {
|
|
353
|
+
throw new Error("Cannot register capabilities after connecting to transport");
|
|
354
|
+
}
|
|
355
|
+
this._capabilities = mergeCapabilities(this._capabilities, capabilities);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Override request handler registration to enforce server-side validation for tools/call.
|
|
359
|
+
*/
|
|
360
|
+
setRequestHandler(requestSchema, handler) {
|
|
361
|
+
const shape = getObjectShape(requestSchema);
|
|
362
|
+
const methodSchema = shape?.method;
|
|
363
|
+
if (!methodSchema) {
|
|
364
|
+
throw new Error("Schema is missing a method literal");
|
|
365
|
+
}
|
|
366
|
+
let methodValue;
|
|
367
|
+
if (isZ4Schema(methodSchema)) {
|
|
368
|
+
const v4Schema = methodSchema;
|
|
369
|
+
const v4Def = v4Schema._zod?.def;
|
|
370
|
+
methodValue = v4Def?.value ?? v4Schema.value;
|
|
371
|
+
} else {
|
|
372
|
+
const v3Schema = methodSchema;
|
|
373
|
+
const legacyDef = v3Schema._def;
|
|
374
|
+
methodValue = legacyDef?.value ?? v3Schema.value;
|
|
375
|
+
}
|
|
376
|
+
if (typeof methodValue !== "string") {
|
|
377
|
+
throw new Error("Schema method literal must be a string");
|
|
378
|
+
}
|
|
379
|
+
const method = methodValue;
|
|
380
|
+
if (method === "tools/call") {
|
|
381
|
+
const wrappedHandler = async (request, extra) => {
|
|
382
|
+
const validatedRequest = safeParse(CallToolRequestSchema, request);
|
|
383
|
+
if (!validatedRequest.success) {
|
|
384
|
+
const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
|
|
385
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
|
|
386
|
+
}
|
|
387
|
+
const { params } = validatedRequest.data;
|
|
388
|
+
const result = await Promise.resolve(handler(request, extra));
|
|
389
|
+
if (params.task) {
|
|
390
|
+
const taskValidationResult = safeParse(CreateTaskResultSchema, result);
|
|
391
|
+
if (!taskValidationResult.success) {
|
|
392
|
+
const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
|
|
393
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
|
|
394
|
+
}
|
|
395
|
+
return taskValidationResult.data;
|
|
396
|
+
}
|
|
397
|
+
const validationResult = safeParse(CallToolResultSchema, result);
|
|
398
|
+
if (!validationResult.success) {
|
|
399
|
+
const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
|
|
400
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
|
|
401
|
+
}
|
|
402
|
+
return validationResult.data;
|
|
403
|
+
};
|
|
404
|
+
return super.setRequestHandler(requestSchema, wrappedHandler);
|
|
405
|
+
}
|
|
406
|
+
return super.setRequestHandler(requestSchema, handler);
|
|
407
|
+
}
|
|
408
|
+
assertCapabilityForMethod(method) {
|
|
409
|
+
switch (method) {
|
|
410
|
+
case "sampling/createMessage":
|
|
411
|
+
if (!this._clientCapabilities?.sampling) {
|
|
412
|
+
throw new Error(`Client does not support sampling (required for ${method})`);
|
|
413
|
+
}
|
|
414
|
+
break;
|
|
415
|
+
case "elicitation/create":
|
|
416
|
+
if (!this._clientCapabilities?.elicitation) {
|
|
417
|
+
throw new Error(`Client does not support elicitation (required for ${method})`);
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
case "roots/list":
|
|
421
|
+
if (!this._clientCapabilities?.roots) {
|
|
422
|
+
throw new Error(`Client does not support listing roots (required for ${method})`);
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
case "ping":
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
assertNotificationCapability(method) {
|
|
430
|
+
switch (method) {
|
|
431
|
+
case "notifications/message":
|
|
432
|
+
if (!this._capabilities.logging) {
|
|
433
|
+
throw new Error(`Server does not support logging (required for ${method})`);
|
|
434
|
+
}
|
|
435
|
+
break;
|
|
436
|
+
case "notifications/resources/updated":
|
|
437
|
+
case "notifications/resources/list_changed":
|
|
438
|
+
if (!this._capabilities.resources) {
|
|
439
|
+
throw new Error(`Server does not support notifying about resources (required for ${method})`);
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
case "notifications/tools/list_changed":
|
|
443
|
+
if (!this._capabilities.tools) {
|
|
444
|
+
throw new Error(`Server does not support notifying of tool list changes (required for ${method})`);
|
|
445
|
+
}
|
|
446
|
+
break;
|
|
447
|
+
case "notifications/prompts/list_changed":
|
|
448
|
+
if (!this._capabilities.prompts) {
|
|
449
|
+
throw new Error(`Server does not support notifying of prompt list changes (required for ${method})`);
|
|
450
|
+
}
|
|
451
|
+
break;
|
|
452
|
+
case "notifications/elicitation/complete":
|
|
453
|
+
if (!this._clientCapabilities?.elicitation?.url) {
|
|
454
|
+
throw new Error(`Client does not support URL elicitation (required for ${method})`);
|
|
455
|
+
}
|
|
456
|
+
break;
|
|
457
|
+
case "notifications/cancelled":
|
|
458
|
+
break;
|
|
459
|
+
case "notifications/progress":
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
assertRequestHandlerCapability(method) {
|
|
464
|
+
if (!this._capabilities) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
switch (method) {
|
|
468
|
+
case "completion/complete":
|
|
469
|
+
if (!this._capabilities.completions) {
|
|
470
|
+
throw new Error(`Server does not support completions (required for ${method})`);
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
case "logging/setLevel":
|
|
474
|
+
if (!this._capabilities.logging) {
|
|
475
|
+
throw new Error(`Server does not support logging (required for ${method})`);
|
|
476
|
+
}
|
|
477
|
+
break;
|
|
478
|
+
case "prompts/get":
|
|
479
|
+
case "prompts/list":
|
|
480
|
+
if (!this._capabilities.prompts) {
|
|
481
|
+
throw new Error(`Server does not support prompts (required for ${method})`);
|
|
482
|
+
}
|
|
483
|
+
break;
|
|
484
|
+
case "resources/list":
|
|
485
|
+
case "resources/templates/list":
|
|
486
|
+
case "resources/read":
|
|
487
|
+
if (!this._capabilities.resources) {
|
|
488
|
+
throw new Error(`Server does not support resources (required for ${method})`);
|
|
489
|
+
}
|
|
490
|
+
break;
|
|
491
|
+
case "tools/call":
|
|
492
|
+
case "tools/list":
|
|
493
|
+
if (!this._capabilities.tools) {
|
|
494
|
+
throw new Error(`Server does not support tools (required for ${method})`);
|
|
495
|
+
}
|
|
496
|
+
break;
|
|
497
|
+
case "tasks/get":
|
|
498
|
+
case "tasks/list":
|
|
499
|
+
case "tasks/result":
|
|
500
|
+
case "tasks/cancel":
|
|
501
|
+
if (!this._capabilities.tasks) {
|
|
502
|
+
throw new Error(`Server does not support tasks capability (required for ${method})`);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
case "ping":
|
|
506
|
+
case "initialize":
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
assertTaskCapability(method) {
|
|
511
|
+
assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client");
|
|
512
|
+
}
|
|
513
|
+
assertTaskHandlerCapability(method) {
|
|
514
|
+
if (!this._capabilities) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server");
|
|
518
|
+
}
|
|
519
|
+
async _oninitialize(request) {
|
|
520
|
+
const requestedVersion = request.params.protocolVersion;
|
|
521
|
+
this._clientCapabilities = request.params.capabilities;
|
|
522
|
+
this._clientVersion = request.params.clientInfo;
|
|
523
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION;
|
|
524
|
+
return {
|
|
525
|
+
protocolVersion,
|
|
526
|
+
capabilities: this.getCapabilities(),
|
|
527
|
+
serverInfo: this._serverInfo,
|
|
528
|
+
...this._instructions && { instructions: this._instructions }
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* After initialization has completed, this will be populated with the client's reported capabilities.
|
|
533
|
+
*/
|
|
534
|
+
getClientCapabilities() {
|
|
535
|
+
return this._clientCapabilities;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* After initialization has completed, this will be populated with information about the client's name and version.
|
|
539
|
+
*/
|
|
540
|
+
getClientVersion() {
|
|
541
|
+
return this._clientVersion;
|
|
542
|
+
}
|
|
543
|
+
getCapabilities() {
|
|
544
|
+
return this._capabilities;
|
|
545
|
+
}
|
|
546
|
+
async ping() {
|
|
547
|
+
return this.request({ method: "ping" }, EmptyResultSchema);
|
|
548
|
+
}
|
|
549
|
+
// Implementation
|
|
550
|
+
async createMessage(params, options) {
|
|
551
|
+
if (params.tools || params.toolChoice) {
|
|
552
|
+
if (!this._clientCapabilities?.sampling?.tools) {
|
|
553
|
+
throw new Error("Client does not support sampling tools capability.");
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (params.messages.length > 0) {
|
|
557
|
+
const lastMessage = params.messages[params.messages.length - 1];
|
|
558
|
+
const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
|
|
559
|
+
const hasToolResults = lastContent.some((c) => c.type === "tool_result");
|
|
560
|
+
const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : void 0;
|
|
561
|
+
const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
|
|
562
|
+
const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
|
|
563
|
+
if (hasToolResults) {
|
|
564
|
+
if (lastContent.some((c) => c.type !== "tool_result")) {
|
|
565
|
+
throw new Error("The last message must contain only tool_result content if any is present");
|
|
566
|
+
}
|
|
567
|
+
if (!hasPreviousToolUse) {
|
|
568
|
+
throw new Error("tool_result blocks are not matching any tool_use from the previous message");
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (hasPreviousToolUse) {
|
|
572
|
+
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
573
|
+
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
574
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
|
|
575
|
+
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (params.tools) {
|
|
580
|
+
return this.request({ method: "sampling/createMessage", params }, CreateMessageResultWithToolsSchema, options);
|
|
581
|
+
}
|
|
582
|
+
return this.request({ method: "sampling/createMessage", params }, CreateMessageResultSchema, options);
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Creates an elicitation request for the given parameters.
|
|
586
|
+
* For backwards compatibility, `mode` may be omitted for form requests and will default to `'form'`.
|
|
587
|
+
* @param params The parameters for the elicitation request.
|
|
588
|
+
* @param options Optional request options.
|
|
589
|
+
* @returns The result of the elicitation request.
|
|
590
|
+
*/
|
|
591
|
+
async elicitInput(params, options) {
|
|
592
|
+
const mode = params.mode ?? "form";
|
|
593
|
+
switch (mode) {
|
|
594
|
+
case "url": {
|
|
595
|
+
if (!this._clientCapabilities?.elicitation?.url) {
|
|
596
|
+
throw new Error("Client does not support url elicitation.");
|
|
597
|
+
}
|
|
598
|
+
const urlParams = params;
|
|
599
|
+
return this.request({ method: "elicitation/create", params: urlParams }, ElicitResultSchema, options);
|
|
600
|
+
}
|
|
601
|
+
case "form": {
|
|
602
|
+
if (!this._clientCapabilities?.elicitation?.form) {
|
|
603
|
+
throw new Error("Client does not support form elicitation.");
|
|
604
|
+
}
|
|
605
|
+
const formParams = params.mode === "form" ? params : { ...params, mode: "form" };
|
|
606
|
+
const result = await this.request({ method: "elicitation/create", params: formParams }, ElicitResultSchema, options);
|
|
607
|
+
if (result.action === "accept" && result.content && formParams.requestedSchema) {
|
|
608
|
+
try {
|
|
609
|
+
const validator = this._jsonSchemaValidator.getValidator(formParams.requestedSchema);
|
|
610
|
+
const validationResult = validator(result.content);
|
|
611
|
+
if (!validationResult.valid) {
|
|
612
|
+
throw new McpError(ErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${validationResult.errorMessage}`);
|
|
613
|
+
}
|
|
614
|
+
} catch (error) {
|
|
615
|
+
if (error instanceof McpError) {
|
|
616
|
+
throw error;
|
|
617
|
+
}
|
|
618
|
+
throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error instanceof Error ? error.message : String(error)}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return result;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Creates a reusable callback that, when invoked, will send a `notifications/elicitation/complete`
|
|
627
|
+
* notification for the specified elicitation ID.
|
|
628
|
+
*
|
|
629
|
+
* @param elicitationId The ID of the elicitation to mark as complete.
|
|
630
|
+
* @param options Optional notification options. Useful when the completion notification should be related to a prior request.
|
|
631
|
+
* @returns A function that emits the completion notification when awaited.
|
|
632
|
+
*/
|
|
633
|
+
createElicitationCompletionNotifier(elicitationId, options) {
|
|
634
|
+
if (!this._clientCapabilities?.elicitation?.url) {
|
|
635
|
+
throw new Error("Client does not support URL elicitation (required for notifications/elicitation/complete)");
|
|
636
|
+
}
|
|
637
|
+
return () => this.notification({
|
|
638
|
+
method: "notifications/elicitation/complete",
|
|
639
|
+
params: {
|
|
640
|
+
elicitationId
|
|
641
|
+
}
|
|
642
|
+
}, options);
|
|
643
|
+
}
|
|
644
|
+
async listRoots(params, options) {
|
|
645
|
+
return this.request({ method: "roots/list", params }, ListRootsResultSchema, options);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Sends a logging message to the client, if connected.
|
|
649
|
+
* Note: You only need to send the parameters object, not the entire JSON RPC message
|
|
650
|
+
* @see LoggingMessageNotification
|
|
651
|
+
* @param params
|
|
652
|
+
* @param sessionId optional for stateless and backward compatibility
|
|
653
|
+
*/
|
|
654
|
+
async sendLoggingMessage(params, sessionId) {
|
|
655
|
+
if (this._capabilities.logging) {
|
|
656
|
+
if (!this.isMessageIgnored(params.level, sessionId)) {
|
|
657
|
+
return this.notification({ method: "notifications/message", params });
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
async sendResourceUpdated(params) {
|
|
662
|
+
return this.notification({
|
|
663
|
+
method: "notifications/resources/updated",
|
|
664
|
+
params
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
async sendResourceListChanged() {
|
|
668
|
+
return this.notification({
|
|
669
|
+
method: "notifications/resources/list_changed"
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
async sendToolListChanged() {
|
|
673
|
+
return this.notification({ method: "notifications/tools/list_changed" });
|
|
674
|
+
}
|
|
675
|
+
async sendPromptListChanged() {
|
|
676
|
+
return this.notification({ method: "notifications/prompts/list_changed" });
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
|
|
681
|
+
var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
|
|
682
|
+
function isCompletable(schema) {
|
|
683
|
+
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
684
|
+
}
|
|
685
|
+
function getCompleter(schema) {
|
|
686
|
+
const meta = schema[COMPLETABLE_SYMBOL];
|
|
687
|
+
return meta?.complete;
|
|
688
|
+
}
|
|
689
|
+
var McpZodTypeKind;
|
|
690
|
+
(function(McpZodTypeKind2) {
|
|
691
|
+
McpZodTypeKind2["Completable"] = "McpCompletable";
|
|
692
|
+
})(McpZodTypeKind || (McpZodTypeKind = {}));
|
|
693
|
+
|
|
694
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
|
|
695
|
+
var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
|
|
696
|
+
function validateToolName(name) {
|
|
697
|
+
const warnings = [];
|
|
698
|
+
if (name.length === 0) {
|
|
699
|
+
return {
|
|
700
|
+
isValid: false,
|
|
701
|
+
warnings: ["Tool name cannot be empty"]
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
if (name.length > 128) {
|
|
705
|
+
return {
|
|
706
|
+
isValid: false,
|
|
707
|
+
warnings: [`Tool name exceeds maximum length of 128 characters (current: ${name.length})`]
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
if (name.includes(" ")) {
|
|
711
|
+
warnings.push("Tool name contains spaces, which may cause parsing issues");
|
|
712
|
+
}
|
|
713
|
+
if (name.includes(",")) {
|
|
714
|
+
warnings.push("Tool name contains commas, which may cause parsing issues");
|
|
715
|
+
}
|
|
716
|
+
if (name.startsWith("-") || name.endsWith("-")) {
|
|
717
|
+
warnings.push("Tool name starts or ends with a dash, which may cause parsing issues in some contexts");
|
|
718
|
+
}
|
|
719
|
+
if (name.startsWith(".") || name.endsWith(".")) {
|
|
720
|
+
warnings.push("Tool name starts or ends with a dot, which may cause parsing issues in some contexts");
|
|
721
|
+
}
|
|
722
|
+
if (!TOOL_NAME_REGEX.test(name)) {
|
|
723
|
+
const invalidChars = name.split("").filter((char) => !/[A-Za-z0-9._-]/.test(char)).filter((char, index, arr) => arr.indexOf(char) === index);
|
|
724
|
+
warnings.push(`Tool name contains invalid characters: ${invalidChars.map((c) => `"${c}"`).join(", ")}`, "Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)");
|
|
725
|
+
return {
|
|
726
|
+
isValid: false,
|
|
727
|
+
warnings
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
isValid: true,
|
|
732
|
+
warnings
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function issueToolNameWarning(name, warnings) {
|
|
736
|
+
if (warnings.length > 0) {
|
|
737
|
+
console.warn(`Tool name validation warning for "${name}":`);
|
|
738
|
+
for (const warning of warnings) {
|
|
739
|
+
console.warn(` - ${warning}`);
|
|
740
|
+
}
|
|
741
|
+
console.warn("Tool registration will proceed, but this may cause compatibility issues.");
|
|
742
|
+
console.warn("Consider updating the tool name to conform to the MCP tool naming standard.");
|
|
743
|
+
console.warn("See SEP: Specify Format for Tool Names (https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986) for more details.");
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
function validateAndWarnToolName(name) {
|
|
747
|
+
const result = validateToolName(name);
|
|
748
|
+
issueToolNameWarning(name, result.warnings);
|
|
749
|
+
return result.isValid;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
|
|
753
|
+
var ExperimentalMcpServerTasks = class {
|
|
754
|
+
constructor(_mcpServer) {
|
|
755
|
+
this._mcpServer = _mcpServer;
|
|
756
|
+
}
|
|
757
|
+
registerToolTask(name, config, handler) {
|
|
758
|
+
const execution = { taskSupport: "required", ...config.execution };
|
|
759
|
+
if (execution.taskSupport === "forbidden") {
|
|
760
|
+
throw new Error(`Cannot register task-based tool '${name}' with taskSupport 'forbidden'. Use registerTool() instead.`);
|
|
761
|
+
}
|
|
762
|
+
const mcpServerInternal = this._mcpServer;
|
|
763
|
+
return mcpServerInternal._createRegisteredTool(name, config.title, config.description, config.inputSchema, config.outputSchema, config.annotations, execution, config._meta, handler);
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
768
|
+
var McpServer = class {
|
|
769
|
+
constructor(serverInfo, options) {
|
|
770
|
+
this._registeredResources = {};
|
|
771
|
+
this._registeredResourceTemplates = {};
|
|
772
|
+
this._registeredTools = {};
|
|
773
|
+
this._registeredPrompts = {};
|
|
774
|
+
this._toolHandlersInitialized = false;
|
|
775
|
+
this._completionHandlerInitialized = false;
|
|
776
|
+
this._resourceHandlersInitialized = false;
|
|
777
|
+
this._promptHandlersInitialized = false;
|
|
778
|
+
this.server = new Server(serverInfo, options);
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Access experimental features.
|
|
782
|
+
*
|
|
783
|
+
* WARNING: These APIs are experimental and may change without notice.
|
|
784
|
+
*
|
|
785
|
+
* @experimental
|
|
786
|
+
*/
|
|
787
|
+
get experimental() {
|
|
788
|
+
if (!this._experimental) {
|
|
789
|
+
this._experimental = {
|
|
790
|
+
tasks: new ExperimentalMcpServerTasks(this)
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
return this._experimental;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Attaches to the given transport, starts it, and starts listening for messages.
|
|
797
|
+
*
|
|
798
|
+
* The `server` object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
|
|
799
|
+
*/
|
|
800
|
+
async connect(transport) {
|
|
801
|
+
return await this.server.connect(transport);
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Closes the connection.
|
|
805
|
+
*/
|
|
806
|
+
async close() {
|
|
807
|
+
await this.server.close();
|
|
808
|
+
}
|
|
809
|
+
setToolRequestHandlers() {
|
|
810
|
+
if (this._toolHandlersInitialized) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListToolsRequestSchema));
|
|
814
|
+
this.server.assertCanSetRequestHandler(getMethodValue(CallToolRequestSchema));
|
|
815
|
+
this.server.registerCapabilities({
|
|
816
|
+
tools: {
|
|
817
|
+
listChanged: true
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
this.server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
821
|
+
tools: Object.entries(this._registeredTools).filter(([, tool]) => tool.enabled).map(([name, tool]) => {
|
|
822
|
+
const toolDefinition = {
|
|
823
|
+
name,
|
|
824
|
+
title: tool.title,
|
|
825
|
+
description: tool.description,
|
|
826
|
+
inputSchema: (() => {
|
|
827
|
+
const obj = normalizeObjectSchema(tool.inputSchema);
|
|
828
|
+
return obj ? toJsonSchemaCompat(obj, {
|
|
829
|
+
strictUnions: true,
|
|
830
|
+
pipeStrategy: "input"
|
|
831
|
+
}) : EMPTY_OBJECT_JSON_SCHEMA;
|
|
832
|
+
})(),
|
|
833
|
+
annotations: tool.annotations,
|
|
834
|
+
execution: tool.execution,
|
|
835
|
+
_meta: tool._meta
|
|
836
|
+
};
|
|
837
|
+
if (tool.outputSchema) {
|
|
838
|
+
const obj = normalizeObjectSchema(tool.outputSchema);
|
|
839
|
+
if (obj) {
|
|
840
|
+
toolDefinition.outputSchema = toJsonSchemaCompat(obj, {
|
|
841
|
+
strictUnions: true,
|
|
842
|
+
pipeStrategy: "output"
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return toolDefinition;
|
|
847
|
+
})
|
|
848
|
+
}));
|
|
849
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
850
|
+
try {
|
|
851
|
+
const tool = this._registeredTools[request.params.name];
|
|
852
|
+
if (!tool) {
|
|
853
|
+
throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} not found`);
|
|
854
|
+
}
|
|
855
|
+
if (!tool.enabled) {
|
|
856
|
+
throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} disabled`);
|
|
857
|
+
}
|
|
858
|
+
const isTaskRequest = !!request.params.task;
|
|
859
|
+
const taskSupport = tool.execution?.taskSupport;
|
|
860
|
+
const isTaskHandler = "createTask" in tool.handler;
|
|
861
|
+
if ((taskSupport === "required" || taskSupport === "optional") && !isTaskHandler) {
|
|
862
|
+
throw new McpError(ErrorCode.InternalError, `Tool ${request.params.name} has taskSupport '${taskSupport}' but was not registered with registerToolTask`);
|
|
863
|
+
}
|
|
864
|
+
if (taskSupport === "required" && !isTaskRequest) {
|
|
865
|
+
throw new McpError(ErrorCode.MethodNotFound, `Tool ${request.params.name} requires task augmentation (taskSupport: 'required')`);
|
|
866
|
+
}
|
|
867
|
+
if (taskSupport === "optional" && !isTaskRequest && isTaskHandler) {
|
|
868
|
+
return await this.handleAutomaticTaskPolling(tool, request, extra);
|
|
869
|
+
}
|
|
870
|
+
const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
|
|
871
|
+
const result = await this.executeToolHandler(tool, args, extra);
|
|
872
|
+
if (isTaskRequest) {
|
|
873
|
+
return result;
|
|
874
|
+
}
|
|
875
|
+
await this.validateToolOutput(tool, result, request.params.name);
|
|
876
|
+
return result;
|
|
877
|
+
} catch (error) {
|
|
878
|
+
if (error instanceof McpError) {
|
|
879
|
+
if (error.code === ErrorCode.UrlElicitationRequired) {
|
|
880
|
+
throw error;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return this.createToolError(error instanceof Error ? error.message : String(error));
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
this._toolHandlersInitialized = true;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Creates a tool error result.
|
|
890
|
+
*
|
|
891
|
+
* @param errorMessage - The error message.
|
|
892
|
+
* @returns The tool error result.
|
|
893
|
+
*/
|
|
894
|
+
createToolError(errorMessage) {
|
|
895
|
+
return {
|
|
896
|
+
content: [
|
|
897
|
+
{
|
|
898
|
+
type: "text",
|
|
899
|
+
text: errorMessage
|
|
900
|
+
}
|
|
901
|
+
],
|
|
902
|
+
isError: true
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Validates tool input arguments against the tool's input schema.
|
|
907
|
+
*/
|
|
908
|
+
async validateToolInput(tool, args, toolName) {
|
|
909
|
+
if (!tool.inputSchema) {
|
|
910
|
+
return void 0;
|
|
911
|
+
}
|
|
912
|
+
const inputObj = normalizeObjectSchema(tool.inputSchema);
|
|
913
|
+
const schemaToParse = inputObj ?? tool.inputSchema;
|
|
914
|
+
const parseResult = await safeParseAsync(schemaToParse, args);
|
|
915
|
+
if (!parseResult.success) {
|
|
916
|
+
const error = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
917
|
+
const errorMessage = getParseErrorMessage(error);
|
|
918
|
+
throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`);
|
|
919
|
+
}
|
|
920
|
+
return parseResult.data;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Validates tool output against the tool's output schema.
|
|
924
|
+
*/
|
|
925
|
+
async validateToolOutput(tool, result, toolName) {
|
|
926
|
+
if (!tool.outputSchema) {
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
if (!("content" in result)) {
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
if (result.isError) {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (!result.structuredContent) {
|
|
936
|
+
throw new McpError(ErrorCode.InvalidParams, `Output validation error: Tool ${toolName} has an output schema but no structured content was provided`);
|
|
937
|
+
}
|
|
938
|
+
const outputObj = normalizeObjectSchema(tool.outputSchema);
|
|
939
|
+
const parseResult = await safeParseAsync(outputObj, result.structuredContent);
|
|
940
|
+
if (!parseResult.success) {
|
|
941
|
+
const error = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
942
|
+
const errorMessage = getParseErrorMessage(error);
|
|
943
|
+
throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Executes a tool handler (either regular or task-based).
|
|
948
|
+
*/
|
|
949
|
+
async executeToolHandler(tool, args, extra) {
|
|
950
|
+
const handler = tool.handler;
|
|
951
|
+
const isTaskHandler = "createTask" in handler;
|
|
952
|
+
if (isTaskHandler) {
|
|
953
|
+
if (!extra.taskStore) {
|
|
954
|
+
throw new Error("No task store provided.");
|
|
955
|
+
}
|
|
956
|
+
const taskExtra = { ...extra, taskStore: extra.taskStore };
|
|
957
|
+
if (tool.inputSchema) {
|
|
958
|
+
const typedHandler = handler;
|
|
959
|
+
return await Promise.resolve(typedHandler.createTask(args, taskExtra));
|
|
960
|
+
} else {
|
|
961
|
+
const typedHandler = handler;
|
|
962
|
+
return await Promise.resolve(typedHandler.createTask(taskExtra));
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (tool.inputSchema) {
|
|
966
|
+
const typedHandler = handler;
|
|
967
|
+
return await Promise.resolve(typedHandler(args, extra));
|
|
968
|
+
} else {
|
|
969
|
+
const typedHandler = handler;
|
|
970
|
+
return await Promise.resolve(typedHandler(extra));
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Handles automatic task polling for tools with taskSupport 'optional'.
|
|
975
|
+
*/
|
|
976
|
+
async handleAutomaticTaskPolling(tool, request, extra) {
|
|
977
|
+
if (!extra.taskStore) {
|
|
978
|
+
throw new Error("No task store provided for task-capable tool.");
|
|
979
|
+
}
|
|
980
|
+
const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
|
|
981
|
+
const handler = tool.handler;
|
|
982
|
+
const taskExtra = { ...extra, taskStore: extra.taskStore };
|
|
983
|
+
const createTaskResult = args ? await Promise.resolve(handler.createTask(args, taskExtra)) : (
|
|
984
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
985
|
+
await Promise.resolve(handler.createTask(taskExtra))
|
|
986
|
+
);
|
|
987
|
+
const taskId = createTaskResult.task.taskId;
|
|
988
|
+
let task = createTaskResult.task;
|
|
989
|
+
const pollInterval = task.pollInterval ?? 5e3;
|
|
990
|
+
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
991
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
992
|
+
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
993
|
+
if (!updatedTask) {
|
|
994
|
+
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
995
|
+
}
|
|
996
|
+
task = updatedTask;
|
|
997
|
+
}
|
|
998
|
+
return await extra.taskStore.getTaskResult(taskId);
|
|
999
|
+
}
|
|
1000
|
+
setCompletionRequestHandler() {
|
|
1001
|
+
if (this._completionHandlerInitialized) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
this.server.assertCanSetRequestHandler(getMethodValue(CompleteRequestSchema));
|
|
1005
|
+
this.server.registerCapabilities({
|
|
1006
|
+
completions: {}
|
|
1007
|
+
});
|
|
1008
|
+
this.server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
|
1009
|
+
switch (request.params.ref.type) {
|
|
1010
|
+
case "ref/prompt":
|
|
1011
|
+
assertCompleteRequestPrompt(request);
|
|
1012
|
+
return this.handlePromptCompletion(request, request.params.ref);
|
|
1013
|
+
case "ref/resource":
|
|
1014
|
+
assertCompleteRequestResourceTemplate(request);
|
|
1015
|
+
return this.handleResourceCompletion(request, request.params.ref);
|
|
1016
|
+
default:
|
|
1017
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid completion reference: ${request.params.ref}`);
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
this._completionHandlerInitialized = true;
|
|
1021
|
+
}
|
|
1022
|
+
async handlePromptCompletion(request, ref) {
|
|
1023
|
+
const prompt = this._registeredPrompts[ref.name];
|
|
1024
|
+
if (!prompt) {
|
|
1025
|
+
throw new McpError(ErrorCode.InvalidParams, `Prompt ${ref.name} not found`);
|
|
1026
|
+
}
|
|
1027
|
+
if (!prompt.enabled) {
|
|
1028
|
+
throw new McpError(ErrorCode.InvalidParams, `Prompt ${ref.name} disabled`);
|
|
1029
|
+
}
|
|
1030
|
+
if (!prompt.argsSchema) {
|
|
1031
|
+
return EMPTY_COMPLETION_RESULT;
|
|
1032
|
+
}
|
|
1033
|
+
const promptShape = getObjectShape(prompt.argsSchema);
|
|
1034
|
+
const field = promptShape?.[request.params.argument.name];
|
|
1035
|
+
if (!isCompletable(field)) {
|
|
1036
|
+
return EMPTY_COMPLETION_RESULT;
|
|
1037
|
+
}
|
|
1038
|
+
const completer = getCompleter(field);
|
|
1039
|
+
if (!completer) {
|
|
1040
|
+
return EMPTY_COMPLETION_RESULT;
|
|
1041
|
+
}
|
|
1042
|
+
const suggestions = await completer(request.params.argument.value, request.params.context);
|
|
1043
|
+
return createCompletionResult(suggestions);
|
|
1044
|
+
}
|
|
1045
|
+
async handleResourceCompletion(request, ref) {
|
|
1046
|
+
const template = Object.values(this._registeredResourceTemplates).find((t) => t.resourceTemplate.uriTemplate.toString() === ref.uri);
|
|
1047
|
+
if (!template) {
|
|
1048
|
+
if (this._registeredResources[ref.uri]) {
|
|
1049
|
+
return EMPTY_COMPLETION_RESULT;
|
|
1050
|
+
}
|
|
1051
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource template ${request.params.ref.uri} not found`);
|
|
1052
|
+
}
|
|
1053
|
+
const completer = template.resourceTemplate.completeCallback(request.params.argument.name);
|
|
1054
|
+
if (!completer) {
|
|
1055
|
+
return EMPTY_COMPLETION_RESULT;
|
|
1056
|
+
}
|
|
1057
|
+
const suggestions = await completer(request.params.argument.value, request.params.context);
|
|
1058
|
+
return createCompletionResult(suggestions);
|
|
1059
|
+
}
|
|
1060
|
+
setResourceRequestHandlers() {
|
|
1061
|
+
if (this._resourceHandlersInitialized) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListResourcesRequestSchema));
|
|
1065
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListResourceTemplatesRequestSchema));
|
|
1066
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ReadResourceRequestSchema));
|
|
1067
|
+
this.server.registerCapabilities({
|
|
1068
|
+
resources: {
|
|
1069
|
+
listChanged: true
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
this.server.setRequestHandler(ListResourcesRequestSchema, async (request, extra) => {
|
|
1073
|
+
const resources = Object.entries(this._registeredResources).filter(([_, resource]) => resource.enabled).map(([uri, resource]) => ({
|
|
1074
|
+
uri,
|
|
1075
|
+
name: resource.name,
|
|
1076
|
+
...resource.metadata
|
|
1077
|
+
}));
|
|
1078
|
+
const templateResources = [];
|
|
1079
|
+
for (const template of Object.values(this._registeredResourceTemplates)) {
|
|
1080
|
+
if (!template.resourceTemplate.listCallback) {
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
const result = await template.resourceTemplate.listCallback(extra);
|
|
1084
|
+
for (const resource of result.resources) {
|
|
1085
|
+
templateResources.push({
|
|
1086
|
+
...template.metadata,
|
|
1087
|
+
// the defined resource metadata should override the template metadata if present
|
|
1088
|
+
...resource
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
return { resources: [...resources, ...templateResources] };
|
|
1093
|
+
});
|
|
1094
|
+
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
1095
|
+
const resourceTemplates = Object.entries(this._registeredResourceTemplates).map(([name, template]) => ({
|
|
1096
|
+
name,
|
|
1097
|
+
uriTemplate: template.resourceTemplate.uriTemplate.toString(),
|
|
1098
|
+
...template.metadata
|
|
1099
|
+
}));
|
|
1100
|
+
return { resourceTemplates };
|
|
1101
|
+
});
|
|
1102
|
+
this.server.setRequestHandler(ReadResourceRequestSchema, async (request, extra) => {
|
|
1103
|
+
const uri = new URL(request.params.uri);
|
|
1104
|
+
const resource = this._registeredResources[uri.toString()];
|
|
1105
|
+
if (resource) {
|
|
1106
|
+
if (!resource.enabled) {
|
|
1107
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} disabled`);
|
|
1108
|
+
}
|
|
1109
|
+
return resource.readCallback(uri, extra);
|
|
1110
|
+
}
|
|
1111
|
+
for (const template of Object.values(this._registeredResourceTemplates)) {
|
|
1112
|
+
const variables = template.resourceTemplate.uriTemplate.match(uri.toString());
|
|
1113
|
+
if (variables) {
|
|
1114
|
+
return template.readCallback(uri, variables, extra);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found`);
|
|
1118
|
+
});
|
|
1119
|
+
this._resourceHandlersInitialized = true;
|
|
1120
|
+
}
|
|
1121
|
+
setPromptRequestHandlers() {
|
|
1122
|
+
if (this._promptHandlersInitialized) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListPromptsRequestSchema));
|
|
1126
|
+
this.server.assertCanSetRequestHandler(getMethodValue(GetPromptRequestSchema));
|
|
1127
|
+
this.server.registerCapabilities({
|
|
1128
|
+
prompts: {
|
|
1129
|
+
listChanged: true
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, () => ({
|
|
1133
|
+
prompts: Object.entries(this._registeredPrompts).filter(([, prompt]) => prompt.enabled).map(([name, prompt]) => {
|
|
1134
|
+
return {
|
|
1135
|
+
name,
|
|
1136
|
+
title: prompt.title,
|
|
1137
|
+
description: prompt.description,
|
|
1138
|
+
arguments: prompt.argsSchema ? promptArgumentsFromSchema(prompt.argsSchema) : void 0
|
|
1139
|
+
};
|
|
1140
|
+
})
|
|
1141
|
+
}));
|
|
1142
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request, extra) => {
|
|
1143
|
+
const prompt = this._registeredPrompts[request.params.name];
|
|
1144
|
+
if (!prompt) {
|
|
1145
|
+
throw new McpError(ErrorCode.InvalidParams, `Prompt ${request.params.name} not found`);
|
|
1146
|
+
}
|
|
1147
|
+
if (!prompt.enabled) {
|
|
1148
|
+
throw new McpError(ErrorCode.InvalidParams, `Prompt ${request.params.name} disabled`);
|
|
1149
|
+
}
|
|
1150
|
+
if (prompt.argsSchema) {
|
|
1151
|
+
const argsObj = normalizeObjectSchema(prompt.argsSchema);
|
|
1152
|
+
const parseResult = await safeParseAsync(argsObj, request.params.arguments);
|
|
1153
|
+
if (!parseResult.success) {
|
|
1154
|
+
const error = "error" in parseResult ? parseResult.error : "Unknown error";
|
|
1155
|
+
const errorMessage = getParseErrorMessage(error);
|
|
1156
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage}`);
|
|
1157
|
+
}
|
|
1158
|
+
const args = parseResult.data;
|
|
1159
|
+
const cb = prompt.callback;
|
|
1160
|
+
return await Promise.resolve(cb(args, extra));
|
|
1161
|
+
} else {
|
|
1162
|
+
const cb = prompt.callback;
|
|
1163
|
+
return await Promise.resolve(cb(extra));
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
this._promptHandlersInitialized = true;
|
|
1167
|
+
}
|
|
1168
|
+
resource(name, uriOrTemplate, ...rest) {
|
|
1169
|
+
let metadata;
|
|
1170
|
+
if (typeof rest[0] === "object") {
|
|
1171
|
+
metadata = rest.shift();
|
|
1172
|
+
}
|
|
1173
|
+
const readCallback = rest[0];
|
|
1174
|
+
if (typeof uriOrTemplate === "string") {
|
|
1175
|
+
if (this._registeredResources[uriOrTemplate]) {
|
|
1176
|
+
throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
1177
|
+
}
|
|
1178
|
+
const registeredResource = this._createRegisteredResource(name, void 0, uriOrTemplate, metadata, readCallback);
|
|
1179
|
+
this.setResourceRequestHandlers();
|
|
1180
|
+
this.sendResourceListChanged();
|
|
1181
|
+
return registeredResource;
|
|
1182
|
+
} else {
|
|
1183
|
+
if (this._registeredResourceTemplates[name]) {
|
|
1184
|
+
throw new Error(`Resource template ${name} is already registered`);
|
|
1185
|
+
}
|
|
1186
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, void 0, uriOrTemplate, metadata, readCallback);
|
|
1187
|
+
this.setResourceRequestHandlers();
|
|
1188
|
+
this.sendResourceListChanged();
|
|
1189
|
+
return registeredResourceTemplate;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
registerResource(name, uriOrTemplate, config, readCallback) {
|
|
1193
|
+
if (typeof uriOrTemplate === "string") {
|
|
1194
|
+
if (this._registeredResources[uriOrTemplate]) {
|
|
1195
|
+
throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
1196
|
+
}
|
|
1197
|
+
const registeredResource = this._createRegisteredResource(name, config.title, uriOrTemplate, config, readCallback);
|
|
1198
|
+
this.setResourceRequestHandlers();
|
|
1199
|
+
this.sendResourceListChanged();
|
|
1200
|
+
return registeredResource;
|
|
1201
|
+
} else {
|
|
1202
|
+
if (this._registeredResourceTemplates[name]) {
|
|
1203
|
+
throw new Error(`Resource template ${name} is already registered`);
|
|
1204
|
+
}
|
|
1205
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config.title, uriOrTemplate, config, readCallback);
|
|
1206
|
+
this.setResourceRequestHandlers();
|
|
1207
|
+
this.sendResourceListChanged();
|
|
1208
|
+
return registeredResourceTemplate;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
_createRegisteredResource(name, title, uri, metadata, readCallback) {
|
|
1212
|
+
const registeredResource = {
|
|
1213
|
+
name,
|
|
1214
|
+
title,
|
|
1215
|
+
metadata,
|
|
1216
|
+
readCallback,
|
|
1217
|
+
enabled: true,
|
|
1218
|
+
disable: () => registeredResource.update({ enabled: false }),
|
|
1219
|
+
enable: () => registeredResource.update({ enabled: true }),
|
|
1220
|
+
remove: () => registeredResource.update({ uri: null }),
|
|
1221
|
+
update: (updates) => {
|
|
1222
|
+
if (typeof updates.uri !== "undefined" && updates.uri !== uri) {
|
|
1223
|
+
delete this._registeredResources[uri];
|
|
1224
|
+
if (updates.uri)
|
|
1225
|
+
this._registeredResources[updates.uri] = registeredResource;
|
|
1226
|
+
}
|
|
1227
|
+
if (typeof updates.name !== "undefined")
|
|
1228
|
+
registeredResource.name = updates.name;
|
|
1229
|
+
if (typeof updates.title !== "undefined")
|
|
1230
|
+
registeredResource.title = updates.title;
|
|
1231
|
+
if (typeof updates.metadata !== "undefined")
|
|
1232
|
+
registeredResource.metadata = updates.metadata;
|
|
1233
|
+
if (typeof updates.callback !== "undefined")
|
|
1234
|
+
registeredResource.readCallback = updates.callback;
|
|
1235
|
+
if (typeof updates.enabled !== "undefined")
|
|
1236
|
+
registeredResource.enabled = updates.enabled;
|
|
1237
|
+
this.sendResourceListChanged();
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
this._registeredResources[uri] = registeredResource;
|
|
1241
|
+
return registeredResource;
|
|
1242
|
+
}
|
|
1243
|
+
_createRegisteredResourceTemplate(name, title, template, metadata, readCallback) {
|
|
1244
|
+
const registeredResourceTemplate = {
|
|
1245
|
+
resourceTemplate: template,
|
|
1246
|
+
title,
|
|
1247
|
+
metadata,
|
|
1248
|
+
readCallback,
|
|
1249
|
+
enabled: true,
|
|
1250
|
+
disable: () => registeredResourceTemplate.update({ enabled: false }),
|
|
1251
|
+
enable: () => registeredResourceTemplate.update({ enabled: true }),
|
|
1252
|
+
remove: () => registeredResourceTemplate.update({ name: null }),
|
|
1253
|
+
update: (updates) => {
|
|
1254
|
+
if (typeof updates.name !== "undefined" && updates.name !== name) {
|
|
1255
|
+
delete this._registeredResourceTemplates[name];
|
|
1256
|
+
if (updates.name)
|
|
1257
|
+
this._registeredResourceTemplates[updates.name] = registeredResourceTemplate;
|
|
1258
|
+
}
|
|
1259
|
+
if (typeof updates.title !== "undefined")
|
|
1260
|
+
registeredResourceTemplate.title = updates.title;
|
|
1261
|
+
if (typeof updates.template !== "undefined")
|
|
1262
|
+
registeredResourceTemplate.resourceTemplate = updates.template;
|
|
1263
|
+
if (typeof updates.metadata !== "undefined")
|
|
1264
|
+
registeredResourceTemplate.metadata = updates.metadata;
|
|
1265
|
+
if (typeof updates.callback !== "undefined")
|
|
1266
|
+
registeredResourceTemplate.readCallback = updates.callback;
|
|
1267
|
+
if (typeof updates.enabled !== "undefined")
|
|
1268
|
+
registeredResourceTemplate.enabled = updates.enabled;
|
|
1269
|
+
this.sendResourceListChanged();
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
this._registeredResourceTemplates[name] = registeredResourceTemplate;
|
|
1273
|
+
const variableNames = template.uriTemplate.variableNames;
|
|
1274
|
+
const hasCompleter = Array.isArray(variableNames) && variableNames.some((v) => !!template.completeCallback(v));
|
|
1275
|
+
if (hasCompleter) {
|
|
1276
|
+
this.setCompletionRequestHandler();
|
|
1277
|
+
}
|
|
1278
|
+
return registeredResourceTemplate;
|
|
1279
|
+
}
|
|
1280
|
+
_createRegisteredPrompt(name, title, description, argsSchema, callback) {
|
|
1281
|
+
const registeredPrompt = {
|
|
1282
|
+
title,
|
|
1283
|
+
description,
|
|
1284
|
+
argsSchema: argsSchema === void 0 ? void 0 : objectFromShape(argsSchema),
|
|
1285
|
+
callback,
|
|
1286
|
+
enabled: true,
|
|
1287
|
+
disable: () => registeredPrompt.update({ enabled: false }),
|
|
1288
|
+
enable: () => registeredPrompt.update({ enabled: true }),
|
|
1289
|
+
remove: () => registeredPrompt.update({ name: null }),
|
|
1290
|
+
update: (updates) => {
|
|
1291
|
+
if (typeof updates.name !== "undefined" && updates.name !== name) {
|
|
1292
|
+
delete this._registeredPrompts[name];
|
|
1293
|
+
if (updates.name)
|
|
1294
|
+
this._registeredPrompts[updates.name] = registeredPrompt;
|
|
1295
|
+
}
|
|
1296
|
+
if (typeof updates.title !== "undefined")
|
|
1297
|
+
registeredPrompt.title = updates.title;
|
|
1298
|
+
if (typeof updates.description !== "undefined")
|
|
1299
|
+
registeredPrompt.description = updates.description;
|
|
1300
|
+
if (typeof updates.argsSchema !== "undefined")
|
|
1301
|
+
registeredPrompt.argsSchema = objectFromShape(updates.argsSchema);
|
|
1302
|
+
if (typeof updates.callback !== "undefined")
|
|
1303
|
+
registeredPrompt.callback = updates.callback;
|
|
1304
|
+
if (typeof updates.enabled !== "undefined")
|
|
1305
|
+
registeredPrompt.enabled = updates.enabled;
|
|
1306
|
+
this.sendPromptListChanged();
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
this._registeredPrompts[name] = registeredPrompt;
|
|
1310
|
+
if (argsSchema) {
|
|
1311
|
+
const hasCompletable = Object.values(argsSchema).some((field) => {
|
|
1312
|
+
const inner = field instanceof ZodOptional ? field._def?.innerType : field;
|
|
1313
|
+
return isCompletable(inner);
|
|
1314
|
+
});
|
|
1315
|
+
if (hasCompletable) {
|
|
1316
|
+
this.setCompletionRequestHandler();
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return registeredPrompt;
|
|
1320
|
+
}
|
|
1321
|
+
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, execution, _meta, handler) {
|
|
1322
|
+
validateAndWarnToolName(name);
|
|
1323
|
+
const registeredTool = {
|
|
1324
|
+
title,
|
|
1325
|
+
description,
|
|
1326
|
+
inputSchema: getZodSchemaObject(inputSchema),
|
|
1327
|
+
outputSchema: getZodSchemaObject(outputSchema),
|
|
1328
|
+
annotations,
|
|
1329
|
+
execution,
|
|
1330
|
+
_meta,
|
|
1331
|
+
handler,
|
|
1332
|
+
enabled: true,
|
|
1333
|
+
disable: () => registeredTool.update({ enabled: false }),
|
|
1334
|
+
enable: () => registeredTool.update({ enabled: true }),
|
|
1335
|
+
remove: () => registeredTool.update({ name: null }),
|
|
1336
|
+
update: (updates) => {
|
|
1337
|
+
if (typeof updates.name !== "undefined" && updates.name !== name) {
|
|
1338
|
+
if (typeof updates.name === "string") {
|
|
1339
|
+
validateAndWarnToolName(updates.name);
|
|
1340
|
+
}
|
|
1341
|
+
delete this._registeredTools[name];
|
|
1342
|
+
if (updates.name)
|
|
1343
|
+
this._registeredTools[updates.name] = registeredTool;
|
|
1344
|
+
}
|
|
1345
|
+
if (typeof updates.title !== "undefined")
|
|
1346
|
+
registeredTool.title = updates.title;
|
|
1347
|
+
if (typeof updates.description !== "undefined")
|
|
1348
|
+
registeredTool.description = updates.description;
|
|
1349
|
+
if (typeof updates.paramsSchema !== "undefined")
|
|
1350
|
+
registeredTool.inputSchema = objectFromShape(updates.paramsSchema);
|
|
1351
|
+
if (typeof updates.outputSchema !== "undefined")
|
|
1352
|
+
registeredTool.outputSchema = objectFromShape(updates.outputSchema);
|
|
1353
|
+
if (typeof updates.callback !== "undefined")
|
|
1354
|
+
registeredTool.handler = updates.callback;
|
|
1355
|
+
if (typeof updates.annotations !== "undefined")
|
|
1356
|
+
registeredTool.annotations = updates.annotations;
|
|
1357
|
+
if (typeof updates._meta !== "undefined")
|
|
1358
|
+
registeredTool._meta = updates._meta;
|
|
1359
|
+
if (typeof updates.enabled !== "undefined")
|
|
1360
|
+
registeredTool.enabled = updates.enabled;
|
|
1361
|
+
this.sendToolListChanged();
|
|
1362
|
+
}
|
|
1363
|
+
};
|
|
1364
|
+
this._registeredTools[name] = registeredTool;
|
|
1365
|
+
this.setToolRequestHandlers();
|
|
1366
|
+
this.sendToolListChanged();
|
|
1367
|
+
return registeredTool;
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* tool() implementation. Parses arguments passed to overrides defined above.
|
|
1371
|
+
*/
|
|
1372
|
+
tool(name, ...rest) {
|
|
1373
|
+
if (this._registeredTools[name]) {
|
|
1374
|
+
throw new Error(`Tool ${name} is already registered`);
|
|
1375
|
+
}
|
|
1376
|
+
let description;
|
|
1377
|
+
let inputSchema;
|
|
1378
|
+
let outputSchema;
|
|
1379
|
+
let annotations;
|
|
1380
|
+
if (typeof rest[0] === "string") {
|
|
1381
|
+
description = rest.shift();
|
|
1382
|
+
}
|
|
1383
|
+
if (rest.length > 1) {
|
|
1384
|
+
const firstArg = rest[0];
|
|
1385
|
+
if (isZodRawShapeCompat(firstArg)) {
|
|
1386
|
+
inputSchema = rest.shift();
|
|
1387
|
+
if (rest.length > 1 && typeof rest[0] === "object" && rest[0] !== null && !isZodRawShapeCompat(rest[0])) {
|
|
1388
|
+
annotations = rest.shift();
|
|
1389
|
+
}
|
|
1390
|
+
} else if (typeof firstArg === "object" && firstArg !== null) {
|
|
1391
|
+
if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) {
|
|
1392
|
+
throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
|
|
1393
|
+
}
|
|
1394
|
+
annotations = rest.shift();
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
const callback = rest[0];
|
|
1398
|
+
return this._createRegisteredTool(name, void 0, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, void 0, callback);
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Registers a tool with a config object and callback.
|
|
1402
|
+
*/
|
|
1403
|
+
registerTool(name, config, cb) {
|
|
1404
|
+
if (this._registeredTools[name]) {
|
|
1405
|
+
throw new Error(`Tool ${name} is already registered`);
|
|
1406
|
+
}
|
|
1407
|
+
const { title, description, inputSchema, outputSchema, annotations, _meta } = config;
|
|
1408
|
+
return this._createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, _meta, cb);
|
|
1409
|
+
}
|
|
1410
|
+
prompt(name, ...rest) {
|
|
1411
|
+
if (this._registeredPrompts[name]) {
|
|
1412
|
+
throw new Error(`Prompt ${name} is already registered`);
|
|
1413
|
+
}
|
|
1414
|
+
let description;
|
|
1415
|
+
if (typeof rest[0] === "string") {
|
|
1416
|
+
description = rest.shift();
|
|
1417
|
+
}
|
|
1418
|
+
let argsSchema;
|
|
1419
|
+
if (rest.length > 1) {
|
|
1420
|
+
argsSchema = rest.shift();
|
|
1421
|
+
}
|
|
1422
|
+
const cb = rest[0];
|
|
1423
|
+
const registeredPrompt = this._createRegisteredPrompt(name, void 0, description, argsSchema, cb);
|
|
1424
|
+
this.setPromptRequestHandlers();
|
|
1425
|
+
this.sendPromptListChanged();
|
|
1426
|
+
return registeredPrompt;
|
|
1427
|
+
}
|
|
1428
|
+
/**
|
|
1429
|
+
* Registers a prompt with a config object and callback.
|
|
1430
|
+
*/
|
|
1431
|
+
registerPrompt(name, config, cb) {
|
|
1432
|
+
if (this._registeredPrompts[name]) {
|
|
1433
|
+
throw new Error(`Prompt ${name} is already registered`);
|
|
1434
|
+
}
|
|
1435
|
+
const { title, description, argsSchema } = config;
|
|
1436
|
+
const registeredPrompt = this._createRegisteredPrompt(name, title, description, argsSchema, cb);
|
|
1437
|
+
this.setPromptRequestHandlers();
|
|
1438
|
+
this.sendPromptListChanged();
|
|
1439
|
+
return registeredPrompt;
|
|
1440
|
+
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Checks if the server is connected to a transport.
|
|
1443
|
+
* @returns True if the server is connected
|
|
1444
|
+
*/
|
|
1445
|
+
isConnected() {
|
|
1446
|
+
return this.server.transport !== void 0;
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Sends a logging message to the client, if connected.
|
|
1450
|
+
* Note: You only need to send the parameters object, not the entire JSON RPC message
|
|
1451
|
+
* @see LoggingMessageNotification
|
|
1452
|
+
* @param params
|
|
1453
|
+
* @param sessionId optional for stateless and backward compatibility
|
|
1454
|
+
*/
|
|
1455
|
+
async sendLoggingMessage(params, sessionId) {
|
|
1456
|
+
return this.server.sendLoggingMessage(params, sessionId);
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Sends a resource list changed event to the client, if connected.
|
|
1460
|
+
*/
|
|
1461
|
+
sendResourceListChanged() {
|
|
1462
|
+
if (this.isConnected()) {
|
|
1463
|
+
this.server.sendResourceListChanged();
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Sends a tool list changed event to the client, if connected.
|
|
1468
|
+
*/
|
|
1469
|
+
sendToolListChanged() {
|
|
1470
|
+
if (this.isConnected()) {
|
|
1471
|
+
this.server.sendToolListChanged();
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Sends a prompt list changed event to the client, if connected.
|
|
1476
|
+
*/
|
|
1477
|
+
sendPromptListChanged() {
|
|
1478
|
+
if (this.isConnected()) {
|
|
1479
|
+
this.server.sendPromptListChanged();
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
var EMPTY_OBJECT_JSON_SCHEMA = {
|
|
1484
|
+
type: "object",
|
|
1485
|
+
properties: {}
|
|
1486
|
+
};
|
|
1487
|
+
function isZodTypeLike(value) {
|
|
1488
|
+
return value !== null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "safeParse" in value && typeof value.safeParse === "function";
|
|
1489
|
+
}
|
|
1490
|
+
function isZodSchemaInstance(obj) {
|
|
1491
|
+
return "_def" in obj || "_zod" in obj || isZodTypeLike(obj);
|
|
1492
|
+
}
|
|
1493
|
+
function isZodRawShapeCompat(obj) {
|
|
1494
|
+
if (typeof obj !== "object" || obj === null) {
|
|
1495
|
+
return false;
|
|
1496
|
+
}
|
|
1497
|
+
if (isZodSchemaInstance(obj)) {
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1500
|
+
if (Object.keys(obj).length === 0) {
|
|
1501
|
+
return true;
|
|
1502
|
+
}
|
|
1503
|
+
return Object.values(obj).some(isZodTypeLike);
|
|
1504
|
+
}
|
|
1505
|
+
function getZodSchemaObject(schema) {
|
|
1506
|
+
if (!schema) {
|
|
1507
|
+
return void 0;
|
|
1508
|
+
}
|
|
1509
|
+
if (isZodRawShapeCompat(schema)) {
|
|
1510
|
+
return objectFromShape(schema);
|
|
1511
|
+
}
|
|
1512
|
+
if (!isZodSchemaInstance(schema)) {
|
|
1513
|
+
throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
|
|
1514
|
+
}
|
|
1515
|
+
return schema;
|
|
1516
|
+
}
|
|
1517
|
+
function promptArgumentsFromSchema(schema) {
|
|
1518
|
+
const shape = getObjectShape(schema);
|
|
1519
|
+
if (!shape)
|
|
1520
|
+
return [];
|
|
1521
|
+
return Object.entries(shape).map(([name, field]) => {
|
|
1522
|
+
const description = getSchemaDescription(field);
|
|
1523
|
+
const isOptional = isSchemaOptional(field);
|
|
1524
|
+
return {
|
|
1525
|
+
name,
|
|
1526
|
+
description,
|
|
1527
|
+
required: !isOptional
|
|
1528
|
+
};
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
function getMethodValue(schema) {
|
|
1532
|
+
const shape = getObjectShape(schema);
|
|
1533
|
+
const methodSchema = shape?.method;
|
|
1534
|
+
if (!methodSchema) {
|
|
1535
|
+
throw new Error("Schema is missing a method literal");
|
|
1536
|
+
}
|
|
1537
|
+
const value = getLiteralValue(methodSchema);
|
|
1538
|
+
if (typeof value === "string") {
|
|
1539
|
+
return value;
|
|
1540
|
+
}
|
|
1541
|
+
throw new Error("Schema method literal must be a string");
|
|
1542
|
+
}
|
|
1543
|
+
function createCompletionResult(suggestions) {
|
|
1544
|
+
return {
|
|
1545
|
+
completion: {
|
|
1546
|
+
values: suggestions.slice(0, 100),
|
|
1547
|
+
total: suggestions.length,
|
|
1548
|
+
hasMore: suggestions.length > 100
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
var EMPTY_COMPLETION_RESULT = {
|
|
1553
|
+
completion: {
|
|
1554
|
+
values: [],
|
|
1555
|
+
hasMore: false
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1559
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
1560
|
+
import process from "node:process";
|
|
1561
|
+
var StdioServerTransport = class {
|
|
1562
|
+
constructor(_stdin = process.stdin, _stdout = process.stdout) {
|
|
1563
|
+
this._stdin = _stdin;
|
|
1564
|
+
this._stdout = _stdout;
|
|
1565
|
+
this._readBuffer = new ReadBuffer();
|
|
1566
|
+
this._started = false;
|
|
1567
|
+
this._ondata = (chunk) => {
|
|
1568
|
+
this._readBuffer.append(chunk);
|
|
1569
|
+
this.processReadBuffer();
|
|
1570
|
+
};
|
|
1571
|
+
this._onerror = (error) => {
|
|
1572
|
+
this.onerror?.(error);
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Starts listening for messages on stdin.
|
|
1577
|
+
*/
|
|
1578
|
+
async start() {
|
|
1579
|
+
if (this._started) {
|
|
1580
|
+
throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
1581
|
+
}
|
|
1582
|
+
this._started = true;
|
|
1583
|
+
this._stdin.on("data", this._ondata);
|
|
1584
|
+
this._stdin.on("error", this._onerror);
|
|
1585
|
+
}
|
|
1586
|
+
processReadBuffer() {
|
|
1587
|
+
while (true) {
|
|
1588
|
+
try {
|
|
1589
|
+
const message = this._readBuffer.readMessage();
|
|
1590
|
+
if (message === null) {
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1593
|
+
this.onmessage?.(message);
|
|
1594
|
+
} catch (error) {
|
|
1595
|
+
this.onerror?.(error);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
async close() {
|
|
1600
|
+
this._stdin.off("data", this._ondata);
|
|
1601
|
+
this._stdin.off("error", this._onerror);
|
|
1602
|
+
const remainingDataListeners = this._stdin.listenerCount("data");
|
|
1603
|
+
if (remainingDataListeners === 0) {
|
|
1604
|
+
this._stdin.pause();
|
|
1605
|
+
}
|
|
1606
|
+
this._readBuffer.clear();
|
|
1607
|
+
this.onclose?.();
|
|
1608
|
+
}
|
|
1609
|
+
send(message) {
|
|
1610
|
+
return new Promise((resolve) => {
|
|
1611
|
+
const json = serializeMessage(message);
|
|
1612
|
+
if (this._stdout.write(json)) {
|
|
1613
|
+
resolve();
|
|
1614
|
+
} else {
|
|
1615
|
+
this._stdout.once("drain", resolve);
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
// node_modules/.pnpm/@hono+node-server@1.19.14_hono@4.12.32/node_modules/@hono/node-server/dist/index.mjs
|
|
1622
|
+
import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
|
|
1623
|
+
import { Http2ServerRequest } from "http2";
|
|
1624
|
+
import { Readable } from "stream";
|
|
1625
|
+
import crypto2 from "crypto";
|
|
1626
|
+
var RequestError = class extends Error {
|
|
1627
|
+
constructor(message, options) {
|
|
1628
|
+
super(message, options);
|
|
1629
|
+
this.name = "RequestError";
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
var toRequestError = (e) => {
|
|
1633
|
+
if (e instanceof RequestError) {
|
|
1634
|
+
return e;
|
|
1635
|
+
}
|
|
1636
|
+
return new RequestError(e.message, { cause: e });
|
|
1637
|
+
};
|
|
1638
|
+
var GlobalRequest = global.Request;
|
|
1639
|
+
var Request = class extends GlobalRequest {
|
|
1640
|
+
constructor(input, options) {
|
|
1641
|
+
if (typeof input === "object" && getRequestCache in input) {
|
|
1642
|
+
input = input[getRequestCache]();
|
|
1643
|
+
}
|
|
1644
|
+
if (typeof options?.body?.getReader !== "undefined") {
|
|
1645
|
+
;
|
|
1646
|
+
options.duplex ??= "half";
|
|
1647
|
+
}
|
|
1648
|
+
super(input, options);
|
|
1649
|
+
}
|
|
1650
|
+
};
|
|
1651
|
+
var newHeadersFromIncoming = (incoming) => {
|
|
1652
|
+
const headerRecord = [];
|
|
1653
|
+
const rawHeaders = incoming.rawHeaders;
|
|
1654
|
+
for (let i = 0; i < rawHeaders.length; i += 2) {
|
|
1655
|
+
const { [i]: key, [i + 1]: value } = rawHeaders;
|
|
1656
|
+
if (key.charCodeAt(0) !== /*:*/
|
|
1657
|
+
58) {
|
|
1658
|
+
headerRecord.push([key, value]);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
return new Headers(headerRecord);
|
|
1662
|
+
};
|
|
1663
|
+
var wrapBodyStream = /* @__PURE__ */ Symbol("wrapBodyStream");
|
|
1664
|
+
var newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
|
|
1665
|
+
const init = {
|
|
1666
|
+
method,
|
|
1667
|
+
headers,
|
|
1668
|
+
signal: abortController.signal
|
|
1669
|
+
};
|
|
1670
|
+
if (method === "TRACE") {
|
|
1671
|
+
init.method = "GET";
|
|
1672
|
+
const req = new Request(url, init);
|
|
1673
|
+
Object.defineProperty(req, "method", {
|
|
1674
|
+
get() {
|
|
1675
|
+
return "TRACE";
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
return req;
|
|
1679
|
+
}
|
|
1680
|
+
if (!(method === "GET" || method === "HEAD")) {
|
|
1681
|
+
if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) {
|
|
1682
|
+
init.body = new ReadableStream({
|
|
1683
|
+
start(controller) {
|
|
1684
|
+
controller.enqueue(incoming.rawBody);
|
|
1685
|
+
controller.close();
|
|
1686
|
+
}
|
|
1687
|
+
});
|
|
1688
|
+
} else if (incoming[wrapBodyStream]) {
|
|
1689
|
+
let reader;
|
|
1690
|
+
init.body = new ReadableStream({
|
|
1691
|
+
async pull(controller) {
|
|
1692
|
+
try {
|
|
1693
|
+
reader ||= Readable.toWeb(incoming).getReader();
|
|
1694
|
+
const { done, value } = await reader.read();
|
|
1695
|
+
if (done) {
|
|
1696
|
+
controller.close();
|
|
1697
|
+
} else {
|
|
1698
|
+
controller.enqueue(value);
|
|
1699
|
+
}
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
controller.error(error);
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
});
|
|
1705
|
+
} else {
|
|
1706
|
+
init.body = Readable.toWeb(incoming);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
return new Request(url, init);
|
|
1710
|
+
};
|
|
1711
|
+
var getRequestCache = /* @__PURE__ */ Symbol("getRequestCache");
|
|
1712
|
+
var requestCache = /* @__PURE__ */ Symbol("requestCache");
|
|
1713
|
+
var incomingKey = /* @__PURE__ */ Symbol("incomingKey");
|
|
1714
|
+
var urlKey = /* @__PURE__ */ Symbol("urlKey");
|
|
1715
|
+
var headersKey = /* @__PURE__ */ Symbol("headersKey");
|
|
1716
|
+
var abortControllerKey = /* @__PURE__ */ Symbol("abortControllerKey");
|
|
1717
|
+
var getAbortController = /* @__PURE__ */ Symbol("getAbortController");
|
|
1718
|
+
var requestPrototype = {
|
|
1719
|
+
get method() {
|
|
1720
|
+
return this[incomingKey].method || "GET";
|
|
1721
|
+
},
|
|
1722
|
+
get url() {
|
|
1723
|
+
return this[urlKey];
|
|
1724
|
+
},
|
|
1725
|
+
get headers() {
|
|
1726
|
+
return this[headersKey] ||= newHeadersFromIncoming(this[incomingKey]);
|
|
1727
|
+
},
|
|
1728
|
+
[getAbortController]() {
|
|
1729
|
+
this[getRequestCache]();
|
|
1730
|
+
return this[abortControllerKey];
|
|
1731
|
+
},
|
|
1732
|
+
[getRequestCache]() {
|
|
1733
|
+
this[abortControllerKey] ||= new AbortController();
|
|
1734
|
+
return this[requestCache] ||= newRequestFromIncoming(
|
|
1735
|
+
this.method,
|
|
1736
|
+
this[urlKey],
|
|
1737
|
+
this.headers,
|
|
1738
|
+
this[incomingKey],
|
|
1739
|
+
this[abortControllerKey]
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
};
|
|
1743
|
+
[
|
|
1744
|
+
"body",
|
|
1745
|
+
"bodyUsed",
|
|
1746
|
+
"cache",
|
|
1747
|
+
"credentials",
|
|
1748
|
+
"destination",
|
|
1749
|
+
"integrity",
|
|
1750
|
+
"mode",
|
|
1751
|
+
"redirect",
|
|
1752
|
+
"referrer",
|
|
1753
|
+
"referrerPolicy",
|
|
1754
|
+
"signal",
|
|
1755
|
+
"keepalive"
|
|
1756
|
+
].forEach((k) => {
|
|
1757
|
+
Object.defineProperty(requestPrototype, k, {
|
|
1758
|
+
get() {
|
|
1759
|
+
return this[getRequestCache]()[k];
|
|
1760
|
+
}
|
|
1761
|
+
});
|
|
1762
|
+
});
|
|
1763
|
+
["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
|
|
1764
|
+
Object.defineProperty(requestPrototype, k, {
|
|
1765
|
+
value: function() {
|
|
1766
|
+
return this[getRequestCache]()[k]();
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
});
|
|
1770
|
+
Object.defineProperty(requestPrototype, /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom"), {
|
|
1771
|
+
value: function(depth, options, inspectFn) {
|
|
1772
|
+
const props = {
|
|
1773
|
+
method: this.method,
|
|
1774
|
+
url: this.url,
|
|
1775
|
+
headers: this.headers,
|
|
1776
|
+
nativeRequest: this[requestCache]
|
|
1777
|
+
};
|
|
1778
|
+
return `Request (lightweight) ${inspectFn(props, { ...options, depth: depth == null ? null : depth - 1 })}`;
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
Object.setPrototypeOf(requestPrototype, Request.prototype);
|
|
1782
|
+
var newRequest = (incoming, defaultHostname) => {
|
|
1783
|
+
const req = Object.create(requestPrototype);
|
|
1784
|
+
req[incomingKey] = incoming;
|
|
1785
|
+
const incomingUrl = incoming.url || "";
|
|
1786
|
+
if (incomingUrl[0] !== "/" && // short-circuit for performance. most requests are relative URL.
|
|
1787
|
+
(incomingUrl.startsWith("http://") || incomingUrl.startsWith("https://"))) {
|
|
1788
|
+
if (incoming instanceof Http2ServerRequest) {
|
|
1789
|
+
throw new RequestError("Absolute URL for :path is not allowed in HTTP/2");
|
|
1790
|
+
}
|
|
1791
|
+
try {
|
|
1792
|
+
const url2 = new URL(incomingUrl);
|
|
1793
|
+
req[urlKey] = url2.href;
|
|
1794
|
+
} catch (e) {
|
|
1795
|
+
throw new RequestError("Invalid absolute URL", { cause: e });
|
|
1796
|
+
}
|
|
1797
|
+
return req;
|
|
1798
|
+
}
|
|
1799
|
+
const host = (incoming instanceof Http2ServerRequest ? incoming.authority : incoming.headers.host) || defaultHostname;
|
|
1800
|
+
if (!host) {
|
|
1801
|
+
throw new RequestError("Missing host header");
|
|
1802
|
+
}
|
|
1803
|
+
let scheme;
|
|
1804
|
+
if (incoming instanceof Http2ServerRequest) {
|
|
1805
|
+
scheme = incoming.scheme;
|
|
1806
|
+
if (!(scheme === "http" || scheme === "https")) {
|
|
1807
|
+
throw new RequestError("Unsupported scheme");
|
|
1808
|
+
}
|
|
1809
|
+
} else {
|
|
1810
|
+
scheme = incoming.socket && incoming.socket.encrypted ? "https" : "http";
|
|
1811
|
+
}
|
|
1812
|
+
const url = new URL(`${scheme}://${host}${incomingUrl}`);
|
|
1813
|
+
if (url.hostname.length !== host.length && url.hostname !== host.replace(/:\d+$/, "")) {
|
|
1814
|
+
throw new RequestError("Invalid host header");
|
|
1815
|
+
}
|
|
1816
|
+
req[urlKey] = url.href;
|
|
1817
|
+
return req;
|
|
1818
|
+
};
|
|
1819
|
+
var responseCache = /* @__PURE__ */ Symbol("responseCache");
|
|
1820
|
+
var getResponseCache = /* @__PURE__ */ Symbol("getResponseCache");
|
|
1821
|
+
var cacheKey = /* @__PURE__ */ Symbol("cache");
|
|
1822
|
+
var GlobalResponse = global.Response;
|
|
1823
|
+
var Response2 = class _Response {
|
|
1824
|
+
#body;
|
|
1825
|
+
#init;
|
|
1826
|
+
[getResponseCache]() {
|
|
1827
|
+
delete this[cacheKey];
|
|
1828
|
+
return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
|
|
1829
|
+
}
|
|
1830
|
+
constructor(body, init) {
|
|
1831
|
+
let headers;
|
|
1832
|
+
this.#body = body;
|
|
1833
|
+
if (init instanceof _Response) {
|
|
1834
|
+
const cachedGlobalResponse = init[responseCache];
|
|
1835
|
+
if (cachedGlobalResponse) {
|
|
1836
|
+
this.#init = cachedGlobalResponse;
|
|
1837
|
+
this[getResponseCache]();
|
|
1838
|
+
return;
|
|
1839
|
+
} else {
|
|
1840
|
+
this.#init = init.#init;
|
|
1841
|
+
headers = new Headers(init.#init.headers);
|
|
1842
|
+
}
|
|
1843
|
+
} else {
|
|
1844
|
+
this.#init = init;
|
|
1845
|
+
}
|
|
1846
|
+
if (typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) {
|
|
1847
|
+
;
|
|
1848
|
+
this[cacheKey] = [init?.status || 200, body, headers || init?.headers];
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
get headers() {
|
|
1852
|
+
const cache = this[cacheKey];
|
|
1853
|
+
if (cache) {
|
|
1854
|
+
if (!(cache[2] instanceof Headers)) {
|
|
1855
|
+
cache[2] = new Headers(
|
|
1856
|
+
cache[2] || { "content-type": "text/plain; charset=UTF-8" }
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
return cache[2];
|
|
1860
|
+
}
|
|
1861
|
+
return this[getResponseCache]().headers;
|
|
1862
|
+
}
|
|
1863
|
+
get status() {
|
|
1864
|
+
return this[cacheKey]?.[0] ?? this[getResponseCache]().status;
|
|
1865
|
+
}
|
|
1866
|
+
get ok() {
|
|
1867
|
+
const status = this.status;
|
|
1868
|
+
return status >= 200 && status < 300;
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
["body", "bodyUsed", "redirected", "statusText", "trailers", "type", "url"].forEach((k) => {
|
|
1872
|
+
Object.defineProperty(Response2.prototype, k, {
|
|
1873
|
+
get() {
|
|
1874
|
+
return this[getResponseCache]()[k];
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
});
|
|
1878
|
+
["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
|
|
1879
|
+
Object.defineProperty(Response2.prototype, k, {
|
|
1880
|
+
value: function() {
|
|
1881
|
+
return this[getResponseCache]()[k]();
|
|
1882
|
+
}
|
|
1883
|
+
});
|
|
1884
|
+
});
|
|
1885
|
+
Object.defineProperty(Response2.prototype, /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom"), {
|
|
1886
|
+
value: function(depth, options, inspectFn) {
|
|
1887
|
+
const props = {
|
|
1888
|
+
status: this.status,
|
|
1889
|
+
headers: this.headers,
|
|
1890
|
+
ok: this.ok,
|
|
1891
|
+
nativeResponse: this[responseCache]
|
|
1892
|
+
};
|
|
1893
|
+
return `Response (lightweight) ${inspectFn(props, { ...options, depth: depth == null ? null : depth - 1 })}`;
|
|
1894
|
+
}
|
|
1895
|
+
});
|
|
1896
|
+
Object.setPrototypeOf(Response2, GlobalResponse);
|
|
1897
|
+
Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
|
|
1898
|
+
async function readWithoutBlocking(readPromise) {
|
|
1899
|
+
return Promise.race([readPromise, Promise.resolve().then(() => Promise.resolve(void 0))]);
|
|
1900
|
+
}
|
|
1901
|
+
function writeFromReadableStreamDefaultReader(reader, writable, currentReadPromise) {
|
|
1902
|
+
const cancel = (error) => {
|
|
1903
|
+
reader.cancel(error).catch(() => {
|
|
1904
|
+
});
|
|
1905
|
+
};
|
|
1906
|
+
writable.on("close", cancel);
|
|
1907
|
+
writable.on("error", cancel);
|
|
1908
|
+
(currentReadPromise ?? reader.read()).then(flow, handleStreamError);
|
|
1909
|
+
return reader.closed.finally(() => {
|
|
1910
|
+
writable.off("close", cancel);
|
|
1911
|
+
writable.off("error", cancel);
|
|
1912
|
+
});
|
|
1913
|
+
function handleStreamError(error) {
|
|
1914
|
+
if (error) {
|
|
1915
|
+
writable.destroy(error);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
function onDrain() {
|
|
1919
|
+
reader.read().then(flow, handleStreamError);
|
|
1920
|
+
}
|
|
1921
|
+
function flow({ done, value }) {
|
|
1922
|
+
try {
|
|
1923
|
+
if (done) {
|
|
1924
|
+
writable.end();
|
|
1925
|
+
} else if (!writable.write(value)) {
|
|
1926
|
+
writable.once("drain", onDrain);
|
|
1927
|
+
} else {
|
|
1928
|
+
return reader.read().then(flow, handleStreamError);
|
|
1929
|
+
}
|
|
1930
|
+
} catch (e) {
|
|
1931
|
+
handleStreamError(e);
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
function writeFromReadableStream(stream, writable) {
|
|
1936
|
+
if (stream.locked) {
|
|
1937
|
+
throw new TypeError("ReadableStream is locked.");
|
|
1938
|
+
} else if (writable.destroyed) {
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
return writeFromReadableStreamDefaultReader(stream.getReader(), writable);
|
|
1942
|
+
}
|
|
1943
|
+
var buildOutgoingHttpHeaders = (headers) => {
|
|
1944
|
+
const res = {};
|
|
1945
|
+
if (!(headers instanceof Headers)) {
|
|
1946
|
+
headers = new Headers(headers ?? void 0);
|
|
1947
|
+
}
|
|
1948
|
+
const cookies = [];
|
|
1949
|
+
for (const [k, v] of headers) {
|
|
1950
|
+
if (k === "set-cookie") {
|
|
1951
|
+
cookies.push(v);
|
|
1952
|
+
} else {
|
|
1953
|
+
res[k] = v;
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
if (cookies.length > 0) {
|
|
1957
|
+
res["set-cookie"] = cookies;
|
|
1958
|
+
}
|
|
1959
|
+
res["content-type"] ??= "text/plain; charset=UTF-8";
|
|
1960
|
+
return res;
|
|
1961
|
+
};
|
|
1962
|
+
var X_ALREADY_SENT = "x-hono-already-sent";
|
|
1963
|
+
if (typeof global.crypto === "undefined") {
|
|
1964
|
+
global.crypto = crypto2;
|
|
1965
|
+
}
|
|
1966
|
+
var outgoingEnded = /* @__PURE__ */ Symbol("outgoingEnded");
|
|
1967
|
+
var incomingDraining = /* @__PURE__ */ Symbol("incomingDraining");
|
|
1968
|
+
var DRAIN_TIMEOUT_MS = 500;
|
|
1969
|
+
var MAX_DRAIN_BYTES = 64 * 1024 * 1024;
|
|
1970
|
+
var drainIncoming = (incoming) => {
|
|
1971
|
+
const incomingWithDrainState = incoming;
|
|
1972
|
+
if (incoming.destroyed || incomingWithDrainState[incomingDraining]) {
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
incomingWithDrainState[incomingDraining] = true;
|
|
1976
|
+
if (incoming instanceof Http2ServerRequest2) {
|
|
1977
|
+
try {
|
|
1978
|
+
;
|
|
1979
|
+
incoming.stream?.close?.(h2constants.NGHTTP2_NO_ERROR);
|
|
1980
|
+
} catch {
|
|
1981
|
+
}
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
let bytesRead = 0;
|
|
1985
|
+
const cleanup = () => {
|
|
1986
|
+
clearTimeout(timer);
|
|
1987
|
+
incoming.off("data", onData);
|
|
1988
|
+
incoming.off("end", cleanup);
|
|
1989
|
+
incoming.off("error", cleanup);
|
|
1990
|
+
};
|
|
1991
|
+
const forceClose = () => {
|
|
1992
|
+
cleanup();
|
|
1993
|
+
const socket = incoming.socket;
|
|
1994
|
+
if (socket && !socket.destroyed) {
|
|
1995
|
+
socket.destroySoon();
|
|
1996
|
+
}
|
|
1997
|
+
};
|
|
1998
|
+
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
1999
|
+
timer.unref?.();
|
|
2000
|
+
const onData = (chunk) => {
|
|
2001
|
+
bytesRead += chunk.length;
|
|
2002
|
+
if (bytesRead > MAX_DRAIN_BYTES) {
|
|
2003
|
+
forceClose();
|
|
2004
|
+
}
|
|
2005
|
+
};
|
|
2006
|
+
incoming.on("data", onData);
|
|
2007
|
+
incoming.on("end", cleanup);
|
|
2008
|
+
incoming.on("error", cleanup);
|
|
2009
|
+
incoming.resume();
|
|
2010
|
+
};
|
|
2011
|
+
var handleRequestError = () => new Response(null, {
|
|
2012
|
+
status: 400
|
|
2013
|
+
});
|
|
2014
|
+
var handleFetchError = (e) => new Response(null, {
|
|
2015
|
+
status: e instanceof Error && (e.name === "TimeoutError" || e.constructor.name === "TimeoutError") ? 504 : 500
|
|
2016
|
+
});
|
|
2017
|
+
var handleResponseError = (e, outgoing) => {
|
|
2018
|
+
const err = e instanceof Error ? e : new Error("unknown error", { cause: e });
|
|
2019
|
+
if (err.code === "ERR_STREAM_PREMATURE_CLOSE") {
|
|
2020
|
+
console.info("The user aborted a request.");
|
|
2021
|
+
} else {
|
|
2022
|
+
console.error(e);
|
|
2023
|
+
if (!outgoing.headersSent) {
|
|
2024
|
+
outgoing.writeHead(500, { "Content-Type": "text/plain" });
|
|
2025
|
+
}
|
|
2026
|
+
outgoing.end(`Error: ${err.message}`);
|
|
2027
|
+
outgoing.destroy(err);
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2030
|
+
var flushHeaders = (outgoing) => {
|
|
2031
|
+
if ("flushHeaders" in outgoing && outgoing.writable) {
|
|
2032
|
+
outgoing.flushHeaders();
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
var responseViaCache = async (res, outgoing) => {
|
|
2036
|
+
let [status, body, header] = res[cacheKey];
|
|
2037
|
+
let hasContentLength = false;
|
|
2038
|
+
if (!header) {
|
|
2039
|
+
header = { "content-type": "text/plain; charset=UTF-8" };
|
|
2040
|
+
} else if (header instanceof Headers) {
|
|
2041
|
+
hasContentLength = header.has("content-length");
|
|
2042
|
+
header = buildOutgoingHttpHeaders(header);
|
|
2043
|
+
} else if (Array.isArray(header)) {
|
|
2044
|
+
const headerObj = new Headers(header);
|
|
2045
|
+
hasContentLength = headerObj.has("content-length");
|
|
2046
|
+
header = buildOutgoingHttpHeaders(headerObj);
|
|
2047
|
+
} else {
|
|
2048
|
+
for (const key in header) {
|
|
2049
|
+
if (key.length === 14 && key.toLowerCase() === "content-length") {
|
|
2050
|
+
hasContentLength = true;
|
|
2051
|
+
break;
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
if (!hasContentLength) {
|
|
2056
|
+
if (typeof body === "string") {
|
|
2057
|
+
header["Content-Length"] = Buffer.byteLength(body);
|
|
2058
|
+
} else if (body instanceof Uint8Array) {
|
|
2059
|
+
header["Content-Length"] = body.byteLength;
|
|
2060
|
+
} else if (body instanceof Blob) {
|
|
2061
|
+
header["Content-Length"] = body.size;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
outgoing.writeHead(status, header);
|
|
2065
|
+
if (typeof body === "string" || body instanceof Uint8Array) {
|
|
2066
|
+
outgoing.end(body);
|
|
2067
|
+
} else if (body instanceof Blob) {
|
|
2068
|
+
outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
2069
|
+
} else {
|
|
2070
|
+
flushHeaders(outgoing);
|
|
2071
|
+
await writeFromReadableStream(body, outgoing)?.catch(
|
|
2072
|
+
(e) => handleResponseError(e, outgoing)
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
;
|
|
2076
|
+
outgoing[outgoingEnded]?.();
|
|
2077
|
+
};
|
|
2078
|
+
var isPromise = (res) => typeof res.then === "function";
|
|
2079
|
+
var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
2080
|
+
if (isPromise(res)) {
|
|
2081
|
+
if (options.errorHandler) {
|
|
2082
|
+
try {
|
|
2083
|
+
res = await res;
|
|
2084
|
+
} catch (err) {
|
|
2085
|
+
const errRes = await options.errorHandler(err);
|
|
2086
|
+
if (!errRes) {
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
res = errRes;
|
|
2090
|
+
}
|
|
2091
|
+
} else {
|
|
2092
|
+
res = await res.catch(handleFetchError);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
if (cacheKey in res) {
|
|
2096
|
+
return responseViaCache(res, outgoing);
|
|
2097
|
+
}
|
|
2098
|
+
const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
|
|
2099
|
+
if (res.body) {
|
|
2100
|
+
const reader = res.body.getReader();
|
|
2101
|
+
const values = [];
|
|
2102
|
+
let done = false;
|
|
2103
|
+
let currentReadPromise = void 0;
|
|
2104
|
+
if (resHeaderRecord["transfer-encoding"] !== "chunked") {
|
|
2105
|
+
let maxReadCount = 2;
|
|
2106
|
+
for (let i = 0; i < maxReadCount; i++) {
|
|
2107
|
+
currentReadPromise ||= reader.read();
|
|
2108
|
+
const chunk = await readWithoutBlocking(currentReadPromise).catch((e) => {
|
|
2109
|
+
console.error(e);
|
|
2110
|
+
done = true;
|
|
2111
|
+
});
|
|
2112
|
+
if (!chunk) {
|
|
2113
|
+
if (i === 1) {
|
|
2114
|
+
await new Promise((resolve) => setTimeout(resolve));
|
|
2115
|
+
maxReadCount = 3;
|
|
2116
|
+
continue;
|
|
2117
|
+
}
|
|
2118
|
+
break;
|
|
2119
|
+
}
|
|
2120
|
+
currentReadPromise = void 0;
|
|
2121
|
+
if (chunk.value) {
|
|
2122
|
+
values.push(chunk.value);
|
|
2123
|
+
}
|
|
2124
|
+
if (chunk.done) {
|
|
2125
|
+
done = true;
|
|
2126
|
+
break;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
if (done && !("content-length" in resHeaderRecord)) {
|
|
2130
|
+
resHeaderRecord["content-length"] = values.reduce((acc, value) => acc + value.length, 0);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
outgoing.writeHead(res.status, resHeaderRecord);
|
|
2134
|
+
values.forEach((value) => {
|
|
2135
|
+
;
|
|
2136
|
+
outgoing.write(value);
|
|
2137
|
+
});
|
|
2138
|
+
if (done) {
|
|
2139
|
+
outgoing.end();
|
|
2140
|
+
} else {
|
|
2141
|
+
if (values.length === 0) {
|
|
2142
|
+
flushHeaders(outgoing);
|
|
2143
|
+
}
|
|
2144
|
+
await writeFromReadableStreamDefaultReader(reader, outgoing, currentReadPromise);
|
|
2145
|
+
}
|
|
2146
|
+
} else if (resHeaderRecord[X_ALREADY_SENT]) {
|
|
2147
|
+
} else {
|
|
2148
|
+
outgoing.writeHead(res.status, resHeaderRecord);
|
|
2149
|
+
outgoing.end();
|
|
2150
|
+
}
|
|
2151
|
+
;
|
|
2152
|
+
outgoing[outgoingEnded]?.();
|
|
2153
|
+
};
|
|
2154
|
+
var getRequestListener = (fetchCallback, options = {}) => {
|
|
2155
|
+
const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
|
|
2156
|
+
if (options.overrideGlobalObjects !== false && global.Request !== Request) {
|
|
2157
|
+
Object.defineProperty(global, "Request", {
|
|
2158
|
+
value: Request
|
|
2159
|
+
});
|
|
2160
|
+
Object.defineProperty(global, "Response", {
|
|
2161
|
+
value: Response2
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
return async (incoming, outgoing) => {
|
|
2165
|
+
let res, req;
|
|
2166
|
+
try {
|
|
2167
|
+
req = newRequest(incoming, options.hostname);
|
|
2168
|
+
let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
|
|
2169
|
+
if (!incomingEnded) {
|
|
2170
|
+
;
|
|
2171
|
+
incoming[wrapBodyStream] = true;
|
|
2172
|
+
incoming.on("end", () => {
|
|
2173
|
+
incomingEnded = true;
|
|
2174
|
+
});
|
|
2175
|
+
if (incoming instanceof Http2ServerRequest2) {
|
|
2176
|
+
;
|
|
2177
|
+
outgoing[outgoingEnded] = () => {
|
|
2178
|
+
if (!incomingEnded) {
|
|
2179
|
+
setTimeout(() => {
|
|
2180
|
+
if (!incomingEnded) {
|
|
2181
|
+
setTimeout(() => {
|
|
2182
|
+
drainIncoming(incoming);
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
outgoing.on("finish", () => {
|
|
2190
|
+
if (!incomingEnded) {
|
|
2191
|
+
drainIncoming(incoming);
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
2194
|
+
}
|
|
2195
|
+
outgoing.on("close", () => {
|
|
2196
|
+
const abortController = req[abortControllerKey];
|
|
2197
|
+
if (abortController) {
|
|
2198
|
+
if (incoming.errored) {
|
|
2199
|
+
req[abortControllerKey].abort(incoming.errored.toString());
|
|
2200
|
+
} else if (!outgoing.writableFinished) {
|
|
2201
|
+
req[abortControllerKey].abort("Client connection prematurely closed.");
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
if (!incomingEnded) {
|
|
2205
|
+
setTimeout(() => {
|
|
2206
|
+
if (!incomingEnded) {
|
|
2207
|
+
setTimeout(() => {
|
|
2208
|
+
drainIncoming(incoming);
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
2214
|
+
res = fetchCallback(req, { incoming, outgoing });
|
|
2215
|
+
if (cacheKey in res) {
|
|
2216
|
+
return responseViaCache(res, outgoing);
|
|
2217
|
+
}
|
|
2218
|
+
} catch (e) {
|
|
2219
|
+
if (!res) {
|
|
2220
|
+
if (options.errorHandler) {
|
|
2221
|
+
res = await options.errorHandler(req ? e : toRequestError(e));
|
|
2222
|
+
if (!res) {
|
|
2223
|
+
return;
|
|
2224
|
+
}
|
|
2225
|
+
} else if (!req) {
|
|
2226
|
+
res = handleRequestError();
|
|
2227
|
+
} else {
|
|
2228
|
+
res = handleFetchError(e);
|
|
2229
|
+
}
|
|
2230
|
+
} else {
|
|
2231
|
+
return handleResponseError(e, outgoing);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
try {
|
|
2235
|
+
return await responseViaResponseObject(res, outgoing, options);
|
|
2236
|
+
} catch (e) {
|
|
2237
|
+
return handleResponseError(e, outgoing);
|
|
2238
|
+
}
|
|
2239
|
+
};
|
|
2240
|
+
};
|
|
2241
|
+
|
|
2242
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js
|
|
2243
|
+
var WebStandardStreamableHTTPServerTransport = class {
|
|
2244
|
+
constructor(options = {}) {
|
|
2245
|
+
this._started = false;
|
|
2246
|
+
this._hasHandledRequest = false;
|
|
2247
|
+
this._streamMapping = /* @__PURE__ */ new Map();
|
|
2248
|
+
this._requestToStreamMapping = /* @__PURE__ */ new Map();
|
|
2249
|
+
this._requestResponseMap = /* @__PURE__ */ new Map();
|
|
2250
|
+
this._initialized = false;
|
|
2251
|
+
this._enableJsonResponse = false;
|
|
2252
|
+
this._standaloneSseStreamId = "_GET_stream";
|
|
2253
|
+
this.sessionIdGenerator = options.sessionIdGenerator;
|
|
2254
|
+
this._enableJsonResponse = options.enableJsonResponse ?? false;
|
|
2255
|
+
this._eventStore = options.eventStore;
|
|
2256
|
+
this._onsessioninitialized = options.onsessioninitialized;
|
|
2257
|
+
this._onsessionclosed = options.onsessionclosed;
|
|
2258
|
+
this._allowedHosts = options.allowedHosts;
|
|
2259
|
+
this._allowedOrigins = options.allowedOrigins;
|
|
2260
|
+
this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;
|
|
2261
|
+
this._retryInterval = options.retryInterval;
|
|
2262
|
+
}
|
|
2263
|
+
/**
|
|
2264
|
+
* Starts the transport. This is required by the Transport interface but is a no-op
|
|
2265
|
+
* for the Streamable HTTP transport as connections are managed per-request.
|
|
2266
|
+
*/
|
|
2267
|
+
async start() {
|
|
2268
|
+
if (this._started) {
|
|
2269
|
+
throw new Error("Transport already started");
|
|
2270
|
+
}
|
|
2271
|
+
this._started = true;
|
|
2272
|
+
}
|
|
2273
|
+
/**
|
|
2274
|
+
* Helper to create a JSON error response
|
|
2275
|
+
*/
|
|
2276
|
+
createJsonErrorResponse(status, code, message, options) {
|
|
2277
|
+
const error = { code, message };
|
|
2278
|
+
if (options?.data !== void 0) {
|
|
2279
|
+
error.data = options.data;
|
|
2280
|
+
}
|
|
2281
|
+
return new Response(JSON.stringify({
|
|
2282
|
+
jsonrpc: "2.0",
|
|
2283
|
+
error,
|
|
2284
|
+
id: null
|
|
2285
|
+
}), {
|
|
2286
|
+
status,
|
|
2287
|
+
headers: {
|
|
2288
|
+
"Content-Type": "application/json",
|
|
2289
|
+
...options?.headers
|
|
2290
|
+
}
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Validates request headers for DNS rebinding protection.
|
|
2295
|
+
* @returns Error response if validation fails, undefined if validation passes.
|
|
2296
|
+
*/
|
|
2297
|
+
validateRequestHeaders(req) {
|
|
2298
|
+
if (!this._enableDnsRebindingProtection) {
|
|
2299
|
+
return void 0;
|
|
2300
|
+
}
|
|
2301
|
+
if (this._allowedHosts && this._allowedHosts.length > 0) {
|
|
2302
|
+
const hostHeader = req.headers.get("host");
|
|
2303
|
+
if (!hostHeader || !this._allowedHosts.includes(hostHeader)) {
|
|
2304
|
+
const error = `Invalid Host header: ${hostHeader}`;
|
|
2305
|
+
this.onerror?.(new Error(error));
|
|
2306
|
+
return this.createJsonErrorResponse(403, -32e3, error);
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
if (this._allowedOrigins && this._allowedOrigins.length > 0) {
|
|
2310
|
+
const originHeader = req.headers.get("origin");
|
|
2311
|
+
if (originHeader && !this._allowedOrigins.includes(originHeader)) {
|
|
2312
|
+
const error = `Invalid Origin header: ${originHeader}`;
|
|
2313
|
+
this.onerror?.(new Error(error));
|
|
2314
|
+
return this.createJsonErrorResponse(403, -32e3, error);
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
return void 0;
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Handles an incoming HTTP request, whether GET, POST, or DELETE
|
|
2321
|
+
* Returns a Response object (Web Standard)
|
|
2322
|
+
*/
|
|
2323
|
+
async handleRequest(req, options) {
|
|
2324
|
+
if (!this.sessionIdGenerator && this._hasHandledRequest) {
|
|
2325
|
+
throw new Error("Stateless transport cannot be reused across requests. Create a new transport per request.");
|
|
2326
|
+
}
|
|
2327
|
+
this._hasHandledRequest = true;
|
|
2328
|
+
const validationError = this.validateRequestHeaders(req);
|
|
2329
|
+
if (validationError) {
|
|
2330
|
+
return validationError;
|
|
2331
|
+
}
|
|
2332
|
+
switch (req.method) {
|
|
2333
|
+
case "POST":
|
|
2334
|
+
return this.handlePostRequest(req, options);
|
|
2335
|
+
case "GET":
|
|
2336
|
+
return this.handleGetRequest(req);
|
|
2337
|
+
case "DELETE":
|
|
2338
|
+
return this.handleDeleteRequest(req);
|
|
2339
|
+
default:
|
|
2340
|
+
return this.handleUnsupportedRequest();
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Writes a priming event to establish resumption capability.
|
|
2345
|
+
* Only sends if eventStore is configured (opt-in for resumability) and
|
|
2346
|
+
* the client's protocol version supports empty SSE data (>= 2025-11-25).
|
|
2347
|
+
*/
|
|
2348
|
+
async writePrimingEvent(controller, encoder, streamId, protocolVersion) {
|
|
2349
|
+
if (!this._eventStore) {
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
if (protocolVersion < "2025-11-25") {
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
const primingEventId = await this._eventStore.storeEvent(streamId, {});
|
|
2356
|
+
let primingEvent = `id: ${primingEventId}
|
|
2357
|
+
data:
|
|
2358
|
+
|
|
2359
|
+
`;
|
|
2360
|
+
if (this._retryInterval !== void 0) {
|
|
2361
|
+
primingEvent = `id: ${primingEventId}
|
|
2362
|
+
retry: ${this._retryInterval}
|
|
2363
|
+
data:
|
|
2364
|
+
|
|
2365
|
+
`;
|
|
2366
|
+
}
|
|
2367
|
+
controller.enqueue(encoder.encode(primingEvent));
|
|
2368
|
+
}
|
|
2369
|
+
/**
|
|
2370
|
+
* Handles GET requests for SSE stream
|
|
2371
|
+
*/
|
|
2372
|
+
async handleGetRequest(req) {
|
|
2373
|
+
const acceptHeader = req.headers.get("accept");
|
|
2374
|
+
if (!acceptHeader?.includes("text/event-stream")) {
|
|
2375
|
+
this.onerror?.(new Error("Not Acceptable: Client must accept text/event-stream"));
|
|
2376
|
+
return this.createJsonErrorResponse(406, -32e3, "Not Acceptable: Client must accept text/event-stream");
|
|
2377
|
+
}
|
|
2378
|
+
const sessionError = this.validateSession(req);
|
|
2379
|
+
if (sessionError) {
|
|
2380
|
+
return sessionError;
|
|
2381
|
+
}
|
|
2382
|
+
const protocolError = this.validateProtocolVersion(req);
|
|
2383
|
+
if (protocolError) {
|
|
2384
|
+
return protocolError;
|
|
2385
|
+
}
|
|
2386
|
+
if (this._eventStore) {
|
|
2387
|
+
const lastEventId = req.headers.get("last-event-id");
|
|
2388
|
+
if (lastEventId) {
|
|
2389
|
+
return this.replayEvents(lastEventId);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
if (this._streamMapping.get(this._standaloneSseStreamId) !== void 0) {
|
|
2393
|
+
this.onerror?.(new Error("Conflict: Only one SSE stream is allowed per session"));
|
|
2394
|
+
return this.createJsonErrorResponse(409, -32e3, "Conflict: Only one SSE stream is allowed per session");
|
|
2395
|
+
}
|
|
2396
|
+
const encoder = new TextEncoder();
|
|
2397
|
+
let streamController;
|
|
2398
|
+
const readable = new ReadableStream({
|
|
2399
|
+
start: (controller) => {
|
|
2400
|
+
streamController = controller;
|
|
2401
|
+
},
|
|
2402
|
+
cancel: () => {
|
|
2403
|
+
this._streamMapping.delete(this._standaloneSseStreamId);
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2406
|
+
const headers = {
|
|
2407
|
+
"Content-Type": "text/event-stream",
|
|
2408
|
+
"Cache-Control": "no-cache, no-transform",
|
|
2409
|
+
Connection: "keep-alive"
|
|
2410
|
+
};
|
|
2411
|
+
if (this.sessionId !== void 0) {
|
|
2412
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
2413
|
+
}
|
|
2414
|
+
this._streamMapping.set(this._standaloneSseStreamId, {
|
|
2415
|
+
controller: streamController,
|
|
2416
|
+
encoder,
|
|
2417
|
+
cleanup: () => {
|
|
2418
|
+
this._streamMapping.delete(this._standaloneSseStreamId);
|
|
2419
|
+
try {
|
|
2420
|
+
streamController.close();
|
|
2421
|
+
} catch {
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
});
|
|
2425
|
+
return new Response(readable, { headers });
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Replays events that would have been sent after the specified event ID
|
|
2429
|
+
* Only used when resumability is enabled
|
|
2430
|
+
*/
|
|
2431
|
+
async replayEvents(lastEventId) {
|
|
2432
|
+
if (!this._eventStore) {
|
|
2433
|
+
this.onerror?.(new Error("Event store not configured"));
|
|
2434
|
+
return this.createJsonErrorResponse(400, -32e3, "Event store not configured");
|
|
2435
|
+
}
|
|
2436
|
+
try {
|
|
2437
|
+
let streamId;
|
|
2438
|
+
if (this._eventStore.getStreamIdForEventId) {
|
|
2439
|
+
streamId = await this._eventStore.getStreamIdForEventId(lastEventId);
|
|
2440
|
+
if (!streamId) {
|
|
2441
|
+
this.onerror?.(new Error("Invalid event ID format"));
|
|
2442
|
+
return this.createJsonErrorResponse(400, -32e3, "Invalid event ID format");
|
|
2443
|
+
}
|
|
2444
|
+
if (this._streamMapping.get(streamId) !== void 0) {
|
|
2445
|
+
this.onerror?.(new Error("Conflict: Stream already has an active connection"));
|
|
2446
|
+
return this.createJsonErrorResponse(409, -32e3, "Conflict: Stream already has an active connection");
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
const headers = {
|
|
2450
|
+
"Content-Type": "text/event-stream",
|
|
2451
|
+
"Cache-Control": "no-cache, no-transform",
|
|
2452
|
+
Connection: "keep-alive"
|
|
2453
|
+
};
|
|
2454
|
+
if (this.sessionId !== void 0) {
|
|
2455
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
2456
|
+
}
|
|
2457
|
+
const encoder = new TextEncoder();
|
|
2458
|
+
let streamController;
|
|
2459
|
+
const readable = new ReadableStream({
|
|
2460
|
+
start: (controller) => {
|
|
2461
|
+
streamController = controller;
|
|
2462
|
+
},
|
|
2463
|
+
cancel: () => {
|
|
2464
|
+
}
|
|
2465
|
+
});
|
|
2466
|
+
const replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, {
|
|
2467
|
+
send: async (eventId, message) => {
|
|
2468
|
+
const success = this.writeSSEEvent(streamController, encoder, message, eventId);
|
|
2469
|
+
if (!success) {
|
|
2470
|
+
this.onerror?.(new Error("Failed replay events"));
|
|
2471
|
+
try {
|
|
2472
|
+
streamController.close();
|
|
2473
|
+
} catch {
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
});
|
|
2478
|
+
this._streamMapping.set(replayedStreamId, {
|
|
2479
|
+
controller: streamController,
|
|
2480
|
+
encoder,
|
|
2481
|
+
cleanup: () => {
|
|
2482
|
+
this._streamMapping.delete(replayedStreamId);
|
|
2483
|
+
try {
|
|
2484
|
+
streamController.close();
|
|
2485
|
+
} catch {
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2489
|
+
return new Response(readable, { headers });
|
|
2490
|
+
} catch (error) {
|
|
2491
|
+
this.onerror?.(error);
|
|
2492
|
+
return this.createJsonErrorResponse(500, -32e3, "Error replaying events");
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Writes an event to an SSE stream via controller with proper formatting
|
|
2497
|
+
*/
|
|
2498
|
+
writeSSEEvent(controller, encoder, message, eventId) {
|
|
2499
|
+
try {
|
|
2500
|
+
let eventData = `event: message
|
|
2501
|
+
`;
|
|
2502
|
+
if (eventId) {
|
|
2503
|
+
eventData += `id: ${eventId}
|
|
2504
|
+
`;
|
|
2505
|
+
}
|
|
2506
|
+
eventData += `data: ${JSON.stringify(message)}
|
|
2507
|
+
|
|
2508
|
+
`;
|
|
2509
|
+
controller.enqueue(encoder.encode(eventData));
|
|
2510
|
+
return true;
|
|
2511
|
+
} catch (error) {
|
|
2512
|
+
this.onerror?.(error);
|
|
2513
|
+
return false;
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
/**
|
|
2517
|
+
* Handles unsupported requests (PUT, PATCH, etc.)
|
|
2518
|
+
*/
|
|
2519
|
+
handleUnsupportedRequest() {
|
|
2520
|
+
this.onerror?.(new Error("Method not allowed."));
|
|
2521
|
+
return new Response(JSON.stringify({
|
|
2522
|
+
jsonrpc: "2.0",
|
|
2523
|
+
error: {
|
|
2524
|
+
code: -32e3,
|
|
2525
|
+
message: "Method not allowed."
|
|
2526
|
+
},
|
|
2527
|
+
id: null
|
|
2528
|
+
}), {
|
|
2529
|
+
status: 405,
|
|
2530
|
+
headers: {
|
|
2531
|
+
Allow: "GET, POST, DELETE",
|
|
2532
|
+
"Content-Type": "application/json"
|
|
2533
|
+
}
|
|
2534
|
+
});
|
|
2535
|
+
}
|
|
2536
|
+
/**
|
|
2537
|
+
* Handles POST requests containing JSON-RPC messages
|
|
2538
|
+
*/
|
|
2539
|
+
async handlePostRequest(req, options) {
|
|
2540
|
+
try {
|
|
2541
|
+
const acceptHeader = req.headers.get("accept");
|
|
2542
|
+
if (!acceptHeader?.includes("application/json") || !acceptHeader.includes("text/event-stream")) {
|
|
2543
|
+
this.onerror?.(new Error("Not Acceptable: Client must accept both application/json and text/event-stream"));
|
|
2544
|
+
return this.createJsonErrorResponse(406, -32e3, "Not Acceptable: Client must accept both application/json and text/event-stream");
|
|
2545
|
+
}
|
|
2546
|
+
const ct = req.headers.get("content-type");
|
|
2547
|
+
if (!ct || !ct.includes("application/json")) {
|
|
2548
|
+
this.onerror?.(new Error("Unsupported Media Type: Content-Type must be application/json"));
|
|
2549
|
+
return this.createJsonErrorResponse(415, -32e3, "Unsupported Media Type: Content-Type must be application/json");
|
|
2550
|
+
}
|
|
2551
|
+
const requestInfo = {
|
|
2552
|
+
headers: Object.fromEntries(req.headers.entries()),
|
|
2553
|
+
url: new URL(req.url)
|
|
2554
|
+
};
|
|
2555
|
+
let rawMessage;
|
|
2556
|
+
if (options?.parsedBody !== void 0) {
|
|
2557
|
+
rawMessage = options.parsedBody;
|
|
2558
|
+
} else {
|
|
2559
|
+
try {
|
|
2560
|
+
rawMessage = await req.json();
|
|
2561
|
+
} catch {
|
|
2562
|
+
this.onerror?.(new Error("Parse error: Invalid JSON"));
|
|
2563
|
+
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON");
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
let messages;
|
|
2567
|
+
try {
|
|
2568
|
+
if (Array.isArray(rawMessage)) {
|
|
2569
|
+
messages = rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg));
|
|
2570
|
+
} else {
|
|
2571
|
+
messages = [JSONRPCMessageSchema.parse(rawMessage)];
|
|
2572
|
+
}
|
|
2573
|
+
} catch {
|
|
2574
|
+
this.onerror?.(new Error("Parse error: Invalid JSON-RPC message"));
|
|
2575
|
+
return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
|
|
2576
|
+
}
|
|
2577
|
+
const isInitializationRequest = messages.some(isInitializeRequest);
|
|
2578
|
+
if (isInitializationRequest) {
|
|
2579
|
+
if (this._initialized && this.sessionId !== void 0) {
|
|
2580
|
+
this.onerror?.(new Error("Invalid Request: Server already initialized"));
|
|
2581
|
+
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
|
|
2582
|
+
}
|
|
2583
|
+
if (messages.length > 1) {
|
|
2584
|
+
this.onerror?.(new Error("Invalid Request: Only one initialization request is allowed"));
|
|
2585
|
+
return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
|
|
2586
|
+
}
|
|
2587
|
+
this.sessionId = this.sessionIdGenerator?.();
|
|
2588
|
+
this._initialized = true;
|
|
2589
|
+
if (this.sessionId && this._onsessioninitialized) {
|
|
2590
|
+
await Promise.resolve(this._onsessioninitialized(this.sessionId));
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
if (!isInitializationRequest) {
|
|
2594
|
+
const sessionError = this.validateSession(req);
|
|
2595
|
+
if (sessionError) {
|
|
2596
|
+
return sessionError;
|
|
2597
|
+
}
|
|
2598
|
+
const protocolError = this.validateProtocolVersion(req);
|
|
2599
|
+
if (protocolError) {
|
|
2600
|
+
return protocolError;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
const hasRequests = messages.some(isJSONRPCRequest);
|
|
2604
|
+
if (!hasRequests) {
|
|
2605
|
+
for (const message of messages) {
|
|
2606
|
+
this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo });
|
|
2607
|
+
}
|
|
2608
|
+
return new Response(null, { status: 202 });
|
|
2609
|
+
}
|
|
2610
|
+
const streamId = crypto.randomUUID();
|
|
2611
|
+
const initRequest = messages.find((m) => isInitializeRequest(m));
|
|
2612
|
+
const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
|
|
2613
|
+
if (this._enableJsonResponse) {
|
|
2614
|
+
return new Promise((resolve) => {
|
|
2615
|
+
this._streamMapping.set(streamId, {
|
|
2616
|
+
resolveJson: resolve,
|
|
2617
|
+
cleanup: () => {
|
|
2618
|
+
this._streamMapping.delete(streamId);
|
|
2619
|
+
}
|
|
2620
|
+
});
|
|
2621
|
+
for (const message of messages) {
|
|
2622
|
+
if (isJSONRPCRequest(message)) {
|
|
2623
|
+
this._requestToStreamMapping.set(message.id, streamId);
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
for (const message of messages) {
|
|
2627
|
+
this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo });
|
|
2628
|
+
}
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
const encoder = new TextEncoder();
|
|
2632
|
+
let streamController;
|
|
2633
|
+
const readable = new ReadableStream({
|
|
2634
|
+
start: (controller) => {
|
|
2635
|
+
streamController = controller;
|
|
2636
|
+
},
|
|
2637
|
+
cancel: () => {
|
|
2638
|
+
this._streamMapping.delete(streamId);
|
|
2639
|
+
}
|
|
2640
|
+
});
|
|
2641
|
+
const headers = {
|
|
2642
|
+
"Content-Type": "text/event-stream",
|
|
2643
|
+
"Cache-Control": "no-cache",
|
|
2644
|
+
Connection: "keep-alive"
|
|
2645
|
+
};
|
|
2646
|
+
if (this.sessionId !== void 0) {
|
|
2647
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
2648
|
+
}
|
|
2649
|
+
for (const message of messages) {
|
|
2650
|
+
if (isJSONRPCRequest(message)) {
|
|
2651
|
+
this._streamMapping.set(streamId, {
|
|
2652
|
+
controller: streamController,
|
|
2653
|
+
encoder,
|
|
2654
|
+
cleanup: () => {
|
|
2655
|
+
this._streamMapping.delete(streamId);
|
|
2656
|
+
try {
|
|
2657
|
+
streamController.close();
|
|
2658
|
+
} catch {
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
});
|
|
2662
|
+
this._requestToStreamMapping.set(message.id, streamId);
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
|
|
2666
|
+
for (const message of messages) {
|
|
2667
|
+
let closeSSEStream;
|
|
2668
|
+
let closeStandaloneSSEStream;
|
|
2669
|
+
if (isJSONRPCRequest(message) && this._eventStore && clientProtocolVersion >= "2025-11-25") {
|
|
2670
|
+
closeSSEStream = () => {
|
|
2671
|
+
this.closeSSEStream(message.id);
|
|
2672
|
+
};
|
|
2673
|
+
closeStandaloneSSEStream = () => {
|
|
2674
|
+
this.closeStandaloneSSEStream();
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo, closeSSEStream, closeStandaloneSSEStream });
|
|
2678
|
+
}
|
|
2679
|
+
return new Response(readable, { status: 200, headers });
|
|
2680
|
+
} catch (error) {
|
|
2681
|
+
this.onerror?.(error);
|
|
2682
|
+
return this.createJsonErrorResponse(400, -32700, "Parse error", { data: String(error) });
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* Handles DELETE requests to terminate sessions
|
|
2687
|
+
*/
|
|
2688
|
+
async handleDeleteRequest(req) {
|
|
2689
|
+
const sessionError = this.validateSession(req);
|
|
2690
|
+
if (sessionError) {
|
|
2691
|
+
return sessionError;
|
|
2692
|
+
}
|
|
2693
|
+
const protocolError = this.validateProtocolVersion(req);
|
|
2694
|
+
if (protocolError) {
|
|
2695
|
+
return protocolError;
|
|
2696
|
+
}
|
|
2697
|
+
await Promise.resolve(this._onsessionclosed?.(this.sessionId));
|
|
2698
|
+
await this.close();
|
|
2699
|
+
return new Response(null, { status: 200 });
|
|
2700
|
+
}
|
|
2701
|
+
/**
|
|
2702
|
+
* Validates session ID for non-initialization requests.
|
|
2703
|
+
* Returns Response error if invalid, undefined otherwise
|
|
2704
|
+
*/
|
|
2705
|
+
validateSession(req) {
|
|
2706
|
+
if (this.sessionIdGenerator === void 0) {
|
|
2707
|
+
return void 0;
|
|
2708
|
+
}
|
|
2709
|
+
if (!this._initialized) {
|
|
2710
|
+
this.onerror?.(new Error("Bad Request: Server not initialized"));
|
|
2711
|
+
return this.createJsonErrorResponse(400, -32e3, "Bad Request: Server not initialized");
|
|
2712
|
+
}
|
|
2713
|
+
const sessionId = req.headers.get("mcp-session-id");
|
|
2714
|
+
if (!sessionId) {
|
|
2715
|
+
this.onerror?.(new Error("Bad Request: Mcp-Session-Id header is required"));
|
|
2716
|
+
return this.createJsonErrorResponse(400, -32e3, "Bad Request: Mcp-Session-Id header is required");
|
|
2717
|
+
}
|
|
2718
|
+
if (sessionId !== this.sessionId) {
|
|
2719
|
+
this.onerror?.(new Error("Session not found"));
|
|
2720
|
+
return this.createJsonErrorResponse(404, -32001, "Session not found");
|
|
2721
|
+
}
|
|
2722
|
+
return void 0;
|
|
2723
|
+
}
|
|
2724
|
+
/**
|
|
2725
|
+
* Validates the MCP-Protocol-Version header on incoming requests.
|
|
2726
|
+
*
|
|
2727
|
+
* For initialization: Version negotiation handles unknown versions gracefully
|
|
2728
|
+
* (server responds with its supported version).
|
|
2729
|
+
*
|
|
2730
|
+
* For subsequent requests with MCP-Protocol-Version header:
|
|
2731
|
+
* - Accept if in supported list
|
|
2732
|
+
* - 400 if unsupported
|
|
2733
|
+
*
|
|
2734
|
+
* For HTTP requests without the MCP-Protocol-Version header:
|
|
2735
|
+
* - Accept and default to the version negotiated at initialization
|
|
2736
|
+
*/
|
|
2737
|
+
validateProtocolVersion(req) {
|
|
2738
|
+
const protocolVersion = req.headers.get("mcp-protocol-version");
|
|
2739
|
+
if (protocolVersion !== null && !SUPPORTED_PROTOCOL_VERSIONS.includes(protocolVersion)) {
|
|
2740
|
+
this.onerror?.(new Error(`Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${SUPPORTED_PROTOCOL_VERSIONS.join(", ")})`));
|
|
2741
|
+
return this.createJsonErrorResponse(400, -32e3, `Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${SUPPORTED_PROTOCOL_VERSIONS.join(", ")})`);
|
|
2742
|
+
}
|
|
2743
|
+
return void 0;
|
|
2744
|
+
}
|
|
2745
|
+
async close() {
|
|
2746
|
+
this._streamMapping.forEach(({ cleanup }) => {
|
|
2747
|
+
cleanup();
|
|
2748
|
+
});
|
|
2749
|
+
this._streamMapping.clear();
|
|
2750
|
+
this._requestResponseMap.clear();
|
|
2751
|
+
this.onclose?.();
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Close an SSE stream for a specific request, triggering client reconnection.
|
|
2755
|
+
* Use this to implement polling behavior during long-running operations -
|
|
2756
|
+
* client will reconnect after the retry interval specified in the priming event.
|
|
2757
|
+
*/
|
|
2758
|
+
closeSSEStream(requestId) {
|
|
2759
|
+
const streamId = this._requestToStreamMapping.get(requestId);
|
|
2760
|
+
if (!streamId)
|
|
2761
|
+
return;
|
|
2762
|
+
const stream = this._streamMapping.get(streamId);
|
|
2763
|
+
if (stream) {
|
|
2764
|
+
stream.cleanup();
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
/**
|
|
2768
|
+
* Close the standalone GET SSE stream, triggering client reconnection.
|
|
2769
|
+
* Use this to implement polling behavior for server-initiated notifications.
|
|
2770
|
+
*/
|
|
2771
|
+
closeStandaloneSSEStream() {
|
|
2772
|
+
const stream = this._streamMapping.get(this._standaloneSseStreamId);
|
|
2773
|
+
if (stream) {
|
|
2774
|
+
stream.cleanup();
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
async send(message, options) {
|
|
2778
|
+
let requestId = options?.relatedRequestId;
|
|
2779
|
+
if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
|
|
2780
|
+
requestId = message.id;
|
|
2781
|
+
}
|
|
2782
|
+
if (requestId === void 0) {
|
|
2783
|
+
if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
|
|
2784
|
+
throw new Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");
|
|
2785
|
+
}
|
|
2786
|
+
let eventId;
|
|
2787
|
+
if (this._eventStore) {
|
|
2788
|
+
eventId = await this._eventStore.storeEvent(this._standaloneSseStreamId, message);
|
|
2789
|
+
}
|
|
2790
|
+
const standaloneSse = this._streamMapping.get(this._standaloneSseStreamId);
|
|
2791
|
+
if (standaloneSse === void 0) {
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
if (standaloneSse.controller && standaloneSse.encoder) {
|
|
2795
|
+
this.writeSSEEvent(standaloneSse.controller, standaloneSse.encoder, message, eventId);
|
|
2796
|
+
}
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
const streamId = this._requestToStreamMapping.get(requestId);
|
|
2800
|
+
if (!streamId) {
|
|
2801
|
+
throw new Error(`No connection established for request ID: ${String(requestId)}`);
|
|
2802
|
+
}
|
|
2803
|
+
const stream = this._streamMapping.get(streamId);
|
|
2804
|
+
if (!this._enableJsonResponse && stream?.controller && stream?.encoder) {
|
|
2805
|
+
let eventId;
|
|
2806
|
+
if (this._eventStore) {
|
|
2807
|
+
eventId = await this._eventStore.storeEvent(streamId, message);
|
|
2808
|
+
}
|
|
2809
|
+
this.writeSSEEvent(stream.controller, stream.encoder, message, eventId);
|
|
2810
|
+
}
|
|
2811
|
+
if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
|
|
2812
|
+
this._requestResponseMap.set(requestId, message);
|
|
2813
|
+
const relatedIds = Array.from(this._requestToStreamMapping.entries()).filter(([_, sid]) => sid === streamId).map(([id]) => id);
|
|
2814
|
+
const allResponsesReady = relatedIds.every((id) => this._requestResponseMap.has(id));
|
|
2815
|
+
if (allResponsesReady) {
|
|
2816
|
+
if (!stream) {
|
|
2817
|
+
throw new Error(`No connection established for request ID: ${String(requestId)}`);
|
|
2818
|
+
}
|
|
2819
|
+
if (this._enableJsonResponse && stream.resolveJson) {
|
|
2820
|
+
const headers = {
|
|
2821
|
+
"Content-Type": "application/json"
|
|
2822
|
+
};
|
|
2823
|
+
if (this.sessionId !== void 0) {
|
|
2824
|
+
headers["mcp-session-id"] = this.sessionId;
|
|
2825
|
+
}
|
|
2826
|
+
const responses = relatedIds.map((id) => this._requestResponseMap.get(id));
|
|
2827
|
+
if (responses.length === 1) {
|
|
2828
|
+
stream.resolveJson(new Response(JSON.stringify(responses[0]), { status: 200, headers }));
|
|
2829
|
+
} else {
|
|
2830
|
+
stream.resolveJson(new Response(JSON.stringify(responses), { status: 200, headers }));
|
|
2831
|
+
}
|
|
2832
|
+
} else {
|
|
2833
|
+
stream.cleanup();
|
|
2834
|
+
}
|
|
2835
|
+
for (const id of relatedIds) {
|
|
2836
|
+
this._requestResponseMap.delete(id);
|
|
2837
|
+
this._requestToStreamMapping.delete(id);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
|
|
2844
|
+
// node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/streamableHttp.js
|
|
2845
|
+
var StreamableHTTPServerTransport = class {
|
|
2846
|
+
constructor(options = {}) {
|
|
2847
|
+
this._requestContext = /* @__PURE__ */ new WeakMap();
|
|
2848
|
+
this._webStandardTransport = new WebStandardStreamableHTTPServerTransport(options);
|
|
2849
|
+
this._requestListener = getRequestListener(async (webRequest) => {
|
|
2850
|
+
const context = this._requestContext.get(webRequest);
|
|
2851
|
+
return this._webStandardTransport.handleRequest(webRequest, {
|
|
2852
|
+
authInfo: context?.authInfo,
|
|
2853
|
+
parsedBody: context?.parsedBody
|
|
2854
|
+
});
|
|
2855
|
+
}, { overrideGlobalObjects: false });
|
|
2856
|
+
}
|
|
2857
|
+
/**
|
|
2858
|
+
* Gets the session ID for this transport instance.
|
|
2859
|
+
*/
|
|
2860
|
+
get sessionId() {
|
|
2861
|
+
return this._webStandardTransport.sessionId;
|
|
2862
|
+
}
|
|
2863
|
+
/**
|
|
2864
|
+
* Sets callback for when the transport is closed.
|
|
2865
|
+
*/
|
|
2866
|
+
set onclose(handler) {
|
|
2867
|
+
this._webStandardTransport.onclose = handler;
|
|
2868
|
+
}
|
|
2869
|
+
get onclose() {
|
|
2870
|
+
return this._webStandardTransport.onclose;
|
|
2871
|
+
}
|
|
2872
|
+
/**
|
|
2873
|
+
* Sets callback for transport errors.
|
|
2874
|
+
*/
|
|
2875
|
+
set onerror(handler) {
|
|
2876
|
+
this._webStandardTransport.onerror = handler;
|
|
2877
|
+
}
|
|
2878
|
+
get onerror() {
|
|
2879
|
+
return this._webStandardTransport.onerror;
|
|
2880
|
+
}
|
|
2881
|
+
/**
|
|
2882
|
+
* Sets callback for incoming messages.
|
|
2883
|
+
*/
|
|
2884
|
+
set onmessage(handler) {
|
|
2885
|
+
this._webStandardTransport.onmessage = handler;
|
|
2886
|
+
}
|
|
2887
|
+
get onmessage() {
|
|
2888
|
+
return this._webStandardTransport.onmessage;
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Starts the transport. This is required by the Transport interface but is a no-op
|
|
2892
|
+
* for the Streamable HTTP transport as connections are managed per-request.
|
|
2893
|
+
*/
|
|
2894
|
+
async start() {
|
|
2895
|
+
return this._webStandardTransport.start();
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Closes the transport and all active connections.
|
|
2899
|
+
*/
|
|
2900
|
+
async close() {
|
|
2901
|
+
return this._webStandardTransport.close();
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Sends a JSON-RPC message through the transport.
|
|
2905
|
+
*/
|
|
2906
|
+
async send(message, options) {
|
|
2907
|
+
return this._webStandardTransport.send(message, options);
|
|
2908
|
+
}
|
|
2909
|
+
/**
|
|
2910
|
+
* Handles an incoming HTTP request, whether GET or POST.
|
|
2911
|
+
*
|
|
2912
|
+
* This method converts Node.js HTTP objects to Web Standard Request/Response
|
|
2913
|
+
* and delegates to the underlying WebStandardStreamableHTTPServerTransport.
|
|
2914
|
+
*
|
|
2915
|
+
* @param req - Node.js IncomingMessage, optionally with auth property from middleware
|
|
2916
|
+
* @param res - Node.js ServerResponse
|
|
2917
|
+
* @param parsedBody - Optional pre-parsed body from body-parser middleware
|
|
2918
|
+
*/
|
|
2919
|
+
async handleRequest(req, res, parsedBody) {
|
|
2920
|
+
const authInfo = req.auth;
|
|
2921
|
+
const handler = getRequestListener(async (webRequest) => {
|
|
2922
|
+
return this._webStandardTransport.handleRequest(webRequest, {
|
|
2923
|
+
authInfo,
|
|
2924
|
+
parsedBody
|
|
2925
|
+
});
|
|
2926
|
+
}, { overrideGlobalObjects: false });
|
|
2927
|
+
await handler(req, res);
|
|
2928
|
+
}
|
|
2929
|
+
/**
|
|
2930
|
+
* Close an SSE stream for a specific request, triggering client reconnection.
|
|
2931
|
+
* Use this to implement polling behavior during long-running operations -
|
|
2932
|
+
* client will reconnect after the retry interval specified in the priming event.
|
|
2933
|
+
*/
|
|
2934
|
+
closeSSEStream(requestId) {
|
|
2935
|
+
this._webStandardTransport.closeSSEStream(requestId);
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* Close the standalone GET SSE stream, triggering client reconnection.
|
|
2939
|
+
* Use this to implement polling behavior for server-initiated notifications.
|
|
2940
|
+
*/
|
|
2941
|
+
closeStandaloneSSEStream() {
|
|
2942
|
+
this._webStandardTransport.closeStandaloneSSEStream();
|
|
2943
|
+
}
|
|
2944
|
+
};
|
|
2945
|
+
|
|
2946
|
+
// node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/serve.js
|
|
2947
|
+
var TOOL_SEPARATOR = "__";
|
|
2948
|
+
var DEFAULT_OBJECT_SCHEMA = { type: "object" };
|
|
2949
|
+
var DEFAULT_SERVE_HTTP_HOST = "127.0.0.1";
|
|
2950
|
+
async function serveStdio(options) {
|
|
2951
|
+
const server = createBridgeServer(options);
|
|
2952
|
+
const transport = new StdioServerTransport();
|
|
2953
|
+
const closed = new Promise((resolve, reject) => {
|
|
2954
|
+
transport.onclose = () => resolve();
|
|
2955
|
+
transport.onerror = (error) => reject(error);
|
|
2956
|
+
});
|
|
2957
|
+
await server.connect(transport);
|
|
2958
|
+
await closed;
|
|
2959
|
+
}
|
|
2960
|
+
async function serveHttp(options) {
|
|
2961
|
+
const httpServer = http.createServer((request, response) => {
|
|
2962
|
+
const url = new URL(request.url ?? "/", `http://${DEFAULT_SERVE_HTTP_HOST}`);
|
|
2963
|
+
let bridgeOptions;
|
|
2964
|
+
if (url.pathname === "/mcp") {
|
|
2965
|
+
bridgeOptions = options;
|
|
2966
|
+
} else if (url.pathname.startsWith("/mcp/")) {
|
|
2967
|
+
let only;
|
|
2968
|
+
try {
|
|
2969
|
+
only = decodeURIComponent(url.pathname.slice("/mcp/".length));
|
|
2970
|
+
} catch {
|
|
2971
|
+
response.writeHead(400).end("Bad request");
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
const known = selectServedServers(options.definitions, options.servers).some((served) => served.name === only);
|
|
2975
|
+
if (!known) {
|
|
2976
|
+
response.writeHead(404, { "content-type": "text/plain; charset=utf-8" }).end(`Unknown server '${only}'`);
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
bridgeOptions = { ...options, servers: [only], bare: true };
|
|
2980
|
+
} else {
|
|
2981
|
+
response.writeHead(404).end("Not found");
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
const bridgeServer = createBridgeServer(bridgeOptions);
|
|
2985
|
+
const transport = new StreamableHTTPServerTransport({
|
|
2986
|
+
sessionIdGenerator: void 0
|
|
2987
|
+
});
|
|
2988
|
+
response.on("close", () => {
|
|
2989
|
+
void transport.close().catch(() => {
|
|
2990
|
+
});
|
|
2991
|
+
void bridgeServer.close().catch(() => {
|
|
2992
|
+
});
|
|
2993
|
+
});
|
|
2994
|
+
void (async () => {
|
|
2995
|
+
await bridgeServer.connect(transport);
|
|
2996
|
+
await transport.handleRequest(request, response);
|
|
2997
|
+
})().catch((error) => {
|
|
2998
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2999
|
+
if (!response.headersSent) {
|
|
3000
|
+
response.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
|
|
3001
|
+
}
|
|
3002
|
+
response.end(message);
|
|
3003
|
+
});
|
|
3004
|
+
});
|
|
3005
|
+
await new Promise((resolve, reject) => {
|
|
3006
|
+
httpServer.once("error", reject);
|
|
3007
|
+
httpServer.listen(options.port, options.host ?? DEFAULT_SERVE_HTTP_HOST, () => {
|
|
3008
|
+
httpServer.off("error", reject);
|
|
3009
|
+
resolve();
|
|
3010
|
+
});
|
|
3011
|
+
});
|
|
3012
|
+
return httpServer;
|
|
3013
|
+
}
|
|
3014
|
+
function createBridgeServer(options) {
|
|
3015
|
+
const servedServers = selectServedServers(options.definitions, options.servers);
|
|
3016
|
+
const [firstServed] = servedServers;
|
|
3017
|
+
if (!firstServed) {
|
|
3018
|
+
throw new Error("No keep-alive MCP servers are available to serve.");
|
|
3019
|
+
}
|
|
3020
|
+
const bare = options.bare === true;
|
|
3021
|
+
if (bare && servedServers.length !== 1) {
|
|
3022
|
+
throw new Error("Bare serve mode requires exactly one served server.");
|
|
3023
|
+
}
|
|
3024
|
+
const server = new McpServer({ name: "mcporter-serve", version: MCPORTER_VERSION }, {
|
|
3025
|
+
capabilities: {
|
|
3026
|
+
tools: {}
|
|
3027
|
+
},
|
|
3028
|
+
instructions: bare ? `MCPorter bridge exposing the '${firstServed.name}' server.` : "MCPorter bridge exposing daemon-managed MCP servers. Tool names are namespaced as server__tool."
|
|
3029
|
+
});
|
|
3030
|
+
server.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
3031
|
+
const tools = [];
|
|
3032
|
+
for (const served of servedServers) {
|
|
3033
|
+
const listed = await options.runtime.listTools(served.name, {
|
|
3034
|
+
includeSchema: true,
|
|
3035
|
+
autoAuthorize: true
|
|
3036
|
+
});
|
|
3037
|
+
for (const tool of listed) {
|
|
3038
|
+
tools.push({
|
|
3039
|
+
name: bare ? tool.name : encodeToolName(served.name, tool.name),
|
|
3040
|
+
description: bare ? tool.description : describeTool(served.name, tool.description),
|
|
3041
|
+
inputSchema: normalizeInputSchema(tool.inputSchema),
|
|
3042
|
+
outputSchema: normalizeOutputSchema(tool.outputSchema)
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
return { tools };
|
|
3047
|
+
});
|
|
3048
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
3049
|
+
const target = bare ? { server: firstServed.name, tool: request.params.name } : decodeToolName(request.params.name, servedServers);
|
|
3050
|
+
if (!target) {
|
|
3051
|
+
throw new McpError(ErrorCode.InvalidParams, `Unknown bridged tool '${request.params.name}'.`);
|
|
3052
|
+
}
|
|
3053
|
+
const result = await options.runtime.callTool(target.server, target.tool, {
|
|
3054
|
+
args: request.params.arguments
|
|
3055
|
+
});
|
|
3056
|
+
return result;
|
|
3057
|
+
});
|
|
3058
|
+
return server;
|
|
3059
|
+
}
|
|
3060
|
+
function selectServedServers(definitions, requested) {
|
|
3061
|
+
const keepAlive = definitions.filter(isKeepAliveServer);
|
|
3062
|
+
if (!requested || requested.length === 0) {
|
|
3063
|
+
return keepAlive.map((definition) => ({ name: definition.name, definition }));
|
|
3064
|
+
}
|
|
3065
|
+
const byName = new Map(keepAlive.map((definition) => [definition.name, definition]));
|
|
3066
|
+
return requested.map((name) => {
|
|
3067
|
+
const definition = byName.get(name);
|
|
3068
|
+
if (!definition) {
|
|
3069
|
+
throw new Error(`Server '${name}' is not configured for keep-alive and cannot be served by the daemon bridge.`);
|
|
3070
|
+
}
|
|
3071
|
+
return { name, definition };
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
3074
|
+
function encodeToolName(server, tool) {
|
|
3075
|
+
return `${encodeToolNamePart(server, { escapeTrailingUnderscore: true })}${TOOL_SEPARATOR}${encodeToolNamePart(tool, { escapeLeadingUnderscore: true })}`;
|
|
3076
|
+
}
|
|
3077
|
+
function decodeToolName(name, servedServers) {
|
|
3078
|
+
const candidates = servedServers.map((served) => ({
|
|
3079
|
+
name: served.name,
|
|
3080
|
+
prefix: `${encodeToolNamePart(served.name, { escapeTrailingUnderscore: true })}${TOOL_SEPARATOR}`
|
|
3081
|
+
})).toSorted((first, second) => second.prefix.length - first.prefix.length);
|
|
3082
|
+
for (const candidate of candidates) {
|
|
3083
|
+
if (!name.startsWith(candidate.prefix)) {
|
|
3084
|
+
continue;
|
|
3085
|
+
}
|
|
3086
|
+
const tool = decodeToolNamePart(name.slice(candidate.prefix.length));
|
|
3087
|
+
if (tool.length === 0) {
|
|
3088
|
+
return void 0;
|
|
3089
|
+
}
|
|
3090
|
+
return { server: candidate.name, tool };
|
|
3091
|
+
}
|
|
3092
|
+
return void 0;
|
|
3093
|
+
}
|
|
3094
|
+
function encodeToolNamePart(value, options = {}) {
|
|
3095
|
+
let encoded = value.replaceAll("%", "%25").replaceAll(TOOL_SEPARATOR, "%5F%5F");
|
|
3096
|
+
if (options.escapeLeadingUnderscore && encoded.startsWith("_")) {
|
|
3097
|
+
encoded = `%5F${encoded.slice(1)}`;
|
|
3098
|
+
}
|
|
3099
|
+
if (options.escapeTrailingUnderscore && encoded.endsWith("_")) {
|
|
3100
|
+
encoded = `${encoded.slice(0, -1)}%5F`;
|
|
3101
|
+
}
|
|
3102
|
+
return encoded;
|
|
3103
|
+
}
|
|
3104
|
+
function decodeToolNamePart(value) {
|
|
3105
|
+
return value.replaceAll("%5F", "_").replaceAll("%25", "%");
|
|
3106
|
+
}
|
|
3107
|
+
function describeTool(server, description) {
|
|
3108
|
+
if (!description) {
|
|
3109
|
+
return `Tool from MCPorter server '${server}'.`;
|
|
3110
|
+
}
|
|
3111
|
+
return `[${server}] ${description}`;
|
|
3112
|
+
}
|
|
3113
|
+
function normalizeInputSchema(schema) {
|
|
3114
|
+
if (isObjectSchema(schema)) {
|
|
3115
|
+
return schema;
|
|
3116
|
+
}
|
|
3117
|
+
return DEFAULT_OBJECT_SCHEMA;
|
|
3118
|
+
}
|
|
3119
|
+
function normalizeOutputSchema(schema) {
|
|
3120
|
+
if (isObjectSchema(schema)) {
|
|
3121
|
+
return schema;
|
|
3122
|
+
}
|
|
3123
|
+
return void 0;
|
|
3124
|
+
}
|
|
3125
|
+
function isObjectSchema(schema) {
|
|
3126
|
+
if (!schema || typeof schema !== "object") {
|
|
3127
|
+
return false;
|
|
3128
|
+
}
|
|
3129
|
+
return schema.type === "object";
|
|
3130
|
+
}
|
|
3131
|
+
|
|
3132
|
+
// node_modules/.pnpm/mcporter@0.12.3/node_modules/mcporter/dist/cli/serve-command.js
|
|
3133
|
+
async function handleServeCli(args, options) {
|
|
3134
|
+
const parsed = parseServeArgs(args);
|
|
3135
|
+
const baseRuntime = await createRuntime({
|
|
3136
|
+
configPath: options.configExplicit ? options.configPath : void 0,
|
|
3137
|
+
rootDir: options.rootDir
|
|
3138
|
+
});
|
|
3139
|
+
const definitions = baseRuntime.getDefinitions();
|
|
3140
|
+
const keepAliveServers = new Set(definitions.filter(isKeepAliveServer).map((definition) => definition.name));
|
|
3141
|
+
let selectedServers;
|
|
3142
|
+
try {
|
|
3143
|
+
const servedServers = selectServedServers(definitions, parsed.servers);
|
|
3144
|
+
selectedServers = servedServers.map((server) => server.name);
|
|
3145
|
+
if (selectedServers.length === 0) {
|
|
3146
|
+
throw new Error("No MCP servers are configured for keep-alive; nothing to serve.");
|
|
3147
|
+
}
|
|
3148
|
+
} catch (error) {
|
|
3149
|
+
await baseRuntime.close().catch(() => {
|
|
3150
|
+
});
|
|
3151
|
+
throw error;
|
|
3152
|
+
}
|
|
3153
|
+
const daemonClient = new DaemonClient({
|
|
3154
|
+
configPath: options.configPath,
|
|
3155
|
+
configExplicit: options.configExplicit,
|
|
3156
|
+
rootDir: options.rootDir
|
|
3157
|
+
});
|
|
3158
|
+
const runtime = createKeepAliveRuntime(baseRuntime, {
|
|
3159
|
+
daemonClient,
|
|
3160
|
+
keepAliveServers
|
|
3161
|
+
});
|
|
3162
|
+
if (parsed.mode === "http") {
|
|
3163
|
+
let server;
|
|
3164
|
+
try {
|
|
3165
|
+
server = await serveHttp({
|
|
3166
|
+
runtime,
|
|
3167
|
+
definitions,
|
|
3168
|
+
servers: selectedServers,
|
|
3169
|
+
port: parsed.port ?? 0,
|
|
3170
|
+
host: parsed.host
|
|
3171
|
+
});
|
|
3172
|
+
} catch (error) {
|
|
3173
|
+
await runtime.close().catch(() => {
|
|
3174
|
+
});
|
|
3175
|
+
throw error;
|
|
3176
|
+
}
|
|
3177
|
+
server.once("close", () => {
|
|
3178
|
+
void runtime.close().catch(() => {
|
|
3179
|
+
});
|
|
3180
|
+
});
|
|
3181
|
+
const address = server.address();
|
|
3182
|
+
const location = typeof address === "object" && address ? `http://${address.address === "::" ? "localhost" : address.address}:${address.port}/mcp` : "listening";
|
|
3183
|
+
console.error(`MCPorter serve HTTP bridge ${location}`);
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
try {
|
|
3187
|
+
await serveStdio({
|
|
3188
|
+
runtime,
|
|
3189
|
+
definitions,
|
|
3190
|
+
servers: selectedServers
|
|
3191
|
+
});
|
|
3192
|
+
} finally {
|
|
3193
|
+
await runtime.close().catch(() => {
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
function printServeHelp() {
|
|
3198
|
+
console.log(`Usage: mcporter serve [--servers a,b,c] [--stdio | --http <port>]
|
|
3199
|
+
|
|
3200
|
+
Expose daemon-managed keep-alive MCP servers as one MCP server.
|
|
3201
|
+
|
|
3202
|
+
Flags:
|
|
3203
|
+
--servers <csv> Restrict the bridge to the listed keep-alive server names.
|
|
3204
|
+
--stdio Serve MCP over stdio (default).
|
|
3205
|
+
--http <port> Serve MCP Streamable HTTP on /mcp and /mcp/<server>.
|
|
3206
|
+
--host <host> Host for --http (default: ${DEFAULT_SERVE_HTTP_HOST}).`);
|
|
3207
|
+
}
|
|
3208
|
+
function parseServeArgs(args) {
|
|
3209
|
+
let mode = "stdio";
|
|
3210
|
+
let port;
|
|
3211
|
+
let host;
|
|
3212
|
+
let servers;
|
|
3213
|
+
let explicitStdio = false;
|
|
3214
|
+
let explicitHttp = false;
|
|
3215
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
3216
|
+
const token = args[index];
|
|
3217
|
+
if (!token) {
|
|
3218
|
+
continue;
|
|
3219
|
+
}
|
|
3220
|
+
if (token === "--stdio") {
|
|
3221
|
+
explicitStdio = true;
|
|
3222
|
+
mode = "stdio";
|
|
3223
|
+
continue;
|
|
3224
|
+
}
|
|
3225
|
+
if (token === "--http") {
|
|
3226
|
+
explicitHttp = true;
|
|
3227
|
+
mode = "http";
|
|
3228
|
+
const value = args[index + 1];
|
|
3229
|
+
if (!value) {
|
|
3230
|
+
throw new Error("Flag '--http' requires a port.");
|
|
3231
|
+
}
|
|
3232
|
+
port = parsePort(value);
|
|
3233
|
+
index += 1;
|
|
3234
|
+
continue;
|
|
3235
|
+
}
|
|
3236
|
+
if (token.startsWith("--http=")) {
|
|
3237
|
+
explicitHttp = true;
|
|
3238
|
+
mode = "http";
|
|
3239
|
+
port = parsePort(token.slice("--http=".length));
|
|
3240
|
+
continue;
|
|
3241
|
+
}
|
|
3242
|
+
if (token === "--servers") {
|
|
3243
|
+
const value = args[index + 1];
|
|
3244
|
+
if (!value) {
|
|
3245
|
+
throw new Error("Flag '--servers' requires a comma-separated list.");
|
|
3246
|
+
}
|
|
3247
|
+
servers = parseServerList(value);
|
|
3248
|
+
index += 1;
|
|
3249
|
+
continue;
|
|
3250
|
+
}
|
|
3251
|
+
if (token.startsWith("--servers=")) {
|
|
3252
|
+
servers = parseServerList(token.slice("--servers=".length));
|
|
3253
|
+
continue;
|
|
3254
|
+
}
|
|
3255
|
+
if (token === "--host") {
|
|
3256
|
+
const value = args[index + 1];
|
|
3257
|
+
if (!value) {
|
|
3258
|
+
throw new Error("Flag '--host' requires a value.");
|
|
3259
|
+
}
|
|
3260
|
+
host = value;
|
|
3261
|
+
index += 1;
|
|
3262
|
+
continue;
|
|
3263
|
+
}
|
|
3264
|
+
if (token.startsWith("--host=")) {
|
|
3265
|
+
host = token.slice("--host=".length);
|
|
3266
|
+
if (!host) {
|
|
3267
|
+
throw new Error("Flag '--host' requires a value.");
|
|
3268
|
+
}
|
|
3269
|
+
continue;
|
|
3270
|
+
}
|
|
3271
|
+
throw new Error(`Unknown serve flag '${token}'.`);
|
|
3272
|
+
}
|
|
3273
|
+
if (explicitStdio && explicitHttp) {
|
|
3274
|
+
throw new Error("Flags '--stdio' and '--http' cannot be used together.");
|
|
3275
|
+
}
|
|
3276
|
+
if (host && mode !== "http") {
|
|
3277
|
+
throw new Error("Flag '--host' can only be used with '--http'.");
|
|
3278
|
+
}
|
|
3279
|
+
return { mode, port, host, servers };
|
|
3280
|
+
}
|
|
3281
|
+
function parsePort(value) {
|
|
3282
|
+
if (value.trim().length === 0) {
|
|
3283
|
+
throw new Error("Flag '--http' requires a port.");
|
|
3284
|
+
}
|
|
3285
|
+
const port = Number(value);
|
|
3286
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
3287
|
+
throw new Error(`Invalid HTTP port '${value}'.`);
|
|
3288
|
+
}
|
|
3289
|
+
return port;
|
|
3290
|
+
}
|
|
3291
|
+
function parseServerList(value) {
|
|
3292
|
+
const servers = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
3293
|
+
if (servers.length === 0) {
|
|
3294
|
+
throw new Error("Flag '--servers' requires at least one server name.");
|
|
3295
|
+
}
|
|
3296
|
+
return servers;
|
|
3297
|
+
}
|
|
3298
|
+
export {
|
|
3299
|
+
handleServeCli,
|
|
3300
|
+
parseServeArgs,
|
|
3301
|
+
printServeHelp
|
|
3302
|
+
};
|