{"record":{"id":"ebb6330593cd118d","repo":"Panniantong/Agent-Reach","slug":"mcporter-json-servers","errorCode":null,"errorMessage":"mcporter JSON 缺少 servers 列表","messagePattern":"mcporter JSON 缺少 servers 列表","errorType":"exception","errorClass":"McporterConfigError","httpStatus":null,"severity":"error","filePath":"agent_reach/channels/mcporter.py","lineNumber":145,"sourceCode":"        raise McporterConfigError(\"mcporter 配置不是有效的 UTF-8 JSON\") from exc\n    if not isinstance(payload, dict):\n        raise McporterConfigError(\"mcporter 配置顶层必须是对象\")\n    return payload\n\n\ndef configured_server_names(output: str) -> set[str]:\n    \"\"\"Return exact configured server names from ``mcporter ... --json``.\n\n    Paths, descriptions, endpoints, and other metadata are deliberately\n    ignored: only each server object's ``name`` field is a routing signal.\n    \"\"\"\n    try:\n        payload = json.loads(output)\n    except (json.JSONDecodeError, TypeError) as exc:\n        raise McporterConfigError(\"mcporter 返回的 JSON 无法解析\") from exc\n\n    if not isinstance(payload, dict) or not isinstance(payload.get(\"servers\"), list):\n        raise McporterConfigError(\"mcporter JSON 缺少 servers 列表\")\n\n    return {\n        name.casefold()\n        for server in payload[\"servers\"]\n        if isinstance(server, dict)\n        if isinstance(name := server.get(\"name\"), str) and name.strip()\n    }\n","sourceCodeStart":127,"sourceCodeEnd":153,"githubUrl":"https://github.com/Panniantong/Agent-Reach/blob/93ae1d18c37b707dec053c7c4f9d91cd8ef8943d/agent_reach/channels/mcporter.py#L127-L153","documentation":"Raised by configured_server_names(output) when the parsed mcporter JSON is a dict but its 'servers' value is missing or not a list. mcporter's --json contract is an object like {\"servers\": [{\"name\": ...}, ...]}; anything else (different key name, servers as object, empty output '{}') fails this shape check before name extraction.","triggerScenarios":"mcporter emits {\"status\":\"ok\"} (no servers key), {\"servers\": {\"a\":{}}} (object not list), or a newer schema renaming the key. Empty list '[]' is valid — returns empty set, no error.","commonSituations":"mcporter version change altering the --json schema; command succeeded but reported an error payload (error JSON without servers); wrong subcommand whose JSON output has a different shape.","solutions":["Print the raw mcporter JSON output and compare against the expected {\"servers\": [...]} shape","Pin or upgrade mcporter to a version compatible with Agent Reach (check Agent Reach release notes / doctor)","If mcporter returned an error payload, fix the underlying mcporter error first (auth, missing config)","Report a schema change to Agent Reach if upstream mcporter changed its --json contract"],"exampleFix":"# before: mcporter --json prints {\"status\":\"ok\"}\n# after (mcporter list --json prints)\n {\"servers\": [{\"name\": \"fetch\"}, {\"name\": \"github\"}]}","handlingStrategy":"type-guard","validationCode":"import json\n\ndef output_has_servers_list(output) -> bool:\n    try:\n        payload = json.loads(output)\n    except (ValueError, TypeError):\n        return False\n    return isinstance(payload, dict) and isinstance(payload.get(\"servers\"), list)","typeGuard":"def is_mcporter_listing(payload) -> bool:\n    return isinstance(payload, dict) and isinstance(payload.get(\"servers\"), list)","tryCatchPattern":"except McporterConfigError as exc:\n    if \"servers 列表\" in str(exc):\n        check_mcporter_version_and_schema()  # pin a compatible release","preventionTips":["Pin the mcporter version you test against","Expect {\"servers\": [...]} shape; anything else is an error payload or schema drift","Check mcporter's own error field before blaming the list shape"],"tags":["mcporter","mcp","json","subprocess","schema"],"backgroundTag":null,"analyzedSha":"93ae1d18c37b707dec053c7c4f9d91cd8ef8943d","analyzedAt":"2026-08-14T22:54:06.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}