{"record":{"id":"33310a4e0667254a","repo":"CoplayDev/unity-mcp","slug":"api-key-authentication-required-provide-a-valid-x","errorCode":null,"errorMessage":"API key authentication required. Provide a valid X-API-Key header.","messagePattern":"API key authentication required\\. Provide a valid X-API-Key header\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"Server/src/transport/unity_instance_middleware.py","lineNumber":331,"sourceCode":"\n        return None\n\n    async def _resolve_user_id(self) -> str | None:\n        \"\"\"Extract user_id from the current HTTP request's API key.\"\"\"\n        if not config.http_remote_hosted:\n            return None\n        # Lazy import to avoid circular dependencies (same pattern as _maybe_autoselect_instance).\n        from transport.unity_transport import _resolve_user_id_from_request\n        return await _resolve_user_id_from_request()\n\n    async def _inject_unity_instance(self, context: MiddlewareContext) -> None:\n        \"\"\"Inject active Unity instance and user_id into context if available.\"\"\"\n        ctx = context.fastmcp_context\n\n        # Resolve user_id from the HTTP request's API key header\n        user_id = await self._resolve_user_id()\n        if config.http_remote_hosted and user_id is None:\n            raise RuntimeError(\n                \"API key authentication required. Provide a valid X-API-Key header.\"\n            )\n        if user_id:\n            await ctx.set_state(\"user_id\", user_id)\n\n        # Per-call routing: check if this tool call explicitly specifies unity_instance.\n        # context.message.arguments is a mutable dict on CallToolRequestParams; resource\n        # reads use ReadResourceRequestParams which has no .arguments, so this is a no-op for them.\n        # We pop the key here so Pydantic's type_adapter.validate_python() never sees it.\n        active_instance: str | None = None\n        msg_args = getattr(getattr(context, \"message\", None), \"arguments\", None)\n        if isinstance(msg_args, dict) and \"unity_instance\" in msg_args:\n            raw = msg_args.pop(\"unity_instance\")\n            if raw is not None:\n                raw_str = str(raw).strip()\n                if raw_str:\n                    # Raises ValueError with a user-friendly message on invalid input.\n                    active_instance = await self._resolve_instance_value(raw_str, ctx)","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/CoplayDev/unity-mcp/blob/c21bf496bca87d54e75bad048563c3adb1782081/Server/src/transport/unity_instance_middleware.py#L313-L349","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Send a valid X-API-Key header (value matching a key configured on the server) on every HTTP request.","Regenerate/rotate the key and update the client config to match.","If the deployment is genuinely local single-user, disable config.http_remote_hosted so the auth requirement is dropped."],"exampleFix":"// before\nresp = httpx.post(url, json={...})  // no header\n// after\nresp = httpx.post(url, json={...}, headers={'X-API-Key': API_KEY})","handlingStrategy":"validation","validationCode":"if config.http_remote_hosted and not request.headers.get('X-API-Key'):\n    raise RuntimeError('Missing X-API-Key for remote-hosted mode')","typeGuard":"def has_valid_api_key(headers: dict, valid_keys: set[str]) -> bool:\n    return headers.get('X-API-Key') in valid_keys","tryCatchPattern":"try:\n    resp = await client.call_tool(cmd, params)\nexcept RuntimeError as e:\n    if 'API key authentication required' in str(e):\n        client.headers['X-API-Key'] = API_KEY\n        resp = await client.call_tool(cmd, params)","preventionTips":["Always set X-API-Key in client headers for remote-hosted servers","Rotate keys in lockstep across server and clients","Confirm http_remote_hosted is intended before enabling"],"tags":["auth","api-key","remote-hosted","http"],"backgroundTag":null,"analyzedSha":"c21bf496bca87d54e75bad048563c3adb1782081","analyzedAt":"2026-08-13T17:36:56.095Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}