@xiaou66/vite-plugin-vue-mcp-next 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,6 +35,11 @@ var MCP_TOOL_NAMES = {
35
35
  getNetworkRequests: "get_network_requests",
36
36
  getNetworkRequestDetail: "get_network_request_detail",
37
37
  clearNetworkRequests: "clear_network_requests",
38
+ listStorage: "list_storage",
39
+ getStorageItem: "get_storage_item",
40
+ setStorageItem: "set_storage_item",
41
+ deleteStorageItem: "delete_storage_item",
42
+ clearStorage: "clear_storage",
38
43
  recordPerformance: "record_performance",
39
44
  startPerformanceRecording: "start_performance_recording",
40
45
  stopPerformanceRecording: "stop_performance_recording",
@@ -46,7 +51,8 @@ var MCP_TOOL_NAMES = {
46
51
  highlightComponent: "highlight_component",
47
52
  getRouterInfo: "get_router_info",
48
53
  getPiniaTree: "get_pinia_tree",
49
- getPiniaState: "get_pinia_state"
54
+ getPiniaState: "get_pinia_state",
55
+ getElementContext: "get_element_context"
50
56
  };
51
57
  var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
52
58
  var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
@@ -62,6 +68,7 @@ var RUNTIME_PAGE_DISCONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-disconnecte
62
68
  var RUNTIME_PAGE_HEARTBEAT_EVENT = "vite-plugin-vue-mcp-next:heartbeat";
63
69
  var DEFAULT_RUNTIME_PAGE_HEARTBEAT_TIMEOUT_MS = 45e3;
64
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/evaluate.ts
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: z3.string().optional(),
601
- expression: z3.string(),
602
- awaitPromise: z3.boolean().optional()
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 z4 } from "zod";
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: z4.string().optional(),
659
- urlContains: z4.string().optional(),
660
- method: z4.string().optional(),
661
- status: z4.number().optional(),
662
- limit: z4.number().optional()
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: z4.string() }
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: z4.string().optional()
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 z5 } from "zod";
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: z5.string().optional(),
996
- durationMs: z5.number().optional(),
997
- includeMemory: z5.boolean().optional(),
998
- includeStacks: z5.boolean().optional()
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: z5.string().optional(),
1009
- includeMemory: z5.boolean().optional(),
1010
- includeStacks: z5.boolean().optional()
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: z5.string()
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: z5.string().optional(),
1031
- recordingId: z5.string().optional(),
1032
- limit: z5.number().optional()
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: z5.string().optional()
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 z6 } from "zod";
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 relative2 = normalizePath(path.relative(root, fullPath));
1504
+ const relative3 = normalizePath(path.relative(root, fullPath));
1325
1505
  entries.push({
1326
- file: relative2,
1327
- pathname: relative2 === "index.html" ? "/" : `/${relative2}`
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: z6.boolean().optional()
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: z6.string().optional(),
1363
- ignoreCache: z6.boolean().optional()
1542
+ pageId: z7.string().optional(),
1543
+ ignoreCache: z7.boolean().optional()
1364
1544
  }
1365
1545
  },
1366
1546
  async (input) => {
@@ -1511,8 +1691,430 @@ function getPathname(url) {
1511
1691
  }
1512
1692
  }
1513
1693
 
