{"record":{"id":"ce066b195fc36e8d","repo":"JuliusBrussee/caveman","slug":"native-mcp-clients-require-asyncmiddlewareruntime","errorCode":null,"errorMessage":"Native MCP clients require AsyncMiddlewareRuntime","messagePattern":"Native MCP clients require AsyncMiddlewareRuntime","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/mcp.py","lineNumber":44,"sourceCode":"@dataclass(frozen=True)\nclass MCPToolBinding:\n    \"\"\"The native MCP definition and the callable registered in the host loop.\"\"\"\n    tool: Tool\n    execute: Callable[..., Awaitable[CallToolResult]]\n\n\ndef bind_mcp_tool(client, tool: Tool) -> MCPToolBinding:\n    \"\"\"Use an existing native client; it keeps auth, IDs, validation and options.\"\"\"\n    async def execute(arguments=None, **native_options):\n        return await client.call_tool(tool.name, arguments, **native_options)\n    return MCPToolBinding(tool, execute)\n\n\nclass CavemanMCPHost:\n    def __init__(self, *, runtime: AsyncMiddlewareRuntime, scope: Scope,\n                 server_id: str, protocol_version: str):\n        if not isinstance(runtime, AsyncMiddlewareRuntime):\n            raise TypeError(\"Native MCP clients require AsyncMiddlewareRuntime\")\n        self._version_supported = matches_framework((\"mcp\", \"2.2\", \"3\"))\n        if not self._version_supported and runtime.mode != \"off\":\n            runtime.decline(\"unsupported_version\")\n        if not server_id or not protocol_version:\n            raise ValueError(\"Provide the host's server identity and negotiated protocol version\")\n        self.runtime, self.scope, self.server_id = runtime, scope, server_id\n        self.adapter = Adapter(\"mcp\", \"0.1.0\", \"2.2.0\", \"mcp-native-\" + protocol_version + \"-v1\")\n        binding = runtime.recovery(scope)\n        self._binding = binding\n        tool = Tool(name=binding.name, description=binding.description, input_schema=dict(binding.input_schema))\n\n        async def execute(arguments=None, **_native_options):\n            # This is a host-local executor, not an outbound MCP tools/call.\n            page = await binding.execute(arguments or {})\n            return CallToolResult(content=[TextContent(type=\"text\", text=json.dumps(page, ensure_ascii=False, separators=(\",\", \":\")))])\n        self.recovery = MCPToolBinding(tool, execute)\n        self._recovery_executor = execute\n        self._recovery_definition = tool.model_dump_json(by_alias=True, exclude_none=True)","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/mcp.py#L26-L62","documentation":"CavemanMCPHost's constructor requires that the runtime passed in be an AsyncMiddlewareRuntime instance; any other object (sync runtime, None, or wrong class) raises this TypeError. Native MCP hosting is only implemented against the async runtime contract, so the guard prevents a misconfigured host from failing later at request time.","triggerScenarios":"Passing a sync MiddlewareRuntime, a mock/stub, or None as the `runtime` argument to CavemanMCPHost(...) instead of an AsyncMiddlewareRuntime instance.","commonSituations":"Developers reusing a sync runtime built for the OpenAI/Anthropic adapters, or constructing the host from a config dict where the runtime was never instantiated as the async class.","solutions":["Construct the runtime as AsyncMiddlewareRuntime (e.g. AsyncMiddlewareRuntime(mode=...)) and pass it to CavemanMCPHost","If you only have a sync MiddlewareRuntime, wrap or replace it with the async variant before building the host","Check that a factory/dependency-injection helper is not returning the sync runtime class"],"exampleFix":"// before\nhost = CavemanMCPHost(runtime=MiddlewareRuntime(mode=\"compress\"), scope=scope, server_id=\"srv\", protocol_version=\"2025-03-26\")\n// after\nhost = CavemanMCPHost(runtime=AsyncMiddlewareRuntime(mode=\"compress\"), scope=scope, server_id=\"srv\", protocol_version=\"2025-03-26\")","handlingStrategy":"type-guard","validationCode":"from caveman_cloud.middleware import AsyncMiddlewareRuntime\nassert isinstance(runtime, AsyncMiddlewareRuntime), \"pass an AsyncMiddlewareRuntime\"","typeGuard":"def is_async_runtime(r): return isinstance(r, AsyncMiddlewareRuntime)","tryCatchPattern":"try:\n    host = CavemanMCPHost(runtime=runtime, scope=scope, server_id=sid, protocol_version=pv)\nexcept TypeError as e:\n    if \"AsyncMiddlewareRuntime\" in str(e):\n        runtime = AsyncMiddlewareRuntime(mode=runtime.mode)\n        host = CavemanMCPHost(runtime=runtime, scope=scope, server_id=sid, protocol_version=pv)\n    else:\n        raise","preventionTips":["Always build MCP host runtimes from a single factory that returns AsyncMiddlewareRuntime","Add isinstance assertions at wiring/dependency-injection boundaries","Never reuse sync runtimes across adapters"],"tags":["python","type-mismatch","constructor"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}