Skip to content

parse_mcp_config

Python · package toolnexus · SPEC §2 · python/src/toolnexus/mcp_source.py

def parse_mcp_config(input: str | dict[str, Any]) -> McpConfig

Reads MCP server configuration and normalises it into a flat dict of server name → config. It connects to nothing — no child processes, no HTTP. That is the whole point: it is the cheap, synchronous check you can run before paying for load_mcp.

  • Validate at startup or in a test, so a typo in mcp.json fails immediately rather than halfway through connecting to five servers.
  • Inspect or modify config before loading — filter servers by environment, inject a header, disable one in CI.
  • Accept config from somewhere other than a file — a database, an env var, an API response.

It accepts a path or a dict, wrapped under mcpServers, servers, or mcp — three spellings in the wild — and returns one shape.

This is examples/mcp.json, the fixture every port is tested against.

from toolnexus import parse_mcp_config
# A path is read and json-parsed for you.
config = parse_mcp_config("examples/mcp.json")
# The `mcpServers` wrapper is unwrapped — you get the servers directly.
assert sorted(config.keys()) == ["everything", "example-remote"]
local = config["everything"]
assert local["type"] == "local"
assert local["command"] == ["npx", "-y", "@modelcontextprotocol/server-everything"]
assert local["timeout"] == 30000
remote = config["example-remote"]
assert remote["type"] == "remote"
assert remote["url"] == "https://example.com/mcp"
# Disabled servers are still returned — parsing does not filter.
assert remote["enabled"] is False
print("ok:", ", ".join(sorted(config.keys())))

mcpServers, servers and mcp all mean the same thing.

from toolnexus import parse_mcp_config
server = {"type": "local", "command": ["echo", "hi"]}
wrapped = parse_mcp_config({"mcpServers": {"a": server}})
alt = parse_mcp_config({"servers": {"a": server}})
short = parse_mcp_config({"mcp": {"a": server}})
# All three normalise to the same flat dict.
for c in (wrapped, alt, short):
assert list(c.keys()) == ["a"]
assert c["a"]["command"] == ["echo", "hi"]
print("ok: 3 spellings ->", ",".join(wrapped.keys()))

3. Validate before loading, and fail loudly

Section titled “3. Validate before loading, and fail loudly”

The pattern this function exists for — check the config, then decide whether to connect.

from toolnexus import parse_mcp_config
def validate(raw):
config = parse_mcp_config(raw)
enabled, problems = [], []
for name, cfg in config.items():
# Disabled either way round: `enabled: false` or `disabled: true`.
if cfg.get("disabled") is True or cfg.get("enabled") is False:
continue
enabled.append(name)
if cfg.get("type") == "remote":
if not cfg.get("url"):
problems.append(f"{name}: remote server without a url")
elif not cfg.get("command"):
problems.append(f"{name}: local server without a command")
return enabled, problems
good_enabled, good_problems = validate({
"mcpServers": {
"ok_local": {"type": "local", "command": ["npx", "server"]},
"ok_remote": {"type": "remote", "url": "https://example.com/mcp"},
"off": {"type": "local", "command": ["x"], "enabled": False},
}
})
assert good_enabled == ["ok_local", "ok_remote"]
assert good_problems == []
_, bad_problems = validate({
"mcpServers": {
"broken_remote": {"type": "remote"},
"broken_local": {"type": "local", "command": []},
}
})
assert len(bad_problems) == 2
# A missing file raises rather than returning a half-config.
try:
parse_mcp_config("does-not-exist.json")
raise AssertionError("expected an error")
except FileNotFoundError:
pass
print("ok:", ",".join(good_enabled), "| problems:", len(bad_problems))
Input Behaviour
"./mcp.json" Read from disk and json.loaded. Raises if missing or malformed.
{"mcpServers": {...}} Unwrapped.
{"servers": {...}} Unwrapped — alternate spelling.
{"mcp": {...}} Unwrapped — alternate spelling.
Field Applies to What it is
type both "local" or "remote".
command local Argv list, e.g. ["npx", "-y", "server"].
environment / env local Extra environment for the child process.
cwd local Working directory for the child.
url remote Streamable-HTTP endpoint.
headers remote ${ENV_VAR} values expand at call time and are never logged.
enabled / disabled both Preserved by parsing; honoured by load_mcp.
timeout both Per-server connect/call budget in ms.
tools both Per-server allowlist keyed on the original tool name.