# ADR-005: Downgrade from FastMCP to Low-Level MCP Server Pattern
**Status:** Accepted
**Date:** 2026-02-08
**Deciders:** Brock Webb, Claude (AI pair)
## Context
Census MCP server v3.0 was implemented using `FastMCP` from `mcp.server.fastmcp`
(mcp SDK 1.9.4). During Phase 4A manual validation, the server:
1. Started successfully (no import errors, no crashes)
2. Responded correctly to JSON-RPC `initialize` handshake via stdin
3. Showed "running" status in Claude Desktop settings
4. **Failed to surface tools in any Claude Desktop conversation**
All other MCP servers in the same Claude Desktop configuration (arnold-training,
arnold-profile, arnold-memory, arnold-journal, arnold-analytics, neo4j-mcp,
postgres-mcp, trace, github) work correctly. Every working server uses the
low-level pattern:
```python
from mcp.server import Server
from mcp.server.stdio import stdio_server
```
The census-mcp server was the only one using:
```python
from mcp.server.fastmcp import FastMCP
```
## Decision
Rewrite `server.py` to use the low-level `Server` + `stdio_server` pattern
matching the proven Arnold MCP servers. This replaces `FastMCP` decorator-based
tool registration with explicit `@server.list_tools()` and `@server.call_tool()`
handlers.
## Rationale
- **Empirical:** 9/9 working MCPs use low-level pattern. 1/1 broken MCP uses
FastMCP. The correlation is dispositive.
- **Risk:** FastMCP is a convenience wrapper merged into the SDK recently (post
1.0). It may have protocol negotiation differences that Claude Desktop's MCP
client doesn't handle.
- **Debugging cost:** We spent significant time proving the server works
(handshake succeeds, tools defined correctly, deps installed) before
identifying the pattern mismatch. Further debugging FastMCP internals has
poor ROI when a proven alternative exists.
- **No functional loss:** FastMCP is syntactic sugar. The low-level pattern
exposes identical capabilities with more explicit control.
## Consequences
### Positive
- Tools should surface in Claude Desktop (matching proven pattern)
- Explicit tool definitions (no decorator magic)
- Log file at `/tmp/census-mcp.log` for debugging
- Consistent pattern across all MCP servers in the environment
### Negative
- More verbose tool registration (explicit `inputSchema` dicts vs type hints)
- Single dispatcher function instead of per-tool functions
- If FastMCP is fixed in a future SDK release, we've diverged from the
"recommended" approach
### Neutral
- `census_tools.py` is no longer the tool registration point; tools are defined
inline in `server.py` matching the Arnold pattern. The file can be removed or
repurposed for shared logic if tools grow complex.
## Verification
After applying this change:
1. Restart Claude Desktop (full Cmd+Q quit)
2. Open new conversation
3. Verify `get_methodology_guidance`, `get_acs_data`, `explore_variables` appear
in available tools
4. Run Phase 4A test queries (G.3, G.4)