vrc-mcp-proxy
An owned stdio MCP interception proxy that wraps the pinnedMCP-for-Unity server(mcpforunityserver==10.1.0, via uvx) and corrects a handful of ways its transportlies to the model โ a "success:false" that actually moved the file on disk, a snippetsilently executed twice by a connection-level retry, a benign importer line mis-tagged asan error, a timeout that doesn't mean the work didn't run. It also narrows the exposed toolsurface to an allowlist and refuses execute_code snippets that can't compile in a methodbody.
It is a thin line-based JSON-RPC relay, not an MCP-SDK re-serve: it spawns the pinnedserver as a subprocess and passes every message through untouched except at namedinterception points. See docs/design.md for the full rationale and theper-failure verdicts, and docs/bump-runbook.md for moving theupstream pin.
Behaviors
| Behavior | Point | What it does |
|---|---|---|
canary |
tools/list resp | Validates upstream schemas against the committed baseline; refuses calls to a tool whose schema drifted. |
allowlist |
tools/list resp + tools/call req | Exposes only the allowlisted tools; refuses the rest, naming the one-line fix. |
execute_code_using_refusal |
tools/call req | Refuses snippets with top-level using directives (they can't live in a method body). |
execute_code_idempotency_guard |
tools/call req | Wraps snippets in a SessionState guard so an upstream transport re-send returns the cached result instead of running twice. |
manage_asset_truth_correction |
tools/call resp | On a move/rename reported as failed, verifies on disk and rewrites a false failure to success. |
read_console_strip |
tools/call resp | Drops known-benign console noise and appends a trailer naming what was stripped (never silent). |
timeout_notes |
tools/call resp | Appends a note to timeout errors: the work may have run; verify on disk before retrying. |
Wiring it into .mcp.json
Keep the server key UnityMCP so every mcp__UnityMCP__* name and settings matchersurvives unchanged:
{
"mcpServers": {
"UnityMCP": {
"command": "uv",
"args": ["run", "--project", "<path-to-this-repo>", "vrc-mcp-proxy"]
}
}
}
The proxy spawns the pinned upstream server itself; you do not point .mcp.json atuvx mcpforunityserver anymore.
Disabling a behavior
Each behavior is independently disableable at launch via one env var (comma- orspace-separated names from the table above):
"env": { "VRC_MCP_PROXY_DISABLE": "read_console_strip,canary" }
Development
uv run pytest
Tests need no Unity: transforms are unit-tested as pure functions, and one end-to-end testrelays the proxy against a scripted fake child process. The pin lives in exactly one place โsrc/vrc_mcp_proxy/config.py.