{"record":{"id":"f17457a62451081a","repo":"OpenBB-finance/OpenBB","slug":"mcp-config-must-be-a-dictionary","errorCode":null,"errorMessage":"mcp_config must be a dictionary.","messagePattern":"mcp_config must be a dictionary\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"openbb_platform/extensions/mcp_server/openbb_mcp_server/utils/fastapi.py","lineNumber":72,"sourceCode":"\n\ndef get_mcp_config(route: APIRoute, *, strict: bool = False) -> MCPConfigModel:\n    \"\"\"\n    Read and validate per-route MCP config from openapi_extra.\n\n    Args:\n        route: The APIRoute to process.\n        strict: If True, raise validation errors. If False, log warnings.\n\n    Returns:\n        A validated MCPConfigModel instance.\n    \"\"\"\n    extra = route.openapi_extra or {}\n    raw_config = extra.get(\"mcp_config\") or extra.get(\"x-mcp\") or {}\n\n    if not isinstance(raw_config, dict):\n        if strict:\n            raise TypeError(\"mcp_config must be a dictionary.\")\n        raw_config = {}\n\n    try:\n        return validate_mcp_config(raw_config, strict=strict)\n    except (ValidationError, TypeError, ValueError) as e:\n        if strict:\n            raise e from e\n        return MCPConfigModel()\n\n\ndef _get_prompt_configs(route: APIRoute) -> list[dict]:\n    \"\"\"Extract prompt configurations from per-route MCP config.\n\n    Supports a 'prompts' list of dicts.\n    Returns a list of prompt configurations.\n    \"\"\"\n    mcp_cfg = get_mcp_config(route)\n    # Convert PromptConfigModel to dict","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/OpenBB-finance/OpenBB/blob/3e071fcc2cd9f891cac6040ae60296dba76dab46/openbb_platform/extensions/mcp_server/openbb_mcp_server/utils/fastapi.py#L54-L90","documentation":"TypeError raised while extracting per-route MCP configuration: the route's openapi_extra['mcp_config'] (or 'x-mcp') value is present but is not a dictionary. In strict mode this raises; in non-strict mode the config is replaced with an empty dict (route simply gets no MCP annotations).","triggerScenarios":"openapi_extra={\"mcp_config\": [\"tags\"]} or {\"x-mcp\": \"true\"} on a FastAPI route. Anything truthy that is not a dict fails the isinstance check — lists, strings, numbers, tuples.","commonSituations":"Confusing the schema-extension convention where x-mcp is sometimes written as a string flag, config generators emitting a JSON list, hand-written openapi_extra where braces were replaced by brackets, migrating from a version that tolerated scalar truthy values.","solutions":["Make the value a dict: openapi_extra={\"mcp_config\": {\"expose\": True, \"tags\": [\"news\"]}}","If a boolean 'expose this route' is all you need, put it inside the dict: {\"mcp_config\": {\"expose\": true}}","For third-party routes you cannot edit, run the collector in non-strict mode so bad extras degrade to a warning"],"exampleFix":"# before\n@app.get(\"/news\", openapi_extra={\"x-mcp\": \"true\"})\n\n# after\n@app.get(\"/news\", openapi_extra={\"x-mcp\": {\"expose\": True}})","handlingStrategy":"type-guard","validationCode":"extra = route.openapi_extra or {}\nraw = extra.get(\"mcp_config\") or extra.get(\"x-mcp\") or {}\nif not isinstance(raw, dict):\n    route.openapi_extra[\"mcp_config\"] = {}  # or repair/log\n    raw = {}\nvalidated = validate_mcp_config(raw, strict=False)","typeGuard":"def route_has_dict_mcp_config(route) -> bool:\n    extra = route.openapi_extra or {}\n    raw = extra.get(\"mcp_config\", extra.get(\"x-mcp\", {}))\n    return isinstance(raw, dict)","tryCatchPattern":"try:\n    cfg = _get_route_mcp_config(route, strict=True)\nexcept TypeError as e:\n    if \"must be a dictionary\" in str(e):\n        cfg = _get_route_mcp_config(route, strict=False)  # degrade to warning\n    else:\n        raise","preventionTips":["Always write mcp_config/x-mcp as an object, never a list or string flag","Add a test that walks all routes and asserts isinstance(extra['mcp_config'], dict)","Use strict=False when scanning third-party routers you do not control"],"tags":["fastapi","openapi","validation","config","mcp"],"backgroundTag":null,"analyzedSha":"3e071fcc2cd9f891cac6040ae60296dba76dab46","analyzedAt":"2026-08-14T23:40:48.960Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}