# FastMCP + Hermes MCP Server Deployment Pattern > Super Search (Jul 2026) reference implementation ## Architecture Hermes supports **two MCP server transport modes** for custom tools: | Mode | Config | Use Case | |------|--------|----------| | **Command** | `command: python3` + `args: [...]` | Subprocess managed by Hermes | | **HTTP/URL** | `url: http://127.0.0.1:PORT/mcp` | Standalone service (systemd/Docker) | For production services with auto-restart, use **HTTP mode** behind a systemd service. ## Build Pattern ### 1. FastMCP Server ```python from fastmcp import FastMCP mcp = FastMCP("Service Name", version="1.0.0") @mcp.tool(description="Tool description shown to Hermes.") async def my_tool(query: str, limit: int = 5) -> str: """Tool docstring.""" # implementation pass if __name__ == "__main__": mcp.run(transport="http", host="127.0.0.1", port=8899, path="/mcp") ``` Key points: - `transport="http"` exposes SSE + JSON-RPC on the HTTP path - Bind to `127.0.0.1` (loopback) — never expose MCP endpoints publicly - Each `@mcp.tool` becomes a discoverable tool in Hermes ### 2. Virtual Environment ```bash python3 -m venv /path/to/service/venv /path/to/service/venv/bin/pip install fastmcp httpx trafilatura # etc. ``` ### 3. Environment Variables Secrets via `.env` file with systemd `EnvironmentFile` directive: ``` MY_API_KEY=sk-... ``` Load in Python with `python-dotenv`: ```python from pathlib import Path from dotenv import load_dotenv load_dotenv(Path.home() / ".hermes" / ".env") # or use a local .env: # load_dotenv(Path(__file__).parent / ".env") ``` ### 4. Systemd Service ``` [Unit] Description=My MCP Server After=network.target [Service] Type=simple User=root WorkingDirectory=/path/to/service Environment="PATH=/path/to/service/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" Environment="PYTHONUNBUFFERED=1" EnvironmentFile=/path/to/.env ExecStart=/path/to/service/venv/bin/python3 /path/to/service/server.py Restart=always RestartSec=3 [Install] WantedBy=multi-user.target ``` Install: ```bash cp service.service /etc/systemd/system/ systemctl daemon-reload && systemctl enable --now my-service ``` ### 5. Hermes MCP Config Wiring Add to `~/.hermes/config.yaml` under `mcp_servers:`: ```yaml mcp_servers: my-service: url: http://127.0.0.1:8899/mcp enabled: true ``` Or via CLI: ```bash hermes config set mcp_servers.my-service.url 'http://127.0.0.1:8899/mcp' hermes config set mcp_servers.my-service.enabled 'true' ``` ### 6. Verification ```bash systemctl is-active my-service hermes mcp test my-service ``` Expected output: ``` Testing 'my-service'... Transport: HTTP → http://127.0.0.1:8899/mcp Auth: none ✓ Connected (XXms) ✓ Tools discovered: N ``` ### 7. Gateway Restart (if tools don't appear in session) ```bash # From outside the gateway session (SSH): systemctl restart hermes.service # Avoid using 'hermes gateway restart' from inside the Telegram session # — it kills the process you're talking through. ``` ## Multi-Provider Fallback Pattern For services that combine multiple backends (search, AI, extraction), use an ordered fallback chain: ```python async def web_search(query, limit=5): # Tier 1 — Free/self-hosted try: results = await free_backend(query, limit) if results: return results except Exception: pass # Tier 2 — Premium API try: results = await premium_backend(query, limit) if results: return results except Exception: pass # Tier 3 — Paid fallback (last resort) try: return await paid_backend(query, limit) except Exception as e: return {"error": str(e)} ``` Each tier catches exceptions independently so one backend's failure doesn't cascade. ## Pitfalls - **Systemd `EnvironmentFile` loads from `.env` format** — lines must be `KEY=VALUE`. No export, no quotes around values (unlike shell). The file is read by systemd, not sourced by bash. - **FastMCP HTTP transport requires `Accept: application/json, text/event-stream` header** — simple `curl` requests without this header get a 406 error. Hermes' MCP client handles this automatically. - **Port conflicts after subagent test runs** — a subagent may leave a test process running on the target port. Before enabling the systemd service, check: `ss -tlnp | grep :PORT`. Kill stale processes with `fuser -k PORT/tcp`. - **Subagent timeout for multi-step builds** — the default 600s child timeout may be insufficient for pip installs + service wiring. Either raise delegation.child_timeout_seconds or plan to handle the final 20% (install + enable + test) yourself.