{"record":{"id":"becae0f4ed62b7dd","repo":"1Panel-dev/1Panel","slug":"aitools-mcp-importmcpjsonerror","errorCode":null,"errorMessage":"aiTools.mcp.importMcpJsonError","messagePattern":"aiTools\\.mcp\\.importMcpJsonError","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"frontend/src/views/ai/mcp/server/import/index.vue","lineNumber":56,"sourceCode":"<script lang=\"ts\" setup>\nimport i18n from '@/lang';\nimport { ref } from 'vue';\n\nconst submitVisible = ref(false);\nconst mcpServerJson = ref();\nconst mcpServerConfig = ref();\n\nconst acceptParams = (): void => {\n    mcpServerJson.value = '';\n    submitVisible.value = true;\n};\nconst emit = defineEmits(['confirm', 'cancel']);\n\nconst onConfirm = async () => {\n    try {\n        const data = JSON.parse(mcpServerJson.value);\n        if (!data.mcpServers || typeof data.mcpServers !== 'object') {\n            throw new Error(i18n.global.t('aiTools.mcp.importMcpJsonError'));\n        }\n        mcpServerConfig.value = Object.entries(data.mcpServers).map(([name, config]: any) => ({\n            name,\n            command: [config.command, ...config.args].join(' '),\n            environments: config.env ? Object.entries(config.env).map(([key, value]) => ({ key, value })) : [],\n            ssePath: '/' + name,\n            containerName: name,\n        }));\n    } catch (error) {\n        return;\n    }\n    emit('confirm', mcpServerConfig.value);\n    submitVisible.value = false;\n};\n\nconst onCancel = async () => {\n    emit('cancel');\n    submitVisible.value = false;","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/1Panel-dev/1Panel/blob/5ac7c808815b0691009cd390414f03471312262f/frontend/src/views/ai/mcp/server/import/index.vue#L38-L74","documentation":"In the MCP-server import dialog (frontend/src/views/ai/mcp/server/import/index.vue:56), the parsed JSON must contain a top-level `mcpServers` object; anything else throws aiTools.mcp.importMcpJsonError. Note the local try/catch immediately swallows the thrown Error and returns, so the user sees no feedback and the dialog simply does not proceed — the message currently never surfaces.","triggerScenarios":"Pasting a Claude/Cursor config whose root key is `mcpServers` nested under another key (e.g. a full settings.json with `mcpServers` at depth 2); pasting `{\"servers\": {...}}`; pasting a JSON array; passing invalid JSON (caught by the same catch).","commonSituations":"Exporting from a client that wraps servers under a different root (e.g. `mcpServers` inside `projects` in some Claude configs); hand-written configs; extra commas/BOM breaking JSON.parse.","solutions":["Ensure the pasted JSON is an object with `mcpServers` at the TOP level: {\"mcpServers\":{\"name\":{\"command\":\"npx\",\"args\":[...]}}}","If copying from a nested settings file, extract only the mcpServers object before pasting","Also confirm each entry has `command` (string) and optionally `args` (array) and `env` (object), since `[config.command, ...config.args]` throws for malformed entries (also swallowed)","Fix the swallowed-error UX: display the caught error (e.g. Message.error) instead of a bare return, so invalid input is visible"],"exampleFix":"// before (nested — throws, silently swallowed)\n{ \"projects\": { \"mcpServers\": { \"foo\": { \"command\": \"npx\" } } } }\n// after\n{ \"mcpServers\": { \"foo\": { \"command\": \"npx\", \"args\": [\"-y\", \"foo\"] } } }\n\n// component fix: surface the error\n// catch (error) { Message.error(error instanceof Error ? error.message : 'import failed'); return; }","handlingStrategy":"validation","validationCode":"const isMcpImportShape = (v: unknown): v is { mcpServers: Record<string, { command: string; args?: string[]; env?: Record<string, string> }> } =>\n    typeof v === 'object' && v !== null &&\n    typeof (v as any).mcpServers === 'object' && (v as any).mcpServers !== null &&\n    Object.values((v as any).mcpServers).every((s: any) => typeof s?.command === 'string' && Array.isArray(s?.args ?? []));","typeGuard":"// see validationCode — isMcpImportShape narrows the parsed JSON before mapping","tryCatchPattern":"try { const data = JSON.parse(raw); if (!isMcpImportShape(data)) throw new Error(t('aiTools.mcp.importMcpJsonError')); }\ncatch (e) { Message.error(e instanceof Error ? e.message : t('aiTools.mcp.importMcpJsonError')); return; } // show, don't swallow","preventionTips":["Keep mcpServers at the top level of the pasted JSON","Validate per-entry command/args shapes before Object.entries mapping","Replace the silent `catch { return; }` with a visible error toast so users know why nothing happened"],"tags":["frontend","mcp","json-import","silent-failure"],"backgroundTag":null,"analyzedSha":"5ac7c808815b0691009cd390414f03471312262f","analyzedAt":"2026-08-15T14:02:06.953Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}