@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.cjs CHANGED
@@ -53,11 +53,13 @@ var DEFAULT_MASK_HEADERS = [
53
53
 
54
54
  // src/constants.ts
55
55
  var DEFAULT_MCP_PATH = "/__mcp";
56
+ var DEFAULT_SCREENSHOT_MAX_BYTES = 5 * 1024 * 1024;
56
57
  var MCP_TOOL_NAMES = {
57
58
  listPages: "list_pages",
58
59
  getPageState: "get_page_state",
59
60
  getDomTree: "get_dom_tree",
60
61
  queryDom: "query_dom",
62
+ takeScreenshot: "take_screenshot",
61
63
  getConsoleLogs: "get_console_logs",
62
64
  clearConsoleLogs: "clear_console_logs",
63
65
  evaluateScript: "evaluate_script",
@@ -74,6 +76,8 @@ var MCP_TOOL_NAMES = {
74
76
  };
75
77
  var VIRTUAL_RUNTIME_ID = "virtual:vite-plugin-vue-mcp-next/runtime";
76
78
  var RESOLVED_VIRTUAL_RUNTIME_ID = `\0${VIRTUAL_RUNTIME_ID}`;
79
+ var VIRTUAL_SCREENSHOT_CONFIG_ID = "virtual:vite-plugin-vue-mcp-next/screenshot-config";
80
+ var RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID = `\0${VIRTUAL_SCREENSHOT_CONFIG_ID}`;
77
81
  var DEFAULT_MCP_CLIENT_SERVER_NAME = "vue-mcp-next";
78
82
  var DEFAULT_OPTIONS = {
79
83
  mcpPath: DEFAULT_MCP_PATH,
@@ -113,6 +117,14 @@ var DEFAULT_OPTIONS = {
113
117
  },
114
118
  console: {
115
119
  maxRecords: DEFAULT_CONSOLE_MAX_RECORDS
120
+ },
121
+ screenshot: {
122
+ prefer: "auto",
123
+ maxBytes: DEFAULT_SCREENSHOT_MAX_BYTES,
124
+ snapdom: {
125
+ options: {},
126
+ plugins: []
127
+ }
116
128
  }
117
129
  };
118
130
  function mergeMcpClientOptions(cursorConfig, mcpClients) {
@@ -163,6 +175,21 @@ function mergeOptions(options = {}) {
163
175
  console: {
164
176
  ...DEFAULT_OPTIONS.console,
165
177
  ...options.console
178
+ },
179
+ screenshot: {
180
+ ...DEFAULT_OPTIONS.screenshot,
181
+ ...options.screenshot,
182
+ snapdom: {
183
+ ...DEFAULT_OPTIONS.screenshot.snapdom,
184
+ ...options.screenshot?.snapdom,
185
+ options: {
186
+ ...DEFAULT_OPTIONS.screenshot.snapdom.options,
187
+ ...options.screenshot?.snapdom?.options
188
+ },
189
+ plugins: options.screenshot?.snapdom?.plugins ?? [
190
+ ...DEFAULT_OPTIONS.screenshot.snapdom.plugins
191
+ ]
192
+ }
166
193
  }
167
194
  };
168
195
  }
@@ -717,9 +744,225 @@ function getPathname(url) {
717
744
  }
718
745
  }
719
746
 
747
+ // src/mcp/tools/screenshot.ts
748
+ var import_zod5 = require("zod");
749
+
750
+ // src/cdp/cdpScreenshot.ts
751
+ async function cdpCaptureScreenshot(options) {
752
+ if (options.target === "element") {
753
+ return captureElementScreenshot(options);
754
+ }
755
+ if (options.target === "fullPage") {
756
+ return captureFullPageScreenshot(options);
757
+ }
758
+ return captureViewportScreenshot(options);
759
+ }
760
+ async function captureViewportScreenshot(options) {
761
+ const metrics = await options.client.Page.getLayoutMetrics();
762
+ const width = Math.ceil(metrics.cssLayoutViewport.clientWidth);
763
+ const height = Math.ceil(metrics.cssLayoutViewport.clientHeight);
764
+ const result = await options.client.Page.captureScreenshot(
765
+ omitUndefined({
766
+ format: options.format,
767
+ quality: createQuality(options),
768
+ captureBeyondViewport: false
769
+ })
770
+ );
771
+ return { data: result.data, width, height };
772
+ }
773
+ async function captureFullPageScreenshot(options) {
774
+ const metrics = await options.client.Page.getLayoutMetrics();
775
+ const width = Math.ceil(metrics.cssContentSize.width);
776
+ const height = Math.ceil(metrics.cssContentSize.height);
777
+ const result = await options.client.Page.captureScreenshot(
778
+ omitUndefined({
779
+ format: options.format,
780
+ quality: createQuality(options),
781
+ captureBeyondViewport: true,
782
+ clip: { x: 0, y: 0, width, height, scale: 1 }
783
+ })
784
+ );
785
+ return { data: result.data, width, height };
786
+ }
787
+ async function captureElementScreenshot(options) {
788
+ if (!options.selector) {
789
+ throw new Error("selector is required when target is element");
790
+ }
791
+ const rect = await getElementRect(options.client, options.selector);
792
+ const result = await options.client.Page.captureScreenshot(
793
+ omitUndefined({
794
+ format: options.format,
795
+ quality: createQuality(options),
796
+ captureBeyondViewport: true,
797
+ clip: {
798
+ x: rect.x,
799
+ y: rect.y,
800
+ width: rect.width,
801
+ height: rect.height,
802
+ scale: 1
803
+ }
804
+ })
805
+ );
806
+ return { data: result.data, width: rect.width, height: rect.height };
807
+ }
808
+ async function getElementRect(client, selector) {
809
+ const result = await client.Runtime.evaluate({
810
+ expression: createElementRectExpression(selector),
811
+ awaitPromise: true,
812
+ returnByValue: true
813
+ });
814
+ if (result.exceptionDetails) {
815
+ throw new Error(result.exceptionDetails.text || "element query failed");
816
+ }
817
+ const value = result.result.value;
818
+ if (!isElementRect(value)) {
819
+ throw new Error(`element not found: ${selector}`);
820
+ }
821
+ return value;
822
+ }
823
+ function createElementRectExpression(selector) {
824
+ return `(() => {
825
+ const el = document.querySelector(${JSON.stringify(selector)});
826
+ if (!el) return null;
827
+ const rect = el.getBoundingClientRect();
828
+ return {
829
+ x: rect.x + window.scrollX,
830
+ y: rect.y + window.scrollY,
831
+ width: rect.width,
832
+ height: rect.height
833
+ };
834
+ })()`;
835
+ }
836
+ function createQuality(options) {
837
+ return options.format === "png" ? void 0 : options.quality;
838
+ }
839
+ function omitUndefined(value) {
840
+ return Object.fromEntries(
841
+ Object.entries(value).filter(([, item]) => item !== void 0)
842
+ );
843
+ }
844
+ function isElementRect(value) {
845
+ if (!value || typeof value !== "object") {
846
+ return false;
847
+ }
848
+ const rect = value;
849
+ return typeof rect.x === "number" && typeof rect.y === "number" && typeof rect.width === "number" && rect.width > 0 && typeof rect.height === "number" && rect.height > 0;
850
+ }
851
+
852
+ // src/mcp/tools/screenshot.ts
853
+ var DEFAULT_SCREENSHOT_TARGET = "viewport";
854
+ var DEFAULT_SCREENSHOT_FORMAT = "png";
855
+ var screenshotInputSchema = {
856
+ pageId: import_zod5.z.string().optional(),
857
+ target: import_zod5.z.enum(["viewport", "fullPage", "element"]).optional(),
858
+ selector: import_zod5.z.string().optional(),
859
+ format: import_zod5.z.enum(["png", "jpeg", "webp"]).optional(),
860
+ prefer: import_zod5.z.enum(["auto", "cdp", "runtime"]).optional(),
861
+ quality: import_zod5.z.number().optional(),
862
+ scale: import_zod5.z.number().optional(),
863
+ snapdom: import_zod5.z.record(import_zod5.z.string(), import_zod5.z.unknown()).optional()
864
+ };
865
+ function registerScreenshotTools(server, ctx) {
866
+ server.registerTool(
867
+ MCP_TOOL_NAMES.takeScreenshot,
868
+ {
869
+ description: "Take a page screenshot using CDP or snapdom fallback.",
870
+ inputSchema: screenshotInputSchema
871
+ },
872
+ async (input) => handleTakeScreenshot(ctx, input)
873
+ );
874
+ }
875
+ async function handleTakeScreenshot(ctx, input) {
876
+ const target = input.target ?? DEFAULT_SCREENSHOT_TARGET;
877
+ const format = input.format ?? DEFAULT_SCREENSHOT_FORMAT;
878
+ const prefer = input.prefer ?? ctx.options.screenshot.prefer;
879
+ if (target === "element" && !input.selector) {
880
+ return createToolError("selector is required when target is element");
881
+ }
882
+ if (prefer !== "runtime") {
883
+ const cdp = await connectCdpForPage(ctx, input.pageId);
884
+ if (cdp) {
885
+ try {
886
+ const screenshot = await cdpCaptureScreenshot({
887
+ client: cdp.client,
888
+ target,
889
+ selector: input.selector,
890
+ format,
891
+ quality: input.quality
892
+ });
893
+ return createScreenshotResponse(ctx, {
894
+ source: "cdp",
895
+ target,
896
+ format,
897
+ data: screenshot.data,
898
+ width: screenshot.width,
899
+ height: screenshot.height,
900
+ mimeType: createMimeType(format),
901
+ byteLength: getBase64ByteLength(screenshot.data)
902
+ });
903
+ } finally {
904
+ await closeCdpClient(cdp.client);
905
+ }
906
+ }
907
+ if (prefer === "cdp") {
908
+ return createToolError("CDP screenshot is unavailable");
909
+ }
910
+ }
911
+ return createRuntimeScreenshot(ctx, input, { target, format });
912
+ }
913
+ async function createRuntimeScreenshot(ctx, input, normalized) {
914
+ const result = await requestRuntimeData(ctx, (event) => {
915
+ void ctx.rpcServer?.takeScreenshot({
916
+ event,
917
+ target: normalized.target,
918
+ selector: input.selector,
919
+ format: normalized.format,
920
+ quality: input.quality,
921
+ scale: input.scale,
922
+ snapdom: {
923
+ ...ctx.options.screenshot.snapdom,
924
+ options: {
925
+ ...ctx.options.screenshot.snapdom.options,
926
+ ...input.snapdom
927
+ }
928
+ }
929
+ });
930
+ });
931
+ if (isScreenshotTooLarge(ctx, result)) {
932
+ return createToolError(
933
+ `screenshot is too large: ${String(result.byteLength)} bytes`
934
+ );
935
+ }
936
+ if (!isPlainRecord(result)) {
937
+ return createToolError("runtime screenshot returned an invalid response");
938
+ }
939
+ return createToolResponse(result);
940
+ }
941
+ function createScreenshotResponse(ctx, result) {
942
+ if (result.byteLength > ctx.options.screenshot.maxBytes) {
943
+ return createToolError(
944
+ `screenshot is too large: ${String(result.byteLength)} bytes`
945
+ );
946
+ }
947
+ return createToolResponse(result);
948
+ }
949
+ function isScreenshotTooLarge(ctx, result) {
950
+ return isPlainRecord(result) && "byteLength" in result && typeof result.byteLength === "number" && result.byteLength > ctx.options.screenshot.maxBytes;
951
+ }
952
+ function isPlainRecord(value) {
953
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
954
+ }
955
+ function createMimeType(format) {
956
+ return `image/${format}`;
957
+ }
958
+ function getBase64ByteLength(data) {
959
+ const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
960
+ return Math.ceil(data.length * 3 / 4) - padding;
961
+ }
962
+
720
963
  // src/mcp/tools/vue.ts
721
964
  var import_nanoid2 = require("nanoid");
722
- var import_zod5 = require("zod");
965
+ var import_zod6 = require("zod");
723
966
  function registerVueTools(server, ctx) {
724
967
  server.registerTool(
725
968
  MCP_TOOL_NAMES.getComponentTree,
@@ -732,7 +975,7 @@ function registerVueTools(server, ctx) {
732
975
  MCP_TOOL_NAMES.getComponentState,
733
976
  {
734
977
  description: "Get Vue component state.",
735
- inputSchema: { componentName: import_zod5.z.string() }
978
+ inputSchema: { componentName: import_zod6.z.string() }
736
979
  },
737
980
  async ({ componentName }) => requestVueData(ctx, (event) => {
738
981
  void ctx.rpcServer?.getInspectorState({ event, componentName });
@@ -743,10 +986,10 @@ function registerVueTools(server, ctx) {
743
986
  {
744
987
  description: "Edit Vue component state.",
745
988
  inputSchema: {
746
- componentName: import_zod5.z.string(),
747
- path: import_zod5.z.array(import_zod5.z.string()),
748
- value: import_zod5.z.string(),
749
- valueType: import_zod5.z.enum(["string", "number", "boolean", "object", "array"])
989
+ componentName: import_zod6.z.string(),
990
+ path: import_zod6.z.array(import_zod6.z.string()),
991
+ value: import_zod6.z.string(),
992
+ valueType: import_zod6.z.enum(["string", "number", "boolean", "object", "array"])
750
993
  }
751
994
  },
752
995
  ({ componentName, path: path5, value, valueType }) => {
@@ -766,7 +1009,7 @@ function registerVueTools(server, ctx) {
766
1009
  MCP_TOOL_NAMES.highlightComponent,
767
1010
  {
768
1011
  description: "Highlight a Vue component.",
769
- inputSchema: { componentName: import_zod5.z.string() }
1012
+ inputSchema: { componentName: import_zod6.z.string() }
770
1013
  },
771
1014
  ({ componentName }) => {
772
1015
  if (!ctx.rpcServer) {
@@ -794,7 +1037,7 @@ function registerVueTools(server, ctx) {
794
1037
  MCP_TOOL_NAMES.getPiniaState,
795
1038
  {
796
1039
  description: "Get Pinia store state.",
797
- inputSchema: { storeName: import_zod5.z.string() }
1040
+ inputSchema: { storeName: import_zod6.z.string() }
798
1041
  },
799
1042
  async ({ storeName }) => requestVueData(ctx, (event) => {
800
1043
  void ctx.rpcServer?.getPiniaState({ event, storeName });
@@ -837,6 +1080,7 @@ function createMcpServer(ctx, vite) {
837
1080
  });
838
1081
  registerPageTools(server, ctx, vite);
839
1082
  registerDomTools(server, ctx);
1083
+ registerScreenshotTools(server, ctx);
840
1084
  registerConsoleTools(server, ctx);
841
1085
  registerEvaluateTools(server, ctx);
842
1086
  registerNetworkTools(server, ctx);
@@ -846,18 +1090,39 @@ function createMcpServer(ctx, vite) {
846
1090
 
847
1091
  // src/mcp/transport.ts
848
1092
  var import_sse = require("@modelcontextprotocol/sdk/server/sse.js");
849
- function setupMcpTransport(base, server, vite) {
1093
+ var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
1094
+ function setupMcpTransport(base, createServer, vite) {
850
1095
  const transports = /* @__PURE__ */ new Map();
851
1096
  vite.middlewares.use(`${base}/sse`, (_req, res) => {
852
1097
  const transport = new import_sse.SSEServerTransport(`${base}/messages`, res);
853
- transports.set(transport.sessionId, transport);
1098
+ const server = createServer();
1099
+ transports.set(transport.sessionId, { server, transport });
854
1100
  res.on("close", () => {
855
1101
  transports.delete(transport.sessionId);
1102
+ void server.close();
856
1103
  });
857
1104
  void server.connect(transport).catch((error) => {
858
1105
  res.destroy(error instanceof Error ? error : new Error(String(error)));
859
1106
  });
860
1107
  });
1108
+ vite.middlewares.use(`${base}/mcp`, (req, res) => {
1109
+ if (req.method !== "POST") {
1110
+ res.statusCode = 405;
1111
+ res.end("Method Not Allowed");
1112
+ return;
1113
+ }
1114
+ const transport = new import_streamableHttp.StreamableHTTPServerTransport({
1115
+ sessionIdGenerator: void 0
1116
+ });
1117
+ const server = createServer();
1118
+ res.on("close", () => {
1119
+ void transport.close();
1120
+ void server.close();
1121
+ });
1122
+ void server.connect(transport).then(() => transport.handleRequest(req, res)).catch((error) => {
1123
+ res.destroy(error instanceof Error ? error : new Error(String(error)));
1124
+ });
1125
+ });
861
1126
  vite.middlewares.use(`${base}/messages`, (req, res) => {
862
1127
  if (req.method !== "POST") {
863
1128
  res.statusCode = 405;
@@ -871,13 +1136,13 @@ function setupMcpTransport(base, server, vite) {
871
1136
  res.end("Bad Request");
872
1137
  return;
873
1138
  }
874
- const transport = transports.get(sessionId);
875
- if (!transport) {
1139
+ const entry = transports.get(sessionId);
1140
+ if (!entry) {
876
1141
  res.statusCode = 404;
877
1142
  res.end("Not Found");
878
1143
  return;
879
1144
  }
880
- void transport.handlePostMessage(req, res).catch((error) => {
1145
+ void entry.transport.handlePostMessage(req, res).catch((error) => {
881
1146
  res.destroy(error instanceof Error ? error : new Error(String(error)));
882
1147
  });
883
1148
  });
@@ -898,6 +1163,10 @@ function createServerVueRuntimeRpc(ctx) {
898
1163
  onEvaluateScriptUpdated: (event, data) => {
899
1164
  void ctx.hooks.callHook(event, data);
900
1165
  },
1166
+ takeScreenshot: () => void 0,
1167
+ onScreenshotTaken: (event, data) => {
1168
+ void ctx.hooks.callHook(event, data);
1169
+ },
901
1170
  getInspectorTree: () => void 0,
902
1171
  onInspectorTreeUpdated: (event, data) => {
903
1172
  void ctx.hooks.callHook(event, data);
@@ -1189,13 +1458,19 @@ function createRuntimeInjectionController(options, getConfig) {
1189
1458
  if (importee === VIRTUAL_RUNTIME_ID) {
1190
1459
  return RESOLVED_VIRTUAL_RUNTIME_ID;
1191
1460
  }
1461
+ if (importee === VIRTUAL_SCREENSHOT_CONFIG_ID) {
1462
+ return RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID;
1463
+ }
1192
1464
  return void 0;
1193
1465
  },
1194
1466
  load(id) {
1195
- if (id !== RESOLVED_VIRTUAL_RUNTIME_ID) {
1196
- return void 0;
1467
+ if (id === RESOLVED_VIRTUAL_RUNTIME_ID) {
1468
+ return "import { startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';\nvoid startRuntimeClient();";
1469
+ }
1470
+ if (id === RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID) {
1471
+ return createScreenshotConfigModule(options);
1197
1472
  }
1198
- return "import { startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';\nvoid startRuntimeClient();";
1473
+ return void 0;
1199
1474
  },
1200
1475
  transformIndexHtml(html) {
1201
1476
  if (options.appendTo) {
@@ -1230,6 +1505,32 @@ ${code}`;
1230
1505
  }
1231
1506
  };
1232
1507
  }
1508
+ function createScreenshotConfigModule(options) {
1509
+ const paths = collectScreenshotImportPaths(options);
1510
+ const imports = paths.map((item, index) => `import * as m${String(index)} from ${JSON.stringify(item)};`).join("\n");
1511
+ const entries = paths.map((item, index) => `${JSON.stringify(item)}: m${String(index)}`).join(",\n ");
1512
+ return `${imports}
1513
+ export const screenshotModuleRegistry = {
1514
+ ${entries}
1515
+ };
1516
+ `;
1517
+ }
1518
+ function collectScreenshotImportPaths(options) {
1519
+ const paths = /* @__PURE__ */ new Set();
1520
+ for (const plugin of options.screenshot.snapdom.plugins) {
1521
+ paths.add(getPluginPath(plugin));
1522
+ }
1523
+ if (options.screenshot.snapdom.filter) {
1524
+ paths.add(options.screenshot.snapdom.filter);
1525
+ }
1526
+ if (options.screenshot.snapdom.fallbackURL) {
1527
+ paths.add(options.screenshot.snapdom.fallbackURL);
1528
+ }
1529
+ return [...paths];
1530
+ }
1531
+ function getPluginPath(plugin) {
1532
+ return typeof plugin === "string" ? plugin : plugin.path;
1533
+ }
1233
1534
 
1234
1535
  // src/plugin/mcpClientConfig/index.ts
1235
1536
  var import_node_path4 = __toESM(require("path"), 1);
@@ -1253,7 +1554,7 @@ function replaceOrAppendOwnedBlock(current, options) {
1253
1554
  const block = createCodexServerBlock(options);
1254
1555
  const matcher = createOwnedBlockMatcher(options.serverName);
1255
1556
  if (matcher.test(current)) {
1256
- return current.replace(matcher, block);
1557
+ return ensureTrailingNewline(current);
1257
1558
  }
1258
1559
  const separator = current.trim() ? "\n\n" : "";
1259
1560
  return `${trimEndNewline(current)}${separator}${block}`;
@@ -1295,6 +1596,10 @@ async function readOptionalTextFile(filePath) {
1295
1596
  function trimEndNewline(value) {
1296
1597
  return value.replace(/\n+$/u, "");
1297
1598
  }
1599
+ function ensureTrailingNewline(value) {
1600
+ return value.endsWith("\n") ? value : `${value}
1601
+ `;
1602
+ }
1298
1603
  function escapeRegExp(value) {
1299
1604
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1300
1605
  }
@@ -1311,12 +1616,15 @@ var import_node_path3 = __toESM(require("path"), 1);
1311
1616
  async function updateJsonMcpClientConfig(options) {
1312
1617
  try {
1313
1618
  const config = await readJsonConfig(options.configPath);
1314
- if (!isPlainRecord(config)) {
1619
+ if (!isPlainRecord2(config)) {
1315
1620
  warnConfigFailure(options, "config root must be a JSON object");
1316
1621
  return;
1317
1622
  }
1318
- const mcpServers = isPlainRecord(config.mcpServers) ? config.mcpServers : {};
1319
- mcpServers[options.serverName] = { url: options.mcpUrl };
1623
+ const mcpServers = isPlainRecord2(config.mcpServers) ? config.mcpServers : {};
1624
+ if (Object.hasOwn(mcpServers, options.serverName)) {
1625
+ return;
1626
+ }
1627
+ mcpServers[options.serverName] = { type: "sse", url: options.mcpUrl };
1320
1628
  config.mcpServers = mcpServers;
1321
1629
  await import_promises2.default.mkdir(import_node_path3.default.dirname(options.configPath), { recursive: true });
1322
1630
  await import_promises2.default.writeFile(
@@ -1345,7 +1653,7 @@ async function readOptionalTextFile2(filePath) {
1345
1653
  throw error;
1346
1654
  }
1347
1655
  }
1348
- function isPlainRecord(value) {
1656
+ function isPlainRecord2(value) {
1349
1657
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1350
1658
  }
1351
1659
  function warnConfigFailure(options, reason) {
@@ -1361,7 +1669,7 @@ function isNodeError2(error) {
1361
1669
  }
1362
1670
 
1363
1671
  // src/plugin/mcpClientConfig/index.ts
1364
- async function updateMcpClientConfigs(root, mcpUrl, options) {
1672
+ async function updateMcpClientConfigs(root, sseUrl, streamableHttpUrl, options) {
1365
1673
  const serverName = options.serverName;
1366
1674
  const jobs = [];
1367
1675
  if (options.cursor) {
@@ -1369,7 +1677,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1369
1677
  updateJsonMcpClientConfig({
1370
1678
  clientName: "Cursor",
1371
1679
  configPath: import_node_path4.default.join(root, ".cursor", "mcp.json"),
1372
- mcpUrl,
1680
+ mcpUrl: sseUrl,
1373
1681
  serverName
1374
1682
  })
1375
1683
  );
@@ -1378,7 +1686,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1378
1686
  jobs.push(
1379
1687
  updateCodexMcpClientConfig({
1380
1688
  configPath: import_node_path4.default.join(root, ".codex", "config.toml"),
1381
- mcpUrl,
1689
+ mcpUrl: streamableHttpUrl,
1382
1690
  serverName
1383
1691
  })
1384
1692
  );
@@ -1388,7 +1696,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1388
1696
  updateJsonMcpClientConfig({
1389
1697
  clientName: "Claude Code",
1390
1698
  configPath: import_node_path4.default.join(root, ".mcp.json"),
1391
- mcpUrl,
1699
+ mcpUrl: sseUrl,
1392
1700
  serverName
1393
1701
  })
1394
1702
  );
@@ -1398,7 +1706,7 @@ async function updateMcpClientConfigs(root, mcpUrl, options) {
1398
1706
  updateJsonMcpClientConfig({
1399
1707
  clientName: "Trae",
1400
1708
  configPath: import_node_path4.default.join(root, ".trae", "mcp.json"),
1401
- mcpUrl,
1709
+ mcpUrl: sseUrl,
1402
1710
  serverName
1403
1711
  })
1404
1712
  );
@@ -1434,8 +1742,11 @@ function vueMcpNext(userOptions = {}) {
1434
1742
  timeout: -1
1435
1743
  }
1436
1744
  );
1437
- const mcpServer = createMcpServer(ctx, server);
1438
- setupMcpTransport(options.mcpPath, mcpServer, server);
1745
+ setupMcpTransport(
1746
+ options.mcpPath,
1747
+ () => createMcpServer(ctx, server),
1748
+ server
1749
+ );
1439
1750
  server.ws.on(
1440
1751
  "vite-plugin-vue-mcp-next:page-connected",
1441
1752
  (payload) => {
@@ -1462,12 +1773,21 @@ function vueMcpNext(userOptions = {}) {
1462
1773
  }
1463
1774
  );
1464
1775
  const port = String(server.config.server.port || 5173);
1465
- const mcpUrl = `http://${options.host}:${port}${options.mcpPath}/sse`;
1776
+ const mcpSseUrl = `http://${options.host}:${port}${options.mcpPath}/sse`;
1777
+ const mcpStreamableHttpUrl = `http://${options.host}:${port}${options.mcpPath}/mcp`;
1466
1778
  const root = (0, import_vite2.searchForWorkspaceRoot)(server.config.root);
1467
- await updateMcpClientConfigs(root, mcpUrl, options.mcpClients);
1779
+ await updateMcpClientConfigs(
1780
+ root,
1781
+ mcpSseUrl,
1782
+ mcpStreamableHttpUrl,
1783
+ options.mcpClients
1784
+ );
1468
1785
  if (options.printUrl) {
1469
1786
  setTimeout(() => {
1470
- console.log(` \u279C MCP: Server is running at ${mcpUrl}`);
1787
+ console.log(` \u279C MCP: SSE server is running at ${mcpSseUrl}`);
1788
+ console.log(
1789
+ ` \u279C MCP: Streamable HTTP server is running at ${mcpStreamableHttpUrl}`
1790
+ );
1471
1791
  }, 300);
1472
1792
  }
1473
1793
  server.httpServer?.once("close", () => {