{"record":{"id":"0d5e6813f7b7e5b7","repo":"Panniantong/Agent-Reach","slug":"mcporter-server-name","errorCode":null,"errorMessage":"mcporter 配置包含无效的 server name","messagePattern":"mcporter 配置包含无效的 server name","errorType":"exception","errorClass":"McporterConfigError","httpStatus":null,"severity":"error","filePath":"agent_reach/channels/mcporter.py","lineNumber":60,"sourceCode":"    deliberately not opened because Doctor must not expand its\n    credential-read boundary.\n    \"\"\"\n    selected_layers = _select_config_layers(root_dir)\n    if not selected_layers:\n        return McporterConfigInspection(frozenset(), None)\n\n    names = set()\n    imports_unchecked = False\n    sources = []\n    for config_path, source in selected_layers:\n        payload = _read_config_object(config_path)\n        servers = payload.get(\"mcpServers\")\n        if not isinstance(servers, dict):\n            raise McporterConfigError(\"mcporter 配置缺少 mcpServers 对象\")\n\n        for name, definition in servers.items():\n            if not isinstance(name, str) or not name.strip():\n                raise McporterConfigError(\"mcporter 配置包含无效的 server name\")\n            if not isinstance(definition, dict):\n                raise McporterConfigError(\"mcporter server 定义必须是对象\")\n            names.add(name.casefold())\n\n        imports = payload.get(\"imports\", _MISSING)\n        if imports is _MISSING:\n            # mcporter defaults to importing supported editor configs when the\n            # key is omitted. Doctor intentionally does not open those files.\n            imports_unchecked = True\n        elif not isinstance(imports, list) or not all(\n            isinstance(item, str) for item in imports\n        ):\n            raise McporterConfigError(\"mcporter imports 必须是字符串列表\")\n        elif imports:\n            imports_unchecked = True\n        sources.append(source)\n\n    return McporterConfigInspection(","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/Panniantong/Agent-Reach/blob/93ae1d18c37b707dec053c7c4f9d91cd8ef8943d/agent_reach/channels/mcporter.py#L42-L78","documentation":"Raised while iterating a layer's mcpServers entries when a key is not a non-empty string after strip(). JSON object keys are normally strings, but whitespace-only or empty names (possible via programmatic writes or lenient parsers) are rejected because they cannot be valid MCP server names for routing.","triggerScenarios":"mcpServers contains a key like \"\", \" \", or \"\\t\" in any selected config layer. Keys are then casefolded into the name set used for doctor output, so garbage names are rejected up front.","commonSituations":"Templating bugs producing '\"${name}\"': '' style keys; jq scripts inserting empty keys; merge tools leaving blank placeholder entries.","solutions":["Find and remove/rename blank keys: jq 'keys' on each config file","Regenerate the server entry with a real name matching ^[A-Za-z0-9_-]+$","If the entry is a leftover placeholder, delete the whole key/value pair"],"exampleFix":"// before\n { \"mcpServers\": { \"\": { \"command\": \"npx\" } } }\n// after\n { \"mcpServers\": { \"fetch\": { \"command\": \"npx\" } } }","handlingStrategy":"validation","validationCode":"import json\n\ndef server_names_ok(path) -> bool:\n    try:\n        servers = json.load(open(path, encoding=\"utf-8\")).get(\"mcpServers\", {})\n    except Exception:\n        return False\n    return all(isinstance(k, str) and k.strip() for k in servers)","typeGuard":"def valid_server_entries(servers: dict) -> bool:\n    return all(isinstance(k, str) and k.strip() and isinstance(v, dict) for k, v in servers.items())","tryCatchPattern":"except McporterConfigError as exc:\n    if \"server name\" in str(exc):\n        point_user_at_blank_key(str(exc))","preventionTips":["Never emit empty or whitespace server keys from templates","Lint generated configs with jq before deploying","Delete placeholder entries instead of blanking their names"],"tags":["mcporter","mcp","json","config"],"backgroundTag":null,"analyzedSha":"93ae1d18c37b707dec053c7c4f9d91cd8ef8943d","analyzedAt":"2026-08-14T22:54:06.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}