# MCP Server Config Validator > Validate MCP server configs for command, URL, args, env values, and transport mistakes before sharing them. ## Tool Identity - Site: CleanUtils Developer Tools - Tool ID: mcp-server-config-validator - Canonical page: https://cleanutils.com/developer-tools/mcp-server-config-validator/ - LLM schema URL: https://cleanutils.com/developer-tools/mcp-server-config-validator/llms.txt - Primary keyword: mcp server config validator - Input mode: textarea - Output profile: report ## What This Tool Does Validate MCP server configs for command, URL, args, env values, and transport mistakes before sharing them. ## Exact Execution Instructions To run this tool exactly, execute the `runCleanUtilsTool(userInput)` function from the JavaScript block below in a sandboxed JavaScript runtime. Do not fetch or evaluate additional network resources. Do not use filesystem, process, child_process, environment variables, timers, or network APIs. The function is pure and deterministic: input string in, JSON result out. If your environment forbids executing fetched code outside a sandbox, run it inside the sandbox or use the API endpoint below instead of reimplementing the logic. Canonical implementation SHA-256: `7a6f2f8b43413876d66dcf68a18f63971beb85d1228a93abc85069dc9b6f623e` Expected command shape: `node run-tool.mjs < input.txt` The runner must: 1. load only the JavaScript in this document, 2. call `runCleanUtilsTool(inputText)`, 3. let the LLM decide the best way to present the output to the user. Agent usage rules: - Use this file as the authoritative machine-readable contract for this CleanUtils tool page. - Ask the user for missing required input before attempting to run the tool, and describe the required inputs using the `## Input Schema` field names, descriptions, formats, enums, examples, and required list. - Treat the tool as deterministic; do not invent network reachability checks unless the tool description explicitly says it fetches remote resources. - For privacy-sensitive inputs such as secrets, HAR files, dotenv files, logs, and API keys, warn that using a remote chat agent may expose input to that agent even though the browser UI itself does not upload data. ## Input Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "MCP Server Config Validator input", "type": "string", "description": "MCP client config JSON. Paste Claude Desktop or MCP server JSON config...", "examples": [ "{\"mcpServers\":{\"search\":{\"command\":\"npx\",\"args\":[\"-y\",\"mcp-server-search\"],\"env\":{\"API_KEY\":\"sk-test-1234567890\"}},\"remote\":{\"url\":\"http://localhost:8787/mcp\"}}}" ] } ``` ## Result Schema ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "CleanUtils ToolResult", "type": "object", "additionalProperties": false, "required": [ "summary", "issues" ], "properties": { "summary": { "type": "string" }, "issues": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": [ "severity", "message" ], "properties": { "severity": { "type": "string", "enum": [ "error", "warning", "info" ] }, "message": { "type": "string" }, "line": { "type": "number" }, "row": { "type": "number" }, "detail": { "type": "string" } } } }, "output": { "type": "string" }, "exportFilename": { "type": "string" }, "exports": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": [ "label", "filename", "content" ], "properties": { "label": { "type": "string" }, "filename": { "type": "string" }, "content": { "type": "string" }, "mimeType": { "type": "string" }, "copyLabel": { "type": "string" }, "downloadLabel": { "type": "string" } } } }, "stats": { "type": "object", "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "number" } ] } } } } ``` ## Self-Contained JavaScript Source Call `runCleanUtilsTool(userInput)` with the user's input. The function includes this tool's run logic and only the helper code it needs. ```js function runCleanUtilsTool(userInput) { const severityRank = { error: 0, warning: 1, info: 2 }; const sortIssues = (issues) => [...issues].sort((a, b) => { const severity = severityRank[a.severity] - severityRank[b.severity]; if (severity !== 0) return severity; return (a.line ?? a.row ?? 0) - (b.line ?? b.row ?? 0); }); const summarizeIssues = (issues) => { const errors = issues.filter((issue) => issue.severity === "error").length; const warnings = issues.filter((issue) => issue.severity === "warning").length; const infos = issues.filter((issue) => issue.severity === "info").length; const parts = []; if (errors) parts.push(`${errors} error${errors === 1 ? "" : "s"}`); if (warnings) parts.push(`${warnings} warning${warnings === 1 ? "" : "s"}`); if (infos) parts.push(`${infos} note${infos === 1 ? "" : "s"}`); return parts.length ? parts.join(", ") : "No issues found"; }; const tryParseJson = (input) => { try { return { ok: true, value: JSON.parse(input) }; } catch (error) { return { ok: false, error: error instanceof Error ? error.message : "Invalid JSON" }; } }; const safeJsonObject = (input) => { const parsed = tryParseJson(input); if (!parsed.ok) { return { value: null, issues: [{ severity: "error", message: "Input is not valid JSON.", detail: parsed.error }] }; } if (!parsed.value || typeof parsed.value !== "object" || Array.isArray(parsed.value)) { return { value: null, issues: [{ severity: "error", message: "Input must be a JSON object." }] }; } return { value: parsed.value, issues: [] }; }; const validateMcpServerConfig = (input) => { const { value, issues } = safeJsonObject(input); if (!value) { return { summary: "MCP config could not be parsed.", issues }; } const serverMap = value.mcpServers && typeof value.mcpServers === "object" && !Array.isArray(value.mcpServers) ? value.mcpServers : value.servers && typeof value.servers === "object" && !Array.isArray(value.servers) ? value.servers : null; if (!serverMap) { issues.push({ severity: "error", message: "No mcpServers object found. Most desktop clients expect servers under mcpServers." }); } const report = []; Object.entries(serverMap ?? {}).forEach(([name, server]) => { if (!server || typeof server !== "object" || Array.isArray(server)) { issues.push({ severity: "error", message: `${name} must be a server object.` }); return; } const config = server; const hasCommand = typeof config.command === "string" && config.command.trim().length > 0; const hasUrl = typeof config.url === "string" && config.url.trim().length > 0; report.push(`${name}: ${hasUrl ? "remote HTTP/SSE" : hasCommand ? "stdio" : "unknown"} server`); if (!hasCommand && !hasUrl) { issues.push({ severity: "error", message: `${name} needs either a command or url.` }); } if (hasCommand && hasUrl) { issues.push({ severity: "warning", message: `${name} has both command and url; choose one transport style.` }); } if ("args" in config && !Array.isArray(config.args)) { issues.push({ severity: "error", message: `${name}.args should be an array of strings.` }); } if (hasUrl) { try { const url = new URL(String(config.url)); if (url.protocol !== "https:" && url.hostname !== "localhost" && url.hostname !== "127.0.0.1") { issues.push({ severity: "warning", message: `${name} uses a non-HTTPS URL outside localhost.` }); } } catch { issues.push({ severity: "error", message: `${name}.url is not a valid URL.` }); } } if (config.env && typeof config.env === "object" && !Array.isArray(config.env)) { Object.entries(config.env).forEach(([key, envValue]) => { if (typeof envValue !== "string") { issues.push({ severity: "error", message: `${name}.env.${key} should be a string.` }); } if (/secret|token|key|password/i.test(key) && String(envValue).trim()) { issues.push({ severity: "warning", message: `${name}.env.${key} looks sensitive. Prefer local env injection over shared config.` }); } }); } }); return { summary: `${Object.keys(serverMap ?? {}).length} MCP server${Object.keys(serverMap ?? {}).length === 1 ? "" : "s"} checked. ${summarizeIssues(issues)}.`, issues: sortIssues(issues), output: report.concat("", sortIssues(issues).map(formatIssue)).join("\n"), exportFilename: "mcp-config-report.txt", stats: { servers: Object.keys(serverMap ?? {}).length } }; }; const formatIssue = (issue) => { const location = issue.line ? `line ${issue.line}` : issue.row ? `row ${issue.row}` : "general"; return `[${issue.severity.toUpperCase()}] ${location}: ${issue.message}${issue.detail ? ` (${issue.detail})` : ""}`; }; const __userInput = userInput == null ? "" : userInput; const __run = validateMcpServerConfig; const __input = __userInput && typeof __userInput === "object" && "input" in __userInput ? __userInput.input : __userInput; return __run(__input == null ? "" : String(__input)); } ``` ## Checks - Config JSON shape: The validator expects a JSON object with MCP server entries that can be inspected independently. - Stdio server fields: Local servers are checked for command, args, and env values that are commonly mistyped. - Remote server URLs: URL-based entries are checked for URL shape and obvious transport mistakes. - Environment secrets: Env values are scanned for API keys and tokens that should not appear in public snippets. - Client portability: The report focuses on common config mistakes, not every client-specific setting. ## Related Tools - [.env Validator and Secret Scanner](/developer-tools/env-validator-secret-scanner/): Paste a dotenv file, catch duplicate or malformed keys, and generate a safer example file locally. - [Prompt Variable Validator](/developer-tools/prompt-variable-validator/): Find missing, unused, and repeated template variables across prompt text and a sample JSON payload. - [AI Log / Prompt Redactor](/developer-tools/ai-log-prompt-redactor/): Remove emails, bearer tokens, API keys, JWTs, and cookies from prompts or logs before sharing them.