1694
+ // src/mcp/tools/storage.ts
1695
+ import { z as z8 } from "zod";
1696
+
1697
+ // src/cdp/cdpStorage.ts
1698
+ function createCdpStorageAdapter(client) {
1699
+ return {
1700
+ async manageStorage(request) {
1701
+ try {
1702
+ return await manageCdpStorage(client, request);
1703
+ } catch (error) {
1704
+ return createCdpStorageError(
1705
+ request,
1706
+ error instanceof Error ? error.message : String(error)
1707
+ );
1708
+ }
1709
+ }
1710
+ };
1711
+ }
1712
+ async function manageCdpStorage(client, request) {
1713
+ if (request.scope === "cookie") {
1714
+ return manageCdpCookies(client, request);
1715
+ }
1716
+ if (request.scope === "indexedDB") {
1717
+ return manageCdpIndexedDb(client, request);
1718
+ }
1719
+ return manageCdpDomStorage(client, request);
1720
+ }
1721
+ async function manageCdpCookies(client, request) {
1722
+ if (request.action === "list" || request.action === "get") {
1723
+ const result2 = await client.Storage.getCookies();
1724
+ const cookies2 = result2.cookies.filter(
1725
+ (cookie) => isCookieInOrigin(cookie, request.origin)
1726
+ );
1727
+ return createCdpStorageSuccess(request, {
1728
+ origin: request.origin,
1729
+ cookies: request.action === "get" && request.cookie?.name ? cookies2.filter((cookie) => cookie.name === request.cookie?.name) : cookies2
1730
+ });
1731
+ }
1732
+ if (request.action === "set") {
1733
+ if (!request.cookie?.name) {
1734
+ throw new Error("Cookie name is required for set operation");
1735
+ }
1736
+ await client.Storage.setCookies({
1737
+ cookies: [
1738
+ {
1739
+ name: request.cookie.name,
1740
+ value: request.cookie.value ?? request.value ?? "",
1741
+ url: request.cookie.url ?? request.origin,
1742
+ domain: request.cookie.domain,
1743
+ path: request.cookie.path,
1744
+ httpOnly: request.cookie.httpOnly,
1745
+ secure: request.cookie.secure,
1746
+ sameSite: normalizeCookieSameSite(request.cookie.sameSite),
1747
+ expires: request.cookie.expires
1748
+ }
1749
+ ]
1750
+ });
1751
+ return createCdpStorageSuccess(request, { ok: true });
1752
+ }
1753
+ const result = await client.Storage.getCookies();
1754
+ const cookies = result.cookies.filter(
1755
+ (cookie) => isCookieInOrigin(cookie, request.origin)
1756
+ );
1757
+ const candidates = request.action === "delete" && request.cookie?.name ? cookies.filter((cookie) => cookie.name === request.cookie?.name) : cookies;
1758
+ const deletable = candidates.filter((cookie) => !cookie.httpOnly);
1759
+ const skippedHttpOnlyCount = candidates.length - deletable.length;
1760
+ for (const cookie of deletable) {
1761
+ await client.Network.deleteCookies({
1762
+ name: cookie.name,
1763
+ domain: cookie.domain,
1764
+ path: cookie.path
1765
+ });
1766
+ }
1767
+ return createCdpStorageSuccess(request, {
1768
+ deletedCount: deletable.length,
1769
+ skippedHttpOnlyCount
1770
+ });
1771
+ }
1772
+ async function manageCdpDomStorage(client, request) {
1773
+ const storageId = {
1774
+ securityOrigin: request.origin,
1775
+ isLocalStorage: request.scope === "localStorage"
1776
+ };
1777
+ if (request.action === "list") {
1778
+ const result2 = await client.DOMStorage.getDOMStorageItems({ storageId });
1779
+ return createCdpStorageSuccess(request, {
1780
+ origin: request.origin,
1781
+ scope: request.scope,
1782
+ entries: result2.entries.map(([key, value]) => ({ key, value }))
1783
+ });
1784
+ }
1785
+ if (request.action === "get") {
1786
+ assertStorageKey(request);
1787
+ const result2 = await client.DOMStorage.getDOMStorageItems({ storageId });
1788
+ const entry = result2.entries.find(([key]) => key === request.key);
1789
+ return createCdpStorageSuccess(request, {
1790
+ key: request.key,
1791
+ value: entry?.[1] ?? null
1792
+ });
1793
+ }
1794
+ if (request.action === "set") {
1795
+ assertStorageKey(request);
1796
+ await client.DOMStorage.setDOMStorageItem({
1797
+ storageId,
1798
+ key: request.key,
1799
+ value: request.value ?? ""
1800
+ });
1801
+ return createCdpStorageSuccess(request, { ok: true });
1802
+ }
1803
+ if (request.action === "delete") {
1804
+ assertStorageKey(request);
1805
+ await client.DOMStorage.removeDOMStorageItem({
1806
+ storageId,
1807
+ key: request.key
1808
+ });
1809
+ return createCdpStorageSuccess(request, { ok: true });
1810
+ }
1811
+ const result = await client.DOMStorage.getDOMStorageItems({ storageId });
1812
+ for (const [key] of result.entries) {
1813
+ await client.DOMStorage.removeDOMStorageItem({ storageId, key });
1814
+ }
1815
+ return createCdpStorageSuccess(request, { deletedCount: result.entries.length });
1816
+ }
1817
+ async function manageCdpIndexedDb(client, request) {
1818
+ if (request.action === "list") {
1819
+ const result = await client.IndexedDB.requestDatabaseNames({
1820
+ securityOrigin: request.origin
1821
+ });
1822
+ return createCdpStorageSuccess(request, {
1823
+ origin: request.origin,
1824
+ databases: result.databaseNames
1825
+ });
1826
+ }
1827
+ assertIndexedDbTarget(request);
1828
+ if (request.action === "get") {
1829
+ const result = await client.IndexedDB.requestData({
1830
+ securityOrigin: request.origin,
1831
+ databaseName: request.databaseName,
1832
+ objectStoreName: request.objectStoreName,
1833
+ indexName: request.indexName ?? "",
1834
+ skipCount: 0,
1835
+ pageSize: 100
1836
+ });
1837
+ return createCdpStorageSuccess(request, {
1838
+ entries: result.objectStoreDataEntries,
1839
+ hasMore: result.hasMore
1840
+ });
1841
+ }
1842
+ if (request.action === "delete") {
1843
+ assertStorageKey(request);
1844
+ await client.IndexedDB.deleteObjectStoreEntries({
1845
+ securityOrigin: request.origin,
1846
+ databaseName: request.databaseName,
1847
+ objectStoreName: request.objectStoreName,
1848
+ keyRange: createExactCdpKeyRange(request.key)
1849
+ });
1850
+ return createCdpStorageSuccess(request, { ok: true });
1851
+ }
1852
+ if (request.action === "clear") {
1853
+ if (request.objectStoreName) {
1854
+ await client.IndexedDB.clearObjectStore({
1855
+ securityOrigin: request.origin,
1856
+ databaseName: request.databaseName,
1857
+ objectStoreName: request.objectStoreName
1858
+ });
1859
+ return createCdpStorageSuccess(request, { ok: true });
1860
+ }
1861
+ await client.IndexedDB.deleteDatabase({
1862
+ securityOrigin: request.origin,
1863
+ databaseName: request.databaseName
1864
+ });
1865
+ return createCdpStorageSuccess(request, { ok: true });
1866
+ }
1867
+ return createCdpStorageError(
1868
+ request,
1869
+ "IndexedDB set operation requires runtime bridge"
1870
+ );
1871
+ }
1872
+ function isCookieInOrigin(cookie, origin) {
1873
+ const hostname = new URL(origin).hostname;
1874
+ const domain = cookie.domain?.replace(/^\./, "");
1875
+ return Boolean(domain && (hostname === domain || hostname.endsWith(`.${domain}`)));
1876
+ }
1877
+ function normalizeCookieSameSite(sameSite) {
1878
+ if (!sameSite) {
1879
+ return void 0;
1880
+ }
1881
+ if (sameSite === "strict") {
1882
+ return "Strict";
1883
+ }
1884
+ if (sameSite === "lax") {
1885
+ return "Lax";
1886
+ }
1887
+ return "None";
1888
+ }
1889
+ function createExactCdpKeyRange(key) {
1890
+ const parsedKey = parseJsonValue(key);
1891
+ return {
1892
+ lower: parsedKey,
1893
+ upper: parsedKey,
1894
+ lowerOpen: false,
1895
+ upperOpen: false
1896
+ };
1897
+ }
1898
+ function parseJsonValue(value) {
1899
+ try {
1900
+ return JSON.parse(value);
1901
+ } catch {
1902
+ return value;
1903
+ }
1904
+ }
1905
+ function assertStorageKey(request) {
1906
+ if (!request.key) {
1907
+ throw new Error("Storage key is required for this operation");
1908
+ }
1909
+ }
1910
+ function assertIndexedDbTarget(request) {
1911
+ if (!request.databaseName || !request.objectStoreName) {
1912
+ throw new Error("IndexedDB databaseName and objectStoreName are required");
1913
+ }
1914
+ }
1915
+ function createCdpStorageSuccess(request, data) {
1916
+ return {
1917
+ ok: true,
1918
+ source: "cdp",
1919
+ action: request.action,
1920
+ scope: request.scope,
1921
+ data
1922
+ };
1923
+ }
1924
+ function createCdpStorageError(request, error) {
1925
+ return {
1926
+ ok: false,
1927
+ source: "cdp",
1928
+ action: request.action,
1929
+ scope: request.scope,
1930
+ error
1931
+ };
1932
+ }
1933
+
1934
+ // src/mcp/tools/storage.ts
1935
+ function registerStorageTools(server, ctx) {
1936
+ registerStorageTool(server, MCP_TOOL_NAMES.listStorage, {
1937
+ description: "List same-origin storage and CDP cookies when available.",
1938
+ inputSchema: {
1939
+ pageId: z8.string().optional()
1940
+ },
1941
+ action: "list",
1942
+ handler: (input) => handleListStorage(ctx, input.pageId)
1943
+ });
1944
+ registerStorageTool(server, MCP_TOOL_NAMES.getStorageItem, {
1945
+ description: "Read one storage entry.",
1946
+ inputSchema: createStorageInputSchema(),
1947
+ action: "get",
1948
+ handler: (input) => handleStorageAction(ctx, { ...input, action: "get" })
1949
+ });
1950
+ registerStorageTool(server, MCP_TOOL_NAMES.setStorageItem, {
1951
+ description: "Write one storage entry.",
1952
+ inputSchema: createStorageInputSchema(),
1953
+ action: "set",
1954
+ handler: (input) => handleStorageAction(ctx, { ...input, action: "set" })
1955
+ });
1956
+ registerStorageTool(server, MCP_TOOL_NAMES.deleteStorageItem, {
1957
+ description: "Delete one storage entry.",
1958
+ inputSchema: createStorageInputSchema(),
1959
+ action: "delete",
1960
+ handler: (input) => handleStorageAction(ctx, { ...input, action: "delete" })
1961
+ });
1962
+ registerStorageTool(server, MCP_TOOL_NAMES.clearStorage, {
1963
+ description: "Clear one storage scope.",
1964
+ inputSchema: createStorageInputSchema(),
1965
+ action: "clear",
1966
+ handler: (input) => handleStorageAction(ctx, { ...input, action: "clear" })
1967
+ });
1968
+ }
1969
+ function registerStorageTool(server, name, options) {
1970
+ server.registerTool(
1971
+ name,
1972
+ {
1973
+ description: options.description,
1974
+ inputSchema: options.inputSchema
1975
+ },
1976
+ (async (input) => options.handler(
1977
+ input
1978
+ ))
1979
+ );
1980
+ }
1981
+ function createStorageInputSchema() {
1982
+ return {
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()
2000
+ }).optional()
2001
+ };
2002
+ }
2003
+ async function handleListStorage(ctx, pageId) {
2004
+ let baseRequest;
2005
+ try {
2006
+ baseRequest = createStorageRequest(ctx, {
2007
+ pageId,
2008
+ action: "list",
2009
+ scope: "localStorage"
2010
+ });
2011
+ } catch (error) {
2012
+ return createToolError(error instanceof Error ? error.message : String(error));
2013
+ }
2014
+ const [localStorage, sessionStorage, indexedDB] = await Promise.all([
2015
+ requestRuntimeStorage(ctx, { ...baseRequest, scope: "localStorage" }),
2016
+ requestRuntimeStorage(ctx, { ...baseRequest, scope: "sessionStorage" }),
2017
+ requestRuntimeStorage(ctx, { ...baseRequest, scope: "indexedDB" })
2018
+ ]);
2019
+ const cookie = await listCookiesIfCdpAvailable(ctx, baseRequest, pageId);
2020
+ return createToolResponse({
2021
+ ok: true,
2022
+ origin: baseRequest.origin,
2023
+ localStorage: extractStorageData(localStorage),
2024
+ sessionStorage: extractStorageData(sessionStorage),
2025
+ indexedDB: extractStorageData(indexedDB),
2026
+ cookie
2027
+ });
2028
+ }
2029
+ async function handleStorageAction(ctx, input) {
2030
+ let request;
2031
+ try {
2032
+ request = createStorageRequest(ctx, input);
2033
+ } catch (error) {
2034
+ return createToolError(error instanceof Error ? error.message : String(error));
2035
+ }
2036
+ const cdp = await connectCdpForPage(ctx, input.pageId);
2037
+ if (cdp && shouldUseCdpStorage(request)) {
2038
+ try {
2039
+ const result2 = await createCdpStorageAdapter(cdp.client).manageStorage(
2040
+ request
2041
+ );
2042
+ return createToolResponse(result2);
2043
+ } finally {
2044
+ await closeCdpClient(cdp.client);
2045
+ }
2046
+ }
2047
+ const result = await requestRuntimeStorage(ctx, request);
2048
+ return createToolResponse(result);
2049
+ }
2050
+ async function requestRuntimeStorage(ctx, request) {
2051
+ return requestRuntimeData(ctx, (event) => {
2052
+ void ctx.rpcServer?.manageStorage({
2053
+ ...request,
2054
+ event
2055
+ });
2056
+ });
2057
+ }
2058
+ async function listCookiesIfCdpAvailable(ctx, request, pageId) {
2059
+ if (!hasCdpConfig2(ctx)) {
2060
+ return extractStorageData(
2061
+ await requestRuntimeStorage(ctx, { ...request, scope: "cookie" })
2062
+ );
2063
+ }
2064
+ const cdp = await connectCdpForPage(ctx, pageId);
2065
+ if (!cdp) {
2066
+ return extractStorageData(
2067
+ await requestRuntimeStorage(ctx, { ...request, scope: "cookie" })
2068
+ );
2069
+ }
2070
+ try {
2071
+ const result = await createCdpStorageAdapter(cdp.client).manageStorage({
2072
+ ...request,
2073
+ scope: "cookie"
2074
+ });
2075
+ return extractStorageData(result);
2076
+ } finally {
2077
+ await closeCdpClient(cdp.client);
2078
+ }
2079
+ }
2080
+ function extractStorageData(result) {
2081
+ if (!isStorageResultRecord(result)) {
2082
+ return result;
2083
+ }
2084
+ return result.data ?? result;
2085
+ }
2086
+ function isStorageResultRecord(value) {
2087
+ return typeof value === "object" && value !== null && "ok" in value;
2088
+ }
2089
+ function shouldUseCdpStorage(request) {
2090
+ return request.scope === "cookie";
2091
+ }
2092
+ function createStorageRequest(ctx, input) {
2093
+ const page = resolvePageTarget(ctx, input.pageId);
2094
+ const origin = new URL(page.url).origin;
2095
+ return {
2096
+ event: "",
2097
+ pageId: page.pageId,
2098
+ origin,
2099
+ action: input.action,
2100
+ scope: normalizeScope(input.scope),
2101
+ key: input.key,
2102
+ value: input.value,
2103
+ databaseName: input.databaseName,
2104
+ objectStoreName: input.objectStoreName,
2105
+ indexName: input.indexName,
2106
+ cookie: input.cookie
2107
+ };
2108
+ }
2109
+ function normalizeScope(scope) {
2110
+ return scope ?? "localStorage";
2111
+ }
2112
+ function hasCdpConfig2(ctx) {
2113
+ return Boolean(ctx.options.cdp.browserUrl || ctx.options.cdp.wsEndpoint);
2114
+ }
2115
+
1514
2116
  // src/mcp/tools/screenshot.ts
