@xiaou66/vite-plugin-vue-mcp-next 1.1.1 → 1.2.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/README.md +20 -0
- package/dist/index.cjs +470 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +470 -79
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +500 -126
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.js +500 -126
- package/dist/runtime/client.js.map +1 -1
- package/package.json +1 -1
- package/skills/vite-mcp-next/SKILL.md +7 -2
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
| Console 日志 | Runtime Hook | CDP 优先,Hook 兜底 | 采集 `log/info/warn/error/debug` 和运行时日志 |
|
|
16
16
|
| Evaluate 控制台执行 | Runtime Hook | CDP 优先,Hook 兜底 | 默认关闭,必须显式开启 |
|
|
17
17
|
| Network 请求 | Runtime Hook | CDP 优先,Hook 兜底 | 返回请求 URL、query、body、status、headers、response body |
|
|
18
|
+
| 浏览器存储 | Runtime Hook | CDP 优先,Hook 兜底 | 访问同源 Web Storage、IndexedDB;Cookie 在无 CDP 时回退到 `document.cookie` |
|
|
18
19
|
| Vue 组件树 | Vue Runtime Bridge | Vue Runtime Bridge | Vue 专属语义不走 CDP |
|
|
19
20
|
| Vue 组件状态 | Vue Runtime Bridge | Vue Runtime Bridge | 读取和编辑组件状态 |
|
|
20
21
|
| Router 信息 | Vue Runtime Bridge | Vue Runtime Bridge | 返回当前路由和路由表 |
|
|
@@ -548,6 +549,25 @@ interface NetworkRecord {
|
|
|
548
549
|
|
|
549
550
|
Vue 组件、Router、Pinia 是应用层语义,固定走 Vue Runtime Bridge,不用 CDP 替代。
|
|
550
551
|
|
|
552
|
+
### 浏览器存储
|
|
553
|
+
|
|
554
|
+
浏览器存储工具组面向调试当前页面的运行时数据。`localStorage`、`sessionStorage` 和 `IndexedDB` 只作用于当前选中页面同源,避免跨站误操作。Cookie 在无 CDP 时通过 `document.cookie` 访问当前页面可见的同源条目;配置 CDP 后可查询浏览器级 Cookie,并能读取 `HttpOnly` 条目,但删除和清空时会跳过 `HttpOnly` 并返回跳过数量。
|
|
555
|
+
|
|
556
|
+
| 资源 | Runtime Hook | CDP |
|
|
557
|
+
| --- | --- | --- |
|
|
558
|
+
| `localStorage` | 读 / 写 / 删 | 读 / 写 / 删 |
|
|
559
|
+
| `sessionStorage` | 读 / 写 / 删 | 读 / 写 / 删 |
|
|
560
|
+
| `IndexedDB` | 同源库和记录操作 | 同源库和记录操作 |
|
|
561
|
+
| `Cookie` | 查询 / 写入 / 删除当前页面可见条目 | 查询 / 写入 / 删除非 `HttpOnly` 条目,`HttpOnly` 仅可查询 |
|
|
562
|
+
|
|
563
|
+
相关 MCP 工具:
|
|
564
|
+
|
|
565
|
+
- `list_storage`:列出当前页面同源存储和 Cookie;有 CDP 时补充浏览器级 Cookie
|
|
566
|
+
- `get_storage_item`:读取指定 key、IndexedDB 记录或 Cookie
|
|
567
|
+
- `set_storage_item`:写入 Web Storage、IndexedDB 记录或 Cookie
|
|
568
|
+
- `delete_storage_item`:删除 Web Storage、IndexedDB 记录或 Cookie;`HttpOnly` Cookie 仅在 CDP 下可见且删除时会跳过
|
|
569
|
+
- `clear_storage`:清空指定范围,Cookie 清空会跳过 `HttpOnly`
|
|
570
|
+
|
|
551
571
|
## 本地验证
|
|
552
572
|
|
|
553
573
|
### 自动化检查
|
package/dist/index.cjs
CHANGED
|
@@ -72,6 +72,11 @@ var MCP_TOOL_NAMES = {
|
|
|
72
72
|
getNetworkRequests: "get_network_requests",
|
|
73
73
|
getNetworkRequestDetail: "get_network_request_detail",
|
|
74
74
|
clearNetworkRequests: "clear_network_requests",
|
|
75
|
+
listStorage: "list_storage",
|
|
76
|
+
getStorageItem: "get_storage_item",
|
|
77
|
+
setStorageItem: "set_storage_item",
|
|
78
|
+
deleteStorageItem: "delete_storage_item",
|
|
79
|
+
clearStorage: "clear_storage",
|
|
75
80
|
recordPerformance: "record_performance",
|
|
76
81
|
startPerformanceRecording: "start_performance_recording",
|
|
77
82
|
stopPerformanceRecording: "stop_performance_recording",
|
|
@@ -94,11 +99,6 @@ var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
|
|
|
94
99
|
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
|
|
95
100
|
var LEGACY_MCP_CLIENT_SERVER_NAMES = ["vue-mcp-next"];
|
|
96
101
|
var RUNTIME_PAGE_RECONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-reconnected";
|
|
97
|
-
var RUNTIME_PAGE_CONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-connected";
|
|
98
|
-
var RUNTIME_PAGE_DISCONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-disconnected";
|
|
99
|
-
var RUNTIME_PAGE_HEARTBEAT_EVENT = "vite-plugin-vue-mcp-next:heartbeat";
|
|
100
|
-
var DEFAULT_RUNTIME_PAGE_HEARTBEAT_TIMEOUT_MS = 45e3;
|
|
101
|
-
var DEFAULT_RUNTIME_PAGE_HEARTBEAT_SCAN_INTERVAL_MS = 45e3;
|
|
102
102
|
var DEFAULT_OPTIONS = {
|
|
103
103
|
mcpPath: DEFAULT_MCP_PATH,
|
|
104
104
|
host: "localhost",
|
|
@@ -1548,9 +1548,431 @@ function getPathname(url) {
|
|
|
1548
1548
|
}
|
|
1549
1549
|
}
|
|
1550
1550
|
|
|
1551
|
-
// src/mcp/tools/
|
|
1551
|
+
// src/mcp/tools/storage.ts
|
|
1552
1552
|
var import_zod7 = require("zod");
|
|
1553
1553
|
|
|
1554
|
+
// src/cdp/cdpStorage.ts
|
|
1555
|
+
function createCdpStorageAdapter(client) {
|
|
1556
|
+
return {
|
|
1557
|
+
async manageStorage(request) {
|
|
1558
|
+
try {
|
|
1559
|
+
return await manageCdpStorage(client, request);
|
|
1560
|
+
} catch (error) {
|
|
1561
|
+
return createCdpStorageError(
|
|
1562
|
+
request,
|
|
1563
|
+
error instanceof Error ? error.message : String(error)
|
|
1564
|
+
);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
async function manageCdpStorage(client, request) {
|
|
1570
|
+
if (request.scope === "cookie") {
|
|
1571
|
+
return manageCdpCookies(client, request);
|
|
1572
|
+
}
|
|
1573
|
+
if (request.scope === "indexedDB") {
|
|
1574
|
+
return manageCdpIndexedDb(client, request);
|
|
1575
|
+
}
|
|
1576
|
+
return manageCdpDomStorage(client, request);
|
|
1577
|
+
}
|
|
1578
|
+
async function manageCdpCookies(client, request) {
|
|
1579
|
+
if (request.action === "list" || request.action === "get") {
|
|
1580
|
+
const result2 = await client.Storage.getCookies();
|
|
1581
|
+
const cookies2 = result2.cookies.filter(
|
|
1582
|
+
(cookie) => isCookieInOrigin(cookie, request.origin)
|
|
1583
|
+
);
|
|
1584
|
+
return createCdpStorageSuccess(request, {
|
|
1585
|
+
origin: request.origin,
|
|
1586
|
+
cookies: request.action === "get" && request.cookie?.name ? cookies2.filter((cookie) => cookie.name === request.cookie?.name) : cookies2
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
if (request.action === "set") {
|
|
1590
|
+
if (!request.cookie?.name) {
|
|
1591
|
+
throw new Error("Cookie name is required for set operation");
|
|
1592
|
+
}
|
|
1593
|
+
await client.Storage.setCookies({
|
|
1594
|
+
cookies: [
|
|
1595
|
+
{
|
|
1596
|
+
name: request.cookie.name,
|
|
1597
|
+
value: request.cookie.value ?? request.value ?? "",
|
|
1598
|
+
url: request.cookie.url ?? request.origin,
|
|
1599
|
+
domain: request.cookie.domain,
|
|
1600
|
+
path: request.cookie.path,
|
|
1601
|
+
httpOnly: request.cookie.httpOnly,
|
|
1602
|
+
secure: request.cookie.secure,
|
|
1603
|
+
sameSite: normalizeCookieSameSite(request.cookie.sameSite),
|
|
1604
|
+
expires: request.cookie.expires
|
|
1605
|
+
}
|
|
1606
|
+
]
|
|
1607
|
+
});
|
|
1608
|
+
return createCdpStorageSuccess(request, { ok: true });
|
|
1609
|
+
}
|
|
1610
|
+
const result = await client.Storage.getCookies();
|
|
1611
|
+
const cookies = result.cookies.filter(
|
|
1612
|
+
(cookie) => isCookieInOrigin(cookie, request.origin)
|
|
1613
|
+
);
|
|
1614
|
+
const candidates = request.action === "delete" && request.cookie?.name ? cookies.filter((cookie) => cookie.name === request.cookie?.name) : cookies;
|
|
1615
|
+
const deletable = candidates.filter((cookie) => !cookie.httpOnly);
|
|
1616
|
+
const skippedHttpOnlyCount = candidates.length - deletable.length;
|
|
1617
|
+
for (const cookie of deletable) {
|
|
1618
|
+
await client.Network.deleteCookies({
|
|
1619
|
+
name: cookie.name,
|
|
1620
|
+
domain: cookie.domain,
|
|
1621
|
+
path: cookie.path
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
return createCdpStorageSuccess(request, {
|
|
1625
|
+
deletedCount: deletable.length,
|
|
1626
|
+
skippedHttpOnlyCount
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
async function manageCdpDomStorage(client, request) {
|
|
1630
|
+
const storageId = {
|
|
1631
|
+
securityOrigin: request.origin,
|
|
1632
|
+
isLocalStorage: request.scope === "localStorage"
|
|
1633
|
+
};
|
|
1634
|
+
if (request.action === "list") {
|
|
1635
|
+
const result2 = await client.DOMStorage.getDOMStorageItems({ storageId });
|
|
1636
|
+
return createCdpStorageSuccess(request, {
|
|
1637
|
+
origin: request.origin,
|
|
1638
|
+
scope: request.scope,
|
|
1639
|
+
entries: result2.entries.map(([key, value]) => ({ key, value }))
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
if (request.action === "get") {
|
|
1643
|
+
assertStorageKey(request);
|
|
1644
|
+
const result2 = await client.DOMStorage.getDOMStorageItems({ storageId });
|
|
1645
|
+
const entry = result2.entries.find(([key]) => key === request.key);
|
|
1646
|
+
return createCdpStorageSuccess(request, {
|
|
1647
|
+
key: request.key,
|
|
1648
|
+
value: entry?.[1] ?? null
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
if (request.action === "set") {
|
|
1652
|
+
assertStorageKey(request);
|
|
1653
|
+
await client.DOMStorage.setDOMStorageItem({
|
|
1654
|
+
storageId,
|
|
1655
|
+
key: request.key,
|
|
1656
|
+
value: request.value ?? ""
|
|
1657
|
+
});
|
|
1658
|
+
return createCdpStorageSuccess(request, { ok: true });
|
|
1659
|
+
}
|
|
1660
|
+
if (request.action === "delete") {
|
|
1661
|
+
assertStorageKey(request);
|
|
1662
|
+
await client.DOMStorage.removeDOMStorageItem({
|
|
1663
|
+
storageId,
|
|
1664
|
+
key: request.key
|
|
1665
|
+
});
|
|
1666
|
+
return createCdpStorageSuccess(request, { ok: true });
|
|
1667
|
+
}
|
|
1668
|
+
const result = await client.DOMStorage.getDOMStorageItems({ storageId });
|
|
1669
|
+
for (const [key] of result.entries) {
|
|
1670
|
+
await client.DOMStorage.removeDOMStorageItem({ storageId, key });
|
|
1671
|
+
}
|
|
1672
|
+
return createCdpStorageSuccess(request, { deletedCount: result.entries.length });
|
|
1673
|
+
}
|
|
1674
|
+
async function manageCdpIndexedDb(client, request) {
|
|
1675
|
+
if (request.action === "list") {
|
|
1676
|
+
const result = await client.IndexedDB.requestDatabaseNames({
|
|
1677
|
+
securityOrigin: request.origin
|
|
1678
|
+
});
|
|
1679
|
+
return createCdpStorageSuccess(request, {
|
|
1680
|
+
origin: request.origin,
|
|
1681
|
+
databases: result.databaseNames
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
assertIndexedDbTarget(request);
|
|
1685
|
+
if (request.action === "get") {
|
|
1686
|
+
const result = await client.IndexedDB.requestData({
|
|
1687
|
+
securityOrigin: request.origin,
|
|
1688
|
+
databaseName: request.databaseName,
|
|
1689
|
+
objectStoreName: request.objectStoreName,
|
|
1690
|
+
indexName: request.indexName ?? "",
|
|
1691
|
+
skipCount: 0,
|
|
1692
|
+
pageSize: 100
|
|
1693
|
+
});
|
|
1694
|
+
return createCdpStorageSuccess(request, {
|
|
1695
|
+
entries: result.objectStoreDataEntries,
|
|
1696
|
+
hasMore: result.hasMore
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
if (request.action === "delete") {
|
|
1700
|
+
assertStorageKey(request);
|
|
1701
|
+
await client.IndexedDB.deleteObjectStoreEntries({
|
|
1702
|
+
securityOrigin: request.origin,
|
|
1703
|
+
databaseName: request.databaseName,
|
|
1704
|
+
objectStoreName: request.objectStoreName,
|
|
1705
|
+
keyRange: createExactCdpKeyRange(request.key)
|
|
1706
|
+
});
|
|
1707
|
+
return createCdpStorageSuccess(request, { ok: true });
|
|
1708
|
+
}
|
|
1709
|
+
if (request.action === "clear") {
|
|
1710
|
+
if (request.objectStoreName) {
|
|
1711
|
+
await client.IndexedDB.clearObjectStore({
|
|
1712
|
+
securityOrigin: request.origin,
|
|
1713
|
+
databaseName: request.databaseName,
|
|
1714
|
+
objectStoreName: request.objectStoreName
|
|
1715
|
+
});
|
|
1716
|
+
return createCdpStorageSuccess(request, { ok: true });
|
|
1717
|
+
}
|
|
1718
|
+
await client.IndexedDB.deleteDatabase({
|
|
1719
|
+
securityOrigin: request.origin,
|
|
1720
|
+
databaseName: request.databaseName
|
|
1721
|
+
});
|
|
1722
|
+
return createCdpStorageSuccess(request, { ok: true });
|
|
1723
|
+
}
|
|
1724
|
+
return createCdpStorageError(
|
|
1725
|
+
request,
|
|
1726
|
+
"IndexedDB set operation requires runtime bridge"
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
function isCookieInOrigin(cookie, origin) {
|
|
1730
|
+
const hostname = new URL(origin).hostname;
|
|
1731
|
+
const domain = cookie.domain?.replace(/^\./, "");
|
|
1732
|
+
return Boolean(domain && (hostname === domain || hostname.endsWith(`.${domain}`)));
|
|
1733
|
+
}
|
|
1734
|
+
function normalizeCookieSameSite(sameSite) {
|
|
1735
|
+
if (!sameSite) {
|
|
1736
|
+
return void 0;
|
|
1737
|
+
}
|
|
1738
|
+
if (sameSite === "strict") {
|
|
1739
|
+
return "Strict";
|
|
1740
|
+
}
|
|
1741
|
+
if (sameSite === "lax") {
|
|
1742
|
+
return "Lax";
|
|
1743
|
+
}
|
|
1744
|
+
return "None";
|
|
1745
|
+
}
|
|
1746
|
+
function createExactCdpKeyRange(key) {
|
|
1747
|
+
const parsedKey = parseJsonValue(key);
|
|
1748
|
+
return {
|
|
1749
|
+
lower: parsedKey,
|
|
1750
|
+
upper: parsedKey,
|
|
1751
|
+
lowerOpen: false,
|
|
1752
|
+
upperOpen: false
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
function parseJsonValue(value) {
|
|
1756
|
+
try {
|
|
1757
|
+
return JSON.parse(value);
|
|
1758
|
+
} catch {
|
|
1759
|
+
return value;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
function assertStorageKey(request) {
|
|
1763
|
+
if (!request.key) {
|
|
1764
|
+
throw new Error("Storage key is required for this operation");
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
function assertIndexedDbTarget(request) {
|
|
1768
|
+
if (!request.databaseName || !request.objectStoreName) {
|
|
1769
|
+
throw new Error("IndexedDB databaseName and objectStoreName are required");
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
function createCdpStorageSuccess(request, data) {
|
|
1773
|
+
return {
|
|
1774
|
+
ok: true,
|
|
1775
|
+
source: "cdp",
|
|
1776
|
+
action: request.action,
|
|
1777
|
+
scope: request.scope,
|
|
1778
|
+
data
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
function createCdpStorageError(request, error) {
|
|
1782
|
+
return {
|
|
1783
|
+
ok: false,
|
|
1784
|
+
source: "cdp",
|
|
1785
|
+
action: request.action,
|
|
1786
|
+
scope: request.scope,
|
|
1787
|
+
error
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
// src/mcp/tools/storage.ts
|
|
1792
|
+
function registerStorageTools(server, ctx) {
|
|
1793
|
+
registerStorageTool(server, MCP_TOOL_NAMES.listStorage, {
|
|
1794
|
+
description: "List same-origin storage and CDP cookies when available.",
|
|
1795
|
+
inputSchema: {
|
|
1796
|
+
pageId: import_zod7.z.string().optional()
|
|
1797
|
+
},
|
|
1798
|
+
action: "list",
|
|
1799
|
+
handler: (input) => handleListStorage(ctx, input.pageId)
|
|
1800
|
+
});
|
|
1801
|
+
registerStorageTool(server, MCP_TOOL_NAMES.getStorageItem, {
|
|
1802
|
+
description: "Read one storage entry.",
|
|
1803
|
+
inputSchema: createStorageInputSchema(),
|
|
1804
|
+
action: "get",
|
|
1805
|
+
handler: (input) => handleStorageAction(ctx, { ...input, action: "get" })
|
|
1806
|
+
});
|
|
1807
|
+
registerStorageTool(server, MCP_TOOL_NAMES.setStorageItem, {
|
|
1808
|
+
description: "Write one storage entry.",
|
|
1809
|
+
inputSchema: createStorageInputSchema(),
|
|
1810
|
+
action: "set",
|
|
1811
|
+
handler: (input) => handleStorageAction(ctx, { ...input, action: "set" })
|
|
1812
|
+
});
|
|
1813
|
+
registerStorageTool(server, MCP_TOOL_NAMES.deleteStorageItem, {
|
|
1814
|
+
description: "Delete one storage entry.",
|
|
1815
|
+
inputSchema: createStorageInputSchema(),
|
|
1816
|
+
action: "delete",
|
|
1817
|
+
handler: (input) => handleStorageAction(ctx, { ...input, action: "delete" })
|
|
1818
|
+
});
|
|
1819
|
+
registerStorageTool(server, MCP_TOOL_NAMES.clearStorage, {
|
|
1820
|
+
description: "Clear one storage scope.",
|
|
1821
|
+
inputSchema: createStorageInputSchema(),
|
|
1822
|
+
action: "clear",
|
|
1823
|
+
handler: (input) => handleStorageAction(ctx, { ...input, action: "clear" })
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
function registerStorageTool(server, name, options) {
|
|
1827
|
+
server.registerTool(
|
|
1828
|
+
name,
|
|
1829
|
+
{
|
|
1830
|
+
description: options.description,
|
|
1831
|
+
inputSchema: options.inputSchema
|
|
1832
|
+
},
|
|
1833
|
+
(async (input) => options.handler(
|
|
1834
|
+
input
|
|
1835
|
+
))
|
|
1836
|
+
);
|
|
1837
|
+
}
|
|
1838
|
+
function createStorageInputSchema() {
|
|
1839
|
+
return {
|
|
1840
|
+
pageId: import_zod7.z.string().optional(),
|
|
1841
|
+
scope: import_zod7.z.enum(["localStorage", "sessionStorage", "indexedDB", "cookie"]).optional(),
|
|
1842
|
+
key: import_zod7.z.string().optional(),
|
|
1843
|
+
value: import_zod7.z.string().optional(),
|
|
1844
|
+
databaseName: import_zod7.z.string().optional(),
|
|
1845
|
+
objectStoreName: import_zod7.z.string().optional(),
|
|
1846
|
+
indexName: import_zod7.z.string().optional(),
|
|
1847
|
+
cookie: import_zod7.z.object({
|
|
1848
|
+
name: import_zod7.z.string(),
|
|
1849
|
+
value: import_zod7.z.string().optional(),
|
|
1850
|
+
domain: import_zod7.z.string().optional(),
|
|
1851
|
+
path: import_zod7.z.string().optional(),
|
|
1852
|
+
url: import_zod7.z.string().optional(),
|
|
1853
|
+
httpOnly: import_zod7.z.boolean().optional(),
|
|
1854
|
+
secure: import_zod7.z.boolean().optional(),
|
|
1855
|
+
sameSite: import_zod7.z.enum(["strict", "lax", "none"]).optional(),
|
|
1856
|
+
expires: import_zod7.z.number().optional()
|
|
1857
|
+
}).optional()
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
async function handleListStorage(ctx, pageId) {
|
|
1861
|
+
let baseRequest;
|
|
1862
|
+
try {
|
|
1863
|
+
baseRequest = createStorageRequest(ctx, {
|
|
1864
|
+
pageId,
|
|
1865
|
+
action: "list",
|
|
1866
|
+
scope: "localStorage"
|
|
1867
|
+
});
|
|
1868
|
+
} catch (error) {
|
|
1869
|
+
return createToolError(error instanceof Error ? error.message : String(error));
|
|
1870
|
+
}
|
|
1871
|
+
const [localStorage, sessionStorage, indexedDB] = await Promise.all([
|
|
1872
|
+
requestRuntimeStorage(ctx, { ...baseRequest, scope: "localStorage" }),
|
|
1873
|
+
requestRuntimeStorage(ctx, { ...baseRequest, scope: "sessionStorage" }),
|
|
1874
|
+
requestRuntimeStorage(ctx, { ...baseRequest, scope: "indexedDB" })
|
|
1875
|
+
]);
|
|
1876
|
+
const cookie = await listCookiesIfCdpAvailable(ctx, baseRequest, pageId);
|
|
1877
|
+
return createToolResponse({
|
|
1878
|
+
ok: true,
|
|
1879
|
+
origin: baseRequest.origin,
|
|
1880
|
+
localStorage: extractStorageData(localStorage),
|
|
1881
|
+
sessionStorage: extractStorageData(sessionStorage),
|
|
1882
|
+
indexedDB: extractStorageData(indexedDB),
|
|
1883
|
+
cookie
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
async function handleStorageAction(ctx, input) {
|
|
1887
|
+
let request;
|
|
1888
|
+
try {
|
|
1889
|
+
request = createStorageRequest(ctx, input);
|
|
1890
|
+
} catch (error) {
|
|
1891
|
+
return createToolError(error instanceof Error ? error.message : String(error));
|
|
1892
|
+
}
|
|
1893
|
+
const cdp = await connectCdpForPage(ctx, input.pageId);
|
|
1894
|
+
if (cdp && shouldUseCdpStorage(request)) {
|
|
1895
|
+
try {
|
|
1896
|
+
const result2 = await createCdpStorageAdapter(cdp.client).manageStorage(
|
|
1897
|
+
request
|
|
1898
|
+
);
|
|
1899
|
+
return createToolResponse(result2);
|
|
1900
|
+
} finally {
|
|
1901
|
+
await closeCdpClient(cdp.client);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
const result = await requestRuntimeStorage(ctx, request);
|
|
1905
|
+
return createToolResponse(result);
|
|
1906
|
+
}
|
|
1907
|
+
async function requestRuntimeStorage(ctx, request) {
|
|
1908
|
+
return requestRuntimeData(ctx, (event) => {
|
|
1909
|
+
void ctx.rpcServer?.manageStorage({
|
|
1910
|
+
...request,
|
|
1911
|
+
event
|
|
1912
|
+
});
|
|
1913
|
+
});
|
|
1914
|
+
}
|
|
1915
|
+
async function listCookiesIfCdpAvailable(ctx, request, pageId) {
|
|
1916
|
+
if (!hasCdpConfig2(ctx)) {
|
|
1917
|
+
return extractStorageData(
|
|
1918
|
+
await requestRuntimeStorage(ctx, { ...request, scope: "cookie" })
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
const cdp = await connectCdpForPage(ctx, pageId);
|
|
1922
|
+
if (!cdp) {
|
|
1923
|
+
return extractStorageData(
|
|
1924
|
+
await requestRuntimeStorage(ctx, { ...request, scope: "cookie" })
|
|
1925
|
+
);
|
|
1926
|
+
}
|
|
1927
|
+
try {
|
|
1928
|
+
const result = await createCdpStorageAdapter(cdp.client).manageStorage({
|
|
1929
|
+
...request,
|
|
1930
|
+
scope: "cookie"
|
|
1931
|
+
});
|
|
1932
|
+
return extractStorageData(result);
|
|
1933
|
+
} finally {
|
|
1934
|
+
await closeCdpClient(cdp.client);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
function extractStorageData(result) {
|
|
1938
|
+
if (!isStorageResultRecord(result)) {
|
|
1939
|
+
return result;
|
|
1940
|
+
}
|
|
1941
|
+
return result.data ?? result;
|
|
1942
|
+
}
|
|
1943
|
+
function isStorageResultRecord(value) {
|
|
1944
|
+
return typeof value === "object" && value !== null && "ok" in value;
|
|
1945
|
+
}
|
|
1946
|
+
function shouldUseCdpStorage(request) {
|
|
1947
|
+
return request.scope === "cookie";
|
|
1948
|
+
}
|
|
1949
|
+
function createStorageRequest(ctx, input) {
|
|
1950
|
+
const page = resolvePageTarget(ctx, input.pageId);
|
|
1951
|
+
const origin = new URL(page.url).origin;
|
|
1952
|
+
return {
|
|
1953
|
+
event: "",
|
|
1954
|
+
pageId: page.pageId,
|
|
1955
|
+
origin,
|
|
1956
|
+
action: input.action,
|
|
1957
|
+
scope: normalizeScope(input.scope),
|
|
1958
|
+
key: input.key,
|
|
1959
|
+
value: input.value,
|
|
1960
|
+
databaseName: input.databaseName,
|
|
1961
|
+
objectStoreName: input.objectStoreName,
|
|
1962
|
+
indexName: input.indexName,
|
|
1963
|
+
cookie: input.cookie
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
function normalizeScope(scope) {
|
|
1967
|
+
return scope ?? "localStorage";
|
|
1968
|
+
}
|
|
1969
|
+
function hasCdpConfig2(ctx) {
|
|
1970
|
+
return Boolean(ctx.options.cdp.browserUrl || ctx.options.cdp.wsEndpoint);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
// src/mcp/tools/screenshot.ts
|
|
1974
|
+
var import_zod8 = require("zod");
|
|
1975
|
+
|
|
1554
1976
|
// src/cdp/cdpScreenshot.ts
|
|
1555
1977
|
async function cdpCaptureScreenshot(options) {
|
|
1556
1978
|
if (options.target === "element") {
|
|
@@ -1709,14 +2131,14 @@ function createProjectRelativePath(ctx, filePath) {
|
|
|
1709
2131
|
var DEFAULT_SCREENSHOT_TARGET = "viewport";
|
|
1710
2132
|
var DEFAULT_SCREENSHOT_FORMAT = "png";
|
|
1711
2133
|
var screenshotInputSchema = {
|
|
1712
|
-
pageId:
|
|
1713
|
-
target:
|
|
1714
|
-
selector:
|
|
1715
|
-
format:
|
|
1716
|
-
prefer:
|
|
1717
|
-
quality:
|
|
1718
|
-
scale:
|
|
1719
|
-
snapdom:
|
|
2134
|
+
pageId: import_zod8.z.string().optional(),
|
|
2135
|
+
target: import_zod8.z.enum(["viewport", "fullPage", "element"]).optional(),
|
|
2136
|
+
selector: import_zod8.z.string().optional(),
|
|
2137
|
+
format: import_zod8.z.enum(["png", "jpeg", "webp"]).optional(),
|
|
2138
|
+
prefer: import_zod8.z.enum(["auto", "cdp", "runtime"]).optional(),
|
|
2139
|
+
quality: import_zod8.z.number().optional(),
|
|
2140
|
+
scale: import_zod8.z.number().optional(),
|
|
2141
|
+
snapdom: import_zod8.z.record(import_zod8.z.string(), import_zod8.z.unknown()).optional()
|
|
1720
2142
|
};
|
|
1721
2143
|
function registerScreenshotTools(server, ctx) {
|
|
1722
2144
|
server.registerTool(
|
|
@@ -1836,7 +2258,7 @@ function isScreenshotImagePayload(result) {
|
|
|
1836
2258
|
|
|
1837
2259
|
// src/mcp/tools/vue.ts
|
|
1838
2260
|
var import_nanoid3 = require("nanoid");
|
|
1839
|
-
var
|
|
2261
|
+
var import_zod9 = require("zod");
|
|
1840
2262
|
function registerVueTools(server, ctx) {
|
|
1841
2263
|
server.registerTool(
|
|
1842
2264
|
MCP_TOOL_NAMES.getComponentTree,
|
|
@@ -1849,7 +2271,7 @@ function registerVueTools(server, ctx) {
|
|
|
1849
2271
|
MCP_TOOL_NAMES.getComponentState,
|
|
1850
2272
|
{
|
|
1851
2273
|
description: "Get Vue component state.",
|
|
1852
|
-
inputSchema: { componentName:
|
|
2274
|
+
inputSchema: { componentName: import_zod9.z.string() }
|
|
1853
2275
|
},
|
|
1854
2276
|
async ({ componentName }) => requestVueData(ctx, (event) => {
|
|
1855
2277
|
void ctx.rpcServer?.getInspectorState({ event, componentName });
|
|
@@ -1860,10 +2282,10 @@ function registerVueTools(server, ctx) {
|
|
|
1860
2282
|
{
|
|
1861
2283
|
description: "Edit Vue component state.",
|
|
1862
2284
|
inputSchema: {
|
|
1863
|
-
componentName:
|
|
1864
|
-
path:
|
|
1865
|
-
value:
|
|
1866
|
-
valueType:
|
|
2285
|
+
componentName: import_zod9.z.string(),
|
|
2286
|
+
path: import_zod9.z.array(import_zod9.z.string()),
|
|
2287
|
+
value: import_zod9.z.string(),
|
|
2288
|
+
valueType: import_zod9.z.enum(["string", "number", "boolean", "object", "array"])
|
|
1867
2289
|
}
|
|
1868
2290
|
},
|
|
1869
2291
|
({ componentName, path: path8, value, valueType }) => {
|
|
@@ -1883,7 +2305,7 @@ function registerVueTools(server, ctx) {
|
|
|
1883
2305
|
MCP_TOOL_NAMES.highlightComponent,
|
|
1884
2306
|
{
|
|
1885
2307
|
description: "Highlight a Vue component.",
|
|
1886
|
-
inputSchema: { componentName:
|
|
2308
|
+
inputSchema: { componentName: import_zod9.z.string() }
|
|
1887
2309
|
},
|
|
1888
2310
|
({ componentName }) => {
|
|
1889
2311
|
if (!ctx.rpcServer) {
|
|
@@ -1911,7 +2333,7 @@ function registerVueTools(server, ctx) {
|
|
|
1911
2333
|
MCP_TOOL_NAMES.getPiniaState,
|
|
1912
2334
|
{
|
|
1913
2335
|
description: "Get Pinia store state.",
|
|
1914
|
-
inputSchema: { storeName:
|
|
2336
|
+
inputSchema: { storeName: import_zod9.z.string() }
|
|
1915
2337
|
},
|
|
1916
2338
|
async ({ storeName }) => requestVueData(ctx, (event) => {
|
|
1917
2339
|
void ctx.rpcServer?.getPiniaState({ event, storeName });
|
|
@@ -1958,6 +2380,7 @@ function createMcpServer(ctx, vite) {
|
|
|
1958
2380
|
registerConsoleTools(server, ctx);
|
|
1959
2381
|
registerEvaluateTools(server, ctx);
|
|
1960
2382
|
registerNetworkTools(server, ctx);
|
|
2383
|
+
registerStorageTools(server, ctx);
|
|
1961
2384
|
registerPerformanceTools(server, ctx);
|
|
1962
2385
|
registerVueTools(server, ctx);
|
|
1963
2386
|
return server;
|
|
@@ -2046,6 +2469,10 @@ function createServerVueRuntimeRpc(ctx) {
|
|
|
2046
2469
|
onScreenshotTaken: (event, data) => {
|
|
2047
2470
|
void ctx.hooks.callHook(event, data);
|
|
2048
2471
|
},
|
|
2472
|
+
manageStorage: () => void 0,
|
|
2473
|
+
onStorageUpdated: (event, data) => {
|
|
2474
|
+
void ctx.hooks.callHook(event, data);
|
|
2475
|
+
},
|
|
2049
2476
|
recordPerformance: () => void 0,
|
|
2050
2477
|
onPerformanceRecorded: (event, data) => {
|
|
2051
2478
|
void ctx.hooks.callHook(event, data);
|
|
@@ -2954,53 +3381,32 @@ function vueMcpNext(userOptions = {}) {
|
|
|
2954
3381
|
() => createMcpServer(ctx, server),
|
|
2955
3382
|
server
|
|
2956
3383
|
);
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
continue;
|
|
2965
|
-
}
|
|
2966
|
-
if (now - seenAt >= DEFAULT_RUNTIME_PAGE_HEARTBEAT_TIMEOUT_MS) {
|
|
2967
|
-
ctx.pages.disconnect(pageId, now);
|
|
2968
|
-
lastSeenAt.delete(pageId);
|
|
3384
|
+
server.ws.on(
|
|
3385
|
+
"vite-plugin-vue-mcp-next:page-connected",
|
|
3386
|
+
(payload) => {
|
|
3387
|
+
if (isRuntimePageTarget(payload)) {
|
|
3388
|
+
ctx.pages.upsert(payload);
|
|
3389
|
+
void ctx.hooks.callHook(RUNTIME_PAGE_RECONNECTED_EVENT, payload);
|
|
3390
|
+
void cdpLifecycle.connectPage(payload);
|
|
2969
3391
|
}
|
|
2970
3392
|
}
|
|
2971
|
-
|
|
2972
|
-
server.ws.on(
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
void cdpLifecycle.connectPage(payload);
|
|
2978
|
-
}
|
|
2979
|
-
});
|
|
2980
|
-
server.ws.on(RUNTIME_PAGE_HEARTBEAT_EVENT, (payload) => {
|
|
2981
|
-
if (isRuntimeHeartbeatTarget(payload)) {
|
|
2982
|
-
const target = ctx.pages.get(payload.pageId);
|
|
2983
|
-
if (target?.source === "runtime" && target.connected) {
|
|
2984
|
-
lastSeenAt.set(payload.pageId, payload.timestamp);
|
|
3393
|
+
);
|
|
3394
|
+
server.ws.on(
|
|
3395
|
+
"vite-plugin-vue-mcp-next:console-record",
|
|
3396
|
+
(payload) => {
|
|
3397
|
+
if (isConsoleRecord(payload)) {
|
|
3398
|
+
ctx.consoleRecords.push(payload);
|
|
2985
3399
|
}
|
|
2986
3400
|
}
|
|
2987
|
-
|
|
2988
|
-
server.ws.on(
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
server.ws.on("vite-plugin-vue-mcp-next:console-record", (payload) => {
|
|
2995
|
-
if (isConsoleRecord(payload)) {
|
|
2996
|
-
ctx.consoleRecords.push(payload);
|
|
2997
|
-
}
|
|
2998
|
-
});
|
|
2999
|
-
server.ws.on("vite-plugin-vue-mcp-next:network-record", (payload) => {
|
|
3000
|
-
if (isNetworkRecord(payload)) {
|
|
3001
|
-
ctx.networkRecords.push(payload);
|
|
3401
|
+
);
|
|
3402
|
+
server.ws.on(
|
|
3403
|
+
"vite-plugin-vue-mcp-next:network-record",
|
|
3404
|
+
(payload) => {
|
|
3405
|
+
if (isNetworkRecord(payload)) {
|
|
3406
|
+
ctx.networkRecords.push(payload);
|
|
3407
|
+
}
|
|
3002
3408
|
}
|
|
3003
|
-
|
|
3409
|
+
);
|
|
3004
3410
|
server.ws.on(
|
|
3005
3411
|
"vite-plugin-vue-mcp-next:performance-record",
|
|
3006
3412
|
(payload) => {
|
|
@@ -3030,7 +3436,6 @@ function vueMcpNext(userOptions = {}) {
|
|
|
3030
3436
|
}, 300);
|
|
3031
3437
|
}
|
|
3032
3438
|
server.httpServer?.once("close", () => {
|
|
3033
|
-
clearInterval(heartbeatTimer);
|
|
3034
3439
|
void cdpLifecycle.closeAll();
|
|
3035
3440
|
});
|
|
3036
3441
|
},
|
|
@@ -3055,20 +3460,6 @@ function isRuntimePageTarget(payload) {
|
|
|
3055
3460
|
const target = payload;
|
|
3056
3461
|
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");
|
|
3057
3462
|
}
|
|
3058
|
-
function isRuntimeHeartbeatTarget(payload) {
|
|
3059
|
-
if (!payload || typeof payload !== "object") {
|
|
3060
|
-
return false;
|
|
3061
|
-
}
|
|
3062
|
-
const target = payload;
|
|
3063
|
-
return typeof target.pageId === "string" && typeof target.timestamp === "number";
|
|
3064
|
-
}
|
|
3065
|
-
function isRuntimeDisconnectTarget(payload) {
|
|
3066
|
-
if (!payload || typeof payload !== "object") {
|
|
3067
|
-
return false;
|
|
3068
|
-
}
|
|
3069
|
-
const target = payload;
|
|
3070
|
-
return typeof target.pageId === "string";
|
|
3071
|
-
}
|
|
3072
3463
|
function isConsoleRecord(payload) {
|
|
3073
3464
|
if (!payload || typeof payload !== "object") {
|
|
3074
3465
|
return false;
|