← All products
Template
Python MCP Starter Template
Skip the blank-file phase of building an MCP server.
A working FastMCP server you rename and extend instead of building an MCP server from scratch. Five example tools cover the shapes most servers need — a trivial tool, a zero-argument tool, an async HTTP call, a filesystem read with path-traversal validation, and a tool that rejects malformed input safely (AST-walked, not eval()'d). An OAuth 2.0 Authorization Code + PKCE helper is included for tools that call a third-party API on the user's behalf. A 38-test pytest suite shows how to test each piece, including the async tool and the OAuth token-refresh flow.
What's included
- FastMCP server boilerplate — src/mcp_starter/server.py, ready to rename and extend
- 5 example tools: echo, get_server_time, fetch_url (async), read_local_file (path-validated), calculate (AST-safe, no eval())
- OAuth 2.0 Authorization Code + PKCE helper — build_authorize_url, exchange_code, refresh_token, ensure_token
- Local callback server for testing OAuth redirects during development
- Path traversal + symlink escape protection for any tool that touches the filesystem
- Environment-driven config (config.py) — same server binary runs unchanged in Claude Desktop, CI, or a container
- 38-test pytest suite — tools, security, OAuth token expiry/refresh, and the registered server tools
- pyproject.toml with console script entry point — installs as a `mcp-starter` command
- README with Claude Desktop config snippet and a guide for renaming the package
Requirements
- Python 3.9 or later
- pip / a virtualenv
- An MCP-compatible client (Claude Desktop, or any MCP SDK client) to connect to it
tools/safe_eval.py
def _eval_node(node: ast.expr):
"""Recursively evaluate an AST node (safe evaluation without eval)."""
if isinstance(node, ast.Constant):
if isinstance(node.value, (int, float)):
return node.value
raise ValueError(f"Only numeric constants allowed")
if isinstance(node, ast.BinOp):
op_type = type(node.op)
if op_type not in _BINARY_OPS:
raise ValueError(f"Operator not allowed")
return _BINARY_OPS[op_type](_eval_node(node.left), _eval_node(node.right))