Skip to content

Codex / MCP SDK get_scene_info times out while Unity WebSocket is connected; repeated bridge clients and stale request behavior #148

Description

@Fen747

Summary

When using mcp-unity from Codex CLI project-local config, simple read-only tool calls such as get_scene_info time out. Unity accepts WebSocket connections, and direct raw WebSocket requests can return successfully, but MCP stdio tool calls through the Node server time out after 60 seconds.

This appears related to the known Codex/multiple-connection and timeout cascade issues, but it still reproduces on latest main commit cce8b57de9cb69633deb222c9162dad539a52b87.

Environment

  • Unity: 6000.3.8f1
  • OS: macOS / Apple Silicon
  • Node.js: v22.22.0
  • npm: 10.9.4
  • mcp-unity install method: Unity Package Manager Git URL
  • mcp-unity package URL: https://github.com/CoderGamester/mcp-unity.git
  • Resolved commit: cce8b57de9cb69633deb222c9162dad539a52b87
  • mcp-unity version in server.json: 1.3.0
  • MCP client: Codex CLI project-local .codex/config.toml
  • Unity MCP settings:
{
  "Port": 8090,
  "RequestTimeoutSeconds": 60,
  "AutoStartServer": true,
  "EnableInfoLogs": false,
  "NpmExecutablePath": "",
  "AllowRemoteConnections": false
}

Project-local Codex config:

[mcp_servers.mcp-unity]
command = "node"
args = ["Library/PackageCache/com.gamelovers.mcp-unity@cce8b57de9cb/Server~/build/index.js"]

Reproduction Steps

  1. Install mcp-unity via Unity Package Manager using:
https://github.com/CoderGamester/mcp-unity.git
  1. Open Unity and start the MCP Unity server on port 8090.
  2. Confirm Unity logs:
[MCP Unity] WebSocket server started successfully on localhost:8090.
  1. Configure Codex CLI project-local MCP config:
[mcp_servers.mcp-unity]
command = "node"
args = ["Library/PackageCache/com.gamelovers.mcp-unity@cce8b57de9cb/Server~/build/index.js"]
  1. Restart Codex from the project root.
  2. Call get_scene_info through the MCP client.

Actual Result

Codex reports:

Request timed out

When reproduced with a standalone MCP SDK client against the same stdio server, the result is also a timeout:

McpError: MCP error -32001: Request timed out
    at Timeout.timeoutHandler (.../@modelcontextprotocol/sdk/dist/esm/shared/protocol.js:282:49)
  code: -32001,
  data: { timeout: 60000 }

lsof -nP -iTCP:8090 shows multiple Node bridge clients connected to Unity during/after attempts:

node  ... TCP [::1]:63010->[::1]:8090 (ESTABLISHED)
node  ... TCP [::1]:63064->[::1]:8090 (ESTABLISHED)
Unity ... TCP [::1]:8090 (LISTEN)

Unity logs show WebSocket clients connecting, sometimes the request arriving and a response being produced, but the MCP client still times out:

[MCP Unity] WebSocket client connected (... Name: Unknown MCP Client ...)
[MCP Unity] WebSocket message received: {"method":"get_scene_info","params":{},"id":"..."}
[MCP Unity] Retrieved scene info for active scene 'SampleScene'
[MCP Unity] WebSocket message response for request ID '...': {"id":"...","result":{"success":true,...}}

In other clean MCP SDK runs, the Node server log shows the tool call timing out even though the WebSocket connection was established:

[INFO] [Unity] WebSocket connected to Unity
[INFO] [Unity] Successfully connected to Unity WebSocket
[INFO] [Tools] Executing tool: get_scene_info
{}
[ERROR] [Unity] Request ... timed out after 60000ms
[ERROR] [Tools] Tool execution failed: get_scene_info
{
  "type": "timeout_error",
  "message": "Request timed out"
}

Unity also logs this after clients time out or disconnect:

[MCP Unity] WebSocket error: An error has occurred in sending data.
WebSocketSharp.WebSocketException: The header of a frame cannot be read from the stream.

Expected Result

get_scene_info should return promptly through the MCP stdio server, matching the direct raw WebSocket behavior.

Expected response shape:

{
  "activeScene": {
    "name": "SampleScene",
    "path": "Assets/Scenes/SampleScene.unity",
    "isDirty": false,
    "isLoaded": true,
    "rootCount": 2
  }
}

Direct WebSocket Control Test

A direct raw WebSocket request to Unity can succeed:

node -e "import WebSocket from './Library/PackageCache/com.gamelovers.mcp-unity@cce8b57de9cb/Server~/node_modules/ws/wrapper.mjs'; const ws=new WebSocket('ws://localhost:8090/McpUnity',{headers:{'X-Client-Name':'direct-long-scene-test'},origin:'direct-long-scene-test'}); const t=setTimeout(()=>{console.error('timeout'); ws.close(); process.exit(2)},65000); ws.on('open',()=>{console.log('open'); ws.send(JSON.stringify({id:'direct-long-scene-1',method:'get_scene_info',params:{}}));}); ws.on('message',d=>{clearTimeout(t); console.log(d.toString()); setTimeout(()=>{ws.close(); process.exit(0)},500);}); ws.on('error',e=>{clearTimeout(t); console.error('error',e.message || e.code || e); process.exit(1);});"

This returned successfully:

{"id":"direct-long-scene-1","result":{"success":true,"type":"text","message":"Active scene: 'SampleScene'","activeScene":{"name":"SampleScene","path":"Assets/Scenes/SampleScene.unity","buildIndex":0,"isDirty":false,"isLoaded":true,"rootCount":2},"loadedSceneCount":1,"loadedScenes":[{"name":"SampleScene","path":"Assets/Scenes/SampleScene.unity","buildIndex":0,"isLoaded":true,"isDirty":false,"rootCount":2,"isActive":true}]}}

This suggests Unity's editor-side tool implementation can work, while the stdio MCP bridge lifecycle/request handling still fails.

Standalone MCP SDK Repro

This reproduces outside Codex using the MCP SDK directly:

LOGGING_FILE=true node --input-type=module -e "import { Client } from './Library/PackageCache/com.gamelovers.mcp-unity@cce8b57de9cb/Server~/node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js'; import { StdioClientTransport } from './Library/PackageCache/com.gamelovers.mcp-unity@cce8b57de9cb/Server~/node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js'; const client = new Client({name:'manual-mcp-client', version:'1.0.0'}, {capabilities:{}}); const transport = new StdioClientTransport({command:'node', args:['Library/PackageCache/com.gamelovers.mcp-unity@cce8b57de9cb/Server~/build/index.js'], cwd:process.cwd(), env:{...process.env, LOGGING_FILE:'true'}}); await client.connect(transport); console.error('connected'); const result = await client.callTool({name:'get_scene_info', arguments:{}}); console.log(JSON.stringify(result,null,2)); await client.close();"

It connects, then times out after 60 seconds.

Related Existing Issues

Notes

The issue still reproduces on main after fixes for #110/#119 appear to have landed. It may be a remaining edge case around Codex/MCP SDK stdio startup, multiple concurrent bridge processes, or the WebSocket request/response matching path in mcpUnity.sendRequestInternal.

No local source patches are required to reproduce; avoid relying on edits under Library/PackageCache because Unity can regenerate that directory.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions