@weapp-vite/mcp 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4,13 +4,14 @@ import { Buffer } from 'node:buffer';
4
4
  import http from 'node:http';
5
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
6
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
7
- import fs$1 from 'node:fs/promises';
8
- import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
7
+ import { ResourceTemplate, McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
9
8
  import { z } from 'zod';
10
- import { createRequire } from 'node:module';
11
9
  import path from 'node:path';
12
10
  import fs from 'node:fs';
11
+ import fs$1 from 'node:fs/promises';
12
+ import { createRequire } from 'node:module';
13
13
  import { spawn } from 'node:child_process';
14
+ import { closeSharedMiniProgram, acquireSharedMiniProgram, releaseSharedMiniProgram } from '@weapp-vite/devtools-runtime';
14
15
 
15
16
  const MCP_SERVER_NAME = "@weapp-vite/mcp";
16
17
  const MCP_SERVER_VERSION = "2.0.0";
@@ -84,6 +85,118 @@ function resolveSubPath(root, relativePath) {
84
85
  return assertInsideRoot(root, path.resolve(root, relativePath));
85
86
  }
86
87
 
88
+ function registerServerPrompts(server, options) {
89
+ const { packageIds, packageIdSchema } = options;
90
+ server.registerPrompt("plan-weapp-vite-change", {
91
+ title: "Plan weapp-vite Change",
92
+ description: "\u6839\u636E\u53D8\u66F4\u76EE\u6807\u751F\u6210 weapp-vite / wevu \u4FEE\u6539\u8BA1\u5212\u63D0\u793A\u8BCD",
93
+ argsSchema: {
94
+ objective: z.string().min(1),
95
+ focusPackage: packageIdSchema.optional()
96
+ }
97
+ }, async ({ objective, focusPackage }) => {
98
+ const targets = focusPackage ? [focusPackage] : packageIds;
99
+ return {
100
+ messages: [
101
+ {
102
+ role: "user",
103
+ content: {
104
+ type: "text",
105
+ text: [
106
+ "\u4F60\u662F weapp-vite monorepo \u7EF4\u62A4\u8005\uFF0C\u8BF7\u7ED9\u51FA\u53EF\u6267\u884C\u7684\u6539\u9020\u8BA1\u5212\u3002",
107
+ `\u76EE\u6807\uFF1A${objective}`,
108
+ `\u805A\u7126\u5305\uFF1A${targets.join(", ")}`,
109
+ "\u8BF7\u5305\u542B\uFF1A\u5F71\u54CD\u9762\u3001\u98CE\u9669\u70B9\u3001\u6D4B\u8BD5\u7B56\u7565\u3001\u56DE\u6EDA\u7B56\u7565\u3002"
110
+ ].join("\n")
111
+ }
112
+ }
113
+ ]
114
+ };
115
+ });
116
+ server.registerPrompt("debug-wevu-runtime", {
117
+ title: "Debug wevu Runtime",
118
+ description: "\u7528\u4E8E\u5B9A\u4F4D wevu runtime \u751F\u547D\u5468\u671F/\u54CD\u5E94\u5F0F\u95EE\u9898\u7684\u6807\u51C6\u63D0\u793A\u8BCD",
119
+ argsSchema: {
120
+ symptom: z.string().min(1)
121
+ }
122
+ }, async ({ symptom }) => {
123
+ return {
124
+ messages: [
125
+ {
126
+ role: "user",
127
+ content: {
128
+ type: "text",
129
+ text: [
130
+ "\u8BF7\u57FA\u4E8E wevu runtime \u4EE3\u7801\u8DEF\u5F84\u8FDB\u884C\u5206\u5C42\u6392\u67E5\uFF1A",
131
+ "1. \u590D\u73B0\u573A\u666F\u4E0E\u6700\u5C0F\u6837\u4F8B",
132
+ "2. \u751F\u547D\u5468\u671F\u94A9\u5B50\u89E6\u53D1\u94FE",
133
+ "3. \u54CD\u5E94\u5F0F\u4E0E setData \u5DEE\u91CF\u540C\u6B65\u94FE",
134
+ "4. \u5355\u6D4B\u4E0E e2e \u56DE\u5F52\u8865\u4E01",
135
+ `\u73B0\u8C61\uFF1A${symptom}`
136
+ ].join("\n")
137
+ }
138
+ }
139
+ ]
140
+ };
141
+ });
142
+ server.registerPrompt("inspect-mini-program-page", {
143
+ title: "Inspect Mini Program Page",
144
+ description: "\u8FDE\u63A5\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u5E76\u68C0\u67E5\u5F53\u524D\u5C0F\u7A0B\u5E8F\u9875\u9762\u7684\u6807\u51C6\u6D41\u7A0B",
145
+ argsSchema: {
146
+ projectPath: z.string().min(1),
147
+ pagePath: z.string().optional(),
148
+ focus: z.string().optional()
149
+ }
150
+ }, async ({ projectPath, pagePath, focus }) => {
151
+ return {
152
+ messages: [
153
+ {
154
+ role: "user",
155
+ content: {
156
+ type: "text",
157
+ text: [
158
+ "\u8BF7\u6309\u987A\u5E8F\u4F7F\u7528 weapp-vite MCP runtime tools \u68C0\u67E5\u5C0F\u7A0B\u5E8F\u9875\u9762\uFF1A",
159
+ `1. \u8C03\u7528 weapp_devtools_connect\uFF0CprojectPath=${projectPath}`,
160
+ pagePath ? `2. \u8C03\u7528 weapp_devtools_route \u8DF3\u8F6C\u5230 ${pagePath}` : "2. \u8C03\u7528 weapp_devtools_active_page \u786E\u8BA4\u5F53\u524D\u9875\u9762",
161
+ "3. \u8C03\u7528 weapp_devtools_capture \u83B7\u53D6\u622A\u56FE",
162
+ "4. \u5982\u9700\u68C0\u67E5\u7ED3\u6784\uFF0C\u4F18\u5148\u8C03\u7528 weapp_runtime_find_node/weapp_runtime_find_nodes\uFF0C\u5FC5\u8981\u65F6\u8BBE\u7F6E withWxml=true",
163
+ "5. \u8C03\u7528 weapp_devtools_console \u68C0\u67E5 console/exception \u65E5\u5FD7",
164
+ focus ? `\u5173\u6CE8\u70B9\uFF1A${focus}` : "\u5173\u6CE8\u70B9\uFF1A\u9875\u9762\u662F\u5426\u6B63\u786E\u6E32\u67D3\u3001\u662F\u5426\u5B58\u5728\u8FD0\u884C\u65F6\u9519\u8BEF\u3002"
165
+ ].join("\n")
166
+ }
167
+ }
168
+ ]
169
+ };
170
+ });
171
+ server.registerPrompt("recover-mini-program-connection", {
172
+ title: "Recover Mini Program Connection",
173
+ description: "\u6062\u590D\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177 automator \u8FDE\u63A5\u7684\u6807\u51C6\u6D41\u7A0B",
174
+ argsSchema: {
175
+ projectPath: z.string().min(1),
176
+ lastError: z.string().optional()
177
+ }
178
+ }, async ({ projectPath, lastError }) => {
179
+ return {
180
+ messages: [
181
+ {
182
+ role: "user",
183
+ content: {
184
+ type: "text",
185
+ text: [
186
+ "\u8BF7\u6309\u987A\u5E8F\u6062\u590D weapp-vite MCP runtime \u8FDE\u63A5\uFF1A",
187
+ `1. \u8C03\u7528 weapp_devtools_connect\uFF0CprojectPath=${projectPath}\uFF0Creconnect=true`,
188
+ "2. \u5982\u679C\u4ECD\u5931\u8D25\uFF0C\u68C0\u67E5\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177\u662F\u5426\u5DF2\u5F00\u542F\u670D\u52A1\u7AEF\u53E3\u4E0E\u81EA\u52A8\u5316\u6D4B\u8BD5\u80FD\u529B",
189
+ "3. \u5982\u679C\u662F\u534F\u8BAE\u8D85\u65F6\uFF0C\u5173\u95ED\u591A\u4F59 DevTools \u7A97\u53E3\u540E\u53EA\u91CD\u8BD5\u4E00\u6B21",
190
+ "4. \u6062\u590D\u540E\u8C03\u7528 weapp_devtools_active_page \u548C weapp_devtools_console \u786E\u8BA4\u72B6\u6001",
191
+ lastError ? `\u4E0A\u4E00\u6B21\u9519\u8BEF\uFF1A${lastError}` : "\u4E0A\u4E00\u6B21\u9519\u8BEF\uFF1A\u672A\u63D0\u4F9B\u3002"
192
+ ].join("\n")
193
+ }
194
+ }
195
+ ]
196
+ };
197
+ });
198
+ }
199
+
87
200
  async function pathExists(filePath) {
88
201
  try {
89
202
  await fs$1.access(filePath);
@@ -195,87 +308,6 @@ async function resolveExposedPackages(workspaceRoot) {
195
308
  }).sort((a, b) => a.id.localeCompare(b.id));
196
309
  }
197
310
 
198
- async function loadPackageSummary(workspaceRoot, id) {
199
- return resolveExposedPackage(workspaceRoot, id);
200
- }
201
- async function loadExposedCatalog(workspaceRoot) {
202
- return resolveExposedPackages(workspaceRoot);
203
- }
204
-
205
- const ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
206
- "pnpm",
207
- "node",
208
- "git",
209
- "rg"
210
- ]);
211
- function resolveExecutable(command) {
212
- if (process.platform === "win32") {
213
- if (command === "pnpm") {
214
- return "pnpm.cmd";
215
- }
216
- if (command === "git") {
217
- return "git.exe";
218
- }
219
- if (command === "rg") {
220
- return "rg.exe";
221
- }
222
- }
223
- return command;
224
- }
225
- function truncateOutput(text, maxChars) {
226
- if (text.length <= maxChars) {
227
- return text;
228
- }
229
- return `${text.slice(0, maxChars)}
230
-
231
- [truncated: ${text.length - maxChars} chars omitted]`;
232
- }
233
- async function runCommand(workspaceRoot, command, args, options) {
234
- if (!ALLOWED_COMMANDS.has(command)) {
235
- throw new Error(`\u4E0D\u5141\u8BB8\u7684\u547D\u4EE4\uFF1A${command}`);
236
- }
237
- const cwd = resolveSubPath(workspaceRoot, options?.cwdRelative ?? ".");
238
- const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
239
- const maxOutputChars = options?.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
240
- const executable = resolveExecutable(command);
241
- const child = spawn(executable, args, {
242
- cwd,
243
- env: process.env,
244
- stdio: ["ignore", "pipe", "pipe"],
245
- shell: false
246
- });
247
- let stdout = "";
248
- let stderr = "";
249
- let timedOut = false;
250
- const timer = setTimeout(() => {
251
- timedOut = true;
252
- child.kill("SIGTERM");
253
- }, timeoutMs);
254
- child.stdout.on("data", (chunk) => {
255
- stdout += chunk.toString();
256
- });
257
- child.stderr.on("data", (chunk) => {
258
- stderr += chunk.toString();
259
- });
260
- const exitCode = await new Promise((resolve, reject) => {
261
- child.on("error", reject);
262
- child.on("close", (code) => {
263
- resolve(code ?? -1);
264
- });
265
- }).finally(() => {
266
- clearTimeout(timer);
267
- });
268
- return {
269
- command,
270
- args,
271
- cwd: path.resolve(cwd),
272
- exitCode,
273
- stdout: truncateOutput(stdout.trim(), maxOutputChars),
274
- stderr: truncateOutput(stderr.trim(), maxOutputChars),
275
- timedOut
276
- };
277
- }
278
-
279
311
  async function walkFilesRecursive(root, current, output, maxResults) {
280
312
  if (output.length >= maxResults) {
281
313
  return;
@@ -398,8 +430,80 @@ function toToolError(error) {
398
430
  };
399
431
  }
400
432
 
401
- const packageIds = Object.keys(EXPOSED_PACKAGES);
402
- const packageIdSchema = z.enum(packageIds);
433
+ const ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
434
+ "pnpm",
435
+ "node",
436
+ "git",
437
+ "rg"
438
+ ]);
439
+ function resolveExecutable(command) {
440
+ if (process.platform === "win32") {
441
+ if (command === "pnpm") {
442
+ return "pnpm.cmd";
443
+ }
444
+ if (command === "git") {
445
+ return "git.exe";
446
+ }
447
+ if (command === "rg") {
448
+ return "rg.exe";
449
+ }
450
+ }
451
+ return command;
452
+ }
453
+ function truncateOutput(text, maxChars) {
454
+ if (text.length <= maxChars) {
455
+ return text;
456
+ }
457
+ return `${text.slice(0, maxChars)}
458
+
459
+ [truncated: ${text.length - maxChars} chars omitted]`;
460
+ }
461
+ async function runCommand(workspaceRoot, command, args, options) {
462
+ if (!ALLOWED_COMMANDS.has(command)) {
463
+ throw new Error(`\u4E0D\u5141\u8BB8\u7684\u547D\u4EE4\uFF1A${command}`);
464
+ }
465
+ const cwd = resolveSubPath(workspaceRoot, options?.cwdRelative ?? ".");
466
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
467
+ const maxOutputChars = options?.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
468
+ const executable = resolveExecutable(command);
469
+ const child = spawn(executable, args, {
470
+ cwd,
471
+ env: process.env,
472
+ stdio: ["ignore", "pipe", "pipe"],
473
+ shell: false
474
+ });
475
+ let stdout = "";
476
+ let stderr = "";
477
+ let timedOut = false;
478
+ const timer = setTimeout(() => {
479
+ timedOut = true;
480
+ child.kill("SIGTERM");
481
+ }, timeoutMs);
482
+ child.stdout.on("data", (chunk) => {
483
+ stdout += chunk.toString();
484
+ });
485
+ child.stderr.on("data", (chunk) => {
486
+ stderr += chunk.toString();
487
+ });
488
+ const exitCode = await new Promise((resolve, reject) => {
489
+ child.on("error", reject);
490
+ child.on("close", (code) => {
491
+ resolve(code ?? -1);
492
+ });
493
+ }).finally(() => {
494
+ clearTimeout(timer);
495
+ });
496
+ return {
497
+ command,
498
+ args,
499
+ cwd: path.resolve(cwd),
500
+ exitCode,
501
+ stdout: truncateOutput(stdout.trim(), maxOutputChars),
502
+ stderr: truncateOutput(stderr.trim(), maxOutputChars),
503
+ timedOut
504
+ };
505
+ }
506
+
403
507
  async function resolvePackageRoot(workspaceRoot, packageId) {
404
508
  const resolved = await resolveExposedPackage(workspaceRoot, packageId);
405
509
  if (!resolved.sourceRoot) {
@@ -432,21 +536,1119 @@ async function runWeappViteCliTool(workspaceRoot, input) {
432
536
  timeoutMs: input.timeoutMs ?? DEFAULT_TIMEOUT_MS
433
537
  });
434
538
  }
