--- name: new-mcp-server description: Scaffolds a new FastMCP server following pi_mcps conventions. Use this skill when creating any new MCP server in the pi_mcps monorepo — produces the full directory structure with server.py, pyproject.toml, tests, and README in one pass. --- # New MCP Server ## When to use - Creating a new MCP server in `pi_mcps/mcp/{name}/` - Bootstrapping a server scaffold before filling in tool logic ## When NOT to use - Adding tools to an existing server (edit `src/server.py` directly) - Non-MCP Python projects ## Inputs required - **Server name** — e.g., `homelab-docker` (will become `mcp-homelab-docker`) - **Purpose** — one sentence description - **Tools list** — names + brief descriptions - **Dependencies** — any Python packages beyond fastmcp - **Environment variables** — any auth/config env vars needed ## Workflow ### Step 1 — Create directory structure ```bash mkdir -p mcp/{name}/src mkdir -p mcp/{name}/tests touch mcp/{name}/src/__init__.py ``` ### Step 2 — Write `mcp/{name}/src/server.py` **Convention:** All tool parameters **must** use `Annotated[type, Field(description="...")]` for descriptions. Do **not** use docstring `Args:` sections — FastMCP reads `Field` metadata directly to expose parameter descriptions in the MCP schema. ```python from typing import Annotated from fastmcp import FastMCP from pydantic import Field mcp = FastMCP("mcp-{name}") @mcp.tool() def {tool_name}( param: Annotated[str, Field(description="What this parameter controls")], ) -> str: """One-line tool description (no Args: section needed).""" # implementation ... if __name__ == "__main__": mcp.run() ``` > Optional parameters with defaults: `param: Annotated[int, Field(description="...")] = 10` ### Step 3 — Write `mcp/{name}/pyproject.toml` ```toml [project] name = "mcp-{name}" version = "0.1.0" requires-python = ">=3.11" dependencies = [ "fastmcp>=0.1.0", # add other deps here ] [project.optional-dependencies] test = ["pytest", "pytest-mock", "pytest-cov"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" ``` ### Step 4 — Write `mcp/{name}/tests/conftest.py` ```python import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent / "src")) ``` ### Step 5 — Write `mcp/{name}/tests/test_server.py` - Import each tool function directly - Mock all external calls with `pytest-mock` - Cover: happy path, error path, edge cases - Run: `cd mcp/{name} && uv run pytest tests/ -v` ### Step 6 — Write `mcp/{name}/README.md` Include: purpose, tools table, env vars, usage example, test command ### Step 7 — Wire into `.roo/mcp.json` ```json "mcp-{name}": { "command": "uv", "args": ["--directory", "/home/pplate/pi_mcps/mcp/{name}", "run", "src/server.py"], "env": { "ENV_VAR": "${ENV_VAR}" } } ``` ### Step 8 — Store in BigMind ``` memory_store_fact("codebase", "mcp/{name} has N tools: [tool1, tool2]. Stack: fastmcp + X. Env vars: Y.") ``` ## Troubleshooting - **FastMCP import error:** Run `uv sync` in the server directory first - **Tool not showing in IDE:** Restart the MCP server via IDE settings - **Test isolation:** Each test should monkeypatch env vars to avoid cross-test pollution