vite-plugin-aipanel 1.2.23 → 1.2.24

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.
@@ -5,9 +5,17 @@
5
5
  * (schema/描述运行时同步自官方 tools/list,见 official-tools.ts)+ 本项目自定义工具。
6
6
  * 工具层限制(白名单、必填 pageId、仅项目页)由本文件 + endpoints/mcp.ts 的 tools/call 共同执行。
7
7
  */
8
+ import { type VueDevtoolsAction } from "@aipanel/core";
8
9
  /** 模型可见名前缀:官方短名 → chrome-devtools_<name> */
9
10
  export declare const MCP_PREFIX = "chrome-devtools_";
10
11
  export declare function displayToolName(short: string): string;
12
+ /** 本地桥接工具族前缀(tools/list 暴露面与 tools/call 路由共用) */
13
+ export declare const VUE_DEVTOOLS_PREFIX = "vue-devtools_";
14
+ export declare const LOGS_DEVTOOLS_PREFIX = "logs-devtools_";
15
+ /** Vite 进程日志工具名(工具定义与调用路由共用) */
16
+ export declare const VITE_LOGS_TOOL_NAME = "logs-devtools_vite_logs";
17
+ /** 服务日志文件工具名(按 LogFileConfig.name 生成,暴露与路由共用) */
18
+ export declare function serviceLogToolName(name: string): string;
11
19
  export interface CustomTool {
12
20
  name: string;
13
21
  description: string;
@@ -44,6 +52,20 @@ export declare function withPageIdSchema(schema: CustomTool["inputSchema"]): Cus
44
52
  export declare const PROJECT_DESCRIPTION_NOTES: Record<string, string>;
45
53
  /** 自定义工具(官方无对应,工具层本地实现) */
46
54
  export declare const CUSTOM_TOOLS: CustomTool[];
55
+ /**
56
+ * 页面桥接工具:描述与输入 schema(模型可见)+ 后端 action(调用分发)单一来源。
57
+ * tools/list 与 tools/call 都取自本定义,避免"清单"与"路由"两份来源各自漂移。
58
+ * 只在注入 Vue DevTools 桥的项目(Vue 项目)暴露。
59
+ */
60
+ export interface VueDevtoolsTool extends CustomTool {
61
+ /** 页面桥后端 action(endpoints/vue-devtools.ts executeAction) */
62
+ action: VueDevtoolsAction;
63
+ }
64
+ export declare const VUE_DEVTOOLS_TOOLS: readonly VueDevtoolsTool[];
65
+ /** vue-devtools_* 工具名 → 定义(未知名称返回 undefined) */
66
+ export declare function findVueDevtoolsTool(name: string): VueDevtoolsTool | undefined;
67
+ /** vue-devtools_* 的 tools/list 暴露面(剥掉仅用于路由分发的 action 字段) */
68
+ export declare function vueDevtoolsToolList(): CustomTool[];
47
69
  /** 官方声明了条件、且属于可开启范围的工具(由官方元数据派生,无手写) */
48
70
  export declare function isOfficialExtraTool(short: string): boolean;
49
71
  /** 由官方 conditions 推导所需 flag(experimentalX → --experimental-x,与官方 CLI 一致) */
@@ -1,8 +1,15 @@
1
+ import { VUE_DEVTOOLS_ACTIONS } from "@aipanel/core";
1
2
  import { OFFICIAL_TOOL_META } from "./official-meta.mjs";
2
3
  const MCP_PREFIX = "chrome-devtools_";
3
4
  function displayToolName(short) {
4
5
  return MCP_PREFIX + short;
5
6
  }
7
+ const VUE_DEVTOOLS_PREFIX = "vue-devtools_";
8
+ const LOGS_DEVTOOLS_PREFIX = "logs-devtools_";
9
+ const VITE_LOGS_TOOL_NAME = `${LOGS_DEVTOOLS_PREFIX}vite_logs`;
10
+ function serviceLogToolName(name) {
11
+ return `${LOGS_DEVTOOLS_PREFIX}${name}_logs`;
12
+ }
6
13
  const OFFICIAL_GLOBAL_POLICY = [
7
14
  "evaluate_script",
8
15
  "list_console_messages",
@@ -33,8 +40,153 @@ const CUSTOM_TOOLS = [
33
40
  name: displayToolName("current_page"),
34
41
  description: "Get the page the user is currently browsing (URL, title, and page ID). Resolves the project page with injected context; for allowOrigins pages use list_pages and pass its pageId to page tools instead.",
35
42
  inputSchema: { type: "object", properties: {} }
43
+ },
44
+ {
45
+ name: VITE_LOGS_TOOL_NAME,
46
+ description: `\u83B7\u53D6 Vite \u5F00\u53D1\u670D\u52A1\u5668\u7684\u8FD0\u884C\u65E5\u5FD7\u3002
47
+
48
+ **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
49
+ - \u7528\u6237\u62A5\u544A"\u9875\u9762\u6CA1\u66F4\u65B0"\u3001"\u70ED\u66F4\u65B0\u4E0D\u5DE5\u4F5C"\u3001"HMR \u5931\u6548"\u65F6
50
+ - \u6784\u5EFA\u62A5\u9519\u6216\u7F16\u8BD1\u5931\u8D25\uFF0C\u9700\u8981\u67E5\u770B\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F
51
+ - \u9875\u9762\u767D\u5C4F\u3001\u6837\u5F0F\u4E22\u5931\u3001\u6A21\u5757\u52A0\u8F7D\u5931\u8D25\u7B49\u5F00\u53D1\u95EE\u9898
52
+ - \u7528\u6237\u63D0\u5230"\u5F00\u53D1\u670D\u52A1\u5668\u6709\u95EE\u9898"\u3001"vite \u62A5\u9519"
53
+ - \u9700\u8981\u786E\u8BA4\u6700\u8FD1\u7684\u6587\u4EF6\u53D8\u66F4\u662F\u5426\u88AB Vite \u6B63\u786E\u5904\u7406
54
+
55
+ **\u65E5\u5FD7\u5185\u5BB9**\uFF1A
56
+ - Vite HMR \u70ED\u66F4\u65B0\u65E5\u5FD7\uFF08\u54EA\u4E9B\u6587\u4EF6\u88AB\u66F4\u65B0\u3001\u66F4\u65B0\u72B6\u6001\uFF09
57
+ - \u6784\u5EFA\u7F16\u8BD1\u65E5\u5FD7\uFF08\u9519\u8BEF\u3001\u8B66\u544A\u3001\u6210\u529F\u4FE1\u606F\uFF09
58
+ - OpenCode Web \u8FDB\u7A0B\u8F93\u51FA
59
+ - \u63D2\u4EF6\u8FD0\u884C\u65E5\u5FD7
60
+
61
+ \u65E5\u5FD7\u4FDD\u5B58\u5728\u5185\u5B58\u7F13\u51B2\u533A\uFF08\u6700\u8FD1 500 \u6761\uFF09\u3002`,
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: {
65
+ level: {
66
+ type: "string",
67
+ description: "\u65E5\u5FD7\u7EA7\u522B\u8FC7\u6EE4\uFF1Aerror(\u9519\u8BEF)\u3001warn(\u8B66\u544A)\u3001info(\u4FE1\u606F)\u3001debug(\u8C03\u8BD5)\u3001log(\u666E\u901A)\u3002\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF0C\u5982 'error,warn'"
68
+ },
69
+ limit: {
70
+ type: "integer",
71
+ minimum: 1,
72
+ maximum: 200,
73
+ default: 50,
74
+ description: "\u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA4 50\uFF0C\u6700\u5927 200"
75
+ },
76
+ source: {
77
+ type: "string",
78
+ description: "\u6765\u6E90\u8FC7\u6EE4\uFF1Aconsole(\u63A7\u5236\u53F0)\u3001provider-stdout(\u670D\u52A1\u8F93\u51FA)\u3001provider-stderr(\u670D\u52A1\u9519\u8BEF)"
79
+ }
80
+ }
81
+ }
82
+ }
83
+ ];
84
+ const VUE_DEVTOOLS_TOOLS = [
85
+ {
86
+ name: `${VUE_DEVTOOLS_PREFIX}get_apps`,
87
+ action: VUE_DEVTOOLS_ACTIONS.GET_APPS,
88
+ description: `\u83B7\u53D6\u6307\u5B9A\u9875\u9762\u6240\u6709 Vue \u5E94\u7528\u5B9E\u4F8B\u5217\u8868\u3002
89
+
90
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
91
+ - \u6392\u67E5\u5FAE\u524D\u7AEF/\u591A\u5B9E\u4F8B\u573A\u666F\u4E0B\u64CD\u4F5C\u7684\u662F\u54EA\u4E2A\u5E94\u7528
92
+ - \u5207\u6362\u6D3B\u8DC3\u5E94\u7528\u524D\u67E5\u770B\u6709\u54EA\u4E9B\u53EF\u7528`,
93
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
94
+ },
95
+ {
96
+ name: `${VUE_DEVTOOLS_PREFIX}set_active_app`,
97
+ action: VUE_DEVTOOLS_ACTIONS.TOGGLE_APP,
98
+ description: `\u5207\u6362\u6307\u5B9A\u9875\u9762\u7684\u6D3B\u8DC3 Vue \u5E94\u7528\u5B9E\u4F8B\u3002\u540E\u7EED\u6240\u6709 vue-devtools_* \u5DE5\u5177\u90FD\u64CD\u4F5C\u8FD9\u4E2A\u5E94\u7528\u3002`,
99
+ inputSchema: withPageIdSchema({
100
+ type: "object",
101
+ properties: {
102
+ appId: { type: "string", description: "\u5E94\u7528 ID\uFF08\u4ECE vue-devtools_get_apps \u83B7\u53D6\uFF09" }
103
+ },
104
+ required: ["appId"]
105
+ })
106
+ },
107
+ {
108
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_tree`,
109
+ action: VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_TREE,
110
+ description: `\u83B7\u53D6\u6307\u5B9A\u9875\u9762\u5F53\u524D\u6D3B\u8DC3 Vue \u5E94\u7528\u7684\u7EC4\u4EF6\u6811\u3002
111
+
112
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
113
+ - \u4E86\u89E3\u9875\u9762\u7EC4\u4EF6\u5C42\u7EA7\u7ED3\u6784
114
+ - \u627E\u5230\u76EE\u6807\u7EC4\u4EF6\u7684 nodeId\uFF08\u540E\u7EED\u67E5\u72B6\u6001\u7528\uFF09
115
+ - \u6392\u67E5\u7EC4\u4EF6\u672A\u6E32\u67D3\u95EE\u9898
116
+ - \u53EA\u5173\u5FC3\u67D0\u7C7B\u7EC4\u4EF6\u65F6\u7528 filter \u7F29\u5C0F\u8303\u56F4
117
+
118
+ **\u8FD4\u56DE**\uFF1A\u7EC4\u4EF6\u6811 [{ id, name, children, file, ... }]`,
119
+ inputSchema: withPageIdSchema({
120
+ type: "object",
121
+ properties: {
122
+ filter: { type: "string", description: "\u6309\u7EC4\u4EF6\u540D\u8FC7\u6EE4\uFF08\u5927\u5C0F\u5199\u4E0D\u654F\u611F\u7684\u5B50\u4E32\u5339\u914D\uFF09\uFF0C\u53EF\u9009" }
123
+ }
124
+ })
125
+ },
126
+ {
127
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_state`,
128
+ action: VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_STATE,
129
+ description: `\u83B7\u53D6\u6307\u5B9A\u7EC4\u4EF6\u7684\u5B8C\u6574\u8FD0\u884C\u65F6\u72B6\u6001\u3002
130
+
131
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
132
+ - \u6392\u67E5 props \u4F20\u503C\u662F\u5426\u6B63\u786E
133
+ - \u67E5\u770B ref/reactive \u54CD\u5E94\u5F0F\u6570\u636E\u7684\u5F53\u524D\u503C
134
+ - \u68C0\u67E5 computed \u8BA1\u7B97\u7ED3\u679C
135
+ - \u67E5\u770B attrs / events / inject / provide / template refs`,
136
+ inputSchema: withPageIdSchema({
137
+ type: "object",
138
+ properties: {
139
+ nodeId: {
140
+ type: "string",
141
+ description: "\u7EC4\u4EF6\u8282\u70B9 ID\uFF08\u4ECE vue-devtools_get_component_tree \u83B7\u53D6\uFF09"
142
+ }
143
+ },
144
+ required: ["nodeId"]
145
+ })
146
+ },
147
+ {
148
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_render_code`,
149
+ action: VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_RENDER_CODE,
150
+ description: `\u83B7\u53D6\u7EC4\u4EF6\u7684\u6E32\u67D3\u51FD\u6570\u6E90\u7801\u3002`,
151
+ inputSchema: withPageIdSchema({
152
+ type: "object",
153
+ properties: { nodeId: { type: "string", description: "\u7EC4\u4EF6\u8282\u70B9 ID" } },
154
+ required: ["nodeId"]
155
+ })
156
+ },
157
+ {
158
+ name: `${VUE_DEVTOOLS_PREFIX}get_current_route`,
159
+ action: VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
160
+ description: `\u83B7\u53D6 Vue Router \u7684\u5F53\u524D\u8DEF\u7531\u4FE1\u606F\u3002
161
+
162
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
163
+ - \u6392\u67E5\u8DEF\u7531\u8DF3\u8F6C\u95EE\u9898
164
+ - \u67E5\u770B\u5F53\u524D\u8DEF\u7531 path/params/query/hash
165
+ - \u786E\u8BA4\u8DEF\u7531\u5B88\u536B\u548C matched \u8BB0\u5F55`,
166
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
167
+ },
168
+ {
169
+ name: `${VUE_DEVTOOLS_PREFIX}get_routes`,
170
+ action: VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
171
+ description: `\u83B7\u53D6 Vue Router \u7684\u5B8C\u6574\u8DEF\u7531\u8868\u3002
172
+
173
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
174
+ - \u67E5\u770B\u6240\u6709\u5DF2\u6CE8\u518C\u8DEF\u7531
175
+ - \u786E\u8BA4\u8DEF\u7531\u914D\u7F6E\u662F\u5426\u6B63\u786E
176
+ - \u67E5\u770B\u8DEF\u7531\u5D4C\u5957\u5173\u7CFB`,
177
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
36
178
  }
37
179
  ];
180
+ function findVueDevtoolsTool(name) {
181
+ return VUE_DEVTOOLS_TOOLS.find((t) => t.name === name);
182
+ }
183
+ function vueDevtoolsToolList() {
184
+ return VUE_DEVTOOLS_TOOLS.map(({ name, description, inputSchema }) => ({
185
+ name,
186
+ description,
187
+ inputSchema
188
+ }));
189
+ }
38
190
  const UNSAFE_CATEGORIES = /* @__PURE__ */ new Set(["EXTENSIONS", "PWA", "THIRD_PARTY", "WEBMCP"]);
39
191
  function metaByName(short) {
40
192
  return OFFICIAL_TOOL_META.find((m) => m.name === short);
@@ -106,18 +258,25 @@ function isAllowedToolName(name) {
106
258
  }
107
259
  export {
108
260
  CUSTOM_TOOLS,
261
+ LOGS_DEVTOOLS_PREFIX,
109
262
  MCP_PREFIX,
110
263
  OFFICIAL_GLOBAL_POLICY,
111
264
  OFFICIAL_NO_PAGE_TOOLS,
112
265
  PAGE_ID_PROP,
113
266
  PROJECT_DESCRIPTION_NOTES,
267
+ VITE_LOGS_TOOL_NAME,
268
+ VUE_DEVTOOLS_PREFIX,
269
+ VUE_DEVTOOLS_TOOLS,
114
270
  configureToolScope,
115
271
  currentOfficialShorts,
116
272
  displayToolName,
117
273
  extraToolFlag,
274
+ findVueDevtoolsTool,
118
275
  isAllowedToolName,
119
276
  isOfficialExtraTool,
120
277
  officialDefaultShorts,
121
278
  officialExtraCandidates,
279
+ serviceLogToolName,
280
+ vueDevtoolsToolList,
122
281
  withPageIdSchema
123
282
  };
@@ -6,4 +6,6 @@ import type { McpProxy } from "../core/mcp-proxy";
6
6
  import { LOGS_API_PATH, type ChromeProjectOptions, type LogFileConfig } from "@aipanel/core";
7
7
  export * from "./types";
8
8
  export { LOGS_API_PATH, MCP_API_PATH, VUE_DEVTOOLS_API_PATH };
9
- export declare function setupMiddlewares(server: ViteDevServer, ctx: EndpointContext, mcp?: McpProxy, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions): void;
9
+ export declare function setupMiddlewares(server: ViteDevServer, ctx: EndpointContext, mcp?: McpProxy, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions,
10
+ /** 是否暴露 vue-devtools_* 工具(Vue 项目才有页面桥;未识别框架按保守策略视为 Vue) */
11
+ exposeVueDevtools?: boolean): void;
@@ -10,7 +10,7 @@ import { setupMcpEndpoint, MCP_API_PATH } from "./mcp.mjs";
10
10
  import { setupVueDevtoolsEndpoint, VUE_DEVTOOLS_API_PATH } from "./vue-devtools.mjs";
11
11
  import { LOGS_API_PATH } from "@aipanel/core";
12
12
  export * from "./types.mjs";
13
- function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject) {
13
+ function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject, exposeVueDevtools = true) {
14
14
  setupWidgetEndpoints(server, ctx);
15
15
  setupContextEndpoint(server, ctx);
16
16
  setupStartEndpoint(server, ctx);
@@ -20,7 +20,14 @@ function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject) {
20
20
  setupWarmupEndpoint(server, ctx);
21
21
  setupLogsEndpoint(server);
22
22
  if (mcp) {
23
- setupMcpEndpoint(server, mcp, () => ctx.getPageContext(), logFiles ?? [], chromeProject);
23
+ setupMcpEndpoint(
24
+ server,
25
+ mcp,
26
+ () => ctx.getPageContext(),
27
+ logFiles ?? [],
28
+ chromeProject,
29
+ exposeVueDevtools
30
+ );
24
31
  setupVueDevtoolsEndpoint(server, mcp);
25
32
  }
26
33
  }
@@ -3,4 +3,6 @@ import type { ChromeProjectOptions, LogFileConfig, PageContext } from "@aipanel/
3
3
  import { MCP_API_PATH } from "@aipanel/core";
4
4
  import { McpProxy } from "../core/mcp-proxy";
5
5
  export { MCP_API_PATH };
6
- export declare function setupMcpEndpoint(server: ViteDevServer, mcp: McpProxy, getPageContext: () => PageContext, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions): void;
6
+ export declare function setupMcpEndpoint(server: ViteDevServer, mcp: McpProxy, getPageContext: () => PageContext, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions,
7
+ /** Vue 项目才注入 vue-devtools 桥;非 Vue 项目既不上架也不路由这些工具,避免模型调用必失败的工具 */
8
+ exposeVueDevtools?: boolean): void;
@@ -1,4 +1,4 @@
1
- import { MCP_API_PATH, VUE_DEVTOOLS_ACTIONS } from "@aipanel/core";
1
+ import { MCP_API_PATH } from "@aipanel/core";
2
2
  import {
3
3
  createLogger,
4
4
  getProcessLogBuffer,
@@ -12,12 +12,20 @@ import {
12
12
  validatePageId
13
13
  } from "../core/mcp-chrome.mjs";
14
14
  import { resolveProjectScope } from "../core/chrome-project.mjs";
15
- import { CUSTOM_TOOLS, isAllowedToolName } from "../core/mcp-tools.mjs";
15
+ import {
16
+ CUSTOM_TOOLS,
17
+ findVueDevtoolsTool,
18
+ isAllowedToolName,
19
+ serviceLogToolName,
20
+ VITE_LOGS_TOOL_NAME,
21
+ VUE_DEVTOOLS_PREFIX,
22
+ vueDevtoolsToolList
23
+ } from "../core/mcp-tools.mjs";
16
24
  import { getOfficialProjectTools } from "../core/official-tools.mjs";
17
25
  import { executeAction } from "./vue-devtools.mjs";
18
26
  import { findGitRoot } from "@aipanel/core/node";
19
27
  const log = createLogger("McpEndpoint");
20
- function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProject) {
28
+ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProject, exposeVueDevtools = true) {
21
29
  const projectRoot = findGitRoot(process.cwd());
22
30
  server.middlewares.use(async (req, res, next) => {
23
31
  if (!req.url?.startsWith(MCP_API_PATH)) return next();
@@ -50,7 +58,8 @@ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProj
50
58
  scope.includeExtensions,
51
59
  getPageContext,
52
60
  logFiles,
53
- projectRoot
61
+ projectRoot,
62
+ exposeVueDevtools
54
63
  );
55
64
  return;
56
65
  }
@@ -68,7 +77,7 @@ function handleGetSse(req, res, mcp) {
68
77
  const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
69
78
  req.on("close", () => clearInterval(keepAlive));
70
79
  }
71
- async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
80
+ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot, exposeVueDevtools) {
72
81
  try {
73
82
  const body = await readBody(req);
74
83
  if (!body) {
@@ -80,7 +89,7 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
80
89
  log.debug("MCP request", { method, body: body.substring(0, 150) });
81
90
  switch (method) {
82
91
  case "tools/list":
83
- return await handleToolsList(res, id, mcp, logFiles);
92
+ return await handleToolsList(res, id, mcp, logFiles, exposeVueDevtools);
84
93
  case "tools/call":
85
94
  return await handleToolsCall(
86
95
  res,
@@ -92,7 +101,8 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
92
101
  includeExtensions,
93
102
  getPageContext,
94
103
  logFiles,
95
- projectRoot
104
+ projectRoot,
105
+ exposeVueDevtools
96
106
  );
97
107
  default:
98
108
  return await handleForward(res, body, mcp);
@@ -105,7 +115,7 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
105
115
  });
106
116
  }
107
117
  }
108
- async function handleToolsList(res, id, mcp, logFiles) {
118
+ async function handleToolsList(res, id, mcp, logFiles, exposeVueDevtools) {
109
119
  let official = [];
110
120
  try {
111
121
  official = await getOfficialProjectTools(mcp);
@@ -114,20 +124,28 @@ async function handleToolsList(res, id, mcp, logFiles) {
114
124
  error: e instanceof Error ? e.message : String(e)
115
125
  });
116
126
  }
117
- const tools = [...official, ...CUSTOM_TOOLS, ...buildServiceLogTools(logFiles)];
127
+ const tools = [
128
+ ...official,
129
+ ...CUSTOM_TOOLS,
130
+ ...exposeVueDevtools ? vueDevtoolsToolList() : [],
131
+ ...buildServiceLogTools(logFiles)
132
+ ];
118
133
  sendMcpJson(res, 200, { jsonrpc: "2.0", id, result: { tools } }, mcp.sessionId);
119
134
  }
120
- function handleToolsCall(res, id, body, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
135
+ function handleToolsCall(res, id, body, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot, exposeVueDevtools) {
121
136
  const params = tryParseParams(body);
122
137
  const toolName = params?.name;
123
138
  const args = params?.arguments ?? {};
124
139
  if (toolName === "chrome-devtools_current_page") {
125
140
  return handleGetPageContext(res, id, mcp, operationsOrigins, includeExtensions, getPageContext);
126
141
  }
127
- if (toolName?.startsWith("vue-devtools_")) {
142
+ if (toolName?.startsWith(VUE_DEVTOOLS_PREFIX)) {
143
+ if (!exposeVueDevtools) {
144
+ return sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
145
+ }
128
146
  return handleVueDevtoolsTool(res, id, mcp, projectOrigins, toolName, args);
129
147
  }
130
- if (toolName === "logs-devtools_vite_logs") {
148
+ if (toolName === VITE_LOGS_TOOL_NAME) {
131
149
  return handleViteLogsTool(res, id, args, mcp.sessionId);
132
150
  }
133
151
  if (toolName && isServiceLogTool(toolName, logFiles)) {
@@ -388,23 +406,14 @@ async function handleDevTool(res, id, mcp, mapped, args, operationsOrigins, incl
388
406
  sendMcpError(res, id, -32603, `MCP \u8C03\u7528\u5931\u8D25: ${e.message}`, mcp.sessionId);
389
407
  }
390
408
  }
391
- const VUE_DEVTOOLS_TOOL_ACTIONS = {
392
- "vue-devtools_get_apps": VUE_DEVTOOLS_ACTIONS.GET_APPS,
393
- "vue-devtools_set_active_app": VUE_DEVTOOLS_ACTIONS.TOGGLE_APP,
394
- "vue-devtools_get_component_tree": VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_TREE,
395
- "vue-devtools_get_component_state": VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_STATE,
396
- "vue-devtools_get_component_render_code": VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_RENDER_CODE,
397
- "vue-devtools_get_current_route": VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
398
- "vue-devtools_get_routes": VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO
399
- };
400
409
  async function handleVueDevtoolsTool(res, id, mcp, projectOrigins, toolName, args) {
401
410
  try {
402
- const action = VUE_DEVTOOLS_TOOL_ACTIONS[toolName];
403
- if (!action) {
411
+ const tool = findVueDevtoolsTool(toolName);
412
+ if (!tool) {
404
413
  sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
405
414
  return;
406
415
  }
407
- const result = await executeAction(action, args, mcp, projectOrigins);
416
+ const result = await executeAction(tool.action, args, mcp, projectOrigins);
408
417
  switch (toolName) {
409
418
  case "vue-devtools_set_active_app":
410
419
  sendMcpResult(res, id, `\u5DF2\u5207\u6362\u5230\u5E94\u7528 ${args["appId"]}`, mcp.sessionId);
@@ -483,7 +492,7 @@ function parseProcessLevelFilter(level) {
483
492
  }
484
493
  function buildServiceLogTools(logFiles) {
485
494
  return logFiles.map((cfg) => ({
486
- name: `logs-devtools_${cfg.name}_logs`,
495
+ name: serviceLogToolName(cfg.name),
487
496
  description: `\u83B7\u53D6 ${cfg.name} \u7684\u65E5\u5FD7\u3002
488
497
 
489
498
  **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
@@ -511,11 +520,11 @@ ${cfg.description}
511
520
  }));
