scream-code 0.3.4 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{dist-CbMTNo2C.mjs → dist-DC_fxyJs.mjs} +1 -1
- package/dist/{from-BNHTbliy.mjs → from-BrTq-Se7.mjs} +1 -1
- package/dist/main.mjs +2745 -11757
- package/dist/mcp-servers/desktop-server.d.mts +2 -0
- package/dist/mcp-servers/desktop-server.mjs +1713 -0
- package/dist/{multipart-parser-D-SpbT4q.mjs → multipart-parser-D-7Huqdj.mjs} +2 -2
- package/dist/{src-BKW7KbPt.mjs → src-BCd6SJpS.mjs} +3 -3
- package/dist/stdio-C2-psya7.mjs +10453 -0
- package/icon.ico +0 -0
- package/package.json +7 -2
- package/scripts/postinstall/shortcut.mjs +74 -0
- package/scripts/postinstall.mjs +7 -0
|
@@ -0,0 +1,1713 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
3
|
+
import { dirname as __cjsShimDirname } from 'node:path';
|
|
4
|
+
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = __cjsShimDirname(__filename);
|
|
6
|
+
import { A as ErrorCode, E as CreateTaskResultSchema, F as InitializedNotificationSchema, G as ListRootsResultSchema, J as LoggingLevelSchema, K as ListToolsRequestSchema, L as LATEST_PROTOCOL_VERSION, N as InitializeRequestSchema, O as ElicitResultSchema, T as CreateMessageResultWithToolsSchema, U as ListResourcesRequestSchema, V as ListResourceTemplatesRequestSchema, Y as McpError, Z as ReadResourceRequestSchema, _ as safeParse, a as AjvJsonSchemaValidator, b as CallToolResultSchema, c as toJsonSchemaCompat, d as getParseErrorMessage, et as SUPPORTED_PROTOCOL_VERSIONS, f as getSchemaDescription, g as objectFromShape, h as normalizeObjectSchema, i as assertToolsCallTaskCapability, it as assertCompleteRequestResourceTemplate, j as GetPromptRequestSchema, k as EmptyResultSchema, l as getLiteralValue, m as isZ4Schema, n as serializeMessage, o as Protocol, p as isSchemaOptional, r as assertClientRequestTaskCapability, rt as assertCompleteRequestPrompt, s as mergeCapabilities, t as ReadBuffer, tt as SetLevelRequestSchema, u as getObjectShape, v as safeParseAsync, w as CreateMessageResultSchema, x as CompleteRequestSchema, y as CallToolRequestSchema, z as ListPromptsRequestSchema } from "../stdio-C2-psya7.mjs";
|
|
7
|
+
import { createRequire } from "node:module";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { ZodOptional, z } from "zod";
|
|
10
|
+
import { execFile } from "node:child_process";
|
|
11
|
+
import process$1 from "node:process";
|
|
12
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
|
|
13
|
+
/**
|
|
14
|
+
* Experimental server task features for MCP SDK.
|
|
15
|
+
* WARNING: These APIs are experimental and may change without notice.
|
|
16
|
+
*
|
|
17
|
+
* @experimental
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Experimental task features for low-level MCP servers.
|
|
21
|
+
*
|
|
22
|
+
* Access via `server.experimental.tasks`:
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const stream = server.experimental.tasks.requestStream(request, schema, options);
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* For high-level server usage with task-based tools, use `McpServer.experimental.tasks` instead.
|
|
28
|
+
*
|
|
29
|
+
* @experimental
|
|
30
|
+
*/
|
|
31
|
+
var ExperimentalServerTasks = class {
|
|
32
|
+
constructor(_server) {
|
|
33
|
+
this._server = _server;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Sends a request and returns an AsyncGenerator that yields response messages.
|
|
37
|
+
* The generator is guaranteed to end with either a 'result' or 'error' message.
|
|
38
|
+
*
|
|
39
|
+
* This method provides streaming access to request processing, allowing you to
|
|
40
|
+
* observe intermediate task status updates for task-augmented requests.
|
|
41
|
+
*
|
|
42
|
+
* @param request - The request to send
|
|
43
|
+
* @param resultSchema - Zod schema for validating the result
|
|
44
|
+
* @param options - Optional request options (timeout, signal, task creation params, etc.)
|
|
45
|
+
* @returns AsyncGenerator that yields ResponseMessage objects
|
|
46
|
+
*
|
|
47
|
+
* @experimental
|
|
48
|
+
*/
|
|
49
|
+
requestStream(request, resultSchema, options) {
|
|
50
|
+
return this._server.requestStream(request, resultSchema, options);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sends a sampling request and returns an AsyncGenerator that yields response messages.
|
|
54
|
+
* The generator is guaranteed to end with either a 'result' or 'error' message.
|
|
55
|
+
*
|
|
56
|
+
* For task-augmented requests, yields 'taskCreated' and 'taskStatus' messages
|
|
57
|
+
* before the final result.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const stream = server.experimental.tasks.createMessageStream({
|
|
62
|
+
* messages: [{ role: 'user', content: { type: 'text', text: 'Hello' } }],
|
|
63
|
+
* maxTokens: 100
|
|
64
|
+
* }, {
|
|
65
|
+
* onprogress: (progress) => {
|
|
66
|
+
* // Handle streaming tokens via progress notifications
|
|
67
|
+
* console.log('Progress:', progress.message);
|
|
68
|
+
* }
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* for await (const message of stream) {
|
|
72
|
+
* switch (message.type) {
|
|
73
|
+
* case 'taskCreated':
|
|
74
|
+
* console.log('Task created:', message.task.taskId);
|
|
75
|
+
* break;
|
|
76
|
+
* case 'taskStatus':
|
|
77
|
+
* console.log('Task status:', message.task.status);
|
|
78
|
+
* break;
|
|
79
|
+
* case 'result':
|
|
80
|
+
* console.log('Final result:', message.result);
|
|
81
|
+
* break;
|
|
82
|
+
* case 'error':
|
|
83
|
+
* console.error('Error:', message.error);
|
|
84
|
+
* break;
|
|
85
|
+
* }
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @param params - The sampling request parameters
|
|
90
|
+
* @param options - Optional request options (timeout, signal, task creation params, onprogress, etc.)
|
|
91
|
+
* @returns AsyncGenerator that yields ResponseMessage objects
|
|
92
|
+
*
|
|
93
|
+
* @experimental
|
|
94
|
+
*/
|
|
95
|
+
createMessageStream(params, options) {
|
|
96
|
+
const clientCapabilities = this._server.getClientCapabilities();
|
|
97
|
+
if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) throw new Error("Client does not support sampling tools capability.");
|
|
98
|
+
if (params.messages.length > 0) {
|
|
99
|
+
const lastMessage = params.messages[params.messages.length - 1];
|
|
100
|
+
const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
|
|
101
|
+
const hasToolResults = lastContent.some((c) => c.type === "tool_result");
|
|
102
|
+
const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : void 0;
|
|
103
|
+
const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
|
|
104
|
+
const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
|
|
105
|
+
if (hasToolResults) {
|
|
106
|
+
if (lastContent.some((c) => c.type !== "tool_result")) throw new Error("The last message must contain only tool_result content if any is present");
|
|
107
|
+
if (!hasPreviousToolUse) throw new Error("tool_result blocks are not matching any tool_use from the previous message");
|
|
108
|
+
}
|
|
109
|
+
if (hasPreviousToolUse) {
|
|
110
|
+
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
111
|
+
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
112
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return this.requestStream({
|
|
116
|
+
method: "sampling/createMessage",
|
|
117
|
+
params
|
|
118
|
+
}, CreateMessageResultSchema, options);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Sends an elicitation request and returns an AsyncGenerator that yields response messages.
|
|
122
|
+
* The generator is guaranteed to end with either a 'result' or 'error' message.
|
|
123
|
+
*
|
|
124
|
+
* For task-augmented requests (especially URL-based elicitation), yields 'taskCreated'
|
|
125
|
+
* and 'taskStatus' messages before the final result.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```typescript
|
|
129
|
+
* const stream = server.experimental.tasks.elicitInputStream({
|
|
130
|
+
* mode: 'url',
|
|
131
|
+
* message: 'Please authenticate',
|
|
132
|
+
* elicitationId: 'auth-123',
|
|
133
|
+
* url: 'https://example.com/auth'
|
|
134
|
+
* }, {
|
|
135
|
+
* task: { ttl: 300000 } // Task-augmented for long-running auth flow
|
|
136
|
+
* });
|
|
137
|
+
*
|
|
138
|
+
* for await (const message of stream) {
|
|
139
|
+
* switch (message.type) {
|
|
140
|
+
* case 'taskCreated':
|
|
141
|
+
* console.log('Task created:', message.task.taskId);
|
|
142
|
+
* break;
|
|
143
|
+
* case 'taskStatus':
|
|
144
|
+
* console.log('Task status:', message.task.status);
|
|
145
|
+
* break;
|
|
146
|
+
* case 'result':
|
|
147
|
+
* console.log('User action:', message.result.action);
|
|
148
|
+
* break;
|
|
149
|
+
* case 'error':
|
|
150
|
+
* console.error('Error:', message.error);
|
|
151
|
+
* break;
|
|
152
|
+
* }
|
|
153
|
+
* }
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @param params - The elicitation request parameters
|
|
157
|
+
* @param options - Optional request options (timeout, signal, task creation params, etc.)
|
|
158
|
+
* @returns AsyncGenerator that yields ResponseMessage objects
|
|
159
|
+
*
|
|
160
|
+
* @experimental
|
|
161
|
+
*/
|
|
162
|
+
elicitInputStream(params, options) {
|
|
163
|
+
const clientCapabilities = this._server.getClientCapabilities();
|
|
164
|
+
const mode = params.mode ?? "form";
|
|
165
|
+
switch (mode) {
|
|
166
|
+
case "url":
|
|
167
|
+
if (!clientCapabilities?.elicitation?.url) throw new Error("Client does not support url elicitation.");
|
|
168
|
+
break;
|
|
169
|
+
case "form":
|
|
170
|
+
if (!clientCapabilities?.elicitation?.form) throw new Error("Client does not support form elicitation.");
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
const normalizedParams = mode === "form" && params.mode === void 0 ? {
|
|
174
|
+
...params,
|
|
175
|
+
mode: "form"
|
|
176
|
+
} : params;
|
|
177
|
+
return this.requestStream({
|
|
178
|
+
method: "elicitation/create",
|
|
179
|
+
params: normalizedParams
|
|
180
|
+
}, ElicitResultSchema, options);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Gets the current status of a task.
|
|
184
|
+
*
|
|
185
|
+
* @param taskId - The task identifier
|
|
186
|
+
* @param options - Optional request options
|
|
187
|
+
* @returns The task status
|
|
188
|
+
*
|
|
189
|
+
* @experimental
|
|
190
|
+
*/
|
|
191
|
+
async getTask(taskId, options) {
|
|
192
|
+
return this._server.getTask({ taskId }, options);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Retrieves the result of a completed task.
|
|
196
|
+
*
|
|
197
|
+
* @param taskId - The task identifier
|
|
198
|
+
* @param resultSchema - Zod schema for validating the result
|
|
199
|
+
* @param options - Optional request options
|
|
200
|
+
* @returns The task result
|
|
201
|
+
*
|
|
202
|
+
* @experimental
|
|
203
|
+
*/
|
|
204
|
+
async getTaskResult(taskId, resultSchema, options) {
|
|
205
|
+
return this._server.getTaskResult({ taskId }, resultSchema, options);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Lists tasks with optional pagination.
|
|
209
|
+
*
|
|
210
|
+
* @param cursor - Optional pagination cursor
|
|
211
|
+
* @param options - Optional request options
|
|
212
|
+
* @returns List of tasks with optional next cursor
|
|
213
|
+
*
|
|
214
|
+
* @experimental
|
|
215
|
+
*/
|
|
216
|
+
async listTasks(cursor, options) {
|
|
217
|
+
return this._server.listTasks(cursor ? { cursor } : void 0, options);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Cancels a running task.
|
|
221
|
+
*
|
|
222
|
+
* @param taskId - The task identifier
|
|
223
|
+
* @param options - Optional request options
|
|
224
|
+
*
|
|
225
|
+
* @experimental
|
|
226
|
+
*/
|
|
227
|
+
async cancelTask(taskId, options) {
|
|
228
|
+
return this._server.cancelTask({ taskId }, options);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
|
|
233
|
+
/**
|
|
234
|
+
* An MCP server on top of a pluggable transport.
|
|
235
|
+
*
|
|
236
|
+
* This server will automatically respond to the initialization flow as initiated from the client.
|
|
237
|
+
*
|
|
238
|
+
* To use with custom types, extend the base Request/Notification/Result types and pass them as type parameters:
|
|
239
|
+
*
|
|
240
|
+
* ```typescript
|
|
241
|
+
* // Custom schemas
|
|
242
|
+
* const CustomRequestSchema = RequestSchema.extend({...})
|
|
243
|
+
* const CustomNotificationSchema = NotificationSchema.extend({...})
|
|
244
|
+
* const CustomResultSchema = ResultSchema.extend({...})
|
|
245
|
+
*
|
|
246
|
+
* // Type aliases
|
|
247
|
+
* type CustomRequest = z.infer<typeof CustomRequestSchema>
|
|
248
|
+
* type CustomNotification = z.infer<typeof CustomNotificationSchema>
|
|
249
|
+
* type CustomResult = z.infer<typeof CustomResultSchema>
|
|
250
|
+
*
|
|
251
|
+
* // Create typed server
|
|
252
|
+
* const server = new Server<CustomRequest, CustomNotification, CustomResult>({
|
|
253
|
+
* name: "CustomServer",
|
|
254
|
+
* version: "1.0.0"
|
|
255
|
+
* })
|
|
256
|
+
* ```
|
|
257
|
+
* @deprecated Use `McpServer` instead for the high-level API. Only use `Server` for advanced use cases.
|
|
258
|
+
*/
|
|
259
|
+
var Server = class extends Protocol {
|
|
260
|
+
/**
|
|
261
|
+
* Initializes this server with the given name and version information.
|
|
262
|
+
*/
|
|
263
|
+
constructor(_serverInfo, options) {
|
|
264
|
+
super(options);
|
|
265
|
+
this._serverInfo = _serverInfo;
|
|
266
|
+
this._loggingLevels = /* @__PURE__ */ new Map();
|
|
267
|
+
this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
|
|
268
|
+
this.isMessageIgnored = (level, sessionId) => {
|
|
269
|
+
const currentLevel = this._loggingLevels.get(sessionId);
|
|
270
|
+
return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
|
|
271
|
+
};
|
|
272
|
+
this._capabilities = options?.capabilities ?? {};
|
|
273
|
+
this._instructions = options?.instructions;
|
|
274
|
+
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator();
|
|
275
|
+
this.setRequestHandler(InitializeRequestSchema, (request) => this._oninitialize(request));
|
|
276
|
+
this.setNotificationHandler(InitializedNotificationSchema, () => this.oninitialized?.());
|
|
277
|
+
if (this._capabilities.logging) this.setRequestHandler(SetLevelRequestSchema, async (request, extra) => {
|
|
278
|
+
const transportSessionId = extra.sessionId || extra.requestInfo?.headers["mcp-session-id"] || void 0;
|
|
279
|
+
const { level } = request.params;
|
|
280
|
+
const parseResult = LoggingLevelSchema.safeParse(level);
|
|
281
|
+
if (parseResult.success) this._loggingLevels.set(transportSessionId, parseResult.data);
|
|
282
|
+
return {};
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Access experimental features.
|
|
287
|
+
*
|
|
288
|
+
* WARNING: These APIs are experimental and may change without notice.
|
|
289
|
+
*
|
|
290
|
+
* @experimental
|
|
291
|
+
*/
|
|
292
|
+
get experimental() {
|
|
293
|
+
if (!this._experimental) this._experimental = { tasks: new ExperimentalServerTasks(this) };
|
|
294
|
+
return this._experimental;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Registers new capabilities. This can only be called before connecting to a transport.
|
|
298
|
+
*
|
|
299
|
+
* The new capabilities will be merged with any existing capabilities previously given (e.g., at initialization).
|
|
300
|
+
*/
|
|
301
|
+
registerCapabilities(capabilities) {
|
|
302
|
+
if (this.transport) throw new Error("Cannot register capabilities after connecting to transport");
|
|
303
|
+
this._capabilities = mergeCapabilities(this._capabilities, capabilities);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Override request handler registration to enforce server-side validation for tools/call.
|
|
307
|
+
*/
|
|
308
|
+
setRequestHandler(requestSchema, handler) {
|
|
309
|
+
const methodSchema = getObjectShape(requestSchema)?.method;
|
|
310
|
+
if (!methodSchema) throw new Error("Schema is missing a method literal");
|
|
311
|
+
let methodValue;
|
|
312
|
+
if (isZ4Schema(methodSchema)) {
|
|
313
|
+
const v4Schema = methodSchema;
|
|
314
|
+
methodValue = (v4Schema._zod?.def)?.value ?? v4Schema.value;
|
|
315
|
+
} else {
|
|
316
|
+
const v3Schema = methodSchema;
|
|
317
|
+
methodValue = v3Schema._def?.value ?? v3Schema.value;
|
|
318
|
+
}
|
|
319
|
+
if (typeof methodValue !== "string") throw new Error("Schema method literal must be a string");
|
|
320
|
+
if (methodValue === "tools/call") {
|
|
321
|
+
const wrappedHandler = async (request, extra) => {
|
|
322
|
+
const validatedRequest = safeParse(CallToolRequestSchema, request);
|
|
323
|
+
if (!validatedRequest.success) {
|
|
324
|
+
const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
|
|
325
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
|
|
326
|
+
}
|
|
327
|
+
const { params } = validatedRequest.data;
|
|
328
|
+
const result = await Promise.resolve(handler(request, extra));
|
|
329
|
+
if (params.task) {
|
|
330
|
+
const taskValidationResult = safeParse(CreateTaskResultSchema, result);
|
|
331
|
+
if (!taskValidationResult.success) {
|
|
332
|
+
const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
|
|
333
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
|
|
334
|
+
}
|
|
335
|
+
return taskValidationResult.data;
|
|
336
|
+
}
|
|
337
|
+
const validationResult = safeParse(CallToolResultSchema, result);
|
|
338
|
+
if (!validationResult.success) {
|
|
339
|
+
const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
|
|
340
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
|
|
341
|
+
}
|
|
342
|
+
return validationResult.data;
|
|
343
|
+
};
|
|
344
|
+
return super.setRequestHandler(requestSchema, wrappedHandler);
|
|
345
|
+
}
|
|
346
|
+
return super.setRequestHandler(requestSchema, handler);
|
|
347
|
+
}
|
|
348
|
+
assertCapabilityForMethod(method) {
|
|
349
|
+
switch (method) {
|
|
350
|
+
case "sampling/createMessage":
|
|
351
|
+
if (!this._clientCapabilities?.sampling) throw new Error(`Client does not support sampling (required for ${method})`);
|
|
352
|
+
break;
|
|
353
|
+
case "elicitation/create":
|
|
354
|
+
if (!this._clientCapabilities?.elicitation) throw new Error(`Client does not support elicitation (required for ${method})`);
|
|
355
|
+
break;
|
|
356
|
+
case "roots/list":
|
|
357
|
+
if (!this._clientCapabilities?.roots) throw new Error(`Client does not support listing roots (required for ${method})`);
|
|
358
|
+
break;
|
|
359
|
+
case "ping": break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
assertNotificationCapability(method) {
|
|
363
|
+
switch (method) {
|
|
364
|
+
case "notifications/message":
|
|
365
|
+
if (!this._capabilities.logging) throw new Error(`Server does not support logging (required for ${method})`);
|
|
366
|
+
break;
|
|
367
|
+
case "notifications/resources/updated":
|
|
368
|
+
case "notifications/resources/list_changed":
|
|
369
|
+
if (!this._capabilities.resources) throw new Error(`Server does not support notifying about resources (required for ${method})`);
|
|
370
|
+
break;
|
|
371
|
+
case "notifications/tools/list_changed":
|
|
372
|
+
if (!this._capabilities.tools) throw new Error(`Server does not support notifying of tool list changes (required for ${method})`);
|
|
373
|
+
break;
|
|
374
|
+
case "notifications/prompts/list_changed":
|
|
375
|
+
if (!this._capabilities.prompts) throw new Error(`Server does not support notifying of prompt list changes (required for ${method})`);
|
|
376
|
+
break;
|
|
377
|
+
case "notifications/elicitation/complete":
|
|
378
|
+
if (!this._clientCapabilities?.elicitation?.url) throw new Error(`Client does not support URL elicitation (required for ${method})`);
|
|
379
|
+
break;
|
|
380
|
+
case "notifications/cancelled": break;
|
|
381
|
+
case "notifications/progress": break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
assertRequestHandlerCapability(method) {
|
|
385
|
+
if (!this._capabilities) return;
|
|
386
|
+
switch (method) {
|
|
387
|
+
case "completion/complete":
|
|
388
|
+
if (!this._capabilities.completions) throw new Error(`Server does not support completions (required for ${method})`);
|
|
389
|
+
break;
|
|
390
|
+
case "logging/setLevel":
|
|
391
|
+
if (!this._capabilities.logging) throw new Error(`Server does not support logging (required for ${method})`);
|
|
392
|
+
break;
|
|
393
|
+
case "prompts/get":
|
|
394
|
+
case "prompts/list":
|
|
395
|
+
if (!this._capabilities.prompts) throw new Error(`Server does not support prompts (required for ${method})`);
|
|
396
|
+
break;
|
|
397
|
+
case "resources/list":
|
|
398
|
+
case "resources/templates/list":
|
|
399
|
+
case "resources/read":
|
|
400
|
+
if (!this._capabilities.resources) throw new Error(`Server does not support resources (required for ${method})`);
|
|
401
|
+
break;
|
|
402
|
+
case "tools/call":
|
|
403
|
+
case "tools/list":
|
|
404
|
+
if (!this._capabilities.tools) throw new Error(`Server does not support tools (required for ${method})`);
|
|
405
|
+
break;
|
|
406
|
+
case "tasks/get":
|
|
407
|
+
case "tasks/list":
|
|
408
|
+
case "tasks/result":
|
|
409
|
+
case "tasks/cancel":
|
|
410
|
+
if (!this._capabilities.tasks) throw new Error(`Server does not support tasks capability (required for ${method})`);
|
|
411
|
+
break;
|
|
412
|
+
case "ping":
|
|
413
|
+
case "initialize": break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
assertTaskCapability(method) {
|
|
417
|
+
assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client");
|
|
418
|
+
}
|
|
419
|
+
assertTaskHandlerCapability(method) {
|
|
420
|
+
if (!this._capabilities) return;
|
|
421
|
+
assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server");
|
|
422
|
+
}
|
|
423
|
+
async _oninitialize(request) {
|
|
424
|
+
const requestedVersion = request.params.protocolVersion;
|
|
425
|
+
this._clientCapabilities = request.params.capabilities;
|
|
426
|
+
this._clientVersion = request.params.clientInfo;
|
|
427
|
+
return {
|
|
428
|
+
protocolVersion: SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION,
|
|
429
|
+
capabilities: this.getCapabilities(),
|
|
430
|
+
serverInfo: this._serverInfo,
|
|
431
|
+
...this._instructions && { instructions: this._instructions }
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* After initialization has completed, this will be populated with the client's reported capabilities.
|
|
436
|
+
*/
|
|
437
|
+
getClientCapabilities() {
|
|
438
|
+
return this._clientCapabilities;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* After initialization has completed, this will be populated with information about the client's name and version.
|
|
442
|
+
*/
|
|
443
|
+
getClientVersion() {
|
|
444
|
+
return this._clientVersion;
|
|
445
|
+
}
|
|
446
|
+
getCapabilities() {
|
|
447
|
+
return this._capabilities;
|
|
448
|
+
}
|
|
449
|
+
async ping() {
|
|
450
|
+
return this.request({ method: "ping" }, EmptyResultSchema);
|
|
451
|
+
}
|
|
452
|
+
async createMessage(params, options) {
|
|
453
|
+
if (params.tools || params.toolChoice) {
|
|
454
|
+
if (!this._clientCapabilities?.sampling?.tools) throw new Error("Client does not support sampling tools capability.");
|
|
455
|
+
}
|
|
456
|
+
if (params.messages.length > 0) {
|
|
457
|
+
const lastMessage = params.messages[params.messages.length - 1];
|
|
458
|
+
const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
|
|
459
|
+
const hasToolResults = lastContent.some((c) => c.type === "tool_result");
|
|
460
|
+
const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : void 0;
|
|
461
|
+
const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
|
|
462
|
+
const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
|
|
463
|
+
if (hasToolResults) {
|
|
464
|
+
if (lastContent.some((c) => c.type !== "tool_result")) throw new Error("The last message must contain only tool_result content if any is present");
|
|
465
|
+
if (!hasPreviousToolUse) throw new Error("tool_result blocks are not matching any tool_use from the previous message");
|
|
466
|
+
}
|
|
467
|
+
if (hasPreviousToolUse) {
|
|
468
|
+
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
469
|
+
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
470
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (params.tools) return this.request({
|
|
474
|
+
method: "sampling/createMessage",
|
|
475
|
+
params
|
|
476
|
+
}, CreateMessageResultWithToolsSchema, options);
|
|
477
|
+
return this.request({
|
|
478
|
+
method: "sampling/createMessage",
|
|
479
|
+
params
|
|
480
|
+
}, CreateMessageResultSchema, options);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Creates an elicitation request for the given parameters.
|
|
484
|
+
* For backwards compatibility, `mode` may be omitted for form requests and will default to `'form'`.
|
|
485
|
+
* @param params The parameters for the elicitation request.
|
|
486
|
+
* @param options Optional request options.
|
|
487
|
+
* @returns The result of the elicitation request.
|
|
488
|
+
*/
|
|
489
|
+
async elicitInput(params, options) {
|
|
490
|
+
switch (params.mode ?? "form") {
|
|
491
|
+
case "url": {
|
|
492
|
+
if (!this._clientCapabilities?.elicitation?.url) throw new Error("Client does not support url elicitation.");
|
|
493
|
+
const urlParams = params;
|
|
494
|
+
return this.request({
|
|
495
|
+
method: "elicitation/create",
|
|
496
|
+
params: urlParams
|
|
497
|
+
}, ElicitResultSchema, options);
|
|
498
|
+
}
|
|
499
|
+
case "form": {
|
|
500
|
+
if (!this._clientCapabilities?.elicitation?.form) throw new Error("Client does not support form elicitation.");
|
|
501
|
+
const formParams = params.mode === "form" ? params : {
|
|
502
|
+
...params,
|
|
503
|
+
mode: "form"
|
|
504
|
+
};
|
|
505
|
+
const result = await this.request({
|
|
506
|
+
method: "elicitation/create",
|
|
507
|
+
params: formParams
|
|
508
|
+
}, ElicitResultSchema, options);
|
|
509
|
+
if (result.action === "accept" && result.content && formParams.requestedSchema) try {
|
|
510
|
+
const validationResult = this._jsonSchemaValidator.getValidator(formParams.requestedSchema)(result.content);
|
|
511
|
+
if (!validationResult.valid) throw new McpError(ErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${validationResult.errorMessage}`);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
if (error instanceof McpError) throw error;
|
|
514
|
+
throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error instanceof Error ? error.message : String(error)}`);
|
|
515
|
+
}
|
|
516
|
+
return result;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Creates a reusable callback that, when invoked, will send a `notifications/elicitation/complete`
|
|
522
|
+
* notification for the specified elicitation ID.
|
|
523
|
+
*
|
|
524
|
+
* @param elicitationId The ID of the elicitation to mark as complete.
|
|
525
|
+
* @param options Optional notification options. Useful when the completion notification should be related to a prior request.
|
|
526
|
+
* @returns A function that emits the completion notification when awaited.
|
|
527
|
+
*/
|
|
528
|
+
createElicitationCompletionNotifier(elicitationId, options) {
|
|
529
|
+
if (!this._clientCapabilities?.elicitation?.url) throw new Error("Client does not support URL elicitation (required for notifications/elicitation/complete)");
|
|
530
|
+
return () => this.notification({
|
|
531
|
+
method: "notifications/elicitation/complete",
|
|
532
|
+
params: { elicitationId }
|
|
533
|
+
}, options);
|
|
534
|
+
}
|
|
535
|
+
async listRoots(params, options) {
|
|
536
|
+
return this.request({
|
|
537
|
+
method: "roots/list",
|
|
538
|
+
params
|
|
539
|
+
}, ListRootsResultSchema, options);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Sends a logging message to the client, if connected.
|
|
543
|
+
* Note: You only need to send the parameters object, not the entire JSON RPC message
|
|
544
|
+
* @see LoggingMessageNotification
|
|
545
|
+
* @param params
|
|
546
|
+
* @param sessionId optional for stateless and backward compatibility
|
|
547
|
+
*/
|
|
548
|
+
async sendLoggingMessage(params, sessionId) {
|
|
549
|
+
if (this._capabilities.logging) {
|
|
550
|
+
if (!this.isMessageIgnored(params.level, sessionId)) return this.notification({
|
|
551
|
+
method: "notifications/message",
|
|
552
|
+
params
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async sendResourceUpdated(params) {
|
|
557
|
+
return this.notification({
|
|
558
|
+
method: "notifications/resources/updated",
|
|
559
|
+
params
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
async sendResourceListChanged() {
|
|
563
|
+
return this.notification({ method: "notifications/resources/list_changed" });
|
|
564
|
+
}
|
|
565
|
+
async sendToolListChanged() {
|
|
566
|
+
return this.notification({ method: "notifications/tools/list_changed" });
|
|
567
|
+
}
|
|
568
|
+
async sendPromptListChanged() {
|
|
569
|
+
return this.notification({ method: "notifications/prompts/list_changed" });
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
//#endregion
|
|
573
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
|
|
574
|
+
const COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
|
|
575
|
+
/**
|
|
576
|
+
* Checks if a schema is completable (has completion metadata).
|
|
577
|
+
*/
|
|
578
|
+
function isCompletable(schema) {
|
|
579
|
+
return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Gets the completer callback from a completable schema, if it exists.
|
|
583
|
+
*/
|
|
584
|
+
function getCompleter(schema) {
|
|
585
|
+
return schema[COMPLETABLE_SYMBOL]?.complete;
|
|
586
|
+
}
|
|
587
|
+
var McpZodTypeKind;
|
|
588
|
+
(function(McpZodTypeKind) {
|
|
589
|
+
McpZodTypeKind["Completable"] = "McpCompletable";
|
|
590
|
+
})(McpZodTypeKind || (McpZodTypeKind = {}));
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
|
|
593
|
+
/**
|
|
594
|
+
* Tool name validation utilities according to SEP: Specify Format for Tool Names
|
|
595
|
+
*
|
|
596
|
+
* Tool names SHOULD be between 1 and 128 characters in length (inclusive).
|
|
597
|
+
* Tool names are case-sensitive.
|
|
598
|
+
* Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), digits
|
|
599
|
+
* (0-9), underscore (_), dash (-), and dot (.).
|
|
600
|
+
* Tool names SHOULD NOT contain spaces, commas, or other special characters.
|
|
601
|
+
*/
|
|
602
|
+
/**
|
|
603
|
+
* Regular expression for valid tool names according to SEP-986 specification
|
|
604
|
+
*/
|
|
605
|
+
const TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
|
|
606
|
+
/**
|
|
607
|
+
* Validates a tool name according to the SEP specification
|
|
608
|
+
* @param name - The tool name to validate
|
|
609
|
+
* @returns An object containing validation result and any warnings
|
|
610
|
+
*/
|
|
611
|
+
function validateToolName(name) {
|
|
612
|
+
const warnings = [];
|
|
613
|
+
if (name.length === 0) return {
|
|
614
|
+
isValid: false,
|
|
615
|
+
warnings: ["Tool name cannot be empty"]
|
|
616
|
+
};
|
|
617
|
+
if (name.length > 128) return {
|
|
618
|
+
isValid: false,
|
|
619
|
+
warnings: [`Tool name exceeds maximum length of 128 characters (current: ${name.length})`]
|
|
620
|
+
};
|
|
621
|
+
if (name.includes(" ")) warnings.push("Tool name contains spaces, which may cause parsing issues");
|
|
622
|
+
if (name.includes(",")) warnings.push("Tool name contains commas, which may cause parsing issues");
|
|
623
|
+
if (name.startsWith("-") || name.endsWith("-")) warnings.push("Tool name starts or ends with a dash, which may cause parsing issues in some contexts");
|
|
624
|
+
if (name.startsWith(".") || name.endsWith(".")) warnings.push("Tool name starts or ends with a dot, which may cause parsing issues in some contexts");
|
|
625
|
+
if (!TOOL_NAME_REGEX.test(name)) {
|
|
626
|
+
const invalidChars = name.split("").filter((char) => !/[A-Za-z0-9._-]/.test(char)).filter((char, index, arr) => arr.indexOf(char) === index);
|
|
627
|
+
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 (.)");
|
|
628
|
+
return {
|
|
629
|
+
isValid: false,
|
|
630
|
+
warnings
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
isValid: true,
|
|
635
|
+
warnings
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Issues warnings for non-conforming tool names
|
|
640
|
+
* @param name - The tool name that triggered the warnings
|
|
641
|
+
* @param warnings - Array of warning messages
|
|
642
|
+
*/
|
|
643
|
+
function issueToolNameWarning(name, warnings) {
|
|
644
|
+
if (warnings.length > 0) {
|
|
645
|
+
console.warn(`Tool name validation warning for "${name}":`);
|
|
646
|
+
for (const warning of warnings) console.warn(` - ${warning}`);
|
|
647
|
+
console.warn("Tool registration will proceed, but this may cause compatibility issues.");
|
|
648
|
+
console.warn("Consider updating the tool name to conform to the MCP tool naming standard.");
|
|
649
|
+
console.warn("See SEP: Specify Format for Tool Names (https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986) for more details.");
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Validates a tool name and issues warnings for non-conforming names
|
|
654
|
+
* @param name - The tool name to validate
|
|
655
|
+
* @returns true if the name is valid, false otherwise
|
|
656
|
+
*/
|
|
657
|
+
function validateAndWarnToolName(name) {
|
|
658
|
+
const result = validateToolName(name);
|
|
659
|
+
issueToolNameWarning(name, result.warnings);
|
|
660
|
+
return result.isValid;
|
|
661
|
+
}
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
|
|
664
|
+
/**
|
|
665
|
+
* Experimental McpServer task features for MCP SDK.
|
|
666
|
+
* WARNING: These APIs are experimental and may change without notice.
|
|
667
|
+
*
|
|
668
|
+
* @experimental
|
|
669
|
+
*/
|
|
670
|
+
/**
|
|
671
|
+
* Experimental task features for McpServer.
|
|
672
|
+
*
|
|
673
|
+
* Access via `server.experimental.tasks`:
|
|
674
|
+
* ```typescript
|
|
675
|
+
* server.experimental.tasks.registerToolTask('long-running', config, handler);
|
|
676
|
+
* ```
|
|
677
|
+
*
|
|
678
|
+
* @experimental
|
|
679
|
+
*/
|
|
680
|
+
var ExperimentalMcpServerTasks = class {
|
|
681
|
+
constructor(_mcpServer) {
|
|
682
|
+
this._mcpServer = _mcpServer;
|
|
683
|
+
}
|
|
684
|
+
registerToolTask(name, config, handler) {
|
|
685
|
+
const execution = {
|
|
686
|
+
taskSupport: "required",
|
|
687
|
+
...config.execution
|
|
688
|
+
};
|
|
689
|
+
if (execution.taskSupport === "forbidden") throw new Error(`Cannot register task-based tool '${name}' with taskSupport 'forbidden'. Use registerTool() instead.`);
|
|
690
|
+
return this._mcpServer._createRegisteredTool(name, config.title, config.description, config.inputSchema, config.outputSchema, config.annotations, execution, config._meta, handler);
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
//#endregion
|
|
694
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
|
|
695
|
+
/**
|
|
696
|
+
* High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
|
|
697
|
+
* For advanced usage (like sending notifications or setting custom request handlers), use the underlying
|
|
698
|
+
* Server instance available via the `server` property.
|
|
699
|
+
*/
|
|
700
|
+
var McpServer = class {
|
|
701
|
+
constructor(serverInfo, options) {
|
|
702
|
+
this._registeredResources = {};
|
|
703
|
+
this._registeredResourceTemplates = {};
|
|
704
|
+
this._registeredTools = {};
|
|
705
|
+
this._registeredPrompts = {};
|
|
706
|
+
this._toolHandlersInitialized = false;
|
|
707
|
+
this._completionHandlerInitialized = false;
|
|
708
|
+
this._resourceHandlersInitialized = false;
|
|
709
|
+
this._promptHandlersInitialized = false;
|
|
710
|
+
this.server = new Server(serverInfo, options);
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* Access experimental features.
|
|
714
|
+
*
|
|
715
|
+
* WARNING: These APIs are experimental and may change without notice.
|
|
716
|
+
*
|
|
717
|
+
* @experimental
|
|
718
|
+
*/
|
|
719
|
+
get experimental() {
|
|
720
|
+
if (!this._experimental) this._experimental = { tasks: new ExperimentalMcpServerTasks(this) };
|
|
721
|
+
return this._experimental;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Attaches to the given transport, starts it, and starts listening for messages.
|
|
725
|
+
*
|
|
726
|
+
* 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.
|
|
727
|
+
*/
|
|
728
|
+
async connect(transport) {
|
|
729
|
+
return await this.server.connect(transport);
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Closes the connection.
|
|
733
|
+
*/
|
|
734
|
+
async close() {
|
|
735
|
+
await this.server.close();
|
|
736
|
+
}
|
|
737
|
+
setToolRequestHandlers() {
|
|
738
|
+
if (this._toolHandlersInitialized) return;
|
|
739
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListToolsRequestSchema));
|
|
740
|
+
this.server.assertCanSetRequestHandler(getMethodValue(CallToolRequestSchema));
|
|
741
|
+
this.server.registerCapabilities({ tools: { listChanged: true } });
|
|
742
|
+
this.server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: Object.entries(this._registeredTools).filter(([, tool]) => tool.enabled).map(([name, tool]) => {
|
|
743
|
+
const toolDefinition = {
|
|
744
|
+
name,
|
|
745
|
+
title: tool.title,
|
|
746
|
+
description: tool.description,
|
|
747
|
+
inputSchema: (() => {
|
|
748
|
+
const obj = normalizeObjectSchema(tool.inputSchema);
|
|
749
|
+
return obj ? toJsonSchemaCompat(obj, {
|
|
750
|
+
strictUnions: true,
|
|
751
|
+
pipeStrategy: "input"
|
|
752
|
+
}) : EMPTY_OBJECT_JSON_SCHEMA;
|
|
753
|
+
})(),
|
|
754
|
+
annotations: tool.annotations,
|
|
755
|
+
execution: tool.execution,
|
|
756
|
+
_meta: tool._meta
|
|
757
|
+
};
|
|
758
|
+
if (tool.outputSchema) {
|
|
759
|
+
const obj = normalizeObjectSchema(tool.outputSchema);
|
|
760
|
+
if (obj) toolDefinition.outputSchema = toJsonSchemaCompat(obj, {
|
|
761
|
+
strictUnions: true,
|
|
762
|
+
pipeStrategy: "output"
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
return toolDefinition;
|
|
766
|
+
}) }));
|
|
767
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
768
|
+
try {
|
|
769
|
+
const tool = this._registeredTools[request.params.name];
|
|
770
|
+
if (!tool) throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} not found`);
|
|
771
|
+
if (!tool.enabled) throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} disabled`);
|
|
772
|
+
const isTaskRequest = !!request.params.task;
|
|
773
|
+
const taskSupport = tool.execution?.taskSupport;
|
|
774
|
+
const isTaskHandler = "createTask" in tool.handler;
|
|
775
|
+
if ((taskSupport === "required" || taskSupport === "optional") && !isTaskHandler) throw new McpError(ErrorCode.InternalError, `Tool ${request.params.name} has taskSupport '${taskSupport}' but was not registered with registerToolTask`);
|
|
776
|
+
if (taskSupport === "required" && !isTaskRequest) throw new McpError(ErrorCode.MethodNotFound, `Tool ${request.params.name} requires task augmentation (taskSupport: 'required')`);
|
|
777
|
+
if (taskSupport === "optional" && !isTaskRequest && isTaskHandler) return await this.handleAutomaticTaskPolling(tool, request, extra);
|
|
778
|
+
const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
|
|
779
|
+
const result = await this.executeToolHandler(tool, args, extra);
|
|
780
|
+
if (isTaskRequest) return result;
|
|
781
|
+
await this.validateToolOutput(tool, result, request.params.name);
|
|
782
|
+
return result;
|
|
783
|
+
} catch (error) {
|
|
784
|
+
if (error instanceof McpError) {
|
|
785
|
+
if (error.code === ErrorCode.UrlElicitationRequired) throw error;
|
|
786
|
+
}
|
|
787
|
+
return this.createToolError(error instanceof Error ? error.message : String(error));
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
this._toolHandlersInitialized = true;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Creates a tool error result.
|
|
794
|
+
*
|
|
795
|
+
* @param errorMessage - The error message.
|
|
796
|
+
* @returns The tool error result.
|
|
797
|
+
*/
|
|
798
|
+
createToolError(errorMessage) {
|
|
799
|
+
return {
|
|
800
|
+
content: [{
|
|
801
|
+
type: "text",
|
|
802
|
+
text: errorMessage
|
|
803
|
+
}],
|
|
804
|
+
isError: true
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Validates tool input arguments against the tool's input schema.
|
|
809
|
+
*/
|
|
810
|
+
async validateToolInput(tool, args, toolName) {
|
|
811
|
+
if (!tool.inputSchema) return;
|
|
812
|
+
const parseResult = await safeParseAsync(normalizeObjectSchema(tool.inputSchema) ?? tool.inputSchema, args);
|
|
813
|
+
if (!parseResult.success) {
|
|
814
|
+
const errorMessage = getParseErrorMessage("error" in parseResult ? parseResult.error : "Unknown error");
|
|
815
|
+
throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`);
|
|
816
|
+
}
|
|
817
|
+
return parseResult.data;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Validates tool output against the tool's output schema.
|
|
821
|
+
*/
|
|
822
|
+
async validateToolOutput(tool, result, toolName) {
|
|
823
|
+
if (!tool.outputSchema) return;
|
|
824
|
+
if (!("content" in result)) return;
|
|
825
|
+
if (result.isError) return;
|
|
826
|
+
if (!result.structuredContent) throw new McpError(ErrorCode.InvalidParams, `Output validation error: Tool ${toolName} has an output schema but no structured content was provided`);
|
|
827
|
+
const parseResult = await safeParseAsync(normalizeObjectSchema(tool.outputSchema), result.structuredContent);
|
|
828
|
+
if (!parseResult.success) {
|
|
829
|
+
const errorMessage = getParseErrorMessage("error" in parseResult ? parseResult.error : "Unknown error");
|
|
830
|
+
throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Executes a tool handler (either regular or task-based).
|
|
835
|
+
*/
|
|
836
|
+
async executeToolHandler(tool, args, extra) {
|
|
837
|
+
const handler = tool.handler;
|
|
838
|
+
if ("createTask" in handler) {
|
|
839
|
+
if (!extra.taskStore) throw new Error("No task store provided.");
|
|
840
|
+
const taskExtra = {
|
|
841
|
+
...extra,
|
|
842
|
+
taskStore: extra.taskStore
|
|
843
|
+
};
|
|
844
|
+
if (tool.inputSchema) {
|
|
845
|
+
const typedHandler = handler;
|
|
846
|
+
return await Promise.resolve(typedHandler.createTask(args, taskExtra));
|
|
847
|
+
} else {
|
|
848
|
+
const typedHandler = handler;
|
|
849
|
+
return await Promise.resolve(typedHandler.createTask(taskExtra));
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (tool.inputSchema) {
|
|
853
|
+
const typedHandler = handler;
|
|
854
|
+
return await Promise.resolve(typedHandler(args, extra));
|
|
855
|
+
} else {
|
|
856
|
+
const typedHandler = handler;
|
|
857
|
+
return await Promise.resolve(typedHandler(extra));
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Handles automatic task polling for tools with taskSupport 'optional'.
|
|
862
|
+
*/
|
|
863
|
+
async handleAutomaticTaskPolling(tool, request, extra) {
|
|
864
|
+
if (!extra.taskStore) throw new Error("No task store provided for task-capable tool.");
|
|
865
|
+
const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
|
|
866
|
+
const handler = tool.handler;
|
|
867
|
+
const taskExtra = {
|
|
868
|
+
...extra,
|
|
869
|
+
taskStore: extra.taskStore
|
|
870
|
+
};
|
|
871
|
+
const createTaskResult = args ? await Promise.resolve(handler.createTask(args, taskExtra)) : await Promise.resolve(handler.createTask(taskExtra));
|
|
872
|
+
const taskId = createTaskResult.task.taskId;
|
|
873
|
+
let task = createTaskResult.task;
|
|
874
|
+
const pollInterval = task.pollInterval ?? 5e3;
|
|
875
|
+
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
876
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
877
|
+
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
878
|
+
if (!updatedTask) throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
879
|
+
task = updatedTask;
|
|
880
|
+
}
|
|
881
|
+
return await extra.taskStore.getTaskResult(taskId);
|
|
882
|
+
}
|
|
883
|
+
setCompletionRequestHandler() {
|
|
884
|
+
if (this._completionHandlerInitialized) return;
|
|
885
|
+
this.server.assertCanSetRequestHandler(getMethodValue(CompleteRequestSchema));
|
|
886
|
+
this.server.registerCapabilities({ completions: {} });
|
|
887
|
+
this.server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
|
888
|
+
switch (request.params.ref.type) {
|
|
889
|
+
case "ref/prompt":
|
|
890
|
+
assertCompleteRequestPrompt(request);
|
|
891
|
+
return this.handlePromptCompletion(request, request.params.ref);
|
|
892
|
+
case "ref/resource":
|
|
893
|
+
assertCompleteRequestResourceTemplate(request);
|
|
894
|
+
return this.handleResourceCompletion(request, request.params.ref);
|
|
895
|
+
default: throw new McpError(ErrorCode.InvalidParams, `Invalid completion reference: ${request.params.ref}`);
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
this._completionHandlerInitialized = true;
|
|
899
|
+
}
|
|
900
|
+
async handlePromptCompletion(request, ref) {
|
|
901
|
+
const prompt = this._registeredPrompts[ref.name];
|
|
902
|
+
if (!prompt) throw new McpError(ErrorCode.InvalidParams, `Prompt ${ref.name} not found`);
|
|
903
|
+
if (!prompt.enabled) throw new McpError(ErrorCode.InvalidParams, `Prompt ${ref.name} disabled`);
|
|
904
|
+
if (!prompt.argsSchema) return EMPTY_COMPLETION_RESULT;
|
|
905
|
+
const field = getObjectShape(prompt.argsSchema)?.[request.params.argument.name];
|
|
906
|
+
if (!isCompletable(field)) return EMPTY_COMPLETION_RESULT;
|
|
907
|
+
const completer = getCompleter(field);
|
|
908
|
+
if (!completer) return EMPTY_COMPLETION_RESULT;
|
|
909
|
+
return createCompletionResult(await completer(request.params.argument.value, request.params.context));
|
|
910
|
+
}
|
|
911
|
+
async handleResourceCompletion(request, ref) {
|
|
912
|
+
const template = Object.values(this._registeredResourceTemplates).find((t) => t.resourceTemplate.uriTemplate.toString() === ref.uri);
|
|
913
|
+
if (!template) {
|
|
914
|
+
if (this._registeredResources[ref.uri]) return EMPTY_COMPLETION_RESULT;
|
|
915
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource template ${request.params.ref.uri} not found`);
|
|
916
|
+
}
|
|
917
|
+
const completer = template.resourceTemplate.completeCallback(request.params.argument.name);
|
|
918
|
+
if (!completer) return EMPTY_COMPLETION_RESULT;
|
|
919
|
+
return createCompletionResult(await completer(request.params.argument.value, request.params.context));
|
|
920
|
+
}
|
|
921
|
+
setResourceRequestHandlers() {
|
|
922
|
+
if (this._resourceHandlersInitialized) return;
|
|
923
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListResourcesRequestSchema));
|
|
924
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListResourceTemplatesRequestSchema));
|
|
925
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ReadResourceRequestSchema));
|
|
926
|
+
this.server.registerCapabilities({ resources: { listChanged: true } });
|
|
927
|
+
this.server.setRequestHandler(ListResourcesRequestSchema, async (request, extra) => {
|
|
928
|
+
const resources = Object.entries(this._registeredResources).filter(([_, resource]) => resource.enabled).map(([uri, resource]) => ({
|
|
929
|
+
uri,
|
|
930
|
+
name: resource.name,
|
|
931
|
+
...resource.metadata
|
|
932
|
+
}));
|
|
933
|
+
const templateResources = [];
|
|
934
|
+
for (const template of Object.values(this._registeredResourceTemplates)) {
|
|
935
|
+
if (!template.resourceTemplate.listCallback) continue;
|
|
936
|
+
const result = await template.resourceTemplate.listCallback(extra);
|
|
937
|
+
for (const resource of result.resources) templateResources.push({
|
|
938
|
+
...template.metadata,
|
|
939
|
+
...resource
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
return { resources: [...resources, ...templateResources] };
|
|
943
|
+
});
|
|
944
|
+
this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
|
|
945
|
+
return { resourceTemplates: Object.entries(this._registeredResourceTemplates).map(([name, template]) => ({
|
|
946
|
+
name,
|
|
947
|
+
uriTemplate: template.resourceTemplate.uriTemplate.toString(),
|
|
948
|
+
...template.metadata
|
|
949
|
+
})) };
|
|
950
|
+
});
|
|
951
|
+
this.server.setRequestHandler(ReadResourceRequestSchema, async (request, extra) => {
|
|
952
|
+
const uri = new URL(request.params.uri);
|
|
953
|
+
const resource = this._registeredResources[uri.toString()];
|
|
954
|
+
if (resource) {
|
|
955
|
+
if (!resource.enabled) throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} disabled`);
|
|
956
|
+
return resource.readCallback(uri, extra);
|
|
957
|
+
}
|
|
958
|
+
for (const template of Object.values(this._registeredResourceTemplates)) {
|
|
959
|
+
const variables = template.resourceTemplate.uriTemplate.match(uri.toString());
|
|
960
|
+
if (variables) return template.readCallback(uri, variables, extra);
|
|
961
|
+
}
|
|
962
|
+
throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found`);
|
|
963
|
+
});
|
|
964
|
+
this._resourceHandlersInitialized = true;
|
|
965
|
+
}
|
|
966
|
+
setPromptRequestHandlers() {
|
|
967
|
+
if (this._promptHandlersInitialized) return;
|
|
968
|
+
this.server.assertCanSetRequestHandler(getMethodValue(ListPromptsRequestSchema));
|
|
969
|
+
this.server.assertCanSetRequestHandler(getMethodValue(GetPromptRequestSchema));
|
|
970
|
+
this.server.registerCapabilities({ prompts: { listChanged: true } });
|
|
971
|
+
this.server.setRequestHandler(ListPromptsRequestSchema, () => ({ prompts: Object.entries(this._registeredPrompts).filter(([, prompt]) => prompt.enabled).map(([name, prompt]) => {
|
|
972
|
+
return {
|
|
973
|
+
name,
|
|
974
|
+
title: prompt.title,
|
|
975
|
+
description: prompt.description,
|
|
976
|
+
arguments: prompt.argsSchema ? promptArgumentsFromSchema(prompt.argsSchema) : void 0
|
|
977
|
+
};
|
|
978
|
+
}) }));
|
|
979
|
+
this.server.setRequestHandler(GetPromptRequestSchema, async (request, extra) => {
|
|
980
|
+
const prompt = this._registeredPrompts[request.params.name];
|
|
981
|
+
if (!prompt) throw new McpError(ErrorCode.InvalidParams, `Prompt ${request.params.name} not found`);
|
|
982
|
+
if (!prompt.enabled) throw new McpError(ErrorCode.InvalidParams, `Prompt ${request.params.name} disabled`);
|
|
983
|
+
if (prompt.argsSchema) {
|
|
984
|
+
const parseResult = await safeParseAsync(normalizeObjectSchema(prompt.argsSchema), request.params.arguments);
|
|
985
|
+
if (!parseResult.success) {
|
|
986
|
+
const errorMessage = getParseErrorMessage("error" in parseResult ? parseResult.error : "Unknown error");
|
|
987
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage}`);
|
|
988
|
+
}
|
|
989
|
+
const args = parseResult.data;
|
|
990
|
+
const cb = prompt.callback;
|
|
991
|
+
return await Promise.resolve(cb(args, extra));
|
|
992
|
+
} else {
|
|
993
|
+
const cb = prompt.callback;
|
|
994
|
+
return await Promise.resolve(cb(extra));
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
this._promptHandlersInitialized = true;
|
|
998
|
+
}
|
|
999
|
+
resource(name, uriOrTemplate, ...rest) {
|
|
1000
|
+
let metadata;
|
|
1001
|
+
if (typeof rest[0] === "object") metadata = rest.shift();
|
|
1002
|
+
const readCallback = rest[0];
|
|
1003
|
+
if (typeof uriOrTemplate === "string") {
|
|
1004
|
+
if (this._registeredResources[uriOrTemplate]) throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
1005
|
+
const registeredResource = this._createRegisteredResource(name, void 0, uriOrTemplate, metadata, readCallback);
|
|
1006
|
+
this.setResourceRequestHandlers();
|
|
1007
|
+
this.sendResourceListChanged();
|
|
1008
|
+
return registeredResource;
|
|
1009
|
+
} else {
|
|
1010
|
+
if (this._registeredResourceTemplates[name]) throw new Error(`Resource template ${name} is already registered`);
|
|
1011
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, void 0, uriOrTemplate, metadata, readCallback);
|
|
1012
|
+
this.setResourceRequestHandlers();
|
|
1013
|
+
this.sendResourceListChanged();
|
|
1014
|
+
return registeredResourceTemplate;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
registerResource(name, uriOrTemplate, config, readCallback) {
|
|
1018
|
+
if (typeof uriOrTemplate === "string") {
|
|
1019
|
+
if (this._registeredResources[uriOrTemplate]) throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
1020
|
+
const registeredResource = this._createRegisteredResource(name, config.title, uriOrTemplate, config, readCallback);
|
|
1021
|
+
this.setResourceRequestHandlers();
|
|
1022
|
+
this.sendResourceListChanged();
|
|
1023
|
+
return registeredResource;
|
|
1024
|
+
} else {
|
|
1025
|
+
if (this._registeredResourceTemplates[name]) throw new Error(`Resource template ${name} is already registered`);
|
|
1026
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config.title, uriOrTemplate, config, readCallback);
|
|
1027
|
+
this.setResourceRequestHandlers();
|
|
1028
|
+
this.sendResourceListChanged();
|
|
1029
|
+
return registeredResourceTemplate;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
_createRegisteredResource(name, title, uri, metadata, readCallback) {
|
|
1033
|
+
const registeredResource = {
|
|
1034
|
+
name,
|
|
1035
|
+
title,
|
|
1036
|
+
metadata,
|
|
1037
|
+
readCallback,
|
|
1038
|
+
enabled: true,
|
|
1039
|
+
disable: () => registeredResource.update({ enabled: false }),
|
|
1040
|
+
enable: () => registeredResource.update({ enabled: true }),
|
|
1041
|
+
remove: () => registeredResource.update({ uri: null }),
|
|
1042
|
+
update: (updates) => {
|
|
1043
|
+
if (typeof updates.uri !== "undefined" && updates.uri !== uri) {
|
|
1044
|
+
delete this._registeredResources[uri];
|
|
1045
|
+
if (updates.uri) this._registeredResources[updates.uri] = registeredResource;
|
|
1046
|
+
}
|
|
1047
|
+
if (typeof updates.name !== "undefined") registeredResource.name = updates.name;
|
|
1048
|
+
if (typeof updates.title !== "undefined") registeredResource.title = updates.title;
|
|
1049
|
+
if (typeof updates.metadata !== "undefined") registeredResource.metadata = updates.metadata;
|
|
1050
|
+
if (typeof updates.callback !== "undefined") registeredResource.readCallback = updates.callback;
|
|
1051
|
+
if (typeof updates.enabled !== "undefined") registeredResource.enabled = updates.enabled;
|
|
1052
|
+
this.sendResourceListChanged();
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
this._registeredResources[uri] = registeredResource;
|
|
1056
|
+
return registeredResource;
|
|
1057
|
+
}
|
|
1058
|
+
_createRegisteredResourceTemplate(name, title, template, metadata, readCallback) {
|
|
1059
|
+
const registeredResourceTemplate = {
|
|
1060
|
+
resourceTemplate: template,
|
|
1061
|
+
title,
|
|
1062
|
+
metadata,
|
|
1063
|
+
readCallback,
|
|
1064
|
+
enabled: true,
|
|
1065
|
+
disable: () => registeredResourceTemplate.update({ enabled: false }),
|
|
1066
|
+
enable: () => registeredResourceTemplate.update({ enabled: true }),
|
|
1067
|
+
remove: () => registeredResourceTemplate.update({ name: null }),
|
|
1068
|
+
update: (updates) => {
|
|
1069
|
+
if (typeof updates.name !== "undefined" && updates.name !== name) {
|
|
1070
|
+
delete this._registeredResourceTemplates[name];
|
|
1071
|
+
if (updates.name) this._registeredResourceTemplates[updates.name] = registeredResourceTemplate;
|
|
1072
|
+
}
|
|
1073
|
+
if (typeof updates.title !== "undefined") registeredResourceTemplate.title = updates.title;
|
|
1074
|
+
if (typeof updates.template !== "undefined") registeredResourceTemplate.resourceTemplate = updates.template;
|
|
1075
|
+
if (typeof updates.metadata !== "undefined") registeredResourceTemplate.metadata = updates.metadata;
|
|
1076
|
+
if (typeof updates.callback !== "undefined") registeredResourceTemplate.readCallback = updates.callback;
|
|
1077
|
+
if (typeof updates.enabled !== "undefined") registeredResourceTemplate.enabled = updates.enabled;
|
|
1078
|
+
this.sendResourceListChanged();
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
this._registeredResourceTemplates[name] = registeredResourceTemplate;
|
|
1082
|
+
const variableNames = template.uriTemplate.variableNames;
|
|
1083
|
+
if (Array.isArray(variableNames) && variableNames.some((v) => !!template.completeCallback(v))) this.setCompletionRequestHandler();
|
|
1084
|
+
return registeredResourceTemplate;
|
|
1085
|
+
}
|
|
1086
|
+
_createRegisteredPrompt(name, title, description, argsSchema, callback) {
|
|
1087
|
+
const registeredPrompt = {
|
|
1088
|
+
title,
|
|
1089
|
+
description,
|
|
1090
|
+
argsSchema: argsSchema === void 0 ? void 0 : objectFromShape(argsSchema),
|
|
1091
|
+
callback,
|
|
1092
|
+
enabled: true,
|
|
1093
|
+
disable: () => registeredPrompt.update({ enabled: false }),
|
|
1094
|
+
enable: () => registeredPrompt.update({ enabled: true }),
|
|
1095
|
+
remove: () => registeredPrompt.update({ name: null }),
|
|
1096
|
+
update: (updates) => {
|
|
1097
|
+
if (typeof updates.name !== "undefined" && updates.name !== name) {
|
|
1098
|
+
delete this._registeredPrompts[name];
|
|
1099
|
+
if (updates.name) this._registeredPrompts[updates.name] = registeredPrompt;
|
|
1100
|
+
}
|
|
1101
|
+
if (typeof updates.title !== "undefined") registeredPrompt.title = updates.title;
|
|
1102
|
+
if (typeof updates.description !== "undefined") registeredPrompt.description = updates.description;
|
|
1103
|
+
if (typeof updates.argsSchema !== "undefined") registeredPrompt.argsSchema = objectFromShape(updates.argsSchema);
|
|
1104
|
+
if (typeof updates.callback !== "undefined") registeredPrompt.callback = updates.callback;
|
|
1105
|
+
if (typeof updates.enabled !== "undefined") registeredPrompt.enabled = updates.enabled;
|
|
1106
|
+
this.sendPromptListChanged();
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
this._registeredPrompts[name] = registeredPrompt;
|
|
1110
|
+
if (argsSchema) {
|
|
1111
|
+
if (Object.values(argsSchema).some((field) => {
|
|
1112
|
+
return isCompletable(field instanceof ZodOptional ? field._def?.innerType : field);
|
|
1113
|
+
})) this.setCompletionRequestHandler();
|
|
1114
|
+
}
|
|
1115
|
+
return registeredPrompt;
|
|
1116
|
+
}
|
|
1117
|
+
_createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, execution, _meta, handler) {
|
|
1118
|
+
validateAndWarnToolName(name);
|
|
1119
|
+
const registeredTool = {
|
|
1120
|
+
title,
|
|
1121
|
+
description,
|
|
1122
|
+
inputSchema: getZodSchemaObject(inputSchema),
|
|
1123
|
+
outputSchema: getZodSchemaObject(outputSchema),
|
|
1124
|
+
annotations,
|
|
1125
|
+
execution,
|
|
1126
|
+
_meta,
|
|
1127
|
+
handler,
|
|
1128
|
+
enabled: true,
|
|
1129
|
+
disable: () => registeredTool.update({ enabled: false }),
|
|
1130
|
+
enable: () => registeredTool.update({ enabled: true }),
|
|
1131
|
+
remove: () => registeredTool.update({ name: null }),
|
|
1132
|
+
update: (updates) => {
|
|
1133
|
+
if (typeof updates.name !== "undefined" && updates.name !== name) {
|
|
1134
|
+
if (typeof updates.name === "string") validateAndWarnToolName(updates.name);
|
|
1135
|
+
delete this._registeredTools[name];
|
|
1136
|
+
if (updates.name) this._registeredTools[updates.name] = registeredTool;
|
|
1137
|
+
}
|
|
1138
|
+
if (typeof updates.title !== "undefined") registeredTool.title = updates.title;
|
|
1139
|
+
if (typeof updates.description !== "undefined") registeredTool.description = updates.description;
|
|
1140
|
+
if (typeof updates.paramsSchema !== "undefined") registeredTool.inputSchema = objectFromShape(updates.paramsSchema);
|
|
1141
|
+
if (typeof updates.outputSchema !== "undefined") registeredTool.outputSchema = objectFromShape(updates.outputSchema);
|
|
1142
|
+
if (typeof updates.callback !== "undefined") registeredTool.handler = updates.callback;
|
|
1143
|
+
if (typeof updates.annotations !== "undefined") registeredTool.annotations = updates.annotations;
|
|
1144
|
+
if (typeof updates._meta !== "undefined") registeredTool._meta = updates._meta;
|
|
1145
|
+
if (typeof updates.enabled !== "undefined") registeredTool.enabled = updates.enabled;
|
|
1146
|
+
this.sendToolListChanged();
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
this._registeredTools[name] = registeredTool;
|
|
1150
|
+
this.setToolRequestHandlers();
|
|
1151
|
+
this.sendToolListChanged();
|
|
1152
|
+
return registeredTool;
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* tool() implementation. Parses arguments passed to overrides defined above.
|
|
1156
|
+
*/
|
|
1157
|
+
tool(name, ...rest) {
|
|
1158
|
+
if (this._registeredTools[name]) throw new Error(`Tool ${name} is already registered`);
|
|
1159
|
+
let description;
|
|
1160
|
+
let inputSchema;
|
|
1161
|
+
let outputSchema;
|
|
1162
|
+
let annotations;
|
|
1163
|
+
if (typeof rest[0] === "string") description = rest.shift();
|
|
1164
|
+
if (rest.length > 1) {
|
|
1165
|
+
const firstArg = rest[0];
|
|
1166
|
+
if (isZodRawShapeCompat(firstArg)) {
|
|
1167
|
+
inputSchema = rest.shift();
|
|
1168
|
+
if (rest.length > 1 && typeof rest[0] === "object" && rest[0] !== null && !isZodRawShapeCompat(rest[0])) annotations = rest.shift();
|
|
1169
|
+
} else if (typeof firstArg === "object" && firstArg !== null) {
|
|
1170
|
+
if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
|
|
1171
|
+
annotations = rest.shift();
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
const callback = rest[0];
|
|
1175
|
+
return this._createRegisteredTool(name, void 0, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, void 0, callback);
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Registers a tool with a config object and callback.
|
|
1179
|
+
*/
|
|
1180
|
+
registerTool(name, config, cb) {
|
|
1181
|
+
if (this._registeredTools[name]) throw new Error(`Tool ${name} is already registered`);
|
|
1182
|
+
const { title, description, inputSchema, outputSchema, annotations, _meta } = config;
|
|
1183
|
+
return this._createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, _meta, cb);
|
|
1184
|
+
}
|
|
1185
|
+
prompt(name, ...rest) {
|
|
1186
|
+
if (this._registeredPrompts[name]) throw new Error(`Prompt ${name} is already registered`);
|
|
1187
|
+
let description;
|
|
1188
|
+
if (typeof rest[0] === "string") description = rest.shift();
|
|
1189
|
+
let argsSchema;
|
|
1190
|
+
if (rest.length > 1) argsSchema = rest.shift();
|
|
1191
|
+
const cb = rest[0];
|
|
1192
|
+
const registeredPrompt = this._createRegisteredPrompt(name, void 0, description, argsSchema, cb);
|
|
1193
|
+
this.setPromptRequestHandlers();
|
|
1194
|
+
this.sendPromptListChanged();
|
|
1195
|
+
return registeredPrompt;
|
|
1196
|
+
}
|
|
1197
|
+
/**
|
|
1198
|
+
* Registers a prompt with a config object and callback.
|
|
1199
|
+
*/
|
|
1200
|
+
registerPrompt(name, config, cb) {
|
|
1201
|
+
if (this._registeredPrompts[name]) throw new Error(`Prompt ${name} is already registered`);
|
|
1202
|
+
const { title, description, argsSchema } = config;
|
|
1203
|
+
const registeredPrompt = this._createRegisteredPrompt(name, title, description, argsSchema, cb);
|
|
1204
|
+
this.setPromptRequestHandlers();
|
|
1205
|
+
this.sendPromptListChanged();
|
|
1206
|
+
return registeredPrompt;
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Checks if the server is connected to a transport.
|
|
1210
|
+
* @returns True if the server is connected
|
|
1211
|
+
*/
|
|
1212
|
+
isConnected() {
|
|
1213
|
+
return this.server.transport !== void 0;
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Sends a logging message to the client, if connected.
|
|
1217
|
+
* Note: You only need to send the parameters object, not the entire JSON RPC message
|
|
1218
|
+
* @see LoggingMessageNotification
|
|
1219
|
+
* @param params
|
|
1220
|
+
* @param sessionId optional for stateless and backward compatibility
|
|
1221
|
+
*/
|
|
1222
|
+
async sendLoggingMessage(params, sessionId) {
|
|
1223
|
+
return this.server.sendLoggingMessage(params, sessionId);
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Sends a resource list changed event to the client, if connected.
|
|
1227
|
+
*/
|
|
1228
|
+
sendResourceListChanged() {
|
|
1229
|
+
if (this.isConnected()) this.server.sendResourceListChanged();
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Sends a tool list changed event to the client, if connected.
|
|
1233
|
+
*/
|
|
1234
|
+
sendToolListChanged() {
|
|
1235
|
+
if (this.isConnected()) this.server.sendToolListChanged();
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Sends a prompt list changed event to the client, if connected.
|
|
1239
|
+
*/
|
|
1240
|
+
sendPromptListChanged() {
|
|
1241
|
+
if (this.isConnected()) this.server.sendPromptListChanged();
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
const EMPTY_OBJECT_JSON_SCHEMA = {
|
|
1245
|
+
type: "object",
|
|
1246
|
+
properties: {}
|
|
1247
|
+
};
|
|
1248
|
+
/**
|
|
1249
|
+
* Checks if a value looks like a Zod schema by checking for parse/safeParse methods.
|
|
1250
|
+
*/
|
|
1251
|
+
function isZodTypeLike(value) {
|
|
1252
|
+
return value !== null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "safeParse" in value && typeof value.safeParse === "function";
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Checks if an object is a Zod schema instance (v3 or v4).
|
|
1256
|
+
*
|
|
1257
|
+
* Zod schemas have internal markers:
|
|
1258
|
+
* - v3: `_def` property
|
|
1259
|
+
* - v4: `_zod` property
|
|
1260
|
+
*
|
|
1261
|
+
* This includes transformed schemas like z.preprocess(), z.transform(), z.pipe().
|
|
1262
|
+
*/
|
|
1263
|
+
function isZodSchemaInstance(obj) {
|
|
1264
|
+
return "_def" in obj || "_zod" in obj || isZodTypeLike(obj);
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Checks if an object is a "raw shape" - a plain object where values are Zod schemas.
|
|
1268
|
+
*
|
|
1269
|
+
* Raw shapes are used as shorthand: `{ name: z.string() }` instead of `z.object({ name: z.string() })`.
|
|
1270
|
+
*
|
|
1271
|
+
* IMPORTANT: This must NOT match actual Zod schema instances (like z.preprocess, z.pipe),
|
|
1272
|
+
* which have internal properties that could be mistaken for schema values.
|
|
1273
|
+
*/
|
|
1274
|
+
function isZodRawShapeCompat(obj) {
|
|
1275
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
1276
|
+
if (isZodSchemaInstance(obj)) return false;
|
|
1277
|
+
if (Object.keys(obj).length === 0) return true;
|
|
1278
|
+
return Object.values(obj).some(isZodTypeLike);
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Converts a provided Zod schema to a Zod object if it is a ZodRawShapeCompat,
|
|
1282
|
+
* otherwise returns the schema as is. Throws if the value is not a valid Zod schema.
|
|
1283
|
+
*/
|
|
1284
|
+
function getZodSchemaObject(schema) {
|
|
1285
|
+
if (!schema) return;
|
|
1286
|
+
if (isZodRawShapeCompat(schema)) return objectFromShape(schema);
|
|
1287
|
+
if (!isZodSchemaInstance(schema)) throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
|
|
1288
|
+
return schema;
|
|
1289
|
+
}
|
|
1290
|
+
function promptArgumentsFromSchema(schema) {
|
|
1291
|
+
const shape = getObjectShape(schema);
|
|
1292
|
+
if (!shape) return [];
|
|
1293
|
+
return Object.entries(shape).map(([name, field]) => {
|
|
1294
|
+
return {
|
|
1295
|
+
name,
|
|
1296
|
+
description: getSchemaDescription(field),
|
|
1297
|
+
required: !isSchemaOptional(field)
|
|
1298
|
+
};
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
function getMethodValue(schema) {
|
|
1302
|
+
const methodSchema = getObjectShape(schema)?.method;
|
|
1303
|
+
if (!methodSchema) throw new Error("Schema is missing a method literal");
|
|
1304
|
+
const value = getLiteralValue(methodSchema);
|
|
1305
|
+
if (typeof value === "string") return value;
|
|
1306
|
+
throw new Error("Schema method literal must be a string");
|
|
1307
|
+
}
|
|
1308
|
+
function createCompletionResult(suggestions) {
|
|
1309
|
+
return { completion: {
|
|
1310
|
+
values: suggestions.slice(0, 100),
|
|
1311
|
+
total: suggestions.length,
|
|
1312
|
+
hasMore: suggestions.length > 100
|
|
1313
|
+
} };
|
|
1314
|
+
}
|
|
1315
|
+
const EMPTY_COMPLETION_RESULT = { completion: {
|
|
1316
|
+
values: [],
|
|
1317
|
+
hasMore: false
|
|
1318
|
+
} };
|
|
1319
|
+
//#endregion
|
|
1320
|
+
//#region ../../node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.4.3/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
1321
|
+
/**
|
|
1322
|
+
* Server transport for stdio: this communicates with an MCP client by reading from the current process' stdin and writing to stdout.
|
|
1323
|
+
*
|
|
1324
|
+
* This transport is only available in Node.js environments.
|
|
1325
|
+
*/
|
|
1326
|
+
var StdioServerTransport = class {
|
|
1327
|
+
constructor(_stdin = process$1.stdin, _stdout = process$1.stdout) {
|
|
1328
|
+
this._stdin = _stdin;
|
|
1329
|
+
this._stdout = _stdout;
|
|
1330
|
+
this._readBuffer = new ReadBuffer();
|
|
1331
|
+
this._started = false;
|
|
1332
|
+
this._ondata = (chunk) => {
|
|
1333
|
+
this._readBuffer.append(chunk);
|
|
1334
|
+
this.processReadBuffer();
|
|
1335
|
+
};
|
|
1336
|
+
this._onerror = (error) => {
|
|
1337
|
+
this.onerror?.(error);
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Starts listening for messages on stdin.
|
|
1342
|
+
*/
|
|
1343
|
+
async start() {
|
|
1344
|
+
if (this._started) throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
1345
|
+
this._started = true;
|
|
1346
|
+
this._stdin.on("data", this._ondata);
|
|
1347
|
+
this._stdin.on("error", this._onerror);
|
|
1348
|
+
}
|
|
1349
|
+
processReadBuffer() {
|
|
1350
|
+
while (true) try {
|
|
1351
|
+
const message = this._readBuffer.readMessage();
|
|
1352
|
+
if (message === null) break;
|
|
1353
|
+
this.onmessage?.(message);
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
this.onerror?.(error);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
async close() {
|
|
1359
|
+
this._stdin.off("data", this._ondata);
|
|
1360
|
+
this._stdin.off("error", this._onerror);
|
|
1361
|
+
if (this._stdin.listenerCount("data") === 0) this._stdin.pause();
|
|
1362
|
+
this._readBuffer.clear();
|
|
1363
|
+
this.onclose?.();
|
|
1364
|
+
}
|
|
1365
|
+
send(message) {
|
|
1366
|
+
return new Promise((resolve) => {
|
|
1367
|
+
const json = serializeMessage(message);
|
|
1368
|
+
if (this._stdout.write(json)) resolve();
|
|
1369
|
+
else this._stdout.once("drain", resolve);
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
//#endregion
|
|
1374
|
+
//#region src/mcp-servers/desktop-server.ts
|
|
1375
|
+
/**
|
|
1376
|
+
* agent-desktop MCP Server
|
|
1377
|
+
*
|
|
1378
|
+
* Stdio MCP server that wraps the agent-desktop native binary. Exposes
|
|
1379
|
+
* 12 computer_* tools — one per agent-desktop subcommand — and translates
|
|
1380
|
+
* between MCP JSON-RPC and the binary's CLI/JSON interface.
|
|
1381
|
+
*/
|
|
1382
|
+
const ComputerSnapshotInputSchema = z.object({
|
|
1383
|
+
app: z.string().optional(),
|
|
1384
|
+
window_id: z.string().optional(),
|
|
1385
|
+
interactive_only: z.boolean().optional(),
|
|
1386
|
+
include_bounds: z.boolean().optional(),
|
|
1387
|
+
compact: z.boolean().optional(),
|
|
1388
|
+
max_depth: z.number().optional(),
|
|
1389
|
+
surface: z.string().optional()
|
|
1390
|
+
});
|
|
1391
|
+
const ComputerScreenshotInputSchema = z.object({
|
|
1392
|
+
output_path: z.string().optional(),
|
|
1393
|
+
app: z.string().optional(),
|
|
1394
|
+
window_id: z.string().optional()
|
|
1395
|
+
});
|
|
1396
|
+
const ComputerClickInputSchema = z.object({
|
|
1397
|
+
ref: z.string().optional(),
|
|
1398
|
+
x: z.number().optional(),
|
|
1399
|
+
y: z.number().optional(),
|
|
1400
|
+
button: z.enum([
|
|
1401
|
+
"left",
|
|
1402
|
+
"right",
|
|
1403
|
+
"middle"
|
|
1404
|
+
]).optional(),
|
|
1405
|
+
count: z.enum([
|
|
1406
|
+
"single",
|
|
1407
|
+
"double",
|
|
1408
|
+
"triple"
|
|
1409
|
+
]).optional()
|
|
1410
|
+
});
|
|
1411
|
+
const ComputerMouseMoveInputSchema = z.object({
|
|
1412
|
+
ref: z.string().optional(),
|
|
1413
|
+
x: z.number().optional(),
|
|
1414
|
+
y: z.number().optional(),
|
|
1415
|
+
duration_ms: z.number().optional()
|
|
1416
|
+
});
|
|
1417
|
+
const ComputerTypeInputSchema = z.object({
|
|
1418
|
+
ref: z.string(),
|
|
1419
|
+
text: z.string()
|
|
1420
|
+
});
|
|
1421
|
+
const ComputerPressInputSchema = z.object({
|
|
1422
|
+
key: z.string(),
|
|
1423
|
+
app: z.string().optional()
|
|
1424
|
+
});
|
|
1425
|
+
const ComputerScrollInputSchema = z.object({
|
|
1426
|
+
ref: z.string(),
|
|
1427
|
+
direction: z.enum([
|
|
1428
|
+
"left",
|
|
1429
|
+
"right",
|
|
1430
|
+
"up",
|
|
1431
|
+
"down"
|
|
1432
|
+
]),
|
|
1433
|
+
amount: z.number().optional()
|
|
1434
|
+
});
|
|
1435
|
+
const ComputerLaunchInputSchema = z.object({
|
|
1436
|
+
app: z.string(),
|
|
1437
|
+
timeout_ms: z.number().optional()
|
|
1438
|
+
});
|
|
1439
|
+
const ComputerListWindowsInputSchema = z.object({ app: z.string().optional() });
|
|
1440
|
+
const ComputerFocusWindowInputSchema = z.object({
|
|
1441
|
+
window_id: z.string().optional(),
|
|
1442
|
+
app: z.string().optional(),
|
|
1443
|
+
title: z.string().optional()
|
|
1444
|
+
});
|
|
1445
|
+
const ComputerGetInputSchema = z.object({
|
|
1446
|
+
ref: z.string(),
|
|
1447
|
+
property: z.enum([
|
|
1448
|
+
"text",
|
|
1449
|
+
"role",
|
|
1450
|
+
"value",
|
|
1451
|
+
"title",
|
|
1452
|
+
"bounds",
|
|
1453
|
+
"states"
|
|
1454
|
+
]).optional()
|
|
1455
|
+
});
|
|
1456
|
+
const ComputerWaitInputSchema = z.object({
|
|
1457
|
+
milliseconds: z.number().optional(),
|
|
1458
|
+
element: z.string().optional(),
|
|
1459
|
+
window: z.string().optional(),
|
|
1460
|
+
text: z.string().optional(),
|
|
1461
|
+
timeout_ms: z.number().optional(),
|
|
1462
|
+
menu_open: z.string().optional(),
|
|
1463
|
+
menu_closed: z.string().optional()
|
|
1464
|
+
});
|
|
1465
|
+
function resolveAgentDesktopInvoker() {
|
|
1466
|
+
try {
|
|
1467
|
+
const jsPath = join(dirname(createRequire(import.meta.url).resolve("agent-desktop/package.json")), "bin", "agent-desktop.js");
|
|
1468
|
+
return {
|
|
1469
|
+
command: process.execPath,
|
|
1470
|
+
prefixArgs: [jsPath],
|
|
1471
|
+
available: true
|
|
1472
|
+
};
|
|
1473
|
+
} catch {
|
|
1474
|
+
return {
|
|
1475
|
+
command: "agent-desktop",
|
|
1476
|
+
prefixArgs: [],
|
|
1477
|
+
available: true
|
|
1478
|
+
};
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
const ENV_ALLOWLIST = [
|
|
1482
|
+
"HOME",
|
|
1483
|
+
"LANG",
|
|
1484
|
+
"LC_ALL",
|
|
1485
|
+
"LC_CTYPE",
|
|
1486
|
+
"LOGNAME",
|
|
1487
|
+
"PATH",
|
|
1488
|
+
"SHELL",
|
|
1489
|
+
"TERM",
|
|
1490
|
+
"TERM_PROGRAM",
|
|
1491
|
+
"TERM_PROGRAM_VERSION",
|
|
1492
|
+
"TMP",
|
|
1493
|
+
"TMPDIR",
|
|
1494
|
+
"TEMP",
|
|
1495
|
+
"USER",
|
|
1496
|
+
"__CF_USER_TEXT_ENCODING"
|
|
1497
|
+
];
|
|
1498
|
+
function buildEnv() {
|
|
1499
|
+
const env = { FORCE_COLOR: "0" };
|
|
1500
|
+
for (const key of ENV_ALLOWLIST) if (process.env[key]) env[key] = process.env[key];
|
|
1501
|
+
return env;
|
|
1502
|
+
}
|
|
1503
|
+
function runDesktop(args) {
|
|
1504
|
+
const invoker = resolveAgentDesktopInvoker();
|
|
1505
|
+
return new Promise((resolve) => {
|
|
1506
|
+
execFile(invoker.command, [...invoker.prefixArgs, ...args], {
|
|
1507
|
+
env: buildEnv(),
|
|
1508
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
1509
|
+
timeout: 12e4
|
|
1510
|
+
}, (error, stdout, stderr) => {
|
|
1511
|
+
if (stdout.trim()) try {
|
|
1512
|
+
const parsed = JSON.parse(stdout.trim());
|
|
1513
|
+
if (parsed && typeof parsed === "object") {
|
|
1514
|
+
resolve({
|
|
1515
|
+
success: parsed.ok === true && !error,
|
|
1516
|
+
data: parsed,
|
|
1517
|
+
error: !parsed.ok ? parsed.error?.message ?? JSON.stringify(parsed.error) : error?.message
|
|
1518
|
+
});
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
} catch {}
|
|
1522
|
+
if (error) {
|
|
1523
|
+
resolve({
|
|
1524
|
+
success: false,
|
|
1525
|
+
data: null,
|
|
1526
|
+
error: stderr?.trim() || error.message
|
|
1527
|
+
});
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
resolve({
|
|
1531
|
+
success: true,
|
|
1532
|
+
data: stdout.trim()
|
|
1533
|
+
});
|
|
1534
|
+
});
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
function formatError(result) {
|
|
1538
|
+
if (!result.error) return "Unknown agent-desktop error";
|
|
1539
|
+
const lower = result.error.toLowerCase();
|
|
1540
|
+
if (lower.includes("accessibility")) return `${result.error}\nEnable Accessibility permission for your terminal in System Settings > Privacy & Security > Accessibility.`;
|
|
1541
|
+
if (lower.includes("supports macos only") || lower.includes("macos only")) return `${result.error}\nagent-desktop supports macOS only.`;
|
|
1542
|
+
return result.error;
|
|
1543
|
+
}
|
|
1544
|
+
function isMacOS() {
|
|
1545
|
+
return process.platform === "darwin";
|
|
1546
|
+
}
|
|
1547
|
+
function macOnlyError() {
|
|
1548
|
+
return "Desktop automation is only available on macOS. agent-desktop does not support this platform.";
|
|
1549
|
+
}
|
|
1550
|
+
function safeStr(value) {
|
|
1551
|
+
if (typeof value === "string") return value;
|
|
1552
|
+
if (typeof value === "number" || typeof value === "boolean") return String(value);
|
|
1553
|
+
return JSON.stringify(value);
|
|
1554
|
+
}
|
|
1555
|
+
function buildCliArgs(subcommand, input, mapping) {
|
|
1556
|
+
const args = [subcommand];
|
|
1557
|
+
if (mapping.positional) for (const key of mapping.positional) {
|
|
1558
|
+
const value = input[key];
|
|
1559
|
+
if (value !== void 0 && value !== null) args.push(safeStr(value));
|
|
1560
|
+
}
|
|
1561
|
+
if (mapping.flags) for (const [key, flagName] of Object.entries(mapping.flags)) {
|
|
1562
|
+
const value = input[key];
|
|
1563
|
+
if (value === void 0 || value === null) continue;
|
|
1564
|
+
if (typeof value === "boolean") {
|
|
1565
|
+
if (value) args.push("--" + flagName);
|
|
1566
|
+
} else args.push("--" + flagName, safeStr(value));
|
|
1567
|
+
}
|
|
1568
|
+
return args;
|
|
1569
|
+
}
|
|
1570
|
+
const server = new McpServer({
|
|
1571
|
+
name: "agent-desktop",
|
|
1572
|
+
version: "0.1.0"
|
|
1573
|
+
}, { capabilities: { tools: {} } });
|
|
1574
|
+
function register(name, description, inputSchema, toArgs) {
|
|
1575
|
+
server.registerTool(name, {
|
|
1576
|
+
description,
|
|
1577
|
+
inputSchema
|
|
1578
|
+
}, async (input) => {
|
|
1579
|
+
if (!isMacOS()) return {
|
|
1580
|
+
content: [{
|
|
1581
|
+
type: "text",
|
|
1582
|
+
text: macOnlyError()
|
|
1583
|
+
}],
|
|
1584
|
+
isError: true
|
|
1585
|
+
};
|
|
1586
|
+
if (!resolveAgentDesktopInvoker().available) return {
|
|
1587
|
+
content: [{
|
|
1588
|
+
type: "text",
|
|
1589
|
+
text: "agent-desktop is not installed. Run /mcp and install Desktop Automation to set it up."
|
|
1590
|
+
}],
|
|
1591
|
+
isError: true
|
|
1592
|
+
};
|
|
1593
|
+
let args;
|
|
1594
|
+
try {
|
|
1595
|
+
args = toArgs(input ?? {});
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
return {
|
|
1598
|
+
content: [{
|
|
1599
|
+
type: "text",
|
|
1600
|
+
text: `Invalid arguments: ${err instanceof Error ? err.message : String(err)}`
|
|
1601
|
+
}],
|
|
1602
|
+
isError: true
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
const result = await runDesktop(args);
|
|
1606
|
+
if (!result.success) return {
|
|
1607
|
+
content: [{
|
|
1608
|
+
type: "text",
|
|
1609
|
+
text: formatError(result)
|
|
1610
|
+
}],
|
|
1611
|
+
isError: true
|
|
1612
|
+
};
|
|
1613
|
+
return { content: [{
|
|
1614
|
+
type: "text",
|
|
1615
|
+
text: typeof result.data === "string" ? result.data : JSON.stringify(result.data, null, 2)
|
|
1616
|
+
}] };
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
register("computer_snapshot", "Capture an accessibility-tree snapshot of the macOS desktop. Returns stable element refs (e.g. @e1, @e2) that can be used with computer_click, computer_type, computer_scroll, and computer_get.", ComputerSnapshotInputSchema, (input) => buildCliArgs("snapshot", input, { flags: {
|
|
1620
|
+
app: "app",
|
|
1621
|
+
window_id: "window-id",
|
|
1622
|
+
interactive_only: "interactive-only",
|
|
1623
|
+
include_bounds: "include-bounds",
|
|
1624
|
+
compact: "compact",
|
|
1625
|
+
max_depth: "max-depth",
|
|
1626
|
+
surface: "surface"
|
|
1627
|
+
} }));
|
|
1628
|
+
register("computer_screenshot", "Take a PNG screenshot of the macOS desktop, a specific window, or a specific application.", ComputerScreenshotInputSchema, (input) => buildCliArgs("screenshot", input, {
|
|
1629
|
+
positional: ["output_path"],
|
|
1630
|
+
flags: {
|
|
1631
|
+
app: "app",
|
|
1632
|
+
window_id: "window-id"
|
|
1633
|
+
}
|
|
1634
|
+
}));
|
|
1635
|
+
register("computer_click", "Click a desktop UI element by ref (from computer_snapshot) or by screen coordinates. Supports left/right/middle button and single/double/triple click.", ComputerClickInputSchema, (input) => {
|
|
1636
|
+
if (input.ref) return ["click", input.ref];
|
|
1637
|
+
if (input.x !== void 0 && input.y !== void 0) {
|
|
1638
|
+
const args = [
|
|
1639
|
+
"mouse-click",
|
|
1640
|
+
"--xy",
|
|
1641
|
+
`${input.x},${input.y}`
|
|
1642
|
+
];
|
|
1643
|
+
if (input.button) args.push("--button", input.button);
|
|
1644
|
+
if (input.count) args.push("--count", input.count);
|
|
1645
|
+
return args;
|
|
1646
|
+
}
|
|
1647
|
+
throw new Error("Either ref or x,y coordinates are required");
|
|
1648
|
+
});
|
|
1649
|
+
register("computer_mouse_move", "Move the mouse to a desktop element (by ref) or to absolute screen coordinates.", ComputerMouseMoveInputSchema, (input) => {
|
|
1650
|
+
if (input.ref) return ["hover", input.ref];
|
|
1651
|
+
if (input.x !== void 0 && input.y !== void 0) return [
|
|
1652
|
+
"mouse-move",
|
|
1653
|
+
"--xy",
|
|
1654
|
+
`${input.x},${input.y}`
|
|
1655
|
+
];
|
|
1656
|
+
throw new Error("Either ref or x,y coordinates are required");
|
|
1657
|
+
});
|
|
1658
|
+
register("computer_type", "Type text into a focused desktop UI element identified by ref from computer_snapshot.", ComputerTypeInputSchema, (input) => buildCliArgs("type", input, { positional: ["ref", "text"] }));
|
|
1659
|
+
register("computer_press", "Press a key or key chord (e.g. cmd+s, Enter, Escape) on the macOS desktop. Use for keyboard shortcuts.", ComputerPressInputSchema, (input) => buildCliArgs("press", input, {
|
|
1660
|
+
positional: ["key"],
|
|
1661
|
+
flags: { app: "app" }
|
|
1662
|
+
}));
|
|
1663
|
+
register("computer_scroll", "Scroll a desktop UI element (by ref from computer_snapshot) in the specified direction.", ComputerScrollInputSchema, (input) => buildCliArgs("scroll", input, {
|
|
1664
|
+
positional: ["ref"],
|
|
1665
|
+
flags: {
|
|
1666
|
+
direction: "direction",
|
|
1667
|
+
amount: "amount"
|
|
1668
|
+
}
|
|
1669
|
+
}));
|
|
1670
|
+
register("computer_launch", "Launch a macOS application by name and optionally wait for its window to appear.", ComputerLaunchInputSchema, (input) => buildCliArgs("launch", input, {
|
|
1671
|
+
positional: ["app"],
|
|
1672
|
+
flags: { timeout_ms: "timeout" }
|
|
1673
|
+
}));
|
|
1674
|
+
register("computer_list_windows", "List open windows on the macOS desktop, optionally filtered by application. Returns window IDs for computer_focus_window.", ComputerListWindowsInputSchema, (input) => buildCliArgs("list-windows", input, { flags: { app: "app" } }));
|
|
1675
|
+
register("computer_focus_window", "Focus a macOS window by ID, app name, or title. Use after computer_list_windows.", ComputerFocusWindowInputSchema, (input) => buildCliArgs("focus-window", input, { flags: {
|
|
1676
|
+
window_id: "window-id",
|
|
1677
|
+
app: "app",
|
|
1678
|
+
title: "title"
|
|
1679
|
+
} }));
|
|
1680
|
+
register("computer_get", "Read properties (text, value, title, bounds, role, states) from a desktop element ref.", ComputerGetInputSchema, (input) => buildCliArgs("get", input, {
|
|
1681
|
+
positional: ["ref"],
|
|
1682
|
+
flags: { property: "property" }
|
|
1683
|
+
}));
|
|
1684
|
+
register("computer_wait", "Wait for a condition on the macOS desktop: delay, element appearance, window, text, or menu open/close.", ComputerWaitInputSchema, (input) => buildCliArgs("wait", input, {
|
|
1685
|
+
positional: ["milliseconds"],
|
|
1686
|
+
flags: {
|
|
1687
|
+
element: "element",
|
|
1688
|
+
window: "window",
|
|
1689
|
+
text: "text",
|
|
1690
|
+
timeout_ms: "timeout",
|
|
1691
|
+
menu_open: "menu",
|
|
1692
|
+
menu_closed: "menu-closed"
|
|
1693
|
+
}
|
|
1694
|
+
}));
|
|
1695
|
+
register("computer_skill", "CRITICAL: Call this FIRST before any desktop automation. Returns the complete agent-desktop workflow guide: the observe-act loop (skeleton→drill→act→verify), ref system (@e1, @e2), command quick reference, error codes with recovery hints, and key principles. Use this to understand how to navigate and control the desktop effectively.", z.object({ full: z.boolean().optional().describe("Include all reference files (commands, workflows, macos internals)") }), (input) => {
|
|
1696
|
+
const args = [
|
|
1697
|
+
"skills",
|
|
1698
|
+
"get",
|
|
1699
|
+
"desktop"
|
|
1700
|
+
];
|
|
1701
|
+
if (input.full) args.push("--full");
|
|
1702
|
+
return args;
|
|
1703
|
+
});
|
|
1704
|
+
async function main() {
|
|
1705
|
+
const transport = new StdioServerTransport();
|
|
1706
|
+
await server.connect(transport);
|
|
1707
|
+
}
|
|
1708
|
+
main().catch((err) => {
|
|
1709
|
+
console.error("agent-desktop MCP server failed to start:", err);
|
|
1710
|
+
process.exit(1);
|
|
1711
|
+
});
|
|
1712
|
+
//#endregion
|
|
1713
|
+
export {};
|