Spaces:
Sleeping
Sleeping
File size: 4,835 Bytes
3573b21 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | """MCP config validator — paste an MCP client config, get back issues.
Validates against the common shape used by Claude Desktop, Cursor, Cline,
Windsurf, and Zed. Doesn't try to BE all of those — just a quick sanity check.
"""
import json
import gradio as gr
REQUIRED_TOP = {"mcpServers"}
SERVER_REQUIRED = {"command"}
SERVER_OPTIONAL = {"args", "env", "cwd", "transport", "url"}
def validate(config_text: str):
if not config_text.strip():
return "_Paste a config to validate._"
try:
cfg = json.loads(config_text)
except json.JSONDecodeError as e:
return f"❌ **Invalid JSON:** {e.msg} at line {e.lineno}, col {e.colno}"
issues = []
suggestions = []
if not isinstance(cfg, dict):
return "❌ Config must be a JSON object at the top level."
if "mcpServers" not in cfg:
issues.append("Missing top-level `mcpServers` key.")
return _format(issues, suggestions, cfg)
servers = cfg["mcpServers"]
if not isinstance(servers, dict):
issues.append("`mcpServers` must be an object mapping server name → server config.")
return _format(issues, suggestions, cfg)
if not servers:
issues.append("`mcpServers` is empty — no servers will be loaded.")
for name, scfg in servers.items():
if not isinstance(scfg, dict):
issues.append(f"Server `{name}` is not an object.")
continue
# Either command-based (stdio) or url-based (sse/http) — must have one
if "command" not in scfg and "url" not in scfg:
issues.append(f"Server `{name}` has neither `command` (stdio) nor `url` (sse/http).")
if "command" in scfg and "url" in scfg:
suggestions.append(f"Server `{name}` has both `command` and `url`. Most clients use one.")
if "args" in scfg and not isinstance(scfg["args"], list):
issues.append(f"Server `{name}`: `args` must be a list of strings.")
if "env" in scfg and not isinstance(scfg["env"], dict):
issues.append(f"Server `{name}`: `env` must be an object of string→string.")
unknown = set(scfg.keys()) - SERVER_REQUIRED - SERVER_OPTIONAL
if unknown:
suggestions.append(f"Server `{name}` has unknown keys: {sorted(unknown)} (might be client-specific).")
return _format(issues, suggestions, cfg)
def _format(issues, suggestions, cfg):
n_servers = len(cfg.get("mcpServers", {})) if isinstance(cfg.get("mcpServers"), dict) else 0
rows = []
if not issues:
rows.append(f"✅ **Looks good** — {n_servers} server(s) configured.")
else:
rows.append(f"❌ **{len(issues)} issue(s):**")
for i in issues:
rows.append(f"- {i}")
if suggestions:
rows.append("")
rows.append("💡 **Suggestions:**")
for s in suggestions:
rows.append(f"- {s}")
return "\n".join(rows)
EXAMPLES = [
['{"mcpServers": {"filesystem": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me"]}}}'],
['{"mcpServers": {"agentfit": {"command": "npx", "args": ["-y", "@mukundakatta/agentfit-mcp"]}}}'],
['{"mcpServers": {"broken": {"args": ["only-args"]}}}'],
['{"mcpServers": {}}'],
['{}'],
['not even json'],
]
with gr.Blocks(title="MCP Config Validator", theme=gr.themes.Soft()) as demo:
gr.Markdown(
"""
# MCP Config Validator
Paste your MCP client config (Claude Desktop / Cursor / Cline / Windsurf / Zed style) and get a quick sanity check.
Doesn't replace your client's own validator — just catches the common mistakes (missing `command`, wrong types, empty servers map) before you restart the client.
Sample configs from [`mcp-config-examples`](https://huggingface.co/datasets/mukunda1729/mcp-config-examples).
"""
)
with gr.Row():
with gr.Column():
txt = gr.Code(
value=json.dumps({
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me"],
}
}
}, indent=2),
language="json",
label="MCP config",
lines=14,
)
btn = gr.Button("Validate", variant="primary")
out = gr.Markdown()
btn.click(validate, inputs=txt, outputs=out)
gr.Examples(examples=EXAMPLES, inputs=txt)
gr.Markdown(
"""
---
Part of [The Agent Reliability Stack](https://mukundakatta.github.io/agent-stack/) · MIT licensed
"""
)
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)
|