Panniantong/Agent-Reach · error · McporterConfigError

mcporter server 定义必须是对象

Error message

mcporter server 定义必须是对象

What it means

Raised when a value under mcpServers is not a JSON object. Each server must be a mapping (command/args/env or url etc.); a string, array, number, or null value fails the isinstance(definition, dict) check and aborts the whole inspection.

Source

Thrown at agent_reach/channels/mcporter.py:62

    """
    selected_layers = _select_config_layers(root_dir)
    if not selected_layers:
        return McporterConfigInspection(frozenset(), None)

    names = set()
    imports_unchecked = False
    sources = []
    for config_path, source in selected_layers:
        payload = _read_config_object(config_path)
        servers = payload.get("mcpServers")
        if not isinstance(servers, dict):
            raise McporterConfigError("mcporter 配置缺少 mcpServers 对象")

        for name, definition in servers.items():
            if not isinstance(name, str) or not name.strip():
                raise McporterConfigError("mcporter 配置包含无效的 server name")
            if not isinstance(definition, dict):
                raise McporterConfigError("mcporter server 定义必须是对象")
            names.add(name.casefold())

        imports = payload.get("imports", _MISSING)
        if imports is _MISSING:
            # mcporter defaults to importing supported editor configs when the
            # key is omitted. Doctor intentionally does not open those files.
            imports_unchecked = True
        elif not isinstance(imports, list) or not all(
            isinstance(item, str) for item in imports
        ):
            raise McporterConfigError("mcporter imports 必须是字符串列表")
        elif imports:
            imports_unchecked = True
        sources.append(source)

    return McporterConfigInspection(
        frozenset(names),
        "+".join(sources),

View on GitHub (pinned to 93ae1d18c3)

Solutions

  1. Wrap each value in an object with the standard fields: '"name": { "command": "...", "args": [...] }' or '"name": { "url": "https://..." }'
  2. Validate with: jq -e '.mcpServers | to_entries | all(.value | type == "object")' file.json
  3. To disable a server, delete the key rather than setting it to null

Example fix

// before
 { "mcpServers": { "github": "ghcop mcp serve" } }
// after
 { "mcpServers": { "github": { "command": "ghcop", "args": ["mcp", "serve"] } } }
Defensive patterns

Strategy: validation

Validate before calling

import json

def server_defs_ok(path) -> bool:
    try:
        servers = json.load(open(path, encoding="utf-8")).get("mcpServers", {})
    except Exception:
        return False
    return all(isinstance(v, dict) for v in servers.values())

Type guard

def valid_server_entries(servers: dict) -> bool:
    return all(isinstance(k, str) and k.strip() and isinstance(v, dict) for k, v in servers.items())

Try / catch

except McporterConfigError as exc:
    if "定义必须是对象" in str(exc):
        hint = "each mcpServers value needs {command, args} or {url} shape"

Prevention

When it happens

Trigger: mcpServers contains e.g. '"github": "ghcop mcp serve"' (bare command string), '"github": ["cmd", "arg"]', or '"github": null' in any selected layer.

Common situations: Shorthand configs from blog posts that put the command string directly as the value instead of {"command": ...}; YAML-style config pasted into JSON; a key set to null when commenting out a server.

Related errors


AI-assisted analysis of Panniantong/Agent-Reach@93ae1d18c3 (2026-08-14). Data as JSON: /api/errors/6ae9e5b25ff0eae1. Report an issue: GitHub.