@xiaou66/vite-plugin-vue-mcp-next 0.0.1
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 +531 -0
- package/dist/index.cjs +1519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +410 -0
- package/dist/index.d.ts +410 -0
- package/dist/index.js +1482 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime/client.cjs +691 -0
- package/dist/runtime/client.cjs.map +1 -0
- package/dist/runtime/client.d.cts +30 -0
- package/dist/runtime/client.d.ts +30 -0
- package/dist/runtime/client.js +672 -0
- package/dist/runtime/client.js.map +1 -0
- package/package.json +104 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1519 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
default: () => vueMcpNext,
|
|
34
|
+
vueMcpNext: () => vueMcpNext
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/plugin/createPlugin.ts
|
|
39
|
+
var import_vite2 = require("vite");
|
|
40
|
+
|
|
41
|
+
// src/shared/limits.ts
|
|
42
|
+
var DEFAULT_DOM_MAX_DEPTH = 8;
|
|
43
|
+
var DEFAULT_DOM_MAX_NODES = 2e3;
|
|
44
|
+
var DEFAULT_DOM_MAX_TEXT_LENGTH = 300;
|
|
45
|
+
var DEFAULT_CONSOLE_MAX_RECORDS = 1e3;
|
|
46
|
+
var DEFAULT_NETWORK_MAX_RECORDS = 500;
|
|
47
|
+
var DEFAULT_NETWORK_MAX_BODY_SIZE = 1e5;
|
|
48
|
+
var DEFAULT_MASK_HEADERS = [
|
|
49
|
+
"authorization",
|
|
50
|
+
"cookie",
|
|
51
|
+
"set-cookie"
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
// src/constants.ts
|
|
55
|
+
var DEFAULT_MCP_PATH = "/__mcp";
|
|
56
|
+
var MCP_TOOL_NAMES = {
|
|
57
|
+
listPages: "list_pages",
|
|
58
|
+
getPageState: "get_page_state",
|
|
59
|
+
getDomTree: "get_dom_tree",
|
|
60
|
+
queryDom: "query_dom",
|
|
61
|
+
getConsoleLogs: "get_console_logs",
|
|
62
|
+
clearConsoleLogs: "clear_console_logs",
|
|
63
|
+
evaluateScript: "evaluate_script",
|
|
64
|
+
getNetworkRequests: "get_network_requests",
|
|
65
|
+
getNetworkRequestDetail: "get_network_request_detail",
|
|
66
|
+
clearNetworkRequests: "clear_network_requests",
|
|
67
|
+
getComponentTree: "get_component_tree",
|
|
68
|
+
getComponentState: "get_component_state",
|
|
69
|
+
editComponentState: "edit_component_state",
|
|
70
|
+
highlightComponent: "highlight_component",
|
|
71
|
+
getRouterInfo: "get_router_info",
|
|
72
|
+
getPiniaTree: "get_pinia_tree",
|
|
73
|
+
getPiniaState: "get_pinia_state"
|
|
74
|
+
};
|
|
75
|
+
var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
|
|
76
|
+
var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
|
|
77
|
+
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vue-mcp-next";
|
|
78
|
+
var DEFAULT_OPTIONS = {
|
|
79
|
+
mcpPath: DEFAULT_MCP_PATH,
|
|
80
|
+
host: "localhost",
|
|
81
|
+
printUrl: true,
|
|
82
|
+
updateCursorMcpJson: {
|
|
83
|
+
enabled: true,
|
|
84
|
+
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
85
|
+
},
|
|
86
|
+
mcpClients: {
|
|
87
|
+
cursor: true,
|
|
88
|
+
codex: true,
|
|
89
|
+
claudeCode: true,
|
|
90
|
+
trae: true,
|
|
91
|
+
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
92
|
+
},
|
|
93
|
+
runtime: {
|
|
94
|
+
mode: "auto",
|
|
95
|
+
evaluate: {
|
|
96
|
+
enabled: false,
|
|
97
|
+
timeoutMs: 3e3
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
cdp: {},
|
|
101
|
+
network: {
|
|
102
|
+
mode: "auto",
|
|
103
|
+
maxRecords: DEFAULT_NETWORK_MAX_RECORDS,
|
|
104
|
+
captureRequestBody: true,
|
|
105
|
+
captureResponseBody: true,
|
|
106
|
+
maxBodySize: DEFAULT_NETWORK_MAX_BODY_SIZE,
|
|
107
|
+
maskHeaders: [...DEFAULT_MASK_HEADERS]
|
|
108
|
+
},
|
|
109
|
+
dom: {
|
|
110
|
+
maxDepth: DEFAULT_DOM_MAX_DEPTH,
|
|
111
|
+
maxNodes: DEFAULT_DOM_MAX_NODES,
|
|
112
|
+
maxTextLength: DEFAULT_DOM_MAX_TEXT_LENGTH
|
|
113
|
+
},
|
|
114
|
+
console: {
|
|
115
|
+
maxRecords: DEFAULT_CONSOLE_MAX_RECORDS
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
function mergeMcpClientOptions(cursorConfig, mcpClients) {
|
|
119
|
+
return {
|
|
120
|
+
...DEFAULT_OPTIONS.mcpClients,
|
|
121
|
+
cursor: cursorConfig.enabled,
|
|
122
|
+
serverName: cursorConfig.serverName,
|
|
123
|
+
...mcpClients
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function mergeOptions(options = {}) {
|
|
127
|
+
const cursorConfig = typeof options.updateCursorMcpJson === "boolean" ? {
|
|
128
|
+
enabled: options.updateCursorMcpJson,
|
|
129
|
+
serverName: DEFAULT_OPTIONS.updateCursorMcpJson.serverName
|
|
130
|
+
} : {
|
|
131
|
+
...DEFAULT_OPTIONS.updateCursorMcpJson,
|
|
132
|
+
...options.updateCursorMcpJson
|
|
133
|
+
};
|
|
134
|
+
const mcpClients = mergeMcpClientOptions(cursorConfig, options.mcpClients);
|
|
135
|
+
return {
|
|
136
|
+
...DEFAULT_OPTIONS,
|
|
137
|
+
...options,
|
|
138
|
+
updateCursorMcpJson: cursorConfig,
|
|
139
|
+
mcpClients,
|
|
140
|
+
runtime: {
|
|
141
|
+
...DEFAULT_OPTIONS.runtime,
|
|
142
|
+
...options.runtime,
|
|
143
|
+
evaluate: {
|
|
144
|
+
...DEFAULT_OPTIONS.runtime.evaluate,
|
|
145
|
+
...options.runtime?.evaluate
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
cdp: {
|
|
149
|
+
...DEFAULT_OPTIONS.cdp,
|
|
150
|
+
...options.cdp
|
|
151
|
+
},
|
|
152
|
+
network: {
|
|
153
|
+
...DEFAULT_OPTIONS.network,
|
|
154
|
+
...options.network,
|
|
155
|
+
maskHeaders: options.network?.maskHeaders ?? [
|
|
156
|
+
...DEFAULT_OPTIONS.network.maskHeaders
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
dom: {
|
|
160
|
+
...DEFAULT_OPTIONS.dom,
|
|
161
|
+
...options.dom
|
|
162
|
+
},
|
|
163
|
+
console: {
|
|
164
|
+
...DEFAULT_OPTIONS.console,
|
|
165
|
+
...options.console
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/context.ts
|
|
171
|
+
var import_hookable = require("hookable");
|
|
172
|
+
|
|
173
|
+
// src/shared/ringBuffer.ts
|
|
174
|
+
function createRingBuffer(capacity) {
|
|
175
|
+
const items = [];
|
|
176
|
+
return {
|
|
177
|
+
push(value) {
|
|
178
|
+
items.push(value);
|
|
179
|
+
while (items.length > capacity) {
|
|
180
|
+
items.shift();
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
all() {
|
|
184
|
+
return [...items];
|
|
185
|
+
},
|
|
186
|
+
clear() {
|
|
187
|
+
items.length = 0;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/context.ts
|
|
193
|
+
function createPageTargetRegistry() {
|
|
194
|
+
const targets = /* @__PURE__ */ new Map();
|
|
195
|
+
return {
|
|
196
|
+
upsert(target) {
|
|
197
|
+
targets.set(target.pageId, target);
|
|
198
|
+
},
|
|
199
|
+
get(pageId) {
|
|
200
|
+
return targets.get(pageId);
|
|
201
|
+
},
|
|
202
|
+
list() {
|
|
203
|
+
return [...targets.values()];
|
|
204
|
+
},
|
|
205
|
+
disconnect(pageId) {
|
|
206
|
+
const target = targets.get(pageId);
|
|
207
|
+
if (!target) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
targets.set(pageId, { ...target, connected: false });
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function createVueMcpNextContext(options) {
|
|
215
|
+
return {
|
|
216
|
+
options,
|
|
217
|
+
hooks: (0, import_hookable.createHooks)(),
|
|
218
|
+
rpcServer: void 0,
|
|
219
|
+
pages: createPageTargetRegistry(),
|
|
220
|
+
consoleRecords: createRingBuffer(options.console.maxRecords),
|
|
221
|
+
networkRecords: createRingBuffer(options.network.maxRecords)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/mcp/createMcpServer.ts
|
|
226
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
227
|
+
|
|
228
|
+
// src/mcp/tools/console.ts
|
|
229
|
+
var import_zod = require("zod");
|
|
230
|
+
|
|
231
|
+
// src/mcp/routeTools.ts
|
|
232
|
+
var import_nanoid = require("nanoid");
|
|
233
|
+
|
|
234
|
+
// src/cdp/cdpClient.ts
|
|
235
|
+
var import_chrome_remote_interface = __toESM(require("chrome-remote-interface"), 1);
|
|
236
|
+
function createCdpClient(options) {
|
|
237
|
+
return {
|
|
238
|
+
async listTargets() {
|
|
239
|
+
if (!options.browserUrl) {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
const listUrl = new URL("/json/list", options.browserUrl);
|
|
243
|
+
const response = await fetch(listUrl);
|
|
244
|
+
return await response.json();
|
|
245
|
+
},
|
|
246
|
+
async connect(wsEndpoint) {
|
|
247
|
+
return (0, import_chrome_remote_interface.default)({ target: wsEndpoint });
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/cdp/targetMatcher.ts
|
|
253
|
+
function matchCdpTarget(targets, options) {
|
|
254
|
+
const pageTargets = targets.filter(
|
|
255
|
+
(target) => target.type === "page" && target.webSocketDebuggerUrl
|
|
256
|
+
);
|
|
257
|
+
if (options.url) {
|
|
258
|
+
const exact = pageTargets.find((target) => target.url === options.url);
|
|
259
|
+
if (exact) {
|
|
260
|
+
return exact;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (options.targetUrlPattern) {
|
|
264
|
+
const pattern = options.targetUrlPattern;
|
|
265
|
+
return pageTargets.find(
|
|
266
|
+
(target) => typeof pattern === "string" ? target.url.includes(pattern) : pattern.test(target.url)
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return pageTargets[0];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/mcp/routeTools.ts
|
|
273
|
+
function shouldUseCdp(options) {
|
|
274
|
+
if (options.capabilityMode === "off" || options.capabilityMode === "hook") {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
if (!options.hasMatchedCdpTarget) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
return Boolean(
|
|
281
|
+
options.options.cdp.browserUrl || options.options.cdp.wsEndpoint
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
function createToolResponse(data) {
|
|
285
|
+
return {
|
|
286
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
287
|
+
structuredContent: data
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function createToolError(message, data) {
|
|
291
|
+
return createToolResponse({
|
|
292
|
+
ok: false,
|
|
293
|
+
error: message,
|
|
294
|
+
data
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
function resolvePageTarget(ctx, pageId) {
|
|
298
|
+
const targets = ctx.pages.list().filter((target) => target.connected);
|
|
299
|
+
if (pageId) {
|
|
300
|
+
const target = targets.find((item) => item.pageId === pageId);
|
|
301
|
+
if (!target) {
|
|
302
|
+
throw new Error(`Page target not found: ${pageId}`);
|
|
303
|
+
}
|
|
304
|
+
return target;
|
|
305
|
+
}
|
|
306
|
+
if (targets.length === 1) {
|
|
307
|
+
return targets[0];
|
|
308
|
+
}
|
|
309
|
+
throw new Error(
|
|
310
|
+
"Multiple or no page targets available. Call list_pages and pass pageId."
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
async function connectCdpForPage(ctx, pageId) {
|
|
314
|
+
if (!ctx.options.cdp.browserUrl && !ctx.options.cdp.wsEndpoint) {
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
const cdp = createCdpClient(ctx.options.cdp);
|
|
318
|
+
if (ctx.options.cdp.wsEndpoint) {
|
|
319
|
+
return { client: await cdp.connect(ctx.options.cdp.wsEndpoint) };
|
|
320
|
+
}
|
|
321
|
+
const page = resolvePageTarget(ctx, pageId);
|
|
322
|
+
const target = matchCdpTarget(await cdp.listTargets(), {
|
|
323
|
+
url: page.url,
|
|
324
|
+
targetUrlPattern: ctx.options.cdp.targetUrlPattern
|
|
325
|
+
});
|
|
326
|
+
if (!target?.webSocketDebuggerUrl) {
|
|
327
|
+
return void 0;
|
|
328
|
+
}
|
|
329
|
+
return { client: await cdp.connect(target.webSocketDebuggerUrl), target };
|
|
330
|
+
}
|
|
331
|
+
async function closeCdpClient(client) {
|
|
332
|
+
await client.close();
|
|
333
|
+
}
|
|
334
|
+
async function requestRuntimeData(ctx, trigger) {
|
|
335
|
+
if (!ctx.rpcServer) {
|
|
336
|
+
return { ok: false, error: "runtime bridge is not connected" };
|
|
337
|
+
}
|
|
338
|
+
const event = (0, import_nanoid.nanoid)();
|
|
339
|
+
return new Promise((resolve) => {
|
|
340
|
+
const timeout = setTimeout(() => {
|
|
341
|
+
resolve({ ok: false, error: "runtime bridge response timed out" });
|
|
342
|
+
}, 5e3);
|
|
343
|
+
ctx.hooks.hookOnce(event, (data) => {
|
|
344
|
+
clearTimeout(timeout);
|
|
345
|
+
resolve(data);
|
|
346
|
+
});
|
|
347
|
+
trigger(event);
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/mcp/tools/console.ts
|
|
352
|
+
function registerConsoleTools(server, ctx) {
|
|
353
|
+
server.registerTool(
|
|
354
|
+
MCP_TOOL_NAMES.getConsoleLogs,
|
|
355
|
+
{
|
|
356
|
+
description: "Get console logs for the selected page.",
|
|
357
|
+
inputSchema: {
|
|
358
|
+
pageId: import_zod.z.string().optional(),
|
|
359
|
+
level: import_zod.z.enum(["log", "info", "warn", "error", "debug"]).optional(),
|
|
360
|
+
limit: import_zod.z.number().optional()
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
(input) => {
|
|
364
|
+
const logs = ctx.consoleRecords.all().filter((record) => !input.pageId || record.pageId === input.pageId).filter((record) => !input.level || record.level === input.level).slice(-(input.limit ?? ctx.options.console.maxRecords));
|
|
365
|
+
return createToolResponse({ logs });
|
|
366
|
+
}
|
|
367
|
+
);
|
|
368
|
+
server.registerTool(
|
|
369
|
+
MCP_TOOL_NAMES.clearConsoleLogs,
|
|
370
|
+
{
|
|
371
|
+
description: "Clear cached console logs for the selected page.",
|
|
372
|
+
inputSchema: {
|
|
373
|
+
pageId: import_zod.z.string().optional()
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
() => {
|
|
377
|
+
ctx.consoleRecords.clear();
|
|
378
|
+
return createToolResponse({ ok: true });
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/mcp/tools/dom.ts
|
|
384
|
+
var import_zod2 = require("zod");
|
|
385
|
+
|
|
386
|
+
// src/cdp/cdpDom.ts
|
|
387
|
+
async function cdpGetDomSnapshot(client) {
|
|
388
|
+
await client.DOMSnapshot.enable();
|
|
389
|
+
return client.DOMSnapshot.captureSnapshot({
|
|
390
|
+
computedStyles: []
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
async function cdpQueryDom(client, selector, limit) {
|
|
394
|
+
const expression = `Array.from(document.querySelectorAll(${JSON.stringify(selector)})).slice(0, ${String(limit)}).map((el) => ({ tag: el.tagName.toLowerCase(), text: el.textContent?.trim() || '', attrs: Object.fromEntries(Array.from(el.attributes).map((attr) => [attr.name, attr.value])), rect: el.getBoundingClientRect().toJSON() }))`;
|
|
395
|
+
const result = await client.Runtime.evaluate({
|
|
396
|
+
expression,
|
|
397
|
+
returnByValue: true
|
|
398
|
+
});
|
|
399
|
+
if (result.exceptionDetails) {
|
|
400
|
+
throw new Error(result.exceptionDetails.text || "CDP query DOM failed");
|
|
401
|
+
}
|
|
402
|
+
return result.result.value;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// src/mcp/tools/dom.ts
|
|
406
|
+
function registerDomTools(server, ctx) {
|
|
407
|
+
server.registerTool(
|
|
408
|
+
MCP_TOOL_NAMES.getDomTree,
|
|
409
|
+
{
|
|
410
|
+
description: "Get a clipped DOM tree for the selected page.",
|
|
411
|
+
inputSchema: {
|
|
412
|
+
pageId: import_zod2.z.string().optional(),
|
|
413
|
+
maxDepth: import_zod2.z.number().optional(),
|
|
414
|
+
maxNodes: import_zod2.z.number().optional()
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
async (input) => {
|
|
418
|
+
const cdp = await connectCdpForPage(ctx, input.pageId);
|
|
419
|
+
if (cdp && shouldUseCdp({
|
|
420
|
+
options: ctx.options,
|
|
421
|
+
capabilityMode: ctx.options.runtime.mode,
|
|
422
|
+
hasMatchedCdpTarget: true
|
|
423
|
+
})) {
|
|
424
|
+
try {
|
|
425
|
+
const snapshot2 = await cdpGetDomSnapshot(cdp.client);
|
|
426
|
+
return createToolResponse({
|
|
427
|
+
source: "cdp",
|
|
428
|
+
snapshot: snapshot2,
|
|
429
|
+
limits: {
|
|
430
|
+
maxDepth: input.maxDepth ?? ctx.options.dom.maxDepth,
|
|
431
|
+
maxNodes: input.maxNodes ?? ctx.options.dom.maxNodes
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
} finally {
|
|
435
|
+
await closeCdpClient(cdp.client);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const snapshot = await requestRuntimeData(ctx, (event) => {
|
|
439
|
+
void ctx.rpcServer?.getDomTree({
|
|
440
|
+
event,
|
|
441
|
+
maxDepth: input.maxDepth ?? ctx.options.dom.maxDepth,
|
|
442
|
+
maxNodes: input.maxNodes ?? ctx.options.dom.maxNodes,
|
|
443
|
+
maxTextLength: ctx.options.dom.maxTextLength
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
return createToolResponse({ source: "hook", snapshot });
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
server.registerTool(
|
|
450
|
+
MCP_TOOL_NAMES.queryDom,
|
|
451
|
+
{
|
|
452
|
+
description: "Query DOM nodes by selector in the selected page.",
|
|
453
|
+
inputSchema: {
|
|
454
|
+
pageId: import_zod2.z.string().optional(),
|
|
455
|
+
selector: import_zod2.z.string(),
|
|
456
|
+
limit: import_zod2.z.number().optional()
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
async (input) => {
|
|
460
|
+
const cdp = await connectCdpForPage(ctx, input.pageId);
|
|
461
|
+
if (cdp && shouldUseCdp({
|
|
462
|
+
options: ctx.options,
|
|
463
|
+
capabilityMode: ctx.options.runtime.mode,
|
|
464
|
+
hasMatchedCdpTarget: true
|
|
465
|
+
})) {
|
|
466
|
+
try {
|
|
467
|
+
const nodes2 = await cdpQueryDom(
|
|
468
|
+
cdp.client,
|
|
469
|
+
input.selector,
|
|
470
|
+
input.limit ?? 20
|
|
471
|
+
);
|
|
472
|
+
return createToolResponse({ source: "cdp", nodes: nodes2 });
|
|
473
|
+
} finally {
|
|
474
|
+
await closeCdpClient(cdp.client);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const nodes = await requestRuntimeData(ctx, (event) => {
|
|
478
|
+
void ctx.rpcServer?.queryDom({
|
|
479
|
+
event,
|
|
480
|
+
selector: input.selector,
|
|
481
|
+
limit: input.limit ?? 20
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
return createToolResponse({ source: "hook", nodes });
|
|
485
|
+
}
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// src/mcp/tools/evaluate.ts
|
|
490
|
+
var import_zod3 = require("zod");
|
|
491
|
+
|
|
492
|
+
// src/cdp/cdpEvaluate.ts
|
|
493
|
+
async function cdpEvaluate(options) {
|
|
494
|
+
const result = await options.client.Runtime.evaluate({
|
|
495
|
+
expression: options.expression,
|
|
496
|
+
awaitPromise: options.awaitPromise ?? true,
|
|
497
|
+
returnByValue: true
|
|
498
|
+
});
|
|
499
|
+
if (result.exceptionDetails) {
|
|
500
|
+
throw new Error(result.exceptionDetails.text || "CDP evaluate failed");
|
|
501
|
+
}
|
|
502
|
+
return result.result.value;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// src/mcp/tools/evaluate.ts
|
|
506
|
+
function registerEvaluateTools(server, ctx) {
|
|
507
|
+
server.registerTool(
|
|
508
|
+
MCP_TOOL_NAMES.evaluateScript,
|
|
509
|
+
{
|
|
510
|
+
description: "Evaluate a script in the selected page when explicitly enabled.",
|
|
511
|
+
inputSchema: {
|
|
512
|
+
pageId: import_zod3.z.string().optional(),
|
|
513
|
+
expression: import_zod3.z.string(),
|
|
514
|
+
awaitPromise: import_zod3.z.boolean().optional()
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
async (input) => {
|
|
518
|
+
try {
|
|
519
|
+
assertEvaluateEnabled(ctx.options);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
return createToolError(
|
|
522
|
+
error instanceof Error ? error.message : String(error)
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
const cdp = await connectCdpForPage(ctx, input.pageId);
|
|
526
|
+
if (cdp && shouldUseCdp({
|
|
527
|
+
options: ctx.options,
|
|
528
|
+
capabilityMode: ctx.options.runtime.mode,
|
|
529
|
+
hasMatchedCdpTarget: true
|
|
530
|
+
})) {
|
|
531
|
+
try {
|
|
532
|
+
const value = await cdpEvaluate({
|
|
533
|
+
client: cdp.client,
|
|
534
|
+
expression: input.expression,
|
|
535
|
+
awaitPromise: input.awaitPromise
|
|
536
|
+
});
|
|
537
|
+
return createToolResponse({ source: "cdp", value });
|
|
538
|
+
} finally {
|
|
539
|
+
await closeCdpClient(cdp.client);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
const result = await requestRuntimeData(ctx, (event) => {
|
|
543
|
+
void ctx.rpcServer?.evaluateScript({
|
|
544
|
+
event,
|
|
545
|
+
expression: input.expression,
|
|
546
|
+
awaitPromise: input.awaitPromise,
|
|
547
|
+
timeoutMs: ctx.options.runtime.evaluate.timeoutMs
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
return createToolResponse({ source: "hook", result });
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
function assertEvaluateEnabled(options) {
|
|
555
|
+
if (!options.runtime.evaluate.enabled) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
"evaluate_script is disabled. Enable runtime.evaluate.enabled to use it."
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// src/mcp/tools/network.ts
|
|
563
|
+
var import_zod4 = require("zod");
|
|
564
|
+
function registerNetworkTools(server, ctx) {
|
|
565
|
+
server.registerTool(
|
|
566
|
+
MCP_TOOL_NAMES.getNetworkRequests,
|
|
567
|
+
{
|
|
568
|
+
description: "Get captured network request summaries.",
|
|
569
|
+
inputSchema: {
|
|
570
|
+
pageId: import_zod4.z.string().optional(),
|
|
571
|
+
urlContains: import_zod4.z.string().optional(),
|
|
572
|
+
method: import_zod4.z.string().optional(),
|
|
573
|
+
status: import_zod4.z.number().optional(),
|
|
574
|
+
limit: import_zod4.z.number().optional()
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
(input) => {
|
|
578
|
+
if (ctx.options.network.mode === "off") {
|
|
579
|
+
return createToolError(
|
|
580
|
+
"Network collection is disabled by configuration"
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
const records = ctx.networkRecords.all().filter((record) => !input.pageId || record.pageId === input.pageId).filter(
|
|
584
|
+
(record) => !input.urlContains || record.url.includes(input.urlContains)
|
|
585
|
+
).filter(
|
|
586
|
+
(record) => !input.method || record.method.toUpperCase() === input.method.toUpperCase()
|
|
587
|
+
).filter(
|
|
588
|
+
(record) => input.status === void 0 || record.status === input.status
|
|
589
|
+
).slice(-(input.limit ?? ctx.options.network.maxRecords));
|
|
590
|
+
return createToolResponse({ requests: records });
|
|
591
|
+
}
|
|
592
|
+
);
|
|
593
|
+
server.registerTool(
|
|
594
|
+
MCP_TOOL_NAMES.getNetworkRequestDetail,
|
|
595
|
+
{
|
|
596
|
+
description: "Get captured network request detail by id.",
|
|
597
|
+
inputSchema: { id: import_zod4.z.string() }
|
|
598
|
+
},
|
|
599
|
+
(input) => {
|
|
600
|
+
if (ctx.options.network.mode === "off") {
|
|
601
|
+
return createToolError(
|
|
602
|
+
"Network collection is disabled by configuration"
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
const record = ctx.networkRecords.all().find((item) => item.id === input.id);
|
|
606
|
+
return createToolResponse({ request: record ?? null });
|
|
607
|
+
}
|
|
608
|
+
);
|
|
609
|
+
server.registerTool(
|
|
610
|
+
MCP_TOOL_NAMES.clearNetworkRequests,
|
|
611
|
+
{
|
|
612
|
+
description: "Clear cached network requests.",
|
|
613
|
+
inputSchema: {
|
|
614
|
+
pageId: import_zod4.z.string().optional()
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
() => {
|
|
618
|
+
ctx.networkRecords.clear();
|
|
619
|
+
return createToolResponse({ ok: true });
|
|
620
|
+
}
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/plugin/entryDiscovery.ts
|
|
625
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
626
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
627
|
+
var import_vite = require("vite");
|
|
628
|
+
function discoverHtmlEntries(server) {
|
|
629
|
+
const root = server.config.root;
|
|
630
|
+
const entries = [];
|
|
631
|
+
walkHtmlEntries(root, root, entries);
|
|
632
|
+
return entries;
|
|
633
|
+
}
|
|
634
|
+
function walkHtmlEntries(root, dir, entries) {
|
|
635
|
+
for (const item of import_node_fs.default.readdirSync(dir, { withFileTypes: true })) {
|
|
636
|
+
if (item.name === "node_modules" || item.name.startsWith(".")) {
|
|
637
|
+
continue;
|
|
638
|
+
}
|
|
639
|
+
const fullPath = import_node_path.default.join(dir, item.name);
|
|
640
|
+
if (item.isDirectory()) {
|
|
641
|
+
walkHtmlEntries(root, fullPath, entries);
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
if (!item.isFile() || !item.name.endsWith(".html")) {
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
const relative = (0, import_vite.normalizePath)(import_node_path.default.relative(root, fullPath));
|
|
648
|
+
entries.push({
|
|
649
|
+
file: relative,
|
|
650
|
+
pathname: relative === "index.html" ? "/" : `/${relative}`
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/mcp/tools/pages.ts
|
|
656
|
+
function registerPageTools(server, ctx, vite) {
|
|
657
|
+
server.registerTool(
|
|
658
|
+
MCP_TOOL_NAMES.listPages,
|
|
659
|
+
{
|
|
660
|
+
description: "List Vite page entries and connected runtime/CDP targets."
|
|
661
|
+
},
|
|
662
|
+
async () => {
|
|
663
|
+
const cdpResult = await listCdpPageTargets(ctx);
|
|
664
|
+
for (const target of cdpResult.pages) {
|
|
665
|
+
ctx.pages.upsert(target);
|
|
666
|
+
void ctx.cdpLifecycle?.connectPage(target);
|
|
667
|
+
}
|
|
668
|
+
return createToolResponse({
|
|
669
|
+
entries: discoverHtmlEntries(vite),
|
|
670
|
+
pages: ctx.pages.list(),
|
|
671
|
+
cdpError: cdpResult.error
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
async function listCdpPageTargets(ctx) {
|
|
677
|
+
if (ctx.options.cdp.wsEndpoint) {
|
|
678
|
+
return { pages: [createWsEndpointTarget(ctx.options.cdp.wsEndpoint)] };
|
|
679
|
+
}
|
|
680
|
+
if (!ctx.options.cdp.browserUrl) {
|
|
681
|
+
return { pages: [] };
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
const targets = await createCdpClient(ctx.options.cdp).listTargets();
|
|
685
|
+
return {
|
|
686
|
+
pages: targets.filter((target) => target.type === "page").map((target) => ({
|
|
687
|
+
pageId: `cdp:${target.id}`,
|
|
688
|
+
source: "cdp",
|
|
689
|
+
url: target.url,
|
|
690
|
+
pathname: getPathname(target.url),
|
|
691
|
+
title: target.title,
|
|
692
|
+
connected: Boolean(target.webSocketDebuggerUrl)
|
|
693
|
+
}))
|
|
694
|
+
};
|
|
695
|
+
} catch (error) {
|
|
696
|
+
return {
|
|
697
|
+
pages: [],
|
|
698
|
+
error: error instanceof Error ? error.message : String(error)
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function createWsEndpointTarget(wsEndpoint) {
|
|
703
|
+
return {
|
|
704
|
+
pageId: "cdp:ws-endpoint",
|
|
705
|
+
source: "cdp",
|
|
706
|
+
url: wsEndpoint,
|
|
707
|
+
pathname: "cdp:ws-endpoint",
|
|
708
|
+
title: "CDP WebSocket endpoint",
|
|
709
|
+
connected: true
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function getPathname(url) {
|
|
713
|
+
try {
|
|
714
|
+
return new URL(url).pathname;
|
|
715
|
+
} catch {
|
|
716
|
+
return url;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/mcp/tools/vue.ts
|
|
721
|
+
var import_nanoid2 = require("nanoid");
|
|
722
|
+
var import_zod5 = require("zod");
|
|
723
|
+
function registerVueTools(server, ctx) {
|
|
724
|
+
server.registerTool(
|
|
725
|
+
MCP_TOOL_NAMES.getComponentTree,
|
|
726
|
+
{ description: "Get Vue component tree." },
|
|
727
|
+
async () => requestVueData(ctx, (event) => {
|
|
728
|
+
void ctx.rpcServer?.getInspectorTree({ event });
|
|
729
|
+
})
|
|
730
|
+
);
|
|
731
|
+
server.registerTool(
|
|
732
|
+
MCP_TOOL_NAMES.getComponentState,
|
|
733
|
+
{
|
|
734
|
+
description: "Get Vue component state.",
|
|
735
|
+
inputSchema: { componentName: import_zod5.z.string() }
|
|
736
|
+
},
|
|
737
|
+
async ({ componentName }) => requestVueData(ctx, (event) => {
|
|
738
|
+
void ctx.rpcServer?.getInspectorState({ event, componentName });
|
|
739
|
+
})
|
|
740
|
+
);
|
|
741
|
+
server.registerTool(
|
|
742
|
+
MCP_TOOL_NAMES.editComponentState,
|
|
743
|
+
{
|
|
744
|
+
description: "Edit Vue component state.",
|
|
745
|
+
inputSchema: {
|
|
746
|
+
componentName: import_zod5.z.string(),
|
|
747
|
+
path: import_zod5.z.array(import_zod5.z.string()),
|
|
748
|
+
value: import_zod5.z.string(),
|
|
749
|
+
valueType: import_zod5.z.enum(["string", "number", "boolean", "object", "array"])
|
|
750
|
+
}
|
|
751
|
+
},
|
|
752
|
+
({ componentName, path: path5, value, valueType }) => {
|
|
753
|
+
if (!ctx.rpcServer) {
|
|
754
|
+
return vueBridgeUnavailable();
|
|
755
|
+
}
|
|
756
|
+
void ctx.rpcServer.editComponentState({
|
|
757
|
+
componentName,
|
|
758
|
+
path: path5,
|
|
759
|
+
value,
|
|
760
|
+
valueType
|
|
761
|
+
});
|
|
762
|
+
return createToolResponse({ ok: true });
|
|
763
|
+
}
|
|
764
|
+
);
|
|
765
|
+
server.registerTool(
|
|
766
|
+
MCP_TOOL_NAMES.highlightComponent,
|
|
767
|
+
{
|
|
768
|
+
description: "Highlight a Vue component.",
|
|
769
|
+
inputSchema: { componentName: import_zod5.z.string() }
|
|
770
|
+
},
|
|
771
|
+
({ componentName }) => {
|
|
772
|
+
if (!ctx.rpcServer) {
|
|
773
|
+
return vueBridgeUnavailable();
|
|
774
|
+
}
|
|
775
|
+
void ctx.rpcServer.highlightComponent({ componentName });
|
|
776
|
+
return createToolResponse({ ok: true });
|
|
777
|
+
}
|
|
778
|
+
);
|
|
779
|
+
server.registerTool(
|
|
780
|
+
MCP_TOOL_NAMES.getRouterInfo,
|
|
781
|
+
{ description: "Get Vue Router information." },
|
|
782
|
+
async () => requestVueData(ctx, (event) => {
|
|
783
|
+
ctx.rpcServer?.getRouterInfo({ event });
|
|
784
|
+
})
|
|
785
|
+
);
|
|
786
|
+
server.registerTool(
|
|
787
|
+
MCP_TOOL_NAMES.getPiniaTree,
|
|
788
|
+
{ description: "Get Pinia inspector tree." },
|
|
789
|
+
async () => requestVueData(ctx, (event) => {
|
|
790
|
+
void ctx.rpcServer?.getPiniaTree({ event });
|
|
791
|
+
})
|
|
792
|
+
);
|
|
793
|
+
server.registerTool(
|
|
794
|
+
MCP_TOOL_NAMES.getPiniaState,
|
|
795
|
+
{
|
|
796
|
+
description: "Get Pinia store state.",
|
|
797
|
+
inputSchema: { storeName: import_zod5.z.string() }
|
|
798
|
+
},
|
|
799
|
+
async ({ storeName }) => requestVueData(ctx, (event) => {
|
|
800
|
+
void ctx.rpcServer?.getPiniaState({ event, storeName });
|
|
801
|
+
})
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
async function requestVueData(ctx, trigger) {
|
|
805
|
+
if (!ctx.rpcServer) {
|
|
806
|
+
return vueBridgeUnavailable();
|
|
807
|
+
}
|
|
808
|
+
const event = (0, import_nanoid2.nanoid)();
|
|
809
|
+
const data = await waitForVueHook(ctx, event, () => {
|
|
810
|
+
trigger(event);
|
|
811
|
+
});
|
|
812
|
+
return {
|
|
813
|
+
...createToolResponse({ data })
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function waitForVueHook(ctx, event, trigger) {
|
|
817
|
+
return new Promise((resolve) => {
|
|
818
|
+
const timeout = setTimeout(() => {
|
|
819
|
+
resolve({ ok: false, error: "Vue runtime bridge response timed out" });
|
|
820
|
+
}, 5e3);
|
|
821
|
+
ctx.hooks.hookOnce(event, (data) => {
|
|
822
|
+
clearTimeout(timeout);
|
|
823
|
+
resolve(data);
|
|
824
|
+
});
|
|
825
|
+
trigger();
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
function vueBridgeUnavailable() {
|
|
829
|
+
return createToolError("Vue runtime bridge is not connected");
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// src/mcp/createMcpServer.ts
|
|
833
|
+
function createMcpServer(ctx, vite) {
|
|
834
|
+
const server = new import_mcp.McpServer({
|
|
835
|
+
name: "vite-plugin-vue-mcp-next",
|
|
836
|
+
version: "0.0.0"
|
|
837
|
+
});
|
|
838
|
+
registerPageTools(server, ctx, vite);
|
|
839
|
+
registerDomTools(server, ctx);
|
|
840
|
+
registerConsoleTools(server, ctx);
|
|
841
|
+
registerEvaluateTools(server, ctx);
|
|
842
|
+
registerNetworkTools(server, ctx);
|
|
843
|
+
registerVueTools(server, ctx);
|
|
844
|
+
return server;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// src/mcp/transport.ts
|
|
848
|
+
var import_sse = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
849
|
+
function setupMcpTransport(base, server, vite) {
|
|
850
|
+
const transports = /* @__PURE__ */ new Map();
|
|
851
|
+
vite.middlewares.use(`${base}/sse`, (_req, res) => {
|
|
852
|
+
const transport = new import_sse.SSEServerTransport(`${base}/messages`, res);
|
|
853
|
+
transports.set(transport.sessionId, transport);
|
|
854
|
+
res.on("close", () => {
|
|
855
|
+
transports.delete(transport.sessionId);
|
|
856
|
+
});
|
|
857
|
+
void server.connect(transport).catch((error) => {
|
|
858
|
+
res.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
vite.middlewares.use(`${base}/messages`, (req, res) => {
|
|
862
|
+
if (req.method !== "POST") {
|
|
863
|
+
res.statusCode = 405;
|
|
864
|
+
res.end("Method Not Allowed");
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
const query = new URLSearchParams(req.url?.split("?").pop() || "");
|
|
868
|
+
const sessionId = query.get("sessionId");
|
|
869
|
+
if (!sessionId) {
|
|
870
|
+
res.statusCode = 400;
|
|
871
|
+
res.end("Bad Request");
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const transport = transports.get(sessionId);
|
|
875
|
+
if (!transport) {
|
|
876
|
+
res.statusCode = 404;
|
|
877
|
+
res.end("Not Found");
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
void transport.handlePostMessage(req, res).catch((error) => {
|
|
881
|
+
res.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// src/mcp/vueRpc.ts
|
|
887
|
+
function createServerVueRuntimeRpc(ctx) {
|
|
888
|
+
return {
|
|
889
|
+
getDomTree: () => void 0,
|
|
890
|
+
onDomTreeUpdated: (event, data) => {
|
|
891
|
+
void ctx.hooks.callHook(event, data);
|
|
892
|
+
},
|
|
893
|
+
queryDom: () => void 0,
|
|
894
|
+
onDomQueryUpdated: (event, data) => {
|
|
895
|
+
void ctx.hooks.callHook(event, data);
|
|
896
|
+
},
|
|
897
|
+
evaluateScript: () => void 0,
|
|
898
|
+
onEvaluateScriptUpdated: (event, data) => {
|
|
899
|
+
void ctx.hooks.callHook(event, data);
|
|
900
|
+
},
|
|
901
|
+
getInspectorTree: () => void 0,
|
|
902
|
+
onInspectorTreeUpdated: (event, data) => {
|
|
903
|
+
void ctx.hooks.callHook(event, data);
|
|
904
|
+
},
|
|
905
|
+
getInspectorState: () => void 0,
|
|
906
|
+
onInspectorStateUpdated: (event, data) => {
|
|
907
|
+
void ctx.hooks.callHook(event, data);
|
|
908
|
+
},
|
|
909
|
+
editComponentState: () => void 0,
|
|
910
|
+
highlightComponent: () => void 0,
|
|
911
|
+
getRouterInfo: () => void 0,
|
|
912
|
+
onRouterInfoUpdated: (event, data) => {
|
|
913
|
+
void ctx.hooks.callHook(event, data);
|
|
914
|
+
},
|
|
915
|
+
getPiniaTree: () => void 0,
|
|
916
|
+
onPiniaTreeUpdated: (event, data) => {
|
|
917
|
+
void ctx.hooks.callHook(event, data);
|
|
918
|
+
},
|
|
919
|
+
getPiniaState: () => void 0,
|
|
920
|
+
onPiniaInfoUpdated: (event, data) => {
|
|
921
|
+
void ctx.hooks.callHook(event, data);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/cdp/cdpConsole.ts
|
|
927
|
+
var import_nanoid3 = require("nanoid");
|
|
928
|
+
|
|
929
|
+
// src/shared/serialization.ts
|
|
930
|
+
function safeStringify(value) {
|
|
931
|
+
if (typeof value === "string") {
|
|
932
|
+
return value;
|
|
933
|
+
}
|
|
934
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
935
|
+
const serialized = JSON.stringify(
|
|
936
|
+
value,
|
|
937
|
+
(_key, current) => {
|
|
938
|
+
if (typeof current !== "object" || current === null) {
|
|
939
|
+
return current;
|
|
940
|
+
}
|
|
941
|
+
if (seen.has(current)) {
|
|
942
|
+
return "[Circular]";
|
|
943
|
+
}
|
|
944
|
+
seen.add(current);
|
|
945
|
+
return current;
|
|
946
|
+
}
|
|
947
|
+
);
|
|
948
|
+
return serialized;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/cdp/cdpConsole.ts
|
|
952
|
+
async function startCdpConsole(options) {
|
|
953
|
+
await options.client.Runtime.enable();
|
|
954
|
+
options.client.Runtime.consoleAPICalled((event) => {
|
|
955
|
+
options.push({
|
|
956
|
+
id: (0, import_nanoid3.nanoid)(),
|
|
957
|
+
pageId: options.pageId,
|
|
958
|
+
source: "cdp",
|
|
959
|
+
level: normalizeConsoleLevel(event.type),
|
|
960
|
+
message: event.args.map((arg) => safeStringify(arg.value ?? arg.description)).join(" "),
|
|
961
|
+
timestamp: event.timestamp
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
function normalizeConsoleLevel(level) {
|
|
966
|
+
if (level === "warning") {
|
|
967
|
+
return "warn";
|
|
968
|
+
}
|
|
969
|
+
if (level === "error" || level === "debug" || level === "info") {
|
|
970
|
+
return level;
|
|
971
|
+
}
|
|
972
|
+
return "log";
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// src/cdp/cdpNetwork.ts
|
|
976
|
+
var import_nanoid4 = require("nanoid");
|
|
977
|
+
|
|
978
|
+
// src/shared/sanitize.ts
|
|
979
|
+
function maskHeaders(headers = {}, maskNames = []) {
|
|
980
|
+
const normalizedMaskNames = new Set(
|
|
981
|
+
maskNames.map((name) => name.toLowerCase())
|
|
982
|
+
);
|
|
983
|
+
return Object.fromEntries(
|
|
984
|
+
Object.entries(headers).map(([name, value]) => [
|
|
985
|
+
name,
|
|
986
|
+
normalizedMaskNames.has(name.toLowerCase()) ? "[masked]" : value
|
|
987
|
+
])
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// src/shared/url.ts
|
|
992
|
+
function parseRequestQuery(url) {
|
|
993
|
+
const parsed = new URL(url, "http://vite-plugin-vue-mcp-next.local");
|
|
994
|
+
const queryEntries = /* @__PURE__ */ new Map();
|
|
995
|
+
for (const [key, value] of parsed.searchParams.entries()) {
|
|
996
|
+
const existing = queryEntries.get(key);
|
|
997
|
+
if (existing === void 0) {
|
|
998
|
+
queryEntries.set(key, value);
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
if (Array.isArray(existing)) {
|
|
1002
|
+
existing.push(value);
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
queryEntries.set(key, [existing, value]);
|
|
1006
|
+
}
|
|
1007
|
+
return Object.fromEntries(queryEntries);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// src/cdp/cdpNetwork.ts
|
|
1011
|
+
async function startCdpNetwork(options) {
|
|
1012
|
+
const records = /* @__PURE__ */ new Map();
|
|
1013
|
+
await options.client.Network.enable();
|
|
1014
|
+
options.client.Network.requestWillBeSent((event) => {
|
|
1015
|
+
records.set(event.requestId, {
|
|
1016
|
+
id: (0, import_nanoid4.nanoid)(),
|
|
1017
|
+
pageId: options.pageId,
|
|
1018
|
+
source: "cdp",
|
|
1019
|
+
url: event.request.url,
|
|
1020
|
+
method: event.request.method,
|
|
1021
|
+
requestHeaders: maskHeaders(
|
|
1022
|
+
normalizeHeaders(event.request.headers),
|
|
1023
|
+
options.maskHeaders
|
|
1024
|
+
),
|
|
1025
|
+
requestQuery: parseRequestQuery(event.request.url),
|
|
1026
|
+
requestBody: event.request.postData,
|
|
1027
|
+
startedAt: event.timestamp * 1e3
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
1030
|
+
options.client.Network.responseReceived((event) => {
|
|
1031
|
+
const record = records.get(event.requestId);
|
|
1032
|
+
if (!record) {
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
records.set(event.requestId, {
|
|
1036
|
+
...record,
|
|
1037
|
+
status: event.response.status,
|
|
1038
|
+
responseHeaders: normalizeHeaders(event.response.headers)
|
|
1039
|
+
});
|
|
1040
|
+
});
|
|
1041
|
+
options.client.Network.loadingFinished((event) => {
|
|
1042
|
+
void finalizeCdpNetworkRecord(
|
|
1043
|
+
options,
|
|
1044
|
+
records,
|
|
1045
|
+
event.requestId,
|
|
1046
|
+
event.timestamp * 1e3
|
|
1047
|
+
);
|
|
1048
|
+
});
|
|
1049
|
+
options.client.Network.loadingFailed((event) => {
|
|
1050
|
+
const record = records.get(event.requestId);
|
|
1051
|
+
if (!record) {
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
records.delete(event.requestId);
|
|
1055
|
+
options.push({
|
|
1056
|
+
...record,
|
|
1057
|
+
error: event.errorText,
|
|
1058
|
+
endedAt: event.timestamp * 1e3,
|
|
1059
|
+
durationMs: event.timestamp * 1e3 - record.startedAt
|
|
1060
|
+
});
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
async function finalizeCdpNetworkRecord(options, records, requestId, endedAt) {
|
|
1064
|
+
const record = records.get(requestId);
|
|
1065
|
+
if (!record) {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
records.delete(requestId);
|
|
1069
|
+
options.push({
|
|
1070
|
+
...record,
|
|
1071
|
+
responseBody: options.captureResponseBody ? await safeGetResponseBody(options.client, requestId) : void 0,
|
|
1072
|
+
endedAt,
|
|
1073
|
+
durationMs: endedAt - record.startedAt
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
async function safeGetResponseBody(client, requestId) {
|
|
1077
|
+
try {
|
|
1078
|
+
return (await client.Network.getResponseBody({ requestId })).body;
|
|
1079
|
+
} catch {
|
|
1080
|
+
return void 0;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
function normalizeHeaders(headers) {
|
|
1084
|
+
return Object.fromEntries(
|
|
1085
|
+
Object.entries(headers).map(([key, value]) => [key, String(value)])
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// src/plugin/cdpLifecycle.ts
|
|
1090
|
+
function createCdpLifecycleController(ctx) {
|
|
1091
|
+
const clients = /* @__PURE__ */ new Map();
|
|
1092
|
+
return {
|
|
1093
|
+
async connectPage(target) {
|
|
1094
|
+
if (clients.has(target.pageId) || !shouldStartCdp(ctx)) {
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
const client = await safeConnectCdp(ctx, target);
|
|
1098
|
+
if (!client) {
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
clients.set(target.pageId, client);
|
|
1102
|
+
try {
|
|
1103
|
+
await startCdpObservers(ctx, target, client);
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
clients.delete(target.pageId);
|
|
1106
|
+
await client.close();
|
|
1107
|
+
warnWhenCdpForced(ctx, error);
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
async closeAll() {
|
|
1111
|
+
await Promise.all([...clients.values()].map((client) => client.close()));
|
|
1112
|
+
clients.clear();
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
function shouldStartCdp(ctx) {
|
|
1117
|
+
const consoleUsesCdp = shouldUseCdp({
|
|
1118
|
+
options: ctx.options,
|
|
1119
|
+
capabilityMode: ctx.options.runtime.mode,
|
|
1120
|
+
hasMatchedCdpTarget: true
|
|
1121
|
+
});
|
|
1122
|
+
const networkUsesCdp = shouldUseCdp({
|
|
1123
|
+
options: ctx.options,
|
|
1124
|
+
capabilityMode: ctx.options.network.mode,
|
|
1125
|
+
hasMatchedCdpTarget: true
|
|
1126
|
+
});
|
|
1127
|
+
return consoleUsesCdp || networkUsesCdp;
|
|
1128
|
+
}
|
|
1129
|
+
async function safeConnectCdp(ctx, target) {
|
|
1130
|
+
try {
|
|
1131
|
+
return await connectCdp(ctx, target);
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
warnWhenCdpForced(ctx, error);
|
|
1134
|
+
return void 0;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
function warnWhenCdpForced(ctx, error) {
|
|
1138
|
+
if (ctx.options.runtime.mode !== "cdp" && ctx.options.network.mode !== "cdp") {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
console.warn(
|
|
1142
|
+
`[vite-plugin-vue-mcp-next] CDP connect failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
async function connectCdp(ctx, target) {
|
|
1146
|
+
const cdp = createCdpClient(ctx.options.cdp);
|
|
1147
|
+
if (ctx.options.cdp.wsEndpoint) {
|
|
1148
|
+
return cdp.connect(ctx.options.cdp.wsEndpoint);
|
|
1149
|
+
}
|
|
1150
|
+
if (!ctx.options.cdp.browserUrl) {
|
|
1151
|
+
return void 0;
|
|
1152
|
+
}
|
|
1153
|
+
const matched = matchCdpTarget(await cdp.listTargets(), {
|
|
1154
|
+
url: target.url,
|
|
1155
|
+
targetUrlPattern: ctx.options.cdp.targetUrlPattern
|
|
1156
|
+
});
|
|
1157
|
+
if (!matched?.webSocketDebuggerUrl) {
|
|
1158
|
+
return void 0;
|
|
1159
|
+
}
|
|
1160
|
+
return cdp.connect(matched.webSocketDebuggerUrl);
|
|
1161
|
+
}
|
|
1162
|
+
async function startCdpObservers(ctx, target, client) {
|
|
1163
|
+
if (ctx.options.runtime.mode !== "hook") {
|
|
1164
|
+
await startCdpConsole({
|
|
1165
|
+
client,
|
|
1166
|
+
pageId: target.pageId,
|
|
1167
|
+
push: (record) => {
|
|
1168
|
+
ctx.consoleRecords.push(record);
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
if (ctx.options.network.mode === "auto" || ctx.options.network.mode === "cdp") {
|
|
1173
|
+
await startCdpNetwork({
|
|
1174
|
+
client,
|
|
1175
|
+
pageId: target.pageId,
|
|
1176
|
+
maskHeaders: ctx.options.network.maskHeaders,
|
|
1177
|
+
captureResponseBody: ctx.options.network.captureResponseBody,
|
|
1178
|
+
push: (record) => {
|
|
1179
|
+
ctx.networkRecords.push(record);
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/plugin/injectRuntime.ts
|
|
1186
|
+
function createRuntimeInjectionController(options, getConfig) {
|
|
1187
|
+
return {
|
|
1188
|
+
resolveId(importee) {
|
|
1189
|
+
if (importee === VIRTUAL_RUNTIME_ID) {
|
|
1190
|
+
return RESOLVED_VIRTUAL_RUNTIME_ID;
|
|
1191
|
+
}
|
|
1192
|
+
return void 0;
|
|
1193
|
+
},
|
|
1194
|
+
load(id) {
|
|
1195
|
+
if (id !== RESOLVED_VIRTUAL_RUNTIME_ID) {
|
|
1196
|
+
return void 0;
|
|
1197
|
+
}
|
|
1198
|
+
return "import { startRuntimeClient } from 'vite-plugin-vue-mcp-next/runtime/client';\nvoid startRuntimeClient();";
|
|
1199
|
+
},
|
|
1200
|
+
transformIndexHtml(html) {
|
|
1201
|
+
if (options.appendTo) {
|
|
1202
|
+
return void 0;
|
|
1203
|
+
}
|
|
1204
|
+
const base = getConfig()?.base || "/";
|
|
1205
|
+
return {
|
|
1206
|
+
html,
|
|
1207
|
+
tags: [
|
|
1208
|
+
{
|
|
1209
|
+
tag: "script",
|
|
1210
|
+
injectTo: "head-prepend",
|
|
1211
|
+
attrs: {
|
|
1212
|
+
type: "module",
|
|
1213
|
+
src: `${base}@id/${VIRTUAL_RUNTIME_ID}`
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
]
|
|
1217
|
+
};
|
|
1218
|
+
},
|
|
1219
|
+
transform(code, id, ssr) {
|
|
1220
|
+
if (ssr || !options.appendTo) {
|
|
1221
|
+
return void 0;
|
|
1222
|
+
}
|
|
1223
|
+
const [filename] = id.split("?", 2);
|
|
1224
|
+
const matched = typeof options.appendTo === "string" ? filename.endsWith(options.appendTo) : options.appendTo.test(filename);
|
|
1225
|
+
if (!matched) {
|
|
1226
|
+
return void 0;
|
|
1227
|
+
}
|
|
1228
|
+
return `import '${VIRTUAL_RUNTIME_ID}';
|
|
1229
|
+
${code}`;
|
|
1230
|
+
}
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// src/plugin/mcpClientConfig/index.ts
|
|
1235
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
1236
|
+
|
|
1237
|
+
// src/plugin/mcpClientConfig/codexConfig.ts
|
|
1238
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
1239
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
1240
|
+
async function updateCodexMcpClientConfig(options) {
|
|
1241
|
+
try {
|
|
1242
|
+
const current = await readOptionalTextFile(options.configPath);
|
|
1243
|
+
const next = replaceOrAppendOwnedBlock(current, options);
|
|
1244
|
+
await import_promises.default.mkdir(import_node_path2.default.dirname(options.configPath), { recursive: true });
|
|
1245
|
+
await import_promises.default.writeFile(options.configPath, next);
|
|
1246
|
+
} catch (error) {
|
|
1247
|
+
console.warn(
|
|
1248
|
+
`[vite-plugin-vue-mcp-next] Failed to update Codex MCP config at ${options.configPath}: ${formatError(error)}`
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function replaceOrAppendOwnedBlock(current, options) {
|
|
1253
|
+
const block = createCodexServerBlock(options);
|
|
1254
|
+
const matcher = createOwnedBlockMatcher(options.serverName);
|
|
1255
|
+
if (matcher.test(current)) {
|
|
1256
|
+
return current.replace(matcher, block);
|
|
1257
|
+
}
|
|
1258
|
+
const separator = current.trim() ? "\n\n" : "";
|
|
1259
|
+
return `${trimEndNewline(current)}${separator}${block}`;
|
|
1260
|
+
}
|
|
1261
|
+
function createCodexServerBlock(options) {
|
|
1262
|
+
return `${createTableHeader(options.serverName)}
|
|
1263
|
+
url = ${quoteTomlString(options.mcpUrl)}
|
|
1264
|
+
`;
|
|
1265
|
+
}
|
|
1266
|
+
function createOwnedBlockMatcher(serverName) {
|
|
1267
|
+
const plainHeader = escapeRegExp(`[mcp_servers.${serverName}]`);
|
|
1268
|
+
const quotedHeader = escapeRegExp(
|
|
1269
|
+
`[mcp_servers.${quoteTomlKey(serverName)}]`
|
|
1270
|
+
);
|
|
1271
|
+
return new RegExp(
|
|
1272
|
+
`(?:^|\\n)(?:${plainHeader}|${quotedHeader})\\n[\\s\\S]*?(?=\\n\\[|$)`
|
|
1273
|
+
);
|
|
1274
|
+
}
|
|
1275
|
+
function createTableHeader(serverName) {
|
|
1276
|
+
const key = /^[A-Za-z0-9_-]+$/.test(serverName) ? serverName : quoteTomlKey(serverName);
|
|
1277
|
+
return `[mcp_servers.${key}]`;
|
|
1278
|
+
}
|
|
1279
|
+
function quoteTomlKey(value) {
|
|
1280
|
+
return quoteTomlString(value);
|
|
1281
|
+
}
|
|
1282
|
+
function quoteTomlString(value) {
|
|
1283
|
+
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1284
|
+
}
|
|
1285
|
+
async function readOptionalTextFile(filePath) {
|
|
1286
|
+
try {
|
|
1287
|
+
return await import_promises.default.readFile(filePath, "utf-8");
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
1290
|
+
return "";
|
|
1291
|
+
}
|
|
1292
|
+
throw error;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
function trimEndNewline(value) {
|
|
1296
|
+
return value.replace(/\n+$/u, "");
|
|
1297
|
+
}
|
|
1298
|
+
function escapeRegExp(value) {
|
|
1299
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1300
|
+
}
|
|
1301
|
+
function formatError(error) {
|
|
1302
|
+
return error instanceof Error ? error.message : String(error);
|
|
1303
|
+
}
|
|
1304
|
+
function isNodeError(error) {
|
|
1305
|
+
return error instanceof Error && "code" in error;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/plugin/mcpClientConfig/jsonConfig.ts
|
|
1309
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
1310
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
1311
|
+
async function updateJsonMcpClientConfig(options) {
|
|
1312
|
+
try {
|
|
1313
|
+
const config = await readJsonConfig(options.configPath);
|
|
1314
|
+
if (!isPlainRecord(config)) {
|
|
1315
|
+
warnConfigFailure(options, "config root must be a JSON object");
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
const mcpServers = isPlainRecord(config.mcpServers) ? config.mcpServers : {};
|
|
1319
|
+
mcpServers[options.serverName] = { url: options.mcpUrl };
|
|
1320
|
+
config.mcpServers = mcpServers;
|
|
1321
|
+
await import_promises2.default.mkdir(import_node_path3.default.dirname(options.configPath), { recursive: true });
|
|
1322
|
+
await import_promises2.default.writeFile(
|
|
1323
|
+
options.configPath,
|
|
1324
|
+
`${JSON.stringify(config, null, 2)}
|
|
1325
|
+
`
|
|
1326
|
+
);
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
warnConfigFailure(options, formatError2(error));
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
async function readJsonConfig(configPath) {
|
|
1332
|
+
const raw = await readOptionalTextFile2(configPath);
|
|
1333
|
+
if (!raw.trim()) {
|
|
1334
|
+
return {};
|
|
1335
|
+
}
|
|
1336
|
+
return JSON.parse(raw);
|
|
1337
|
+
}
|
|
1338
|
+
async function readOptionalTextFile2(filePath) {
|
|
1339
|
+
try {
|
|
1340
|
+
return await import_promises2.default.readFile(filePath, "utf-8");
|
|
1341
|
+
} catch (error) {
|
|
1342
|
+
if (isNodeError2(error) && error.code === "ENOENT") {
|
|
1343
|
+
return "{}";
|
|
1344
|
+
}
|
|
1345
|
+
throw error;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
function isPlainRecord(value) {
|
|
1349
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1350
|
+
}
|
|
1351
|
+
function warnConfigFailure(options, reason) {
|
|
1352
|
+
console.warn(
|
|
1353
|
+
`[vite-plugin-vue-mcp-next] Failed to update ${options.clientName} MCP config at ${options.configPath}: ${reason}`
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
function formatError2(error) {
|
|
1357
|
+
return error instanceof Error ? error.message : String(error);
|
|
1358
|
+
}
|
|
1359
|
+
function isNodeError2(error) {
|
|
1360
|
+
return error instanceof Error && "code" in error;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
// src/plugin/mcpClientConfig/index.ts
|
|
1364
|
+
async function updateMcpClientConfigs(root, mcpUrl, options) {
|
|
1365
|
+
const serverName = options.serverName;
|
|
1366
|
+
const jobs = [];
|
|
1367
|
+
if (options.cursor) {
|
|
1368
|
+
jobs.push(
|
|
1369
|
+
updateJsonMcpClientConfig({
|
|
1370
|
+
clientName: "Cursor",
|
|
1371
|
+
configPath: import_node_path4.default.join(root, ".cursor", "mcp.json"),
|
|
1372
|
+
mcpUrl,
|
|
1373
|
+
serverName
|
|
1374
|
+
})
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
if (options.codex) {
|
|
1378
|
+
jobs.push(
|
|
1379
|
+
updateCodexMcpClientConfig({
|
|
1380
|
+
configPath: import_node_path4.default.join(root, ".codex", "config.toml"),
|
|
1381
|
+
mcpUrl,
|
|
1382
|
+
serverName
|
|
1383
|
+
})
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
if (options.claudeCode) {
|
|
1387
|
+
jobs.push(
|
|
1388
|
+
updateJsonMcpClientConfig({
|
|
1389
|
+
clientName: "Claude Code",
|
|
1390
|
+
configPath: import_node_path4.default.join(root, ".mcp.json"),
|
|
1391
|
+
mcpUrl,
|
|
1392
|
+
serverName
|
|
1393
|
+
})
|
|
1394
|
+
);
|
|
1395
|
+
}
|
|
1396
|
+
if (options.trae) {
|
|
1397
|
+
jobs.push(
|
|
1398
|
+
updateJsonMcpClientConfig({
|
|
1399
|
+
clientName: "Trae",
|
|
1400
|
+
configPath: import_node_path4.default.join(root, ".trae", "mcp.json"),
|
|
1401
|
+
mcpUrl,
|
|
1402
|
+
serverName
|
|
1403
|
+
})
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
await Promise.all(jobs);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// src/plugin/createPlugin.ts
|
|
1410
|
+
var import_vite_dev_rpc = require("vite-dev-rpc");
|
|
1411
|
+
function vueMcpNext(userOptions = {}) {
|
|
1412
|
+
const options = mergeOptions(userOptions);
|
|
1413
|
+
const ctx = createVueMcpNextContext(options);
|
|
1414
|
+
let config;
|
|
1415
|
+
const runtimeInjection = createRuntimeInjectionController(
|
|
1416
|
+
options,
|
|
1417
|
+
() => config
|
|
1418
|
+
);
|
|
1419
|
+
const cdpLifecycle = createCdpLifecycleController(ctx);
|
|
1420
|
+
ctx.cdpLifecycle = cdpLifecycle;
|
|
1421
|
+
return {
|
|
1422
|
+
name: "vite-plugin-vue-mcp-next",
|
|
1423
|
+
enforce: "pre",
|
|
1424
|
+
apply: "serve",
|
|
1425
|
+
configResolved(resolvedConfig) {
|
|
1426
|
+
config = resolvedConfig;
|
|
1427
|
+
},
|
|
1428
|
+
async configureServer(server) {
|
|
1429
|
+
ctx.rpcServer = (0, import_vite_dev_rpc.createRPCServer)(
|
|
1430
|
+
"vite-plugin-vue-mcp-next",
|
|
1431
|
+
server.ws,
|
|
1432
|
+
createServerVueRuntimeRpc(ctx),
|
|
1433
|
+
{
|
|
1434
|
+
timeout: -1
|
|
1435
|
+
}
|
|
1436
|
+
);
|
|
1437
|
+
const mcpServer = createMcpServer(ctx, server);
|
|
1438
|
+
setupMcpTransport(options.mcpPath, mcpServer, server);
|
|
1439
|
+
server.ws.on(
|
|
1440
|
+
"vite-plugin-vue-mcp-next:page-connected",
|
|
1441
|
+
(payload) => {
|
|
1442
|
+
if (isRuntimePageTarget(payload)) {
|
|
1443
|
+
ctx.pages.upsert(payload);
|
|
1444
|
+
void cdpLifecycle.connectPage(payload);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
);
|
|
1448
|
+
server.ws.on(
|
|
1449
|
+
"vite-plugin-vue-mcp-next:console-record",
|
|
1450
|
+
(payload) => {
|
|
1451
|
+
if (isConsoleRecord(payload)) {
|
|
1452
|
+
ctx.consoleRecords.push(payload);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
);
|
|
1456
|
+
server.ws.on(
|
|
1457
|
+
"vite-plugin-vue-mcp-next:network-record",
|
|
1458
|
+
(payload) => {
|
|
1459
|
+
if (isNetworkRecord(payload)) {
|
|
1460
|
+
ctx.networkRecords.push(payload);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
);
|
|
1464
|
+
const port = String(server.config.server.port || 5173);
|
|
1465
|
+
const mcpUrl = `http://${options.host}:${port}${options.mcpPath}/sse`;
|
|
1466
|
+
const root = (0, import_vite2.searchForWorkspaceRoot)(server.config.root);
|
|
1467
|
+
await updateMcpClientConfigs(root, mcpUrl, options.mcpClients);
|
|
1468
|
+
if (options.printUrl) {
|
|
1469
|
+
setTimeout(() => {
|
|
1470
|
+
console.log(` \u279C MCP: Server is running at ${mcpUrl}`);
|
|
1471
|
+
}, 300);
|
|
1472
|
+
}
|
|
1473
|
+
server.httpServer?.once("close", () => {
|
|
1474
|
+
void cdpLifecycle.closeAll();
|
|
1475
|
+
});
|
|
1476
|
+
},
|
|
1477
|
+
resolveId(importee) {
|
|
1478
|
+
return runtimeInjection.resolveId(importee);
|
|
1479
|
+
},
|
|
1480
|
+
load(id) {
|
|
1481
|
+
return runtimeInjection.load(id);
|
|
1482
|
+
},
|
|
1483
|
+
transform(code, id, transformOptions) {
|
|
1484
|
+
return runtimeInjection.transform(code, id, transformOptions?.ssr);
|
|
1485
|
+
},
|
|
1486
|
+
transformIndexHtml(html) {
|
|
1487
|
+
return runtimeInjection.transformIndexHtml(html);
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
function isRuntimePageTarget(payload) {
|
|
1492
|
+
if (!payload || typeof payload !== "object") {
|
|
1493
|
+
return false;
|
|
1494
|
+
}
|
|
1495
|
+
const target = payload;
|
|
1496
|
+
return target.source === "runtime" && typeof target.pageId === "string" && typeof target.url === "string" && typeof target.pathname === "string" && typeof target.connected === "boolean";
|
|
1497
|
+
}
|
|
1498
|
+
function isConsoleRecord(payload) {
|
|
1499
|
+
if (!payload || typeof payload !== "object") {
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1502
|
+
const record = payload;
|
|
1503
|
+
return typeof record.id === "string" && typeof record.pageId === "string" && record.source === "hook" && isConsoleLevel(record.level) && typeof record.message === "string" && typeof record.timestamp === "number";
|
|
1504
|
+
}
|
|
1505
|
+
function isConsoleLevel(level) {
|
|
1506
|
+
return level === "log" || level === "info" || level === "warn" || level === "error" || level === "debug";
|
|
1507
|
+
}
|
|
1508
|
+
function isNetworkRecord(payload) {
|
|
1509
|
+
if (!payload || typeof payload !== "object") {
|
|
1510
|
+
return false;
|
|
1511
|
+
}
|
|
1512
|
+
const record = payload;
|
|
1513
|
+
return typeof record.id === "string" && typeof record.pageId === "string" && record.source === "hook" && typeof record.url === "string" && typeof record.method === "string" && typeof record.startedAt === "number";
|
|
1514
|
+
}
|
|
1515
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1516
|
+
0 && (module.exports = {
|
|
1517
|
+
vueMcpNext
|
|
1518
|
+
});
|
|
1519
|
+
//# sourceMappingURL=index.cjs.map
|