1515
- import { z as z7 } from "zod";
2117
+ import { z as z9 } from "zod";
1516
2118
 
1517
2119
  // src/cdp/cdpScreenshot.ts
1518
2120
  async function cdpCaptureScreenshot(options) {
@@ -1672,14 +2274,14 @@ function createProjectRelativePath(ctx, filePath) {
1672
2274
  var DEFAULT_SCREENSHOT_TARGET = "viewport";
1673
2275
  var DEFAULT_SCREENSHOT_FORMAT = "png";
1674
2276
  var screenshotInputSchema = {
1675
- pageId: z7.string().optional(),
1676
- target: z7.enum(["viewport", "fullPage", "element"]).optional(),
1677
- selector: z7.string().optional(),
1678
- format: z7.enum(["png", "jpeg", "webp"]).optional(),
1679
- prefer: z7.enum(["auto", "cdp", "runtime"]).optional(),
1680
- quality: z7.number().optional(),
1681
- scale: z7.number().optional(),
1682
- snapdom: z7.record(z7.string(), z7.unknown()).optional()
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()
1683
2285
  };
1684
2286
  function registerScreenshotTools(server, ctx) {
1685
2287
  server.registerTool(
@@ -1799,7 +2401,7 @@ function isScreenshotImagePayload(result) {
1799
2401
 
1800
2402
  // src/mcp/tools/vue.ts
1801
2403
  import { nanoid as nanoid3 } from "nanoid";
1802
- import { z as z8 } from "zod";
2404
+ import { z as z10 } from "zod";
1803
2405
  function registerVueTools(server, ctx) {
1804
2406
  server.registerTool(
1805
2407
  MCP_TOOL_NAMES.getComponentTree,
@@ -1812,7 +2414,7 @@ function registerVueTools(server, ctx) {
1812
2414
  MCP_TOOL_NAMES.getComponentState,
1813
2415
  {
1814
2416
  description: "Get Vue component state.",
1815
- inputSchema: { componentName: z8.string() }
2417
+ inputSchema: { componentName: z10.string() }
1816
2418
  },
1817
2419
  async ({ componentName }) => requestVueData(ctx, (event) => {
1818
2420
  void ctx.rpcServer?.getInspectorState({ event, componentName });
@@ -1823,10 +2425,10 @@ function registerVueTools(server, ctx) {
1823
2425
  {
1824
2426
  description: "Edit Vue component state.",
1825
2427
  inputSchema: {
1826
- componentName: z8.string(),
1827
- path: z8.array(z8.string()),
1828
- value: z8.string(),
1829
- valueType: z8.enum(["string", "number", "boolean", "object", "array"])
2428
+ componentName: z10.string(),
2429
+ path: z10.array(z10.string()),
2430
+ value: z10.string(),
2431
+ valueType: z10.enum(["string", "number", "boolean", "object", "array"])
1830
2432
  }
1831
2433
  },
1832
2434
  ({ componentName, path: path8, value, valueType }) => {
@@ -1846,7 +2448,7 @@ function registerVueTools(server, ctx) {
1846
2448
  MCP_TOOL_NAMES.highlightComponent,
1847
2449
  {
1848
2450
  description: "Highlight a Vue component.",
1849
- inputSchema: { componentName: z8.string() }
2451
+ inputSchema: { componentName: z10.string() }
1850
2452
  },
1851
2453
  ({ componentName }) => {
1852
2454
  if (!ctx.rpcServer) {
@@ -1874,7 +2476,7 @@ function registerVueTools(server, ctx) {
1874
2476
  MCP_TOOL_NAMES.getPiniaState,
1875
2477
  {
1876
2478
  description: "Get Pinia store state.",
1877
- inputSchema: { storeName: z8.string() }
2479
+ inputSchema: { storeName: z10.string() }
1878
2480
  },
1879
2481
  async ({ storeName }) => requestVueData(ctx, (event) => {
1880
2482
  void ctx.rpcServer?.getPiniaState({ event, storeName });
@@ -1917,10 +2519,12 @@ function createMcpServer(ctx, vite) {
1917
2519
  });
1918
2520
  registerPageTools(server, ctx, vite);
1919
2521
  registerDomTools(server, ctx);
2522
+ registerElementContextTools(server, ctx);
1920
2523
  registerScreenshotTools(server, ctx);
1921
2524
  registerConsoleTools(server, ctx);
1922
2525
  registerEvaluateTools(server, ctx);
1923
2526
  registerNetworkTools(server, ctx);
2527
+ registerStorageTools(server, ctx);
1924
2528
  registerPerformanceTools(server, ctx);
1925
2529
  registerVueTools(server, ctx);
1926
2530
  return server;
@@ -1997,6 +2601,10 @@ function createServerVueRuntimeRpc(ctx) {
1997
2601
  onDomQueryUpdated: (event, data) => {
1998
2602
  void ctx.hooks.callHook(event, data);
1999
2603
  },
2604
+ getElementContext: () => void 0,
2605
+ onElementContextUpdated: (event, data) => {
2606
+ void ctx.hooks.callHook(event, data);
2607
+ },
2000
2608
  reloadPage: () => void 0,
2001
2609
  onPageReloaded: (event, data) => {
2002
2610
  void ctx.hooks.callHook(event, data);
@@ -2009,6 +2617,10 @@ function createServerVueRuntimeRpc(ctx) {
2009
2617
  onScreenshotTaken: (event, data) => {
2010
2618
  void ctx.hooks.callHook(event, data);
2011
2619
  },
2620
+ manageStorage: () => void 0,
2621
+ onStorageUpdated: (event, data) => {
2622
+ void ctx.hooks.callHook(event, data);
2623
+ },
2012
2624
  recordPerformance: () => void 0,
2013
2625
  onPerformanceRecorded: (event, data) => {
2014
2626
  void ctx.hooks.callHook(event, data);
@@ -2305,6 +2917,82 @@ async function startCdpObservers(ctx, target, client) {
2305
2917
  }
2306
2918
  }
2307
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
+
2308
2996
  // src/plugin/injectRuntime.ts
2309
2997
  import { createRequire } from "module";
2310
2998
  import { join } from "path";
@@ -2324,7 +3012,7 @@ function createRuntimeInjectionController(options, getConfig) {
2324
3012
  },
2325
3013
  load(id) {
2326
3014
  if (id === RESOLVED_VIRTUAL_RUNTIME_ID) {
2327
- return createRuntimeModule();
3015
+ return createRuntimeModule(options, getConfig()?.root);
2328
3016
  }
2329
3017
  if (id === RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID) {
2330
3018
  return createScreenshotConfigModule(options);
@@ -2367,14 +3055,17 @@ ${code}`;
2367
3055
  }
2368
3056
  };
2369
3057
  }
2370
- function createRuntimeModule() {
3058
+ function createRuntimeModule(options, root) {
2371
3059
  return [
2372
3060
  "import { setScreenshotModuleRegistry, setSnapdomLoader, startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';",
2373
3061
  `import { screenshotModuleRegistry } from '${VIRTUAL_SCREENSHOT_CONFIG_ID}';`,
2374
3062
  `import { loadSnapdom } from '${VIRTUAL_SNAPDOM_LOADER_ID}';`,
2375
3063
  "setScreenshotModuleRegistry(screenshotModuleRegistry);",
2376
3064
  "setSnapdomLoader(loadSnapdom);",
2377
- "void startRuntimeClient();"
3065
+ `void startRuntimeClient(${JSON.stringify({
3066
+ elementPicker: options.elementPicker,
3067
+ projectRoot: root
3068
+ })});`
2378
3069
  ].join("\n");
2379
3070
  }
2380
3071
  function createSnapdomLoaderModule(root) {
@@ -2893,6 +3584,7 @@ function vueMcpNext(userOptions = {}) {
2893
3584
  options,
2894
3585
  () => config
2895
3586
  );
3587
+ let elementInstrumentation;
2896
3588
  const cdpLifecycle = createCdpLifecycleController(ctx);
2897
3589
  ctx.cdpLifecycle = cdpLifecycle;
2898
3590
  return {
@@ -2901,6 +3593,9 @@ function vueMcpNext(userOptions = {}) {
2901
3593
  apply: "serve",
2902
3594
  configResolved(resolvedConfig) {
2903
3595
  config = resolvedConfig;
3596
+ elementInstrumentation = createElementInstrumentationController({
3597
+ root: resolvedConfig.root
3598
+ });
2904
3599
  },
2905
3600
  async configureServer(server) {
2906
3601
  ctx.server = server;
@@ -3004,7 +3699,21 @@ function vueMcpNext(userOptions = {}) {
3004
3699
  return runtimeInjection.load(id);
3005
3700
  },
3006
3701
  transform(code, id, transformOptions) {
3007
- return runtimeInjection.transform(code, id, transformOptions?.ssr);
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;
3008
3717
  },
3009
3718
  transformIndexHtml(html) {
3010
3719
  return runtimeInjection.transformIndexHtml(html);