512
521
  }
513
522
  function isServiceLogTool(toolName, logFiles) {
514
- return logFiles.some((cfg) => `logs-devtools_${cfg.name}_logs` === toolName);
523
+ return logFiles.some((cfg) => serviceLogToolName(cfg.name) === toolName);
515
524
  }
516
525
  async function handleServiceLogsTool(res, id, toolName, args, logFiles, projectRoot, sessionId) {
517
526
  try {
518
- const cfg = logFiles.find((c) => `logs-devtools_${c.name}_logs` === toolName);
527
+ const cfg = logFiles.find((c) => serviceLogToolName(c.name) === toolName);
519
528
  if (!cfg) {
520
529
  sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, sessionId);
521
530
  return;
package/es/index.mjs CHANGED
@@ -210,7 +210,8 @@ function createAIPanelPlugin(options = {}) {
210
210
  },
211
211
  mcpProxy,
212
212
  config.logFiles,
213
- chromeProjectOptions
213
+ chromeProjectOptions,
214
+ isVueProject
214
215
  );
215
216
  if (!config.mcpOnly) {
216
217
  try {
@@ -18,27 +18,41 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
18
18
  var mcp_tools_exports = {};
19
19
  __export(mcp_tools_exports, {
20
20
  CUSTOM_TOOLS: () => CUSTOM_TOOLS,
21
+ LOGS_DEVTOOLS_PREFIX: () => LOGS_DEVTOOLS_PREFIX,
21
22
  MCP_PREFIX: () => MCP_PREFIX,
22
23
  OFFICIAL_GLOBAL_POLICY: () => OFFICIAL_GLOBAL_POLICY,
23
24
  OFFICIAL_NO_PAGE_TOOLS: () => OFFICIAL_NO_PAGE_TOOLS,
24
25
  PAGE_ID_PROP: () => PAGE_ID_PROP,
25
26
  PROJECT_DESCRIPTION_NOTES: () => PROJECT_DESCRIPTION_NOTES,
27
+ VITE_LOGS_TOOL_NAME: () => VITE_LOGS_TOOL_NAME,
28
+ VUE_DEVTOOLS_PREFIX: () => VUE_DEVTOOLS_PREFIX,
29
+ VUE_DEVTOOLS_TOOLS: () => VUE_DEVTOOLS_TOOLS,
26
30
  configureToolScope: () => configureToolScope,
27
31
  currentOfficialShorts: () => currentOfficialShorts,
28
32
  displayToolName: () => displayToolName,
29
33
  extraToolFlag: () => extraToolFlag,
34
+ findVueDevtoolsTool: () => findVueDevtoolsTool,
30
35
  isAllowedToolName: () => isAllowedToolName,
31
36
  isOfficialExtraTool: () => isOfficialExtraTool,
32
37
  officialDefaultShorts: () => officialDefaultShorts,
33
38
  officialExtraCandidates: () => officialExtraCandidates,
39
+ serviceLogToolName: () => serviceLogToolName,
40
+ vueDevtoolsToolList: () => vueDevtoolsToolList,
34
41
  withPageIdSchema: () => withPageIdSchema
35
42
  });
36
43
  module.exports = __toCommonJS(mcp_tools_exports);
44
+ var import_core = require("@aipanel/core");
37
45
  var import_official_meta = require("./official-meta.cjs");
38
46
  const MCP_PREFIX = "chrome-devtools_";
39
47
  function displayToolName(short) {
40
48
  return MCP_PREFIX + short;
41
49
  }
50
+ const VUE_DEVTOOLS_PREFIX = "vue-devtools_";
51
+ const LOGS_DEVTOOLS_PREFIX = "logs-devtools_";
52
+ const VITE_LOGS_TOOL_NAME = `${LOGS_DEVTOOLS_PREFIX}vite_logs`;
53
+ function serviceLogToolName(name) {
54
+ return `${LOGS_DEVTOOLS_PREFIX}${name}_logs`;
55
+ }
42
56
  const OFFICIAL_GLOBAL_POLICY = [
43
57
  "evaluate_script",
44
58
  "list_console_messages",
@@ -69,8 +83,153 @@ const CUSTOM_TOOLS = [
69
83
  name: displayToolName("current_page"),
70
84
  description: "Get the page the user is currently browsing (URL, title, and page ID). Resolves the project page with injected context; for allowOrigins pages use list_pages and pass its pageId to page tools instead.",
71
85
  inputSchema: { type: "object", properties: {} }
86
+ },
87
+ {
88
+ name: VITE_LOGS_TOOL_NAME,
89
+ description: `\u83B7\u53D6 Vite \u5F00\u53D1\u670D\u52A1\u5668\u7684\u8FD0\u884C\u65E5\u5FD7\u3002
90
+
91
+ **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
92
+ - \u7528\u6237\u62A5\u544A"\u9875\u9762\u6CA1\u66F4\u65B0"\u3001"\u70ED\u66F4\u65B0\u4E0D\u5DE5\u4F5C"\u3001"HMR \u5931\u6548"\u65F6
93
+ - \u6784\u5EFA\u62A5\u9519\u6216\u7F16\u8BD1\u5931\u8D25\uFF0C\u9700\u8981\u67E5\u770B\u8BE6\u7EC6\u9519\u8BEF\u4FE1\u606F
94
+ - \u9875\u9762\u767D\u5C4F\u3001\u6837\u5F0F\u4E22\u5931\u3001\u6A21\u5757\u52A0\u8F7D\u5931\u8D25\u7B49\u5F00\u53D1\u95EE\u9898
95
+ - \u7528\u6237\u63D0\u5230"\u5F00\u53D1\u670D\u52A1\u5668\u6709\u95EE\u9898"\u3001"vite \u62A5\u9519"
96
+ - \u9700\u8981\u786E\u8BA4\u6700\u8FD1\u7684\u6587\u4EF6\u53D8\u66F4\u662F\u5426\u88AB Vite \u6B63\u786E\u5904\u7406
97
+
98
+ **\u65E5\u5FD7\u5185\u5BB9**\uFF1A
99
+ - Vite HMR \u70ED\u66F4\u65B0\u65E5\u5FD7\uFF08\u54EA\u4E9B\u6587\u4EF6\u88AB\u66F4\u65B0\u3001\u66F4\u65B0\u72B6\u6001\uFF09
100
+ - \u6784\u5EFA\u7F16\u8BD1\u65E5\u5FD7\uFF08\u9519\u8BEF\u3001\u8B66\u544A\u3001\u6210\u529F\u4FE1\u606F\uFF09
101
+ - OpenCode Web \u8FDB\u7A0B\u8F93\u51FA
102
+ - \u63D2\u4EF6\u8FD0\u884C\u65E5\u5FD7
103
+
104
+ \u65E5\u5FD7\u4FDD\u5B58\u5728\u5185\u5B58\u7F13\u51B2\u533A\uFF08\u6700\u8FD1 500 \u6761\uFF09\u3002`,
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ level: {
109
+ type: "string",
110
+ description: "\u65E5\u5FD7\u7EA7\u522B\u8FC7\u6EE4\uFF1Aerror(\u9519\u8BEF)\u3001warn(\u8B66\u544A)\u3001info(\u4FE1\u606F)\u3001debug(\u8C03\u8BD5)\u3001log(\u666E\u901A)\u3002\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF0C\u5982 'error,warn'"
111
+ },
112
+ limit: {
113
+ type: "integer",
114
+ minimum: 1,
115
+ maximum: 200,
116
+ default: 50,
117
+ description: "\u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA4 50\uFF0C\u6700\u5927 200"
118
+ },
119
+ source: {
120
+ type: "string",
121
+ description: "\u6765\u6E90\u8FC7\u6EE4\uFF1Aconsole(\u63A7\u5236\u53F0)\u3001provider-stdout(\u670D\u52A1\u8F93\u51FA)\u3001provider-stderr(\u670D\u52A1\u9519\u8BEF)"
122
+ }
123
+ }
124
+ }
125
+ }
126
+ ];
127
+ const VUE_DEVTOOLS_TOOLS = [
128
+ {
129
+ name: `${VUE_DEVTOOLS_PREFIX}get_apps`,
130
+ action: import_core.VUE_DEVTOOLS_ACTIONS.GET_APPS,
131
+ description: `\u83B7\u53D6\u6307\u5B9A\u9875\u9762\u6240\u6709 Vue \u5E94\u7528\u5B9E\u4F8B\u5217\u8868\u3002
132
+
133
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
134
+ - \u6392\u67E5\u5FAE\u524D\u7AEF/\u591A\u5B9E\u4F8B\u573A\u666F\u4E0B\u64CD\u4F5C\u7684\u662F\u54EA\u4E2A\u5E94\u7528
135
+ - \u5207\u6362\u6D3B\u8DC3\u5E94\u7528\u524D\u67E5\u770B\u6709\u54EA\u4E9B\u53EF\u7528`,
136
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
137
+ },
138
+ {
139
+ name: `${VUE_DEVTOOLS_PREFIX}set_active_app`,
140
+ action: import_core.VUE_DEVTOOLS_ACTIONS.TOGGLE_APP,
141
+ description: `\u5207\u6362\u6307\u5B9A\u9875\u9762\u7684\u6D3B\u8DC3 Vue \u5E94\u7528\u5B9E\u4F8B\u3002\u540E\u7EED\u6240\u6709 vue-devtools_* \u5DE5\u5177\u90FD\u64CD\u4F5C\u8FD9\u4E2A\u5E94\u7528\u3002`,
142
+ inputSchema: withPageIdSchema({
143
+ type: "object",
144
+ properties: {
145
+ appId: { type: "string", description: "\u5E94\u7528 ID\uFF08\u4ECE vue-devtools_get_apps \u83B7\u53D6\uFF09" }
146
+ },
147
+ required: ["appId"]
148
+ })
149
+ },
150
+ {
151
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_tree`,
152
+ action: import_core.VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_TREE,
153
+ description: `\u83B7\u53D6\u6307\u5B9A\u9875\u9762\u5F53\u524D\u6D3B\u8DC3 Vue \u5E94\u7528\u7684\u7EC4\u4EF6\u6811\u3002
154
+
155
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
156
+ - \u4E86\u89E3\u9875\u9762\u7EC4\u4EF6\u5C42\u7EA7\u7ED3\u6784
157
+ - \u627E\u5230\u76EE\u6807\u7EC4\u4EF6\u7684 nodeId\uFF08\u540E\u7EED\u67E5\u72B6\u6001\u7528\uFF09
158
+ - \u6392\u67E5\u7EC4\u4EF6\u672A\u6E32\u67D3\u95EE\u9898
159
+ - \u53EA\u5173\u5FC3\u67D0\u7C7B\u7EC4\u4EF6\u65F6\u7528 filter \u7F29\u5C0F\u8303\u56F4
160
+
161
+ **\u8FD4\u56DE**\uFF1A\u7EC4\u4EF6\u6811 [{ id, name, children, file, ... }]`,
162
+ inputSchema: withPageIdSchema({
163
+ type: "object",
164
+ properties: {
165
+ filter: { type: "string", description: "\u6309\u7EC4\u4EF6\u540D\u8FC7\u6EE4\uFF08\u5927\u5C0F\u5199\u4E0D\u654F\u611F\u7684\u5B50\u4E32\u5339\u914D\uFF09\uFF0C\u53EF\u9009" }
166
+ }
167
+ })
168
+ },
169
+ {
170
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_state`,
171
+ action: import_core.VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_STATE,
172
+ description: `\u83B7\u53D6\u6307\u5B9A\u7EC4\u4EF6\u7684\u5B8C\u6574\u8FD0\u884C\u65F6\u72B6\u6001\u3002
173
+
174
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
175
+ - \u6392\u67E5 props \u4F20\u503C\u662F\u5426\u6B63\u786E
176
+ - \u67E5\u770B ref/reactive \u54CD\u5E94\u5F0F\u6570\u636E\u7684\u5F53\u524D\u503C
177
+ - \u68C0\u67E5 computed \u8BA1\u7B97\u7ED3\u679C
178
+ - \u67E5\u770B attrs / events / inject / provide / template refs`,
179
+ inputSchema: withPageIdSchema({
180
+ type: "object",
181
+ properties: {
182
+ nodeId: {
183
+ type: "string",
184
+ description: "\u7EC4\u4EF6\u8282\u70B9 ID\uFF08\u4ECE vue-devtools_get_component_tree \u83B7\u53D6\uFF09"
185
+ }
186
+ },
187
+ required: ["nodeId"]
188
+ })
189
+ },
190
+ {
191
+ name: `${VUE_DEVTOOLS_PREFIX}get_component_render_code`,
192
+ action: import_core.VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_RENDER_CODE,
193
+ description: `\u83B7\u53D6\u7EC4\u4EF6\u7684\u6E32\u67D3\u51FD\u6570\u6E90\u7801\u3002`,
194
+ inputSchema: withPageIdSchema({
195
+ type: "object",
196
+ properties: { nodeId: { type: "string", description: "\u7EC4\u4EF6\u8282\u70B9 ID" } },
197
+ required: ["nodeId"]
198
+ })
199
+ },
200
+ {
201
+ name: `${VUE_DEVTOOLS_PREFIX}get_current_route`,
202
+ action: import_core.VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
203
+ description: `\u83B7\u53D6 Vue Router \u7684\u5F53\u524D\u8DEF\u7531\u4FE1\u606F\u3002
204
+
205
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
206
+ - \u6392\u67E5\u8DEF\u7531\u8DF3\u8F6C\u95EE\u9898
207
+ - \u67E5\u770B\u5F53\u524D\u8DEF\u7531 path/params/query/hash
208
+ - \u786E\u8BA4\u8DEF\u7531\u5B88\u536B\u548C matched \u8BB0\u5F55`,
209
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
210
+ },
211
+ {
212
+ name: `${VUE_DEVTOOLS_PREFIX}get_routes`,
213
+ action: import_core.VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
214
+ description: `\u83B7\u53D6 Vue Router \u7684\u5B8C\u6574\u8DEF\u7531\u8868\u3002
215
+
216
+ **\u4F55\u65F6\u4F7F\u7528**\uFF1A
217
+ - \u67E5\u770B\u6240\u6709\u5DF2\u6CE8\u518C\u8DEF\u7531
218
+ - \u786E\u8BA4\u8DEF\u7531\u914D\u7F6E\u662F\u5426\u6B63\u786E
219
+ - \u67E5\u770B\u8DEF\u7531\u5D4C\u5957\u5173\u7CFB`,
220
+ inputSchema: withPageIdSchema({ type: "object", properties: {} })
72
221
  }
73
222
  ];
223
+ function findVueDevtoolsTool(name) {
224
+ return VUE_DEVTOOLS_TOOLS.find((t) => t.name === name);
225
+ }
226
+ function vueDevtoolsToolList() {
227
+ return VUE_DEVTOOLS_TOOLS.map(({ name, description, inputSchema }) => ({
228
+ name,
229
+ description,
230
+ inputSchema
231
+ }));
232
+ }
74
233
  const UNSAFE_CATEGORIES = /* @__PURE__ */ new Set(["EXTENSIONS", "PWA", "THIRD_PARTY", "WEBMCP"]);
75
234
  function metaByName(short) {
76
235
  return import_official_meta.OFFICIAL_TOOL_META.find((m) => m.name === short);
@@ -143,18 +302,25 @@ function isAllowedToolName(name) {
143
302
  // Annotate the CommonJS export names for ESM import in node:
144
303
  0 && (module.exports = {
145
304
  CUSTOM_TOOLS,
305
+ LOGS_DEVTOOLS_PREFIX,
146
306
  MCP_PREFIX,
147
307
  OFFICIAL_GLOBAL_POLICY,
148
308
  OFFICIAL_NO_PAGE_TOOLS,
149
309
  PAGE_ID_PROP,
150
310
  PROJECT_DESCRIPTION_NOTES,
311
+ VITE_LOGS_TOOL_NAME,
312
+ VUE_DEVTOOLS_PREFIX,
313
+ VUE_DEVTOOLS_TOOLS,
151
314
  configureToolScope,
152
315
  currentOfficialShorts,
153
316
  displayToolName,
154
317
  extraToolFlag,
318
+ findVueDevtoolsTool,
155
319
  isAllowedToolName,
156
320
  isOfficialExtraTool,
157
321
  officialDefaultShorts,
158
322
  officialExtraCandidates,
323
+ serviceLogToolName,
324
+ vueDevtoolsToolList,
159
325
  withPageIdSchema
160
326
  });
@@ -5,9 +5,17 @@
5
5
  * (schema/描述运行时同步自官方 tools/list,见 official-tools.ts)+ 本项目自定义工具。
6
6
  * 工具层限制(白名单、必填 pageId、仅项目页)由本文件 + endpoints/mcp.ts 的 tools/call 共同执行。
7
7
  */
8
+ import { type VueDevtoolsAction } from "@aipanel/core";
8
9
  /** 模型可见名前缀:官方短名 → chrome-devtools_<name> */
9
10
  export declare const MCP_PREFIX = "chrome-devtools_";
10
11
  export declare function displayToolName(short: string): string;
12
+ /** 本地桥接工具族前缀(tools/list 暴露面与 tools/call 路由共用) */
13
+ export declare const VUE_DEVTOOLS_PREFIX = "vue-devtools_";
14
+ export declare const LOGS_DEVTOOLS_PREFIX = "logs-devtools_";
15
+ /** Vite 进程日志工具名(工具定义与调用路由共用) */
16
+ export declare const VITE_LOGS_TOOL_NAME = "logs-devtools_vite_logs";
17
+ /** 服务日志文件工具名(按 LogFileConfig.name 生成,暴露与路由共用) */
18
+ export declare function serviceLogToolName(name: string): string;
11
19
  export interface CustomTool {
12
20
  name: string;
13
21
  description: string;
@@ -44,6 +52,20 @@ export declare function withPageIdSchema(schema: CustomTool["inputSchema"]): Cus
44
52
  export declare const PROJECT_DESCRIPTION_NOTES: Record<string, string>;
45
53
  /** 自定义工具(官方无对应,工具层本地实现) */
46
54
  export declare const CUSTOM_TOOLS: CustomTool[];
55
+ /**
56
+ * 页面桥接工具:描述与输入 schema(模型可见)+ 后端 action(调用分发)单一来源。
57
+ * tools/list 与 tools/call 都取自本定义,避免"清单"与"路由"两份来源各自漂移。
58
+ * 只在注入 Vue DevTools 桥的项目(Vue 项目)暴露。
59
+ */
60
+ export interface VueDevtoolsTool extends CustomTool {
61
+ /** 页面桥后端 action(endpoints/vue-devtools.ts executeAction) */
62
+ action: VueDevtoolsAction;
63
+ }
64
+ export declare const VUE_DEVTOOLS_TOOLS: readonly VueDevtoolsTool[];
65
+ /** vue-devtools_* 工具名 → 定义(未知名称返回 undefined) */
66
+ export declare function findVueDevtoolsTool(name: string): VueDevtoolsTool | undefined;
67
+ /** vue-devtools_* 的 tools/list 暴露面(剥掉仅用于路由分发的 action 字段) */
68
+ export declare function vueDevtoolsToolList(): CustomTool[];
47
69
  /** 官方声明了条件、且属于可开启范围的工具(由官方元数据派生,无手写) */
48
70
  export declare function isOfficialExtraTool(short: string): boolean;
49
71
  /** 由官方 conditions 推导所需 flag(experimentalX → --experimental-x,与官方 CLI 一致) */
@@ -36,7 +36,7 @@ var import_mcp = require("./mcp.cjs");
36
36
  var import_vue_devtools = require("./vue-devtools.cjs");
37
37
  var import_core = require("@aipanel/core");
38
38
  __reExport(endpoints_exports, require("./types.cjs"), module.exports);
39
- function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject) {
39
+ function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject, exposeVueDevtools = true) {
40
40
  (0, import_widget.setupWidgetEndpoints)(server, ctx);
41
41
  (0, import_context.setupContextEndpoint)(server, ctx);
42
42
  (0, import_start.setupStartEndpoint)(server, ctx);
@@ -46,7 +46,14 @@ function setupMiddlewares(server, ctx, mcp, logFiles, chromeProject) {
46
46
  (0, import_warmup.setupWarmupEndpoint)(server, ctx);
47
47
  (0, import_logs.setupLogsEndpoint)(server);
48
48
  if (mcp) {
49
- (0, import_mcp.setupMcpEndpoint)(server, mcp, () => ctx.getPageContext(), logFiles ?? [], chromeProject);
49
+ (0, import_mcp.setupMcpEndpoint)(
50
+ server,
51
+ mcp,
52
+ () => ctx.getPageContext(),
53
+ logFiles ?? [],
54
+ chromeProject,
55
+ exposeVueDevtools
56
+ );
50
57
  (0, import_vue_devtools.setupVueDevtoolsEndpoint)(server, mcp);
51
58
  }
52
59
  }
@@ -6,4 +6,6 @@ import type { McpProxy } from "../core/mcp-proxy";
6
6
  import { LOGS_API_PATH, type ChromeProjectOptions, type LogFileConfig } from "@aipanel/core";
7
7
  export * from "./types";
8
8
  export { LOGS_API_PATH, MCP_API_PATH, VUE_DEVTOOLS_API_PATH };
9
- export declare function setupMiddlewares(server: ViteDevServer, ctx: EndpointContext, mcp?: McpProxy, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions): void;
9
+ export declare function setupMiddlewares(server: ViteDevServer, ctx: EndpointContext, mcp?: McpProxy, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions,
10
+ /** 是否暴露 vue-devtools_* 工具(Vue 项目才有页面桥;未识别框架按保守策略视为 Vue) */
11
+ exposeVueDevtools?: boolean): void;
@@ -30,7 +30,7 @@ var import_official_tools = require("../core/official-tools.cjs");
30
30
  var import_vue_devtools = require("./vue-devtools.cjs");
31
31
  var import_node2 = require("@aipanel/core/node");
32
32
  const log = (0, import_node.createLogger)("McpEndpoint");
33
- function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProject) {
33
+ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProject, exposeVueDevtools = true) {
34
34
  const projectRoot = (0, import_node2.findGitRoot)(process.cwd());
35
35
  server.middlewares.use(async (req, res, next) => {
36
36
  if (!req.url?.startsWith(import_core.MCP_API_PATH)) return next();
@@ -63,7 +63,8 @@ function setupMcpEndpoint(server, mcp, getPageContext, logFiles = [], chromeProj
63
63
  scope.includeExtensions,
64
64
  getPageContext,
65
65
  logFiles,
66
- projectRoot
66
+ projectRoot,
67
+ exposeVueDevtools
67
68
  );
68
69
  return;
69
70
  }
@@ -81,7 +82,7 @@ function handleGetSse(req, res, mcp) {
81
82
  const keepAlive = setInterval(() => res.write(":ping\n\n"), 15e3);
82
83
  req.on("close", () => clearInterval(keepAlive));
83
84
  }
84
- async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
85
+ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot, exposeVueDevtools) {
85
86
  try {
86
87
  const body = await readBody(req);
87
88
  if (!body) {
@@ -93,7 +94,7 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
93
94
  log.debug("MCP request", { method, body: body.substring(0, 150) });
94
95
  switch (method) {
95
96
  case "tools/list":
96
- return await handleToolsList(res, id, mcp, logFiles);
97
+ return await handleToolsList(res, id, mcp, logFiles, exposeVueDevtools);
97
98
  case "tools/call":
98
99
  return await handleToolsCall(
99
100
  res,
@@ -105,7 +106,8 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
105
106
  includeExtensions,
106
107
  getPageContext,
107
108
  logFiles,
108
- projectRoot
109
+ projectRoot,
110
+ exposeVueDevtools
109
111
  );
110
112
  default:
111
113
  return await handleForward(res, body, mcp);
@@ -118,7 +120,7 @@ async function handlePost(req, res, mcp, projectOrigins, operationsOrigins, incl
118
120
  });
119
121
  }
120
122
  }
121
- async function handleToolsList(res, id, mcp, logFiles) {
123
+ async function handleToolsList(res, id, mcp, logFiles, exposeVueDevtools) {
122
124
  let official = [];
123
125
  try {
124
126
  official = await (0, import_official_tools.getOfficialProjectTools)(mcp);
@@ -127,20 +129,28 @@ async function handleToolsList(res, id, mcp, logFiles) {
127
129
  error: e instanceof Error ? e.message : String(e)
128
130
  });
129
131
  }
130
- const tools = [...official, ...import_mcp_tools.CUSTOM_TOOLS, ...buildServiceLogTools(logFiles)];
132
+ const tools = [
133
+ ...official,
134
+ ...import_mcp_tools.CUSTOM_TOOLS,
135
+ ...exposeVueDevtools ? (0, import_mcp_tools.vueDevtoolsToolList)() : [],
136
+ ...buildServiceLogTools(logFiles)
137
+ ];
131
138
  sendMcpJson(res, 200, { jsonrpc: "2.0", id, result: { tools } }, mcp.sessionId);
132
139
  }
133
- function handleToolsCall(res, id, body, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot) {
140
+ function handleToolsCall(res, id, body, mcp, projectOrigins, operationsOrigins, includeExtensions, getPageContext, logFiles, projectRoot, exposeVueDevtools) {
134
141
  const params = tryParseParams(body);
135
142
  const toolName = params?.name;
136
143
  const args = params?.arguments ?? {};
137
144
  if (toolName === "chrome-devtools_current_page") {
138
145
  return handleGetPageContext(res, id, mcp, operationsOrigins, includeExtensions, getPageContext);
139
146
  }
140
- if (toolName?.startsWith("vue-devtools_")) {
147
+ if (toolName?.startsWith(import_mcp_tools.VUE_DEVTOOLS_PREFIX)) {
148
+ if (!exposeVueDevtools) {
149
+ return sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
150
+ }
141
151
  return handleVueDevtoolsTool(res, id, mcp, projectOrigins, toolName, args);
142
152
  }
143
- if (toolName === "logs-devtools_vite_logs") {
153
+ if (toolName === import_mcp_tools.VITE_LOGS_TOOL_NAME) {
144
154
  return handleViteLogsTool(res, id, args, mcp.sessionId);
145
155
  }
146
156
  if (toolName && isServiceLogTool(toolName, logFiles)) {
@@ -401,23 +411,14 @@ async function handleDevTool(res, id, mcp, mapped, args, operationsOrigins, incl
401
411
  sendMcpError(res, id, -32603, `MCP \u8C03\u7528\u5931\u8D25: ${e.message}`, mcp.sessionId);
402
412
  }
403
413
  }
404
- const VUE_DEVTOOLS_TOOL_ACTIONS = {
405
- "vue-devtools_get_apps": import_core.VUE_DEVTOOLS_ACTIONS.GET_APPS,
406
- "vue-devtools_set_active_app": import_core.VUE_DEVTOOLS_ACTIONS.TOGGLE_APP,
407
- "vue-devtools_get_component_tree": import_core.VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_TREE,
408
- "vue-devtools_get_component_state": import_core.VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_STATE,
409
- "vue-devtools_get_component_render_code": import_core.VUE_DEVTOOLS_ACTIONS.GET_COMPONENT_RENDER_CODE,
410
- "vue-devtools_get_current_route": import_core.VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO,
411
- "vue-devtools_get_routes": import_core.VUE_DEVTOOLS_ACTIONS.GET_ROUTER_INFO
412
- };
413
414
  async function handleVueDevtoolsTool(res, id, mcp, projectOrigins, toolName, args) {
414
415
  try {
415
- const action = VUE_DEVTOOLS_TOOL_ACTIONS[toolName];
416
- if (!action) {
416
+ const tool = (0, import_mcp_tools.findVueDevtoolsTool)(toolName);
417
+ if (!tool) {
417
418
  sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, mcp.sessionId);
418
419
  return;
419
420
  }
420
- const result = await (0, import_vue_devtools.executeAction)(action, args, mcp, projectOrigins);
421
+ const result = await (0, import_vue_devtools.executeAction)(tool.action, args, mcp, projectOrigins);
421
422
  switch (toolName) {
422
423
  case "vue-devtools_set_active_app":
423
424
  sendMcpResult(res, id, `\u5DF2\u5207\u6362\u5230\u5E94\u7528 ${args["appId"]}`, mcp.sessionId);
@@ -496,7 +497,7 @@ function parseProcessLevelFilter(level) {
496
497
  }
497
498
  function buildServiceLogTools(logFiles) {
498
499
  return logFiles.map((cfg) => ({
499
- name: `logs-devtools_${cfg.name}_logs`,
500
+ name: (0, import_mcp_tools.serviceLogToolName)(cfg.name),
500
501
  description: `\u83B7\u53D6 ${cfg.name} \u7684\u65E5\u5FD7\u3002
501
502
 
502
503
  **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
@@ -524,11 +525,11 @@ ${cfg.description}
524
525
  }));
525
526
  }
526
527
  function isServiceLogTool(toolName, logFiles) {
527
- return logFiles.some((cfg) => `logs-devtools_${cfg.name}_logs` === toolName);
528
+ return logFiles.some((cfg) => (0, import_mcp_tools.serviceLogToolName)(cfg.name) === toolName);
528
529
  }
529
530
  async function handleServiceLogsTool(res, id, toolName, args, logFiles, projectRoot, sessionId) {
530
531
  try {
531
- const cfg = logFiles.find((c) => `logs-devtools_${c.name}_logs` === toolName);
532
+ const cfg = logFiles.find((c) => (0, import_mcp_tools.serviceLogToolName)(c.name) === toolName);
532
533
  if (!cfg) {
533
534
  sendMcpError(res, id, -32601, `Tool not found: ${toolName}`, sessionId);
534
535
  return;
@@ -3,4 +3,6 @@ import type { ChromeProjectOptions, LogFileConfig, PageContext } from "@aipanel/
3
3
  import { MCP_API_PATH } from "@aipanel/core";
4
4
  import { McpProxy } from "../core/mcp-proxy";
5
5
  export { MCP_API_PATH };
6
- export declare function setupMcpEndpoint(server: ViteDevServer, mcp: McpProxy, getPageContext: () => PageContext, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions): void;
6
+ export declare function setupMcpEndpoint(server: ViteDevServer, mcp: McpProxy, getPageContext: () => PageContext, logFiles?: LogFileConfig[], chromeProject?: ChromeProjectOptions,
7
+ /** Vue 项目才注入 vue-devtools 桥;非 Vue 项目既不上架也不路由这些工具,避免模型调用必失败的工具 */
8
+ exposeVueDevtools?: boolean): void;
package/lib/index.cjs CHANGED
@@ -230,7 +230,8 @@ function createAIPanelPlugin(options = {}) {
230
230
  },
231
231
  mcpProxy,
232
232
  config.logFiles,
233
- chromeProjectOptions
233
+ chromeProjectOptions,
234
+ isVueProject
234
235
  );
235
236
  if (!config.mcpOnly) {
236
237
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-aipanel",
3
- "version": "1.2.23",
3
+ "version": "1.2.24",
4
4
  "description": "Embed OpenCode Web UI in your Vite dev server for real-time code modification and preview",
5
5
  "type": "module",
6
6
  "main": "lib/index.cjs",
@@ -35,8 +35,8 @@
35
35
  "author": "",
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "@aipanel/core": "1.2.23",
39
- "@aipanel/provider-opencode": "1.2.23",
38
+ "@aipanel/core": "1.2.24",
39
+ "@aipanel/provider-opencode": "1.2.24",
40
40
  "@code-inspector/core": "^2.0.9",
41
41
  "@vue/devtools-kit": "^8.0.0",
42
42
  "@vue/language-server": "^3.3.7",
@@ -47,7 +47,7 @@
47
47
  "unplugin-vue-inspector": "^3.0.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@aipanel/provider-deepseek": "1.2.23",
50
+ "@aipanel/provider-deepseek": "1.2.24",
51
51
  "vite": ">=5.0.0"
52
52
  },
53
53
  "peerDependenciesMeta": {
@@ -56,7 +56,7 @@
56
56
  }
57
57
  },
58
58
  "devDependencies": {
59
- "@aipanel/client": "1.2.23",
59
+ "@aipanel/client": "1.2.24",
60
60
  "@types/picomatch": "^4.0.0",
61
61
  "sharp": "^0.34.5"
62
62
  },