435
- async function createWeappViteMcpServer(options) {
436
- const workspaceRoot = resolveWorkspaceRoot(options?.workspaceRoot);
437
- const server = new McpServer({
438
- name: MCP_SERVER_NAME,
439
- version: MCP_SERVER_VERSION
440
- });
441
- server.registerTool("workspace_catalog", {
539
+
540
+ async function registerServerResources(server, options) {
541
+ const { workspaceRoot, packageIds } = options;
542
+ server.registerResource("workspace-catalog", "weapp-vite://workspace/catalog", {
442
543
  title: "Workspace Catalog",
443
- description: "\u8BFB\u53D6 weapp-vite / wevu \u76F8\u5173\u5305\u76EE\u5F55\u4E0E\u811A\u672C\u80FD\u529B\u6E05\u5355"
444
- }, async () => {
544
+ description: "weapp-vite / wevu \u5305\u76EE\u5F55\u3001\u7248\u672C\u548C\u811A\u672C\u5217\u8868",
545
+ mimeType: "application/json"
546
+ }, async () => {
547
+ const catalog2 = await resolveExposedPackages(workspaceRoot);
548
+ const text = JSON.stringify({ workspaceRoot, packages: catalog2 }, null, 2);
549
+ return {
550
+ contents: [{
551
+ uri: "weapp-vite://workspace/catalog",
552
+ mimeType: "application/json",
553
+ text
554
+ }]
555
+ };
556
+ });
557
+ const catalog = await resolveExposedPackages(workspaceRoot);
558
+ for (const summary of catalog) {
559
+ if (summary.docs.readme) {
560
+ const uri = toDocsUri(summary.id, "README.md");
561
+ server.registerResource(`docs-${summary.id}-readme`, uri, {
562
+ title: `${summary.id} README`,
563
+ mimeType: "text/markdown"
564
+ }, async () => {
565
+ const text = await readTextFile(summary.docs.readme);
566
+ return {
567
+ contents: [{ uri, mimeType: "text/markdown", text }]
568
+ };
569
+ });
570
+ }
571
+ if (summary.docs.changelog) {
572
+ const uri = toDocsUri(summary.id, "CHANGELOG.md");
573
+ server.registerResource(`docs-${summary.id}-changelog`, uri, {
574
+ title: `${summary.id} CHANGELOG`,
575
+ mimeType: "text/markdown"
576
+ }, async () => {
577
+ const text = await readTextFile(summary.docs.changelog);
578
+ return {
579
+ contents: [{ uri, mimeType: "text/markdown", text }]
580
+ };
581
+ });
582
+ }
583
+ }
584
+ const sourceTemplate = new ResourceTemplate("weapp-vite://source/{package}?path={path}", {
585
+ list: void 0,
586
+ complete: {
587
+ package: () => packageIds
588
+ }
589
+ });
590
+ server.registerResource("source-template", sourceTemplate, {
591
+ title: "Source Template",
592
+ description: "\u8BFB\u53D6 weapp-vite / wevu \u4EFB\u610F\u6E90\u7801\u6587\u4EF6\uFF08\u901A\u8FC7 package + path \u53C2\u6570\uFF09",
593
+ mimeType: "text/plain"
594
+ }, async (uri, variables) => {
595
+ try {
596
+ const packageId = String(variables.package ?? "");
597
+ if (!packageIds.includes(packageId)) {
598
+ throw new Error(`\u672A\u77E5 package\uFF1A${packageId}`);
599
+ }
600
+ const relativePath = decodeURIComponent(String(variables.path ?? ""));
601
+ const packageRoot = await resolvePackageRoot(workspaceRoot, packageId);
602
+ const { content } = await readFileContent(packageRoot, relativePath, {
603
+ maxChars: DEFAULT_MAX_FILE_CHARS
604
+ });
605
+ return {
606
+ contents: [{
607
+ uri: uri.toString(),
608
+ mimeType: "text/plain",
609
+ text: content
610
+ }]
611
+ };
612
+ } catch (error) {
613
+ return {
614
+ contents: [{
615
+ uri: uri.toString(),
616
+ mimeType: "text/plain",
617
+ text: `[resource-error] ${normalizeErrorMessage(error)}`
618
+ }]
619
+ };
620
+ }
621
+ });
622
+ }
623
+
624
+ z.object({
625
+ projectPath: z.string().trim().min(1).describe("\u5C0F\u7A0B\u5E8F\u9879\u76EE\u8DEF\u5F84\uFF1B\u652F\u6301 workspaceRoot \u76F8\u5BF9\u8DEF\u5F84"),
626
+ timeout: z.number().int().positive().optional(),
627
+ preferOpenedSession: z.boolean().optional()
628
+ });
629
+ const connectionInputSchema = {
630
+ projectPath: z.string().trim().min(1).describe("\u5C0F\u7A0B\u5E8F\u9879\u76EE\u8DEF\u5F84\uFF1B\u652F\u6301 workspaceRoot \u76F8\u5BF9\u8DEF\u5F84"),
631
+ timeout: z.number().int().positive().optional(),
632
+ preferOpenedSession: z.boolean().optional()
633
+ };
634
+ class RuntimeSessionManager {
635
+ constructor(workspaceRoot, runtimeHooks = createUnavailableRuntimeHooks()) {
636
+ this.workspaceRoot = workspaceRoot;
637
+ this.runtimeHooks = runtimeHooks;
638
+ }
639
+ logs = [];
640
+ attachedSessions = /* @__PURE__ */ new Map();
641
+ maxLogs = 1e3;
642
+ async close(input) {
643
+ const projectPath = this.resolveProjectPath(input.projectPath);
644
+ this.detach(projectPath);
645
+ await closeSharedMiniProgram(projectPath);
646
+ }
647
+ clearLogs() {
648
+ this.logs.length = 0;
649
+ }
650
+ getLogs() {
651
+ return [...this.logs];
652
+ }
653
+ resolveProjectPath(projectPath) {
654
+ return path.isAbsolute(projectPath) ? path.normalize(projectPath) : path.resolve(this.workspaceRoot, projectPath);
655
+ }
656
+ resolveWorkspacePath(filePath) {
657
+ return path.isAbsolute(filePath) ? path.normalize(filePath) : path.resolve(this.workspaceRoot, filePath);
658
+ }
659
+ async withMiniProgram(input, runner) {
660
+ const projectPath = this.resolveProjectPath(input.projectPath);
661
+ const miniProgram = await acquireSharedMiniProgram(this.runtimeHooks, {
662
+ projectPath,
663
+ timeout: input.timeout,
664
+ preferOpenedSession: input.preferOpenedSession,
665
+ sharedSession: true
666
+ });
667
+ this.attach(projectPath, miniProgram);
668
+ try {
669
+ return await runner(miniProgram);
670
+ } catch (error) {
671
+ this.detach(projectPath);
672
+ await closeSharedMiniProgram(projectPath);
673
+ throw error;
674
+ } finally {
675
+ releaseSharedMiniProgram(projectPath);
676
+ }
677
+ }
678
+ async withPage(input, runner) {
679
+ return await this.withMiniProgram(input, async (miniProgram) => {
680
+ const page = await miniProgram.currentPage();
681
+ if (!page) {
682
+ throw new Error("\u5F53\u524D\u6CA1\u6709\u6D3B\u52A8\u9875\u9762\uFF0C\u8BF7\u5148\u8C03\u7528 weapp_devtools_connect \u786E\u8BA4 DevTools \u4F1A\u8BDD\u3002");
683
+ }
684
+ return await runner(page, miniProgram);
685
+ });
686
+ }
687
+ attach(projectPath, miniProgram) {
688
+ const existing = this.attachedSessions.get(projectPath);
689
+ if (existing?.miniProgram === miniProgram) {
690
+ return;
691
+ }
692
+ this.detach(projectPath);
693
+ const onConsole = (payload) => {
694
+ this.pushLog(normalizeConsoleEvent(payload));
695
+ };
696
+ const onException = (payload) => {
697
+ this.pushLog(normalizeExceptionEvent(payload));
698
+ };
699
+ miniProgram.on("console", onConsole);
700
+ miniProgram.on("exception", onException);
701
+ this.attachedSessions.set(projectPath, {
702
+ miniProgram,
703
+ onConsole,
704
+ onException
705
+ });
706
+ }
707
+ detach(projectPath) {
708
+ const attached = this.attachedSessions.get(projectPath);
709
+ if (!attached) {
710
+ return;
711
+ }
712
+ if (typeof attached.miniProgram.off === "function") {
713
+ attached.miniProgram.off("console", attached.onConsole);
714
+ attached.miniProgram.off("exception", attached.onException);
715
+ }
716
+ this.attachedSessions.delete(projectPath);
717
+ }
718
+ pushLog(entry) {
719
+ this.logs.push(entry);
720
+ while (this.logs.length > this.maxLogs) {
721
+ this.logs.shift();
722
+ }
723
+ }
724
+ }
725
+ function buildUrl(pagePath, query) {
726
+ const normalizedPath = pagePath.startsWith("/") ? pagePath : `/${pagePath}`;
727
+ if (!query || Object.keys(query).length === 0) {
728
+ return normalizedPath;
729
+ }
730
+ const search = new URLSearchParams(query).toString();
731
+ if (!search) {
732
+ return normalizedPath;
733
+ }
734
+ return `${normalizedPath}${normalizedPath.includes("?") ? "&" : "?"}${search}`;
735
+ }
736
+ function parseSelectorWithIndex(selector) {
737
+ const match = selector.match(/^(.+?)\[index=(\d+)\]$/);
738
+ if (!match?.[1] || !match[2]) {
739
+ return {
740
+ selector,
741
+ index: void 0
742
+ };
743
+ }
744
+ return {
745
+ selector: match[1],
746
+ index: Number.parseInt(match[2], 10)
747
+ };
748
+ }
749
+ async function resolveElement(page, selectorInput, innerSelector) {
750
+ const { selector, index } = parseSelectorWithIndex(selectorInput);
751
+ let element;
752
+ if (index === void 0) {
753
+ element = await page.$(selector);
754
+ } else {
755
+ const elements = await queryElements(page, selector);
756
+ if (index < 0 || index >= elements.length) {
757
+ throw new Error(`\u9009\u62E9\u5668 "${selector}" \u7684 index=${index} \u8D85\u51FA\u8303\u56F4\uFF0C\u5F53\u524D\u5339\u914D ${elements.length} \u4E2A\u5143\u7D20\u3002`);
758
+ }
759
+ element = elements[index];
760
+ }
761
+ if (!element) {
762
+ throw new Error(`\u672A\u627E\u5230\u5143\u7D20: ${selectorInput}`);
763
+ }
764
+ if (!innerSelector) {
765
+ return element;
766
+ }
767
+ const inner = await callOptionalMethod(element, "$", innerSelector);
768
+ if (!inner) {
769
+ throw new Error(`\u5728\u5143\u7D20 "${selectorInput}" \u5185\u672A\u627E\u5230\u5143\u7D20: ${innerSelector}`);
770
+ }
771
+ return inner;
772
+ }
773
+ async function queryElements(page, selectorInput) {
774
+ const { selector, index } = parseSelectorWithIndex(selectorInput);
775
+ const elements = await callOptionalMethod(page, "$$", selector);
776
+ if (!Array.isArray(elements)) {
777
+ return [];
778
+ }
779
+ if (index === void 0) {
780
+ return elements;
781
+ }
782
+ if (index < 0 || index >= elements.length) {
783
+ throw new Error(`\u9009\u62E9\u5668 "${selector}" \u7684 index=${index} \u8D85\u51FA\u8303\u56F4\uFF0C\u5F53\u524D\u5339\u914D ${elements.length} \u4E2A\u5143\u7D20\u3002`);
784
+ }
785
+ return [elements[index]];
786
+ }
787
+ async function summarizeElement(element, withWxml = false) {
788
+ const [text, value, size, offset, scrollWidth, scrollHeight, outerWxml] = await Promise.all([
789
+ callMaybe(element, "text"),
790
+ callMaybe(element, "value"),
791
+ callMaybe(element, "size"),
792
+ callMaybe(element, "offset"),
793
+ callMaybe(element, "scrollWidth"),
794
+ callMaybe(element, "scrollHeight"),
795
+ withWxml ? callMaybe(element, "outerWxml") : Promise.resolve(void 0)
796
+ ]);
797
+ return compactObject({
798
+ tagName: readProperty(element, "tagName"),
799
+ text,
800
+ value,
801
+ size,
802
+ offset,
803
+ scrollWidth,
804
+ scrollHeight,
805
+ outerWxml
806
+ });
807
+ }
808
+ function toSerializableValue(value) {
809
+ if (value == null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
810
+ return value;
811
+ }
812
+ if (typeof value === "bigint") {
813
+ return value.toString();
814
+ }
815
+ if (value instanceof Date) {
816
+ return value.toISOString();
817
+ }
818
+ if (Buffer.isBuffer(value)) {
819
+ return value.toString("base64");
820
+ }
821
+ if (Array.isArray(value)) {
822
+ return value.map((item) => toSerializableValue(item));
823
+ }
824
+ if (typeof value === "object") {
825
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, toSerializableValue(item)]));
826
+ }
827
+ return String(value);
828
+ }
829
+ async function callRequiredMethod(target, methodName, ...args) {
830
+ const method = readProperty(target, methodName);
831
+ if (typeof method !== "function") {
832
+ throw new TypeError(`\u5F53\u524D\u5BF9\u8C61\u4E0D\u652F\u6301 ${methodName} \u65B9\u6CD5\u3002`);
833
+ }
834
+ return await method.apply(target, args);
835
+ }
836
+ async function callOptionalMethod(target, methodName, ...args) {
837
+ const method = readProperty(target, methodName);
838
+ if (typeof method !== "function") {
839
+ return void 0;
840
+ }
841
+ return await method.apply(target, args);
842
+ }
843
+ async function callMaybe(target, methodName, ...args) {
844
+ try {
845
+ return await callOptionalMethod(target, methodName, ...args);
846
+ } catch {
847
+ return void 0;
848
+ }
849
+ }
850
+ function compactObject(input) {
851
+ return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
852
+ }
853
+ function readProperty(target, key) {
854
+ if (!target || typeof target !== "object") {
855
+ return void 0;
856
+ }
857
+ return target[key];
858
+ }
859
+ function normalizeConsoleEvent(payload) {
860
+ const record = toRecord$1(payload);
861
+ return {
862
+ level: normalizeLogLevel(record.type ?? record.level ?? record.method),
863
+ message: resolveLogMessage(record, payload),
864
+ timestamp: Date.now(),
865
+ raw: toSerializableValue(payload)
866
+ };
867
+ }
868
+ function normalizeExceptionEvent(payload) {
869
+ const record = toRecord$1(payload);
870
+ const error = toRecord$1(record.error);
871
+ const message = [
872
+ typeof error.message === "string" ? error.message : void 0,
873
+ typeof record.message === "string" ? record.message : void 0,
874
+ typeof error.stack === "string" ? error.stack : void 0,
875
+ typeof record.stack === "string" ? record.stack : void 0
876
+ ].filter(Boolean).join("\n");
877
+ return {
878
+ level: "error",
879
+ message: message || JSON.stringify(toSerializableValue(payload)),
880
+ timestamp: Date.now(),
881
+ raw: toSerializableValue(payload)
882
+ };
883
+ }
884
+ function normalizeLogLevel(value) {
885
+ const normalized = String(value ?? "log").toLowerCase();
886
+ if (normalized === "warning") {
887
+ return "warn";
888
+ }
889
+ if (["debug", "log", "info", "warn", "error"].includes(normalized)) {
890
+ return normalized;
891
+ }
892
+ return "log";
893
+ }
894
+ function resolveLogMessage(record, payload) {
895
+ if (typeof record.text === "string" && record.text) {
896
+ return record.text;
897
+ }
898
+ if (typeof record.message === "string" && record.message) {
899
+ return record.message;
900
+ }
901
+ if (Array.isArray(record.args)) {
902
+ return record.args.map(formatLogArgument).join(" ");
903
+ }
904
+ if (typeof payload === "string") {
905
+ return payload;
906
+ }
907
+ return JSON.stringify(toSerializableValue(payload));
908
+ }
909
+ function formatLogArgument(value) {
910
+ const record = toRecord$1(value);
911
+ if (record.value !== void 0) {
912
+ return typeof record.value === "string" ? record.value : JSON.stringify(toSerializableValue(record.value));
913
+ }
914
+ if (typeof record.description === "string") {
915
+ return record.description;
916
+ }
917
+ return typeof value === "string" ? value : JSON.stringify(toSerializableValue(value));
918
+ }
919
+ function toRecord$1(value) {
920
+ return value && typeof value === "object" ? value : {};
921
+ }
922
+ function createUnavailableRuntimeHooks() {
923
+ return {
924
+ async connectMiniProgram() {
925
+ throw new Error("\u672A\u914D\u7F6E DevTools runtime hooks\u3002\u8BF7\u901A\u8FC7 createWeappViteMcpServer({ runtimeHooks }) \u6CE8\u5165 automator \u8FDE\u63A5\u80FD\u529B\u3002");
926
+ }
927
+ };
928
+ }
929
+
930
+ const navigateSchema = {
931
+ ...connectionInputSchema,
932
+ path: z.string().trim().min(1).optional(),
933
+ query: z.record(z.string(), z.string()).optional(),
934
+ transition: z.enum(["navigateTo", "redirectTo", "reLaunch", "switchTab", "navigateBack"]).optional(),
935
+ waitMs: z.number().int().nonnegative().optional()
936
+ };
937
+ const screenshotSchema = {
938
+ ...connectionInputSchema,
939
+ outputPath: z.string().trim().min(1).optional()
940
+ };
941
+ const hostApiSchema = {
942
+ ...connectionInputSchema,
943
+ method: z.string().trim().min(1),
944
+ args: z.array(z.unknown()).optional()
945
+ };
946
+ function registerDevtoolsRuntimeTools(server, manager) {
947
+ server.registerTool("weapp_devtools_connect", {
948
+ title: "Ensure Mini Program Connection",
949
+ description: "\u786E\u4FDD\u5FAE\u4FE1\u5F00\u53D1\u8005\u5DE5\u5177 automator \u4F1A\u8BDD\u53EF\u7528\uFF0C\u5E76\u8FD4\u56DE\u5F53\u524D\u9875\u9762\u4E0E\u7CFB\u7EDF\u4FE1\u606F\u3002",
950
+ inputSchema: {
951
+ ...connectionInputSchema,
952
+ reconnect: z.boolean().optional()
953
+ }
954
+ }, async ({ reconnect, ...connection }) => {
955
+ try {
956
+ if (reconnect) {
957
+ await manager.close(connection);
958
+ }
959
+ const result = await manager.withMiniProgram(connection, async (miniProgram) => {
960
+ const page = await miniProgram.currentPage().catch(() => null);
961
+ const systemInfo = await miniProgram.systemInfo().catch(() => null);
962
+ return {
963
+ connected: true,
964
+ projectPath: connection.projectPath,
965
+ resolvedProjectPath: manager.resolveProjectPath(connection.projectPath),
966
+ currentPage: page ? { path: page.path, query: toSerializableValue(page.query) } : null,
967
+ systemInfo: toSerializableValue(systemInfo)
968
+ };
969
+ });
970
+ return toToolResult(result);
971
+ } catch (error) {
972
+ return toToolError(error);
973
+ }
974
+ });
975
+ server.registerTool("weapp_devtools_route", {
976
+ title: "Navigate Mini Program",
977
+ description: "\u5728\u5C0F\u7A0B\u5E8F\u5185\u6267\u884C navigateTo\u3001redirectTo\u3001reLaunch\u3001switchTab \u6216 navigateBack\u3002",
978
+ inputSchema: navigateSchema
979
+ }, async ({ path: pagePath, query, transition = "navigateTo", waitMs, ...connection }) => {
980
+ try {
981
+ const result = await manager.withMiniProgram(connection, async (miniProgram) => {
982
+ if (transition === "navigateBack") {
983
+ const page2 = await miniProgram.navigateBack();
984
+ if (waitMs && page2) {
985
+ await page2.waitFor(waitMs);
986
+ }
987
+ return {
988
+ transition,
989
+ activePage: page2 ? { path: page2.path, query: toSerializableValue(page2.query) } : null
990
+ };
991
+ }
992
+ if (!pagePath) {
993
+ throw new Error("transition \u4E0D\u662F navigateBack \u65F6\u5FC5\u987B\u63D0\u4F9B path\u3002");
994
+ }
995
+ const url = buildUrl(pagePath, query);
996
+ const page = await callRequiredMethod(
997
+ miniProgram,
998
+ transition,
999
+ url
1000
+ );
1001
+ if (waitMs && page) {
1002
+ await page.waitFor(waitMs);
1003
+ }
1004
+ return {
1005
+ transition,
1006
+ url,
1007
+ activePage: page ? { path: page.path, query: toSerializableValue(page.query) } : null
1008
+ };
1009
+ });
1010
+ return toToolResult(result);
1011
+ } catch (error) {
1012
+ return toToolError(error);
1013
+ }
1014
+ });
1015
+ server.registerTool("weapp_devtools_active_page", {
1016
+ title: "Current Mini Program Page",
1017
+ description: "\u83B7\u53D6\u5F53\u524D\u9875\u9762\u8DEF\u5F84\u3001\u67E5\u8BE2\u53C2\u6570\u3001\u5C3A\u5BF8\u3001\u6EDA\u52A8\u4F4D\u7F6E\uFF1B\u53EF\u9009\u8FD4\u56DE\u9875\u9762 data\u3002",
1018
+ inputSchema: {
1019
+ ...connectionInputSchema,
1020
+ withData: z.boolean().optional()
1021
+ }
1022
+ }, async ({ withData, ...connection }) => {
1023
+ try {
1024
+ const result = await manager.withPage(connection, async (page) => {
1025
+ const [size, scrollTop, data] = await Promise.all([
1026
+ page.size().catch(() => null),
1027
+ page.scrollTop().catch(() => null),
1028
+ withData ? page.data().catch(() => null) : Promise.resolve(void 0)
1029
+ ]);
1030
+ return compactObject({
1031
+ path: page.path,
1032
+ query: toSerializableValue(page.query),
1033
+ size: toSerializableValue(size),
1034
+ scrollTop: toSerializableValue(scrollTop),
1035
+ data: toSerializableValue(data)
1036
+ });
1037
+ });
1038
+ return toToolResult(result);
1039
+ } catch (error) {
1040
+ return toToolError(error);
1041
+ }
1042
+ });
1043
+ server.registerTool("weapp_devtools_page_stack", {
1044
+ title: "Mini Program Page Stack",
1045
+ description: "\u83B7\u53D6\u5F53\u524D\u5C0F\u7A0B\u5E8F\u9875\u9762\u6808\u3002",
1046
+ inputSchema: connectionInputSchema
1047
+ }, async (connection) => {
1048
+ try {
1049
+ const result = await manager.withMiniProgram(connection, async (miniProgram) => {
1050
+ const stack = await miniProgram.pageStack();
1051
+ return stack.map((page) => ({
1052
+ path: page.path,
1053
+ query: toSerializableValue(page.query)
1054
+ }));
1055
+ });
1056
+ return toToolResult(result);
1057
+ } catch (error) {
1058
+ return toToolError(error);
1059
+ }
1060
+ });
1061
+ server.registerTool("weapp_devtools_capture", {
1062
+ title: "Mini Program Screenshot",
1063
+ description: "\u622A\u53D6\u5F53\u524D\u5C0F\u7A0B\u5E8F\u89C6\u53E3\uFF0C\u8FD4\u56DE base64\uFF0C\u6216\u4FDD\u5B58\u5230 workspaceRoot \u76F8\u5BF9 outputPath\u3002",
1064
+ inputSchema: screenshotSchema
1065
+ }, async ({ outputPath, ...connection }) => {
1066
+ try {
1067
+ const result = await manager.withMiniProgram(connection, async (miniProgram) => {
1068
+ const screenshot = await miniProgram.screenshot();
1069
+ const buffer = typeof screenshot === "string" ? Buffer.from(screenshot, "base64") : Buffer.from(screenshot);
1070
+ if (outputPath) {
1071
+ const resolvedOutputPath = manager.resolveWorkspacePath(outputPath);
1072
+ await fs$1.mkdir(path.dirname(resolvedOutputPath), { recursive: true });
1073
+ await fs$1.writeFile(resolvedOutputPath, buffer);
1074
+ return {
1075
+ path: resolvedOutputPath,
1076
+ bytes: buffer.length
1077
+ };
1078
+ }
1079
+ return {
1080
+ base64: buffer.toString("base64"),
1081
+ bytes: buffer.length
1082
+ };
1083
+ });
1084
+ return toToolResult(result);
1085
+ } catch (error) {
1086
+ return toToolError(error);
1087
+ }
1088
+ });
1089
+ server.registerTool("weapp_devtools_host_api", {
1090
+ title: "Call wx Method",
1091
+ description: "\u8C03\u7528\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F wx API\uFF0C\u4F8B\u5982 wx.pageScrollTo\u3002",
1092
+ inputSchema: hostApiSchema
1093
+ }, async ({ method, args, ...connection }) => {
1094
+ try {
1095
+ const result = await manager.withMiniProgram(connection, async (miniProgram) => {
1096
+ const callArgs = args ?? [];
1097
+ const callResult = await miniProgram.callWxMethod(method, ...callArgs);
1098
+ return {
1099
+ method,
1100
+ args: toSerializableValue(callArgs),
1101
+ result: toSerializableValue(callResult)
1102
+ };
1103
+ });
1104
+ return toToolResult(result);
1105
+ } catch (error) {
1106
+ return toToolError(error);
1107
+ }
1108
+ });
1109
+ server.registerTool("weapp_devtools_console", {
1110
+ title: "Get Mini Program Logs",
1111
+ description: "\u8BFB\u53D6 MCP \u4F1A\u8BDD\u6355\u83B7\u5230\u7684\u5C0F\u7A0B\u5E8F console/exception \u65E5\u5FD7\uFF1B\u53EF\u9009\u8BFB\u53D6\u540E\u6E05\u7A7A\u3002",
1112
+ inputSchema: {
1113
+ clear: z.boolean().optional()
1114
+ }
1115
+ }, async ({ clear }) => {
1116
+ try {
1117
+ const logs = manager.getLogs();
1118
+ if (clear) {
1119
+ manager.clearLogs();
1120
+ }
1121
+ return toToolResult({
1122
+ count: logs.length,
1123
+ logs
1124
+ });
1125
+ } catch (error) {
1126
+ return toToolError(error);
1127
+ }
1128
+ });
1129
+ }
1130
+
1131
+ const elementSelectorSchema = {
1132
+ ...connectionInputSchema,
1133
+ selector: z.string().trim().min(1),
1134
+ innerSelector: z.string().trim().min(1).optional()
1135
+ };
1136
+ function registerRuntimeNodeTools(server, manager) {
1137
+ server.registerTool("weapp_runtime_tap_node", {
1138
+ title: "Tap Element",
1139
+ description: "\u70B9\u51FB\u9875\u9762\u5143\u7D20\uFF0C\u652F\u6301 selector[index=N]\u3001innerSelector \u548C\u70B9\u51FB\u540E\u7B49\u5F85\u3002",
1140
+ inputSchema: {
1141
+ ...elementSelectorSchema,
1142
+ waitMs: z.number().int().nonnegative().optional()
1143
+ }
1144
+ }, async ({ selector, innerSelector, waitMs, ...connection }) => {
1145
+ try {
1146
+ const result = await manager.withPage(connection, async (page) => {
1147
+ const element = await resolveElement(page, selector, innerSelector);
1148
+ await element.tap();
1149
+ if (waitMs) {
1150
+ await page.waitFor(waitMs);
1151
+ }
1152
+ return {
1153
+ selector,
1154
+ innerSelector: innerSelector ?? null,
1155
+ waitedMs: waitMs ?? 0
1156
+ };
1157
+ });
1158
+ return toToolResult(result);
1159
+ } catch (error) {
1160
+ return toToolError(error);
1161
+ }
1162
+ });
1163
+ server.registerTool("weapp_runtime_input_node", {
1164
+ title: "Input Element Text",
1165
+ description: "\u5411 input \u6216 textarea \u5143\u7D20\u8F93\u5165\u6587\u672C\u3002",
1166
+ inputSchema: {
1167
+ ...elementSelectorSchema,
1168
+ value: z.union([z.string(), z.number()])
1169
+ }
1170
+ }, async ({ selector, innerSelector, value, ...connection }) => {
1171
+ try {
1172
+ const result = await manager.withPage(connection, async (page) => {
1173
+ const element = await resolveElement(page, selector, innerSelector);
1174
+ await callRequiredMethod(element, "input", String(value));
1175
+ return {
1176
+ selector,
1177
+ innerSelector: innerSelector ?? null,
1178
+ value: String(value)
1179
+ };
1180
+ });
1181
+ return toToolResult(result);
1182
+ } catch (error) {
1183
+ return toToolError(error);
1184
+ }
1185
+ });
1186
+ server.registerTool("weapp_runtime_invoke_component", {
1187
+ title: "Call Element Method",
1188
+ description: "\u8C03\u7528\u81EA\u5B9A\u4E49\u7EC4\u4EF6\u5143\u7D20\u5B9E\u4F8B\u65B9\u6CD5\u3002",
1189
+ inputSchema: {
1190
+ ...elementSelectorSchema,
1191
+ method: z.string().trim().min(1),
1192
+ args: z.array(z.unknown()).optional()
1193
+ }
1194
+ }, async ({ selector, innerSelector, method, args, ...connection }) => {
1195
+ try {
1196
+ const result = await manager.withPage(connection, async (page) => {
1197
+ const element = await resolveElement(page, selector, innerSelector);
1198
+ const callArgs = args ?? [];
1199
+ return {
1200
+ selector,
1201
+ innerSelector: innerSelector ?? null,
1202
+ method,
1203
+ args: toSerializableValue(callArgs),
1204
+ result: toSerializableValue(await callRequiredMethod(element, method, ...callArgs))
1205
+ };
1206
+ });
1207
+ return toToolResult(result);
1208
+ } catch (error) {
1209
+ return toToolError(error);
1210
+ }
1211
+ });
1212
+ server.registerTool("weapp_runtime_component_state", {
1213
+ title: "Get Element Data",
1214
+ description: "\u8BFB\u53D6\u81EA\u5B9A\u4E49\u7EC4\u4EF6\u5143\u7D20 data\uFF0C\u53EF\u901A\u8FC7 path \u8BFB\u53D6\u5D4C\u5957\u5B57\u6BB5\u3002",
1215
+ inputSchema: {
1216
+ ...elementSelectorSchema,
1217
+ path: z.string().trim().min(1).optional()
1218
+ }
1219
+ }, async ({ selector, innerSelector, path, ...connection }) => {
1220
+ try {
1221
+ const result = await manager.withPage(connection, async (page) => {
1222
+ const element = await resolveElement(page, selector, innerSelector);
1223
+ return {
1224
+ selector,
1225
+ innerSelector: innerSelector ?? null,
1226
+ path: path ?? null,
1227
+ data: toSerializableValue(await callRequiredMethod(element, "data", path))
1228
+ };
1229
+ });
1230
+ return toToolResult(result);
1231
+ } catch (error) {
1232
+ return toToolError(error);
1233
+ }
1234
+ });
1235
+ server.registerTool("weapp_runtime_update_component_state", {
1236
+ title: "Set Element Data",
1237
+ description: "\u8C03\u7528\u81EA\u5B9A\u4E49\u7EC4\u4EF6\u5143\u7D20 setData \u66F4\u65B0 data\u3002",
1238
+ inputSchema: {
1239
+ ...elementSelectorSchema,
1240
+ data: z.record(z.string(), z.unknown())
1241
+ }
1242
+ }, async ({ selector, innerSelector, data, ...connection }) => {
1243
+ try {
1244
+ const result = await manager.withPage(connection, async (page) => {
1245
+ const element = await resolveElement(page, selector, innerSelector);
1246
+ await callRequiredMethod(element, "setData", data);
1247
+ return {
1248
+ selector,
1249
+ innerSelector: innerSelector ?? null,
1250
+ keys: Object.keys(data)
1251
+ };
1252
+ });
1253
+ return toToolResult(result);
1254
+ } catch (error) {
1255
+ return toToolError(error);
1256
+ }
1257
+ });
1258
+ server.registerTool("weapp_runtime_find_child", {
1259
+ title: "Get Inner Element",
1260
+ description: "\u5728\u5143\u7D20\u8303\u56F4\u5185\u67E5\u8BE2\u5355\u4E2A\u5185\u90E8\u5143\u7D20\u3002",
1261
+ inputSchema: {
1262
+ ...elementSelectorSchema,
1263
+ targetSelector: z.string().trim().min(1),
1264
+ withWxml: z.boolean().optional()
1265
+ }
1266
+ }, async ({ selector, innerSelector, targetSelector, withWxml, ...connection }) => {
1267
+ try {
1268
+ const result = await manager.withPage(connection, async (page) => {
1269
+ const parent = await resolveElement(page, selector, innerSelector);
1270
+ const element = await callRequiredMethod(parent, "$", targetSelector);
1271
+ if (!element) {
1272
+ throw new Error(`\u5728\u5143\u7D20 "${selector}" \u5185\u672A\u627E\u5230\u5143\u7D20: ${targetSelector}`);
1273
+ }
1274
+ return {
1275
+ selector,
1276
+ innerSelector: innerSelector ?? null,
1277
+ targetSelector,
1278
+ ...await summarizeElement(element, withWxml)
1279
+ };
1280
+ });
1281
+ return toToolResult(result);
1282
+ } catch (error) {
1283
+ return toToolError(error);
1284
+ }
1285
+ });
1286
+ server.registerTool("weapp_runtime_find_children", {
1287
+ title: "Get Inner Elements",
1288
+ description: "\u5728\u5143\u7D20\u8303\u56F4\u5185\u67E5\u8BE2\u5185\u90E8\u5143\u7D20\u6570\u7EC4\u3002",
1289
+ inputSchema: {
1290
+ ...elementSelectorSchema,
1291
+ targetSelector: z.string().trim().min(1),
1292
+ withWxml: z.boolean().optional()
1293
+ }
1294
+ }, async ({ selector, innerSelector, targetSelector, withWxml, ...connection }) => {
1295
+ try {
1296
+ const result = await manager.withPage(connection, async (page) => {
1297
+ const parent = await resolveElement(page, selector, innerSelector);
1298
+ const elements = await callRequiredMethod(parent, "$$", targetSelector);
1299
+ if (!Array.isArray(elements)) {
1300
+ throw new TypeError(`\u5728\u5143\u7D20 "${selector}" \u5185\u67E5\u8BE2 "${targetSelector}" \u5931\u8D25\u3002`);
1301
+ }
1302
+ return {
1303
+ selector,
1304
+ innerSelector: innerSelector ?? null,
1305
+ targetSelector,
1306
+ count: elements.length,
1307
+ elements: await Promise.all(elements.map(async (element, index) => ({
1308
+ index,
1309
+ ...await summarizeElement(element, withWxml)
1310
+ })))
1311
+ };
1312
+ });
1313
+ return toToolResult(result);
1314
+ } catch (error) {
1315
+ return toToolError(error);
1316
+ }
1317
+ });
1318
+ server.registerTool("weapp_runtime_node_markup", {
1319
+ title: "Get Element WXML",
1320
+ description: "\u8BFB\u53D6\u5143\u7D20 inner WXML \u6216 outer WXML\u3002",
1321
+ inputSchema: {
1322
+ ...elementSelectorSchema,
1323
+ outer: z.boolean().optional()
1324
+ }
1325
+ }, async ({ selector, innerSelector, outer, ...connection }) => {
1326
+ try {
1327
+ const result = await manager.withPage(connection, async (page) => {
1328
+ const element = await resolveElement(page, selector, innerSelector);
1329
+ const method = outer ? "outerWxml" : "wxml";
1330
+ return {
1331
+ selector,
1332
+ innerSelector: innerSelector ?? null,
1333
+ type: method,
1334
+ wxml: toSerializableValue(await callRequiredMethod(element, method))
1335
+ };
1336
+ });
1337
+ return toToolResult(result);
1338
+ } catch (error) {
1339
+ return toToolError(error);
1340
+ }
1341
+ });
1342
+ server.registerTool("weapp_runtime_node_styles", {
1343
+ title: "Get Element Styles",
1344
+ description: "\u8BFB\u53D6\u5143\u7D20\u6837\u5F0F\u503C\u3002",
1345
+ inputSchema: {
1346
+ ...elementSelectorSchema,
1347
+ names: z.array(z.string().trim().min(1)).min(1)
1348
+ }
1349
+ }, async ({ selector, innerSelector, names, ...connection }) => {
1350
+ try {
1351
+ const result = await manager.withPage(connection, async (page) => {
1352
+ const element = await resolveElement(page, selector, innerSelector);
1353
+ return {
1354
+ selector,
1355
+ innerSelector: innerSelector ?? null,
1356
+ styles: Object.fromEntries(await Promise.all(names.map(async (name) => [
1357
+ name,
1358
+ toSerializableValue(await callMaybe(element, "style", name))
1359
+ ])))
1360
+ };
1361
+ });
1362
+ return toToolResult(result);
1363
+ } catch (error) {
1364
+ return toToolError(error);
1365
+ }
1366
+ });
1367
+ server.registerTool("weapp_runtime_node_attrs", {
1368
+ title: "Get Element Attributes",
1369
+ description: "\u8BFB\u53D6\u5143\u7D20 attribute \u503C\u3002",
1370
+ inputSchema: {
1371
+ ...elementSelectorSchema,
1372
+ names: z.array(z.string().trim().min(1)).min(1)
1373
+ }
1374
+ }, async ({ selector, innerSelector, names, ...connection }) => {
1375
+ try {
1376
+ const result = await manager.withPage(connection, async (page) => {
1377
+ const element = await resolveElement(page, selector, innerSelector);
1378
+ return {
1379
+ selector,
1380
+ innerSelector: innerSelector ?? null,
1381
+ attributes: Object.fromEntries(await Promise.all(names.map(async (name) => [
1382
+ name,
1383
+ toSerializableValue(await callMaybe(element, "attribute", name))
1384
+ ])))
1385
+ };
1386
+ });
1387
+ return toToolResult(result);
1388
+ } catch (error) {
1389
+ return toToolError(error);
1390
+ }
1391
+ });
1392
+ server.registerTool("weapp_runtime_scroll_node", {
1393
+ title: "Scroll Element",
1394
+ description: "\u6EDA\u52A8 scroll-view \u5143\u7D20\u5230\u6307\u5B9A\u4F4D\u7F6E\u3002",
1395
+ inputSchema: {
1396
+ ...elementSelectorSchema,
1397
+ x: z.number(),
1398
+ y: z.number()
1399
+ }
1400
+ }, async ({ selector, innerSelector, x, y, ...connection }) => {
1401
+ try {
1402
+ const result = await manager.withPage(connection, async (page) => {
1403
+ const element = await resolveElement(page, selector, innerSelector);
1404
+ await callRequiredMethod(element, "scrollTo", x, y);
1405
+ return {
1406
+ selector,
1407
+ innerSelector: innerSelector ?? null,
1408
+ x,
1409
+ y
1410
+ };
1411
+ });
1412
+ return toToolResult(result);
1413
+ } catch (error) {
1414
+ return toToolError(error);
1415
+ }
1416
+ });
1417
+ server.registerTool("weapp_runtime_measure_node", {
1418
+ title: "Get Element Bounding Rect",
1419
+ description: "\u8BFB\u53D6\u5143\u7D20\u89C6\u53E3\u77E9\u5F62\uFF1B\u4F18\u5148\u4F7F\u7528\u5143\u7D20 offset/size\uFF0C\u7F3A\u5931\u65F6\u8FD4\u56DE\u53EF\u7528\u5B57\u6BB5\u3002",
1420
+ inputSchema: elementSelectorSchema
1421
+ }, async ({ selector, innerSelector, ...connection }) => {
1422
+ try {
1423
+ const result = await manager.withPage(connection, async (page) => {
1424
+ const element = await resolveElement(page, selector, innerSelector);
1425
+ const offset = toRecord(await callMaybe(element, "offset"));
1426
+ const size = toRecord(await callMaybe(element, "size"));
1427
+ const left = toNumber(offset.left);
1428
+ const top = toNumber(offset.top);
1429
+ const width = toNumber(size.width ?? offset.width);
1430
+ const height = toNumber(size.height ?? offset.height);
1431
+ return {
1432
+ selector,
1433
+ innerSelector: innerSelector ?? null,
1434
+ boundingClientRect: compactObject({
1435
+ left,
1436
+ top,
1437
+ width,
1438
+ height,
1439
+ right: left !== void 0 && width !== void 0 ? left + width : void 0,
1440
+ bottom: top !== void 0 && height !== void 0 ? top + height : void 0,
1441
+ rawOffset: toSerializableValue(offset),
1442
+ rawSize: toSerializableValue(size),
1443
+ tagName: readProperty(element, "tagName")
1444
+ })
1445
+ };
1446
+ });
1447
+ return toToolResult(result);
1448
+ } catch (error) {
1449
+ return toToolError(error);
1450
+ }
1451
+ });
1452
+ }
1453
+ function toRecord(value) {
1454
+ return value && typeof value === "object" ? value : {};
1455
+ }
1456
+ function toNumber(value) {
1457
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
1458
+ }
1459
+
1460
+ const selectorSchema = {
1461
+ ...connectionInputSchema,
1462
+ selector: z.string().trim().min(1),
1463
+ innerSelector: z.string().trim().min(1).optional(),
1464
+ withWxml: z.boolean().optional()
1465
+ };
1466
+ function registerRuntimePageTools(server, manager) {
1467
+ server.registerTool("weapp_runtime_find_node", {
1468
+ title: "Get Page Element",
1469
+ description: "\u901A\u8FC7\u9009\u62E9\u5668\u83B7\u53D6\u5F53\u524D\u9875\u9762\u5143\u7D20\u6458\u8981\uFF0C\u652F\u6301 selector[index=N]\u3001innerSelector \u4E0E withWxml\u3002",
1470
+ inputSchema: selectorSchema
1471
+ }, async ({ selector, innerSelector, withWxml, ...connection }) => {
1472
+ try {
1473
+ const result = await manager.withPage(connection, async (page) => {
1474
+ const element = await resolveElement(page, selector, innerSelector);
1475
+ return {
1476
+ selector,
1477
+ innerSelector: innerSelector ?? null,
1478
+ ...await summarizeElement(element, withWxml)
1479
+ };
1480
+ });
1481
+ return toToolResult(result);
1482
+ } catch (error) {
1483
+ return toToolError(error);
1484
+ }
1485
+ });
1486
+ server.registerTool("weapp_runtime_find_nodes", {
1487
+ title: "Get Page Elements",
1488
+ description: "\u901A\u8FC7\u9009\u62E9\u5668\u83B7\u53D6\u5F53\u524D\u9875\u9762\u5143\u7D20\u6570\u7EC4\u6458\u8981\uFF0C\u652F\u6301 selector[index=N] \u4E0E withWxml\u3002",
1489
+ inputSchema: {
1490
+ ...connectionInputSchema,
1491
+ selector: z.string().trim().min(1),
1492
+ withWxml: z.boolean().optional()
1493
+ }
1494
+ }, async ({ selector, withWxml, ...connection }) => {
1495
+ try {
1496
+ const result = await manager.withPage(connection, async (page) => {
1497
+ const elements = await queryElements(page, selector);
1498
+ return {
1499
+ selector,
1500
+ count: elements.length,
1501
+ elements: await Promise.all(elements.map(async (element, index) => ({
1502
+ index,
1503
+ ...await summarizeElement(element, withWxml)
1504
+ })))
1505
+ };
1506
+ });
1507
+ return toToolResult(result);
1508
+ } catch (error) {
1509
+ return toToolError(error);
1510
+ }
1511
+ });
1512
+ server.registerTool("weapp_runtime_wait_node", {
1513
+ title: "Wait Page Element",
1514
+ description: "\u8F6E\u8BE2\u7B49\u5F85\u5143\u7D20\u51FA\u73B0\uFF0C\u652F\u6301 selector[index=N]\u3002",
1515
+ inputSchema: {
1516
+ ...connectionInputSchema,
1517
+ selector: z.string().trim().min(1),
1518
+ timeoutMs: z.number().int().positive().optional(),
1519
+ intervalMs: z.number().int().positive().optional()
1520
+ }
1521
+ }, async ({ selector, timeoutMs = 5e3, intervalMs = 200, ...connection }) => {
1522
+ try {
1523
+ const result = await manager.withPage(connection, async (page) => {
1524
+ const startedAt = Date.now();
1525
+ while (Date.now() - startedAt <= timeoutMs) {
1526
+ const elements = await queryElements(page, selector).catch(() => []);
1527
+ if (elements.length > 0) {
1528
+ return {
1529
+ selector,
1530
+ found: true,
1531
+ count: elements.length,
1532
+ waitMs: Date.now() - startedAt
1533
+ };
1534
+ }
1535
+ await page.waitFor(intervalMs);
1536
+ }
1537
+ throw new Error(`\u7B49\u5F85\u5143\u7D20 "${selector}" \u8D85\u65F6 (${timeoutMs}ms)\u3002`);
1538
+ });
1539
+ return toToolResult(result);
1540
+ } catch (error) {
1541
+ return toToolError(error);
1542
+ }
1543
+ });
1544
+ server.registerTool("weapp_runtime_wait", {
1545
+ title: "Wait Page Timeout",
1546
+ description: "\u5728\u5F53\u524D\u9875\u9762\u7B49\u5F85\u6307\u5B9A\u6BEB\u79D2\u6570\u3002",
1547
+ inputSchema: {
1548
+ ...connectionInputSchema,
1549
+ milliseconds: z.number().int().nonnegative()
1550
+ }
1551
+ }, async ({ milliseconds, ...connection }) => {
1552
+ try {
1553
+ const result = await manager.withPage(connection, async (page) => {
1554
+ await page.waitFor(milliseconds);
1555
+ return { waitedMs: milliseconds };
1556
+ });
1557
+ return toToolResult(result);
1558
+ } catch (error) {
1559
+ return toToolError(error);
1560
+ }
1561
+ });
1562
+ server.registerTool("weapp_runtime_page_state", {
1563
+ title: "Get Page Data",
1564
+ description: "\u8BFB\u53D6\u5F53\u524D\u9875\u9762 data\uFF0C\u53EF\u901A\u8FC7 path \u8BFB\u53D6\u5D4C\u5957\u5B57\u6BB5\u3002",
1565
+ inputSchema: {
1566
+ ...connectionInputSchema,
1567
+ path: z.string().trim().min(1).optional()
1568
+ }
1569
+ }, async ({ path, ...connection }) => {
1570
+ try {
1571
+ const result = await manager.withPage(connection, async (page) => ({
1572
+ path: path ?? null,
1573
+ data: toSerializableValue(await page.data(path))
1574
+ }));
1575
+ return toToolResult(result);
1576
+ } catch (error) {
1577
+ return toToolError(error);
1578
+ }
1579
+ });
1580
+ server.registerTool("weapp_runtime_update_page_state", {
1581
+ title: "Set Page Data",
1582
+ description: "\u8C03\u7528\u5F53\u524D\u9875\u9762 setData \u66F4\u65B0 data\u3002",
1583
+ inputSchema: {
1584
+ ...connectionInputSchema,
1585
+ data: z.record(z.string(), z.unknown()),
1586
+ verify: z.boolean().optional()
1587
+ }
1588
+ }, async ({ data, verify, ...connection }) => {
1589
+ try {
1590
+ const result = await manager.withPage(connection, async (page) => {
1591
+ await callRequiredMethod(page, "setData", data);
1592
+ return {
1593
+ keys: Object.keys(data),
1594
+ data: verify ? toSerializableValue(await page.data()) : void 0
1595
+ };
1596
+ });
1597
+ return toToolResult(result);
1598
+ } catch (error) {
1599
+ return toToolError(error);
1600
+ }
1601
+ });
1602
+ server.registerTool("weapp_runtime_invoke_page", {
1603
+ title: "Call Page Method",
1604
+ description: "\u8C03\u7528\u5F53\u524D\u9875\u9762\u5B9E\u4F8B\u65B9\u6CD5\u3002",
1605
+ inputSchema: {
1606
+ ...connectionInputSchema,
1607
+ method: z.string().trim().min(1),
1608
+ args: z.array(z.unknown()).optional()
1609
+ }
1610
+ }, async ({ method, args, ...connection }) => {
1611
+ try {
1612
+ const result = await manager.withPage(connection, async (page) => {
1613
+ const callArgs = args ?? [];
1614
+ return {
1615
+ method,
1616
+ args: toSerializableValue(callArgs),
1617
+ result: toSerializableValue(await callRequiredMethod(page, method, ...callArgs))
1618
+ };
1619
+ });
1620
+ return toToolResult(result);
1621
+ } catch (error) {
1622
+ return toToolError(error);
1623
+ }
1624
+ });
1625
+ }
1626
+
1627
+ function registerRuntimeTools(server, options) {
1628
+ const manager = new RuntimeSessionManager(options.workspaceRoot, options.runtimeHooks);
1629
+ registerDevtoolsRuntimeTools(server, manager);
1630
+ registerRuntimePageTools(server, manager);
1631
+ registerRuntimeNodeTools(server, manager);
1632
+ }
1633
+
1634
+ async function loadPackageSummary(workspaceRoot, id) {
1635
+ return resolveExposedPackage(workspaceRoot, id);
1636
+ }
1637
+ async function loadExposedCatalog(workspaceRoot) {
1638
+ return resolveExposedPackages(workspaceRoot);
1639
+ }
1640
+
1641
+ function registerServerTools(server, options) {
1642
+ const { workspaceRoot, packageIds, packageIdSchema } = options;
1643
+ server.registerTool("workspace_catalog", {
1644
+ title: "Workspace Catalog",
1645
+ description: "\u8BFB\u53D6 weapp-vite / wevu \u76F8\u5173\u5305\u76EE\u5F55\u4E0E\u811A\u672C\u80FD\u529B\u6E05\u5355"
1646
+ }, async () => {
445
1647
  try {
446
- const catalog2 = await loadExposedCatalog(workspaceRoot);
1648
+ const catalog = await loadExposedCatalog(workspaceRoot);
447
1649
  return toToolResult({
448
1650
  workspaceRoot,
449
- packages: catalog2
1651
+ packages: catalog
450
1652
  });
451
1653
  } catch (error) {
452
1654
  return toToolError(error);
@@ -695,137 +1897,32 @@ async function createWeappViteMcpServer(options) {
695
1897
  return toToolError(error);
696
1898
  }
697
1899
  });
698
- server.registerPrompt("plan-weapp-vite-change", {
699
- title: "Plan weapp-vite Change",
700
- description: "\u6839\u636E\u53D8\u66F4\u76EE\u6807\u751F\u6210 weapp-vite / wevu \u4FEE\u6539\u8BA1\u5212\u63D0\u793A\u8BCD",
701
- argsSchema: {
702
- objective: z.string().min(1),
703
- focusPackage: packageIdSchema.optional()
704
- }
705
- }, async ({ objective, focusPackage }) => {
706
- const targets = focusPackage ? [focusPackage] : packageIds;
707
- return {
708
- messages: [
709
- {
710
- role: "user",
711
- content: {
712
- type: "text",
713
- text: [
714
- "\u4F60\u662F weapp-vite monorepo \u7EF4\u62A4\u8005\uFF0C\u8BF7\u7ED9\u51FA\u53EF\u6267\u884C\u7684\u6539\u9020\u8BA1\u5212\u3002",
715
- `\u76EE\u6807\uFF1A${objective}`,
716
- `\u805A\u7126\u5305\uFF1A${targets.join(", ")}`,
717
- "\u8BF7\u5305\u542B\uFF1A\u5F71\u54CD\u9762\u3001\u98CE\u9669\u70B9\u3001\u6D4B\u8BD5\u7B56\u7565\u3001\u56DE\u6EDA\u7B56\u7565\u3002"
718
- ].join("\n")
719
- }
720
- }
721
- ]
722
- };
1900
+ }
1901
+
1902
+ const packageIds = Object.keys(EXPOSED_PACKAGES);
1903
+ const packageIdSchema = z.enum(packageIds);
1904
+ async function createWeappViteMcpServer(options) {
1905
+ const workspaceRoot = resolveWorkspaceRoot(options?.workspaceRoot);
1906
+ const server = new McpServer({
1907
+ name: MCP_SERVER_NAME,
1908
+ version: MCP_SERVER_VERSION
723
1909
  });
724
- server.registerPrompt("debug-wevu-runtime", {
725
- title: "Debug wevu Runtime",
726
- description: "\u7528\u4E8E\u5B9A\u4F4D wevu runtime \u751F\u547D\u5468\u671F/\u54CD\u5E94\u5F0F\u95EE\u9898\u7684\u6807\u51C6\u63D0\u793A\u8BCD",
727
- argsSchema: {
728
- symptom: z.string().min(1)
729
- }
730
- }, async ({ symptom }) => {
731
- return {
732
- messages: [
733
- {
734
- role: "user",
735
- content: {
736
- type: "text",
737
- text: [
738
- "\u8BF7\u57FA\u4E8E wevu runtime \u4EE3\u7801\u8DEF\u5F84\u8FDB\u884C\u5206\u5C42\u6392\u67E5\uFF1A",
739
- "1. \u590D\u73B0\u573A\u666F\u4E0E\u6700\u5C0F\u6837\u4F8B",
740
- "2. \u751F\u547D\u5468\u671F\u94A9\u5B50\u89E6\u53D1\u94FE",
741
- "3. \u54CD\u5E94\u5F0F\u4E0E setData \u5DEE\u91CF\u540C\u6B65\u94FE",
742
- "4. \u5355\u6D4B\u4E0E e2e \u56DE\u5F52\u8865\u4E01",
743
- `\u73B0\u8C61\uFF1A${symptom}`
744
- ].join("\n")
745
- }
746
- }
747
- ]
748
- };
1910
+ registerServerTools(server, {
1911
+ workspaceRoot,
1912
+ packageIds,
1913
+ packageIdSchema
749
1914
  });
750
- server.registerResource("workspace-catalog", "weapp-vite://workspace/catalog", {
751
- title: "Workspace Catalog",
752
- description: "weapp-vite / wevu \u5305\u76EE\u5F55\u3001\u7248\u672C\u548C\u811A\u672C\u5217\u8868",
753
- mimeType: "application/json"
754
- }, async () => {
755
- const catalog2 = await loadExposedCatalog(workspaceRoot);
756
- const text = JSON.stringify({ workspaceRoot, packages: catalog2 }, null, 2);
757
- return {
758
- contents: [{
759
- uri: "weapp-vite://workspace/catalog",
760
- mimeType: "application/json",
761
- text
762
- }]
763
- };
1915
+ registerRuntimeTools(server, {
1916
+ runtimeHooks: options?.runtimeHooks,
1917
+ workspaceRoot
764
1918
  });
765
- const catalog = await resolveExposedPackages(workspaceRoot);
766
- for (const summary of catalog) {
767
- if (summary.docs.readme) {
768
- const uri = toDocsUri(summary.id, "README.md");
769
- server.registerResource(`docs-${summary.id}-readme`, uri, {
770
- title: `${summary.id} README`,
771
- mimeType: "text/markdown"
772
- }, async () => {
773
- const text = await readTextFile(summary.docs.readme);
774
- return {
775
- contents: [{ uri, mimeType: "text/markdown", text }]
776
- };
777
- });
778
- }
779
- if (summary.docs.changelog) {
780
- const uri = toDocsUri(summary.id, "CHANGELOG.md");
781
- server.registerResource(`docs-${summary.id}-changelog`, uri, {
782
- title: `${summary.id} CHANGELOG`,
783
- mimeType: "text/markdown"
784
- }, async () => {
785
- const text = await readTextFile(summary.docs.changelog);
786
- return {
787
- contents: [{ uri, mimeType: "text/markdown", text }]
788
- };
789
- });
790
- }
791
- }
792
- const sourceTemplate = new ResourceTemplate("weapp-vite://source/{package}?path={path}", {
793
- list: void 0,
794
- complete: {
795
- package: () => packageIds
796
- }
1919
+ registerServerPrompts(server, {
1920
+ packageIds,
1921
+ packageIdSchema
797
1922
  });
798
- server.registerResource("source-template", sourceTemplate, {
799
- title: "Source Template",
800
- description: "\u8BFB\u53D6 weapp-vite / wevu \u4EFB\u610F\u6E90\u7801\u6587\u4EF6\uFF08\u901A\u8FC7 package + path \u53C2\u6570\uFF09",
801
- mimeType: "text/plain"
802
- }, async (uri, variables) => {
803
- try {
804
- const packageId = String(variables.package ?? "");
805
- if (!packageIds.includes(packageId)) {
806
- throw new Error(`\u672A\u77E5 package\uFF1A${packageId}`);
807
- }
808
- const relativePath = decodeURIComponent(String(variables.path ?? ""));
809
- const packageRoot = await resolvePackageRoot(workspaceRoot, packageId);
810
- const { content } = await readFileContent(packageRoot, relativePath, {
811
- maxChars: DEFAULT_MAX_FILE_CHARS
812
- });
813
- return {
814
- contents: [{
815
- uri: uri.toString(),
816
- mimeType: "text/plain",
817
- text: content
818
- }]
819
- };
820
- } catch (error) {
821
- return {
822
- contents: [{
823
- uri: uri.toString(),
824
- mimeType: "text/plain",
825
- text: `[resource-error] ${normalizeErrorMessage(error)}`
826
- }]
827
- };
828
- }
1923
+ await registerServerResources(server, {
1924
+ workspaceRoot,
1925
+ packageIds
829
1926
  });
830
1927
  return {
831
1928
  server,
@@ -895,13 +1992,14 @@ async function startStreamableHttpServer(options) {
895
1992
  host = DEFAULT_MCP_HOST,
896
1993
  port = DEFAULT_MCP_PORT,
897
1994
  workspaceRoot,
1995
+ runtimeHooks,
898
1996
  unref = false,
899
1997
  quiet = false,
900
1998
  onReady
901
1999
  } = options;
902
2000
  const normalizedEndpoint = normalizeEndpoint(endpoint);
903
2001
  const normalizedPort = normalizePort(port);
904
- const { server: mcpServer } = await createWeappViteMcpServer({ workspaceRoot });
2002
+ const { server: mcpServer } = await createWeappViteMcpServer({ runtimeHooks, workspaceRoot });
905
2003
  const transport = new StreamableHTTPServerTransport({
906
2004
  sessionIdGenerator: void 0
907
2005
  });