@xiaou66/vite-plugin-vue-mcp-next 0.0.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,11 +16,13 @@ var DEFAULT_MASK_HEADERS = [
16
16
 
17
17
  // src/constants.ts
18
18
  var DEFAULT_MCP_PATH = "/__mcp";
19
+ var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
19
20
  var MCP_TOOL_NAMES = {
20
21
  listPages: "list_pages",
21
22
  getPageState: "get_page_state",
22
23
  getDomTree: "get_dom_tree",
23
24
  queryDom: "query_dom",
25
+ takeScreenshot: "take_screenshot",
24
26
  getConsoleLogs: "get_console_logs",
25
27
  clearConsoleLogs: "clear_console_logs",
26
28
  evaluateScript: "evaluate_script",
@@ -37,6 +39,8 @@ var MCP_TOOL_NAMES = {
37
39
  };
38
40
  var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
39
41
  var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
42
+ var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-config";
43
+ var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
40
44
  var DEFAULT_MCP_CLIENT_SERVER_NAME = "vue-mcp-next";
41
45
  var DEFAULT_OPTIONS = {
42
46
  mcpPath: DEFAULT_MCP_PATH,
@@ -76,6 +80,14 @@ var DEFAULT_OPTIONS = {
76
80
  },
77
81
  console: {
78
82
  maxRecords: DEFAULT_CONSOLE_MAX_RECORDS
83
+ },
84
+ screenshot: {
85
+ prefer: "auto",
86
+ maxBytes: DEFAULT_SCREENSHOT_MAX_BYTES,
87
+ snapdom: {
88
+ options: {},
89
+ plugins: []
90
+ }
79
91
  }
80
92
  };
81
93
  function mergeMcpClientOptions(cursorConfig, mcpClients) {
@@ -126,6 +138,21 @@ function mergeOptions(options = {}) {
126
138
  console: {
127
139
  ...DEFAULT_OPTIONS.console,
128
140
  ...options.console
141
+ },
142
+ screenshot: {
143
+ ...DEFAULT_OPTIONS.screenshot,
144
+ ...options.screenshot,
145
+ snapdom: {
146
+ ...DEFAULT_OPTIONS.screenshot.snapdom,
147
+ ...options.screenshot?.snapdom,
148
+ options: {
149
+ ...DEFAULT_OPTIONS.screenshot.snapdom.options,
150
+ ...options.screenshot?.snapdom?.options
151
+ },
152
+ plugins: options.screenshot?.snapdom?.plugins ?? [
153
+ ...DEFAULT_OPTIONS.screenshot.snapdom.plugins
154
+ ]
155
+ }
129
156
  }
130
157
  };
131
158
  }
@@ -680,9 +707,225 @@ function getPathname(url) {
680
707
  }
681
708
  }
682
709
 
710
+ // src/mcp/tools/screenshot.ts
711
+ import { z as z5 } from "zod";
712
+
713
+ // src/cdp/cdpScreenshot.ts
714
+ async function cdpCaptureScreenshot(options) {
715
+ if (options.target === "element") {
716
+ return captureElementScreenshot(options);
717
+ }
718
+ if (options.target === "fullPage") {
719
+ return captureFullPageScreenshot(options);
720
+ }
721
+ return captureViewportScreenshot(options);
722
+ }
723
+ async function captureViewportScreenshot(options) {
724
+ const metrics = await options.client.Page.getLayoutMetrics();
725
+ const width = Math.ceil(metrics.cssLayoutViewport.clientWidth);
726
+ const height = Math.ceil(metrics.cssLayoutViewport.clientHeight);
727
+ const result = await options.client.Page.captureScreenshot(
728
+ omitUndefined({
729
+ format: options.format,
730
+ quality: createQuality(options),
731
+ captureBeyondViewport: false
732
+ })
733
+ );
734
+ return { data: result.data, width, height };
735
+ }
736
+ async function captureFullPageScreenshot(options) {
737
+ const metrics = await options.client.Page.getLayoutMetrics();
738
+ const width = Math.ceil(metrics.cssContentSize.width);
739
+ const height = Math.ceil(metrics.cssContentSize.height);
740
+ const result = await options.client.Page.captureScreenshot(
741
+ omitUndefined({
742
+ format: options.format,
743
+ quality: createQuality(options),
744
+ captureBeyondViewport: true,
745
+ clip: { x: 0, y: 0, width, height, scale: 1 }
746
+ })
747
+ );
748
+ return { data: result.data, width, height };
749
+ }
750
+ async function captureElementScreenshot(options) {
751
+ if (!options.selector) {
752
+ throw new Error("selector is required when target is element");
753
+ }
754
+ const rect = await getElementRect(options.client, options.selector);
755
+ const result = await options.client.Page.captureScreenshot(
756
+ omitUndefined({
757
+ format: options.format,
758
+ quality: createQuality(options),
759
+ captureBeyondViewport: true,
760
+ clip: {
761
+ x: rect.x,
762
+ y: rect.y,
763
+ width: rect.width,
764
+ height: rect.height,
765
+ scale: 1
766
+ }
767
+ })
768
+ );
769
+ return { data: result.data, width: rect.width, height: rect.height };
770
+ }
771
+ async function getElementRect(client, selector) {
772
+ const result = await client.Runtime.evaluate({
773
+ expression: createElementRectExpression(selector),
774
+ awaitPromise: true,
775
+ returnByValue: true
776
+ });
777
+ if (result.exceptionDetails) {
778
+ throw new Error(result.exceptionDetails.text || "element query failed");
779
+ }
780
+ const value = result.result.value;
781
+ if (!isElementRect(value)) {
782
+ throw new Error(`element not found: ${selector}`);
783
+ }
784
+ return value;
785
+ }
786
+ function createElementRectExpression(selector) {
787
+ return `(() => {
788
+ const el = document.querySelector(${JSON.stringify(selector)});
789
+ if (!el) return null;
790
+ const rect = el.getBoundingClientRect();
791
+ return {
792
+ x: rect.x + window.scrollX,
793
+ y: rect.y + window.scrollY,
794
+ width: rect.width,
795
+ height: rect.height
796
+ };
797
+ })()`;
798
+ }
799
+ function createQuality(options) {
800
+ return options.format === "png" ? void 0 : options.quality;
801
+ }
802
+ function omitUndefined(value) {
803
+ return Object.fromEntries(
804
+ Object.entries(value).filter(([, item]) => item !== void 0)
805
+ );
806
+ }
807
+ function isElementRect(value) {
808
+ if (!value || typeof value !== "object") {
809
+ return false;
810
+ }
811
+ const rect = value;
812
+ return typeof rect.x === "number" && typeof rect.y === "number" && typeof rect.width === "number" && rect.width > 0 && typeof rect.height === "number" && rect.height > 0;
813
+ }
814
+
815
+ // src/mcp/tools/screenshot.ts
816
+ var DEFAULT_SCREENSHOT_TARGET = "viewport";
817
+ var DEFAULT_SCREENSHOT_FORMAT = "png";
818
+ var screenshotInputSchema = {
819
+ pageId: z5.string().optional(),
820
+ target: z5.enum(["viewport", "fullPage", "element"]).optional(),
821
+ selector: z5.string().optional(),
822
+ format: z5.enum(["png", "jpeg", "webp"]).optional(),
823
+ prefer: z5.enum(["auto", "cdp", "runtime"]).optional(),
824
+ quality: z5.number().optional(),
825
+ scale: z5.number().optional(),
826
+ snapdom: z5.record(z5.string(), z5.unknown()).optional()
827
+ };
828
+ function registerScreenshotTools(server, ctx) {
829
+ server.registerTool(
830
+ MCP_TOOL_NAMES.takeScreenshot,
831
+ {
832
+ description: "Take a page screenshot using CDP or snapdom fallback.",
833
+ inputSchema: screenshotInputSchema
834
+ },
835
+ async (input) => handleTakeScreenshot(ctx, input)
836
+ );
837
+ }
838
+ async function handleTakeScreenshot(ctx, input) {
839
+ const target = input.target ?? DEFAULT_SCREENSHOT_TARGET;
840
+ const format = input.format ?? DEFAULT_SCREENSHOT_FORMAT;
841
+ const prefer = input.prefer ?? ctx.options.screenshot.prefer;
842
+ if (target === "element" && !input.selector) {
843
+ return createToolError("selector is required when target is element");
844
+ }
845
+ if (prefer !== "runtime") {
846
+ const cdp = await connectCdpForPage(ctx, input.pageId);
847
+ if (cdp) {
848
+ try {
849
+ const screenshot = await cdpCaptureScreenshot({
850
+ client: cdp.client,
851
+ target,
852
+ selector: input.selector,
853
+ format,
854
+ quality: input.quality
855
+ });
856
+ return createScreenshotResponse(ctx, {
857
+ source: "cdp",
858
+ target,
859
+ format,
860
+ data: screenshot.data,
861
+ width: screenshot.width,
862
+ height: screenshot.height,
863
+ mimeType: createMimeType(format),
864
+ byteLength: getBase64ByteLength(screenshot.data)
865
+ });
866
+ } finally {
867
+ await closeCdpClient(cdp.client);
868
+ }
869
+ }
870
+ if (prefer === "cdp") {
871
+ return createToolError("CDP screenshot is unavailable");
872
+ }
873
+ }
874
+ return createRuntimeScreenshot(ctx, input, { target, format });
875
+ }
876
+ async function createRuntimeScreenshot(ctx, input, normalized) {
877
+ const result = await requestRuntimeData(ctx, (event) => {
878
+ void ctx.rpcServer?.takeScreenshot({
879
+ event,
880
+ target: normalized.target,
881
+ selector: input.selector,
882
+ format: normalized.format,
883
+ quality: input.quality,
884
+ scale: input.scale,
885
+ snapdom: {
886
+ ...ctx.options.screenshot.snapdom,
887
+ options: {
888
+ ...ctx.options.screenshot.snapdom.options,
889
+ ...input.snapdom
890
+ }
891
+ }
892
+ });
893
+ });
894
+ if (isScreenshotTooLarge(ctx, result)) {
895
+ return createToolError(
896
+ `screenshot is too large: ${String(result.byteLength)} bytes`
897
+ );
898
+ }
899
+ if (!isPlainRecord(result)) {
900
+ return createToolError("runtime screenshot returned an invalid response");
901
+ }
902
+ return createToolResponse(result);
903
+ }
904
+ function createScreenshotResponse(ctx, result) {
905
+ if (result.byteLength > ctx.options.screenshot.maxBytes) {
906
+ return createToolError(
907
+ `screenshot is too large: ${String(result.byteLength)} bytes`
908
+ );
909
+ }
910
+ return createToolResponse(result);
911
+ }
912
+ function isScreenshotTooLarge(ctx, result) {
913
+ return isPlainRecord(result) && "byteLength" in result && typeof result.byteLength === "number" && result.byteLength > ctx.options.screenshot.maxBytes;
914
+ }
915
+ function isPlainRecord(value) {
916
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
917
+ }
918
+ function createMimeType(format) {
919
+ return `image/${format}`;
920
+ }
921
+ function getBase64ByteLength(data) {
922
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
923
+ return Math.ceil(data.length * 3 / 4) - padding;
924
+ }
925
+
683
926
  // src/mcp/tools/vue.ts
684
927
  import { nanoid as nanoid2 } from "nanoid";
685
- import { z as z5 } from "zod";
928
+ import { z as z6 } from "zod";
686
929
  function registerVueTools(server, ctx) {
687
930
  server.registerTool(
688
931
  MCP_TOOL_NAMES.getComponentTree,
@@ -695,7 +938,7 @@ function registerVueTools(server, ctx) {
695
938
  MCP_TOOL_NAMES.getComponentState,
696
939
  {
697
940
  description: "Get Vue component state.",
698
- inputSchema: { componentName: z5.string() }
941
+ inputSchema: { componentName: z6.string() }
699
942
  },
700
943
  async ({ componentName }) => requestVueData(ctx, (event) => {
701
944
  void ctx.rpcServer?.getInspectorState({ event, componentName });
@@ -706,10 +949,10 @@ function registerVueTools(server, ctx) {
706
949
  {
707
950
  description: "Edit Vue component state.",
708
951
  inputSchema: {
709
- componentName: z5.string(),
710
- path: z5.array(z5.string()),
711
- value: z5.string(),
712
- valueType: z5.enum(["string", "number", "boolean", "object", "array"])
952
+ componentName: z6.string(),
953
+ path: z6.array(z6.string()),
954
+ value: z6.string(),
955
+ valueType: z6.enum(["string", "number", "boolean", "object", "array"])
713
956
  }
714
957
  },
715
958
  ({ componentName, path: path5, value, valueType }) => {
@@ -729,7 +972,7 @@ function registerVueTools(server, ctx) {
729
972
  MCP_TOOL_NAMES.highlightComponent,
730
973
  {
731
974
  description: "Highlight a Vue component.",
732
- inputSchema: { componentName: z5.string() }
975
+ inputSchema: { componentName: z6.string() }
733
976
  },
734
977
  ({ componentName }) => {
735
978
  if (!ctx.rpcServer) {
@@ -757,7 +1000,7 @@ function registerVueTools(server, ctx) {
757
1000
  MCP_TOOL_NAMES.getPiniaState,
758
1001
  {
759
1002
  description: "Get Pinia store state.",
760
- inputSchema: { storeName: z5.string() }
1003
+ inputSchema: { storeName: z6.string() }
761
1004
  },
762
1005
  async ({ storeName }) => requestVueData(ctx, (event) => {
763
1006
  void ctx.rpcServer?.getPiniaState({ event, storeName });
@@ -800,6 +1043,7 @@ function createMcpServer(ctx, vite) {
800
1043
  });
801
1044
  registerPageTools(server, ctx, vite);
802
1045
  registerDomTools(server, ctx);
1046
+ registerScreenshotTools(server, ctx);
803
1047
  registerConsoleTools(server, ctx);
804
1048
  registerEvaluateTools(server, ctx);
805
1049
  registerNetworkTools(server, ctx);
@@ -809,18 +1053,39 @@ function createMcpServer(ctx, vite) {
809
1053
 
810
1054
  // src/mcp/transport.ts
811
1055
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
812
- function setupMcpTransport(base, server, vite) {
1056
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
1057
+ function setupMcpTransport(base, createServer, vite) {
813
1058
  const transports = /* @__PURE__ */ new Map();
814
1059
  vite.middlewares.use(`${base}/sse`, (_req, res) => {
815
1060
  const transport = new SSEServerTransport(`${base}/messages`, res);
816
- transports.set(transport.sessionId, transport);
1061
+ const server = createServer();
1062
+ transports.set(transport.sessionId, { server, transport });
817
1063
  res.on("close", () => {
818
1064
  transports.delete(transport.sessionId);
1065
+ void server.close();
819
1066
  });
820
1067
  void server.connect(transport).catch((error) => {
821
1068
  res.destroy(error instanceof Error ? error : new Error(String(error)));
822
1069
  });
823
1070
  });
1071
+ vite.middlewares.use(`${base}/mcp`, (req, res) => {
1072
+ if (req.method !== "POST") {
1073
+ res.statusCode = 405;
1074
+ res.end("Method Not Allowed");
1075
+ return;
1076
+ }
1077
+ const transport = new StreamableHTTPServerTransport({
1078
+ sessionIdGenerator: void 0
1079
+ });
1080
+ const server = createServer();
1081
+ res.on("close", () => {
1082
+ void transport.close();
1083
+ void server.close();
1084
+ });
1085
+ void server.connect(transport).then(() => transport.handleRequest(req, res)).catch((error) => {
1086
+ res.destroy(error instanceof Error ? error : new Error(String(error)));
1087
+ });
1088
+ });
824
1089
  vite.middlewares.use(`${base}/messages`, (req, res) => {
825
1090
  if (req.method !== "POST") {
826
1091
  res.statusCode = 405;
@@ -834,13 +1099,13 @@ function setupMcpTransport(base, server, vite) {
834
1099
  res.end("Bad Request");
835
1100
  return;
836
1101
  }
837
- const transport = transports.get(sessionId);
838
- if (!transport) {
1102
+ const entry = transports.get(sessionId);
1103
+ if (!entry) {
839
1104
  res.statusCode = 404;
840
1105
  res.end("Not Found");
841
1106
  return;
842
1107
  }
843
- void transport.handlePostMessage(req, res).catch((error) => {
1108
+ void entry.transport.handlePostMessage(req, res).catch((error) => {
844
1109
  res.destroy(error instanceof Error ? error : new Error(String(error)));
845
1110
  });
846
1111
  });
@@ -861,6 +1126,10 @@ function createServerVueRuntimeRpc(ctx) {
861
1126
  onEvaluateScriptUpdated: (event, data) => {
862
1127
  void ctx.hooks.callHook(event, data);
863
1128
  },
1129
+ takeScreenshot: () => void 0,
1130
+ onScreenshotTaken: (event, data) => {
1131
+ void ctx.hooks.callHook(event, data);
1132
+ },
864
1133
  getInspectorTree: () => void 0,
865
1134
  onInspectorTreeUpdated: (event, data) => {
866
1135
  void ctx.hooks.callHook(event, data);
@@ -1152,13 +1421,19 @@ function createRuntimeInjectionController(options, getConfig) {
1152
1421
  if (importee === VIRTUAL_RUNTIME_ID) {
1153
1422
  return RESOLVED_VIRTUAL_RUNTIME_ID;
1154
1423
  }
1424
+ if (importee === VIRTUAL_SCREENSHOT_CONFIG_ID) {
1425
+ return RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID;
1426
+ }
1155
1427
  return void 0;
1156
1428
  },
1157
1429
  load(id) {
1158
- if (id !== RESOLVED_VIRTUAL_RUNTIME_ID) {
1159
- return void 0;
1430
+ if (id === RESOLVED_VIRTUAL_RUNTIME_ID) {
1431
+ return "import { startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';\nvoid startRuntimeClient();";
1432
+ }
1433
+ if (id === RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID) {
1434
+ return createScreenshotConfigModule(options);
1160
1435
  }
1161
- return "import { startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';\nvoid startRuntimeClient();";
1436
+ return void 0;
1162
1437
  },
1163
1438
  transformIndexHtml(html) {
1164
1439
  if (options.appendTo) {
@@ -1193,6 +1468,32 @@ ${code}`;
1193
1468
  }
1194
1469
  };
1195
1470
  }
1471
+ function createScreenshotConfigModule(options) {
1472
+ const paths = collectScreenshotImportPaths(options);
1473
+ const imports = paths.map((item, index) => `import * as m${String(index)} from ${JSON.stringify(item)};`).join("\n");
1474
+ const entries = paths.map((item, index) => `${JSON.stringify(item)}: m${String(index)}`).join(",\n ");
1475
+ return `${imports}
1476
+ export const screenshotModuleRegistry = {
1477
+ ${entries}
1478
+ };
1479
+ `;
1480
+ }
1481
+ function collectScreenshotImportPaths(options) {
1482
+ const paths = /* @__PURE__ */ new Set();
1483
+ for (const plugin of options.screenshot.snapdom.plugins) {
1484
+ paths.add(getPluginPath(plugin));
1485
+ }
1486
+ if (options.screenshot.snapdom.filter) {
1487
+ paths.add(options.screenshot.snapdom.filter);
1488
+ }
1489
+ if (options.screenshot.snapdom.fallbackURL) {
1490
+ paths.add(options.screenshot.snapdom.fallbackURL);
1491
+ }
1492
+ return [...paths];
1493
+ }
1494
+ function getPluginPath(plugin) {
1495
+ return typeof plugin === "string" ? plugin : plugin.path;
1496
+ }
1196
1497
 
1197
1498
  // src/plugin/mcpClientConfig/index.ts
1198
1499
  import path4 from "path";
@@ -1216,7 +1517,7 @@ function replaceOrAppendOwnedBlock(current, options) {
1216
1517
  const block = createCodexServerBlock(options);
1217
1518
  const matcher = createOwnedBlockMatcher(options.serverName);
1218
1519
  if (matcher.test(current)) {
1219
- return current.replace(matcher, block);
1520
+ return ensureTrailingNewline(current);
1220
1521
  }
1221
1522
  const separator = current.trim() ? "\n\n" : "";
1222
1523
  return `${trimEndNewline(current)}${separator}${block}`;
@@ -1258,6 +1559,10 @@ async function readOptionalTextFile(filePath) {
1258
1559
  function trimEndNewline(value) {
1259
1560
  return value.replace(/\n+$/u, "");
1260
1561
  }
1562
+ function ensureTrailingNewline(value) {
1563
+ return value.endsWith("\n") ? value : `${value}
1564
+ `;
1565
+ }
1261
1566
  function escapeRegExp(value) {
1262
1567
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1263
1568
  }
@@ -1274,12 +1579,15 @@ import path3 from "path";
1274
1579
  async function updateJsonMcpClientConfig(options) {
1275
1580
  try {
1276
1581
  const config = await readJsonConfig(options.configPath);
1277
- if (!isPlainRecord(config)) {
1582
+ if (!isPlainRecord2(config)) {
1278
1583
  warnConfigFailure(options, "config root must be a JSON object");
1279
1584
  return;
1280
1585
  }
1281
- const mcpServers = isPlainRecord(config.mcpServers) ? config.mcpServers : {};
1282
- mcpServers[options.serverName] = { url: options.mcpUrl };
1586
+ const mcpServers = isPlainRecord2(config.mcpServers) ? config.mcpServers : {};
1587
+ if (Object.hasOwn(mcpServers, options.serverName)) {
1588
+ return;
1589
+ }
1590
+ mcpServers[options.serverName] = { type: "sse", url: options.mcpUrl };
1283
1591
  config.mcpServers = mcpServers;
1284
1592
  await fs3.mkdir(path3.dirname(options.configPath), { recursive: true });
1285
1593
  await fs3.writeFile(
@@ -1308,7 +1616,7 @@ async function readOptionalTextFile2(filePath) {
1308
1616
  throw error;
1309
1617
  }
1310
1618
  }
1311
- function isPlainRecord(value) {
1619
+ function isPlainRecord2(value) {
1312
1620
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1313
1621
  }
1314
1622
  function warnConfigFailure(options, reason) {
@@ -1324,7 +1632,7 @@ function isNodeError2(error) {
1324
1632
  }
1325
1633
 
1326
1634
  // src/plugin/mcpClientConfig/index.ts
1327
- async function updateMcpClientConfigs(root, mcpUrl, options) {
1635
+ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options) {
1328
1636
  const serverName = options.serverName;
1329
1637
  const jobs = [];
1330
1638
  if (options.cursor) {
@@ -1332,7 +1640,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1332
1640
  updateJsonMcpClientConfig({
1333
1641
  clientName: "Cursor",
1334
1642
  configPath: path4.join(root, ".cursor", "mcp.json"),
1335
- mcpUrl,
1643
+ mcpUrl: sseUrl,
1336
1644
  serverName
1337
1645
  })
1338
1646
  );
@@ -1341,7 +1649,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1341
1649
  jobs.push(
1342
1650
  updateCodexMcpClientConfig({
1343
1651
  configPath: path4.join(root, ".codex", "config.toml"),
1344
- mcpUrl,
1652
+ mcpUrl: streamableHttpUrl,
1345
1653
  serverName
1346
1654
  })
1347
1655
  );
@@ -1351,7 +1659,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1351
1659
  updateJsonMcpClientConfig({
1352
1660
  clientName: "Claude Code",
1353
1661
  configPath: path4.join(root, ".mcp.json"),
1354
- mcpUrl,
1662
+ mcpUrl: sseUrl,
1355
1663
  serverName
1356
1664
  })
1357
1665
  );
@@ -1361,7 +1669,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1361
1669
  updateJsonMcpClientConfig({
1362
1670
  clientName: "Trae",
1363
1671
  configPath: path4.join(root, ".trae", "mcp.json"),
1364
- mcpUrl,
1672
+ mcpUrl: sseUrl,
1365
1673
  serverName
1366
1674
  })
1367
1675
  );
@@ -1397,8 +1705,11 @@ function vueMcpNext(userOptions = {}) {
1397
1705
  timeout: -1
1398
1706
  }
1399
1707
  );
1400
- const mcpServer = createMcpServer(ctx, server);
1401
- setupMcpTransport(options.mcpPath, mcpServer, server);
1708
+ setupMcpTransport(
1709
+ options.mcpPath,
1710
+ () => createMcpServer(ctx, server),
1711
+ server
1712
+ );
1402
1713
  server.ws.on(
1403
1714
  "vite-plugin-vue-mcp-next:page-connected",
1404
1715
  (payload) => {
@@ -1425,12 +1736,21 @@ function vueMcpNext(userOptions = {}) {
1425
1736
  }
1426
1737
  );
1427
1738
  const port = String(server.config.server.port || 5173);
1428
- const mcpUrl = `http://${options.host}:${port}${options.mcpPath}/sse`;
1739
+ const mcpSseUrl = `http://${options.host}:${port}${options.mcpPath}/sse`;
1740
+ const mcpStreamableHttpUrl = `http://${options.host}:${port}${options.mcpPath}/mcp`;
1429
1741
  const root = searchForWorkspaceRoot(server.config.root);
1430
- await updateMcpClientConfigs(root, mcpUrl, options.mcpClients);
1742
+ await updateMcpClientConfigs(
1743
+ root,
1744
+ mcpSseUrl,
1745
+ mcpStreamableHttpUrl,
1746
+ options.mcpClients
1747
+ );
1431
1748
  if (options.printUrl) {
1432
1749
  setTimeout(() => {
1433
- console.log(` \u279C MCP: Server is running at ${mcpUrl}`);
1750
+ console.log(` \u279C MCP: SSE server is running at ${mcpSseUrl}`);
1751
+ console.log(
1752
+ ` \u279C MCP: Streamable HTTP server is running at ${mcpStreamableHttpUrl}`
1753
+ );
1434
1754
  }, 300);
1435
1755
  }
1436
1756
  server.httpServer?.once("close", () => {