@xiaou66/vite-plugin-vue-mcp-next 1.0.1 → 1.0.2
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 +40 -4
- package/dist/index.cjs +283 -66
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +283 -66
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/skills/vite-mcp-next/SKILL.md +71 -0
package/dist/index.d.cts
CHANGED
|
@@ -33,6 +33,8 @@ interface VueMcpNextOptions {
|
|
|
33
33
|
readonly updateCursorMcpJson?: boolean | CursorMcpConfig;
|
|
34
34
|
/** 是否写入常见 AI 客户端的项目级 MCP 配置;默认按项目已有客户端入口自动探测。 */
|
|
35
35
|
readonly mcpClients?: McpClientConfigOptions;
|
|
36
|
+
/** AI 使用指南自动安装配置,用于让 AI 客户端知道何时以及如何使用本 MCP。 */
|
|
37
|
+
readonly skill?: SkillConfigOptions;
|
|
36
38
|
/** 非 HTML 入口的运行时脚本注入点,用于兼容不直接使用 `index.html` 的项目。 */
|
|
37
39
|
readonly appendTo?: string | RegExp;
|
|
38
40
|
/** 通用 DevTools 能力配置;Vue 专属能力不受该配置影响,始终走 Vue Runtime Bridge。 */
|
|
@@ -77,6 +79,16 @@ interface McpClientConfigOptions {
|
|
|
77
79
|
/** MCP 客户端中展示的服务名,同一项目存在多个 MCP 服务时用于避免冲突。 */
|
|
78
80
|
readonly serverName?: string;
|
|
79
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* AI 使用指南自动安装配置。
|
|
84
|
+
*
|
|
85
|
+
* 该配置只影响项目级 skill/rule 文件写入,不影响 MCP 服务是否启动;
|
|
86
|
+
* 独立开关可以让不希望插件修改 AI 客户端上下文的项目完全关闭自动安装。
|
|
87
|
+
*/
|
|
88
|
+
interface SkillConfigOptions {
|
|
89
|
+
/** 是否在 Vite dev server 启动时自动安装 AI 使用指南,默认开启。 */
|
|
90
|
+
readonly autoConfig?: boolean;
|
|
91
|
+
}
|
|
80
92
|
/**
|
|
81
93
|
* 通用 DevTools 能力的运行模式。
|
|
82
94
|
*
|
|
@@ -320,6 +332,8 @@ interface ResolvedVueMcpNextOptions {
|
|
|
320
332
|
readonly updateCursorMcpJson: Required<CursorMcpConfig>;
|
|
321
333
|
/** 已规范化的多客户端 MCP 配置写入策略,内部写入器只读取该对象。 */
|
|
322
334
|
readonly mcpClients: Required<McpClientConfigOptions>;
|
|
335
|
+
/** 已补齐默认值的 AI 使用指南自动安装配置。 */
|
|
336
|
+
readonly skill: Required<SkillConfigOptions>;
|
|
323
337
|
/** 非 HTML 入口注入点,未配置时 HTML 注入路径生效。 */
|
|
324
338
|
readonly appendTo?: string | RegExp;
|
|
325
339
|
/** 已补齐默认值的通用 DevTools 配置。 */
|
|
@@ -569,4 +583,4 @@ interface VueRuntimeRpc {
|
|
|
569
583
|
*/
|
|
570
584
|
declare function vueMcpNext(userOptions?: VueMcpNextOptions): Plugin;
|
|
571
585
|
|
|
572
|
-
export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
|
|
586
|
+
export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type SkillConfigOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ interface VueMcpNextOptions {
|
|
|
33
33
|
readonly updateCursorMcpJson?: boolean | CursorMcpConfig;
|
|
34
34
|
/** 是否写入常见 AI 客户端的项目级 MCP 配置;默认按项目已有客户端入口自动探测。 */
|
|
35
35
|
readonly mcpClients?: McpClientConfigOptions;
|
|
36
|
+
/** AI 使用指南自动安装配置,用于让 AI 客户端知道何时以及如何使用本 MCP。 */
|
|
37
|
+
readonly skill?: SkillConfigOptions;
|
|
36
38
|
/** 非 HTML 入口的运行时脚本注入点,用于兼容不直接使用 `index.html` 的项目。 */
|
|
37
39
|
readonly appendTo?: string | RegExp;
|
|
38
40
|
/** 通用 DevTools 能力配置;Vue 专属能力不受该配置影响,始终走 Vue Runtime Bridge。 */
|
|
@@ -77,6 +79,16 @@ interface McpClientConfigOptions {
|
|
|
77
79
|
/** MCP 客户端中展示的服务名,同一项目存在多个 MCP 服务时用于避免冲突。 */
|
|
78
80
|
readonly serverName?: string;
|
|
79
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* AI 使用指南自动安装配置。
|
|
84
|
+
*
|
|
85
|
+
* 该配置只影响项目级 skill/rule 文件写入,不影响 MCP 服务是否启动;
|
|
86
|
+
* 独立开关可以让不希望插件修改 AI 客户端上下文的项目完全关闭自动安装。
|
|
87
|
+
*/
|
|
88
|
+
interface SkillConfigOptions {
|
|
89
|
+
/** 是否在 Vite dev server 启动时自动安装 AI 使用指南,默认开启。 */
|
|
90
|
+
readonly autoConfig?: boolean;
|
|
91
|
+
}
|
|
80
92
|
/**
|
|
81
93
|
* 通用 DevTools 能力的运行模式。
|
|
82
94
|
*
|
|
@@ -320,6 +332,8 @@ interface ResolvedVueMcpNextOptions {
|
|
|
320
332
|
readonly updateCursorMcpJson: Required<CursorMcpConfig>;
|
|
321
333
|
/** 已规范化的多客户端 MCP 配置写入策略,内部写入器只读取该对象。 */
|
|
322
334
|
readonly mcpClients: Required<McpClientConfigOptions>;
|
|
335
|
+
/** 已补齐默认值的 AI 使用指南自动安装配置。 */
|
|
336
|
+
readonly skill: Required<SkillConfigOptions>;
|
|
323
337
|
/** 非 HTML 入口注入点,未配置时 HTML 注入路径生效。 */
|
|
324
338
|
readonly appendTo?: string | RegExp;
|
|
325
339
|
/** 已补齐默认值的通用 DevTools 配置。 */
|
|
@@ -569,4 +583,4 @@ interface VueRuntimeRpc {
|
|
|
569
583
|
*/
|
|
570
584
|
declare function vueMcpNext(userOptions?: VueMcpNextOptions): Plugin;
|
|
571
585
|
|
|
572
|
-
export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
|
|
586
|
+
export { type CdpOptions, type ConsoleOptions, type ConsoleRecord, type CursorMcpConfig, type DomOptions, type EvaluateOptions, type McpClientConfigOptions, type NetworkOptions, type NetworkRecord, type PageTarget, type ResolvedVueMcpNextOptions, type RuntimeMode, type RuntimeOptions, type SkillConfigOptions, type VueMcpNextContext, type VueMcpNextOptions, vueMcpNext as default, vueMcpNext };
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,8 @@ var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-
|
|
|
44
44
|
var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
|
|
45
45
|
var VIRTUAL_SNAPDOM_LOADER_ID = "virtual:vite-plugin-vue-mcp-next/snapdom-loader";
|
|
46
46
|
var RESOLVED_VIRTUAL_SNAPDOM_LOADER_ID = `\0${VIRTUAL_SNAPDOM_LOADER_ID}`;
|
|
47
|
-
var DEFAULT_MCP_CLIENT_SERVER_NAME = "
|
|
47
|
+
var DEFAULT_MCP_CLIENT_SERVER_NAME = "vite-mcp-next";
|
|
48
|
+
var LEGACY_MCP_CLIENT_SERVER_NAMES = ["vue-mcp-next"];
|
|
48
49
|
var RUNTIME_PAGE_RECONNECTED_EVENT = "vite-plugin-vue-mcp-next:page-reconnected";
|
|
49
50
|
var DEFAULT_OPTIONS = {
|
|
50
51
|
mcpPath: DEFAULT_MCP_PATH,
|
|
@@ -61,6 +62,9 @@ var DEFAULT_OPTIONS = {
|
|
|
61
62
|
trae: true,
|
|
62
63
|
serverName: DEFAULT_MCP_CLIENT_SERVER_NAME
|
|
63
64
|
},
|
|
65
|
+
skill: {
|
|
66
|
+
autoConfig: true
|
|
67
|
+
},
|
|
64
68
|
runtime: {
|
|
65
69
|
mode: "auto",
|
|
66
70
|
evaluate: {
|
|
@@ -118,6 +122,10 @@ function mergeOptions(options = {}) {
|
|
|
118
122
|
...options,
|
|
119
123
|
updateCursorMcpJson: cursorConfig,
|
|
120
124
|
mcpClients,
|
|
125
|
+
skill: {
|
|
126
|
+
...DEFAULT_OPTIONS.skill,
|
|
127
|
+
...options.skill
|
|
128
|
+
},
|
|
121
129
|
runtime: {
|
|
122
130
|
...DEFAULT_OPTIONS.runtime,
|
|
123
131
|
...options.runtime,
|
|
@@ -1146,13 +1154,13 @@ function registerVueTools(server, ctx) {
|
|
|
1146
1154
|
valueType: z7.enum(["string", "number", "boolean", "object", "array"])
|
|
1147
1155
|
}
|
|
1148
1156
|
},
|
|
1149
|
-
({ componentName, path:
|
|
1157
|
+
({ componentName, path: path8, value, valueType }) => {
|
|
1150
1158
|
if (!ctx.rpcServer) {
|
|
1151
1159
|
return vueBridgeUnavailable();
|
|
1152
1160
|
}
|
|
1153
1161
|
void ctx.rpcServer.editComponentState({
|
|
1154
1162
|
componentName,
|
|
1155
|
-
path:
|
|
1163
|
+
path: path8,
|
|
1156
1164
|
value,
|
|
1157
1165
|
valueType
|
|
1158
1166
|
});
|
|
@@ -1754,10 +1762,18 @@ async function updateCodexMcpClientConfig(options) {
|
|
|
1754
1762
|
}
|
|
1755
1763
|
function replaceOrAppendOwnedBlock(current, options) {
|
|
1756
1764
|
const block = createCodexServerBlock(options);
|
|
1757
|
-
const matcher =
|
|
1765
|
+
const matcher = createServerTableMatcher(options.serverName);
|
|
1758
1766
|
if (matcher.test(current)) {
|
|
1759
1767
|
return ensureTrailingNewline(current);
|
|
1760
1768
|
}
|
|
1769
|
+
const legacyServerName = options.legacyServerNames?.find(
|
|
1770
|
+
(serverName) => createServerTableMatcher(serverName).test(current)
|
|
1771
|
+
);
|
|
1772
|
+
if (legacyServerName) {
|
|
1773
|
+
return ensureTrailingNewline(
|
|
1774
|
+
renameServerTableHeaders(current, legacyServerName, options.serverName)
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1761
1777
|
const separator = current.trim() ? "\n\n" : "";
|
|
1762
1778
|
return `${trimEndNewline(current)}${separator}${block}`;
|
|
1763
1779
|
}
|
|
@@ -1766,25 +1782,47 @@ function createCodexServerBlock(options) {
|
|
|
1766
1782
|
url = ${quoteTomlString(options.mcpUrl)}
|
|
1767
1783
|
`;
|
|
1768
1784
|
}
|
|
1769
|
-
function
|
|
1785
|
+
function createServerTableMatcher(serverName) {
|
|
1770
1786
|
const plainHeader = escapeRegExp(`[mcp_servers.${serverName}]`);
|
|
1787
|
+
const plainChildHeader = escapeRegExp(`[mcp_servers.${serverName}.`);
|
|
1771
1788
|
const quotedHeader = escapeRegExp(
|
|
1772
1789
|
`[mcp_servers.${quoteTomlKey(serverName)}]`
|
|
1773
1790
|
);
|
|
1791
|
+
const quotedChildHeader = escapeRegExp(
|
|
1792
|
+
`[mcp_servers.${quoteTomlKey(serverName)}.`
|
|
1793
|
+
);
|
|
1774
1794
|
return new RegExp(
|
|
1775
|
-
`(?:^|\\n)(?:${plainHeader}|${quotedHeader}
|
|
1795
|
+
`(?:^|\\n)(?:${plainHeader}|${plainChildHeader}|${quotedHeader}|${quotedChildHeader})`
|
|
1776
1796
|
);
|
|
1777
1797
|
}
|
|
1778
1798
|
function createTableHeader(serverName) {
|
|
1779
|
-
const key =
|
|
1799
|
+
const key = createTableKey(serverName);
|
|
1780
1800
|
return `[mcp_servers.${key}]`;
|
|
1781
1801
|
}
|
|
1802
|
+
function createTableKey(serverName) {
|
|
1803
|
+
return /^[A-Za-z0-9_-]+$/.test(serverName) ? serverName : quoteTomlKey(serverName);
|
|
1804
|
+
}
|
|
1782
1805
|
function quoteTomlKey(value) {
|
|
1783
1806
|
return quoteTomlString(value);
|
|
1784
1807
|
}
|
|
1785
1808
|
function quoteTomlString(value) {
|
|
1786
1809
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
1787
1810
|
}
|
|
1811
|
+
function renameServerTableHeaders(current, fromServerName, toServerName) {
|
|
1812
|
+
return current.split("\n").map((line) => renameServerTableHeader(line, fromServerName, toServerName)).join("\n");
|
|
1813
|
+
}
|
|
1814
|
+
function renameServerTableHeader(line, fromServerName, toServerName) {
|
|
1815
|
+
const toKey = createTableKey(toServerName);
|
|
1816
|
+
const fromKeys = [fromServerName, quoteTomlKey(fromServerName)];
|
|
1817
|
+
for (const fromKey of fromKeys) {
|
|
1818
|
+
const prefix = `[mcp_servers.${fromKey}`;
|
|
1819
|
+
const nextChar = line[prefix.length];
|
|
1820
|
+
if (line.startsWith(prefix) && (nextChar === "]" || nextChar === ".")) {
|
|
1821
|
+
return `[mcp_servers.${toKey}${line.slice(prefix.length)}`;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
return line;
|
|
1825
|
+
}
|
|
1788
1826
|
async function readOptionalTextFile(filePath) {
|
|
1789
1827
|
try {
|
|
1790
1828
|
return await fs2.readFile(filePath, "utf-8");
|
|
@@ -1826,18 +1864,38 @@ async function updateJsonMcpClientConfig(options) {
|
|
|
1826
1864
|
if (Object.hasOwn(mcpServers, options.serverName)) {
|
|
1827
1865
|
return;
|
|
1828
1866
|
}
|
|
1867
|
+
const migratedMcpServers = renameLegacyServer(mcpServers, options);
|
|
1868
|
+
if (migratedMcpServers) {
|
|
1869
|
+
config.mcpServers = migratedMcpServers;
|
|
1870
|
+
await writeJsonConfig(options.configPath, config);
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1829
1873
|
mcpServers[options.serverName] = { type: "sse", url: options.mcpUrl };
|
|
1830
1874
|
config.mcpServers = mcpServers;
|
|
1831
|
-
await
|
|
1832
|
-
await fs3.writeFile(
|
|
1833
|
-
options.configPath,
|
|
1834
|
-
`${JSON.stringify(config, null, 2)}
|
|
1835
|
-
`
|
|
1836
|
-
);
|
|
1875
|
+
await writeJsonConfig(options.configPath, config);
|
|
1837
1876
|
} catch (error) {
|
|
1838
1877
|
warnConfigFailure(options, formatError2(error));
|
|
1839
1878
|
}
|
|
1840
1879
|
}
|
|
1880
|
+
function renameLegacyServer(mcpServers, options) {
|
|
1881
|
+
const legacyServerName = options.legacyServerNames?.find(
|
|
1882
|
+
(name) => Object.hasOwn(mcpServers, name)
|
|
1883
|
+
);
|
|
1884
|
+
if (!legacyServerName) {
|
|
1885
|
+
return void 0;
|
|
1886
|
+
}
|
|
1887
|
+
return Object.fromEntries(
|
|
1888
|
+
Object.entries(mcpServers).map(([serverName, serverConfig]) => [
|
|
1889
|
+
serverName === legacyServerName ? options.serverName : serverName,
|
|
1890
|
+
serverConfig
|
|
1891
|
+
])
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
async function writeJsonConfig(configPath, config) {
|
|
1895
|
+
await fs3.mkdir(path4.dirname(configPath), { recursive: true });
|
|
1896
|
+
await fs3.writeFile(configPath, `${JSON.stringify(config, null, 2)}
|
|
1897
|
+
`);
|
|
1898
|
+
}
|
|
1841
1899
|
async function readJsonConfig(configPath) {
|
|
1842
1900
|
const raw = await readOptionalTextFile2(configPath);
|
|
1843
1901
|
if (!raw.trim()) {
|
|
@@ -1873,76 +1931,83 @@ function isNodeError2(error) {
|
|
|
1873
1931
|
// src/plugin/mcpClientConfig/index.ts
|
|
1874
1932
|
async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options, userOptions = {}) {
|
|
1875
1933
|
const serverName = options.serverName;
|
|
1876
|
-
const
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
updateJsonMcpClientConfig({
|
|
1934
|
+
const legacyServerNames = getLegacyServerNames(serverName);
|
|
1935
|
+
const descriptors = [
|
|
1936
|
+
{
|
|
1937
|
+
root,
|
|
1938
|
+
clientName: "cursor",
|
|
1939
|
+
enabled: options.cursor,
|
|
1940
|
+
entryPath: path5.join(root, ".cursor"),
|
|
1941
|
+
entryKind: "directory",
|
|
1942
|
+
userOptions,
|
|
1943
|
+
createJob: () => updateJsonMcpClientConfig({
|
|
1887
1944
|
clientName: "Cursor",
|
|
1888
1945
|
configPath: path5.join(root, ".cursor", "mcp.json"),
|
|
1889
1946
|
mcpUrl: sseUrl,
|
|
1890
|
-
serverName
|
|
1947
|
+
serverName,
|
|
1948
|
+
legacyServerNames
|
|
1891
1949
|
})
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
})) {
|
|
1902
|
-
jobs.push(
|
|
1903
|
-
updateCodexMcpClientConfig({
|
|
1950
|
+
},
|
|
1951
|
+
{
|
|
1952
|
+
root,
|
|
1953
|
+
clientName: "codex",
|
|
1954
|
+
enabled: options.codex,
|
|
1955
|
+
entryPath: path5.join(root, ".codex"),
|
|
1956
|
+
entryKind: "directory",
|
|
1957
|
+
userOptions,
|
|
1958
|
+
createJob: () => updateCodexMcpClientConfig({
|
|
1904
1959
|
configPath: path5.join(root, ".codex", "config.toml"),
|
|
1905
1960
|
mcpUrl: streamableHttpUrl,
|
|
1906
|
-
serverName
|
|
1961
|
+
serverName,
|
|
1962
|
+
legacyServerNames
|
|
1907
1963
|
})
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
})) {
|
|
1918
|
-
jobs.push(
|
|
1919
|
-
updateJsonMcpClientConfig({
|
|
1964
|
+
},
|
|
1965
|
+
{
|
|
1966
|
+
root,
|
|
1967
|
+
clientName: "claudeCode",
|
|
1968
|
+
enabled: options.claudeCode,
|
|
1969
|
+
entryPath: path5.join(root, ".mcp.json"),
|
|
1970
|
+
entryKind: "file",
|
|
1971
|
+
userOptions,
|
|
1972
|
+
createJob: () => updateJsonMcpClientConfig({
|
|
1920
1973
|
clientName: "Claude Code",
|
|
1921
1974
|
configPath: path5.join(root, ".mcp.json"),
|
|
1922
1975
|
mcpUrl: sseUrl,
|
|
1923
|
-
serverName
|
|
1976
|
+
serverName,
|
|
1977
|
+
legacyServerNames
|
|
1924
1978
|
})
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
})) {
|
|
1935
|
-
jobs.push(
|
|
1936
|
-
updateJsonMcpClientConfig({
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
root,
|
|
1982
|
+
clientName: "trae",
|
|
1983
|
+
enabled: options.trae,
|
|
1984
|
+
entryPath: path5.join(root, ".trae"),
|
|
1985
|
+
entryKind: "directory",
|
|
1986
|
+
userOptions,
|
|
1987
|
+
createJob: () => updateJsonMcpClientConfig({
|
|
1937
1988
|
clientName: "Trae",
|
|
1938
1989
|
configPath: path5.join(root, ".trae", "mcp.json"),
|
|
1939
1990
|
mcpUrl: sseUrl,
|
|
1940
|
-
serverName
|
|
1991
|
+
serverName,
|
|
1992
|
+
legacyServerNames
|
|
1941
1993
|
})
|
|
1942
|
-
|
|
1943
|
-
|
|
1994
|
+
}
|
|
1995
|
+
];
|
|
1996
|
+
const jobs = await createClientConfigJobs(descriptors);
|
|
1944
1997
|
await Promise.all(jobs);
|
|
1945
1998
|
}
|
|
1999
|
+
async function createClientConfigJobs(descriptors) {
|
|
2000
|
+
const jobs = [];
|
|
2001
|
+
for (const descriptor of descriptors) {
|
|
2002
|
+
if (await shouldUpdateClientConfig(descriptor)) {
|
|
2003
|
+
jobs.push(descriptor.createJob());
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
return jobs;
|
|
2007
|
+
}
|
|
2008
|
+
function getLegacyServerNames(serverName) {
|
|
2009
|
+
return serverName === DEFAULT_MCP_CLIENT_SERVER_NAME ? LEGACY_MCP_CLIENT_SERVER_NAMES : [];
|
|
2010
|
+
}
|
|
1946
2011
|
async function shouldUpdateClientConfig(options) {
|
|
1947
2012
|
if (!options.enabled) {
|
|
1948
2013
|
return false;
|
|
@@ -1979,6 +2044,157 @@ function isNodeError3(error) {
|
|
|
1979
2044
|
return error instanceof Error && "code" in error;
|
|
1980
2045
|
}
|
|
1981
2046
|
|
|
2047
|
+
// src/plugin/skillConfig/index.ts
|
|
2048
|
+
import fs6 from "fs/promises";
|
|
2049
|
+
import path7 from "path";
|
|
2050
|
+
|
|
2051
|
+
// src/plugin/skillConfig/writers.ts
|
|
2052
|
+
import fs5 from "fs/promises";
|
|
2053
|
+
import path6 from "path";
|
|
2054
|
+
var GENERATED_SKILL_CONFIG_MARKER = "<!-- Generated by vite-plugin-vue-mcp-next. Safe to edit, but automatic updates only apply while this marker remains. -->";
|
|
2055
|
+
async function writeGeneratedTextFile(options) {
|
|
2056
|
+
try {
|
|
2057
|
+
const current = await readOptionalTextFile3(options.filePath);
|
|
2058
|
+
if (current && !current.includes(GENERATED_SKILL_CONFIG_MARKER)) {
|
|
2059
|
+
console.warn(
|
|
2060
|
+
`[vite-plugin-vue-mcp-next] Skipped ${options.targetName} at ${options.filePath}: file is not generated by this plugin`
|
|
2061
|
+
);
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
await fs5.mkdir(path6.dirname(options.filePath), { recursive: true });
|
|
2065
|
+
await fs5.writeFile(options.filePath, options.content);
|
|
2066
|
+
} catch (error) {
|
|
2067
|
+
console.warn(
|
|
2068
|
+
`[vite-plugin-vue-mcp-next] Failed to update ${options.targetName} at ${options.filePath}: ${formatError4(error)}`
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
async function readOptionalTextFile3(filePath) {
|
|
2073
|
+
try {
|
|
2074
|
+
return await fs5.readFile(filePath, "utf-8");
|
|
2075
|
+
} catch (error) {
|
|
2076
|
+
if (isNodeError4(error) && error.code === "ENOENT") {
|
|
2077
|
+
return "";
|
|
2078
|
+
}
|
|
2079
|
+
throw error;
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
function formatError4(error) {
|
|
2083
|
+
return error instanceof Error ? error.message : String(error);
|
|
2084
|
+
}
|
|
2085
|
+
function isNodeError4(error) {
|
|
2086
|
+
return error instanceof Error && "code" in error;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
// src/plugin/skillConfig/index.ts
|
|
2090
|
+
var PACKAGE_NAME = "@xiaou66/vite-plugin-vue-mcp-next";
|
|
2091
|
+
var PACKAGED_SKILL_PATH = path7.join("skills", "vite-mcp-next", "SKILL.md");
|
|
2092
|
+
async function updateSkillConfigs(root, options) {
|
|
2093
|
+
if (!options.autoConfig) {
|
|
2094
|
+
return;
|
|
2095
|
+
}
|
|
2096
|
+
const descriptors = createSkillConfigDescriptors(root);
|
|
2097
|
+
const skillContent = await safelyReadPackagedSkillContent(root);
|
|
2098
|
+
if (!skillContent) {
|
|
2099
|
+
return;
|
|
2100
|
+
}
|
|
2101
|
+
const jobs = await createSkillConfigJobs(descriptors, skillContent);
|
|
2102
|
+
await Promise.all(jobs);
|
|
2103
|
+
}
|
|
2104
|
+
function createSkillConfigDescriptors(root) {
|
|
2105
|
+
return [
|
|
2106
|
+
{
|
|
2107
|
+
entryPath: path7.join(root, ".codex"),
|
|
2108
|
+
filePath: path7.join(root, ".codex", "skills", "vite-mcp-next", "SKILL.md"),
|
|
2109
|
+
targetName: "Codex skill"
|
|
2110
|
+
},
|
|
2111
|
+
{
|
|
2112
|
+
entryPath: path7.join(root, ".claude"),
|
|
2113
|
+
filePath: path7.join(
|
|
2114
|
+
root,
|
|
2115
|
+
".claude",
|
|
2116
|
+
"skills",
|
|
2117
|
+
"vite-mcp-next",
|
|
2118
|
+
"SKILL.md"
|
|
2119
|
+
),
|
|
2120
|
+
targetName: "Claude Code skill"
|
|
2121
|
+
},
|
|
2122
|
+
{
|
|
2123
|
+
entryPath: path7.join(root, ".cursor"),
|
|
2124
|
+
filePath: path7.join(root, ".cursor", "rules", "vite-mcp-next.mdc"),
|
|
2125
|
+
targetName: "Cursor rule"
|
|
2126
|
+
}
|
|
2127
|
+
];
|
|
2128
|
+
}
|
|
2129
|
+
async function createSkillConfigJobs(descriptors, content) {
|
|
2130
|
+
const jobs = [];
|
|
2131
|
+
for (const descriptor of descriptors) {
|
|
2132
|
+
if (await hasDirectoryEntry(descriptor.entryPath)) {
|
|
2133
|
+
jobs.push(
|
|
2134
|
+
writeGeneratedTextFile({
|
|
2135
|
+
filePath: descriptor.filePath,
|
|
2136
|
+
content,
|
|
2137
|
+
targetName: descriptor.targetName
|
|
2138
|
+
})
|
|
2139
|
+
);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
return jobs;
|
|
2143
|
+
}
|
|
2144
|
+
async function readPackagedSkillContent(root) {
|
|
2145
|
+
const candidates = getPackagedSkillCandidates(root);
|
|
2146
|
+
for (const candidate of candidates) {
|
|
2147
|
+
try {
|
|
2148
|
+
return await fs6.readFile(candidate, "utf-8");
|
|
2149
|
+
} catch (error) {
|
|
2150
|
+
if (isNodeError5(error) && error.code === "ENOENT") {
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
throw error;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
throw new Error(
|
|
2157
|
+
`Cannot find packaged vite-mcp-next skill at ${candidates.join(", ")}`
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
2160
|
+
async function safelyReadPackagedSkillContent(root) {
|
|
2161
|
+
try {
|
|
2162
|
+
return await readPackagedSkillContent(root);
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
console.warn(
|
|
2165
|
+
`[vite-plugin-vue-mcp-next] Failed to read packaged AI skill: ${formatError5(error)}`
|
|
2166
|
+
);
|
|
2167
|
+
return void 0;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
function getPackagedSkillCandidates(root) {
|
|
2171
|
+
return [
|
|
2172
|
+
path7.resolve(root, "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
|
|
2173
|
+
path7.resolve(process.cwd(), "node_modules", PACKAGE_NAME, PACKAGED_SKILL_PATH),
|
|
2174
|
+
path7.resolve(process.cwd(), PACKAGED_SKILL_PATH)
|
|
2175
|
+
];
|
|
2176
|
+
}
|
|
2177
|
+
async function hasDirectoryEntry(entryPath) {
|
|
2178
|
+
try {
|
|
2179
|
+
const stat = await fs6.stat(entryPath);
|
|
2180
|
+
return stat.isDirectory();
|
|
2181
|
+
} catch (error) {
|
|
2182
|
+
if (isNodeError5(error) && error.code === "ENOENT") {
|
|
2183
|
+
return false;
|
|
2184
|
+
}
|
|
2185
|
+
console.warn(
|
|
2186
|
+
`[vite-plugin-vue-mcp-next] Failed to inspect skill config entry at ${entryPath}: ${formatError5(error)}`
|
|
2187
|
+
);
|
|
2188
|
+
return false;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
function formatError5(error) {
|
|
2192
|
+
return error instanceof Error ? error.message : String(error);
|
|
2193
|
+
}
|
|
2194
|
+
function isNodeError5(error) {
|
|
2195
|
+
return error instanceof Error && "code" in error;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
1982
2198
|
// src/plugin/createPlugin.ts
|
|
1983
2199
|
import { createRPCServer } from "vite-dev-rpc";
|
|
1984
2200
|
function vueMcpNext(userOptions = {}) {
|
|
@@ -2050,6 +2266,7 @@ function vueMcpNext(userOptions = {}) {
|
|
|
2050
2266
|
options.mcpClients,
|
|
2051
2267
|
userOptions
|
|
2052
2268
|
);
|
|
2269
|
+
await updateSkillConfigs(root, options.skill);
|
|
2053
2270
|
if (options.printUrl) {
|
|
2054
2271
|
setTimeout(() => {
|
|
2055
2272
|
console.log(` \u279C MCP: SSE server is running at ${mcpSseUrl}`);
|