JuliusBrussee/caveman · error · ValueError
Provide the host's server identity and negotiated protocol…
Error message
Provide the host's server identity and negotiated protocol version
What it means
CavemanMCPHost requires a non-empty `server_id` (the host's MCP server identity) and a non-empty `protocol_version` (the negotiated MCP protocol version). If either is empty or None it raises ValueError, because binding and version negotiation are meaningless without them.
Solutions
- Pass the server's actual identifier and the negotiated protocol version from the MCP initialize handshake
- Validate config values before constructing the host and fail fast on empty strings
- Default protocol_version from the scope/state where the handshake result is stored
Example fix
// before host = CavemanMCPHost(runtime=runtime, scope=scope, server_id="", protocol_version=None) // after host = CavemanMCPHost(runtime=runtime, scope=scope, server_id="my-mcp-server", protocol_version=session.protocol_version)
Defensive patterns
Strategy: validation
Validate before calling
if not server_id or not protocol_version:
raise ValueError("server_id and protocol_version are required") Type guard
def has_host_identity(sid, pv): return bool(sid) and bool(pv)
Try / catch
try:
host = CavemanMCPHost(runtime=runtime, scope=scope, server_id=sid, protocol_version=pv)
except ValueError as e:
logging.error("MCP host identity missing: %s", e)
raise ConfigError("set CAVE_MAN_SERVER_ID and protocol_version") from e Prevention
- Load server_id/protocol_version from the initialize handshake, not hand-written constants
- Validate config for empty strings at startup
- Fail fast in a setup() function before building the host
When it happens
Trigger: Calling CavemanMCPHost(...) with server_id="", server_id=None, protocol_version="", or protocol_version=None.
Common situations: Config loading returns empty strings for unset settings; the protocol version variable is not populated because ASGI/initialize handshake info was not propagated into the host constructor.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- apiKey, baseURL, and agent are required
- artifact_id is required
- ASGI context resolver or request bounds are invalid
- assemble requires model and session_id
- assembly slot id must be non-empty and unique
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/2fd3a05c1a70b008.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/mcp.py:49
def bind_mcp_tool(client, tool: Tool) -> MCPToolBinding:
"""Use an existing native client; it keeps auth, IDs, validation and options."""
async def execute(arguments=None, **native_options):
return await client.call_tool(tool.name, arguments, **native_options)
return MCPToolBinding(tool, execute)
class CavemanMCPHost:
def __init__(self, *, runtime: AsyncMiddlewareRuntime, scope: Scope,
server_id: str, protocol_version: str):
if not isinstance(runtime, AsyncMiddlewareRuntime):
raise TypeError("Native MCP clients require AsyncMiddlewareRuntime")
self._version_supported = matches_framework(("mcp", "2.2", "3"))
if not self._version_supported and runtime.mode != "off":
runtime.decline("unsupported_version")
if not server_id or not protocol_version:
raise ValueError("Provide the host's server identity and negotiated protocol version")
self.runtime, self.scope, self.server_id = runtime, scope, server_id
self.adapter = Adapter("mcp", "0.1.0", "2.2.0", "mcp-native-" + protocol_version + "-v1")
binding = runtime.recovery(scope)
self._binding = binding
tool = Tool(name=binding.name, description=binding.description, input_schema=dict(binding.input_schema))
async def execute(arguments=None, **_native_options):
# This is a host-local executor, not an outbound MCP tools/call.
page = await binding.execute(arguments or {})
return CallToolResult(content=[TextContent(type="text", text=json.dumps(page, ensure_ascii=False, separators=(",", ":")))])
self.recovery = MCPToolBinding(tool, execute)
self._recovery_executor = execute
self._recovery_definition = tool.model_dump_json(by_alias=True, exclude_none=True)
def register(self, tools: Sequence[MCPToolBinding]) -> list[MCPToolBinding]:
"""Append only our executable tool. A name collision leaves tools intact."""
if not self._version_supported or self.runtime.mode != "compress" or any(item.tool.name == self.recovery.tool.name for item in tools):
return list(tools)View on GitHub (pinned to 3ee70a1026)