mcp-server-create
Scaffold and build a new MCP server with Python FastMCP 3.x. Use this skill when creating a new MCP server project from scratch, setting up server boilerplate, generating pyproject.toml for an MCP project, or when someone asks "create an MCP server", "new MCP project", "scaffold an MCP tool server", or "set up FastMCP". Covers project init, server entrypoint, tool registration, configuration files, and MCP host setup. Always produces Python FastMCP 3.x — never JavaScript.
Create a New MCP Server
Scaffold a FastMCP 3.x server project with correct structure, entrypoint, and configuration. Every MCP server follows the same pattern — this skill ensures you start right so you don't fight the framework later.
1. Determine Architecture Pattern
Before scaffolding, ask the user which architecture they prefer:
- Monolithic Server: Simple, all logic defined within
@mcp.tooldecorators inserver.py. Best for simple specific integrations. - Hybrid (Library-First) Architecture: Core business logic is written in decoupled Python modules, and
server.pyacts only as an MCP envelope. Best if the user wants to reuse the logic in other scripts/CLIs. (Seemcp-hybrid-architectureskill).
2. Scaffolding Steps
mkdir my-mcp-server && cd my-mcp-server
uv init --python 3.12
uv add "fastmcp>=3.1.0"
# Add dependencies as needed: uv add httpx pydantic etc.
FastMCP baseline is latest 3.x (use fastmcp>=3.1.0 today). CodeMode was
introduced in 3.1 and remains experimental; review mcp-code-mode before using
it in production-critical paths.
Server Entrypoint
Use the template in assets/server_template.py as the starting point. Key rules:
- Import as
from fastmcp import FastMCP— neverfrom mcp.server.fastmcp - Set
on_duplicate="error"to catch registration bugs early - Write
instructions=for agents — one sentence each: purpose, use case, constraints - Support both stdio (default) and streamable-http via
PORTenv var
Tool Registration
Register tools with @mcp.tool decorator. Type annotations are essential — they
generate the schema agents use to call your tools. Docstrings become tool descriptions.
from utils import text_response
@mcp.tool
def my_tool(query: str, limit: int = 10) -> ToolResult:
"""Search for items matching the query."""
results = search(query, limit)
return text_response(f"Found {len(results)} items for '{query}'")
See the mcp-tool-design skill for full registration patterns, annotations,
dependency injection, and context access.
For larger catalogs where direct tool exposure causes context bloat, see
mcp-code-mode (FastMCP 3.1+, experimental) for staged discovery and
server-side composition patterns.
Response Formatting
Default to text output. Most consumers are agents — they read formatted text directly. Only return structured data when a programmatic consumer needs it.
- Text (default) → use
text_response()helper frommcp-response-formatskill - Structured data → return
dictor dataclass — only for programmatic consumers - Both → use
structured_response()helper — text for agents, data for automation
Configuration
Read references/configuration.md for:
fastmcp.json— the canonical FastMCP 3.x deployment config- MCP host configs for Claude Desktop, VS Code, Cursor, LM Studio
- Provider selection guide (LocalProvider vs FileSystemProvider vs OpenAPIProvider)
Checklist
Before shipping, verify:
-
from fastmcp import FastMCP(not the old 2.x import) - All tools have type annotations and docstrings
-
on_duplicate="error"is set - Destructive tools have
destructiveHint=Trueannotation - No hardcoded secrets — use
mcp-secretsskill patterns - No blocking I/O at startup — OAuth/auth uses lazy init or background task (see
mcp-startup-patternsskill) - In-memory tests pass — see
mcp-testingskill - MCP host config documented with correct paths