@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/README.md +88 -16
- package/dist/index.cjs +351 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.js +351 -31
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +144 -1
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.js +134 -1
- package/dist/runtime/client.js.map +1 -1
- package/package.json +2 -1
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
|
|
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:
|
|
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:
|
|
747
|
-
path:
|
|
748
|
-
value:
|
|
749
|
-
valueType:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
875
|
-
if (!
|
|
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
|
|
1196
|
-
return
|
|
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
|
|
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
|
|
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 (!
|
|
1619
|
+
if (!isPlainRecord2(config)) {
|
|
1315
1620
|
warnConfigFailure(options, "config root must be a JSON object");
|
|
1316
1621
|
return;
|
|
1317
1622
|
}
|
|
1318
|
-
const mcpServers =
|
|
1319
|
-
mcpServers
|
|
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
|
|
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,
|
|
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
|
-
|
|
1438
|
-
|
|
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
|
|
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(
|
|
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:
|
|
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", () => {
|