@xiaou66/vite-plugin-vue-mcp-next 1.2.0 → 1.3.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/README.md +42 -1
- package/dist/index.cjs +440 -122
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -926
- package/dist/index.d.ts +5 -926
- package/dist/index.js +412 -94
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +661 -178
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.d.cts +5 -1
- package/dist/runtime/client.d.ts +5 -1
- package/dist/runtime/client.js +661 -178
- package/dist/runtime/client.js.map +1 -1
- package/dist/types-DAx3jHdz.d.cts +1019 -0
- package/dist/types-DAx3jHdz.d.ts +1019 -0
- package/package.json +3 -1
- package/skills/vite-mcp-next/SKILL.md +9 -6
package/dist/index.js
CHANGED
|
@@ -51,7 +51,8 @@ var MCP_TOOL_NAMES = {
|
|
|
51
51
|
highlightComponent: "highlight_component",
|
|
52
52
|
getRouterInfo: "get_router_info",
|
|
53
53
|
getPiniaTree: "get_pinia_tree",
|
|
54
|
-
getPiniaState: "get_pinia_state"
|
|
54
|
+
getPiniaState: "get_pinia_state",
|
|
55
|
+
getElementContext: "get_element_context"
|
|
55
56
|
};
|
|
56
57
|
var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
|
|
57
58
|
var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
|
|
@@ -62,6 +63,12 @@ var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
|
|
|
62
63
|
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
|
|
63
64
|
var LEGACY_MCP_CLIENT_SERVER_NAMES = ["vue-mcp-next"];
|
|
64
65
|
var RUNTIME_PAGE_RECONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-reconnected";
|
|
66
|
+
var RUNTIME_PAGE_CONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-connected";
|
|
67
|
+
var RUNTIME_PAGE_DISCONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-disconnected";
|
|
68
|
+
var RUNTIME_PAGE_HEARTBEAT_EVENT = "vite-plugin-vue-mcp-next:heartbeat";
|
|
69
|
+
var DEFAULT_RUNTIME_PAGE_HEARTBEAT_TIMEOUT_MS = 45e3;
|
|
70
|
+
var DEFAULT_RUNTIME_PAGE_HEARTBEAT_SCAN_INTERVAL_MS = 45e3;
|
|
71
|
+
var DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS = 2200;
|
|
65
72
|
var DEFAULT_OPTIONS = {
|
|
66
73
|
mcpPath: DEFAULT_MCP_PATH,
|
|
67
74
|
host: "localhost",
|
|
@@ -80,6 +87,16 @@ var DEFAULT_OPTIONS = {
|
|
|
80
87
|
skill: {
|
|
81
88
|
autoConfig: true
|
|
82
89
|
},
|
|
90
|
+
elementPicker: {
|
|
91
|
+
enabled: true,
|
|
92
|
+
shortcut: {
|
|
93
|
+
altKey: true,
|
|
94
|
+
shiftKey: true,
|
|
95
|
+
metaKey: false,
|
|
96
|
+
ctrlKey: false
|
|
97
|
+
},
|
|
98
|
+
toastDurationMs: DEFAULT_ELEMENT_PICKER_TOAST_DURATION_MS
|
|
99
|
+
},
|
|
83
100
|
runtime: {
|
|
84
101
|
mode: "auto",
|
|
85
102
|
evaluate: {
|
|
@@ -154,6 +171,14 @@ function mergeOptions(options = {}) {
|
|
|
154
171
|
...DEFAULT_OPTIONS.skill,
|
|
155
172
|
...options.skill
|
|
156
173
|
},
|
|
174
|
+
elementPicker: {
|
|
175
|
+
...DEFAULT_OPTIONS.elementPicker,
|
|
176
|
+
...options.elementPicker,
|
|
177
|
+
shortcut: {
|
|
178
|
+
...DEFAULT_OPTIONS.elementPicker.shortcut,
|
|
179
|
+
...options.elementPicker?.shortcut
|
|
180
|
+
}
|
|
181
|
+
},
|
|
157
182
|
runtime: {
|
|
158
183
|
...DEFAULT_OPTIONS.runtime,
|
|
159
184
|
...options.runtime,
|
|
@@ -574,9 +599,164 @@ function registerDomTools(server, ctx) {
|
|
|
574
599
|
);
|
|
575
600
|
}
|
|
576
601
|
|
|
577
|
-
// src/mcp/tools/
|
|
602
|
+
// src/mcp/tools/elementContext.ts
|
|
578
603
|
import { z as z3 } from "zod";
|
|
579
604
|
|
|
605
|
+
// src/shared/elementId.ts
|
|
606
|
+
var PROJECT_SOURCE_ID_PATTERN = /^(.+\.(?:vue|tsx|jsx|ts|js)):(\d+):(\d+)$/;
|
|
607
|
+
var RUNTIME_ID_PATTERN = /^runtime:([A-Za-z0-9_-]+)$/;
|
|
608
|
+
var PACKAGE_ID_PREFIX = "pkg:";
|
|
609
|
+
function parseElementId(elementId) {
|
|
610
|
+
const sourceMatch = PROJECT_SOURCE_ID_PATTERN.exec(elementId);
|
|
611
|
+
if (sourceMatch) {
|
|
612
|
+
return {
|
|
613
|
+
kind: "project-source",
|
|
614
|
+
elementId,
|
|
615
|
+
file: sourceMatch[1],
|
|
616
|
+
line: Number(sourceMatch[2]),
|
|
617
|
+
column: Number(sourceMatch[3])
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
if (elementId.startsWith(PACKAGE_ID_PREFIX)) {
|
|
621
|
+
return parsePackageElementId(elementId);
|
|
622
|
+
}
|
|
623
|
+
const runtimeMatch = RUNTIME_ID_PATTERN.exec(elementId);
|
|
624
|
+
if (runtimeMatch) {
|
|
625
|
+
return {
|
|
626
|
+
kind: "runtime",
|
|
627
|
+
elementId,
|
|
628
|
+
runtimeId: runtimeMatch[1]
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
return {
|
|
632
|
+
kind: "invalid",
|
|
633
|
+
elementId,
|
|
634
|
+
reason: "unsupported elementId format"
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function parsePackageElementId(elementId) {
|
|
638
|
+
const value = elementId.slice(PACKAGE_ID_PREFIX.length);
|
|
639
|
+
const parts = value.split("/").filter(Boolean);
|
|
640
|
+
if (parts.length < 2) {
|
|
641
|
+
return {
|
|
642
|
+
kind: "invalid",
|
|
643
|
+
elementId,
|
|
644
|
+
reason: "package elementId must include packageName and entryFile"
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
const scoped = parts[0]?.startsWith("@");
|
|
648
|
+
const packageName = scoped ? parts.slice(0, 2).join("/") : parts[0];
|
|
649
|
+
const entryFile = parts.slice(scoped ? 2 : 1).join("/");
|
|
650
|
+
if (!entryFile) {
|
|
651
|
+
return {
|
|
652
|
+
kind: "invalid",
|
|
653
|
+
elementId,
|
|
654
|
+
reason: "package elementId must include entryFile"
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
kind: "package",
|
|
659
|
+
elementId,
|
|
660
|
+
packageName,
|
|
661
|
+
entryFile
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/mcp/tools/elementContext.ts
|
|
666
|
+
function registerElementContextTools(server, ctx) {
|
|
667
|
+
server.registerTool(
|
|
668
|
+
MCP_TOOL_NAMES.getElementContext,
|
|
669
|
+
{
|
|
670
|
+
description: "Get editable source, Vue component, and DOM context for a copied elementId.",
|
|
671
|
+
inputSchema: {
|
|
672
|
+
elementId: z3.string(),
|
|
673
|
+
pageId: z3.string().optional()
|
|
674
|
+
}
|
|
675
|
+
},
|
|
676
|
+
async (input) => {
|
|
677
|
+
const runtimeResult = await tryRuntimeElementContext(
|
|
678
|
+
ctx,
|
|
679
|
+
input.elementId,
|
|
680
|
+
input.pageId
|
|
681
|
+
);
|
|
682
|
+
if (runtimeResult) {
|
|
683
|
+
return createToolResponse(runtimeResult);
|
|
684
|
+
}
|
|
685
|
+
return createToolResponse(createStaticElementContext(input.elementId));
|
|
686
|
+
}
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
async function tryRuntimeElementContext(ctx, elementId, pageId) {
|
|
690
|
+
if (!ctx.rpcServer) {
|
|
691
|
+
return parseElementId(elementId).kind === "runtime" ? createRuntimeUnavailableContext(elementId) : void 0;
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
resolvePageTarget(ctx, pageId);
|
|
695
|
+
} catch (error) {
|
|
696
|
+
const parsed = parseElementId(elementId);
|
|
697
|
+
if (parsed.kind === "project-source" || parsed.kind === "package") {
|
|
698
|
+
return void 0;
|
|
699
|
+
}
|
|
700
|
+
return {
|
|
701
|
+
ok: false,
|
|
702
|
+
elementId,
|
|
703
|
+
error: error instanceof Error ? error.message : String(error),
|
|
704
|
+
limitations: ["call list_pages and pass pageId when multiple pages exist"]
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
return await requestRuntimeData(ctx, (event) => {
|
|
708
|
+
void ctx.rpcServer?.getElementContext({ event, elementId });
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
function createStaticElementContext(elementId) {
|
|
712
|
+
const parsed = parseElementId(elementId);
|
|
713
|
+
if (parsed.kind === "project-source") {
|
|
714
|
+
return {
|
|
715
|
+
ok: true,
|
|
716
|
+
elementId,
|
|
717
|
+
editable: true,
|
|
718
|
+
codeLocation: {
|
|
719
|
+
file: parsed.file,
|
|
720
|
+
line: parsed.line,
|
|
721
|
+
column: parsed.column
|
|
722
|
+
},
|
|
723
|
+
limitations: ["runtime unavailable, DOM and component context omitted"]
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
if (parsed.kind === "package") {
|
|
727
|
+
return {
|
|
728
|
+
ok: true,
|
|
729
|
+
elementId,
|
|
730
|
+
editable: false,
|
|
731
|
+
packageLocation: {
|
|
732
|
+
packageName: parsed.packageName,
|
|
733
|
+
entryFile: parsed.entryFile
|
|
734
|
+
},
|
|
735
|
+
limitations: ["third-party package source is not editable from this project"]
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
if (parsed.kind === "runtime") {
|
|
739
|
+
return createRuntimeUnavailableContext(elementId);
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
ok: false,
|
|
743
|
+
elementId,
|
|
744
|
+
error: parsed.reason,
|
|
745
|
+
limitations: ["please provide a copied elementId from the element picker"]
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
function createRuntimeUnavailableContext(elementId) {
|
|
749
|
+
return {
|
|
750
|
+
ok: false,
|
|
751
|
+
elementId,
|
|
752
|
+
error: "runtime bridge is not connected",
|
|
753
|
+
limitations: ["runtime ids are only valid while the page is connected"]
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/mcp/tools/evaluate.ts
|
|
758
|
+
import { z as z4 } from "zod";
|
|
759
|
+
|
|
580
760
|
// src/cdp/cdpEvaluate.ts
|
|
581
761
|
async function cdpEvaluate(options) {
|
|
582
762
|
const result = await options.client.Runtime.evaluate({
|
|
@@ -597,9 +777,9 @@ function registerEvaluateTools(server, ctx) {
|
|
|
597
777
|
{
|
|
598
778
|
description: "Evaluate a script in the selected page when explicitly enabled.",
|
|
599
779
|
inputSchema: {
|
|
600
|
-
pageId:
|
|
601
|
-
expression:
|
|
602
|
-
awaitPromise:
|
|
780
|
+
pageId: z4.string().optional(),
|
|
781
|
+
expression: z4.string(),
|
|
782
|
+
awaitPromise: z4.boolean().optional()
|
|
603
783
|
}
|
|
604
784
|
},
|
|
605
785
|
async (input) => {
|
|
@@ -648,18 +828,18 @@ function assertEvaluateEnabled(options) {
|
|
|
648
828
|
}
|
|
649
829
|
|
|
650
830
|
// src/mcp/tools/network.ts
|
|
651
|
-
import { z as
|
|
831
|
+
import { z as z5 } from "zod";
|
|
652
832
|
function registerNetworkTools(server, ctx) {
|
|
653
833
|
server.registerTool(
|
|
654
834
|
MCP_TOOL_NAMES.getNetworkRequests,
|
|
655
835
|
{
|
|
656
836
|
description: "Get captured network request summaries.",
|
|
657
837
|
inputSchema: {
|
|
658
|
-
pageId:
|
|
659
|
-
urlContains:
|
|
660
|
-
method:
|
|
661
|
-
status:
|
|
662
|
-
limit:
|
|
838
|
+
pageId: z5.string().optional(),
|
|
839
|
+
urlContains: z5.string().optional(),
|
|
840
|
+
method: z5.string().optional(),
|
|
841
|
+
status: z5.number().optional(),
|
|
842
|
+
limit: z5.number().optional()
|
|
663
843
|
}
|
|
664
844
|
},
|
|
665
845
|
(input) => {
|
|
@@ -682,7 +862,7 @@ function registerNetworkTools(server, ctx) {
|
|
|
682
862
|
MCP_TOOL_NAMES.getNetworkRequestDetail,
|
|
683
863
|
{
|
|
684
864
|
description: "Get captured network request detail by id.",
|
|
685
|
-
inputSchema: { id:
|
|
865
|
+
inputSchema: { id: z5.string() }
|
|
686
866
|
},
|
|
687
867
|
(input) => {
|
|
688
868
|
if (ctx.options.network.mode === "off") {
|
|
@@ -699,7 +879,7 @@ function registerNetworkTools(server, ctx) {
|
|
|
699
879
|
{
|
|
700
880
|
description: "Clear cached network requests.",
|
|
701
881
|
inputSchema: {
|
|
702
|
-
pageId:
|
|
882
|
+
pageId: z5.string().optional()
|
|
703
883
|
}
|
|
704
884
|
},
|
|
705
885
|
() => {
|
|
@@ -710,7 +890,7 @@ function registerNetworkTools(server, ctx) {
|
|
|
710
890
|
}
|
|
711
891
|
|
|
712
892
|
// src/mcp/tools/performance.ts
|
|
713
|
-
import { z as
|
|
893
|
+
import { z as z6 } from "zod";
|
|
714
894
|
|
|
715
895
|
// src/performance/summary.ts
|
|
716
896
|
function buildPerformanceSummary(input) {
|
|
@@ -992,10 +1172,10 @@ function registerPerformanceTools(server, ctx) {
|
|
|
992
1172
|
{
|
|
993
1173
|
description: "Record a performance sample for the selected page.",
|
|
994
1174
|
inputSchema: {
|
|
995
|
-
pageId:
|
|
996
|
-
durationMs:
|
|
997
|
-
includeMemory:
|
|
998
|
-
includeStacks:
|
|
1175
|
+
pageId: z6.string().optional(),
|
|
1176
|
+
durationMs: z6.number().optional(),
|
|
1177
|
+
includeMemory: z6.boolean().optional(),
|
|
1178
|
+
includeStacks: z6.boolean().optional()
|
|
999
1179
|
}
|
|
1000
1180
|
},
|
|
1001
1181
|
async (input) => handleRecordPerformance(ctx, input)
|
|
@@ -1005,9 +1185,9 @@ function registerPerformanceTools(server, ctx) {
|
|
|
1005
1185
|
{
|
|
1006
1186
|
description: "Start a performance recording session.",
|
|
1007
1187
|
inputSchema: {
|
|
1008
|
-
pageId:
|
|
1009
|
-
includeMemory:
|
|
1010
|
-
includeStacks:
|
|
1188
|
+
pageId: z6.string().optional(),
|
|
1189
|
+
includeMemory: z6.boolean().optional(),
|
|
1190
|
+
includeStacks: z6.boolean().optional()
|
|
1011
1191
|
}
|
|
1012
1192
|
},
|
|
1013
1193
|
async (input) => handleStartPerformanceRecording(ctx, input)
|
|
@@ -1017,7 +1197,7 @@ function registerPerformanceTools(server, ctx) {
|
|
|
1017
1197
|
{
|
|
1018
1198
|
description: "Stop a performance recording session.",
|
|
1019
1199
|
inputSchema: {
|
|
1020
|
-
recordingId:
|
|
1200
|
+
recordingId: z6.string()
|
|
1021
1201
|
}
|
|
1022
1202
|
},
|
|
1023
1203
|
async (input) => handleStopPerformanceRecording(ctx, input)
|
|
@@ -1027,9 +1207,9 @@ function registerPerformanceTools(server, ctx) {
|
|
|
1027
1207
|
{
|
|
1028
1208
|
description: "Get cached performance reports and active sessions.",
|
|
1029
1209
|
inputSchema: {
|
|
1030
|
-
pageId:
|
|
1031
|
-
recordingId:
|
|
1032
|
-
limit:
|
|
1210
|
+
pageId: z6.string().optional(),
|
|
1211
|
+
recordingId: z6.string().optional(),
|
|
1212
|
+
limit: z6.number().optional()
|
|
1033
1213
|
}
|
|
1034
1214
|
},
|
|
1035
1215
|
(input) => handleGetPerformanceReport(ctx, input)
|
|
@@ -1039,7 +1219,7 @@ function registerPerformanceTools(server, ctx) {
|
|
|
1039
1219
|
{
|
|
1040
1220
|
description: "Take a heap snapshot with CDP.",
|
|
1041
1221
|
inputSchema: {
|
|
1042
|
-
pageId:
|
|
1222
|
+
pageId: z6.string().optional()
|
|
1043
1223
|
}
|
|
1044
1224
|
},
|
|
1045
1225
|
async (input) => handleTakeHeapSnapshot(ctx, input)
|
|
@@ -1296,7 +1476,7 @@ function toStructuredRecord(value) {
|
|
|
1296
1476
|
}
|
|
1297
1477
|
|
|
1298
1478
|
// src/mcp/tools/pages.ts
|
|
1299
|
-
import { z as
|
|
1479
|
+
import { z as z7 } from "zod";
|
|
1300
1480
|
|
|
1301
1481
|
// src/plugin/entryDiscovery.ts
|
|
1302
1482
|
import fs from "fs";
|
|
@@ -1321,10 +1501,10 @@ function walkHtmlEntries(root, dir, entries) {
|
|
|
1321
1501
|
if (!item.isFile() || !item.name.endsWith(".html")) {
|
|
1322
1502
|
continue;
|
|
1323
1503
|
}
|
|
1324
|
-
const
|
|
1504
|
+
const relative3 = normalizePath(path.relative(root, fullPath));
|
|
1325
1505
|
entries.push({
|
|
1326
|
-
file:
|
|
1327
|
-
pathname:
|
|
1506
|
+
file: relative3,
|
|
1507
|
+
pathname: relative3 === "index.html" ? "/" : `/${relative3}`
|
|
1328
1508
|
});
|
|
1329
1509
|
}
|
|
1330
1510
|
}
|
|
@@ -1336,7 +1516,7 @@ function registerPageTools(server, ctx, vite) {
|
|
|
1336
1516
|
{
|
|
1337
1517
|
description: "List Vite page entries and connected runtime/CDP targets.",
|
|
1338
1518
|
inputSchema: {
|
|
1339
|
-
includeDisconnected:
|
|
1519
|
+
includeDisconnected: z7.boolean().optional()
|
|
1340
1520
|
}
|
|
1341
1521
|
},
|
|
1342
1522
|
async (input) => {
|
|
@@ -1359,8 +1539,8 @@ function registerPageTools(server, ctx, vite) {
|
|
|
1359
1539
|
{
|
|
1360
1540
|
description: "Reload the selected page. CDP uses ignoreCache; Runtime Hook falls back to normal reload.",
|
|
1361
1541
|
inputSchema: {
|
|
1362
|
-
pageId:
|
|
1363
|
-
ignoreCache:
|
|
1542
|
+
pageId: z7.string().optional(),
|
|
1543
|
+
ignoreCache: z7.boolean().optional()
|
|
1364
1544
|
}
|
|
1365
1545
|
},
|
|
1366
1546
|
async (input) => {
|
|
@@ -1512,7 +1692,7 @@ function getPathname(url) {
|
|
|
1512
1692
|
}
|
|
1513
1693
|
|
|
1514
1694
|
// src/mcp/tools/storage.ts
|
|
1515
|
-
import { z as
|
|
1695
|
+
import { z as z8 } from "zod";
|
|
1516
1696
|
|
|
1517
1697
|
// src/cdp/cdpStorage.ts
|
|
1518
1698
|
function createCdpStorageAdapter(client) {
|
|
@@ -1756,7 +1936,7 @@ function registerStorageTools(server, ctx) {
|
|
|
1756
1936
|
registerStorageTool(server, MCP_TOOL_NAMES.listStorage, {
|
|
1757
1937
|
description: "List same-origin storage and CDP cookies when available.",
|
|
1758
1938
|
inputSchema: {
|
|
1759
|
-
pageId:
|
|
1939
|
+
pageId: z8.string().optional()
|
|
1760
1940
|
},
|
|
1761
1941
|
action: "list",
|
|
1762
1942
|
handler: (input) => handleListStorage(ctx, input.pageId)
|
|
@@ -1800,23 +1980,23 @@ function registerStorageTool(server, name, options) {
|
|
|
1800
1980
|
}
|
|
1801
1981
|
function createStorageInputSchema() {
|
|
1802
1982
|
return {
|
|
1803
|
-
pageId:
|
|
1804
|
-
scope:
|
|
1805
|
-
key:
|
|
1806
|
-
value:
|
|
1807
|
-
databaseName:
|
|
1808
|
-
objectStoreName:
|
|
1809
|
-
indexName:
|
|
1810
|
-
cookie:
|
|
1811
|
-
name:
|
|
1812
|
-
value:
|
|
1813
|
-
domain:
|
|
1814
|
-
path:
|
|
1815
|
-
url:
|
|
1816
|
-
httpOnly:
|
|
1817
|
-
secure:
|
|
1818
|
-
sameSite:
|
|
1819
|
-
expires:
|
|
1983
|
+
pageId: z8.string().optional(),
|
|
1984
|
+
scope: z8.enum(["localStorage", "sessionStorage", "indexedDB", "cookie"]).optional(),
|
|
1985
|
+
key: z8.string().optional(),
|
|
1986
|
+
value: z8.string().optional(),
|
|
1987
|
+
databaseName: z8.string().optional(),
|
|
1988
|
+
objectStoreName: z8.string().optional(),
|
|
1989
|
+
indexName: z8.string().optional(),
|
|
1990
|
+
cookie: z8.object({
|
|
1991
|
+
name: z8.string(),
|
|
1992
|
+
value: z8.string().optional(),
|
|
1993
|
+
domain: z8.string().optional(),
|
|
1994
|
+
path: z8.string().optional(),
|
|
1995
|
+
url: z8.string().optional(),
|
|
1996
|
+
httpOnly: z8.boolean().optional(),
|
|
1997
|
+
secure: z8.boolean().optional(),
|
|
1998
|
+
sameSite: z8.enum(["strict", "lax", "none"]).optional(),
|
|
1999
|
+
expires: z8.number().optional()
|
|
1820
2000
|
}).optional()
|
|
1821
2001
|
};
|
|
1822
2002
|
}
|
|
@@ -1934,7 +2114,7 @@ function hasCdpConfig2(ctx) {
|
|
|
1934
2114
|
}
|
|
1935
2115
|
|
|
1936
2116
|
// src/mcp/tools/screenshot.ts
|
|
1937
|
-
import { z as
|
|
2117
|
+
import { z as z9 } from "zod";
|
|
1938
2118
|
|
|
1939
2119
|
// src/cdp/cdpScreenshot.ts
|
|
1940
2120
|
async function cdpCaptureScreenshot(options) {
|
|
@@ -2094,14 +2274,14 @@ function createProjectRelativePath(ctx, filePath) {
|
|
|
2094
2274
|
var DEFAULT_SCREENSHOT_TARGET = "viewport";
|
|
2095
2275
|
var DEFAULT_SCREENSHOT_FORMAT = "png";
|
|
2096
2276
|
var screenshotInputSchema = {
|
|
2097
|
-
pageId:
|
|
2098
|
-
target:
|
|
2099
|
-
selector:
|
|
2100
|
-
format:
|
|
2101
|
-
prefer:
|
|
2102
|
-
quality:
|
|
2103
|
-
scale:
|
|
2104
|
-
snapdom:
|
|
2277
|
+
pageId: z9.string().optional(),
|
|
2278
|
+
target: z9.enum(["viewport", "fullPage", "element"]).optional(),
|
|
2279
|
+
selector: z9.string().optional(),
|
|
2280
|
+
format: z9.enum(["png", "jpeg", "webp"]).optional(),
|
|
2281
|
+
prefer: z9.enum(["auto", "cdp", "runtime"]).optional(),
|
|
2282
|
+
quality: z9.number().optional(),
|
|
2283
|
+
scale: z9.number().optional(),
|
|
2284
|
+
snapdom: z9.record(z9.string(), z9.unknown()).optional()
|
|
2105
2285
|
};
|
|
2106
2286
|
function registerScreenshotTools(server, ctx) {
|
|
2107
2287
|
server.registerTool(
|
|
@@ -2221,7 +2401,7 @@ function isScreenshotImagePayload(result) {
|
|
|
2221
2401
|
|
|
2222
2402
|
// src/mcp/tools/vue.ts
|
|
2223
2403
|
import { nanoid as nanoid3 } from "nanoid";
|
|
2224
|
-
import { z as
|
|
2404
|
+
import { z as z10 } from "zod";
|
|
2225
2405
|
function registerVueTools(server, ctx) {
|
|
2226
2406
|
server.registerTool(
|
|
2227
2407
|
MCP_TOOL_NAMES.getComponentTree,
|
|
@@ -2234,7 +2414,7 @@ function registerVueTools(server, ctx) {
|
|
|
2234
2414
|
MCP_TOOL_NAMES.getComponentState,
|
|
2235
2415
|
{
|
|
2236
2416
|
description: "Get Vue component state.",
|
|
2237
|
-
inputSchema: { componentName:
|
|
2417
|
+
inputSchema: { componentName: z10.string() }
|
|
2238
2418
|
},
|
|
2239
2419
|
async ({ componentName }) => requestVueData(ctx, (event) => {
|
|
2240
2420
|
void ctx.rpcServer?.getInspectorState({ event, componentName });
|
|
@@ -2245,10 +2425,10 @@ function registerVueTools(server, ctx) {
|
|
|
2245
2425
|
{
|
|
2246
2426
|
description: "Edit Vue component state.",
|
|
2247
2427
|
inputSchema: {
|
|
2248
|
-
componentName:
|
|
2249
|
-
path:
|
|
2250
|
-
value:
|
|
2251
|
-
valueType:
|
|
2428
|
+
componentName: z10.string(),
|
|
2429
|
+
path: z10.array(z10.string()),
|
|
2430
|
+
value: z10.string(),
|
|
2431
|
+
valueType: z10.enum(["string", "number", "boolean", "object", "array"])
|
|
2252
2432
|
}
|
|
2253
2433
|
},
|
|
2254
2434
|
({ componentName, path: path8, value, valueType }) => {
|
|
@@ -2268,7 +2448,7 @@ function registerVueTools(server, ctx) {
|
|
|
2268
2448
|
MCP_TOOL_NAMES.highlightComponent,
|
|
2269
2449
|
{
|
|
2270
2450
|
description: "Highlight a Vue component.",
|
|
2271
|
-
inputSchema: { componentName:
|
|
2451
|
+
inputSchema: { componentName: z10.string() }
|
|
2272
2452
|
},
|
|
2273
2453
|
({ componentName }) => {
|
|
2274
2454
|
if (!ctx.rpcServer) {
|
|
@@ -2296,7 +2476,7 @@ function registerVueTools(server, ctx) {
|
|
|
2296
2476
|
MCP_TOOL_NAMES.getPiniaState,
|
|
2297
2477
|
{
|
|
2298
2478
|
description: "Get Pinia store state.",
|
|
2299
|
-
inputSchema: { storeName:
|
|
2479
|
+
inputSchema: { storeName: z10.string() }
|
|
2300
2480
|
},
|
|
2301
2481
|
async ({ storeName }) => requestVueData(ctx, (event) => {
|
|
2302
2482
|
void ctx.rpcServer?.getPiniaState({ event, storeName });
|
|
@@ -2339,6 +2519,7 @@ function createMcpServer(ctx, vite) {
|
|
|
2339
2519
|
});
|
|
2340
2520
|
registerPageTools(server, ctx, vite);
|
|
2341
2521
|
registerDomTools(server, ctx);
|
|
2522
|
+
registerElementContextTools(server, ctx);
|
|
2342
2523
|
registerScreenshotTools(server, ctx);
|
|
2343
2524
|
registerConsoleTools(server, ctx);
|
|
2344
2525
|
registerEvaluateTools(server, ctx);
|
|
@@ -2420,6 +2601,10 @@ function createServerVueRuntimeRpc(ctx) {
|
|
|
2420
2601
|
onDomQueryUpdated: (event, data) => {
|
|
2421
2602
|
void ctx.hooks.callHook(event, data);
|
|
2422
2603
|
},
|
|
2604
|
+
getElementContext: () => void 0,
|
|
2605
|
+
onElementContextUpdated: (event, data) => {
|
|
2606
|
+
void ctx.hooks.callHook(event, data);
|
|
2607
|
+
},
|
|
2423
2608
|
reloadPage: () => void 0,
|
|
2424
2609
|
onPageReloaded: (event, data) => {
|
|
2425
2610
|
void ctx.hooks.callHook(event, data);
|
|
@@ -2732,6 +2917,82 @@ async function startCdpObservers(ctx, target, client) {
|
|
|
2732
2917
|
}
|
|
2733
2918
|
}
|
|
2734
2919
|
|
|
2920
|
+
// src/plugin/elementInstrumentation.ts
|
|
2921
|
+
import { parse } from "@vue/compiler-sfc";
|
|
2922
|
+
import MagicString from "magic-string";
|
|
2923
|
+
import { relative as relative2 } from "path";
|
|
2924
|
+
var VUE_FILE_SUFFIX = ".vue";
|
|
2925
|
+
var ELEMENT_NODE_TYPE = 1;
|
|
2926
|
+
var SKIPPED_TAGS = /* @__PURE__ */ new Set(["template", "slot", "script", "style"]);
|
|
2927
|
+
var MCP_ID_ATTR = "data-v-mcp-id";
|
|
2928
|
+
function createElementInstrumentationController(options) {
|
|
2929
|
+
return {
|
|
2930
|
+
transform(code, id, ssr) {
|
|
2931
|
+
if (ssr || shouldSkipInstrumentation(id)) {
|
|
2932
|
+
return void 0;
|
|
2933
|
+
}
|
|
2934
|
+
const filename = id.split("?", 1)[0];
|
|
2935
|
+
if (!filename.endsWith(VUE_FILE_SUFFIX)) {
|
|
2936
|
+
return void 0;
|
|
2937
|
+
}
|
|
2938
|
+
const parsed = parse(code, { filename });
|
|
2939
|
+
const template = parsed.descriptor.template;
|
|
2940
|
+
if (!template?.ast) {
|
|
2941
|
+
return void 0;
|
|
2942
|
+
}
|
|
2943
|
+
const s = new MagicString(code);
|
|
2944
|
+
const relativeFile = normalizePath2(relative2(options.root, filename));
|
|
2945
|
+
for (const node of template.ast.children) {
|
|
2946
|
+
injectNodeId(s, node, relativeFile);
|
|
2947
|
+
}
|
|
2948
|
+
if (!s.hasChanged()) {
|
|
2949
|
+
return void 0;
|
|
2950
|
+
}
|
|
2951
|
+
return {
|
|
2952
|
+
code: s.toString(),
|
|
2953
|
+
map: s.generateMap({ hires: true })
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
};
|
|
2957
|
+
}
|
|
2958
|
+
function shouldSkipInstrumentation(id) {
|
|
2959
|
+
if (id.startsWith("\0")) {
|
|
2960
|
+
return true;
|
|
2961
|
+
}
|
|
2962
|
+
const normalized = normalizePath2(id);
|
|
2963
|
+
if (normalized.includes("/node_modules/")) {
|
|
2964
|
+
return true;
|
|
2965
|
+
}
|
|
2966
|
+
return !normalized.startsWith("/") && !/^[A-Za-z]:\//.test(normalized);
|
|
2967
|
+
}
|
|
2968
|
+
function normalizePath2(path8) {
|
|
2969
|
+
return path8.replace(/\\/g, "/");
|
|
2970
|
+
}
|
|
2971
|
+
function injectNodeId(s, node, relativeFile) {
|
|
2972
|
+
if (node.type !== ELEMENT_NODE_TYPE || !node.tag || !node.loc) {
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
if (!SKIPPED_TAGS.has(node.tag) && !hasMcpIdAttr(node)) {
|
|
2976
|
+
const id = `${relativeFile}:${String(node.loc.start.line)}:${String(node.loc.start.column)}`;
|
|
2977
|
+
const insertAt = node.loc.start.offset + node.tag.length + 1;
|
|
2978
|
+
s.appendLeft(insertAt, ` ${MCP_ID_ATTR}="${id}"`);
|
|
2979
|
+
}
|
|
2980
|
+
for (const child of node.children ?? []) {
|
|
2981
|
+
injectNodeId(s, child, relativeFile);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
function hasMcpIdAttr(node) {
|
|
2985
|
+
return (node.props ?? []).some((prop) => {
|
|
2986
|
+
if (!isTemplateProp(prop)) {
|
|
2987
|
+
return false;
|
|
2988
|
+
}
|
|
2989
|
+
return prop.name === MCP_ID_ATTR;
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
function isTemplateProp(value) {
|
|
2993
|
+
return Boolean(value && typeof value === "object" && "name" in value);
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2735
2996
|
// src/plugin/injectRuntime.ts
|
|
2736
2997
|
import { createRequire } from "module";
|
|
2737
2998
|
import { join } from "path";
|
|
@@ -2751,7 +3012,7 @@ function createRuntimeInjectionController(options, getConfig) {
|
|
|
2751
3012
|
},
|
|
2752
3013
|
load(id) {
|
|
2753
3014
|
if (id === RESOLVED_VIRTUAL_RUNTIME_ID) {
|
|
2754
|
-
return createRuntimeModule();
|
|
3015
|
+
return createRuntimeModule(options, getConfig()?.root);
|
|
2755
3016
|
}
|
|
2756
3017
|
if (id === RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID) {
|
|
2757
3018
|
return createScreenshotConfigModule(options);
|
|
@@ -2794,14 +3055,17 @@ ${code}`;
|
|
|
2794
3055
|
}
|
|
2795
3056
|
};
|
|
2796
3057
|
}
|
|
2797
|
-
function createRuntimeModule() {
|
|
3058
|
+
function createRuntimeModule(options, root) {
|
|
2798
3059
|
return [
|
|
2799
3060
|
"import { setScreenshotModuleRegistry, setSnapdomLoader, startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';",
|
|
2800
3061
|
`import { screenshotModuleRegistry } from '${VIRTUAL_SCREENSHOT_CONFIG_ID}';`,
|
|
2801
3062
|
`import { loadSnapdom } from '${VIRTUAL_SNAPDOM_LOADER_ID}';`,
|
|
2802
3063
|
"setScreenshotModuleRegistry(screenshotModuleRegistry);",
|
|
2803
3064
|
"setSnapdomLoader(loadSnapdom);",
|
|
2804
|
-
|
|
3065
|
+
`void startRuntimeClient(${JSON.stringify({
|
|
3066
|
+
elementPicker: options.elementPicker,
|
|
3067
|
+
projectRoot: root
|
|
3068
|
+
})});`
|
|
2805
3069
|
].join("\n");
|
|
2806
3070
|
}
|
|
2807
3071
|
function createSnapdomLoaderModule(root) {
|
|
@@ -3320,6 +3584,7 @@ function vueMcpNext(userOptions = {}) {
|
|
|
3320
3584
|
options,
|
|
3321
3585
|
() => config
|
|
3322
3586
|
);
|
|
3587
|
+
let elementInstrumentation;
|
|
3323
3588
|
const cdpLifecycle = createCdpLifecycleController(ctx);
|
|
3324
3589
|
ctx.cdpLifecycle = cdpLifecycle;
|
|
3325
3590
|
return {
|
|
@@ -3328,6 +3593,9 @@ function vueMcpNext(userOptions = {}) {
|
|
|
3328
3593
|
apply: "serve",
|
|
3329
3594
|
configResolved(resolvedConfig) {
|
|
3330
3595
|
config = resolvedConfig;
|
|
3596
|
+
elementInstrumentation = createElementInstrumentationController({
|
|
3597
|
+
root: resolvedConfig.root
|
|
3598
|
+
});
|
|
3331
3599
|
},
|
|
3332
3600
|
async configureServer(server) {
|
|
3333
3601
|
ctx.server = server;
|
|
@@ -3344,32 +3612,53 @@ function vueMcpNext(userOptions = {}) {
|
|
|
3344
3612
|
() => createMcpServer(ctx, server),
|
|
3345
3613
|
server
|
|
3346
3614
|
);
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
(
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3615
|
+
const lastSeenAt = /* @__PURE__ */ new Map();
|
|
3616
|
+
const heartbeatTimer = setInterval(() => {
|
|
3617
|
+
const now = Date.now();
|
|
3618
|
+
for (const [pageId, seenAt] of lastSeenAt) {
|
|
3619
|
+
const target = ctx.pages.get(pageId);
|
|
3620
|
+
if (!target || target.source !== "runtime" || !target.connected) {
|
|
3621
|
+
lastSeenAt.delete(pageId);
|
|
3622
|
+
continue;
|
|
3354
3623
|
}
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
"vite-plugin-vue-mcp-next:console-record",
|
|
3359
|
-
(payload) => {
|
|
3360
|
-
if (isConsoleRecord(payload)) {
|
|
3361
|
-
ctx.consoleRecords.push(payload);
|
|
3624
|
+
if (now - seenAt >= DEFAULT_RUNTIME_PAGE_HEARTBEAT_TIMEOUT_MS) {
|
|
3625
|
+
ctx.pages.disconnect(pageId, now);
|
|
3626
|
+
lastSeenAt.delete(pageId);
|
|
3362
3627
|
}
|
|
3363
3628
|
}
|
|
3364
|
-
);
|
|
3365
|
-
server.ws.on(
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3629
|
+
}, DEFAULT_RUNTIME_PAGE_HEARTBEAT_SCAN_INTERVAL_MS);
|
|
3630
|
+
server.ws.on(RUNTIME_PAGE_CONNECTED_EVENT, (payload) => {
|
|
3631
|
+
if (isRuntimePageTarget(payload)) {
|
|
3632
|
+
ctx.pages.upsert(payload);
|
|
3633
|
+
lastSeenAt.set(payload.pageId, Date.now());
|
|
3634
|
+
void ctx.hooks.callHook(RUNTIME_PAGE_RECONNECTED_EVENT, payload);
|
|
3635
|
+
void cdpLifecycle.connectPage(payload);
|
|
3636
|
+
}
|
|
3637
|
+
});
|
|
3638
|
+
server.ws.on(RUNTIME_PAGE_HEARTBEAT_EVENT, (payload) => {
|
|
3639
|
+
if (isRuntimeHeartbeatTarget(payload)) {
|
|
3640
|
+
const target = ctx.pages.get(payload.pageId);
|
|
3641
|
+
if (target?.source === "runtime" && target.connected) {
|
|
3642
|
+
lastSeenAt.set(payload.pageId, payload.timestamp);
|
|
3370
3643
|
}
|
|
3371
3644
|
}
|
|
3372
|
-
);
|
|
3645
|
+
});
|
|
3646
|
+
server.ws.on(RUNTIME_PAGE_DISCONNECTED_EVENT, (payload) => {
|
|
3647
|
+
if (isRuntimeDisconnectTarget(payload)) {
|
|
3648
|
+
ctx.pages.disconnect(payload.pageId);
|
|
3649
|
+
lastSeenAt.delete(payload.pageId);
|
|
3650
|
+
}
|
|
3651
|
+
});
|
|
3652
|
+
server.ws.on("vite-plugin-vue-mcp-next:console-record", (payload) => {
|
|
3653
|
+
if (isConsoleRecord(payload)) {
|
|
3654
|
+
ctx.consoleRecords.push(payload);
|
|
3655
|
+
}
|
|
3656
|
+
});
|
|
3657
|
+
server.ws.on("vite-plugin-vue-mcp-next:network-record", (payload) => {
|
|
3658
|
+
if (isNetworkRecord(payload)) {
|
|
3659
|
+
ctx.networkRecords.push(payload);
|
|
3660
|
+
}
|
|
3661
|
+
});
|
|
3373
3662
|
server.ws.on(
|
|
3374
3663
|
"vite-plugin-vue-mcp-next:performance-record",
|
|
3375
3664
|
(payload) => {
|
|
@@ -3399,6 +3688,7 @@ function vueMcpNext(userOptions = {}) {
|
|
|
3399
3688
|
}, 300);
|
|
3400
3689
|
}
|
|
3401
3690
|
server.httpServer?.once("close", () => {
|
|
3691
|
+
clearInterval(heartbeatTimer);
|
|
3402
3692
|
void cdpLifecycle.closeAll();
|
|
3403
3693
|
});
|
|
3404
3694
|
},
|
|
@@ -3409,7 +3699,21 @@ function vueMcpNext(userOptions = {}) {
|
|
|
3409
3699
|
return runtimeInjection.load(id);
|
|
3410
3700
|
},
|
|
3411
3701
|
transform(code, id, transformOptions) {
|
|
3412
|
-
|
|
3702
|
+
const instrumented = elementInstrumentation?.transform(
|
|
3703
|
+
code,
|
|
3704
|
+
id,
|
|
3705
|
+
transformOptions?.ssr
|
|
3706
|
+
);
|
|
3707
|
+
const nextCode = instrumented && typeof instrumented === "object" && "code" in instrumented && typeof instrumented.code === "string" ? instrumented.code : code;
|
|
3708
|
+
const runtimeInjected = runtimeInjection.transform(
|
|
3709
|
+
nextCode,
|
|
3710
|
+
id,
|
|
3711
|
+
transformOptions?.ssr
|
|
3712
|
+
);
|
|
3713
|
+
if (runtimeInjected) {
|
|
3714
|
+
return runtimeInjected;
|
|
3715
|
+
}
|
|
3716
|
+
return instrumented;
|
|
3413
3717
|
},
|
|
3414
3718
|
transformIndexHtml(html) {
|
|
3415
3719
|
return runtimeInjection.transformIndexHtml(html);
|
|
@@ -3423,6 +3727,20 @@ function isRuntimePageTarget(payload) {
|
|
|
3423
3727
|
const target = payload;
|
|
3424
3728
|
return target.source === "runtime" && typeof target.pageId === "string" && typeof target.url === "string" && typeof target.pathname === "string" && typeof target.connected === "boolean" && (target.runtimeClientId === void 0 || typeof target.runtimeClientId === "string");
|
|
3425
3729
|
}
|
|
3730
|
+
function isRuntimeHeartbeatTarget(payload) {
|
|
3731
|
+
if (!payload || typeof payload !== "object") {
|
|
3732
|
+
return false;
|
|
3733
|
+
}
|
|
3734
|
+
const target = payload;
|
|
3735
|
+
return typeof target.pageId === "string" && typeof target.timestamp === "number";
|
|
3736
|
+
}
|
|
3737
|
+
function isRuntimeDisconnectTarget(payload) {
|
|
3738
|
+
if (!payload || typeof payload !== "object") {
|
|
3739
|
+
return false;
|
|
3740
|
+
}
|
|
3741
|
+
const target = payload;
|
|
3742
|
+
return typeof target.pageId === "string";
|
|
3743
|
+
}
|
|
3426
3744
|
function isConsoleRecord(payload) {
|
|
3427
3745
|
if (!payload || typeof payload !== "object") {
|
|
3428
3746
|
return false;
|