@debugelectron/debug-electron-mcp 1.6.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/LICENSE +21 -0
- package/README.md +828 -0
- package/dist/handlers.d.ts +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4615 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas.d.ts +116 -0
- package/dist/screenshot.d.ts +9 -0
- package/dist/tools.d.ts +13 -0
- package/dist/utils/electron-commands.d.ts +65 -0
- package/dist/utils/electron-connection.d.ts +39 -0
- package/dist/utils/electron-discovery.d.ts +52 -0
- package/dist/utils/electron-enhanced-commands.d.ts +51 -0
- package/dist/utils/electron-input-commands.d.ts +16 -0
- package/dist/utils/electron-logs.d.ts +11 -0
- package/dist/utils/electron-process.d.ts +27 -0
- package/dist/utils/logger.d.ts +19 -0
- package/dist/utils/logs.d.ts +1 -0
- package/dist/utils/project.d.ts +1 -0
- package/package.json +103 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4615 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/******/ (() => { // webpackBootstrap
|
|
3
|
+
/******/ "use strict";
|
|
4
|
+
/******/ // The require scope
|
|
5
|
+
/******/ var __webpack_require__ = {};
|
|
6
|
+
/******/
|
|
7
|
+
/************************************************************************/
|
|
8
|
+
/******/ /* webpack/runtime/compat get default export */
|
|
9
|
+
/******/ (() => {
|
|
10
|
+
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
11
|
+
/******/ __webpack_require__.n = (module) => {
|
|
12
|
+
/******/ var getter = module && module.__esModule ?
|
|
13
|
+
/******/ () => (module['default']) :
|
|
14
|
+
/******/ () => (module);
|
|
15
|
+
/******/ __webpack_require__.d(getter, { a: getter });
|
|
16
|
+
/******/ return getter;
|
|
17
|
+
/******/ };
|
|
18
|
+
/******/ })();
|
|
19
|
+
/******/
|
|
20
|
+
/******/ /* webpack/runtime/define property getters */
|
|
21
|
+
/******/ (() => {
|
|
22
|
+
/******/ // define getter functions for harmony exports
|
|
23
|
+
/******/ __webpack_require__.d = (exports, definition) => {
|
|
24
|
+
/******/ for(var key in definition) {
|
|
25
|
+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
26
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
27
|
+
/******/ }
|
|
28
|
+
/******/ }
|
|
29
|
+
/******/ };
|
|
30
|
+
/******/ })();
|
|
31
|
+
/******/
|
|
32
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
33
|
+
/******/ (() => {
|
|
34
|
+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
35
|
+
/******/ })();
|
|
36
|
+
/******/
|
|
37
|
+
/************************************************************************/
|
|
38
|
+
|
|
39
|
+
;// external "zod"
|
|
40
|
+
const external_zod_namespaceObject = require("zod");
|
|
41
|
+
;// ./node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
42
|
+
|
|
43
|
+
const LATEST_PROTOCOL_VERSION = "2025-06-18";
|
|
44
|
+
const DEFAULT_NEGOTIATED_PROTOCOL_VERSION = "2025-03-26";
|
|
45
|
+
const SUPPORTED_PROTOCOL_VERSIONS = [
|
|
46
|
+
LATEST_PROTOCOL_VERSION,
|
|
47
|
+
"2025-03-26",
|
|
48
|
+
"2024-11-05",
|
|
49
|
+
"2024-10-07",
|
|
50
|
+
];
|
|
51
|
+
/* JSON-RPC types */
|
|
52
|
+
const JSONRPC_VERSION = "2.0";
|
|
53
|
+
/**
|
|
54
|
+
* A progress token, used to associate progress notifications with the original request.
|
|
55
|
+
*/
|
|
56
|
+
const ProgressTokenSchema = external_zod_namespaceObject.z.union([external_zod_namespaceObject.z.string(), external_zod_namespaceObject.z.number().int()]);
|
|
57
|
+
/**
|
|
58
|
+
* An opaque token used to represent a cursor for pagination.
|
|
59
|
+
*/
|
|
60
|
+
const CursorSchema = external_zod_namespaceObject.z.string();
|
|
61
|
+
const RequestMetaSchema = external_zod_namespaceObject.z
|
|
62
|
+
.object({
|
|
63
|
+
/**
|
|
64
|
+
* If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications.
|
|
65
|
+
*/
|
|
66
|
+
progressToken: external_zod_namespaceObject.z.optional(ProgressTokenSchema),
|
|
67
|
+
})
|
|
68
|
+
.passthrough();
|
|
69
|
+
const BaseRequestParamsSchema = external_zod_namespaceObject.z
|
|
70
|
+
.object({
|
|
71
|
+
_meta: external_zod_namespaceObject.z.optional(RequestMetaSchema),
|
|
72
|
+
})
|
|
73
|
+
.passthrough();
|
|
74
|
+
const RequestSchema = external_zod_namespaceObject.z.object({
|
|
75
|
+
method: external_zod_namespaceObject.z.string(),
|
|
76
|
+
params: external_zod_namespaceObject.z.optional(BaseRequestParamsSchema),
|
|
77
|
+
});
|
|
78
|
+
const BaseNotificationParamsSchema = external_zod_namespaceObject.z
|
|
79
|
+
.object({
|
|
80
|
+
/**
|
|
81
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
82
|
+
* for notes on _meta usage.
|
|
83
|
+
*/
|
|
84
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
85
|
+
})
|
|
86
|
+
.passthrough();
|
|
87
|
+
const NotificationSchema = external_zod_namespaceObject.z.object({
|
|
88
|
+
method: external_zod_namespaceObject.z.string(),
|
|
89
|
+
params: external_zod_namespaceObject.z.optional(BaseNotificationParamsSchema),
|
|
90
|
+
});
|
|
91
|
+
const ResultSchema = external_zod_namespaceObject.z
|
|
92
|
+
.object({
|
|
93
|
+
/**
|
|
94
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
95
|
+
* for notes on _meta usage.
|
|
96
|
+
*/
|
|
97
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
98
|
+
})
|
|
99
|
+
.passthrough();
|
|
100
|
+
/**
|
|
101
|
+
* A uniquely identifying ID for a request in JSON-RPC.
|
|
102
|
+
*/
|
|
103
|
+
const RequestIdSchema = external_zod_namespaceObject.z.union([external_zod_namespaceObject.z.string(), external_zod_namespaceObject.z.number().int()]);
|
|
104
|
+
/**
|
|
105
|
+
* A request that expects a response.
|
|
106
|
+
*/
|
|
107
|
+
const JSONRPCRequestSchema = external_zod_namespaceObject.z
|
|
108
|
+
.object({
|
|
109
|
+
jsonrpc: external_zod_namespaceObject.z.literal(JSONRPC_VERSION),
|
|
110
|
+
id: RequestIdSchema,
|
|
111
|
+
})
|
|
112
|
+
.merge(RequestSchema)
|
|
113
|
+
.strict();
|
|
114
|
+
const isJSONRPCRequest = (value) => JSONRPCRequestSchema.safeParse(value).success;
|
|
115
|
+
/**
|
|
116
|
+
* A notification which does not expect a response.
|
|
117
|
+
*/
|
|
118
|
+
const JSONRPCNotificationSchema = external_zod_namespaceObject.z
|
|
119
|
+
.object({
|
|
120
|
+
jsonrpc: external_zod_namespaceObject.z.literal(JSONRPC_VERSION),
|
|
121
|
+
})
|
|
122
|
+
.merge(NotificationSchema)
|
|
123
|
+
.strict();
|
|
124
|
+
const isJSONRPCNotification = (value) => JSONRPCNotificationSchema.safeParse(value).success;
|
|
125
|
+
/**
|
|
126
|
+
* A successful (non-error) response to a request.
|
|
127
|
+
*/
|
|
128
|
+
const JSONRPCResponseSchema = external_zod_namespaceObject.z
|
|
129
|
+
.object({
|
|
130
|
+
jsonrpc: external_zod_namespaceObject.z.literal(JSONRPC_VERSION),
|
|
131
|
+
id: RequestIdSchema,
|
|
132
|
+
result: ResultSchema,
|
|
133
|
+
})
|
|
134
|
+
.strict();
|
|
135
|
+
const isJSONRPCResponse = (value) => JSONRPCResponseSchema.safeParse(value).success;
|
|
136
|
+
/**
|
|
137
|
+
* Error codes defined by the JSON-RPC specification.
|
|
138
|
+
*/
|
|
139
|
+
var ErrorCode;
|
|
140
|
+
(function (ErrorCode) {
|
|
141
|
+
// SDK error codes
|
|
142
|
+
ErrorCode[ErrorCode["ConnectionClosed"] = -32000] = "ConnectionClosed";
|
|
143
|
+
ErrorCode[ErrorCode["RequestTimeout"] = -32001] = "RequestTimeout";
|
|
144
|
+
// Standard JSON-RPC error codes
|
|
145
|
+
ErrorCode[ErrorCode["ParseError"] = -32700] = "ParseError";
|
|
146
|
+
ErrorCode[ErrorCode["InvalidRequest"] = -32600] = "InvalidRequest";
|
|
147
|
+
ErrorCode[ErrorCode["MethodNotFound"] = -32601] = "MethodNotFound";
|
|
148
|
+
ErrorCode[ErrorCode["InvalidParams"] = -32602] = "InvalidParams";
|
|
149
|
+
ErrorCode[ErrorCode["InternalError"] = -32603] = "InternalError";
|
|
150
|
+
})(ErrorCode || (ErrorCode = {}));
|
|
151
|
+
/**
|
|
152
|
+
* A response to a request that indicates an error occurred.
|
|
153
|
+
*/
|
|
154
|
+
const JSONRPCErrorSchema = external_zod_namespaceObject.z
|
|
155
|
+
.object({
|
|
156
|
+
jsonrpc: external_zod_namespaceObject.z.literal(JSONRPC_VERSION),
|
|
157
|
+
id: RequestIdSchema,
|
|
158
|
+
error: external_zod_namespaceObject.z.object({
|
|
159
|
+
/**
|
|
160
|
+
* The error type that occurred.
|
|
161
|
+
*/
|
|
162
|
+
code: external_zod_namespaceObject.z.number().int(),
|
|
163
|
+
/**
|
|
164
|
+
* A short description of the error. The message SHOULD be limited to a concise single sentence.
|
|
165
|
+
*/
|
|
166
|
+
message: external_zod_namespaceObject.z.string(),
|
|
167
|
+
/**
|
|
168
|
+
* Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.).
|
|
169
|
+
*/
|
|
170
|
+
data: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.unknown()),
|
|
171
|
+
}),
|
|
172
|
+
})
|
|
173
|
+
.strict();
|
|
174
|
+
const isJSONRPCError = (value) => JSONRPCErrorSchema.safeParse(value).success;
|
|
175
|
+
const JSONRPCMessageSchema = external_zod_namespaceObject.z.union([
|
|
176
|
+
JSONRPCRequestSchema,
|
|
177
|
+
JSONRPCNotificationSchema,
|
|
178
|
+
JSONRPCResponseSchema,
|
|
179
|
+
JSONRPCErrorSchema,
|
|
180
|
+
]);
|
|
181
|
+
/* Empty result */
|
|
182
|
+
/**
|
|
183
|
+
* A response that indicates success but carries no data.
|
|
184
|
+
*/
|
|
185
|
+
const EmptyResultSchema = ResultSchema.strict();
|
|
186
|
+
/* Cancellation */
|
|
187
|
+
/**
|
|
188
|
+
* This notification can be sent by either side to indicate that it is cancelling a previously-issued request.
|
|
189
|
+
*
|
|
190
|
+
* The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.
|
|
191
|
+
*
|
|
192
|
+
* This notification indicates that the result will be unused, so any associated processing SHOULD cease.
|
|
193
|
+
*
|
|
194
|
+
* A client MUST NOT attempt to cancel its `initialize` request.
|
|
195
|
+
*/
|
|
196
|
+
const CancelledNotificationSchema = NotificationSchema.extend({
|
|
197
|
+
method: external_zod_namespaceObject.z.literal("notifications/cancelled"),
|
|
198
|
+
params: BaseNotificationParamsSchema.extend({
|
|
199
|
+
/**
|
|
200
|
+
* The ID of the request to cancel.
|
|
201
|
+
*
|
|
202
|
+
* This MUST correspond to the ID of a request previously issued in the same direction.
|
|
203
|
+
*/
|
|
204
|
+
requestId: RequestIdSchema,
|
|
205
|
+
/**
|
|
206
|
+
* An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.
|
|
207
|
+
*/
|
|
208
|
+
reason: external_zod_namespaceObject.z.string().optional(),
|
|
209
|
+
}),
|
|
210
|
+
});
|
|
211
|
+
/* Base Metadata */
|
|
212
|
+
/**
|
|
213
|
+
* Base metadata interface for common properties across resources, tools, prompts, and implementations.
|
|
214
|
+
*/
|
|
215
|
+
const BaseMetadataSchema = external_zod_namespaceObject.z
|
|
216
|
+
.object({
|
|
217
|
+
/** Intended for programmatic or logical use, but used as a display name in past specs or fallback */
|
|
218
|
+
name: external_zod_namespaceObject.z.string(),
|
|
219
|
+
/**
|
|
220
|
+
* Intended for UI and end-user contexts — optimized to be human-readable and easily understood,
|
|
221
|
+
* even by those unfamiliar with domain-specific terminology.
|
|
222
|
+
*
|
|
223
|
+
* If not provided, the name should be used for display (except for Tool,
|
|
224
|
+
* where `annotations.title` should be given precedence over using `name`,
|
|
225
|
+
* if present).
|
|
226
|
+
*/
|
|
227
|
+
title: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
228
|
+
})
|
|
229
|
+
.passthrough();
|
|
230
|
+
/* Initialization */
|
|
231
|
+
/**
|
|
232
|
+
* Describes the name and version of an MCP implementation.
|
|
233
|
+
*/
|
|
234
|
+
const ImplementationSchema = BaseMetadataSchema.extend({
|
|
235
|
+
version: external_zod_namespaceObject.z.string(),
|
|
236
|
+
});
|
|
237
|
+
/**
|
|
238
|
+
* Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities.
|
|
239
|
+
*/
|
|
240
|
+
const ClientCapabilitiesSchema = external_zod_namespaceObject.z
|
|
241
|
+
.object({
|
|
242
|
+
/**
|
|
243
|
+
* Experimental, non-standard capabilities that the client supports.
|
|
244
|
+
*/
|
|
245
|
+
experimental: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
246
|
+
/**
|
|
247
|
+
* Present if the client supports sampling from an LLM.
|
|
248
|
+
*/
|
|
249
|
+
sampling: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
250
|
+
/**
|
|
251
|
+
* Present if the client supports eliciting user input.
|
|
252
|
+
*/
|
|
253
|
+
elicitation: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
254
|
+
/**
|
|
255
|
+
* Present if the client supports listing roots.
|
|
256
|
+
*/
|
|
257
|
+
roots: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z
|
|
258
|
+
.object({
|
|
259
|
+
/**
|
|
260
|
+
* Whether the client supports issuing notifications for changes to the roots list.
|
|
261
|
+
*/
|
|
262
|
+
listChanged: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
263
|
+
})
|
|
264
|
+
.passthrough()),
|
|
265
|
+
})
|
|
266
|
+
.passthrough();
|
|
267
|
+
/**
|
|
268
|
+
* This request is sent from the client to the server when it first connects, asking it to begin initialization.
|
|
269
|
+
*/
|
|
270
|
+
const InitializeRequestSchema = RequestSchema.extend({
|
|
271
|
+
method: external_zod_namespaceObject.z.literal("initialize"),
|
|
272
|
+
params: BaseRequestParamsSchema.extend({
|
|
273
|
+
/**
|
|
274
|
+
* The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well.
|
|
275
|
+
*/
|
|
276
|
+
protocolVersion: external_zod_namespaceObject.z.string(),
|
|
277
|
+
capabilities: ClientCapabilitiesSchema,
|
|
278
|
+
clientInfo: ImplementationSchema,
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
const isInitializeRequest = (value) => InitializeRequestSchema.safeParse(value).success;
|
|
282
|
+
/**
|
|
283
|
+
* Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities.
|
|
284
|
+
*/
|
|
285
|
+
const ServerCapabilitiesSchema = external_zod_namespaceObject.z
|
|
286
|
+
.object({
|
|
287
|
+
/**
|
|
288
|
+
* Experimental, non-standard capabilities that the server supports.
|
|
289
|
+
*/
|
|
290
|
+
experimental: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
291
|
+
/**
|
|
292
|
+
* Present if the server supports sending log messages to the client.
|
|
293
|
+
*/
|
|
294
|
+
logging: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
295
|
+
/**
|
|
296
|
+
* Present if the server supports sending completions to the client.
|
|
297
|
+
*/
|
|
298
|
+
completions: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
299
|
+
/**
|
|
300
|
+
* Present if the server offers any prompt templates.
|
|
301
|
+
*/
|
|
302
|
+
prompts: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z
|
|
303
|
+
.object({
|
|
304
|
+
/**
|
|
305
|
+
* Whether this server supports issuing notifications for changes to the prompt list.
|
|
306
|
+
*/
|
|
307
|
+
listChanged: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
308
|
+
})
|
|
309
|
+
.passthrough()),
|
|
310
|
+
/**
|
|
311
|
+
* Present if the server offers any resources to read.
|
|
312
|
+
*/
|
|
313
|
+
resources: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z
|
|
314
|
+
.object({
|
|
315
|
+
/**
|
|
316
|
+
* Whether this server supports clients subscribing to resource updates.
|
|
317
|
+
*/
|
|
318
|
+
subscribe: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
319
|
+
/**
|
|
320
|
+
* Whether this server supports issuing notifications for changes to the resource list.
|
|
321
|
+
*/
|
|
322
|
+
listChanged: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
323
|
+
})
|
|
324
|
+
.passthrough()),
|
|
325
|
+
/**
|
|
326
|
+
* Present if the server offers any tools to call.
|
|
327
|
+
*/
|
|
328
|
+
tools: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z
|
|
329
|
+
.object({
|
|
330
|
+
/**
|
|
331
|
+
* Whether this server supports issuing notifications for changes to the tool list.
|
|
332
|
+
*/
|
|
333
|
+
listChanged: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
334
|
+
})
|
|
335
|
+
.passthrough()),
|
|
336
|
+
})
|
|
337
|
+
.passthrough();
|
|
338
|
+
/**
|
|
339
|
+
* After receiving an initialize request from the client, the server sends this response.
|
|
340
|
+
*/
|
|
341
|
+
const InitializeResultSchema = ResultSchema.extend({
|
|
342
|
+
/**
|
|
343
|
+
* The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect.
|
|
344
|
+
*/
|
|
345
|
+
protocolVersion: external_zod_namespaceObject.z.string(),
|
|
346
|
+
capabilities: ServerCapabilitiesSchema,
|
|
347
|
+
serverInfo: ImplementationSchema,
|
|
348
|
+
/**
|
|
349
|
+
* Instructions describing how to use the server and its features.
|
|
350
|
+
*
|
|
351
|
+
* This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt.
|
|
352
|
+
*/
|
|
353
|
+
instructions: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
354
|
+
});
|
|
355
|
+
/**
|
|
356
|
+
* This notification is sent from the client to the server after initialization has finished.
|
|
357
|
+
*/
|
|
358
|
+
const InitializedNotificationSchema = NotificationSchema.extend({
|
|
359
|
+
method: external_zod_namespaceObject.z.literal("notifications/initialized"),
|
|
360
|
+
});
|
|
361
|
+
const isInitializedNotification = (value) => InitializedNotificationSchema.safeParse(value).success;
|
|
362
|
+
/* Ping */
|
|
363
|
+
/**
|
|
364
|
+
* A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected.
|
|
365
|
+
*/
|
|
366
|
+
const PingRequestSchema = RequestSchema.extend({
|
|
367
|
+
method: external_zod_namespaceObject.z.literal("ping"),
|
|
368
|
+
});
|
|
369
|
+
/* Progress notifications */
|
|
370
|
+
const ProgressSchema = external_zod_namespaceObject.z
|
|
371
|
+
.object({
|
|
372
|
+
/**
|
|
373
|
+
* The progress thus far. This should increase every time progress is made, even if the total is unknown.
|
|
374
|
+
*/
|
|
375
|
+
progress: external_zod_namespaceObject.z.number(),
|
|
376
|
+
/**
|
|
377
|
+
* Total number of items to process (or total progress required), if known.
|
|
378
|
+
*/
|
|
379
|
+
total: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number()),
|
|
380
|
+
/**
|
|
381
|
+
* An optional message describing the current progress.
|
|
382
|
+
*/
|
|
383
|
+
message: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
384
|
+
})
|
|
385
|
+
.passthrough();
|
|
386
|
+
/**
|
|
387
|
+
* An out-of-band notification used to inform the receiver of a progress update for a long-running request.
|
|
388
|
+
*/
|
|
389
|
+
const ProgressNotificationSchema = NotificationSchema.extend({
|
|
390
|
+
method: external_zod_namespaceObject.z.literal("notifications/progress"),
|
|
391
|
+
params: BaseNotificationParamsSchema.merge(ProgressSchema).extend({
|
|
392
|
+
/**
|
|
393
|
+
* The progress token which was given in the initial request, used to associate this notification with the request that is proceeding.
|
|
394
|
+
*/
|
|
395
|
+
progressToken: ProgressTokenSchema,
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
398
|
+
/* Pagination */
|
|
399
|
+
const PaginatedRequestSchema = RequestSchema.extend({
|
|
400
|
+
params: BaseRequestParamsSchema.extend({
|
|
401
|
+
/**
|
|
402
|
+
* An opaque token representing the current pagination position.
|
|
403
|
+
* If provided, the server should return results starting after this cursor.
|
|
404
|
+
*/
|
|
405
|
+
cursor: external_zod_namespaceObject.z.optional(CursorSchema),
|
|
406
|
+
}).optional(),
|
|
407
|
+
});
|
|
408
|
+
const PaginatedResultSchema = ResultSchema.extend({
|
|
409
|
+
/**
|
|
410
|
+
* An opaque token representing the pagination position after the last returned result.
|
|
411
|
+
* If present, there may be more results available.
|
|
412
|
+
*/
|
|
413
|
+
nextCursor: external_zod_namespaceObject.z.optional(CursorSchema),
|
|
414
|
+
});
|
|
415
|
+
/* Resources */
|
|
416
|
+
/**
|
|
417
|
+
* The contents of a specific resource or sub-resource.
|
|
418
|
+
*/
|
|
419
|
+
const ResourceContentsSchema = external_zod_namespaceObject.z
|
|
420
|
+
.object({
|
|
421
|
+
/**
|
|
422
|
+
* The URI of this resource.
|
|
423
|
+
*/
|
|
424
|
+
uri: external_zod_namespaceObject.z.string(),
|
|
425
|
+
/**
|
|
426
|
+
* The MIME type of this resource, if known.
|
|
427
|
+
*/
|
|
428
|
+
mimeType: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
429
|
+
/**
|
|
430
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
431
|
+
* for notes on _meta usage.
|
|
432
|
+
*/
|
|
433
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
434
|
+
})
|
|
435
|
+
.passthrough();
|
|
436
|
+
const TextResourceContentsSchema = ResourceContentsSchema.extend({
|
|
437
|
+
/**
|
|
438
|
+
* The text of the item. This must only be set if the item can actually be represented as text (not binary data).
|
|
439
|
+
*/
|
|
440
|
+
text: external_zod_namespaceObject.z.string(),
|
|
441
|
+
});
|
|
442
|
+
const BlobResourceContentsSchema = ResourceContentsSchema.extend({
|
|
443
|
+
/**
|
|
444
|
+
* A base64-encoded string representing the binary data of the item.
|
|
445
|
+
*/
|
|
446
|
+
blob: external_zod_namespaceObject.z.string().base64(),
|
|
447
|
+
});
|
|
448
|
+
/**
|
|
449
|
+
* A known resource that the server is capable of reading.
|
|
450
|
+
*/
|
|
451
|
+
const ResourceSchema = BaseMetadataSchema.extend({
|
|
452
|
+
/**
|
|
453
|
+
* The URI of this resource.
|
|
454
|
+
*/
|
|
455
|
+
uri: external_zod_namespaceObject.z.string(),
|
|
456
|
+
/**
|
|
457
|
+
* A description of what this resource represents.
|
|
458
|
+
*
|
|
459
|
+
* This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.
|
|
460
|
+
*/
|
|
461
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
462
|
+
/**
|
|
463
|
+
* The MIME type of this resource, if known.
|
|
464
|
+
*/
|
|
465
|
+
mimeType: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
466
|
+
/**
|
|
467
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
468
|
+
* for notes on _meta usage.
|
|
469
|
+
*/
|
|
470
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
471
|
+
});
|
|
472
|
+
/**
|
|
473
|
+
* A template description for resources available on the server.
|
|
474
|
+
*/
|
|
475
|
+
const ResourceTemplateSchema = BaseMetadataSchema.extend({
|
|
476
|
+
/**
|
|
477
|
+
* A URI template (according to RFC 6570) that can be used to construct resource URIs.
|
|
478
|
+
*/
|
|
479
|
+
uriTemplate: external_zod_namespaceObject.z.string(),
|
|
480
|
+
/**
|
|
481
|
+
* A description of what this template is for.
|
|
482
|
+
*
|
|
483
|
+
* This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model.
|
|
484
|
+
*/
|
|
485
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
486
|
+
/**
|
|
487
|
+
* The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type.
|
|
488
|
+
*/
|
|
489
|
+
mimeType: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
490
|
+
/**
|
|
491
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
492
|
+
* for notes on _meta usage.
|
|
493
|
+
*/
|
|
494
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
495
|
+
});
|
|
496
|
+
/**
|
|
497
|
+
* Sent from the client to request a list of resources the server has.
|
|
498
|
+
*/
|
|
499
|
+
const ListResourcesRequestSchema = PaginatedRequestSchema.extend({
|
|
500
|
+
method: external_zod_namespaceObject.z.literal("resources/list"),
|
|
501
|
+
});
|
|
502
|
+
/**
|
|
503
|
+
* The server's response to a resources/list request from the client.
|
|
504
|
+
*/
|
|
505
|
+
const ListResourcesResultSchema = PaginatedResultSchema.extend({
|
|
506
|
+
resources: external_zod_namespaceObject.z.array(ResourceSchema),
|
|
507
|
+
});
|
|
508
|
+
/**
|
|
509
|
+
* Sent from the client to request a list of resource templates the server has.
|
|
510
|
+
*/
|
|
511
|
+
const ListResourceTemplatesRequestSchema = PaginatedRequestSchema.extend({
|
|
512
|
+
method: external_zod_namespaceObject.z.literal("resources/templates/list"),
|
|
513
|
+
});
|
|
514
|
+
/**
|
|
515
|
+
* The server's response to a resources/templates/list request from the client.
|
|
516
|
+
*/
|
|
517
|
+
const ListResourceTemplatesResultSchema = PaginatedResultSchema.extend({
|
|
518
|
+
resourceTemplates: external_zod_namespaceObject.z.array(ResourceTemplateSchema),
|
|
519
|
+
});
|
|
520
|
+
/**
|
|
521
|
+
* Sent from the client to the server, to read a specific resource URI.
|
|
522
|
+
*/
|
|
523
|
+
const ReadResourceRequestSchema = RequestSchema.extend({
|
|
524
|
+
method: external_zod_namespaceObject.z.literal("resources/read"),
|
|
525
|
+
params: BaseRequestParamsSchema.extend({
|
|
526
|
+
/**
|
|
527
|
+
* The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it.
|
|
528
|
+
*/
|
|
529
|
+
uri: external_zod_namespaceObject.z.string(),
|
|
530
|
+
}),
|
|
531
|
+
});
|
|
532
|
+
/**
|
|
533
|
+
* The server's response to a resources/read request from the client.
|
|
534
|
+
*/
|
|
535
|
+
const ReadResourceResultSchema = ResultSchema.extend({
|
|
536
|
+
contents: external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.union([TextResourceContentsSchema, BlobResourceContentsSchema])),
|
|
537
|
+
});
|
|
538
|
+
/**
|
|
539
|
+
* An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client.
|
|
540
|
+
*/
|
|
541
|
+
const ResourceListChangedNotificationSchema = NotificationSchema.extend({
|
|
542
|
+
method: external_zod_namespaceObject.z.literal("notifications/resources/list_changed"),
|
|
543
|
+
});
|
|
544
|
+
/**
|
|
545
|
+
* Sent from the client to request resources/updated notifications from the server whenever a particular resource changes.
|
|
546
|
+
*/
|
|
547
|
+
const SubscribeRequestSchema = RequestSchema.extend({
|
|
548
|
+
method: external_zod_namespaceObject.z.literal("resources/subscribe"),
|
|
549
|
+
params: BaseRequestParamsSchema.extend({
|
|
550
|
+
/**
|
|
551
|
+
* The URI of the resource to subscribe to. The URI can use any protocol; it is up to the server how to interpret it.
|
|
552
|
+
*/
|
|
553
|
+
uri: external_zod_namespaceObject.z.string(),
|
|
554
|
+
}),
|
|
555
|
+
});
|
|
556
|
+
/**
|
|
557
|
+
* Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request.
|
|
558
|
+
*/
|
|
559
|
+
const UnsubscribeRequestSchema = RequestSchema.extend({
|
|
560
|
+
method: external_zod_namespaceObject.z.literal("resources/unsubscribe"),
|
|
561
|
+
params: BaseRequestParamsSchema.extend({
|
|
562
|
+
/**
|
|
563
|
+
* The URI of the resource to unsubscribe from.
|
|
564
|
+
*/
|
|
565
|
+
uri: external_zod_namespaceObject.z.string(),
|
|
566
|
+
}),
|
|
567
|
+
});
|
|
568
|
+
/**
|
|
569
|
+
* A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request.
|
|
570
|
+
*/
|
|
571
|
+
const ResourceUpdatedNotificationSchema = NotificationSchema.extend({
|
|
572
|
+
method: external_zod_namespaceObject.z.literal("notifications/resources/updated"),
|
|
573
|
+
params: BaseNotificationParamsSchema.extend({
|
|
574
|
+
/**
|
|
575
|
+
* The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.
|
|
576
|
+
*/
|
|
577
|
+
uri: external_zod_namespaceObject.z.string(),
|
|
578
|
+
}),
|
|
579
|
+
});
|
|
580
|
+
/* Prompts */
|
|
581
|
+
/**
|
|
582
|
+
* Describes an argument that a prompt can accept.
|
|
583
|
+
*/
|
|
584
|
+
const PromptArgumentSchema = external_zod_namespaceObject.z
|
|
585
|
+
.object({
|
|
586
|
+
/**
|
|
587
|
+
* The name of the argument.
|
|
588
|
+
*/
|
|
589
|
+
name: external_zod_namespaceObject.z.string(),
|
|
590
|
+
/**
|
|
591
|
+
* A human-readable description of the argument.
|
|
592
|
+
*/
|
|
593
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
594
|
+
/**
|
|
595
|
+
* Whether this argument must be provided.
|
|
596
|
+
*/
|
|
597
|
+
required: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
598
|
+
})
|
|
599
|
+
.passthrough();
|
|
600
|
+
/**
|
|
601
|
+
* A prompt or prompt template that the server offers.
|
|
602
|
+
*/
|
|
603
|
+
const PromptSchema = BaseMetadataSchema.extend({
|
|
604
|
+
/**
|
|
605
|
+
* An optional description of what this prompt provides
|
|
606
|
+
*/
|
|
607
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
608
|
+
/**
|
|
609
|
+
* A list of arguments to use for templating the prompt.
|
|
610
|
+
*/
|
|
611
|
+
arguments: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.array(PromptArgumentSchema)),
|
|
612
|
+
/**
|
|
613
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
614
|
+
* for notes on _meta usage.
|
|
615
|
+
*/
|
|
616
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
617
|
+
});
|
|
618
|
+
/**
|
|
619
|
+
* Sent from the client to request a list of prompts and prompt templates the server has.
|
|
620
|
+
*/
|
|
621
|
+
const ListPromptsRequestSchema = PaginatedRequestSchema.extend({
|
|
622
|
+
method: external_zod_namespaceObject.z.literal("prompts/list"),
|
|
623
|
+
});
|
|
624
|
+
/**
|
|
625
|
+
* The server's response to a prompts/list request from the client.
|
|
626
|
+
*/
|
|
627
|
+
const ListPromptsResultSchema = PaginatedResultSchema.extend({
|
|
628
|
+
prompts: external_zod_namespaceObject.z.array(PromptSchema),
|
|
629
|
+
});
|
|
630
|
+
/**
|
|
631
|
+
* Used by the client to get a prompt provided by the server.
|
|
632
|
+
*/
|
|
633
|
+
const GetPromptRequestSchema = RequestSchema.extend({
|
|
634
|
+
method: external_zod_namespaceObject.z.literal("prompts/get"),
|
|
635
|
+
params: BaseRequestParamsSchema.extend({
|
|
636
|
+
/**
|
|
637
|
+
* The name of the prompt or prompt template.
|
|
638
|
+
*/
|
|
639
|
+
name: external_zod_namespaceObject.z.string(),
|
|
640
|
+
/**
|
|
641
|
+
* Arguments to use for templating the prompt.
|
|
642
|
+
*/
|
|
643
|
+
arguments: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.record(external_zod_namespaceObject.z.string())),
|
|
644
|
+
}),
|
|
645
|
+
});
|
|
646
|
+
/**
|
|
647
|
+
* Text provided to or from an LLM.
|
|
648
|
+
*/
|
|
649
|
+
const TextContentSchema = external_zod_namespaceObject.z
|
|
650
|
+
.object({
|
|
651
|
+
type: external_zod_namespaceObject.z.literal("text"),
|
|
652
|
+
/**
|
|
653
|
+
* The text content of the message.
|
|
654
|
+
*/
|
|
655
|
+
text: external_zod_namespaceObject.z.string(),
|
|
656
|
+
/**
|
|
657
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
658
|
+
* for notes on _meta usage.
|
|
659
|
+
*/
|
|
660
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
661
|
+
})
|
|
662
|
+
.passthrough();
|
|
663
|
+
/**
|
|
664
|
+
* An image provided to or from an LLM.
|
|
665
|
+
*/
|
|
666
|
+
const ImageContentSchema = external_zod_namespaceObject.z
|
|
667
|
+
.object({
|
|
668
|
+
type: external_zod_namespaceObject.z.literal("image"),
|
|
669
|
+
/**
|
|
670
|
+
* The base64-encoded image data.
|
|
671
|
+
*/
|
|
672
|
+
data: external_zod_namespaceObject.z.string().base64(),
|
|
673
|
+
/**
|
|
674
|
+
* The MIME type of the image. Different providers may support different image types.
|
|
675
|
+
*/
|
|
676
|
+
mimeType: external_zod_namespaceObject.z.string(),
|
|
677
|
+
/**
|
|
678
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
679
|
+
* for notes on _meta usage.
|
|
680
|
+
*/
|
|
681
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
682
|
+
})
|
|
683
|
+
.passthrough();
|
|
684
|
+
/**
|
|
685
|
+
* An Audio provided to or from an LLM.
|
|
686
|
+
*/
|
|
687
|
+
const AudioContentSchema = external_zod_namespaceObject.z
|
|
688
|
+
.object({
|
|
689
|
+
type: external_zod_namespaceObject.z.literal("audio"),
|
|
690
|
+
/**
|
|
691
|
+
* The base64-encoded audio data.
|
|
692
|
+
*/
|
|
693
|
+
data: external_zod_namespaceObject.z.string().base64(),
|
|
694
|
+
/**
|
|
695
|
+
* The MIME type of the audio. Different providers may support different audio types.
|
|
696
|
+
*/
|
|
697
|
+
mimeType: external_zod_namespaceObject.z.string(),
|
|
698
|
+
/**
|
|
699
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
700
|
+
* for notes on _meta usage.
|
|
701
|
+
*/
|
|
702
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
703
|
+
})
|
|
704
|
+
.passthrough();
|
|
705
|
+
/**
|
|
706
|
+
* The contents of a resource, embedded into a prompt or tool call result.
|
|
707
|
+
*/
|
|
708
|
+
const EmbeddedResourceSchema = external_zod_namespaceObject.z
|
|
709
|
+
.object({
|
|
710
|
+
type: external_zod_namespaceObject.z.literal("resource"),
|
|
711
|
+
resource: external_zod_namespaceObject.z.union([TextResourceContentsSchema, BlobResourceContentsSchema]),
|
|
712
|
+
/**
|
|
713
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
714
|
+
* for notes on _meta usage.
|
|
715
|
+
*/
|
|
716
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
717
|
+
})
|
|
718
|
+
.passthrough();
|
|
719
|
+
/**
|
|
720
|
+
* A resource that the server is capable of reading, included in a prompt or tool call result.
|
|
721
|
+
*
|
|
722
|
+
* Note: resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests.
|
|
723
|
+
*/
|
|
724
|
+
const ResourceLinkSchema = ResourceSchema.extend({
|
|
725
|
+
type: external_zod_namespaceObject.z.literal("resource_link"),
|
|
726
|
+
});
|
|
727
|
+
/**
|
|
728
|
+
* A content block that can be used in prompts and tool results.
|
|
729
|
+
*/
|
|
730
|
+
const ContentBlockSchema = external_zod_namespaceObject.z.union([
|
|
731
|
+
TextContentSchema,
|
|
732
|
+
ImageContentSchema,
|
|
733
|
+
AudioContentSchema,
|
|
734
|
+
ResourceLinkSchema,
|
|
735
|
+
EmbeddedResourceSchema,
|
|
736
|
+
]);
|
|
737
|
+
/**
|
|
738
|
+
* Describes a message returned as part of a prompt.
|
|
739
|
+
*/
|
|
740
|
+
const PromptMessageSchema = external_zod_namespaceObject.z
|
|
741
|
+
.object({
|
|
742
|
+
role: external_zod_namespaceObject.z.enum(["user", "assistant"]),
|
|
743
|
+
content: ContentBlockSchema,
|
|
744
|
+
})
|
|
745
|
+
.passthrough();
|
|
746
|
+
/**
|
|
747
|
+
* The server's response to a prompts/get request from the client.
|
|
748
|
+
*/
|
|
749
|
+
const GetPromptResultSchema = ResultSchema.extend({
|
|
750
|
+
/**
|
|
751
|
+
* An optional description for the prompt.
|
|
752
|
+
*/
|
|
753
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
754
|
+
messages: external_zod_namespaceObject.z.array(PromptMessageSchema),
|
|
755
|
+
});
|
|
756
|
+
/**
|
|
757
|
+
* An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client.
|
|
758
|
+
*/
|
|
759
|
+
const PromptListChangedNotificationSchema = NotificationSchema.extend({
|
|
760
|
+
method: external_zod_namespaceObject.z.literal("notifications/prompts/list_changed"),
|
|
761
|
+
});
|
|
762
|
+
/* Tools */
|
|
763
|
+
/**
|
|
764
|
+
* Additional properties describing a Tool to clients.
|
|
765
|
+
*
|
|
766
|
+
* NOTE: all properties in ToolAnnotations are **hints**.
|
|
767
|
+
* They are not guaranteed to provide a faithful description of
|
|
768
|
+
* tool behavior (including descriptive properties like `title`).
|
|
769
|
+
*
|
|
770
|
+
* Clients should never make tool use decisions based on ToolAnnotations
|
|
771
|
+
* received from untrusted servers.
|
|
772
|
+
*/
|
|
773
|
+
const ToolAnnotationsSchema = external_zod_namespaceObject.z
|
|
774
|
+
.object({
|
|
775
|
+
/**
|
|
776
|
+
* A human-readable title for the tool.
|
|
777
|
+
*/
|
|
778
|
+
title: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
779
|
+
/**
|
|
780
|
+
* If true, the tool does not modify its environment.
|
|
781
|
+
*
|
|
782
|
+
* Default: false
|
|
783
|
+
*/
|
|
784
|
+
readOnlyHint: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
785
|
+
/**
|
|
786
|
+
* If true, the tool may perform destructive updates to its environment.
|
|
787
|
+
* If false, the tool performs only additive updates.
|
|
788
|
+
*
|
|
789
|
+
* (This property is meaningful only when `readOnlyHint == false`)
|
|
790
|
+
*
|
|
791
|
+
* Default: true
|
|
792
|
+
*/
|
|
793
|
+
destructiveHint: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
794
|
+
/**
|
|
795
|
+
* If true, calling the tool repeatedly with the same arguments
|
|
796
|
+
* will have no additional effect on the its environment.
|
|
797
|
+
*
|
|
798
|
+
* (This property is meaningful only when `readOnlyHint == false`)
|
|
799
|
+
*
|
|
800
|
+
* Default: false
|
|
801
|
+
*/
|
|
802
|
+
idempotentHint: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
803
|
+
/**
|
|
804
|
+
* If true, this tool may interact with an "open world" of external
|
|
805
|
+
* entities. If false, the tool's domain of interaction is closed.
|
|
806
|
+
* For example, the world of a web search tool is open, whereas that
|
|
807
|
+
* of a memory tool is not.
|
|
808
|
+
*
|
|
809
|
+
* Default: true
|
|
810
|
+
*/
|
|
811
|
+
openWorldHint: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
812
|
+
})
|
|
813
|
+
.passthrough();
|
|
814
|
+
/**
|
|
815
|
+
* Definition for a tool the client can call.
|
|
816
|
+
*/
|
|
817
|
+
const ToolSchema = BaseMetadataSchema.extend({
|
|
818
|
+
/**
|
|
819
|
+
* A human-readable description of the tool.
|
|
820
|
+
*/
|
|
821
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
822
|
+
/**
|
|
823
|
+
* A JSON Schema object defining the expected parameters for the tool.
|
|
824
|
+
*/
|
|
825
|
+
inputSchema: external_zod_namespaceObject.z
|
|
826
|
+
.object({
|
|
827
|
+
type: external_zod_namespaceObject.z.literal("object"),
|
|
828
|
+
properties: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
829
|
+
required: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string())),
|
|
830
|
+
})
|
|
831
|
+
.passthrough(),
|
|
832
|
+
/**
|
|
833
|
+
* An optional JSON Schema object defining the structure of the tool's output returned in
|
|
834
|
+
* the structuredContent field of a CallToolResult.
|
|
835
|
+
*/
|
|
836
|
+
outputSchema: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({
|
|
837
|
+
type: external_zod_namespaceObject.z.literal("object"),
|
|
838
|
+
properties: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
839
|
+
required: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string())),
|
|
840
|
+
})
|
|
841
|
+
.passthrough()),
|
|
842
|
+
/**
|
|
843
|
+
* Optional additional tool information.
|
|
844
|
+
*/
|
|
845
|
+
annotations: external_zod_namespaceObject.z.optional(ToolAnnotationsSchema),
|
|
846
|
+
/**
|
|
847
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
848
|
+
* for notes on _meta usage.
|
|
849
|
+
*/
|
|
850
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
851
|
+
});
|
|
852
|
+
/**
|
|
853
|
+
* Sent from the client to request a list of tools the server has.
|
|
854
|
+
*/
|
|
855
|
+
const ListToolsRequestSchema = PaginatedRequestSchema.extend({
|
|
856
|
+
method: external_zod_namespaceObject.z.literal("tools/list"),
|
|
857
|
+
});
|
|
858
|
+
/**
|
|
859
|
+
* The server's response to a tools/list request from the client.
|
|
860
|
+
*/
|
|
861
|
+
const ListToolsResultSchema = PaginatedResultSchema.extend({
|
|
862
|
+
tools: external_zod_namespaceObject.z.array(ToolSchema),
|
|
863
|
+
});
|
|
864
|
+
/**
|
|
865
|
+
* The server's response to a tool call.
|
|
866
|
+
*/
|
|
867
|
+
const CallToolResultSchema = ResultSchema.extend({
|
|
868
|
+
/**
|
|
869
|
+
* A list of content objects that represent the result of the tool call.
|
|
870
|
+
*
|
|
871
|
+
* If the Tool does not define an outputSchema, this field MUST be present in the result.
|
|
872
|
+
* For backwards compatibility, this field is always present, but it may be empty.
|
|
873
|
+
*/
|
|
874
|
+
content: external_zod_namespaceObject.z.array(ContentBlockSchema).default([]),
|
|
875
|
+
/**
|
|
876
|
+
* An object containing structured tool output.
|
|
877
|
+
*
|
|
878
|
+
* If the Tool defines an outputSchema, this field MUST be present in the result, and contain a JSON object that matches the schema.
|
|
879
|
+
*/
|
|
880
|
+
structuredContent: external_zod_namespaceObject.z.object({}).passthrough().optional(),
|
|
881
|
+
/**
|
|
882
|
+
* Whether the tool call ended in an error.
|
|
883
|
+
*
|
|
884
|
+
* If not set, this is assumed to be false (the call was successful).
|
|
885
|
+
*
|
|
886
|
+
* Any errors that originate from the tool SHOULD be reported inside the result
|
|
887
|
+
* object, with `isError` set to true, _not_ as an MCP protocol-level error
|
|
888
|
+
* response. Otherwise, the LLM would not be able to see that an error occurred
|
|
889
|
+
* and self-correct.
|
|
890
|
+
*
|
|
891
|
+
* However, any errors in _finding_ the tool, an error indicating that the
|
|
892
|
+
* server does not support tool calls, or any other exceptional conditions,
|
|
893
|
+
* should be reported as an MCP error response.
|
|
894
|
+
*/
|
|
895
|
+
isError: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
896
|
+
});
|
|
897
|
+
/**
|
|
898
|
+
* CallToolResultSchema extended with backwards compatibility to protocol version 2024-10-07.
|
|
899
|
+
*/
|
|
900
|
+
const CompatibilityCallToolResultSchema = CallToolResultSchema.or(ResultSchema.extend({
|
|
901
|
+
toolResult: external_zod_namespaceObject.z.unknown(),
|
|
902
|
+
}));
|
|
903
|
+
/**
|
|
904
|
+
* Used by the client to invoke a tool provided by the server.
|
|
905
|
+
*/
|
|
906
|
+
const CallToolRequestSchema = RequestSchema.extend({
|
|
907
|
+
method: external_zod_namespaceObject.z.literal("tools/call"),
|
|
908
|
+
params: BaseRequestParamsSchema.extend({
|
|
909
|
+
name: external_zod_namespaceObject.z.string(),
|
|
910
|
+
arguments: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.record(external_zod_namespaceObject.z.unknown())),
|
|
911
|
+
}),
|
|
912
|
+
});
|
|
913
|
+
/**
|
|
914
|
+
* An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client.
|
|
915
|
+
*/
|
|
916
|
+
const ToolListChangedNotificationSchema = NotificationSchema.extend({
|
|
917
|
+
method: external_zod_namespaceObject.z.literal("notifications/tools/list_changed"),
|
|
918
|
+
});
|
|
919
|
+
/* Logging */
|
|
920
|
+
/**
|
|
921
|
+
* The severity of a log message.
|
|
922
|
+
*/
|
|
923
|
+
const LoggingLevelSchema = external_zod_namespaceObject.z.enum([
|
|
924
|
+
"debug",
|
|
925
|
+
"info",
|
|
926
|
+
"notice",
|
|
927
|
+
"warning",
|
|
928
|
+
"error",
|
|
929
|
+
"critical",
|
|
930
|
+
"alert",
|
|
931
|
+
"emergency",
|
|
932
|
+
]);
|
|
933
|
+
/**
|
|
934
|
+
* A request from the client to the server, to enable or adjust logging.
|
|
935
|
+
*/
|
|
936
|
+
const SetLevelRequestSchema = RequestSchema.extend({
|
|
937
|
+
method: external_zod_namespaceObject.z.literal("logging/setLevel"),
|
|
938
|
+
params: BaseRequestParamsSchema.extend({
|
|
939
|
+
/**
|
|
940
|
+
* The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/logging/message.
|
|
941
|
+
*/
|
|
942
|
+
level: LoggingLevelSchema,
|
|
943
|
+
}),
|
|
944
|
+
});
|
|
945
|
+
/**
|
|
946
|
+
* Notification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically.
|
|
947
|
+
*/
|
|
948
|
+
const LoggingMessageNotificationSchema = NotificationSchema.extend({
|
|
949
|
+
method: external_zod_namespaceObject.z.literal("notifications/message"),
|
|
950
|
+
params: BaseNotificationParamsSchema.extend({
|
|
951
|
+
/**
|
|
952
|
+
* The severity of this log message.
|
|
953
|
+
*/
|
|
954
|
+
level: LoggingLevelSchema,
|
|
955
|
+
/**
|
|
956
|
+
* An optional name of the logger issuing this message.
|
|
957
|
+
*/
|
|
958
|
+
logger: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
959
|
+
/**
|
|
960
|
+
* The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.
|
|
961
|
+
*/
|
|
962
|
+
data: external_zod_namespaceObject.z.unknown(),
|
|
963
|
+
}),
|
|
964
|
+
});
|
|
965
|
+
/* Sampling */
|
|
966
|
+
/**
|
|
967
|
+
* Hints to use for model selection.
|
|
968
|
+
*/
|
|
969
|
+
const ModelHintSchema = external_zod_namespaceObject.z
|
|
970
|
+
.object({
|
|
971
|
+
/**
|
|
972
|
+
* A hint for a model name.
|
|
973
|
+
*/
|
|
974
|
+
name: external_zod_namespaceObject.z.string().optional(),
|
|
975
|
+
})
|
|
976
|
+
.passthrough();
|
|
977
|
+
/**
|
|
978
|
+
* The server's preferences for model selection, requested of the client during sampling.
|
|
979
|
+
*/
|
|
980
|
+
const ModelPreferencesSchema = external_zod_namespaceObject.z
|
|
981
|
+
.object({
|
|
982
|
+
/**
|
|
983
|
+
* Optional hints to use for model selection.
|
|
984
|
+
*/
|
|
985
|
+
hints: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.array(ModelHintSchema)),
|
|
986
|
+
/**
|
|
987
|
+
* How much to prioritize cost when selecting a model.
|
|
988
|
+
*/
|
|
989
|
+
costPriority: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number().min(0).max(1)),
|
|
990
|
+
/**
|
|
991
|
+
* How much to prioritize sampling speed (latency) when selecting a model.
|
|
992
|
+
*/
|
|
993
|
+
speedPriority: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number().min(0).max(1)),
|
|
994
|
+
/**
|
|
995
|
+
* How much to prioritize intelligence and capabilities when selecting a model.
|
|
996
|
+
*/
|
|
997
|
+
intelligencePriority: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number().min(0).max(1)),
|
|
998
|
+
})
|
|
999
|
+
.passthrough();
|
|
1000
|
+
/**
|
|
1001
|
+
* Describes a message issued to or received from an LLM API.
|
|
1002
|
+
*/
|
|
1003
|
+
const SamplingMessageSchema = external_zod_namespaceObject.z
|
|
1004
|
+
.object({
|
|
1005
|
+
role: external_zod_namespaceObject.z.enum(["user", "assistant"]),
|
|
1006
|
+
content: external_zod_namespaceObject.z.union([TextContentSchema, ImageContentSchema, AudioContentSchema]),
|
|
1007
|
+
})
|
|
1008
|
+
.passthrough();
|
|
1009
|
+
/**
|
|
1010
|
+
* A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it.
|
|
1011
|
+
*/
|
|
1012
|
+
const CreateMessageRequestSchema = RequestSchema.extend({
|
|
1013
|
+
method: external_zod_namespaceObject.z.literal("sampling/createMessage"),
|
|
1014
|
+
params: BaseRequestParamsSchema.extend({
|
|
1015
|
+
messages: external_zod_namespaceObject.z.array(SamplingMessageSchema),
|
|
1016
|
+
/**
|
|
1017
|
+
* An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt.
|
|
1018
|
+
*/
|
|
1019
|
+
systemPrompt: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1020
|
+
/**
|
|
1021
|
+
* A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The client MAY ignore this request.
|
|
1022
|
+
*/
|
|
1023
|
+
includeContext: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.enum(["none", "thisServer", "allServers"])),
|
|
1024
|
+
temperature: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number()),
|
|
1025
|
+
/**
|
|
1026
|
+
* The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested.
|
|
1027
|
+
*/
|
|
1028
|
+
maxTokens: external_zod_namespaceObject.z.number().int(),
|
|
1029
|
+
stopSequences: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string())),
|
|
1030
|
+
/**
|
|
1031
|
+
* Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific.
|
|
1032
|
+
*/
|
|
1033
|
+
metadata: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
1034
|
+
/**
|
|
1035
|
+
* The server's preferences for which model to select.
|
|
1036
|
+
*/
|
|
1037
|
+
modelPreferences: external_zod_namespaceObject.z.optional(ModelPreferencesSchema),
|
|
1038
|
+
}),
|
|
1039
|
+
});
|
|
1040
|
+
/**
|
|
1041
|
+
* The client's response to a sampling/create_message request from the server. The client should inform the user before returning the sampled message, to allow them to inspect the response (human in the loop) and decide whether to allow the server to see it.
|
|
1042
|
+
*/
|
|
1043
|
+
const CreateMessageResultSchema = ResultSchema.extend({
|
|
1044
|
+
/**
|
|
1045
|
+
* The name of the model that generated the message.
|
|
1046
|
+
*/
|
|
1047
|
+
model: external_zod_namespaceObject.z.string(),
|
|
1048
|
+
/**
|
|
1049
|
+
* The reason why sampling stopped.
|
|
1050
|
+
*/
|
|
1051
|
+
stopReason: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.enum(["endTurn", "stopSequence", "maxTokens"]).or(external_zod_namespaceObject.z.string())),
|
|
1052
|
+
role: external_zod_namespaceObject.z.enum(["user", "assistant"]),
|
|
1053
|
+
content: external_zod_namespaceObject.z.discriminatedUnion("type", [
|
|
1054
|
+
TextContentSchema,
|
|
1055
|
+
ImageContentSchema,
|
|
1056
|
+
AudioContentSchema
|
|
1057
|
+
]),
|
|
1058
|
+
});
|
|
1059
|
+
/* Elicitation */
|
|
1060
|
+
/**
|
|
1061
|
+
* Primitive schema definition for boolean fields.
|
|
1062
|
+
*/
|
|
1063
|
+
const BooleanSchemaSchema = external_zod_namespaceObject.z
|
|
1064
|
+
.object({
|
|
1065
|
+
type: external_zod_namespaceObject.z.literal("boolean"),
|
|
1066
|
+
title: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1067
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1068
|
+
default: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
1069
|
+
})
|
|
1070
|
+
.passthrough();
|
|
1071
|
+
/**
|
|
1072
|
+
* Primitive schema definition for string fields.
|
|
1073
|
+
*/
|
|
1074
|
+
const StringSchemaSchema = external_zod_namespaceObject.z
|
|
1075
|
+
.object({
|
|
1076
|
+
type: external_zod_namespaceObject.z.literal("string"),
|
|
1077
|
+
title: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1078
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1079
|
+
minLength: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number()),
|
|
1080
|
+
maxLength: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number()),
|
|
1081
|
+
format: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.enum(["email", "uri", "date", "date-time"])),
|
|
1082
|
+
})
|
|
1083
|
+
.passthrough();
|
|
1084
|
+
/**
|
|
1085
|
+
* Primitive schema definition for number fields.
|
|
1086
|
+
*/
|
|
1087
|
+
const NumberSchemaSchema = external_zod_namespaceObject.z
|
|
1088
|
+
.object({
|
|
1089
|
+
type: external_zod_namespaceObject.z.enum(["number", "integer"]),
|
|
1090
|
+
title: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1091
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1092
|
+
minimum: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number()),
|
|
1093
|
+
maximum: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number()),
|
|
1094
|
+
})
|
|
1095
|
+
.passthrough();
|
|
1096
|
+
/**
|
|
1097
|
+
* Primitive schema definition for enum fields.
|
|
1098
|
+
*/
|
|
1099
|
+
const EnumSchemaSchema = external_zod_namespaceObject.z
|
|
1100
|
+
.object({
|
|
1101
|
+
type: external_zod_namespaceObject.z.literal("string"),
|
|
1102
|
+
title: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1103
|
+
description: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1104
|
+
enum: external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string()),
|
|
1105
|
+
enumNames: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string())),
|
|
1106
|
+
})
|
|
1107
|
+
.passthrough();
|
|
1108
|
+
/**
|
|
1109
|
+
* Union of all primitive schema definitions.
|
|
1110
|
+
*/
|
|
1111
|
+
const PrimitiveSchemaDefinitionSchema = external_zod_namespaceObject.z.union([
|
|
1112
|
+
BooleanSchemaSchema,
|
|
1113
|
+
StringSchemaSchema,
|
|
1114
|
+
NumberSchemaSchema,
|
|
1115
|
+
EnumSchemaSchema,
|
|
1116
|
+
]);
|
|
1117
|
+
/**
|
|
1118
|
+
* A request from the server to elicit user input via the client.
|
|
1119
|
+
* The client should present the message and form fields to the user.
|
|
1120
|
+
*/
|
|
1121
|
+
const ElicitRequestSchema = RequestSchema.extend({
|
|
1122
|
+
method: external_zod_namespaceObject.z.literal("elicitation/create"),
|
|
1123
|
+
params: BaseRequestParamsSchema.extend({
|
|
1124
|
+
/**
|
|
1125
|
+
* The message to present to the user.
|
|
1126
|
+
*/
|
|
1127
|
+
message: external_zod_namespaceObject.z.string(),
|
|
1128
|
+
/**
|
|
1129
|
+
* The schema for the requested user input.
|
|
1130
|
+
*/
|
|
1131
|
+
requestedSchema: external_zod_namespaceObject.z
|
|
1132
|
+
.object({
|
|
1133
|
+
type: external_zod_namespaceObject.z.literal("object"),
|
|
1134
|
+
properties: external_zod_namespaceObject.z.record(external_zod_namespaceObject.z.string(), PrimitiveSchemaDefinitionSchema),
|
|
1135
|
+
required: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string())),
|
|
1136
|
+
})
|
|
1137
|
+
.passthrough(),
|
|
1138
|
+
}),
|
|
1139
|
+
});
|
|
1140
|
+
/**
|
|
1141
|
+
* The client's response to an elicitation/create request from the server.
|
|
1142
|
+
*/
|
|
1143
|
+
const ElicitResultSchema = ResultSchema.extend({
|
|
1144
|
+
/**
|
|
1145
|
+
* The user's response action.
|
|
1146
|
+
*/
|
|
1147
|
+
action: external_zod_namespaceObject.z.enum(["accept", "decline", "cancel"]),
|
|
1148
|
+
/**
|
|
1149
|
+
* The collected user input content (only present if action is "accept").
|
|
1150
|
+
*/
|
|
1151
|
+
content: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.record(external_zod_namespaceObject.z.string(), external_zod_namespaceObject.z.unknown())),
|
|
1152
|
+
});
|
|
1153
|
+
/* Autocomplete */
|
|
1154
|
+
/**
|
|
1155
|
+
* A reference to a resource or resource template definition.
|
|
1156
|
+
*/
|
|
1157
|
+
const ResourceTemplateReferenceSchema = external_zod_namespaceObject.z
|
|
1158
|
+
.object({
|
|
1159
|
+
type: external_zod_namespaceObject.z.literal("ref/resource"),
|
|
1160
|
+
/**
|
|
1161
|
+
* The URI or URI template of the resource.
|
|
1162
|
+
*/
|
|
1163
|
+
uri: external_zod_namespaceObject.z.string(),
|
|
1164
|
+
})
|
|
1165
|
+
.passthrough();
|
|
1166
|
+
/**
|
|
1167
|
+
* @deprecated Use ResourceTemplateReferenceSchema instead
|
|
1168
|
+
*/
|
|
1169
|
+
const ResourceReferenceSchema = (/* unused pure expression or super */ null && (ResourceTemplateReferenceSchema));
|
|
1170
|
+
/**
|
|
1171
|
+
* Identifies a prompt.
|
|
1172
|
+
*/
|
|
1173
|
+
const PromptReferenceSchema = external_zod_namespaceObject.z
|
|
1174
|
+
.object({
|
|
1175
|
+
type: external_zod_namespaceObject.z.literal("ref/prompt"),
|
|
1176
|
+
/**
|
|
1177
|
+
* The name of the prompt or prompt template
|
|
1178
|
+
*/
|
|
1179
|
+
name: external_zod_namespaceObject.z.string(),
|
|
1180
|
+
})
|
|
1181
|
+
.passthrough();
|
|
1182
|
+
/**
|
|
1183
|
+
* A request from the client to the server, to ask for completion options.
|
|
1184
|
+
*/
|
|
1185
|
+
const CompleteRequestSchema = RequestSchema.extend({
|
|
1186
|
+
method: external_zod_namespaceObject.z.literal("completion/complete"),
|
|
1187
|
+
params: BaseRequestParamsSchema.extend({
|
|
1188
|
+
ref: external_zod_namespaceObject.z.union([PromptReferenceSchema, ResourceTemplateReferenceSchema]),
|
|
1189
|
+
/**
|
|
1190
|
+
* The argument's information
|
|
1191
|
+
*/
|
|
1192
|
+
argument: external_zod_namespaceObject.z
|
|
1193
|
+
.object({
|
|
1194
|
+
/**
|
|
1195
|
+
* The name of the argument
|
|
1196
|
+
*/
|
|
1197
|
+
name: external_zod_namespaceObject.z.string(),
|
|
1198
|
+
/**
|
|
1199
|
+
* The value of the argument to use for completion matching.
|
|
1200
|
+
*/
|
|
1201
|
+
value: external_zod_namespaceObject.z.string(),
|
|
1202
|
+
})
|
|
1203
|
+
.passthrough(),
|
|
1204
|
+
context: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({
|
|
1205
|
+
/**
|
|
1206
|
+
* Previously-resolved variables in a URI template or prompt.
|
|
1207
|
+
*/
|
|
1208
|
+
arguments: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.record(external_zod_namespaceObject.z.string(), external_zod_namespaceObject.z.string())),
|
|
1209
|
+
})),
|
|
1210
|
+
}),
|
|
1211
|
+
});
|
|
1212
|
+
/**
|
|
1213
|
+
* The server's response to a completion/complete request
|
|
1214
|
+
*/
|
|
1215
|
+
const CompleteResultSchema = ResultSchema.extend({
|
|
1216
|
+
completion: external_zod_namespaceObject.z
|
|
1217
|
+
.object({
|
|
1218
|
+
/**
|
|
1219
|
+
* An array of completion values. Must not exceed 100 items.
|
|
1220
|
+
*/
|
|
1221
|
+
values: external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string()).max(100),
|
|
1222
|
+
/**
|
|
1223
|
+
* The total number of completion options available. This can exceed the number of values actually sent in the response.
|
|
1224
|
+
*/
|
|
1225
|
+
total: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.number().int()),
|
|
1226
|
+
/**
|
|
1227
|
+
* Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
|
|
1228
|
+
*/
|
|
1229
|
+
hasMore: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.boolean()),
|
|
1230
|
+
})
|
|
1231
|
+
.passthrough(),
|
|
1232
|
+
});
|
|
1233
|
+
/* Roots */
|
|
1234
|
+
/**
|
|
1235
|
+
* Represents a root directory or file that the server can operate on.
|
|
1236
|
+
*/
|
|
1237
|
+
const RootSchema = external_zod_namespaceObject.z
|
|
1238
|
+
.object({
|
|
1239
|
+
/**
|
|
1240
|
+
* The URI identifying the root. This *must* start with file:// for now.
|
|
1241
|
+
*/
|
|
1242
|
+
uri: external_zod_namespaceObject.z.string().startsWith("file://"),
|
|
1243
|
+
/**
|
|
1244
|
+
* An optional name for the root.
|
|
1245
|
+
*/
|
|
1246
|
+
name: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.string()),
|
|
1247
|
+
/**
|
|
1248
|
+
* See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields)
|
|
1249
|
+
* for notes on _meta usage.
|
|
1250
|
+
*/
|
|
1251
|
+
_meta: external_zod_namespaceObject.z.optional(external_zod_namespaceObject.z.object({}).passthrough()),
|
|
1252
|
+
})
|
|
1253
|
+
.passthrough();
|
|
1254
|
+
/**
|
|
1255
|
+
* Sent from the server to request a list of root URIs from the client.
|
|
1256
|
+
*/
|
|
1257
|
+
const ListRootsRequestSchema = RequestSchema.extend({
|
|
1258
|
+
method: external_zod_namespaceObject.z.literal("roots/list"),
|
|
1259
|
+
});
|
|
1260
|
+
/**
|
|
1261
|
+
* The client's response to a roots/list request from the server.
|
|
1262
|
+
*/
|
|
1263
|
+
const ListRootsResultSchema = ResultSchema.extend({
|
|
1264
|
+
roots: external_zod_namespaceObject.z.array(RootSchema),
|
|
1265
|
+
});
|
|
1266
|
+
/**
|
|
1267
|
+
* A notification from the client to the server, informing it that the list of roots has changed.
|
|
1268
|
+
*/
|
|
1269
|
+
const RootsListChangedNotificationSchema = NotificationSchema.extend({
|
|
1270
|
+
method: external_zod_namespaceObject.z.literal("notifications/roots/list_changed"),
|
|
1271
|
+
});
|
|
1272
|
+
/* Client messages */
|
|
1273
|
+
const ClientRequestSchema = external_zod_namespaceObject.z.union([
|
|
1274
|
+
PingRequestSchema,
|
|
1275
|
+
InitializeRequestSchema,
|
|
1276
|
+
CompleteRequestSchema,
|
|
1277
|
+
SetLevelRequestSchema,
|
|
1278
|
+
GetPromptRequestSchema,
|
|
1279
|
+
ListPromptsRequestSchema,
|
|
1280
|
+
ListResourcesRequestSchema,
|
|
1281
|
+
ListResourceTemplatesRequestSchema,
|
|
1282
|
+
ReadResourceRequestSchema,
|
|
1283
|
+
SubscribeRequestSchema,
|
|
1284
|
+
UnsubscribeRequestSchema,
|
|
1285
|
+
CallToolRequestSchema,
|
|
1286
|
+
ListToolsRequestSchema,
|
|
1287
|
+
]);
|
|
1288
|
+
const ClientNotificationSchema = external_zod_namespaceObject.z.union([
|
|
1289
|
+
CancelledNotificationSchema,
|
|
1290
|
+
ProgressNotificationSchema,
|
|
1291
|
+
InitializedNotificationSchema,
|
|
1292
|
+
RootsListChangedNotificationSchema,
|
|
1293
|
+
]);
|
|
1294
|
+
const ClientResultSchema = external_zod_namespaceObject.z.union([
|
|
1295
|
+
EmptyResultSchema,
|
|
1296
|
+
CreateMessageResultSchema,
|
|
1297
|
+
ElicitResultSchema,
|
|
1298
|
+
ListRootsResultSchema,
|
|
1299
|
+
]);
|
|
1300
|
+
/* Server messages */
|
|
1301
|
+
const ServerRequestSchema = external_zod_namespaceObject.z.union([
|
|
1302
|
+
PingRequestSchema,
|
|
1303
|
+
CreateMessageRequestSchema,
|
|
1304
|
+
ElicitRequestSchema,
|
|
1305
|
+
ListRootsRequestSchema,
|
|
1306
|
+
]);
|
|
1307
|
+
const ServerNotificationSchema = external_zod_namespaceObject.z.union([
|
|
1308
|
+
CancelledNotificationSchema,
|
|
1309
|
+
ProgressNotificationSchema,
|
|
1310
|
+
LoggingMessageNotificationSchema,
|
|
1311
|
+
ResourceUpdatedNotificationSchema,
|
|
1312
|
+
ResourceListChangedNotificationSchema,
|
|
1313
|
+
ToolListChangedNotificationSchema,
|
|
1314
|
+
PromptListChangedNotificationSchema,
|
|
1315
|
+
]);
|
|
1316
|
+
const ServerResultSchema = external_zod_namespaceObject.z.union([
|
|
1317
|
+
EmptyResultSchema,
|
|
1318
|
+
InitializeResultSchema,
|
|
1319
|
+
CompleteResultSchema,
|
|
1320
|
+
GetPromptResultSchema,
|
|
1321
|
+
ListPromptsResultSchema,
|
|
1322
|
+
ListResourcesResultSchema,
|
|
1323
|
+
ListResourceTemplatesResultSchema,
|
|
1324
|
+
ReadResourceResultSchema,
|
|
1325
|
+
CallToolResultSchema,
|
|
1326
|
+
ListToolsResultSchema,
|
|
1327
|
+
]);
|
|
1328
|
+
class McpError extends Error {
|
|
1329
|
+
constructor(code, message, data) {
|
|
1330
|
+
super(`MCP error ${code}: ${message}`);
|
|
1331
|
+
this.code = code;
|
|
1332
|
+
this.data = data;
|
|
1333
|
+
this.name = "McpError";
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
//# sourceMappingURL=types.js.map
|
|
1337
|
+
;// ./node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
|
|
1338
|
+
|
|
1339
|
+
/**
|
|
1340
|
+
* The default request timeout, in miliseconds.
|
|
1341
|
+
*/
|
|
1342
|
+
const DEFAULT_REQUEST_TIMEOUT_MSEC = 60000;
|
|
1343
|
+
/**
|
|
1344
|
+
* Implements MCP protocol framing on top of a pluggable transport, including
|
|
1345
|
+
* features like request/response linking, notifications, and progress.
|
|
1346
|
+
*/
|
|
1347
|
+
class Protocol {
|
|
1348
|
+
constructor(_options) {
|
|
1349
|
+
this._options = _options;
|
|
1350
|
+
this._requestMessageId = 0;
|
|
1351
|
+
this._requestHandlers = new Map();
|
|
1352
|
+
this._requestHandlerAbortControllers = new Map();
|
|
1353
|
+
this._notificationHandlers = new Map();
|
|
1354
|
+
this._responseHandlers = new Map();
|
|
1355
|
+
this._progressHandlers = new Map();
|
|
1356
|
+
this._timeoutInfo = new Map();
|
|
1357
|
+
this._pendingDebouncedNotifications = new Set();
|
|
1358
|
+
this.setNotificationHandler(CancelledNotificationSchema, (notification) => {
|
|
1359
|
+
const controller = this._requestHandlerAbortControllers.get(notification.params.requestId);
|
|
1360
|
+
controller === null || controller === void 0 ? void 0 : controller.abort(notification.params.reason);
|
|
1361
|
+
});
|
|
1362
|
+
this.setNotificationHandler(ProgressNotificationSchema, (notification) => {
|
|
1363
|
+
this._onprogress(notification);
|
|
1364
|
+
});
|
|
1365
|
+
this.setRequestHandler(PingRequestSchema,
|
|
1366
|
+
// Automatic pong by default.
|
|
1367
|
+
(_request) => ({}));
|
|
1368
|
+
}
|
|
1369
|
+
_setupTimeout(messageId, timeout, maxTotalTimeout, onTimeout, resetTimeoutOnProgress = false) {
|
|
1370
|
+
this._timeoutInfo.set(messageId, {
|
|
1371
|
+
timeoutId: setTimeout(onTimeout, timeout),
|
|
1372
|
+
startTime: Date.now(),
|
|
1373
|
+
timeout,
|
|
1374
|
+
maxTotalTimeout,
|
|
1375
|
+
resetTimeoutOnProgress,
|
|
1376
|
+
onTimeout
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
_resetTimeout(messageId) {
|
|
1380
|
+
const info = this._timeoutInfo.get(messageId);
|
|
1381
|
+
if (!info)
|
|
1382
|
+
return false;
|
|
1383
|
+
const totalElapsed = Date.now() - info.startTime;
|
|
1384
|
+
if (info.maxTotalTimeout && totalElapsed >= info.maxTotalTimeout) {
|
|
1385
|
+
this._timeoutInfo.delete(messageId);
|
|
1386
|
+
throw new McpError(ErrorCode.RequestTimeout, "Maximum total timeout exceeded", { maxTotalTimeout: info.maxTotalTimeout, totalElapsed });
|
|
1387
|
+
}
|
|
1388
|
+
clearTimeout(info.timeoutId);
|
|
1389
|
+
info.timeoutId = setTimeout(info.onTimeout, info.timeout);
|
|
1390
|
+
return true;
|
|
1391
|
+
}
|
|
1392
|
+
_cleanupTimeout(messageId) {
|
|
1393
|
+
const info = this._timeoutInfo.get(messageId);
|
|
1394
|
+
if (info) {
|
|
1395
|
+
clearTimeout(info.timeoutId);
|
|
1396
|
+
this._timeoutInfo.delete(messageId);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Attaches to the given transport, starts it, and starts listening for messages.
|
|
1401
|
+
*
|
|
1402
|
+
* The Protocol 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.
|
|
1403
|
+
*/
|
|
1404
|
+
async connect(transport) {
|
|
1405
|
+
var _a, _b, _c;
|
|
1406
|
+
this._transport = transport;
|
|
1407
|
+
const _onclose = (_a = this.transport) === null || _a === void 0 ? void 0 : _a.onclose;
|
|
1408
|
+
this._transport.onclose = () => {
|
|
1409
|
+
_onclose === null || _onclose === void 0 ? void 0 : _onclose();
|
|
1410
|
+
this._onclose();
|
|
1411
|
+
};
|
|
1412
|
+
const _onerror = (_b = this.transport) === null || _b === void 0 ? void 0 : _b.onerror;
|
|
1413
|
+
this._transport.onerror = (error) => {
|
|
1414
|
+
_onerror === null || _onerror === void 0 ? void 0 : _onerror(error);
|
|
1415
|
+
this._onerror(error);
|
|
1416
|
+
};
|
|
1417
|
+
const _onmessage = (_c = this._transport) === null || _c === void 0 ? void 0 : _c.onmessage;
|
|
1418
|
+
this._transport.onmessage = (message, extra) => {
|
|
1419
|
+
_onmessage === null || _onmessage === void 0 ? void 0 : _onmessage(message, extra);
|
|
1420
|
+
if (isJSONRPCResponse(message) || isJSONRPCError(message)) {
|
|
1421
|
+
this._onresponse(message);
|
|
1422
|
+
}
|
|
1423
|
+
else if (isJSONRPCRequest(message)) {
|
|
1424
|
+
this._onrequest(message, extra);
|
|
1425
|
+
}
|
|
1426
|
+
else if (isJSONRPCNotification(message)) {
|
|
1427
|
+
this._onnotification(message);
|
|
1428
|
+
}
|
|
1429
|
+
else {
|
|
1430
|
+
this._onerror(new Error(`Unknown message type: ${JSON.stringify(message)}`));
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
await this._transport.start();
|
|
1434
|
+
}
|
|
1435
|
+
_onclose() {
|
|
1436
|
+
var _a;
|
|
1437
|
+
const responseHandlers = this._responseHandlers;
|
|
1438
|
+
this._responseHandlers = new Map();
|
|
1439
|
+
this._progressHandlers.clear();
|
|
1440
|
+
this._pendingDebouncedNotifications.clear();
|
|
1441
|
+
this._transport = undefined;
|
|
1442
|
+
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
1443
|
+
const error = new McpError(ErrorCode.ConnectionClosed, "Connection closed");
|
|
1444
|
+
for (const handler of responseHandlers.values()) {
|
|
1445
|
+
handler(error);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
_onerror(error) {
|
|
1449
|
+
var _a;
|
|
1450
|
+
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
1451
|
+
}
|
|
1452
|
+
_onnotification(notification) {
|
|
1453
|
+
var _a;
|
|
1454
|
+
const handler = (_a = this._notificationHandlers.get(notification.method)) !== null && _a !== void 0 ? _a : this.fallbackNotificationHandler;
|
|
1455
|
+
// Ignore notifications not being subscribed to.
|
|
1456
|
+
if (handler === undefined) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
// Starting with Promise.resolve() puts any synchronous errors into the monad as well.
|
|
1460
|
+
Promise.resolve()
|
|
1461
|
+
.then(() => handler(notification))
|
|
1462
|
+
.catch((error) => this._onerror(new Error(`Uncaught error in notification handler: ${error}`)));
|
|
1463
|
+
}
|
|
1464
|
+
_onrequest(request, extra) {
|
|
1465
|
+
var _a, _b, _c, _d;
|
|
1466
|
+
const handler = (_a = this._requestHandlers.get(request.method)) !== null && _a !== void 0 ? _a : this.fallbackRequestHandler;
|
|
1467
|
+
if (handler === undefined) {
|
|
1468
|
+
(_b = this._transport) === null || _b === void 0 ? void 0 : _b.send({
|
|
1469
|
+
jsonrpc: "2.0",
|
|
1470
|
+
id: request.id,
|
|
1471
|
+
error: {
|
|
1472
|
+
code: ErrorCode.MethodNotFound,
|
|
1473
|
+
message: "Method not found",
|
|
1474
|
+
},
|
|
1475
|
+
}).catch((error) => this._onerror(new Error(`Failed to send an error response: ${error}`)));
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
const abortController = new AbortController();
|
|
1479
|
+
this._requestHandlerAbortControllers.set(request.id, abortController);
|
|
1480
|
+
const fullExtra = {
|
|
1481
|
+
signal: abortController.signal,
|
|
1482
|
+
sessionId: (_c = this._transport) === null || _c === void 0 ? void 0 : _c.sessionId,
|
|
1483
|
+
_meta: (_d = request.params) === null || _d === void 0 ? void 0 : _d._meta,
|
|
1484
|
+
sendNotification: (notification) => this.notification(notification, { relatedRequestId: request.id }),
|
|
1485
|
+
sendRequest: (r, resultSchema, options) => this.request(r, resultSchema, { ...options, relatedRequestId: request.id }),
|
|
1486
|
+
authInfo: extra === null || extra === void 0 ? void 0 : extra.authInfo,
|
|
1487
|
+
requestId: request.id,
|
|
1488
|
+
requestInfo: extra === null || extra === void 0 ? void 0 : extra.requestInfo
|
|
1489
|
+
};
|
|
1490
|
+
// Starting with Promise.resolve() puts any synchronous errors into the monad as well.
|
|
1491
|
+
Promise.resolve()
|
|
1492
|
+
.then(() => handler(request, fullExtra))
|
|
1493
|
+
.then((result) => {
|
|
1494
|
+
var _a;
|
|
1495
|
+
if (abortController.signal.aborted) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
return (_a = this._transport) === null || _a === void 0 ? void 0 : _a.send({
|
|
1499
|
+
result,
|
|
1500
|
+
jsonrpc: "2.0",
|
|
1501
|
+
id: request.id,
|
|
1502
|
+
});
|
|
1503
|
+
}, (error) => {
|
|
1504
|
+
var _a, _b;
|
|
1505
|
+
if (abortController.signal.aborted) {
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
return (_a = this._transport) === null || _a === void 0 ? void 0 : _a.send({
|
|
1509
|
+
jsonrpc: "2.0",
|
|
1510
|
+
id: request.id,
|
|
1511
|
+
error: {
|
|
1512
|
+
code: Number.isSafeInteger(error["code"])
|
|
1513
|
+
? error["code"]
|
|
1514
|
+
: ErrorCode.InternalError,
|
|
1515
|
+
message: (_b = error.message) !== null && _b !== void 0 ? _b : "Internal error",
|
|
1516
|
+
},
|
|
1517
|
+
});
|
|
1518
|
+
})
|
|
1519
|
+
.catch((error) => this._onerror(new Error(`Failed to send response: ${error}`)))
|
|
1520
|
+
.finally(() => {
|
|
1521
|
+
this._requestHandlerAbortControllers.delete(request.id);
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
_onprogress(notification) {
|
|
1525
|
+
const { progressToken, ...params } = notification.params;
|
|
1526
|
+
const messageId = Number(progressToken);
|
|
1527
|
+
const handler = this._progressHandlers.get(messageId);
|
|
1528
|
+
if (!handler) {
|
|
1529
|
+
this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`));
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
const responseHandler = this._responseHandlers.get(messageId);
|
|
1533
|
+
const timeoutInfo = this._timeoutInfo.get(messageId);
|
|
1534
|
+
if (timeoutInfo && responseHandler && timeoutInfo.resetTimeoutOnProgress) {
|
|
1535
|
+
try {
|
|
1536
|
+
this._resetTimeout(messageId);
|
|
1537
|
+
}
|
|
1538
|
+
catch (error) {
|
|
1539
|
+
responseHandler(error);
|
|
1540
|
+
return;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
handler(params);
|
|
1544
|
+
}
|
|
1545
|
+
_onresponse(response) {
|
|
1546
|
+
const messageId = Number(response.id);
|
|
1547
|
+
const handler = this._responseHandlers.get(messageId);
|
|
1548
|
+
if (handler === undefined) {
|
|
1549
|
+
this._onerror(new Error(`Received a response for an unknown message ID: ${JSON.stringify(response)}`));
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
this._responseHandlers.delete(messageId);
|
|
1553
|
+
this._progressHandlers.delete(messageId);
|
|
1554
|
+
this._cleanupTimeout(messageId);
|
|
1555
|
+
if (isJSONRPCResponse(response)) {
|
|
1556
|
+
handler(response);
|
|
1557
|
+
}
|
|
1558
|
+
else {
|
|
1559
|
+
const error = new McpError(response.error.code, response.error.message, response.error.data);
|
|
1560
|
+
handler(error);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
get transport() {
|
|
1564
|
+
return this._transport;
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Closes the connection.
|
|
1568
|
+
*/
|
|
1569
|
+
async close() {
|
|
1570
|
+
var _a;
|
|
1571
|
+
await ((_a = this._transport) === null || _a === void 0 ? void 0 : _a.close());
|
|
1572
|
+
}
|
|
1573
|
+
/**
|
|
1574
|
+
* Sends a request and wait for a response.
|
|
1575
|
+
*
|
|
1576
|
+
* Do not use this method to emit notifications! Use notification() instead.
|
|
1577
|
+
*/
|
|
1578
|
+
request(request, resultSchema, options) {
|
|
1579
|
+
const { relatedRequestId, resumptionToken, onresumptiontoken } = options !== null && options !== void 0 ? options : {};
|
|
1580
|
+
return new Promise((resolve, reject) => {
|
|
1581
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1582
|
+
if (!this._transport) {
|
|
1583
|
+
reject(new Error("Not connected"));
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
if (((_a = this._options) === null || _a === void 0 ? void 0 : _a.enforceStrictCapabilities) === true) {
|
|
1587
|
+
this.assertCapabilityForMethod(request.method);
|
|
1588
|
+
}
|
|
1589
|
+
(_b = options === null || options === void 0 ? void 0 : options.signal) === null || _b === void 0 ? void 0 : _b.throwIfAborted();
|
|
1590
|
+
const messageId = this._requestMessageId++;
|
|
1591
|
+
const jsonrpcRequest = {
|
|
1592
|
+
...request,
|
|
1593
|
+
jsonrpc: "2.0",
|
|
1594
|
+
id: messageId,
|
|
1595
|
+
};
|
|
1596
|
+
if (options === null || options === void 0 ? void 0 : options.onprogress) {
|
|
1597
|
+
this._progressHandlers.set(messageId, options.onprogress);
|
|
1598
|
+
jsonrpcRequest.params = {
|
|
1599
|
+
...request.params,
|
|
1600
|
+
_meta: {
|
|
1601
|
+
...(((_c = request.params) === null || _c === void 0 ? void 0 : _c._meta) || {}),
|
|
1602
|
+
progressToken: messageId
|
|
1603
|
+
},
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
const cancel = (reason) => {
|
|
1607
|
+
var _a;
|
|
1608
|
+
this._responseHandlers.delete(messageId);
|
|
1609
|
+
this._progressHandlers.delete(messageId);
|
|
1610
|
+
this._cleanupTimeout(messageId);
|
|
1611
|
+
(_a = this._transport) === null || _a === void 0 ? void 0 : _a.send({
|
|
1612
|
+
jsonrpc: "2.0",
|
|
1613
|
+
method: "notifications/cancelled",
|
|
1614
|
+
params: {
|
|
1615
|
+
requestId: messageId,
|
|
1616
|
+
reason: String(reason),
|
|
1617
|
+
},
|
|
1618
|
+
}, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => this._onerror(new Error(`Failed to send cancellation: ${error}`)));
|
|
1619
|
+
reject(reason);
|
|
1620
|
+
};
|
|
1621
|
+
this._responseHandlers.set(messageId, (response) => {
|
|
1622
|
+
var _a;
|
|
1623
|
+
if ((_a = options === null || options === void 0 ? void 0 : options.signal) === null || _a === void 0 ? void 0 : _a.aborted) {
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
if (response instanceof Error) {
|
|
1627
|
+
return reject(response);
|
|
1628
|
+
}
|
|
1629
|
+
try {
|
|
1630
|
+
const result = resultSchema.parse(response.result);
|
|
1631
|
+
resolve(result);
|
|
1632
|
+
}
|
|
1633
|
+
catch (error) {
|
|
1634
|
+
reject(error);
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
(_d = options === null || options === void 0 ? void 0 : options.signal) === null || _d === void 0 ? void 0 : _d.addEventListener("abort", () => {
|
|
1638
|
+
var _a;
|
|
1639
|
+
cancel((_a = options === null || options === void 0 ? void 0 : options.signal) === null || _a === void 0 ? void 0 : _a.reason);
|
|
1640
|
+
});
|
|
1641
|
+
const timeout = (_e = options === null || options === void 0 ? void 0 : options.timeout) !== null && _e !== void 0 ? _e : DEFAULT_REQUEST_TIMEOUT_MSEC;
|
|
1642
|
+
const timeoutHandler = () => cancel(new McpError(ErrorCode.RequestTimeout, "Request timed out", { timeout }));
|
|
1643
|
+
this._setupTimeout(messageId, timeout, options === null || options === void 0 ? void 0 : options.maxTotalTimeout, timeoutHandler, (_f = options === null || options === void 0 ? void 0 : options.resetTimeoutOnProgress) !== null && _f !== void 0 ? _f : false);
|
|
1644
|
+
this._transport.send(jsonrpcRequest, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error) => {
|
|
1645
|
+
this._cleanupTimeout(messageId);
|
|
1646
|
+
reject(error);
|
|
1647
|
+
});
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Emits a notification, which is a one-way message that does not expect a response.
|
|
1652
|
+
*/
|
|
1653
|
+
async notification(notification, options) {
|
|
1654
|
+
var _a, _b;
|
|
1655
|
+
if (!this._transport) {
|
|
1656
|
+
throw new Error("Not connected");
|
|
1657
|
+
}
|
|
1658
|
+
this.assertNotificationCapability(notification.method);
|
|
1659
|
+
const debouncedMethods = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.debouncedNotificationMethods) !== null && _b !== void 0 ? _b : [];
|
|
1660
|
+
// A notification can only be debounced if it's in the list AND it's "simple"
|
|
1661
|
+
// (i.e., has no parameters and no related request ID that could be lost).
|
|
1662
|
+
const canDebounce = debouncedMethods.includes(notification.method)
|
|
1663
|
+
&& !notification.params
|
|
1664
|
+
&& !(options === null || options === void 0 ? void 0 : options.relatedRequestId);
|
|
1665
|
+
if (canDebounce) {
|
|
1666
|
+
// If a notification of this type is already scheduled, do nothing.
|
|
1667
|
+
if (this._pendingDebouncedNotifications.has(notification.method)) {
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
// Mark this notification type as pending.
|
|
1671
|
+
this._pendingDebouncedNotifications.add(notification.method);
|
|
1672
|
+
// Schedule the actual send to happen in the next microtask.
|
|
1673
|
+
// This allows all synchronous calls in the current event loop tick to be coalesced.
|
|
1674
|
+
Promise.resolve().then(() => {
|
|
1675
|
+
var _a;
|
|
1676
|
+
// Un-mark the notification so the next one can be scheduled.
|
|
1677
|
+
this._pendingDebouncedNotifications.delete(notification.method);
|
|
1678
|
+
// SAFETY CHECK: If the connection was closed while this was pending, abort.
|
|
1679
|
+
if (!this._transport) {
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const jsonrpcNotification = {
|
|
1683
|
+
...notification,
|
|
1684
|
+
jsonrpc: "2.0",
|
|
1685
|
+
};
|
|
1686
|
+
// Send the notification, but don't await it here to avoid blocking.
|
|
1687
|
+
// Handle potential errors with a .catch().
|
|
1688
|
+
(_a = this._transport) === null || _a === void 0 ? void 0 : _a.send(jsonrpcNotification, options).catch(error => this._onerror(error));
|
|
1689
|
+
});
|
|
1690
|
+
// Return immediately.
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
const jsonrpcNotification = {
|
|
1694
|
+
...notification,
|
|
1695
|
+
jsonrpc: "2.0",
|
|
1696
|
+
};
|
|
1697
|
+
await this._transport.send(jsonrpcNotification, options);
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Registers a handler to invoke when this protocol object receives a request with the given method.
|
|
1701
|
+
*
|
|
1702
|
+
* Note that this will replace any previous request handler for the same method.
|
|
1703
|
+
*/
|
|
1704
|
+
setRequestHandler(requestSchema, handler) {
|
|
1705
|
+
const method = requestSchema.shape.method.value;
|
|
1706
|
+
this.assertRequestHandlerCapability(method);
|
|
1707
|
+
this._requestHandlers.set(method, (request, extra) => {
|
|
1708
|
+
return Promise.resolve(handler(requestSchema.parse(request), extra));
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Removes the request handler for the given method.
|
|
1713
|
+
*/
|
|
1714
|
+
removeRequestHandler(method) {
|
|
1715
|
+
this._requestHandlers.delete(method);
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed.
|
|
1719
|
+
*/
|
|
1720
|
+
assertCanSetRequestHandler(method) {
|
|
1721
|
+
if (this._requestHandlers.has(method)) {
|
|
1722
|
+
throw new Error(`A request handler for ${method} already exists, which would be overridden`);
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* Registers a handler to invoke when this protocol object receives a notification with the given method.
|
|
1727
|
+
*
|
|
1728
|
+
* Note that this will replace any previous notification handler for the same method.
|
|
1729
|
+
*/
|
|
1730
|
+
setNotificationHandler(notificationSchema, handler) {
|
|
1731
|
+
this._notificationHandlers.set(notificationSchema.shape.method.value, (notification) => Promise.resolve(handler(notificationSchema.parse(notification))));
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Removes the notification handler for the given method.
|
|
1735
|
+
*/
|
|
1736
|
+
removeNotificationHandler(method) {
|
|
1737
|
+
this._notificationHandlers.delete(method);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
function mergeCapabilities(base, additional) {
|
|
1741
|
+
return Object.entries(additional).reduce((acc, [key, value]) => {
|
|
1742
|
+
if (value && typeof value === "object") {
|
|
1743
|
+
acc[key] = acc[key] ? { ...acc[key], ...value } : value;
|
|
1744
|
+
}
|
|
1745
|
+
else {
|
|
1746
|
+
acc[key] = value;
|
|
1747
|
+
}
|
|
1748
|
+
return acc;
|
|
1749
|
+
}, { ...base });
|
|
1750
|
+
}
|
|
1751
|
+
//# sourceMappingURL=protocol.js.map
|
|
1752
|
+
;// external "ajv"
|
|
1753
|
+
const external_ajv_namespaceObject = require("ajv");
|
|
1754
|
+
;// ./node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
|
|
1755
|
+
|
|
1756
|
+
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* An MCP server on top of a pluggable transport.
|
|
1760
|
+
*
|
|
1761
|
+
* This server will automatically respond to the initialization flow as initiated from the client.
|
|
1762
|
+
*
|
|
1763
|
+
* To use with custom types, extend the base Request/Notification/Result types and pass them as type parameters:
|
|
1764
|
+
*
|
|
1765
|
+
* ```typescript
|
|
1766
|
+
* // Custom schemas
|
|
1767
|
+
* const CustomRequestSchema = RequestSchema.extend({...})
|
|
1768
|
+
* const CustomNotificationSchema = NotificationSchema.extend({...})
|
|
1769
|
+
* const CustomResultSchema = ResultSchema.extend({...})
|
|
1770
|
+
*
|
|
1771
|
+
* // Type aliases
|
|
1772
|
+
* type CustomRequest = z.infer<typeof CustomRequestSchema>
|
|
1773
|
+
* type CustomNotification = z.infer<typeof CustomNotificationSchema>
|
|
1774
|
+
* type CustomResult = z.infer<typeof CustomResultSchema>
|
|
1775
|
+
*
|
|
1776
|
+
* // Create typed server
|
|
1777
|
+
* const server = new Server<CustomRequest, CustomNotification, CustomResult>({
|
|
1778
|
+
* name: "CustomServer",
|
|
1779
|
+
* version: "1.0.0"
|
|
1780
|
+
* })
|
|
1781
|
+
* ```
|
|
1782
|
+
*/
|
|
1783
|
+
class Server extends Protocol {
|
|
1784
|
+
/**
|
|
1785
|
+
* Initializes this server with the given name and version information.
|
|
1786
|
+
*/
|
|
1787
|
+
constructor(_serverInfo, options) {
|
|
1788
|
+
var _a;
|
|
1789
|
+
super(options);
|
|
1790
|
+
this._serverInfo = _serverInfo;
|
|
1791
|
+
this._capabilities = (_a = options === null || options === void 0 ? void 0 : options.capabilities) !== null && _a !== void 0 ? _a : {};
|
|
1792
|
+
this._instructions = options === null || options === void 0 ? void 0 : options.instructions;
|
|
1793
|
+
this.setRequestHandler(InitializeRequestSchema, (request) => this._oninitialize(request));
|
|
1794
|
+
this.setNotificationHandler(InitializedNotificationSchema, () => { var _a; return (_a = this.oninitialized) === null || _a === void 0 ? void 0 : _a.call(this); });
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Registers new capabilities. This can only be called before connecting to a transport.
|
|
1798
|
+
*
|
|
1799
|
+
* The new capabilities will be merged with any existing capabilities previously given (e.g., at initialization).
|
|
1800
|
+
*/
|
|
1801
|
+
registerCapabilities(capabilities) {
|
|
1802
|
+
if (this.transport) {
|
|
1803
|
+
throw new Error("Cannot register capabilities after connecting to transport");
|
|
1804
|
+
}
|
|
1805
|
+
this._capabilities = mergeCapabilities(this._capabilities, capabilities);
|
|
1806
|
+
}
|
|
1807
|
+
assertCapabilityForMethod(method) {
|
|
1808
|
+
var _a, _b, _c;
|
|
1809
|
+
switch (method) {
|
|
1810
|
+
case "sampling/createMessage":
|
|
1811
|
+
if (!((_a = this._clientCapabilities) === null || _a === void 0 ? void 0 : _a.sampling)) {
|
|
1812
|
+
throw new Error(`Client does not support sampling (required for ${method})`);
|
|
1813
|
+
}
|
|
1814
|
+
break;
|
|
1815
|
+
case "elicitation/create":
|
|
1816
|
+
if (!((_b = this._clientCapabilities) === null || _b === void 0 ? void 0 : _b.elicitation)) {
|
|
1817
|
+
throw new Error(`Client does not support elicitation (required for ${method})`);
|
|
1818
|
+
}
|
|
1819
|
+
break;
|
|
1820
|
+
case "roots/list":
|
|
1821
|
+
if (!((_c = this._clientCapabilities) === null || _c === void 0 ? void 0 : _c.roots)) {
|
|
1822
|
+
throw new Error(`Client does not support listing roots (required for ${method})`);
|
|
1823
|
+
}
|
|
1824
|
+
break;
|
|
1825
|
+
case "ping":
|
|
1826
|
+
// No specific capability required for ping
|
|
1827
|
+
break;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
assertNotificationCapability(method) {
|
|
1831
|
+
switch (method) {
|
|
1832
|
+
case "notifications/message":
|
|
1833
|
+
if (!this._capabilities.logging) {
|
|
1834
|
+
throw new Error(`Server does not support logging (required for ${method})`);
|
|
1835
|
+
}
|
|
1836
|
+
break;
|
|
1837
|
+
case "notifications/resources/updated":
|
|
1838
|
+
case "notifications/resources/list_changed":
|
|
1839
|
+
if (!this._capabilities.resources) {
|
|
1840
|
+
throw new Error(`Server does not support notifying about resources (required for ${method})`);
|
|
1841
|
+
}
|
|
1842
|
+
break;
|
|
1843
|
+
case "notifications/tools/list_changed":
|
|
1844
|
+
if (!this._capabilities.tools) {
|
|
1845
|
+
throw new Error(`Server does not support notifying of tool list changes (required for ${method})`);
|
|
1846
|
+
}
|
|
1847
|
+
break;
|
|
1848
|
+
case "notifications/prompts/list_changed":
|
|
1849
|
+
if (!this._capabilities.prompts) {
|
|
1850
|
+
throw new Error(`Server does not support notifying of prompt list changes (required for ${method})`);
|
|
1851
|
+
}
|
|
1852
|
+
break;
|
|
1853
|
+
case "notifications/cancelled":
|
|
1854
|
+
// Cancellation notifications are always allowed
|
|
1855
|
+
break;
|
|
1856
|
+
case "notifications/progress":
|
|
1857
|
+
// Progress notifications are always allowed
|
|
1858
|
+
break;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
assertRequestHandlerCapability(method) {
|
|
1862
|
+
switch (method) {
|
|
1863
|
+
case "sampling/createMessage":
|
|
1864
|
+
if (!this._capabilities.sampling) {
|
|
1865
|
+
throw new Error(`Server does not support sampling (required for ${method})`);
|
|
1866
|
+
}
|
|
1867
|
+
break;
|
|
1868
|
+
case "logging/setLevel":
|
|
1869
|
+
if (!this._capabilities.logging) {
|
|
1870
|
+
throw new Error(`Server does not support logging (required for ${method})`);
|
|
1871
|
+
}
|
|
1872
|
+
break;
|
|
1873
|
+
case "prompts/get":
|
|
1874
|
+
case "prompts/list":
|
|
1875
|
+
if (!this._capabilities.prompts) {
|
|
1876
|
+
throw new Error(`Server does not support prompts (required for ${method})`);
|
|
1877
|
+
}
|
|
1878
|
+
break;
|
|
1879
|
+
case "resources/list":
|
|
1880
|
+
case "resources/templates/list":
|
|
1881
|
+
case "resources/read":
|
|
1882
|
+
if (!this._capabilities.resources) {
|
|
1883
|
+
throw new Error(`Server does not support resources (required for ${method})`);
|
|
1884
|
+
}
|
|
1885
|
+
break;
|
|
1886
|
+
case "tools/call":
|
|
1887
|
+
case "tools/list":
|
|
1888
|
+
if (!this._capabilities.tools) {
|
|
1889
|
+
throw new Error(`Server does not support tools (required for ${method})`);
|
|
1890
|
+
}
|
|
1891
|
+
break;
|
|
1892
|
+
case "ping":
|
|
1893
|
+
case "initialize":
|
|
1894
|
+
// No specific capability required for these methods
|
|
1895
|
+
break;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
async _oninitialize(request) {
|
|
1899
|
+
const requestedVersion = request.params.protocolVersion;
|
|
1900
|
+
this._clientCapabilities = request.params.capabilities;
|
|
1901
|
+
this._clientVersion = request.params.clientInfo;
|
|
1902
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion)
|
|
1903
|
+
? requestedVersion
|
|
1904
|
+
: LATEST_PROTOCOL_VERSION;
|
|
1905
|
+
return {
|
|
1906
|
+
protocolVersion,
|
|
1907
|
+
capabilities: this.getCapabilities(),
|
|
1908
|
+
serverInfo: this._serverInfo,
|
|
1909
|
+
...(this._instructions && { instructions: this._instructions }),
|
|
1910
|
+
};
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* After initialization has completed, this will be populated with the client's reported capabilities.
|
|
1914
|
+
*/
|
|
1915
|
+
getClientCapabilities() {
|
|
1916
|
+
return this._clientCapabilities;
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* After initialization has completed, this will be populated with information about the client's name and version.
|
|
1920
|
+
*/
|
|
1921
|
+
getClientVersion() {
|
|
1922
|
+
return this._clientVersion;
|
|
1923
|
+
}
|
|
1924
|
+
getCapabilities() {
|
|
1925
|
+
return this._capabilities;
|
|
1926
|
+
}
|
|
1927
|
+
async ping() {
|
|
1928
|
+
return this.request({ method: "ping" }, EmptyResultSchema);
|
|
1929
|
+
}
|
|
1930
|
+
async createMessage(params, options) {
|
|
1931
|
+
return this.request({ method: "sampling/createMessage", params }, CreateMessageResultSchema, options);
|
|
1932
|
+
}
|
|
1933
|
+
async elicitInput(params, options) {
|
|
1934
|
+
const result = await this.request({ method: "elicitation/create", params }, ElicitResultSchema, options);
|
|
1935
|
+
// Validate the response content against the requested schema if action is "accept"
|
|
1936
|
+
if (result.action === "accept" && result.content) {
|
|
1937
|
+
try {
|
|
1938
|
+
const ajv = new external_ajv_namespaceObject();
|
|
1939
|
+
const validate = ajv.compile(params.requestedSchema);
|
|
1940
|
+
const isValid = validate(result.content);
|
|
1941
|
+
if (!isValid) {
|
|
1942
|
+
throw new McpError(ErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${ajv.errorsText(validate.errors)}`);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
catch (error) {
|
|
1946
|
+
if (error instanceof McpError) {
|
|
1947
|
+
throw error;
|
|
1948
|
+
}
|
|
1949
|
+
throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error}`);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
return result;
|
|
1953
|
+
}
|
|
1954
|
+
async listRoots(params, options) {
|
|
1955
|
+
return this.request({ method: "roots/list", params }, ListRootsResultSchema, options);
|
|
1956
|
+
}
|
|
1957
|
+
async sendLoggingMessage(params) {
|
|
1958
|
+
return this.notification({ method: "notifications/message", params });
|
|
1959
|
+
}
|
|
1960
|
+
async sendResourceUpdated(params) {
|
|
1961
|
+
return this.notification({
|
|
1962
|
+
method: "notifications/resources/updated",
|
|
1963
|
+
params,
|
|
1964
|
+
});
|
|
1965
|
+
}
|
|
1966
|
+
async sendResourceListChanged() {
|
|
1967
|
+
return this.notification({
|
|
1968
|
+
method: "notifications/resources/list_changed",
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
async sendToolListChanged() {
|
|
1972
|
+
return this.notification({ method: "notifications/tools/list_changed" });
|
|
1973
|
+
}
|
|
1974
|
+
async sendPromptListChanged() {
|
|
1975
|
+
return this.notification({ method: "notifications/prompts/list_changed" });
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
//# sourceMappingURL=index.js.map
|
|
1979
|
+
;// external "node:process"
|
|
1980
|
+
const external_node_process_namespaceObject = require("node:process");
|
|
1981
|
+
;// ./node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
|
|
1982
|
+
|
|
1983
|
+
/**
|
|
1984
|
+
* Buffers a continuous stdio stream into discrete JSON-RPC messages.
|
|
1985
|
+
*/
|
|
1986
|
+
class ReadBuffer {
|
|
1987
|
+
append(chunk) {
|
|
1988
|
+
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
1989
|
+
}
|
|
1990
|
+
readMessage() {
|
|
1991
|
+
if (!this._buffer) {
|
|
1992
|
+
return null;
|
|
1993
|
+
}
|
|
1994
|
+
const index = this._buffer.indexOf("\n");
|
|
1995
|
+
if (index === -1) {
|
|
1996
|
+
return null;
|
|
1997
|
+
}
|
|
1998
|
+
const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, '');
|
|
1999
|
+
this._buffer = this._buffer.subarray(index + 1);
|
|
2000
|
+
return deserializeMessage(line);
|
|
2001
|
+
}
|
|
2002
|
+
clear() {
|
|
2003
|
+
this._buffer = undefined;
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
function deserializeMessage(line) {
|
|
2007
|
+
return JSONRPCMessageSchema.parse(JSON.parse(line));
|
|
2008
|
+
}
|
|
2009
|
+
function serializeMessage(message) {
|
|
2010
|
+
return JSON.stringify(message) + "\n";
|
|
2011
|
+
}
|
|
2012
|
+
//# sourceMappingURL=stdio.js.map
|
|
2013
|
+
;// ./node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
2014
|
+
|
|
2015
|
+
|
|
2016
|
+
/**
|
|
2017
|
+
* Server transport for stdio: this communicates with a MCP client by reading from the current process' stdin and writing to stdout.
|
|
2018
|
+
*
|
|
2019
|
+
* This transport is only available in Node.js environments.
|
|
2020
|
+
*/
|
|
2021
|
+
class StdioServerTransport {
|
|
2022
|
+
constructor(_stdin = external_node_process_namespaceObject.stdin, _stdout = external_node_process_namespaceObject.stdout) {
|
|
2023
|
+
this._stdin = _stdin;
|
|
2024
|
+
this._stdout = _stdout;
|
|
2025
|
+
this._readBuffer = new ReadBuffer();
|
|
2026
|
+
this._started = false;
|
|
2027
|
+
// Arrow functions to bind `this` properly, while maintaining function identity.
|
|
2028
|
+
this._ondata = (chunk) => {
|
|
2029
|
+
this._readBuffer.append(chunk);
|
|
2030
|
+
this.processReadBuffer();
|
|
2031
|
+
};
|
|
2032
|
+
this._onerror = (error) => {
|
|
2033
|
+
var _a;
|
|
2034
|
+
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
/**
|
|
2038
|
+
* Starts listening for messages on stdin.
|
|
2039
|
+
*/
|
|
2040
|
+
async start() {
|
|
2041
|
+
if (this._started) {
|
|
2042
|
+
throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
2043
|
+
}
|
|
2044
|
+
this._started = true;
|
|
2045
|
+
this._stdin.on("data", this._ondata);
|
|
2046
|
+
this._stdin.on("error", this._onerror);
|
|
2047
|
+
}
|
|
2048
|
+
processReadBuffer() {
|
|
2049
|
+
var _a, _b;
|
|
2050
|
+
while (true) {
|
|
2051
|
+
try {
|
|
2052
|
+
const message = this._readBuffer.readMessage();
|
|
2053
|
+
if (message === null) {
|
|
2054
|
+
break;
|
|
2055
|
+
}
|
|
2056
|
+
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
|
|
2057
|
+
}
|
|
2058
|
+
catch (error) {
|
|
2059
|
+
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
async close() {
|
|
2064
|
+
var _a;
|
|
2065
|
+
// Remove our event listeners first
|
|
2066
|
+
this._stdin.off("data", this._ondata);
|
|
2067
|
+
this._stdin.off("error", this._onerror);
|
|
2068
|
+
// Check if we were the only data listener
|
|
2069
|
+
const remainingDataListeners = this._stdin.listenerCount('data');
|
|
2070
|
+
if (remainingDataListeners === 0) {
|
|
2071
|
+
// Only pause stdin if we were the only listener
|
|
2072
|
+
// This prevents interfering with other parts of the application that might be using stdin
|
|
2073
|
+
this._stdin.pause();
|
|
2074
|
+
}
|
|
2075
|
+
// Clear the buffer and notify closure
|
|
2076
|
+
this._readBuffer.clear();
|
|
2077
|
+
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
2078
|
+
}
|
|
2079
|
+
send(message) {
|
|
2080
|
+
return new Promise((resolve) => {
|
|
2081
|
+
const json = serializeMessage(message);
|
|
2082
|
+
if (this._stdout.write(json)) {
|
|
2083
|
+
resolve();
|
|
2084
|
+
}
|
|
2085
|
+
else {
|
|
2086
|
+
this._stdout.once("drain", resolve);
|
|
2087
|
+
}
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
//# sourceMappingURL=stdio.js.map
|
|
2092
|
+
;// external "zod-to-json-schema"
|
|
2093
|
+
const external_zod_to_json_schema_namespaceObject = require("zod-to-json-schema");
|
|
2094
|
+
;// ./src/schemas.ts
|
|
2095
|
+
|
|
2096
|
+
// Command arguments schema for better type safety and documentation
|
|
2097
|
+
const CommandArgsSchema = external_zod_namespaceObject.z
|
|
2098
|
+
.object({
|
|
2099
|
+
selector: external_zod_namespaceObject.z
|
|
2100
|
+
.string()
|
|
2101
|
+
.optional()
|
|
2102
|
+
.describe('CSS selector for targeting elements (required for click_by_selector, click_button)'),
|
|
2103
|
+
text: external_zod_namespaceObject.z
|
|
2104
|
+
.string()
|
|
2105
|
+
.optional()
|
|
2106
|
+
.describe('Text content for searching or keyboard input (required for click_by_text, send_keyboard_shortcut)'),
|
|
2107
|
+
value: external_zod_namespaceObject.z
|
|
2108
|
+
.string()
|
|
2109
|
+
.optional()
|
|
2110
|
+
.describe('Value to input into form fields (required for fill_input)'),
|
|
2111
|
+
placeholder: external_zod_namespaceObject.z
|
|
2112
|
+
.string()
|
|
2113
|
+
.optional()
|
|
2114
|
+
.describe('Placeholder text to identify input fields (alternative to selector for fill_input)'),
|
|
2115
|
+
message: external_zod_namespaceObject.z.string().optional().describe('Message or content for specific commands'),
|
|
2116
|
+
code: external_zod_namespaceObject.z.string().optional().describe('JavaScript code to execute (for eval command)'),
|
|
2117
|
+
})
|
|
2118
|
+
.describe('Command-specific arguments. Structure depends on the command being executed.');
|
|
2119
|
+
// Schema definitions for tool inputs
|
|
2120
|
+
const SendCommandToElectronSchema = external_zod_namespaceObject.z.object({
|
|
2121
|
+
command: external_zod_namespaceObject.z.string().describe('Command to send to the Electron process'),
|
|
2122
|
+
args: CommandArgsSchema.optional().describe('Arguments for the command - must be an object with appropriate properties based on the command type'),
|
|
2123
|
+
targetId: external_zod_namespaceObject.z
|
|
2124
|
+
.string()
|
|
2125
|
+
.optional()
|
|
2126
|
+
.describe('CDP target ID to send the command to a specific window (exact match)'),
|
|
2127
|
+
windowTitle: external_zod_namespaceObject.z
|
|
2128
|
+
.string()
|
|
2129
|
+
.optional()
|
|
2130
|
+
.describe('Window title to target (case-insensitive partial match). Use list_electron_windows to see available windows.'),
|
|
2131
|
+
});
|
|
2132
|
+
const TakeScreenshotSchema = external_zod_namespaceObject.z.object({
|
|
2133
|
+
outputPath: external_zod_namespaceObject.z
|
|
2134
|
+
.string()
|
|
2135
|
+
.optional()
|
|
2136
|
+
.describe('Path to save the screenshot (optional, defaults to temp directory)'),
|
|
2137
|
+
windowTitle: external_zod_namespaceObject.z.string().optional().describe('Specific window title to screenshot (optional)'),
|
|
2138
|
+
});
|
|
2139
|
+
const ReadElectronLogsSchema = external_zod_namespaceObject.z.object({
|
|
2140
|
+
logType: external_zod_namespaceObject.z
|
|
2141
|
+
.enum(['console', 'main', 'renderer', 'all'])
|
|
2142
|
+
.optional()
|
|
2143
|
+
.describe('Type of logs to read'),
|
|
2144
|
+
lines: external_zod_namespaceObject.z.number().optional().describe('Number of recent lines to read (default: 100)'),
|
|
2145
|
+
follow: external_zod_namespaceObject.z.boolean().optional().describe('Whether to follow/tail the logs'),
|
|
2146
|
+
});
|
|
2147
|
+
const GetElectronWindowInfoSchema = external_zod_namespaceObject.z.object({
|
|
2148
|
+
includeChildren: external_zod_namespaceObject.z.boolean().optional().describe('Include child windows information'),
|
|
2149
|
+
});
|
|
2150
|
+
const ListElectronWindowsSchema = external_zod_namespaceObject.z.object({
|
|
2151
|
+
includeDevTools: external_zod_namespaceObject.z
|
|
2152
|
+
.boolean()
|
|
2153
|
+
.optional()
|
|
2154
|
+
.describe('Include DevTools windows in the list (default: false)'),
|
|
2155
|
+
});
|
|
2156
|
+
|
|
2157
|
+
;// ./src/tools.ts
|
|
2158
|
+
|
|
2159
|
+
|
|
2160
|
+
// Tool name enumeration
|
|
2161
|
+
var ToolName;
|
|
2162
|
+
(function (ToolName) {
|
|
2163
|
+
ToolName["SEND_COMMAND_TO_ELECTRON"] = "send_command_to_electron";
|
|
2164
|
+
ToolName["TAKE_SCREENSHOT"] = "take_screenshot";
|
|
2165
|
+
ToolName["READ_ELECTRON_LOGS"] = "read_electron_logs";
|
|
2166
|
+
ToolName["GET_ELECTRON_WINDOW_INFO"] = "get_electron_window_info";
|
|
2167
|
+
ToolName["LIST_ELECTRON_WINDOWS"] = "list_electron_windows";
|
|
2168
|
+
})(ToolName || (ToolName = {}));
|
|
2169
|
+
// Define tools available to the MCP server
|
|
2170
|
+
const tools = [
|
|
2171
|
+
{
|
|
2172
|
+
name: ToolName.GET_ELECTRON_WINDOW_INFO,
|
|
2173
|
+
description: 'Get information about running Electron applications and their windows. Automatically detects any Electron app with remote debugging enabled (port 9222).',
|
|
2174
|
+
inputSchema: (0,external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(GetElectronWindowInfoSchema),
|
|
2175
|
+
},
|
|
2176
|
+
{
|
|
2177
|
+
name: ToolName.TAKE_SCREENSHOT,
|
|
2178
|
+
description: 'Take a screenshot of any running Electron application window. Returns base64 image data for AI analysis. No files created unless outputPath is specified.',
|
|
2179
|
+
inputSchema: (0,external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(TakeScreenshotSchema),
|
|
2180
|
+
},
|
|
2181
|
+
{
|
|
2182
|
+
name: ToolName.SEND_COMMAND_TO_ELECTRON,
|
|
2183
|
+
description: `Send JavaScript commands to any running Electron application via Chrome DevTools Protocol.
|
|
2184
|
+
|
|
2185
|
+
Enhanced UI interaction commands:
|
|
2186
|
+
- 'find_elements': Analyze all interactive elements (buttons, inputs, selects) with their properties
|
|
2187
|
+
- 'click_by_text': Click elements by their visible text, aria-label, or title
|
|
2188
|
+
- 'click_by_selector': Securely click elements by CSS selector
|
|
2189
|
+
- 'fill_input': Fill input fields by selector, placeholder text, or associated label
|
|
2190
|
+
- 'select_option': Select dropdown options by value or text
|
|
2191
|
+
- 'send_keyboard_shortcut': Send keyboard shortcuts like 'Ctrl+N', 'Meta+N', 'Enter', 'Escape'
|
|
2192
|
+
- 'navigate_to_hash': Safely navigate to hash routes (e.g., '#create', '#settings')
|
|
2193
|
+
- 'get_page_structure': Get organized overview of page elements (buttons, inputs, selects, links)
|
|
2194
|
+
- 'debug_elements': Get debugging info about buttons and form elements on the page
|
|
2195
|
+
- 'verify_form_state': Check current form state and validation status
|
|
2196
|
+
- 'get_title', 'get_url', 'get_body_text': Basic page information
|
|
2197
|
+
- 'eval': Execute custom JavaScript code with enhanced error reporting
|
|
2198
|
+
|
|
2199
|
+
IMPORTANT: Arguments must be passed as an object with the correct properties:
|
|
2200
|
+
|
|
2201
|
+
Examples:
|
|
2202
|
+
- click_by_selector: {"selector": "button.submit-btn"}
|
|
2203
|
+
- click_by_text: {"text": "Submit"}
|
|
2204
|
+
- fill_input: {"placeholder": "Enter name", "value": "John Doe"}
|
|
2205
|
+
- fill_input: {"selector": "#email", "value": "user@example.com"}
|
|
2206
|
+
- send_keyboard_shortcut: {"text": "Enter"}
|
|
2207
|
+
- eval: {"code": "document.title"}
|
|
2208
|
+
|
|
2209
|
+
Use 'get_page_structure' or 'debug_elements' first to understand available elements, then use specific interaction commands.
|
|
2210
|
+
|
|
2211
|
+
Multi-window support:
|
|
2212
|
+
- targetId: Specify a CDP target ID to send commands to a specific window (exact match)
|
|
2213
|
+
- windowTitle: Specify a window title to target (case-insensitive partial match)
|
|
2214
|
+
- If neither is specified, commands are sent to the first available main window (backward compatible)
|
|
2215
|
+
- Use 'list_electron_windows' to see available windows and their IDs`,
|
|
2216
|
+
inputSchema: (0,external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(SendCommandToElectronSchema),
|
|
2217
|
+
},
|
|
2218
|
+
{
|
|
2219
|
+
name: ToolName.LIST_ELECTRON_WINDOWS,
|
|
2220
|
+
description: "List all available Electron window targets across all detected applications. Returns window IDs, titles, URLs, and ports. Use the returned IDs with send_command_to_electron's targetId parameter to target specific windows.",
|
|
2221
|
+
inputSchema: (0,external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(ListElectronWindowsSchema),
|
|
2222
|
+
},
|
|
2223
|
+
{
|
|
2224
|
+
name: ToolName.READ_ELECTRON_LOGS,
|
|
2225
|
+
description: 'Read console logs and output from running Electron applications. Useful for debugging and monitoring app behavior.',
|
|
2226
|
+
inputSchema: (0,external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(ReadElectronLogsSchema),
|
|
2227
|
+
},
|
|
2228
|
+
];
|
|
2229
|
+
|
|
2230
|
+
;// external "ws"
|
|
2231
|
+
const external_ws_namespaceObject = require("ws");
|
|
2232
|
+
var external_ws_default = /*#__PURE__*/__webpack_require__.n(external_ws_namespaceObject);
|
|
2233
|
+
;// external "child_process"
|
|
2234
|
+
const external_child_process_namespaceObject = require("child_process");
|
|
2235
|
+
;// external "util"
|
|
2236
|
+
const external_util_namespaceObject = require("util");
|
|
2237
|
+
;// ./src/utils/logger.ts
|
|
2238
|
+
/* eslint-disable no-console */
|
|
2239
|
+
var LogLevel;
|
|
2240
|
+
(function (LogLevel) {
|
|
2241
|
+
LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
|
|
2242
|
+
LogLevel[LogLevel["WARN"] = 1] = "WARN";
|
|
2243
|
+
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
2244
|
+
LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
|
|
2245
|
+
})(LogLevel || (LogLevel = {}));
|
|
2246
|
+
class Logger {
|
|
2247
|
+
static instance;
|
|
2248
|
+
level;
|
|
2249
|
+
constructor(level = LogLevel.INFO) {
|
|
2250
|
+
this.level = level;
|
|
2251
|
+
}
|
|
2252
|
+
static getInstance() {
|
|
2253
|
+
if (!Logger.instance) {
|
|
2254
|
+
// Check environment variable for log level
|
|
2255
|
+
const envLevel = process.env.MCP_LOG_LEVEL?.toUpperCase();
|
|
2256
|
+
let level = LogLevel.WARN;
|
|
2257
|
+
switch (envLevel) {
|
|
2258
|
+
case 'ERROR':
|
|
2259
|
+
level = LogLevel.ERROR;
|
|
2260
|
+
break;
|
|
2261
|
+
case 'WARN':
|
|
2262
|
+
level = LogLevel.WARN;
|
|
2263
|
+
break;
|
|
2264
|
+
case 'INFO':
|
|
2265
|
+
level = LogLevel.INFO;
|
|
2266
|
+
break;
|
|
2267
|
+
case 'DEBUG':
|
|
2268
|
+
level = LogLevel.DEBUG;
|
|
2269
|
+
break;
|
|
2270
|
+
}
|
|
2271
|
+
Logger.instance = new Logger(level);
|
|
2272
|
+
}
|
|
2273
|
+
return Logger.instance;
|
|
2274
|
+
}
|
|
2275
|
+
setLevel(level) {
|
|
2276
|
+
this.level = level;
|
|
2277
|
+
}
|
|
2278
|
+
error(message, ...args) {
|
|
2279
|
+
if (this.level >= LogLevel.ERROR) {
|
|
2280
|
+
console.error(`[MCP] ERROR: ${message}`, ...args);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
warn(message, ...args) {
|
|
2284
|
+
if (this.level >= LogLevel.WARN) {
|
|
2285
|
+
console.error(`[MCP] WARN: ${message}`, ...args);
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
info(message, ...args) {
|
|
2289
|
+
if (this.level >= LogLevel.INFO) {
|
|
2290
|
+
console.error(`[MCP] INFO: ${message}`, ...args);
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
debug(message, ...args) {
|
|
2294
|
+
if (this.level >= LogLevel.DEBUG) {
|
|
2295
|
+
console.error(`[MCP] DEBUG: ${message}`, ...args);
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
// Helper method to check if a certain level is enabled
|
|
2299
|
+
isEnabled(level) {
|
|
2300
|
+
return this.level >= level;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
// Export singleton instance
|
|
2304
|
+
const logger = Logger.getInstance();
|
|
2305
|
+
|
|
2306
|
+
;// ./src/utils/electron-discovery.ts
|
|
2307
|
+
|
|
2308
|
+
|
|
2309
|
+
|
|
2310
|
+
/**
|
|
2311
|
+
* Scan for running Electron applications with DevTools enabled
|
|
2312
|
+
*/
|
|
2313
|
+
async function scanForElectronApps() {
|
|
2314
|
+
logger.debug('Scanning for running Electron applications...');
|
|
2315
|
+
// Extended port range to include test apps and common custom ports
|
|
2316
|
+
const commonPorts = [
|
|
2317
|
+
9222,
|
|
2318
|
+
9223,
|
|
2319
|
+
9224,
|
|
2320
|
+
9225, // Default ports
|
|
2321
|
+
9200,
|
|
2322
|
+
9201,
|
|
2323
|
+
9202,
|
|
2324
|
+
9203,
|
|
2325
|
+
9204,
|
|
2326
|
+
9205, // Security test range
|
|
2327
|
+
9300,
|
|
2328
|
+
9301,
|
|
2329
|
+
9302,
|
|
2330
|
+
9303,
|
|
2331
|
+
9304,
|
|
2332
|
+
9305, // Integration test range
|
|
2333
|
+
9400,
|
|
2334
|
+
9401,
|
|
2335
|
+
9402,
|
|
2336
|
+
9403,
|
|
2337
|
+
9404,
|
|
2338
|
+
9405, // Additional range
|
|
2339
|
+
];
|
|
2340
|
+
const foundApps = [];
|
|
2341
|
+
for (const port of commonPorts) {
|
|
2342
|
+
try {
|
|
2343
|
+
const response = await fetch(`http://localhost:${port}/json`, {
|
|
2344
|
+
signal: AbortSignal.timeout(1000),
|
|
2345
|
+
});
|
|
2346
|
+
if (response.ok) {
|
|
2347
|
+
const targets = await response.json();
|
|
2348
|
+
const pageTargets = targets.filter((target) => target.type === 'page');
|
|
2349
|
+
if (pageTargets.length > 0) {
|
|
2350
|
+
foundApps.push({
|
|
2351
|
+
port,
|
|
2352
|
+
targets: pageTargets,
|
|
2353
|
+
});
|
|
2354
|
+
logger.debug(`Found Electron app on port ${port} with ${pageTargets.length} windows`);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
catch {
|
|
2359
|
+
// Continue to next port
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
return foundApps;
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Get detailed process information for running Electron applications
|
|
2366
|
+
*/
|
|
2367
|
+
async function getElectronProcessInfo() {
|
|
2368
|
+
const execAsync = (0,external_util_namespaceObject.promisify)(external_child_process_namespaceObject.exec);
|
|
2369
|
+
try {
|
|
2370
|
+
const { stdout } = await execAsync("ps aux | grep -i electron | grep -v grep | grep -v 'Visual Studio Code'");
|
|
2371
|
+
const electronProcesses = stdout
|
|
2372
|
+
.trim()
|
|
2373
|
+
.split('\n')
|
|
2374
|
+
.filter((line) => line.includes('electron'))
|
|
2375
|
+
.map((line) => {
|
|
2376
|
+
const parts = line.trim().split(/\s+/);
|
|
2377
|
+
return {
|
|
2378
|
+
pid: parts[1],
|
|
2379
|
+
cpu: parts[2],
|
|
2380
|
+
memory: parts[3],
|
|
2381
|
+
command: parts.slice(10).join(' '),
|
|
2382
|
+
};
|
|
2383
|
+
});
|
|
2384
|
+
return { electronProcesses };
|
|
2385
|
+
}
|
|
2386
|
+
catch (error) {
|
|
2387
|
+
logger.debug('Could not get process info:', error);
|
|
2388
|
+
return {};
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
/**
|
|
2392
|
+
* Find the main target from a list of targets
|
|
2393
|
+
*/
|
|
2394
|
+
function findMainTarget(targets) {
|
|
2395
|
+
return (targets.find((target) => target.type === 'page' && !target.title.includes('DevTools')) ||
|
|
2396
|
+
targets.find((target) => target.type === 'page'));
|
|
2397
|
+
}
|
|
2398
|
+
/**
|
|
2399
|
+
* List all available Electron window targets across all detected apps.
|
|
2400
|
+
* @param includeDevTools - Whether to include DevTools windows (default: false)
|
|
2401
|
+
* @returns Array of window targets with id, title, url, port, and type
|
|
2402
|
+
*/
|
|
2403
|
+
async function listElectronWindows(includeDevTools = false) {
|
|
2404
|
+
const foundApps = await scanForElectronApps();
|
|
2405
|
+
const windows = [];
|
|
2406
|
+
for (const app of foundApps) {
|
|
2407
|
+
for (const target of app.targets) {
|
|
2408
|
+
// Filter out DevTools windows unless explicitly requested
|
|
2409
|
+
if (!includeDevTools && target.url && target.url.startsWith('devtools://')) {
|
|
2410
|
+
continue;
|
|
2411
|
+
}
|
|
2412
|
+
windows.push({
|
|
2413
|
+
id: target.id,
|
|
2414
|
+
title: target.title || '',
|
|
2415
|
+
url: target.url || '',
|
|
2416
|
+
port: app.port,
|
|
2417
|
+
type: target.type || 'page',
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
return windows;
|
|
2422
|
+
}
|
|
2423
|
+
/**
|
|
2424
|
+
* Get window information from any running Electron app
|
|
2425
|
+
*/
|
|
2426
|
+
async function getElectronWindowInfo(includeChildren = false) {
|
|
2427
|
+
try {
|
|
2428
|
+
const foundApps = await scanForElectronApps();
|
|
2429
|
+
if (foundApps.length === 0) {
|
|
2430
|
+
return {
|
|
2431
|
+
platform: process.platform,
|
|
2432
|
+
windows: [],
|
|
2433
|
+
totalTargets: 0,
|
|
2434
|
+
electronTargets: 0,
|
|
2435
|
+
message: 'No Electron applications found with remote debugging enabled',
|
|
2436
|
+
automationReady: false,
|
|
2437
|
+
};
|
|
2438
|
+
}
|
|
2439
|
+
// Use the first found app
|
|
2440
|
+
const app = foundApps[0];
|
|
2441
|
+
const windows = app.targets.map((target) => ({
|
|
2442
|
+
id: target.id,
|
|
2443
|
+
title: target.title,
|
|
2444
|
+
url: target.url,
|
|
2445
|
+
type: target.type,
|
|
2446
|
+
description: target.description || '',
|
|
2447
|
+
webSocketDebuggerUrl: target.webSocketDebuggerUrl,
|
|
2448
|
+
}));
|
|
2449
|
+
// Get additional process information
|
|
2450
|
+
const processInfo = await getElectronProcessInfo();
|
|
2451
|
+
return {
|
|
2452
|
+
platform: process.platform,
|
|
2453
|
+
devToolsPort: app.port,
|
|
2454
|
+
windows: includeChildren
|
|
2455
|
+
? windows
|
|
2456
|
+
: windows.filter((w) => !w.title.includes('DevTools')),
|
|
2457
|
+
totalTargets: windows.length,
|
|
2458
|
+
electronTargets: windows.length,
|
|
2459
|
+
processInfo,
|
|
2460
|
+
message: `Found running Electron application with ${windows.length} windows on port ${app.port}`,
|
|
2461
|
+
automationReady: true,
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2464
|
+
catch (error) {
|
|
2465
|
+
logger.error('Failed to scan for applications:', error);
|
|
2466
|
+
return {
|
|
2467
|
+
platform: process.platform,
|
|
2468
|
+
windows: [],
|
|
2469
|
+
totalTargets: 0,
|
|
2470
|
+
electronTargets: 0,
|
|
2471
|
+
message: `Failed to scan for Electron applications: ${error instanceof Error ? error.message : String(error)}`,
|
|
2472
|
+
automationReady: false,
|
|
2473
|
+
};
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
|
|
2477
|
+
;// ./src/utils/electron-connection.ts
|
|
2478
|
+
|
|
2479
|
+
|
|
2480
|
+
|
|
2481
|
+
/**
|
|
2482
|
+
* Find and connect to a running Electron application.
|
|
2483
|
+
* @param options - Optional targeting options to select a specific window
|
|
2484
|
+
* @returns The DevTools target matching the given options
|
|
2485
|
+
* @example
|
|
2486
|
+
* findElectronTarget() // first available main window
|
|
2487
|
+
* findElectronTarget({ targetId: 'ABC123' }) // exact ID match
|
|
2488
|
+
* findElectronTarget({ windowTitle: 'Settings' }) // partial title match
|
|
2489
|
+
*/
|
|
2490
|
+
async function findElectronTarget(options) {
|
|
2491
|
+
logger.debug('Looking for running Electron applications...');
|
|
2492
|
+
const foundApps = await scanForElectronApps();
|
|
2493
|
+
if (foundApps.length === 0) {
|
|
2494
|
+
throw new Error('No running Electron application found with remote debugging enabled. Start your app with: electron . --remote-debugging-port=9222');
|
|
2495
|
+
}
|
|
2496
|
+
// If targetId is specified, search all apps for exact ID match
|
|
2497
|
+
if (options?.targetId) {
|
|
2498
|
+
for (const app of foundApps) {
|
|
2499
|
+
const match = app.targets.find((t) => t.id === options.targetId);
|
|
2500
|
+
if (match) {
|
|
2501
|
+
logger.debug(`Found target by ID "${options.targetId}" on port ${app.port}`);
|
|
2502
|
+
return {
|
|
2503
|
+
id: match.id,
|
|
2504
|
+
title: match.title,
|
|
2505
|
+
url: match.url,
|
|
2506
|
+
webSocketDebuggerUrl: match.webSocketDebuggerUrl,
|
|
2507
|
+
type: match.type,
|
|
2508
|
+
};
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
throw new Error(`No window found with targetId "${options.targetId}". Use list_electron_windows to see available targets.`);
|
|
2512
|
+
}
|
|
2513
|
+
// If windowTitle is specified, search all apps for case-insensitive partial match
|
|
2514
|
+
if (options?.windowTitle) {
|
|
2515
|
+
const searchTitle = options.windowTitle.toLowerCase();
|
|
2516
|
+
for (const app of foundApps) {
|
|
2517
|
+
const match = app.targets.find((t) => t.title && t.title.toLowerCase().includes(searchTitle));
|
|
2518
|
+
if (match) {
|
|
2519
|
+
logger.debug(`Found target by title "${options.windowTitle}" on port ${app.port}`);
|
|
2520
|
+
return {
|
|
2521
|
+
id: match.id,
|
|
2522
|
+
title: match.title,
|
|
2523
|
+
url: match.url,
|
|
2524
|
+
webSocketDebuggerUrl: match.webSocketDebuggerUrl,
|
|
2525
|
+
type: match.type,
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
throw new Error(`No window found with title matching "${options.windowTitle}". Use list_electron_windows to see available targets.`);
|
|
2530
|
+
}
|
|
2531
|
+
// Default: use first app's main target (backward compatible)
|
|
2532
|
+
const app = foundApps[0];
|
|
2533
|
+
const mainTarget = findMainTarget(app.targets);
|
|
2534
|
+
if (!mainTarget) {
|
|
2535
|
+
throw new Error('No suitable target found in Electron application');
|
|
2536
|
+
}
|
|
2537
|
+
logger.debug(`Found Electron app on port ${app.port}: ${mainTarget.title}`);
|
|
2538
|
+
return {
|
|
2539
|
+
id: mainTarget.id,
|
|
2540
|
+
title: mainTarget.title,
|
|
2541
|
+
url: mainTarget.url,
|
|
2542
|
+
webSocketDebuggerUrl: mainTarget.webSocketDebuggerUrl,
|
|
2543
|
+
type: mainTarget.type,
|
|
2544
|
+
};
|
|
2545
|
+
}
|
|
2546
|
+
/**
|
|
2547
|
+
* Execute JavaScript code in an Electron application via Chrome DevTools Protocol
|
|
2548
|
+
*/
|
|
2549
|
+
async function executeInElectron(javascriptCode, target) {
|
|
2550
|
+
const targetInfo = target || (await findElectronTarget());
|
|
2551
|
+
if (!targetInfo.webSocketDebuggerUrl) {
|
|
2552
|
+
throw new Error('No WebSocket debugger URL available');
|
|
2553
|
+
}
|
|
2554
|
+
return new Promise((resolve, reject) => {
|
|
2555
|
+
const ws = new (external_ws_default())(targetInfo.webSocketDebuggerUrl);
|
|
2556
|
+
const messageId = Math.floor(Math.random() * 1000000);
|
|
2557
|
+
const timeout = setTimeout(() => {
|
|
2558
|
+
ws.close();
|
|
2559
|
+
reject(new Error('Command execution timeout (10s)'));
|
|
2560
|
+
}, 10000);
|
|
2561
|
+
ws.on('open', () => {
|
|
2562
|
+
logger.debug(`Connected to ${targetInfo.title} via WebSocket`);
|
|
2563
|
+
// Enable Runtime domain first
|
|
2564
|
+
ws.send(JSON.stringify({
|
|
2565
|
+
id: 1,
|
|
2566
|
+
method: 'Runtime.enable',
|
|
2567
|
+
}));
|
|
2568
|
+
// Send Runtime.evaluate command
|
|
2569
|
+
const message = {
|
|
2570
|
+
id: messageId,
|
|
2571
|
+
method: 'Runtime.evaluate',
|
|
2572
|
+
params: {
|
|
2573
|
+
expression: javascriptCode,
|
|
2574
|
+
returnByValue: true,
|
|
2575
|
+
awaitPromise: false,
|
|
2576
|
+
},
|
|
2577
|
+
};
|
|
2578
|
+
logger.debug(`Executing JavaScript code...`);
|
|
2579
|
+
ws.send(JSON.stringify(message));
|
|
2580
|
+
});
|
|
2581
|
+
ws.on('message', (data) => {
|
|
2582
|
+
try {
|
|
2583
|
+
const response = JSON.parse(data.toString());
|
|
2584
|
+
// Filter out noisy CDP events to reduce log spam
|
|
2585
|
+
const FILTERED_CDP_METHODS = [
|
|
2586
|
+
'Runtime.executionContextCreated',
|
|
2587
|
+
'Runtime.consoleAPICalled',
|
|
2588
|
+
'Console.messageAdded',
|
|
2589
|
+
'Page.frameNavigated',
|
|
2590
|
+
'Page.loadEventFired',
|
|
2591
|
+
];
|
|
2592
|
+
// Only log CDP events if debug level is enabled and they're not filtered
|
|
2593
|
+
if (logger.isEnabled(3) &&
|
|
2594
|
+
(!response.method || !FILTERED_CDP_METHODS.includes(response.method))) {
|
|
2595
|
+
logger.debug(`CDP Response for message ${messageId}:`, JSON.stringify(response, null, 2));
|
|
2596
|
+
}
|
|
2597
|
+
if (response.id === messageId) {
|
|
2598
|
+
clearTimeout(timeout);
|
|
2599
|
+
ws.close();
|
|
2600
|
+
if (response.error) {
|
|
2601
|
+
logger.error(`DevTools Protocol error:`, response.error);
|
|
2602
|
+
reject(new Error(`DevTools Protocol error: ${response.error.message}`));
|
|
2603
|
+
}
|
|
2604
|
+
else if (response.result) {
|
|
2605
|
+
const result = response.result.result;
|
|
2606
|
+
logger.debug(`Execution result type: ${result?.type}, value:`, result?.value);
|
|
2607
|
+
if (result.type === 'string') {
|
|
2608
|
+
resolve(`✅ Command executed: ${result.value}`);
|
|
2609
|
+
}
|
|
2610
|
+
else if (result.type === 'number') {
|
|
2611
|
+
resolve(`✅ Result: ${result.value}`);
|
|
2612
|
+
}
|
|
2613
|
+
else if (result.type === 'boolean') {
|
|
2614
|
+
resolve(`✅ Result: ${result.value}`);
|
|
2615
|
+
}
|
|
2616
|
+
else if (result.type === 'undefined') {
|
|
2617
|
+
resolve(`✅ Command executed successfully`);
|
|
2618
|
+
}
|
|
2619
|
+
else if (result.type === 'object') {
|
|
2620
|
+
if (result.value === null) {
|
|
2621
|
+
resolve(`✅ Result: null`);
|
|
2622
|
+
}
|
|
2623
|
+
else if (result.value === undefined) {
|
|
2624
|
+
resolve(`✅ Result: undefined`);
|
|
2625
|
+
}
|
|
2626
|
+
else {
|
|
2627
|
+
try {
|
|
2628
|
+
resolve(`✅ Result: ${JSON.stringify(result.value, null, 2)}`);
|
|
2629
|
+
}
|
|
2630
|
+
catch {
|
|
2631
|
+
resolve(`✅ Result: [Object - could not serialize: ${result.className || result.objectId || 'unknown'}]`);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
else {
|
|
2636
|
+
resolve(`✅ Result type ${result.type}: ${result.description || 'no description'}`);
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
else {
|
|
2640
|
+
logger.debug(`No result in response:`, response);
|
|
2641
|
+
resolve(`✅ Command sent successfully`);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
catch (error) {
|
|
2646
|
+
// Only treat parsing errors as warnings, not errors
|
|
2647
|
+
logger.warn(`Failed to parse CDP response:`, error);
|
|
2648
|
+
}
|
|
2649
|
+
});
|
|
2650
|
+
ws.on('error', (error) => {
|
|
2651
|
+
clearTimeout(timeout);
|
|
2652
|
+
reject(new Error(`WebSocket error: ${error.message}`));
|
|
2653
|
+
});
|
|
2654
|
+
});
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Connect to Electron app for real-time log monitoring
|
|
2658
|
+
*/
|
|
2659
|
+
async function connectForLogs(target, onLog) {
|
|
2660
|
+
const targetInfo = target || (await findElectronTarget());
|
|
2661
|
+
if (!targetInfo.webSocketDebuggerUrl) {
|
|
2662
|
+
throw new Error('No WebSocket debugger URL available for log connection');
|
|
2663
|
+
}
|
|
2664
|
+
return new Promise((resolve, reject) => {
|
|
2665
|
+
const ws = new (external_ws_default())(targetInfo.webSocketDebuggerUrl);
|
|
2666
|
+
ws.on('open', () => {
|
|
2667
|
+
logger.debug(`Connected for log monitoring to: ${targetInfo.title}`);
|
|
2668
|
+
// Enable Runtime and Console domains
|
|
2669
|
+
ws.send(JSON.stringify({ id: 1, method: 'Runtime.enable' }));
|
|
2670
|
+
ws.send(JSON.stringify({ id: 2, method: 'Console.enable' }));
|
|
2671
|
+
resolve(ws);
|
|
2672
|
+
});
|
|
2673
|
+
ws.on('message', (data) => {
|
|
2674
|
+
try {
|
|
2675
|
+
const response = JSON.parse(data.toString());
|
|
2676
|
+
if (response.method === 'Console.messageAdded') {
|
|
2677
|
+
const msg = response.params.message;
|
|
2678
|
+
const timestamp = new Date().toISOString();
|
|
2679
|
+
const logEntry = `[${timestamp}] ${msg.level.toUpperCase()}: ${msg.text}`;
|
|
2680
|
+
onLog?.(logEntry);
|
|
2681
|
+
}
|
|
2682
|
+
else if (response.method === 'Runtime.consoleAPICalled') {
|
|
2683
|
+
const call = response.params;
|
|
2684
|
+
const timestamp = new Date().toISOString();
|
|
2685
|
+
const args = call.args?.map((arg) => arg.value || arg.description).join(' ') || '';
|
|
2686
|
+
const logEntry = `[${timestamp}] ${call.type.toUpperCase()}: ${args}`;
|
|
2687
|
+
onLog?.(logEntry);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
catch (error) {
|
|
2691
|
+
logger.warn(`Failed to parse log message:`, error);
|
|
2692
|
+
}
|
|
2693
|
+
});
|
|
2694
|
+
ws.on('error', (error) => {
|
|
2695
|
+
reject(new Error(`WebSocket error: ${error.message}`));
|
|
2696
|
+
});
|
|
2697
|
+
});
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
;// ./src/utils/electron-commands.ts
|
|
2701
|
+
/**
|
|
2702
|
+
* Enhanced Electron interaction commands for React-based applications
|
|
2703
|
+
* Addresses common issues with form interactions, event handling, and state management
|
|
2704
|
+
*/
|
|
2705
|
+
/**
|
|
2706
|
+
* Securely escape text input for JavaScript code generation
|
|
2707
|
+
*/
|
|
2708
|
+
function escapeJavaScriptString(input) {
|
|
2709
|
+
// Use JSON.stringify for proper escaping of quotes, newlines, and special characters
|
|
2710
|
+
return JSON.stringify(input);
|
|
2711
|
+
}
|
|
2712
|
+
/**
|
|
2713
|
+
* Validate text input for potential security issues
|
|
2714
|
+
*/
|
|
2715
|
+
function validateTextInput(text) {
|
|
2716
|
+
const warnings = [];
|
|
2717
|
+
let sanitized = text;
|
|
2718
|
+
// Check for suspicious patterns
|
|
2719
|
+
if (text.includes('javascript:'))
|
|
2720
|
+
warnings.push('Contains javascript: protocol');
|
|
2721
|
+
if (text.includes('<script'))
|
|
2722
|
+
warnings.push('Contains script tags');
|
|
2723
|
+
if (text.match(/['"]\s*;\s*/))
|
|
2724
|
+
warnings.push('Contains potential code injection');
|
|
2725
|
+
if (text.length > 1000)
|
|
2726
|
+
warnings.push('Input text is unusually long');
|
|
2727
|
+
// Basic sanitization - remove potentially dangerous content
|
|
2728
|
+
sanitized = sanitized.replace(/javascript:/gi, '');
|
|
2729
|
+
sanitized = sanitized.replace(/<script[^>]*>.*?<\/script>/gi, '');
|
|
2730
|
+
sanitized = sanitized.substring(0, 1000); // Limit length
|
|
2731
|
+
return {
|
|
2732
|
+
isValid: warnings.length === 0,
|
|
2733
|
+
sanitized,
|
|
2734
|
+
warnings,
|
|
2735
|
+
};
|
|
2736
|
+
}
|
|
2737
|
+
/**
|
|
2738
|
+
* Generate the enhanced find_elements command with deep DOM analysis
|
|
2739
|
+
*/
|
|
2740
|
+
function generateFindElementsCommand() {
|
|
2741
|
+
return `
|
|
2742
|
+
(function() {
|
|
2743
|
+
// Deep DOM analysis functions
|
|
2744
|
+
function analyzeElement(el) {
|
|
2745
|
+
const rect = el.getBoundingClientRect();
|
|
2746
|
+
const style = getComputedStyle(el);
|
|
2747
|
+
|
|
2748
|
+
return {
|
|
2749
|
+
tag: el.tagName.toLowerCase(),
|
|
2750
|
+
text: (el.textContent || '').trim().substring(0, 100),
|
|
2751
|
+
id: el.id || '',
|
|
2752
|
+
className: el.className || '',
|
|
2753
|
+
name: el.name || '',
|
|
2754
|
+
placeholder: el.placeholder || '',
|
|
2755
|
+
type: el.type || '',
|
|
2756
|
+
value: el.value || '',
|
|
2757
|
+
ariaLabel: el.getAttribute('aria-label') || '',
|
|
2758
|
+
ariaRole: el.getAttribute('role') || '',
|
|
2759
|
+
title: el.title || '',
|
|
2760
|
+
href: el.href || '',
|
|
2761
|
+
src: el.src || '',
|
|
2762
|
+
alt: el.alt || '',
|
|
2763
|
+
position: {
|
|
2764
|
+
x: Math.round(rect.left),
|
|
2765
|
+
y: Math.round(rect.top),
|
|
2766
|
+
width: Math.round(rect.width),
|
|
2767
|
+
height: Math.round(rect.height)
|
|
2768
|
+
},
|
|
2769
|
+
isVisible: rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden' && style.opacity > 0,
|
|
2770
|
+
isInteractive: isInteractiveElement(el),
|
|
2771
|
+
zIndex: parseInt(style.zIndex) || 0,
|
|
2772
|
+
backgroundColor: style.backgroundColor,
|
|
2773
|
+
color: style.color,
|
|
2774
|
+
fontSize: style.fontSize,
|
|
2775
|
+
fontWeight: style.fontWeight,
|
|
2776
|
+
cursor: style.cursor,
|
|
2777
|
+
context: getElementContext(el),
|
|
2778
|
+
selector: generateSelector(el),
|
|
2779
|
+
xpath: generateXPath(el)
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
function isInteractiveElement(el) {
|
|
2784
|
+
const interactiveTags = ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'];
|
|
2785
|
+
const interactiveTypes = ['button', 'submit', 'reset', 'checkbox', 'radio'];
|
|
2786
|
+
const interactiveRoles = ['button', 'link', 'menuitem', 'tab', 'option'];
|
|
2787
|
+
|
|
2788
|
+
return interactiveTags.includes(el.tagName) ||
|
|
2789
|
+
interactiveTypes.includes(el.type) ||
|
|
2790
|
+
interactiveRoles.includes(el.getAttribute('role')) ||
|
|
2791
|
+
el.hasAttribute('onclick') ||
|
|
2792
|
+
el.hasAttribute('onsubmit') ||
|
|
2793
|
+
el.getAttribute('contenteditable') === 'true' ||
|
|
2794
|
+
getComputedStyle(el).cursor === 'pointer';
|
|
2795
|
+
}
|
|
2796
|
+
|
|
2797
|
+
function getElementContext(el) {
|
|
2798
|
+
const context = [];
|
|
2799
|
+
|
|
2800
|
+
// Get form context
|
|
2801
|
+
const form = el.closest('form');
|
|
2802
|
+
if (form) {
|
|
2803
|
+
const formTitle = form.querySelector('h1, h2, h3, h4, h5, h6, .title');
|
|
2804
|
+
if (formTitle) context.push('Form: ' + formTitle.textContent.trim().substring(0, 50));
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// Get parent container context
|
|
2808
|
+
const container = el.closest('section, article, div[class*="container"], div[class*="card"], div[class*="panel"]');
|
|
2809
|
+
if (container && container !== form) {
|
|
2810
|
+
const heading = container.querySelector('h1, h2, h3, h4, h5, h6, .title, .heading');
|
|
2811
|
+
if (heading) context.push('Container: ' + heading.textContent.trim().substring(0, 50));
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
// Get nearby labels
|
|
2815
|
+
const label = findElementLabel(el);
|
|
2816
|
+
if (label) context.push('Label: ' + label.substring(0, 50));
|
|
2817
|
+
|
|
2818
|
+
return context.join(' | ');
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
function findElementLabel(el) {
|
|
2822
|
+
// For inputs, find associated label
|
|
2823
|
+
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') {
|
|
2824
|
+
if (el.id) {
|
|
2825
|
+
const label = document.querySelector(\`label[for="\${el.id}"]\`);
|
|
2826
|
+
if (label) return label.textContent.trim();
|
|
2827
|
+
}
|
|
2828
|
+
|
|
2829
|
+
// Check if nested in label
|
|
2830
|
+
const parentLabel = el.closest('label');
|
|
2831
|
+
if (parentLabel) return parentLabel.textContent.trim();
|
|
2832
|
+
|
|
2833
|
+
// Check aria-labelledby
|
|
2834
|
+
const labelledBy = el.getAttribute('aria-labelledby');
|
|
2835
|
+
if (labelledBy) {
|
|
2836
|
+
const labelEl = document.getElementById(labelledBy);
|
|
2837
|
+
if (labelEl) return labelEl.textContent.trim();
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
return '';
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
function generateSelector(el) {
|
|
2845
|
+
// Generate a robust CSS selector
|
|
2846
|
+
if (el.id) return '#' + el.id;
|
|
2847
|
+
|
|
2848
|
+
let selector = el.tagName.toLowerCase();
|
|
2849
|
+
|
|
2850
|
+
if (el.className) {
|
|
2851
|
+
const classes = el.className.split(' ').filter(c => c && !c.match(/^(ng-|v-|_)/));
|
|
2852
|
+
if (classes.length > 0) {
|
|
2853
|
+
selector += '.' + classes.slice(0, 3).join('.');
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
// Add attribute selectors for better specificity
|
|
2858
|
+
if (el.name) selector += \`[name="\${el.name}"]\`;
|
|
2859
|
+
if (el.type && el.tagName === 'INPUT') selector += \`[type="\${el.type}"]\`;
|
|
2860
|
+
if (el.placeholder) selector += \`[placeholder*="\${el.placeholder.substring(0, 20)}"]\`;
|
|
2861
|
+
|
|
2862
|
+
return selector;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
function generateXPath(el) {
|
|
2866
|
+
if (el.id) return \`//*[@id="\${el.id}"]\`;
|
|
2867
|
+
|
|
2868
|
+
let path = '';
|
|
2869
|
+
let current = el;
|
|
2870
|
+
|
|
2871
|
+
while (current && current.nodeType === Node.ELEMENT_NODE && current !== document.body) {
|
|
2872
|
+
let selector = current.tagName.toLowerCase();
|
|
2873
|
+
|
|
2874
|
+
if (current.id) {
|
|
2875
|
+
path = \`//*[@id="\${current.id}"]\` + path;
|
|
2876
|
+
break;
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
const siblings = Array.from(current.parentNode?.children || []).filter(
|
|
2880
|
+
sibling => sibling.tagName === current.tagName
|
|
2881
|
+
);
|
|
2882
|
+
|
|
2883
|
+
if (siblings.length > 1) {
|
|
2884
|
+
const index = siblings.indexOf(current) + 1;
|
|
2885
|
+
selector += \`[\${index}]\`;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
path = '/' + selector + path;
|
|
2889
|
+
current = current.parentElement;
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2892
|
+
return path || '//body' + path;
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
// Categorize elements by type
|
|
2896
|
+
const analysis = {
|
|
2897
|
+
clickable: [],
|
|
2898
|
+
inputs: [],
|
|
2899
|
+
links: [],
|
|
2900
|
+
images: [],
|
|
2901
|
+
text: [],
|
|
2902
|
+
containers: [],
|
|
2903
|
+
metadata: {
|
|
2904
|
+
totalElements: 0,
|
|
2905
|
+
visibleElements: 0,
|
|
2906
|
+
interactiveElements: 0,
|
|
2907
|
+
pageTitle: document.title,
|
|
2908
|
+
pageUrl: window.location.href,
|
|
2909
|
+
viewport: {
|
|
2910
|
+
width: window.innerWidth,
|
|
2911
|
+
height: window.innerHeight
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
};
|
|
2915
|
+
|
|
2916
|
+
// Analyze all elements
|
|
2917
|
+
const allElements = document.querySelectorAll('*');
|
|
2918
|
+
analysis.metadata.totalElements = allElements.length;
|
|
2919
|
+
|
|
2920
|
+
for (let el of allElements) {
|
|
2921
|
+
const elementAnalysis = analyzeElement(el);
|
|
2922
|
+
|
|
2923
|
+
if (!elementAnalysis.isVisible) continue;
|
|
2924
|
+
analysis.metadata.visibleElements++;
|
|
2925
|
+
|
|
2926
|
+
if (elementAnalysis.isInteractive) {
|
|
2927
|
+
analysis.metadata.interactiveElements++;
|
|
2928
|
+
|
|
2929
|
+
// Categorize clickable elements
|
|
2930
|
+
if (['button', 'a', 'input'].includes(elementAnalysis.tag) ||
|
|
2931
|
+
['button', 'submit'].includes(elementAnalysis.type) ||
|
|
2932
|
+
elementAnalysis.ariaRole === 'button') {
|
|
2933
|
+
analysis.clickable.push(elementAnalysis);
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
// Categorize inputs
|
|
2938
|
+
if (['input', 'textarea', 'select'].includes(elementAnalysis.tag)) {
|
|
2939
|
+
analysis.inputs.push(elementAnalysis);
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
// Categorize links
|
|
2943
|
+
if (elementAnalysis.tag === 'a' && elementAnalysis.href) {
|
|
2944
|
+
analysis.links.push(elementAnalysis);
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
// Categorize images
|
|
2948
|
+
if (elementAnalysis.tag === 'img') {
|
|
2949
|
+
analysis.images.push(elementAnalysis);
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
// Categorize text elements with significant content
|
|
2953
|
+
if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'div'].includes(elementAnalysis.tag) &&
|
|
2954
|
+
elementAnalysis.text.length > 10 && elementAnalysis.text.length < 200) {
|
|
2955
|
+
analysis.text.push(elementAnalysis);
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
// Categorize important containers
|
|
2959
|
+
if (['form', 'section', 'article', 'main', 'nav', 'header', 'footer'].includes(elementAnalysis.tag) ||
|
|
2960
|
+
elementAnalysis.className.match(/(container|wrapper|card|panel|modal|dialog)/i)) {
|
|
2961
|
+
analysis.containers.push(elementAnalysis);
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
// Limit results to prevent overwhelming output
|
|
2966
|
+
const maxResults = 20;
|
|
2967
|
+
Object.keys(analysis).forEach(key => {
|
|
2968
|
+
if (Array.isArray(analysis[key]) && analysis[key].length > maxResults) {
|
|
2969
|
+
analysis[key] = analysis[key].slice(0, maxResults);
|
|
2970
|
+
}
|
|
2971
|
+
});
|
|
2972
|
+
|
|
2973
|
+
return JSON.stringify(analysis, null, 2);
|
|
2974
|
+
})()
|
|
2975
|
+
`;
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* Generate the enhanced click_by_text command with improved element scoring
|
|
2979
|
+
*/
|
|
2980
|
+
function generateClickByTextCommand(text) {
|
|
2981
|
+
// Validate and sanitize input text
|
|
2982
|
+
const validation = validateTextInput(text);
|
|
2983
|
+
if (!validation.isValid) {
|
|
2984
|
+
return `(function() { return "Security validation failed: ${validation.warnings.join(', ')}"; })()`;
|
|
2985
|
+
}
|
|
2986
|
+
// Escape the text to prevent JavaScript injection
|
|
2987
|
+
const escapedText = escapeJavaScriptString(validation.sanitized);
|
|
2988
|
+
return `
|
|
2989
|
+
(function() {
|
|
2990
|
+
const targetText = ${escapedText}; // Safe: JSON.stringify escapes quotes and special chars
|
|
2991
|
+
|
|
2992
|
+
// Deep DOM analysis function
|
|
2993
|
+
function analyzeElement(el) {
|
|
2994
|
+
const rect = el.getBoundingClientRect();
|
|
2995
|
+
const style = getComputedStyle(el);
|
|
2996
|
+
|
|
2997
|
+
return {
|
|
2998
|
+
element: el,
|
|
2999
|
+
text: (el.textContent || '').trim(),
|
|
3000
|
+
ariaLabel: el.getAttribute('aria-label') || '',
|
|
3001
|
+
title: el.title || '',
|
|
3002
|
+
role: el.getAttribute('role') || el.tagName.toLowerCase(),
|
|
3003
|
+
isVisible: rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden',
|
|
3004
|
+
isInteractive: el.tagName.match(/^(BUTTON|A|INPUT)$/) || el.hasAttribute('onclick') || el.getAttribute('role') === 'button' || style.cursor === 'pointer',
|
|
3005
|
+
rect: rect,
|
|
3006
|
+
zIndex: parseInt(style.zIndex) || 0,
|
|
3007
|
+
opacity: parseFloat(style.opacity) || 1
|
|
3008
|
+
};
|
|
3009
|
+
}
|
|
3010
|
+
|
|
3011
|
+
// Score element relevance
|
|
3012
|
+
function scoreElement(analysis, target) {
|
|
3013
|
+
let score = 0;
|
|
3014
|
+
const text = analysis.text.toLowerCase();
|
|
3015
|
+
const label = analysis.ariaLabel.toLowerCase();
|
|
3016
|
+
const title = analysis.title.toLowerCase();
|
|
3017
|
+
const targetLower = target.toLowerCase();
|
|
3018
|
+
|
|
3019
|
+
// Exact match gets highest score
|
|
3020
|
+
if (text === targetLower || label === targetLower || title === targetLower) score += 100;
|
|
3021
|
+
|
|
3022
|
+
// Starts with target
|
|
3023
|
+
if (text.startsWith(targetLower) || label.startsWith(targetLower)) score += 50;
|
|
3024
|
+
|
|
3025
|
+
// Contains target
|
|
3026
|
+
if (text.includes(targetLower) || label.includes(targetLower) || title.includes(targetLower)) score += 25;
|
|
3027
|
+
|
|
3028
|
+
// Fuzzy matching for close matches
|
|
3029
|
+
const similarity = Math.max(
|
|
3030
|
+
calculateSimilarity(text, targetLower),
|
|
3031
|
+
calculateSimilarity(label, targetLower),
|
|
3032
|
+
calculateSimilarity(title, targetLower)
|
|
3033
|
+
);
|
|
3034
|
+
score += similarity * 20;
|
|
3035
|
+
|
|
3036
|
+
// Bonus for interactive elements
|
|
3037
|
+
if (analysis.isInteractive) score += 10;
|
|
3038
|
+
|
|
3039
|
+
// Bonus for visibility
|
|
3040
|
+
if (analysis.isVisible) score += 15;
|
|
3041
|
+
|
|
3042
|
+
// Bonus for larger elements (more likely to be main buttons)
|
|
3043
|
+
if (analysis.rect.width > 100 && analysis.rect.height > 30) score += 5;
|
|
3044
|
+
|
|
3045
|
+
// Bonus for higher z-index (on top)
|
|
3046
|
+
score += Math.min(analysis.zIndex, 5);
|
|
3047
|
+
|
|
3048
|
+
return score;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
// Simple string similarity function
|
|
3052
|
+
function calculateSimilarity(str1, str2) {
|
|
3053
|
+
const len1 = str1.length;
|
|
3054
|
+
const len2 = str2.length;
|
|
3055
|
+
const maxLen = Math.max(len1, len2);
|
|
3056
|
+
if (maxLen === 0) return 0;
|
|
3057
|
+
|
|
3058
|
+
let matches = 0;
|
|
3059
|
+
const minLen = Math.min(len1, len2);
|
|
3060
|
+
for (let i = 0; i < minLen; i++) {
|
|
3061
|
+
if (str1[i] === str2[i]) matches++;
|
|
3062
|
+
}
|
|
3063
|
+
return matches / maxLen;
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
// Find all potentially clickable elements
|
|
3067
|
+
const allElements = document.querySelectorAll('*');
|
|
3068
|
+
const candidates = [];
|
|
3069
|
+
|
|
3070
|
+
for (let el of allElements) {
|
|
3071
|
+
const analysis = analyzeElement(el);
|
|
3072
|
+
|
|
3073
|
+
if (analysis.isVisible && (analysis.isInteractive || analysis.text || analysis.ariaLabel)) {
|
|
3074
|
+
const score = scoreElement(analysis, targetText);
|
|
3075
|
+
if (score > 5) { // Only consider elements with some relevance
|
|
3076
|
+
candidates.push({ ...analysis, score });
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
if (candidates.length === 0) {
|
|
3082
|
+
return \`No clickable elements found containing text: "\${targetText}"\`;
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
// Sort by score and get the best match
|
|
3086
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
3087
|
+
const best = candidates[0];
|
|
3088
|
+
|
|
3089
|
+
// Additional validation before clicking
|
|
3090
|
+
if (best.score < 15) {
|
|
3091
|
+
return \`Found potential matches but confidence too low (score: \${best.score}). Best match was: "\${best.text || best.ariaLabel}" - try being more specific.\`;
|
|
3092
|
+
}
|
|
3093
|
+
|
|
3094
|
+
// Enhanced clicking for React components with duplicate prevention
|
|
3095
|
+
function clickElement(element) {
|
|
3096
|
+
// Enhanced duplicate prevention
|
|
3097
|
+
const elementId = element.id || element.className || element.textContent?.slice(0, 20) || 'element';
|
|
3098
|
+
const clickKey = 'mcp_click_text_' + btoa(elementId).slice(0, 10);
|
|
3099
|
+
|
|
3100
|
+
// Check if this element was recently clicked
|
|
3101
|
+
if (window[clickKey] && Date.now() - window[clickKey] < 2000) {
|
|
3102
|
+
throw new Error('Element click prevented - too soon after previous click');
|
|
3103
|
+
}
|
|
3104
|
+
|
|
3105
|
+
// Mark this element as clicked
|
|
3106
|
+
window[clickKey] = Date.now();
|
|
3107
|
+
|
|
3108
|
+
// Prevent multiple rapid events
|
|
3109
|
+
const originalPointerEvents = element.style.pointerEvents;
|
|
3110
|
+
element.style.pointerEvents = 'none';
|
|
3111
|
+
|
|
3112
|
+
// Scroll element into view if needed
|
|
3113
|
+
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
3114
|
+
|
|
3115
|
+
// Focus the element if focusable
|
|
3116
|
+
try {
|
|
3117
|
+
if (element.focus && typeof element.focus === 'function') {
|
|
3118
|
+
element.focus();
|
|
3119
|
+
}
|
|
3120
|
+
} catch (e) {
|
|
3121
|
+
// Focus may fail on some elements, that's ok
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
// Create and dispatch comprehensive click events for React
|
|
3125
|
+
const events = [
|
|
3126
|
+
new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window }),
|
|
3127
|
+
new MouseEvent('mouseup', { bubbles: true, cancelable: true, view: window }),
|
|
3128
|
+
new MouseEvent('click', { bubbles: true, cancelable: true, view: window })
|
|
3129
|
+
];
|
|
3130
|
+
|
|
3131
|
+
// Dispatch all events - don't treat preventDefault as failure
|
|
3132
|
+
events.forEach(event => {
|
|
3133
|
+
element.dispatchEvent(event);
|
|
3134
|
+
});
|
|
3135
|
+
|
|
3136
|
+
// Trigger additional React events if it's a form element
|
|
3137
|
+
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
|
|
3138
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3139
|
+
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
// Re-enable after delay
|
|
3143
|
+
setTimeout(() => {
|
|
3144
|
+
element.style.pointerEvents = originalPointerEvents;
|
|
3145
|
+
}, 1000);
|
|
3146
|
+
|
|
3147
|
+
return true;
|
|
3148
|
+
}
|
|
3149
|
+
|
|
3150
|
+
try {
|
|
3151
|
+
const clickResult = clickElement(best.element);
|
|
3152
|
+
return \`Successfully clicked element (score: \${best.score}): "\${best.text || best.ariaLabel || best.title}" - searched for: "\${targetText}"\`;
|
|
3153
|
+
} catch (error) {
|
|
3154
|
+
return \`Failed to click element: \${error.message}. Element found (score: \${best.score}): "\${best.text || best.ariaLabel || best.title}"\`;
|
|
3155
|
+
}
|
|
3156
|
+
})()
|
|
3157
|
+
`;
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
;// ./src/utils/electron-input-commands.ts
|
|
3161
|
+
/**
|
|
3162
|
+
* Enhanced input interaction commands for React-based Electron applications
|
|
3163
|
+
* Focuses on proper event handling and React state management
|
|
3164
|
+
*/
|
|
3165
|
+
/**
|
|
3166
|
+
* Securely escape text input for JavaScript code generation
|
|
3167
|
+
*/
|
|
3168
|
+
function electron_input_commands_escapeJavaScriptString(input) {
|
|
3169
|
+
return JSON.stringify(input);
|
|
3170
|
+
}
|
|
3171
|
+
/**
|
|
3172
|
+
* Validate input parameters for security
|
|
3173
|
+
*/
|
|
3174
|
+
function validateInputParams(selector, value, searchText) {
|
|
3175
|
+
const warnings = [];
|
|
3176
|
+
let sanitizedSelector = selector;
|
|
3177
|
+
let sanitizedValue = value;
|
|
3178
|
+
let sanitizedSearchText = searchText;
|
|
3179
|
+
// Validate selector
|
|
3180
|
+
if (selector.includes('javascript:'))
|
|
3181
|
+
warnings.push('Selector contains javascript: protocol');
|
|
3182
|
+
if (selector.includes('<script'))
|
|
3183
|
+
warnings.push('Selector contains script tags');
|
|
3184
|
+
if (selector.length > 500)
|
|
3185
|
+
warnings.push('Selector is unusually long');
|
|
3186
|
+
// Validate value
|
|
3187
|
+
if (value.includes('<script'))
|
|
3188
|
+
warnings.push('Value contains script tags');
|
|
3189
|
+
if (value.length > 10000)
|
|
3190
|
+
warnings.push('Value is unusually long');
|
|
3191
|
+
// Validate search text
|
|
3192
|
+
if (searchText.includes('<script'))
|
|
3193
|
+
warnings.push('Search text contains script tags');
|
|
3194
|
+
if (searchText.length > 1000)
|
|
3195
|
+
warnings.push('Search text is unusually long');
|
|
3196
|
+
// Basic sanitization
|
|
3197
|
+
sanitizedSelector = sanitizedSelector.replace(/javascript:/gi, '').substring(0, 500);
|
|
3198
|
+
sanitizedValue = sanitizedValue.replace(/<script[^>]*>.*?<\/script>/gi, '').substring(0, 10000);
|
|
3199
|
+
sanitizedSearchText = sanitizedSearchText
|
|
3200
|
+
.replace(/<script[^>]*>.*?<\/script>/gi, '')
|
|
3201
|
+
.substring(0, 1000);
|
|
3202
|
+
return {
|
|
3203
|
+
isValid: warnings.length === 0,
|
|
3204
|
+
sanitized: {
|
|
3205
|
+
selector: sanitizedSelector,
|
|
3206
|
+
value: sanitizedValue,
|
|
3207
|
+
searchText: sanitizedSearchText,
|
|
3208
|
+
},
|
|
3209
|
+
warnings,
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
/**
|
|
3213
|
+
* Generate the enhanced fill_input command with React-aware event handling
|
|
3214
|
+
*/
|
|
3215
|
+
function generateFillInputCommand(selector, value, searchText) {
|
|
3216
|
+
// Validate and sanitize inputs
|
|
3217
|
+
const validation = validateInputParams(selector, value, searchText);
|
|
3218
|
+
if (!validation.isValid) {
|
|
3219
|
+
return `(function() { return "Security validation failed: ${validation.warnings.join(', ')}"; })()`;
|
|
3220
|
+
}
|
|
3221
|
+
// Escape all inputs to prevent injection
|
|
3222
|
+
const escapedSelector = electron_input_commands_escapeJavaScriptString(validation.sanitized.selector);
|
|
3223
|
+
const escapedValue = electron_input_commands_escapeJavaScriptString(validation.sanitized.value);
|
|
3224
|
+
const escapedSearchText = electron_input_commands_escapeJavaScriptString(validation.sanitized.searchText);
|
|
3225
|
+
return `
|
|
3226
|
+
(function() {
|
|
3227
|
+
const selector = ${escapedSelector};
|
|
3228
|
+
const value = ${escapedValue};
|
|
3229
|
+
const searchText = ${escapedSearchText};
|
|
3230
|
+
|
|
3231
|
+
// Deep form field analysis
|
|
3232
|
+
function analyzeInput(el) {
|
|
3233
|
+
const rect = el.getBoundingClientRect();
|
|
3234
|
+
const style = getComputedStyle(el);
|
|
3235
|
+
const label = findAssociatedLabel(el);
|
|
3236
|
+
|
|
3237
|
+
return {
|
|
3238
|
+
element: el,
|
|
3239
|
+
type: el.type || el.tagName.toLowerCase(),
|
|
3240
|
+
placeholder: el.placeholder || '',
|
|
3241
|
+
name: el.name || '',
|
|
3242
|
+
id: el.id || '',
|
|
3243
|
+
value: el.value || '',
|
|
3244
|
+
label: label ? label.textContent.trim() : '',
|
|
3245
|
+
ariaLabel: el.getAttribute('aria-label') || '',
|
|
3246
|
+
ariaDescribedBy: el.getAttribute('aria-describedby') || '',
|
|
3247
|
+
isVisible: rect.width > 0 && rect.height > 0 && style.display !== 'none' && style.visibility !== 'hidden',
|
|
3248
|
+
isEnabled: !el.disabled && !el.readOnly,
|
|
3249
|
+
rect: rect,
|
|
3250
|
+
context: getInputContext(el)
|
|
3251
|
+
};
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
// Find associated label for an input
|
|
3255
|
+
function findAssociatedLabel(input) {
|
|
3256
|
+
// Method 1: Label with for attribute
|
|
3257
|
+
if (input.id) {
|
|
3258
|
+
const label = document.querySelector(\`label[for="\${input.id}"]\`);
|
|
3259
|
+
if (label) return label;
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
// Method 2: Input nested inside label
|
|
3263
|
+
let parent = input.parentElement;
|
|
3264
|
+
while (parent && parent.tagName !== 'BODY') {
|
|
3265
|
+
if (parent.tagName === 'LABEL') return parent;
|
|
3266
|
+
parent = parent.parentElement;
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
// Method 3: aria-labelledby
|
|
3270
|
+
const labelledBy = input.getAttribute('aria-labelledby');
|
|
3271
|
+
if (labelledBy) {
|
|
3272
|
+
const label = document.getElementById(labelledBy);
|
|
3273
|
+
if (label) return label;
|
|
3274
|
+
}
|
|
3275
|
+
|
|
3276
|
+
// Method 4: Look for nearby text elements
|
|
3277
|
+
const siblings = Array.from(input.parentElement?.children || []);
|
|
3278
|
+
for (let sibling of siblings) {
|
|
3279
|
+
if (sibling !== input && sibling.textContent?.trim()) {
|
|
3280
|
+
const siblingRect = sibling.getBoundingClientRect();
|
|
3281
|
+
const inputRect = input.getBoundingClientRect();
|
|
3282
|
+
|
|
3283
|
+
// Check if sibling is close to input (likely a label)
|
|
3284
|
+
if (Math.abs(siblingRect.bottom - inputRect.top) < 50 ||
|
|
3285
|
+
Math.abs(siblingRect.right - inputRect.left) < 200) {
|
|
3286
|
+
return sibling;
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
|
|
3291
|
+
return null;
|
|
3292
|
+
}
|
|
3293
|
+
|
|
3294
|
+
// Get surrounding context for better understanding
|
|
3295
|
+
function getInputContext(input) {
|
|
3296
|
+
const context = [];
|
|
3297
|
+
|
|
3298
|
+
// Get form context
|
|
3299
|
+
const form = input.closest('form');
|
|
3300
|
+
if (form) {
|
|
3301
|
+
const formTitle = form.querySelector('h1, h2, h3, h4, h5, h6');
|
|
3302
|
+
if (formTitle) context.push('Form: ' + formTitle.textContent.trim());
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
// Get fieldset context
|
|
3306
|
+
const fieldset = input.closest('fieldset');
|
|
3307
|
+
if (fieldset) {
|
|
3308
|
+
const legend = fieldset.querySelector('legend');
|
|
3309
|
+
if (legend) context.push('Fieldset: ' + legend.textContent.trim());
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
// Get section context
|
|
3313
|
+
const section = input.closest('section, div[class*="section"], div[class*="group"]');
|
|
3314
|
+
if (section) {
|
|
3315
|
+
const heading = section.querySelector('h1, h2, h3, h4, h5, h6, .title, .heading');
|
|
3316
|
+
if (heading) context.push('Section: ' + heading.textContent.trim());
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
return context.join(', ');
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
// Score input field relevance
|
|
3323
|
+
function scoreInput(analysis, target) {
|
|
3324
|
+
let score = 0;
|
|
3325
|
+
const targetLower = target.toLowerCase();
|
|
3326
|
+
|
|
3327
|
+
// Text matching
|
|
3328
|
+
const texts = [
|
|
3329
|
+
analysis.placeholder,
|
|
3330
|
+
analysis.label,
|
|
3331
|
+
analysis.ariaLabel,
|
|
3332
|
+
analysis.name,
|
|
3333
|
+
analysis.id,
|
|
3334
|
+
analysis.context
|
|
3335
|
+
].map(t => (t || '').toLowerCase());
|
|
3336
|
+
|
|
3337
|
+
for (let text of texts) {
|
|
3338
|
+
if (text === targetLower) score += 100;
|
|
3339
|
+
else if (text.includes(targetLower)) score += 50;
|
|
3340
|
+
else if (targetLower.includes(text) && text.length > 2) score += 30;
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
// Fuzzy matching
|
|
3344
|
+
for (let text of texts) {
|
|
3345
|
+
if (text.length > 2) {
|
|
3346
|
+
const similarity = calculateSimilarity(text, targetLower);
|
|
3347
|
+
score += similarity * 25;
|
|
3348
|
+
}
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
// Bonus for visible and enabled
|
|
3352
|
+
if (analysis.isVisible && analysis.isEnabled) score += 20;
|
|
3353
|
+
|
|
3354
|
+
// Bonus for text/password/email inputs (more likely to be forms)
|
|
3355
|
+
if (['text', 'password', 'email', 'search', 'textarea'].includes(analysis.type)) score += 10;
|
|
3356
|
+
|
|
3357
|
+
// Penalty for hidden/system fields
|
|
3358
|
+
if (analysis.type === 'hidden' || analysis.name?.includes('csrf')) score -= 50;
|
|
3359
|
+
|
|
3360
|
+
return score;
|
|
3361
|
+
}
|
|
3362
|
+
|
|
3363
|
+
function calculateSimilarity(str1, str2) {
|
|
3364
|
+
const len1 = str1.length;
|
|
3365
|
+
const len2 = str2.length;
|
|
3366
|
+
const maxLen = Math.max(len1, len2);
|
|
3367
|
+
if (maxLen === 0) return 0;
|
|
3368
|
+
|
|
3369
|
+
let matches = 0;
|
|
3370
|
+
const minLen = Math.min(len1, len2);
|
|
3371
|
+
for (let i = 0; i < minLen; i++) {
|
|
3372
|
+
if (str1[i] === str2[i]) matches++;
|
|
3373
|
+
}
|
|
3374
|
+
return matches / maxLen;
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3377
|
+
// Enhanced input filling for React components
|
|
3378
|
+
function fillInputValue(element, newValue) {
|
|
3379
|
+
try {
|
|
3380
|
+
// Store original value for comparison
|
|
3381
|
+
const originalValue = element.value;
|
|
3382
|
+
|
|
3383
|
+
// Scroll into view
|
|
3384
|
+
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
3385
|
+
|
|
3386
|
+
// Focus the element first
|
|
3387
|
+
element.focus();
|
|
3388
|
+
|
|
3389
|
+
// Wait a moment for focus
|
|
3390
|
+
setTimeout(() => {
|
|
3391
|
+
// For React components, we need to trigger the right events
|
|
3392
|
+
|
|
3393
|
+
// Method 1: Direct value assignment with React events
|
|
3394
|
+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
|
|
3395
|
+
const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
|
|
3396
|
+
|
|
3397
|
+
// Clear existing content first
|
|
3398
|
+
element.select();
|
|
3399
|
+
|
|
3400
|
+
if (element.tagName === 'INPUT' && nativeInputValueSetter) {
|
|
3401
|
+
nativeInputValueSetter.call(element, newValue);
|
|
3402
|
+
} else if (element.tagName === 'TEXTAREA' && nativeTextAreaValueSetter) {
|
|
3403
|
+
nativeTextAreaValueSetter.call(element, newValue);
|
|
3404
|
+
} else {
|
|
3405
|
+
element.value = newValue;
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
// Create and dispatch React-compatible events in proper order
|
|
3409
|
+
const events = [
|
|
3410
|
+
new Event('focus', { bubbles: true, cancelable: true }),
|
|
3411
|
+
new KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: 'a', ctrlKey: true }), // Ctrl+A
|
|
3412
|
+
new KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: 'a', ctrlKey: true }),
|
|
3413
|
+
new Event('input', { bubbles: true, cancelable: true }),
|
|
3414
|
+
new Event('change', { bubbles: true, cancelable: true }),
|
|
3415
|
+
new Event('blur', { bubbles: true, cancelable: true })
|
|
3416
|
+
];
|
|
3417
|
+
|
|
3418
|
+
events.forEach((event, index) => {
|
|
3419
|
+
setTimeout(() => {
|
|
3420
|
+
element.dispatchEvent(event);
|
|
3421
|
+
}, index * 50);
|
|
3422
|
+
});
|
|
3423
|
+
|
|
3424
|
+
// Method 2: Additional React trigger for controlled components
|
|
3425
|
+
if (window.React || window._reactInternalInstance || element._reactInternalFiber) {
|
|
3426
|
+
setTimeout(() => {
|
|
3427
|
+
// Trigger React's internal onChange
|
|
3428
|
+
const reactEvent = new Event('input', { bubbles: true });
|
|
3429
|
+
Object.defineProperty(reactEvent, 'target', { value: element, writable: false });
|
|
3430
|
+
Object.defineProperty(reactEvent, 'currentTarget', { value: element, writable: false });
|
|
3431
|
+
element.dispatchEvent(reactEvent);
|
|
3432
|
+
}, 300);
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
// Method 3: Fallback for contenteditable elements
|
|
3436
|
+
if (element.contentEditable === 'true') {
|
|
3437
|
+
element.textContent = newValue;
|
|
3438
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
// Trigger form validation if present
|
|
3442
|
+
if (element.form && element.form.checkValidity) {
|
|
3443
|
+
setTimeout(() => {
|
|
3444
|
+
element.form.checkValidity();
|
|
3445
|
+
}, 500);
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
// Verify the value was set correctly
|
|
3449
|
+
setTimeout(() => {
|
|
3450
|
+
if (element.value === newValue) {
|
|
3451
|
+
console.log('Input value successfully set and verified');
|
|
3452
|
+
} else {
|
|
3453
|
+
console.warn('Input value verification failed:', element.value, 'vs', newValue);
|
|
3454
|
+
}
|
|
3455
|
+
}, 600);
|
|
3456
|
+
|
|
3457
|
+
}, 100);
|
|
3458
|
+
|
|
3459
|
+
return true;
|
|
3460
|
+
} catch (error) {
|
|
3461
|
+
console.error('Error in fillInputValue:', error);
|
|
3462
|
+
return false;
|
|
3463
|
+
}
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
let targetElement = null;
|
|
3467
|
+
|
|
3468
|
+
// Method 1: Try by selector first if provided
|
|
3469
|
+
if (selector) {
|
|
3470
|
+
targetElement = document.querySelector(selector);
|
|
3471
|
+
if (targetElement) {
|
|
3472
|
+
const analysis = analyzeInput(targetElement);
|
|
3473
|
+
if (analysis.isVisible && analysis.isEnabled) {
|
|
3474
|
+
// Element found by selector, proceed to fill
|
|
3475
|
+
} else {
|
|
3476
|
+
targetElement = null; // Reset if not usable
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
// Method 2: Intelligent search if no selector or selector failed
|
|
3482
|
+
if (!targetElement && searchText) {
|
|
3483
|
+
const inputs = document.querySelectorAll('input, textarea, select, [contenteditable="true"]');
|
|
3484
|
+
const candidates = [];
|
|
3485
|
+
|
|
3486
|
+
for (let input of inputs) {
|
|
3487
|
+
const analysis = analyzeInput(input);
|
|
3488
|
+
if (analysis.isVisible && analysis.isEnabled) {
|
|
3489
|
+
const score = scoreInput(analysis, searchText);
|
|
3490
|
+
if (score > 10) {
|
|
3491
|
+
candidates.push({ ...analysis, score });
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
if (candidates.length > 0) {
|
|
3497
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
3498
|
+
targetElement = candidates[0].element;
|
|
3499
|
+
|
|
3500
|
+
// Log the decision for debugging
|
|
3501
|
+
console.log('Input selection:', {
|
|
3502
|
+
searched: searchText,
|
|
3503
|
+
found: candidates[0].label || candidates[0].placeholder || candidates[0].name,
|
|
3504
|
+
score: candidates[0].score,
|
|
3505
|
+
alternatives: candidates.slice(1, 3).map(c => ({
|
|
3506
|
+
label: c.label || c.placeholder || c.name,
|
|
3507
|
+
score: c.score
|
|
3508
|
+
}))
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
if (!targetElement) {
|
|
3514
|
+
return \`No suitable input found for: "\${searchText || selector}". Available inputs: \${
|
|
3515
|
+
Array.from(document.querySelectorAll('input, textarea')).map(inp => {
|
|
3516
|
+
const analysis = analyzeInput(inp);
|
|
3517
|
+
return analysis.label || analysis.placeholder || analysis.name || analysis.type;
|
|
3518
|
+
}).filter(Boolean).join(', ')
|
|
3519
|
+
}\`;
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3522
|
+
// Fill the input with enhanced interaction
|
|
3523
|
+
try {
|
|
3524
|
+
const success = fillInputValue(targetElement, value);
|
|
3525
|
+
|
|
3526
|
+
if (success) {
|
|
3527
|
+
const analysis = analyzeInput(targetElement);
|
|
3528
|
+
return \`Successfully filled input "\${analysis.label || analysis.placeholder || analysis.name || 'unknown'}" with: "\${value}"\`;
|
|
3529
|
+
} else {
|
|
3530
|
+
return \`Failed to fill input value\`;
|
|
3531
|
+
}
|
|
3532
|
+
} catch (error) {
|
|
3533
|
+
return \`Failed to fill input: \${error.message}\`;
|
|
3534
|
+
}
|
|
3535
|
+
})()
|
|
3536
|
+
`;
|
|
3537
|
+
}
|
|
3538
|
+
/**
|
|
3539
|
+
* Generate the enhanced select_option command
|
|
3540
|
+
*/
|
|
3541
|
+
function generateSelectOptionCommand(selector, value, text) {
|
|
3542
|
+
// Validate and sanitize inputs
|
|
3543
|
+
const validation = validateInputParams(selector, value, text);
|
|
3544
|
+
if (!validation.isValid) {
|
|
3545
|
+
return `(function() { return "Security validation failed: ${validation.warnings.join(', ')}"; })()`;
|
|
3546
|
+
}
|
|
3547
|
+
// Escape all inputs to prevent injection
|
|
3548
|
+
const escapedSelector = electron_input_commands_escapeJavaScriptString(validation.sanitized.selector);
|
|
3549
|
+
const escapedValue = electron_input_commands_escapeJavaScriptString(validation.sanitized.value);
|
|
3550
|
+
const escapedText = electron_input_commands_escapeJavaScriptString(validation.sanitized.searchText);
|
|
3551
|
+
return `
|
|
3552
|
+
(function() {
|
|
3553
|
+
const selector = ${escapedSelector};
|
|
3554
|
+
const value = ${escapedValue};
|
|
3555
|
+
const text = ${escapedText};
|
|
3556
|
+
|
|
3557
|
+
let select = null;
|
|
3558
|
+
|
|
3559
|
+
// Try by selector first
|
|
3560
|
+
if (selector) {
|
|
3561
|
+
select = document.querySelector(selector);
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
// Try by label text
|
|
3565
|
+
if (!select && text) {
|
|
3566
|
+
const selects = document.querySelectorAll('select');
|
|
3567
|
+
for (let sel of selects) {
|
|
3568
|
+
const label = document.querySelector(\`label[for="\${sel.id}"]\`);
|
|
3569
|
+
if (label && label.textContent?.toLowerCase().includes(text.toLowerCase())) {
|
|
3570
|
+
select = sel;
|
|
3571
|
+
break;
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
if (select) {
|
|
3577
|
+
// Try to find option by value or text
|
|
3578
|
+
const options = select.querySelectorAll('option');
|
|
3579
|
+
for (let option of options) {
|
|
3580
|
+
if (option.value === value || option.textContent?.trim().toLowerCase().includes(value.toLowerCase())) {
|
|
3581
|
+
select.value = option.value;
|
|
3582
|
+
|
|
3583
|
+
// Trigger React-compatible events
|
|
3584
|
+
select.dispatchEvent(new Event('change', { bubbles: true, cancelable: true }));
|
|
3585
|
+
select.dispatchEvent(new Event('input', { bubbles: true, cancelable: true }));
|
|
3586
|
+
|
|
3587
|
+
return \`Selected option "\${option.textContent?.trim()}" in select "\${select.name || 'unknown'}"\`;
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
return \`Option "\${value}" not found in select\`;
|
|
3591
|
+
}
|
|
3592
|
+
|
|
3593
|
+
return \`No select found with selector: "\${selector}" or text: "\${text}"\`;
|
|
3594
|
+
})()
|
|
3595
|
+
`;
|
|
3596
|
+
}
|
|
3597
|
+
/**
|
|
3598
|
+
* Generate page structure analysis command
|
|
3599
|
+
*/
|
|
3600
|
+
function generatePageStructureCommand() {
|
|
3601
|
+
return `
|
|
3602
|
+
(function() {
|
|
3603
|
+
const structure = {
|
|
3604
|
+
title: document.title,
|
|
3605
|
+
url: window.location.href,
|
|
3606
|
+
buttons: [],
|
|
3607
|
+
inputs: [],
|
|
3608
|
+
selects: [],
|
|
3609
|
+
links: [],
|
|
3610
|
+
framework: detectFramework()
|
|
3611
|
+
};
|
|
3612
|
+
|
|
3613
|
+
function detectFramework() {
|
|
3614
|
+
if (window.React || document.querySelector('[data-reactroot]')) return 'React';
|
|
3615
|
+
if (window.Vue || document.querySelector('[data-v-]')) return 'Vue';
|
|
3616
|
+
if (window.angular || document.querySelector('[ng-version]')) return 'Angular';
|
|
3617
|
+
return 'Unknown';
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
// Get buttons with enhanced analysis
|
|
3621
|
+
document.querySelectorAll('button, [role="button"], input[type="button"], input[type="submit"]').forEach(el => {
|
|
3622
|
+
const rect = el.getBoundingClientRect();
|
|
3623
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
3624
|
+
structure.buttons.push({
|
|
3625
|
+
text: el.textContent?.trim() || el.value || '',
|
|
3626
|
+
id: el.id || '',
|
|
3627
|
+
ariaLabel: el.getAttribute('aria-label') || '',
|
|
3628
|
+
className: el.className || '',
|
|
3629
|
+
type: el.type || 'button',
|
|
3630
|
+
disabled: el.disabled,
|
|
3631
|
+
visible: !el.hidden && getComputedStyle(el).display !== 'none'
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
});
|
|
3635
|
+
|
|
3636
|
+
// Get inputs with enhanced analysis
|
|
3637
|
+
document.querySelectorAll('input, textarea').forEach(el => {
|
|
3638
|
+
const rect = el.getBoundingClientRect();
|
|
3639
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
3640
|
+
const label = document.querySelector(\`label[for="\${el.id}"]\`);
|
|
3641
|
+
structure.inputs.push({
|
|
3642
|
+
type: el.type || 'text',
|
|
3643
|
+
placeholder: el.placeholder || '',
|
|
3644
|
+
label: label?.textContent?.trim() || '',
|
|
3645
|
+
id: el.id || '',
|
|
3646
|
+
name: el.name || '',
|
|
3647
|
+
ariaLabel: el.getAttribute('aria-label') || '',
|
|
3648
|
+
value: el.value || '',
|
|
3649
|
+
required: el.required,
|
|
3650
|
+
disabled: el.disabled,
|
|
3651
|
+
readOnly: el.readOnly,
|
|
3652
|
+
visible: !el.hidden && getComputedStyle(el).display !== 'none'
|
|
3653
|
+
});
|
|
3654
|
+
}
|
|
3655
|
+
});
|
|
3656
|
+
|
|
3657
|
+
// Get selects with enhanced analysis
|
|
3658
|
+
document.querySelectorAll('select').forEach(el => {
|
|
3659
|
+
const rect = el.getBoundingClientRect();
|
|
3660
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
3661
|
+
const label = document.querySelector(\`label[for="\${el.id}"]\`);
|
|
3662
|
+
const options = Array.from(el.options).map(opt => ({
|
|
3663
|
+
value: opt.value,
|
|
3664
|
+
text: opt.textContent?.trim(),
|
|
3665
|
+
selected: opt.selected
|
|
3666
|
+
}));
|
|
3667
|
+
structure.selects.push({
|
|
3668
|
+
label: label?.textContent?.trim() || '',
|
|
3669
|
+
id: el.id || '',
|
|
3670
|
+
name: el.name || '',
|
|
3671
|
+
options: options,
|
|
3672
|
+
selectedValue: el.value,
|
|
3673
|
+
multiple: el.multiple,
|
|
3674
|
+
disabled: el.disabled,
|
|
3675
|
+
visible: !el.hidden && getComputedStyle(el).display !== 'none'
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
});
|
|
3679
|
+
|
|
3680
|
+
// Get links with enhanced analysis
|
|
3681
|
+
document.querySelectorAll('a[href]').forEach(el => {
|
|
3682
|
+
const rect = el.getBoundingClientRect();
|
|
3683
|
+
if (rect.width > 0 && rect.height > 0) {
|
|
3684
|
+
structure.links.push({
|
|
3685
|
+
text: el.textContent?.trim() || '',
|
|
3686
|
+
href: el.href,
|
|
3687
|
+
id: el.id || '',
|
|
3688
|
+
target: el.target || '',
|
|
3689
|
+
visible: !el.hidden && getComputedStyle(el).display !== 'none'
|
|
3690
|
+
});
|
|
3691
|
+
}
|
|
3692
|
+
});
|
|
3693
|
+
|
|
3694
|
+
return JSON.stringify(structure, null, 2);
|
|
3695
|
+
})()
|
|
3696
|
+
`;
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3699
|
+
;// ./src/utils/electron-enhanced-commands.ts
|
|
3700
|
+
|
|
3701
|
+
|
|
3702
|
+
|
|
3703
|
+
/**
|
|
3704
|
+
* Enhanced command executor with improved React support.
|
|
3705
|
+
* @param command - The command to execute
|
|
3706
|
+
* @param args - Command-specific arguments
|
|
3707
|
+
* @param windowOptions - Optional window targeting (targetId or windowTitle)
|
|
3708
|
+
*/
|
|
3709
|
+
async function sendCommandToElectron(command, args, windowOptions) {
|
|
3710
|
+
try {
|
|
3711
|
+
const target = await findElectronTarget(windowOptions);
|
|
3712
|
+
let javascriptCode;
|
|
3713
|
+
switch (command.toLowerCase()) {
|
|
3714
|
+
case 'get_title':
|
|
3715
|
+
javascriptCode = 'document.title';
|
|
3716
|
+
break;
|
|
3717
|
+
case 'get_url':
|
|
3718
|
+
javascriptCode = 'window.location.href';
|
|
3719
|
+
break;
|
|
3720
|
+
case 'get_body_text':
|
|
3721
|
+
javascriptCode = 'document.body.innerText.substring(0, 500)';
|
|
3722
|
+
break;
|
|
3723
|
+
case 'click_button':
|
|
3724
|
+
// Validate and escape selector input
|
|
3725
|
+
const selector = args?.selector || 'button';
|
|
3726
|
+
if (selector.includes('javascript:') || selector.includes('<script')) {
|
|
3727
|
+
return 'Invalid selector: contains dangerous content';
|
|
3728
|
+
}
|
|
3729
|
+
const escapedSelector = JSON.stringify(selector);
|
|
3730
|
+
javascriptCode = `
|
|
3731
|
+
const button = document.querySelector(${escapedSelector});
|
|
3732
|
+
if (button && !button.disabled) {
|
|
3733
|
+
// Enhanced duplicate prevention
|
|
3734
|
+
const buttonId = button.id || button.className || 'button';
|
|
3735
|
+
const clickKey = 'mcp_click_' + btoa(buttonId).slice(0, 10);
|
|
3736
|
+
|
|
3737
|
+
// Check if this button was recently clicked
|
|
3738
|
+
if (window[clickKey] && Date.now() - window[clickKey] < 2000) {
|
|
3739
|
+
return 'Button click prevented - too soon after previous click';
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
// Mark this button as clicked
|
|
3743
|
+
window[clickKey] = Date.now();
|
|
3744
|
+
|
|
3745
|
+
// Prevent multiple rapid events
|
|
3746
|
+
button.style.pointerEvents = 'none';
|
|
3747
|
+
|
|
3748
|
+
// Trigger React events properly
|
|
3749
|
+
button.focus();
|
|
3750
|
+
|
|
3751
|
+
// Use both React synthetic events and native events
|
|
3752
|
+
const clickEvent = new MouseEvent('click', {
|
|
3753
|
+
bubbles: true,
|
|
3754
|
+
cancelable: true,
|
|
3755
|
+
view: window
|
|
3756
|
+
});
|
|
3757
|
+
|
|
3758
|
+
button.dispatchEvent(clickEvent);
|
|
3759
|
+
|
|
3760
|
+
// Re-enable after delay
|
|
3761
|
+
setTimeout(() => {
|
|
3762
|
+
button.style.pointerEvents = '';
|
|
3763
|
+
}, 1000);
|
|
3764
|
+
|
|
3765
|
+
return 'Button clicked with enhanced protection';
|
|
3766
|
+
}
|
|
3767
|
+
return 'Button not found or disabled';
|
|
3768
|
+
`;
|
|
3769
|
+
break;
|
|
3770
|
+
case 'find_elements':
|
|
3771
|
+
javascriptCode = generateFindElementsCommand();
|
|
3772
|
+
break;
|
|
3773
|
+
case 'click_by_text':
|
|
3774
|
+
const clickText = args?.text || '';
|
|
3775
|
+
if (!clickText) {
|
|
3776
|
+
return 'ERROR: Missing text. Use: {"text": "button text"}. See MCP_USAGE_GUIDE.md for examples.';
|
|
3777
|
+
}
|
|
3778
|
+
javascriptCode = generateClickByTextCommand(clickText);
|
|
3779
|
+
break;
|
|
3780
|
+
case 'click_by_selector':
|
|
3781
|
+
// Secure selector-based clicking
|
|
3782
|
+
const clickSelector = args?.selector || '';
|
|
3783
|
+
// Better error message for common mistake
|
|
3784
|
+
if (!clickSelector) {
|
|
3785
|
+
return 'ERROR: Missing selector. Use: {"selector": "your-css-selector"}. See MCP_USAGE_GUIDE.md for examples.';
|
|
3786
|
+
}
|
|
3787
|
+
if (clickSelector.includes('javascript:') || clickSelector.includes('<script')) {
|
|
3788
|
+
return 'Invalid selector: contains dangerous content';
|
|
3789
|
+
}
|
|
3790
|
+
const escapedClickSelector = JSON.stringify(clickSelector);
|
|
3791
|
+
javascriptCode = `
|
|
3792
|
+
(function() {
|
|
3793
|
+
try {
|
|
3794
|
+
const element = document.querySelector(${escapedClickSelector});
|
|
3795
|
+
if (element) {
|
|
3796
|
+
// Check if element is clickable
|
|
3797
|
+
const rect = element.getBoundingClientRect();
|
|
3798
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
3799
|
+
return 'Element not visible';
|
|
3800
|
+
}
|
|
3801
|
+
|
|
3802
|
+
// Prevent rapid clicks
|
|
3803
|
+
const clickKey = 'mcp_selector_click_' + btoa(${escapedClickSelector}).slice(0, 10);
|
|
3804
|
+
if (window[clickKey] && Date.now() - window[clickKey] < 1000) {
|
|
3805
|
+
return 'Click prevented - too soon after previous click';
|
|
3806
|
+
}
|
|
3807
|
+
window[clickKey] = Date.now();
|
|
3808
|
+
|
|
3809
|
+
// Focus and click
|
|
3810
|
+
element.focus();
|
|
3811
|
+
const event = new MouseEvent('click', {
|
|
3812
|
+
bubbles: true,
|
|
3813
|
+
cancelable: true,
|
|
3814
|
+
view: window
|
|
3815
|
+
});
|
|
3816
|
+
element.dispatchEvent(event);
|
|
3817
|
+
|
|
3818
|
+
return 'Successfully clicked element: ' + element.tagName +
|
|
3819
|
+
(element.textContent ? ' - "' + element.textContent.substring(0, 50) + '"' : '');
|
|
3820
|
+
}
|
|
3821
|
+
return 'Element not found: ' + ${escapedClickSelector};
|
|
3822
|
+
} catch (e) {
|
|
3823
|
+
return 'Error clicking element: ' + e.message;
|
|
3824
|
+
}
|
|
3825
|
+
})();
|
|
3826
|
+
`;
|
|
3827
|
+
break;
|
|
3828
|
+
case 'send_keyboard_shortcut':
|
|
3829
|
+
// Secure keyboard shortcut sending
|
|
3830
|
+
const key = args?.text || '';
|
|
3831
|
+
const validKeys = [
|
|
3832
|
+
'Enter',
|
|
3833
|
+
'Escape',
|
|
3834
|
+
'Tab',
|
|
3835
|
+
'Space',
|
|
3836
|
+
'ArrowUp',
|
|
3837
|
+
'ArrowDown',
|
|
3838
|
+
'ArrowLeft',
|
|
3839
|
+
'ArrowRight',
|
|
3840
|
+
];
|
|
3841
|
+
// Parse shortcut like "Ctrl+N" or "Meta+N"
|
|
3842
|
+
const parts = key.split('+').map((p) => p.trim());
|
|
3843
|
+
const keyPart = parts[parts.length - 1];
|
|
3844
|
+
const modifiers = parts.slice(0, -1);
|
|
3845
|
+
// Helper function to get proper KeyboardEvent.code value
|
|
3846
|
+
function getKeyCode(key) {
|
|
3847
|
+
// Special keys mapping
|
|
3848
|
+
const specialKeys = {
|
|
3849
|
+
Enter: 'Enter',
|
|
3850
|
+
Escape: 'Escape',
|
|
3851
|
+
Tab: 'Tab',
|
|
3852
|
+
Space: 'Space',
|
|
3853
|
+
ArrowUp: 'ArrowUp',
|
|
3854
|
+
ArrowDown: 'ArrowDown',
|
|
3855
|
+
ArrowLeft: 'ArrowLeft',
|
|
3856
|
+
ArrowRight: 'ArrowRight',
|
|
3857
|
+
Backspace: 'Backspace',
|
|
3858
|
+
Delete: 'Delete',
|
|
3859
|
+
Home: 'Home',
|
|
3860
|
+
End: 'End',
|
|
3861
|
+
PageUp: 'PageUp',
|
|
3862
|
+
PageDown: 'PageDown',
|
|
3863
|
+
};
|
|
3864
|
+
if (specialKeys[key]) {
|
|
3865
|
+
return specialKeys[key];
|
|
3866
|
+
}
|
|
3867
|
+
// Single character keys
|
|
3868
|
+
if (key.length === 1) {
|
|
3869
|
+
const upperKey = key.toUpperCase();
|
|
3870
|
+
if (upperKey >= 'A' && upperKey <= 'Z') {
|
|
3871
|
+
return `Key${upperKey}`;
|
|
3872
|
+
}
|
|
3873
|
+
if (upperKey >= '0' && upperKey <= '9') {
|
|
3874
|
+
return `Digit${upperKey}`;
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
return `Key${key.toUpperCase()}`;
|
|
3878
|
+
}
|
|
3879
|
+
if (keyPart.length === 1 || validKeys.includes(keyPart)) {
|
|
3880
|
+
const modifierProps = modifiers
|
|
3881
|
+
.map((mod) => {
|
|
3882
|
+
switch (mod.toLowerCase()) {
|
|
3883
|
+
case 'ctrl':
|
|
3884
|
+
return 'ctrlKey: true';
|
|
3885
|
+
case 'shift':
|
|
3886
|
+
return 'shiftKey: true';
|
|
3887
|
+
case 'alt':
|
|
3888
|
+
return 'altKey: true';
|
|
3889
|
+
case 'meta':
|
|
3890
|
+
case 'cmd':
|
|
3891
|
+
return 'metaKey: true';
|
|
3892
|
+
default:
|
|
3893
|
+
return '';
|
|
3894
|
+
}
|
|
3895
|
+
})
|
|
3896
|
+
.filter(Boolean)
|
|
3897
|
+
.join(', ');
|
|
3898
|
+
javascriptCode = `
|
|
3899
|
+
(function() {
|
|
3900
|
+
try {
|
|
3901
|
+
const event = new KeyboardEvent('keydown', {
|
|
3902
|
+
key: '${keyPart}',
|
|
3903
|
+
code: '${getKeyCode(keyPart)}',
|
|
3904
|
+
${modifierProps},
|
|
3905
|
+
bubbles: true,
|
|
3906
|
+
cancelable: true
|
|
3907
|
+
});
|
|
3908
|
+
document.dispatchEvent(event);
|
|
3909
|
+
return 'Keyboard shortcut sent: ${key}';
|
|
3910
|
+
} catch (e) {
|
|
3911
|
+
return 'Error sending shortcut: ' + e.message;
|
|
3912
|
+
}
|
|
3913
|
+
})();
|
|
3914
|
+
`;
|
|
3915
|
+
}
|
|
3916
|
+
else {
|
|
3917
|
+
return `Invalid keyboard shortcut: ${key}`;
|
|
3918
|
+
}
|
|
3919
|
+
break;
|
|
3920
|
+
case 'navigate_to_hash':
|
|
3921
|
+
// Secure hash navigation
|
|
3922
|
+
const hash = args?.text || '';
|
|
3923
|
+
if (hash.includes('javascript:') || hash.includes('<script') || hash.includes('://')) {
|
|
3924
|
+
return 'Invalid hash: contains dangerous content';
|
|
3925
|
+
}
|
|
3926
|
+
const cleanHash = hash.startsWith('#') ? hash : '#' + hash;
|
|
3927
|
+
javascriptCode = `
|
|
3928
|
+
(function() {
|
|
3929
|
+
try {
|
|
3930
|
+
// Use pushState for safer navigation
|
|
3931
|
+
if (window.history && window.history.pushState) {
|
|
3932
|
+
const newUrl = window.location.pathname + window.location.search + '${cleanHash}';
|
|
3933
|
+
window.history.pushState({}, '', newUrl);
|
|
3934
|
+
|
|
3935
|
+
// Trigger hashchange event for React Router
|
|
3936
|
+
window.dispatchEvent(new HashChangeEvent('hashchange', {
|
|
3937
|
+
newURL: window.location.href,
|
|
3938
|
+
oldURL: window.location.href.replace('${cleanHash}', '')
|
|
3939
|
+
}));
|
|
3940
|
+
|
|
3941
|
+
return 'Navigated to hash: ${cleanHash}';
|
|
3942
|
+
} else {
|
|
3943
|
+
// Fallback to direct assignment
|
|
3944
|
+
window.location.hash = '${cleanHash}';
|
|
3945
|
+
return 'Navigated to hash (fallback): ${cleanHash}';
|
|
3946
|
+
}
|
|
3947
|
+
} catch (e) {
|
|
3948
|
+
return 'Error navigating: ' + e.message;
|
|
3949
|
+
}
|
|
3950
|
+
})();
|
|
3951
|
+
`;
|
|
3952
|
+
break;
|
|
3953
|
+
case 'fill_input':
|
|
3954
|
+
const inputValue = args?.value || args?.text || '';
|
|
3955
|
+
if (!inputValue) {
|
|
3956
|
+
return 'ERROR: Missing value. Use: {"value": "text", "selector": "..."} or {"value": "text", "placeholder": "..."}. See MCP_USAGE_GUIDE.md for examples.';
|
|
3957
|
+
}
|
|
3958
|
+
javascriptCode = generateFillInputCommand(args?.selector || '', inputValue, args?.text || args?.placeholder || '');
|
|
3959
|
+
break;
|
|
3960
|
+
case 'select_option':
|
|
3961
|
+
javascriptCode = generateSelectOptionCommand(args?.selector || '', args?.value || '', args?.text || '');
|
|
3962
|
+
break;
|
|
3963
|
+
case 'get_page_structure':
|
|
3964
|
+
javascriptCode = generatePageStructureCommand();
|
|
3965
|
+
break;
|
|
3966
|
+
case 'debug_elements':
|
|
3967
|
+
javascriptCode = `
|
|
3968
|
+
(function() {
|
|
3969
|
+
const buttons = Array.from(document.querySelectorAll('button')).map(btn => ({
|
|
3970
|
+
text: btn.textContent?.trim(),
|
|
3971
|
+
id: btn.id,
|
|
3972
|
+
className: btn.className,
|
|
3973
|
+
disabled: btn.disabled,
|
|
3974
|
+
visible: btn.getBoundingClientRect().width > 0,
|
|
3975
|
+
type: btn.type || 'button'
|
|
3976
|
+
}));
|
|
3977
|
+
|
|
3978
|
+
const inputs = Array.from(document.querySelectorAll('input, textarea, select')).map(inp => ({
|
|
3979
|
+
name: inp.name,
|
|
3980
|
+
placeholder: inp.placeholder,
|
|
3981
|
+
type: inp.type,
|
|
3982
|
+
id: inp.id,
|
|
3983
|
+
value: inp.value,
|
|
3984
|
+
visible: inp.getBoundingClientRect().width > 0,
|
|
3985
|
+
enabled: !inp.disabled
|
|
3986
|
+
}));
|
|
3987
|
+
|
|
3988
|
+
return JSON.stringify({
|
|
3989
|
+
buttons: buttons.filter(b => b.visible).slice(0, 10),
|
|
3990
|
+
inputs: inputs.filter(i => i.visible).slice(0, 10),
|
|
3991
|
+
url: window.location.href,
|
|
3992
|
+
title: document.title
|
|
3993
|
+
}, null, 2);
|
|
3994
|
+
})()
|
|
3995
|
+
`;
|
|
3996
|
+
break;
|
|
3997
|
+
case 'verify_form_state':
|
|
3998
|
+
javascriptCode = `
|
|
3999
|
+
(function() {
|
|
4000
|
+
const forms = Array.from(document.querySelectorAll('form')).map(form => {
|
|
4001
|
+
const inputs = Array.from(form.querySelectorAll('input, textarea, select')).map(inp => ({
|
|
4002
|
+
name: inp.name,
|
|
4003
|
+
type: inp.type,
|
|
4004
|
+
value: inp.value,
|
|
4005
|
+
placeholder: inp.placeholder,
|
|
4006
|
+
required: inp.required,
|
|
4007
|
+
valid: inp.validity?.valid
|
|
4008
|
+
}));
|
|
4009
|
+
|
|
4010
|
+
return {
|
|
4011
|
+
id: form.id,
|
|
4012
|
+
action: form.action,
|
|
4013
|
+
method: form.method,
|
|
4014
|
+
inputs: inputs,
|
|
4015
|
+
isValid: form.checkValidity?.() || 'unknown'
|
|
4016
|
+
};
|
|
4017
|
+
});
|
|
4018
|
+
|
|
4019
|
+
return JSON.stringify({ forms, formCount: forms.length }, null, 2);
|
|
4020
|
+
})()
|
|
4021
|
+
`;
|
|
4022
|
+
break;
|
|
4023
|
+
case 'console_log':
|
|
4024
|
+
javascriptCode = `console.log('MCP Command:', '${args?.message || 'Hello from MCP!'}'); 'Console message sent'`;
|
|
4025
|
+
break;
|
|
4026
|
+
case 'eval':
|
|
4027
|
+
const rawCode = typeof args === 'string' ? args : args?.code || command;
|
|
4028
|
+
// Enhanced eval with better error handling and result reporting
|
|
4029
|
+
const codeHash = Buffer.from(rawCode).toString('base64').slice(0, 10);
|
|
4030
|
+
const isStateTest = rawCode.includes('window.testState') ||
|
|
4031
|
+
rawCode.includes('persistent-test-value') ||
|
|
4032
|
+
rawCode.includes('window.testValue');
|
|
4033
|
+
javascriptCode = `
|
|
4034
|
+
(function() {
|
|
4035
|
+
try {
|
|
4036
|
+
// Prevent rapid execution of the same code unless it's a state test
|
|
4037
|
+
const codeHash = '${codeHash}';
|
|
4038
|
+
const isStateTest = ${isStateTest};
|
|
4039
|
+
const rawCode = ${JSON.stringify(rawCode)};
|
|
4040
|
+
|
|
4041
|
+
if (!isStateTest && window._mcpExecuting && window._mcpExecuting[codeHash]) {
|
|
4042
|
+
return { success: false, error: 'Code already executing', result: null };
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
window._mcpExecuting = window._mcpExecuting || {};
|
|
4046
|
+
if (!isStateTest) {
|
|
4047
|
+
window._mcpExecuting[codeHash] = true;
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
let result;
|
|
4051
|
+
${rawCode.trim().startsWith('() =>') || rawCode.trim().startsWith('function')
|
|
4052
|
+
? `result = (${rawCode})();`
|
|
4053
|
+
: rawCode.includes('return')
|
|
4054
|
+
? `result = (function() { ${rawCode} })();`
|
|
4055
|
+
: rawCode.includes(';')
|
|
4056
|
+
? `result = (function() { ${rawCode}; return "executed"; })();`
|
|
4057
|
+
: `result = (function() { return (${rawCode}); })();`}
|
|
4058
|
+
|
|
4059
|
+
setTimeout(() => {
|
|
4060
|
+
if (!isStateTest && window._mcpExecuting) {
|
|
4061
|
+
delete window._mcpExecuting[codeHash];
|
|
4062
|
+
}
|
|
4063
|
+
}, 1000);
|
|
4064
|
+
|
|
4065
|
+
// Enhanced result reporting
|
|
4066
|
+
// For simple expressions, undefined might be a valid result for some cases
|
|
4067
|
+
if (result === undefined && !rawCode.includes('window.') && !rawCode.includes('document.') && !rawCode.includes('||')) {
|
|
4068
|
+
return { success: false, error: 'Command returned undefined - element may not exist or action failed', result: null };
|
|
4069
|
+
}
|
|
4070
|
+
if (result === null) {
|
|
4071
|
+
return { success: false, error: 'Command returned null - element may not exist', result: null };
|
|
4072
|
+
}
|
|
4073
|
+
if (result === false && rawCode.includes('click') || rawCode.includes('querySelector')) {
|
|
4074
|
+
return { success: false, error: 'Command returned false - action likely failed', result: false };
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
return { success: true, error: null, result: result };
|
|
4078
|
+
} catch (error) {
|
|
4079
|
+
return {
|
|
4080
|
+
success: false,
|
|
4081
|
+
error: 'JavaScript error: ' + error.message,
|
|
4082
|
+
stack: error.stack,
|
|
4083
|
+
result: null
|
|
4084
|
+
};
|
|
4085
|
+
}
|
|
4086
|
+
})()
|
|
4087
|
+
`;
|
|
4088
|
+
break;
|
|
4089
|
+
default:
|
|
4090
|
+
javascriptCode = command;
|
|
4091
|
+
}
|
|
4092
|
+
const rawResult = await executeInElectron(javascriptCode, target);
|
|
4093
|
+
// Try to parse structured response from enhanced eval
|
|
4094
|
+
if (command.toLowerCase() === 'eval') {
|
|
4095
|
+
try {
|
|
4096
|
+
const parsedResult = JSON.parse(rawResult);
|
|
4097
|
+
if (parsedResult && typeof parsedResult === 'object' && 'success' in parsedResult) {
|
|
4098
|
+
if (!parsedResult.success) {
|
|
4099
|
+
return `❌ Command failed: ${parsedResult.error}${parsedResult.stack ? '\nStack: ' + parsedResult.stack : ''}`;
|
|
4100
|
+
}
|
|
4101
|
+
return `✅ Command successful${parsedResult.result !== null ? ': ' + JSON.stringify(parsedResult.result) : ''}`;
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
catch {
|
|
4105
|
+
// If it's not JSON, treat as regular result
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
// Handle regular results
|
|
4109
|
+
if (rawResult === 'undefined' || rawResult === 'null' || rawResult === '') {
|
|
4110
|
+
return `⚠️ Command executed but returned ${rawResult || 'empty'} - this may indicate the element wasn't found or the action failed`;
|
|
4111
|
+
}
|
|
4112
|
+
return `✅ Result: ${rawResult}`;
|
|
4113
|
+
}
|
|
4114
|
+
catch (error) {
|
|
4115
|
+
throw new Error(`Failed to send command: ${error instanceof Error ? error.message : String(error)}`);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
/**
|
|
4119
|
+
* Enhanced click function with better React support
|
|
4120
|
+
*/
|
|
4121
|
+
async function clickByText(text) {
|
|
4122
|
+
return sendCommandToElectron('click_by_text', { text });
|
|
4123
|
+
}
|
|
4124
|
+
/**
|
|
4125
|
+
* Enhanced input filling with React state management
|
|
4126
|
+
*/
|
|
4127
|
+
async function fillInput(searchText, value, selector) {
|
|
4128
|
+
return sendCommandToElectron('fill_input', {
|
|
4129
|
+
selector,
|
|
4130
|
+
value,
|
|
4131
|
+
text: searchText,
|
|
4132
|
+
});
|
|
4133
|
+
}
|
|
4134
|
+
/**
|
|
4135
|
+
* Enhanced select option with proper event handling
|
|
4136
|
+
*/
|
|
4137
|
+
async function selectOption(value, selector, text) {
|
|
4138
|
+
return sendCommandToElectron('select_option', {
|
|
4139
|
+
selector,
|
|
4140
|
+
value,
|
|
4141
|
+
text,
|
|
4142
|
+
});
|
|
4143
|
+
}
|
|
4144
|
+
/**
|
|
4145
|
+
* Get comprehensive page structure analysis
|
|
4146
|
+
*/
|
|
4147
|
+
async function getPageStructure() {
|
|
4148
|
+
return sendCommandToElectron('get_page_structure');
|
|
4149
|
+
}
|
|
4150
|
+
/**
|
|
4151
|
+
* Get enhanced element analysis
|
|
4152
|
+
*/
|
|
4153
|
+
async function findElements() {
|
|
4154
|
+
return sendCommandToElectron('find_elements');
|
|
4155
|
+
}
|
|
4156
|
+
/**
|
|
4157
|
+
* Execute custom JavaScript with error handling
|
|
4158
|
+
*/
|
|
4159
|
+
async function executeCustomScript(code) {
|
|
4160
|
+
return sendCommandToElectron('eval', { code });
|
|
4161
|
+
}
|
|
4162
|
+
/**
|
|
4163
|
+
* Get debugging information about page elements
|
|
4164
|
+
*/
|
|
4165
|
+
async function debugElements() {
|
|
4166
|
+
return sendCommandToElectron('debug_elements');
|
|
4167
|
+
}
|
|
4168
|
+
/**
|
|
4169
|
+
* Verify current form state and validation
|
|
4170
|
+
*/
|
|
4171
|
+
async function verifyFormState() {
|
|
4172
|
+
return sendCommandToElectron('verify_form_state');
|
|
4173
|
+
}
|
|
4174
|
+
async function getTitle() {
|
|
4175
|
+
return sendCommandToElectron('get_title');
|
|
4176
|
+
}
|
|
4177
|
+
async function getUrl() {
|
|
4178
|
+
return sendCommandToElectron('get_url');
|
|
4179
|
+
}
|
|
4180
|
+
async function getBodyText() {
|
|
4181
|
+
return sendCommandToElectron('get_body_text');
|
|
4182
|
+
}
|
|
4183
|
+
|
|
4184
|
+
;// ./src/utils/electron-logs.ts
|
|
4185
|
+
|
|
4186
|
+
|
|
4187
|
+
|
|
4188
|
+
|
|
4189
|
+
/**
|
|
4190
|
+
* Read logs from running Electron applications
|
|
4191
|
+
*/
|
|
4192
|
+
async function readElectronLogs(logType = 'all', lines = 100, follow = false) {
|
|
4193
|
+
try {
|
|
4194
|
+
logger.info('[MCP] Looking for running Electron applications for log access...');
|
|
4195
|
+
try {
|
|
4196
|
+
const target = await findElectronTarget();
|
|
4197
|
+
// Connect via WebSocket to get console logs
|
|
4198
|
+
if (logType === 'console' || logType === 'all') {
|
|
4199
|
+
return await getConsoleLogsViaDevTools(target, lines, follow);
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
catch {
|
|
4203
|
+
logger.info('[MCP] No DevTools connection found, checking system logs...');
|
|
4204
|
+
}
|
|
4205
|
+
// Fallback to system logs if DevTools not available
|
|
4206
|
+
return await getSystemElectronLogs(lines);
|
|
4207
|
+
}
|
|
4208
|
+
catch (error) {
|
|
4209
|
+
throw new Error(`Failed to read logs: ${error instanceof Error ? error.message : String(error)}`);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
/**
|
|
4213
|
+
* Get console logs via Chrome DevTools Protocol
|
|
4214
|
+
*/
|
|
4215
|
+
async function getConsoleLogsViaDevTools(target, lines, follow) {
|
|
4216
|
+
const logs = [];
|
|
4217
|
+
return new Promise((resolve, reject) => {
|
|
4218
|
+
(async () => {
|
|
4219
|
+
try {
|
|
4220
|
+
const ws = await connectForLogs(target, (log) => {
|
|
4221
|
+
logs.push(log);
|
|
4222
|
+
if (logs.length >= lines && !follow) {
|
|
4223
|
+
ws.close();
|
|
4224
|
+
resolve(logs.slice(-lines).join('\n'));
|
|
4225
|
+
}
|
|
4226
|
+
});
|
|
4227
|
+
// For non-follow mode, try to get console history first
|
|
4228
|
+
if (!follow) {
|
|
4229
|
+
// Request console API calls from Runtime
|
|
4230
|
+
ws.send(JSON.stringify({
|
|
4231
|
+
id: 99,
|
|
4232
|
+
method: 'Runtime.evaluate',
|
|
4233
|
+
params: {
|
|
4234
|
+
expression: `console.log("Reading console history for MCP test"); "History checked"`,
|
|
4235
|
+
includeCommandLineAPI: true,
|
|
4236
|
+
},
|
|
4237
|
+
}));
|
|
4238
|
+
// Wait longer for logs to be captured and history to be available
|
|
4239
|
+
setTimeout(() => {
|
|
4240
|
+
ws.close();
|
|
4241
|
+
resolve(logs.length > 0 ? logs.slice(-lines).join('\n') : 'No console logs available');
|
|
4242
|
+
}, 7000); // Increased timeout to 7 seconds
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
catch (error) {
|
|
4246
|
+
reject(error);
|
|
4247
|
+
}
|
|
4248
|
+
})();
|
|
4249
|
+
});
|
|
4250
|
+
}
|
|
4251
|
+
/**
|
|
4252
|
+
* Get system logs for Electron processes
|
|
4253
|
+
*/
|
|
4254
|
+
async function getSystemElectronLogs(lines = 100) {
|
|
4255
|
+
logger.info('[MCP] Reading system logs for Electron processes...');
|
|
4256
|
+
try {
|
|
4257
|
+
const execAsync = (0,external_util_namespaceObject.promisify)(external_child_process_namespaceObject.exec);
|
|
4258
|
+
// Get running Electron processes
|
|
4259
|
+
const { stdout } = await execAsync('ps aux | grep -i electron | grep -v grep');
|
|
4260
|
+
const electronProcesses = stdout
|
|
4261
|
+
.trim()
|
|
4262
|
+
.split('\n')
|
|
4263
|
+
.filter((line) => line.length > 0);
|
|
4264
|
+
if (electronProcesses.length === 0) {
|
|
4265
|
+
return 'No Electron processes found running on the system.';
|
|
4266
|
+
}
|
|
4267
|
+
let logOutput = `Found ${electronProcesses.length} Electron process(es):\n\n`;
|
|
4268
|
+
electronProcesses.forEach((process, index) => {
|
|
4269
|
+
const parts = process.trim().split(/\s+/);
|
|
4270
|
+
const pid = parts[1];
|
|
4271
|
+
const command = parts.slice(10).join(' ');
|
|
4272
|
+
logOutput += `Process ${index + 1}:\n`;
|
|
4273
|
+
logOutput += ` PID: ${pid}\n`;
|
|
4274
|
+
logOutput += ` Command: ${command}\n\n`;
|
|
4275
|
+
});
|
|
4276
|
+
try {
|
|
4277
|
+
const { stdout: logContent } = await execAsync(`log show --last 1h --predicate 'process == "Electron"' --style compact | tail -${lines}`);
|
|
4278
|
+
if (logContent.trim()) {
|
|
4279
|
+
logOutput += 'Recent Electron logs from system:\n';
|
|
4280
|
+
logOutput += '==========================================\n';
|
|
4281
|
+
logOutput += logContent;
|
|
4282
|
+
}
|
|
4283
|
+
else {
|
|
4284
|
+
logOutput +=
|
|
4285
|
+
'No recent Electron logs found in system logs. Try enabling remote debugging with --remote-debugging-port=9222 for better log access.';
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
catch {
|
|
4289
|
+
logOutput +=
|
|
4290
|
+
'Could not access system logs. For detailed logging, start Electron app with --remote-debugging-port=9222';
|
|
4291
|
+
}
|
|
4292
|
+
return logOutput;
|
|
4293
|
+
}
|
|
4294
|
+
catch (error) {
|
|
4295
|
+
return `Error reading system logs: ${error instanceof Error ? error.message : String(error)}`;
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
;// external "playwright"
|
|
4300
|
+
const external_playwright_namespaceObject = require("playwright");
|
|
4301
|
+
;// external "fs/promises"
|
|
4302
|
+
const promises_namespaceObject = require("fs/promises");
|
|
4303
|
+
;// ./src/screenshot.ts
|
|
4304
|
+
|
|
4305
|
+
|
|
4306
|
+
|
|
4307
|
+
|
|
4308
|
+
/**
|
|
4309
|
+
* Take a screenshot of the running Electron application using Chrome DevTools Protocol
|
|
4310
|
+
*/
|
|
4311
|
+
async function takeScreenshot(outputPath, windowTitle) {
|
|
4312
|
+
logger.info('📸 Taking screenshot of Electron application', {
|
|
4313
|
+
outputPath,
|
|
4314
|
+
windowTitle,
|
|
4315
|
+
timestamp: new Date().toISOString(),
|
|
4316
|
+
});
|
|
4317
|
+
try {
|
|
4318
|
+
// Find running Electron applications
|
|
4319
|
+
const apps = await scanForElectronApps();
|
|
4320
|
+
if (apps.length === 0) {
|
|
4321
|
+
throw new Error('No running Electron applications found with remote debugging enabled');
|
|
4322
|
+
}
|
|
4323
|
+
// Use the first app found (or find by title if specified)
|
|
4324
|
+
let targetApp = apps[0];
|
|
4325
|
+
if (windowTitle) {
|
|
4326
|
+
const namedApp = apps.find((app) => app.targets.some((target) => target.title?.toLowerCase().includes(windowTitle.toLowerCase())));
|
|
4327
|
+
if (namedApp) {
|
|
4328
|
+
targetApp = namedApp;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
// Connect to the Electron app's debugging port
|
|
4332
|
+
const browser = await external_playwright_namespaceObject.chromium.connectOverCDP(`http://localhost:${targetApp.port}`);
|
|
4333
|
+
const contexts = browser.contexts();
|
|
4334
|
+
if (contexts.length === 0) {
|
|
4335
|
+
throw new Error('No browser contexts found - make sure Electron app is running with remote debugging enabled');
|
|
4336
|
+
}
|
|
4337
|
+
const context = contexts[0];
|
|
4338
|
+
const pages = context.pages();
|
|
4339
|
+
if (pages.length === 0) {
|
|
4340
|
+
throw new Error('No pages found in the browser context');
|
|
4341
|
+
}
|
|
4342
|
+
// Find the main application page (skip DevTools pages)
|
|
4343
|
+
let targetPage = pages[0];
|
|
4344
|
+
for (const page of pages) {
|
|
4345
|
+
const url = page.url();
|
|
4346
|
+
const title = await page.title().catch(() => '');
|
|
4347
|
+
// Skip DevTools and about:blank pages
|
|
4348
|
+
if (!url.includes('devtools://') &&
|
|
4349
|
+
!url.includes('about:blank') &&
|
|
4350
|
+
title &&
|
|
4351
|
+
!title.includes('DevTools')) {
|
|
4352
|
+
// If windowTitle is specified, try to match it
|
|
4353
|
+
if (windowTitle && title.toLowerCase().includes(windowTitle.toLowerCase())) {
|
|
4354
|
+
targetPage = page;
|
|
4355
|
+
break;
|
|
4356
|
+
}
|
|
4357
|
+
else if (!windowTitle) {
|
|
4358
|
+
targetPage = page;
|
|
4359
|
+
break;
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
logger.info(`Taking screenshot of page: ${targetPage.url()} (${await targetPage.title()})`);
|
|
4364
|
+
// Take screenshot as buffer (in memory)
|
|
4365
|
+
const screenshotBuffer = await targetPage.screenshot({
|
|
4366
|
+
type: 'png',
|
|
4367
|
+
fullPage: false,
|
|
4368
|
+
});
|
|
4369
|
+
await browser.close();
|
|
4370
|
+
// Convert buffer to base64 for transmission
|
|
4371
|
+
const base64Data = screenshotBuffer.toString('base64');
|
|
4372
|
+
logger.info(`Screenshot captured successfully (${screenshotBuffer.length} bytes)`);
|
|
4373
|
+
// If outputPath is provided, save to file
|
|
4374
|
+
if (outputPath) {
|
|
4375
|
+
await promises_namespaceObject.writeFile(outputPath, screenshotBuffer);
|
|
4376
|
+
return {
|
|
4377
|
+
filePath: outputPath,
|
|
4378
|
+
base64: base64Data,
|
|
4379
|
+
data: `Screenshot saved to: ${outputPath}`,
|
|
4380
|
+
};
|
|
4381
|
+
}
|
|
4382
|
+
else {
|
|
4383
|
+
return {
|
|
4384
|
+
base64: base64Data,
|
|
4385
|
+
data: `Screenshot captured as base64 data (${screenshotBuffer.length} bytes)`,
|
|
4386
|
+
};
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
catch (error) {
|
|
4390
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4391
|
+
throw new Error(`Screenshot failed: ${errorMessage}. Make sure the Electron app is running with remote debugging enabled (--remote-debugging-port=9222)`);
|
|
4392
|
+
}
|
|
4393
|
+
}
|
|
4394
|
+
|
|
4395
|
+
;// ./src/handlers.ts
|
|
4396
|
+
|
|
4397
|
+
|
|
4398
|
+
|
|
4399
|
+
|
|
4400
|
+
|
|
4401
|
+
|
|
4402
|
+
|
|
4403
|
+
async function handleToolCall(request) {
|
|
4404
|
+
const { name, arguments: args } = request.params;
|
|
4405
|
+
try {
|
|
4406
|
+
switch (name) {
|
|
4407
|
+
case ToolName.GET_ELECTRON_WINDOW_INFO: {
|
|
4408
|
+
const { includeChildren } = GetElectronWindowInfoSchema.parse(args);
|
|
4409
|
+
const result = await getElectronWindowInfo(includeChildren);
|
|
4410
|
+
return {
|
|
4411
|
+
content: [
|
|
4412
|
+
{
|
|
4413
|
+
type: 'text',
|
|
4414
|
+
text: `Window Information:\n\n${JSON.stringify(result, null, 2)}`,
|
|
4415
|
+
},
|
|
4416
|
+
],
|
|
4417
|
+
isError: false,
|
|
4418
|
+
};
|
|
4419
|
+
}
|
|
4420
|
+
case ToolName.TAKE_SCREENSHOT: {
|
|
4421
|
+
const { outputPath, windowTitle } = TakeScreenshotSchema.parse(args);
|
|
4422
|
+
const result = await takeScreenshot(outputPath, windowTitle);
|
|
4423
|
+
const content = [];
|
|
4424
|
+
if (result.filePath) {
|
|
4425
|
+
content.push({
|
|
4426
|
+
type: 'text',
|
|
4427
|
+
text: `Screenshot saved to: ${result.filePath}`,
|
|
4428
|
+
});
|
|
4429
|
+
}
|
|
4430
|
+
else {
|
|
4431
|
+
content.push({
|
|
4432
|
+
type: 'text',
|
|
4433
|
+
text: 'Screenshot captured in memory (no file saved)',
|
|
4434
|
+
});
|
|
4435
|
+
}
|
|
4436
|
+
// Add the image data for AI evaluation
|
|
4437
|
+
content.push({
|
|
4438
|
+
type: 'image',
|
|
4439
|
+
data: result.base64,
|
|
4440
|
+
mimeType: 'image/png',
|
|
4441
|
+
});
|
|
4442
|
+
return { content, isError: false };
|
|
4443
|
+
}
|
|
4444
|
+
case ToolName.SEND_COMMAND_TO_ELECTRON: {
|
|
4445
|
+
const { command, args: commandArgs, targetId, windowTitle, } = SendCommandToElectronSchema.parse(args);
|
|
4446
|
+
// Build window target options if specified
|
|
4447
|
+
const windowOptions = targetId || windowTitle ? { targetId, windowTitle } : undefined;
|
|
4448
|
+
const result = await sendCommandToElectron(command, commandArgs, windowOptions);
|
|
4449
|
+
return {
|
|
4450
|
+
content: [{ type: 'text', text: result }],
|
|
4451
|
+
isError: false,
|
|
4452
|
+
};
|
|
4453
|
+
}
|
|
4454
|
+
case ToolName.READ_ELECTRON_LOGS: {
|
|
4455
|
+
const { logType, lines, follow } = ReadElectronLogsSchema.parse(args);
|
|
4456
|
+
const logs = await readElectronLogs(logType, lines);
|
|
4457
|
+
if (follow) {
|
|
4458
|
+
return {
|
|
4459
|
+
content: [
|
|
4460
|
+
{
|
|
4461
|
+
type: 'text',
|
|
4462
|
+
text: `Following logs (${logType}). This is a snapshot of recent logs:\n\n${logs}`,
|
|
4463
|
+
},
|
|
4464
|
+
],
|
|
4465
|
+
isError: false,
|
|
4466
|
+
};
|
|
4467
|
+
}
|
|
4468
|
+
return {
|
|
4469
|
+
content: [
|
|
4470
|
+
{
|
|
4471
|
+
type: 'text',
|
|
4472
|
+
text: `Electron logs (${logType}):\n\n${logs}`,
|
|
4473
|
+
},
|
|
4474
|
+
],
|
|
4475
|
+
isError: false,
|
|
4476
|
+
};
|
|
4477
|
+
}
|
|
4478
|
+
case ToolName.LIST_ELECTRON_WINDOWS: {
|
|
4479
|
+
const { includeDevTools } = ListElectronWindowsSchema.parse(args);
|
|
4480
|
+
const windows = await listElectronWindows(includeDevTools);
|
|
4481
|
+
if (windows.length === 0) {
|
|
4482
|
+
return {
|
|
4483
|
+
content: [
|
|
4484
|
+
{
|
|
4485
|
+
type: 'text',
|
|
4486
|
+
text: 'No Electron windows found. Ensure your app is running with --remote-debugging-port=9222',
|
|
4487
|
+
},
|
|
4488
|
+
],
|
|
4489
|
+
isError: false,
|
|
4490
|
+
};
|
|
4491
|
+
}
|
|
4492
|
+
const formatted = windows
|
|
4493
|
+
.map((w) => `- [${w.id}] "${w.title}" (port: ${w.port}, type: ${w.type})\n URL: ${w.url}`)
|
|
4494
|
+
.join('\n');
|
|
4495
|
+
return {
|
|
4496
|
+
content: [
|
|
4497
|
+
{
|
|
4498
|
+
type: 'text',
|
|
4499
|
+
text: `Available Electron windows (${windows.length}):\n\n${formatted}`,
|
|
4500
|
+
},
|
|
4501
|
+
],
|
|
4502
|
+
isError: false,
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
default:
|
|
4506
|
+
return {
|
|
4507
|
+
content: [
|
|
4508
|
+
{
|
|
4509
|
+
type: 'text',
|
|
4510
|
+
text: `Unknown tool: ${name}`,
|
|
4511
|
+
},
|
|
4512
|
+
],
|
|
4513
|
+
isError: true,
|
|
4514
|
+
};
|
|
4515
|
+
}
|
|
4516
|
+
}
|
|
4517
|
+
catch (error) {
|
|
4518
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4519
|
+
const errorStack = error instanceof Error ? error.stack : undefined;
|
|
4520
|
+
logger.error(`Tool execution failed: ${name}`, {
|
|
4521
|
+
error: errorMessage,
|
|
4522
|
+
stack: errorStack,
|
|
4523
|
+
args,
|
|
4524
|
+
});
|
|
4525
|
+
return {
|
|
4526
|
+
content: [
|
|
4527
|
+
{
|
|
4528
|
+
type: 'text',
|
|
4529
|
+
text: `Error executing ${name}: ${errorMessage}`,
|
|
4530
|
+
},
|
|
4531
|
+
],
|
|
4532
|
+
isError: true,
|
|
4533
|
+
};
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4536
|
+
|
|
4537
|
+
;// ./src/index.ts
|
|
4538
|
+
//#!/usr/bin/env node
|
|
4539
|
+
|
|
4540
|
+
|
|
4541
|
+
|
|
4542
|
+
|
|
4543
|
+
|
|
4544
|
+
|
|
4545
|
+
// Create MCP server instance
|
|
4546
|
+
const server = new Server({
|
|
4547
|
+
name: '@debugelectron/debug-electron-mcp',
|
|
4548
|
+
version: '1.6.7',
|
|
4549
|
+
}, {
|
|
4550
|
+
capabilities: {
|
|
4551
|
+
tools: {},
|
|
4552
|
+
},
|
|
4553
|
+
});
|
|
4554
|
+
// List available tools
|
|
4555
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
4556
|
+
logger.debug('Listing tools request received');
|
|
4557
|
+
return { tools: tools };
|
|
4558
|
+
});
|
|
4559
|
+
// Handle tool execution
|
|
4560
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
4561
|
+
const start = Date.now();
|
|
4562
|
+
logger.info(`Tool call: ${request.params.name}`);
|
|
4563
|
+
logger.debug(`Tool call args:`, JSON.stringify(request.params.arguments, null, 2));
|
|
4564
|
+
const result = await handleToolCall(request);
|
|
4565
|
+
const duration = Date.now() - start;
|
|
4566
|
+
if (duration > 1000) {
|
|
4567
|
+
logger.warn(`Slow tool execution: ${request.params.name} took ${duration}ms`);
|
|
4568
|
+
}
|
|
4569
|
+
// Log result but truncate large base64 data to avoid spam
|
|
4570
|
+
if (logger.isEnabled(2)) {
|
|
4571
|
+
// Only if DEBUG level
|
|
4572
|
+
const logResult = { ...result };
|
|
4573
|
+
if (logResult.content && Array.isArray(logResult.content)) {
|
|
4574
|
+
logResult.content = logResult.content.map((item) => {
|
|
4575
|
+
if (item.type === 'text' &&
|
|
4576
|
+
item.text &&
|
|
4577
|
+
typeof item.text === 'string' &&
|
|
4578
|
+
item.text.length > 1000) {
|
|
4579
|
+
return {
|
|
4580
|
+
...item,
|
|
4581
|
+
text: item.text.substring(0, 100) + '... [truncated]',
|
|
4582
|
+
};
|
|
4583
|
+
}
|
|
4584
|
+
if (item.type === 'image' &&
|
|
4585
|
+
item.data &&
|
|
4586
|
+
typeof item.data === 'string' &&
|
|
4587
|
+
item.data.length > 100) {
|
|
4588
|
+
return {
|
|
4589
|
+
...item,
|
|
4590
|
+
data: item.data.substring(0, 50) + '... [base64 truncated]',
|
|
4591
|
+
};
|
|
4592
|
+
}
|
|
4593
|
+
return item;
|
|
4594
|
+
});
|
|
4595
|
+
}
|
|
4596
|
+
logger.debug(`Tool call result:`, JSON.stringify(logResult, null, 2));
|
|
4597
|
+
}
|
|
4598
|
+
return result;
|
|
4599
|
+
});
|
|
4600
|
+
// Start the server
|
|
4601
|
+
async function main() {
|
|
4602
|
+
const transport = new StdioServerTransport();
|
|
4603
|
+
logger.info('Electron MCP Server starting...');
|
|
4604
|
+
await server.connect(transport);
|
|
4605
|
+
logger.info('Electron MCP Server running on stdio');
|
|
4606
|
+
logger.info('Available tools:', tools.map((t) => t.name).join(', '));
|
|
4607
|
+
}
|
|
4608
|
+
main().catch((error) => {
|
|
4609
|
+
logger.error('Server error:', error);
|
|
4610
|
+
process.exit(1);
|
|
4611
|
+
});
|
|
4612
|
+
|
|
4613
|
+
/******/ })()
|
|
4614
|
+
;
|
|
4615
|
+
//# sourceMappingURL=index.js.map
|