website/docs/architecture/python-layers.md
The Python server (Server/src/) exposes three distinct surfaces. They look similar but serve different consumers and are not auto-generated from each other.
| Layer | Where | Framework | Consumer | Transport to Unity |
|---|---|---|---|---|
| MCP Tools | Server/src/services/tools/ | FastMCP (@mcp_for_unity_tool) | AI assistants via MCP | WebSocket (send_with_unity_instance) |
| CLI Commands | Server/src/cli/commands/ | Click (@click.command) | Developers in a terminal | HTTP (run_command) |
| Resources | Server/src/services/resources/ | FastMCP (@mcp_for_unity_resource) | AI assistants, read-only | WebSocket |
Both MCP tools and CLI commands eventually call the same C# HandleCommand methods inside MCPForUnity/Editor/Tools/. Resources are read-only — they observe state without mutating it.
Each surface has a different shape of consumer:
Annotated[Type, "description"]) because they're handed to an LLM. The descriptions are the prompt the LLM reads.Trying to autogenerate one layer from another erodes the ergonomics of all three. The cost is keeping the three in sync — which is mostly a discipline problem solved by domain symmetry.
When you add a new domain (say, manage_navigation), you write three files:
Server/src/services/tools/manage_navigation.py # @mcp_for_unity_tool
Server/src/cli/commands/navigation.py # @click.command
MCPForUnity/Editor/Tools/ManageNavigation.cs # [McpForUnityTool]
The Python tool and CLI command both invoke the C# handler — they just take different paths to it.
Tools are auto-discovered by walking Server/src/services/tools/. Each .py file with @mcp_for_unity_tool-decorated functions is imported at server startup; the decorator side-effects populate a global registry (services.registry). The registry is also what tools/generate_docs_reference.py reads to emit the tool reference.
A tool's group parameter controls per-session visibility — see Tool Groups. group=None means the tool is always visible (server meta-tools like set_active_instance and manage_tools).
ParamNormalizerMiddleware), telemetry, and per-session routing.@handle_unity_errors for terminal-friendly stack traces, and synchronous wrappers around the async core.Server/src/main.py (~935 lines) is the orchestrator:
register_all_tools(mcp) — auto-discoveryregister_all_resources(mcp) — same pattern, different decoratorhttp/stdio from core.config)Server/src/services/tools/manage_material.py is a canonical exampleServer/src/services/registry/tool_registry.py (~130 LOC, the single source the docs reference generator reads)Server/src/transport/ — plugin hub (plugin_hub.py), websocket client, legacy stdio bridgeMCPForUnity/Editor/Tools/ManageMaterial.cs is the C# half of manage_material