CoplayDev/unity-mcp · critical · RuntimeError

API key authentication required. Provide a valid X-API-Key h

Error message

API key authentication required. Provide a valid X-API-Key header.

What it means

Raised in remote-hosted HTTP mode when the middleware could not resolve a user_id from the request, meaning no valid X-API-Key header was supplied. Remote-hosted deployments require an API key on every request for tenant isolation, so a missing/invalid key blocks the call before any Unity routing happens.

Source

Thrown at Server/src/transport/unity_instance_middleware.py:331

        return None

    async def _resolve_user_id(self) -> str | None:
        """Extract user_id from the current HTTP request's API key."""
        if not config.http_remote_hosted:
            return None
        # Lazy import to avoid circular dependencies (same pattern as _maybe_autoselect_instance).
        from transport.unity_transport import _resolve_user_id_from_request
        return await _resolve_user_id_from_request()

    async def _inject_unity_instance(self, context: MiddlewareContext) -> None:
        """Inject active Unity instance and user_id into context if available."""
        ctx = context.fastmcp_context

        # Resolve user_id from the HTTP request's API key header
        user_id = await self._resolve_user_id()
        if config.http_remote_hosted and user_id is None:
            raise RuntimeError(
                "API key authentication required. Provide a valid X-API-Key header."
            )
        if user_id:
            await ctx.set_state("user_id", user_id)

        # Per-call routing: check if this tool call explicitly specifies unity_instance.
        # context.message.arguments is a mutable dict on CallToolRequestParams; resource
        # reads use ReadResourceRequestParams which has no .arguments, so this is a no-op for them.
        # We pop the key here so Pydantic's type_adapter.validate_python() never sees it.
        active_instance: str | None = None
        msg_args = getattr(getattr(context, "message", None), "arguments", None)
        if isinstance(msg_args, dict) and "unity_instance" in msg_args:
            raw = msg_args.pop("unity_instance")
            if raw is not None:
                raw_str = str(raw).strip()
                if raw_str:
                    # Raises ValueError with a user-friendly message on invalid input.
                    active_instance = await self._resolve_instance_value(raw_str, ctx)

View on GitHub (pinned to c21bf496bc)

Solutions

  1. Send a valid X-API-Key header (value matching a key configured on the server) on every HTTP request.
  2. Regenerate/rotate the key and update the client config to match.
  3. If the deployment is genuinely local single-user, disable config.http_remote_hosted so the auth requirement is dropped.

Example fix

// before
resp = httpx.post(url, json={...})  // no header
// after
resp = httpx.post(url, json={...}, headers={'X-API-Key': API_KEY})
Defensive patterns

Strategy: validation

Validate before calling

if config.http_remote_hosted and not request.headers.get('X-API-Key'):
    raise RuntimeError('Missing X-API-Key for remote-hosted mode')

Type guard

def has_valid_api_key(headers: dict, valid_keys: set[str]) -> bool:
    return headers.get('X-API-Key') in valid_keys

Try / catch

try:
    resp = await client.call_tool(cmd, params)
except RuntimeError as e:
    if 'API key authentication required' in str(e):
        client.headers['X-API-Key'] = API_KEY
        resp = await client.call_tool(cmd, params)

Prevention

When it happens

Trigger: config.http_remote_hosted is true and _resolve_user_id() returned None (the API key header absent or not in the configured key set). The guard at unity_instance_middleware.py:329-332 fires.

Common situations: Client was configured for stdio/local and then pointed at a remote-hosted server without adding the X-API-Key header; the key was rotated and the client still sends the old one; a proxy stripped the header.

Understand the failure class

Related errors


AI-assisted analysis of CoplayDev/unity-mcp@c21bf496bc (2026-08-13). Data as JSON: /api/errors/33310a4e0667254a. Report an issue: GitHub.