@xiaou66/vite-plugin-vue-mcp-next 0.0.3 → 0.0.6
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 +71 -2
- package/dist/index.cjs +302 -14
- 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 +302 -14
- package/dist/index.js.map +1 -1
- package/dist/runtime/client.cjs +148 -1
- package/dist/runtime/client.cjs.map +1 -1
- package/dist/runtime/client.d.cts +15 -1
- package/dist/runtime/client.d.ts +15 -1
- package/dist/runtime/client.js +137 -1
- package/dist/runtime/client.js.map +1 -1
- package/package.json +2 -1
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
|
|
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:
|
|
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:
|
|
710
|
-
path:
|
|
711
|
-
value:
|
|
712
|
-
valueType:
|
|
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:
|
|
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:
|
|
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);
|
|
@@ -882,6 +1126,10 @@ function createServerVueRuntimeRpc(ctx) {
|
|
|
882
1126
|
onEvaluateScriptUpdated: (event, data) => {
|
|
883
1127
|
void ctx.hooks.callHook(event, data);
|
|
884
1128
|
},
|
|
1129
|
+
takeScreenshot: () => void 0,
|
|
1130
|
+
onScreenshotTaken: (event, data) => {
|
|
1131
|
+
void ctx.hooks.callHook(event, data);
|
|
1132
|
+
},
|
|
885
1133
|
getInspectorTree: () => void 0,
|
|
886
1134
|
onInspectorTreeUpdated: (event, data) => {
|
|
887
1135
|
void ctx.hooks.callHook(event, data);
|
|
@@ -1173,13 +1421,19 @@ function createRuntimeInjectionController(options, getConfig) {
|
|
|
1173
1421
|
if (importee === VIRTUAL_RUNTIME_ID) {
|
|
1174
1422
|
return RESOLVED_VIRTUAL_RUNTIME_ID;
|
|
1175
1423
|
}
|
|
1424
|
+
if (importee === VIRTUAL_SCREENSHOT_CONFIG_ID) {
|
|
1425
|
+
return RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID;
|
|
1426
|
+
}
|
|
1176
1427
|
return void 0;
|
|
1177
1428
|
},
|
|
1178
1429
|
load(id) {
|
|
1179
|
-
if (id
|
|
1180
|
-
return
|
|
1430
|
+
if (id === RESOLVED_VIRTUAL_RUNTIME_ID) {
|
|
1431
|
+
return createRuntimeModule();
|
|
1432
|
+
}
|
|
1433
|
+
if (id === RESOLVED_VIRTUAL_SCREENSHOT_CONFIG_ID) {
|
|
1434
|
+
return createScreenshotConfigModule(options);
|
|
1181
1435
|
}
|
|
1182
|
-
return
|
|
1436
|
+
return void 0;
|
|
1183
1437
|
},
|
|
1184
1438
|
transformIndexHtml(html) {
|
|
1185
1439
|
if (options.appendTo) {
|
|
@@ -1214,6 +1468,40 @@ ${code}`;
|
|
|
1214
1468
|
}
|
|
1215
1469
|
};
|
|
1216
1470
|
}
|
|
1471
|
+
function createRuntimeModule() {
|
|
1472
|
+
return [
|
|
1473
|
+
"import { setScreenshotModuleRegistry, startRuntimeClient } from '@xiaou66/vite-plugin-vue-mcp-next/runtime/client';",
|
|
1474
|
+
`import { screenshotModuleRegistry } from '${VIRTUAL_SCREENSHOT_CONFIG_ID}';`,
|
|
1475
|
+
"setScreenshotModuleRegistry(screenshotModuleRegistry);",
|
|
1476
|
+
"void startRuntimeClient();"
|
|
1477
|
+
].join("\n");
|
|
1478
|
+
}
|
|
1479
|
+
function createScreenshotConfigModule(options) {
|
|
1480
|
+
const paths = collectScreenshotImportPaths(options);
|
|
1481
|
+
const imports = paths.map((item, index) => `import * as m${String(index)} from ${JSON.stringify(item)};`).join("\n");
|
|
1482
|
+
const entries = paths.map((item, index) => `${JSON.stringify(item)}: m${String(index)}`).join(",\n ");
|
|
1483
|
+
return `${imports}
|
|
1484
|
+
export const screenshotModuleRegistry = {
|
|
1485
|
+
${entries}
|
|
1486
|
+
};
|
|
1487
|
+
`;
|
|
1488
|
+
}
|
|
1489
|
+
function collectScreenshotImportPaths(options) {
|
|
1490
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1491
|
+
for (const plugin of options.screenshot.snapdom.plugins) {
|
|
1492
|
+
paths.add(getPluginPath(plugin));
|
|
1493
|
+
}
|
|
1494
|
+
if (options.screenshot.snapdom.filter) {
|
|
1495
|
+
paths.add(options.screenshot.snapdom.filter);
|
|
1496
|
+
}
|
|
1497
|
+
if (options.screenshot.snapdom.fallbackURL) {
|
|
1498
|
+
paths.add(options.screenshot.snapdom.fallbackURL);
|
|
1499
|
+
}
|
|
1500
|
+
return [...paths];
|
|
1501
|
+
}
|
|
1502
|
+
function getPluginPath(plugin) {
|
|
1503
|
+
return typeof plugin === "string" ? plugin : plugin.path;
|
|
1504
|
+
}
|
|
1217
1505
|
|
|
1218
1506
|
// src/plugin/mcpClientConfig/index.ts
|
|
1219
1507
|
import path4 from "path";
|
|
@@ -1299,11 +1587,11 @@ import path3 from "path";
|
|
|
1299
1587
|
async function updateJsonMcpClientConfig(options) {
|
|
1300
1588
|
try {
|
|
1301
1589
|
const config = await readJsonConfig(options.configPath);
|
|
1302
|
-
if (!
|
|
1590
|
+
if (!isPlainRecord2(config)) {
|
|
1303
1591
|
warnConfigFailure(options, "config root must be a JSON object");
|
|
1304
1592
|
return;
|
|
1305
1593
|
}
|
|
1306
|
-
const mcpServers =
|
|
1594
|
+
const mcpServers = isPlainRecord2(config.mcpServers) ? config.mcpServers : {};
|
|
1307
1595
|
if (Object.hasOwn(mcpServers, options.serverName)) {
|
|
1308
1596
|
return;
|
|
1309
1597
|
}
|
|
@@ -1336,7 +1624,7 @@ async function readOptionalTextFile2(filePath) {
|
|
|
1336
1624
|
throw error;
|
|
1337
1625
|
}
|
|
1338
1626
|
}
|
|
1339
|
-
function
|
|
1627
|
+
function isPlainRecord2(value) {
|
|
1340
1628
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1341
1629
|
}
|
|
1342
1630
|
function warnConfigFailure(options, reason) {
|