Context: Decisions.md records (2025-05-26): "What we built that we shouldn't have: Custom MCP transport (Task 4.5 fixes)." The original implementation had a hand-rolled MCP server transport. FastMCP is the established Python MCP server library.
Decision: Replace the custom transport with FastMCP (transport.py:24,39-62). Each ProtocolMCPServer is mounted as a FastMCP server named snodo-{protocol_id} (current mode) or snodo-{protocol_id}-{mode_id} (pinned mode). Tools are registered via mcp.add_tool(fn, name=..., description=...).
Consequences: Tool registration is declarative. FastMCP handles stdio transport, shared server infrastructure, and MCP protocol compliance. The snodo serve command accepts --mode to pin one mode; without it, the server serves the current project mode and enforces that grant if the mode changes while it runs.
Alternatives considered: Custom transport — rejected; recorded in decisions.md as a mistake. Third MCP library — not evaluated; FastMCP is the standard.