mcp-server-template
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-server-templateShow me how to add a new custom tool to this MCP server template."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-server-template
Template repository for building MCP servers in Python with FastMCP.
It ships a working server with one example of each building block (tool, resource, prompt, service, schema) so you can clone it, delete the examples, and start writing your own features immediately.
Design goals: simple, organized, low coupling. server.py only assembles the server —
everything else lives behind explicit register_* functions.
Requirements
Python 3.12+
Related MCP server: Python MCP Server Template
Installation
uv syncThat creates .venv, installs runtime + dev dependencies, and installs the project itself
(so import mcp_server works with the src/ layout — no sys.path hacks).
Optional configuration:
cp .env.example .envRunning
STDIO (default — for local clients like Claude Desktop)
uv run fastmcp run src/mcp_server/server.py:mcpHTTP
uv run fastmcp run src/mcp_server/server.py:mcp \
--transport http \
--host 0.0.0.0 \
--port 8000Using the settings from .env
The installed entry point reads MCP_TRANSPORT, MCP_HOST and MCP_PORT:
uv run mcp-serverTests
uv run pytestTests drive the server in memory through fastmcp.Client(server) — no HTTP server, no
subprocess. The client fixture in tests/conftest.py does the wiring:
async def test_echo(client):
result = await client.call_tool("echo", {"message": "hello"})
assert result.data.result == "hello"Quality
uv run ruff check . # lint
uv run ruff format . # formatProject structure
mcp-server-template/
├── src/
│ └── mcp_server/
│ ├── server.py # assembly only: create_server() + mcp
│ ├── config.py # pydantic-settings (MCP_* env vars)
│ ├── tools/ # register_tools(mcp)
│ ├── resources/ # register_resources(mcp)
│ ├── prompts/ # register_prompts(mcp)
│ ├── services/ # business logic, MCP-agnostic
│ └── schemas/ # pydantic models
├── tests/
├── .env.example
├── Dockerfile
├── pyproject.toml
└── README.mdThe flow is always the same:
tool/resource/prompt -> service -> schema
(MCP surface) (logic) (data)Configuration
Settings come from environment variables (or .env) via pydantic-settings. Each field maps
to an MCP_-prefixed variable:
Variable | Default | Description |
|
| Name advertised to clients |
|
|
|
|
| HTTP host |
|
| HTTP port |
|
| Log level |
Add a new setting by adding a typed field to Settings in
src/mcp_server/config.py and documenting it in .env.example.
How to create a new tool
Create
src/mcp_server/tools/my_feature.py:
from fastmcp import FastMCP
from mcp_server.services.my_service import do_the_work
def register_my_feature_tool(mcp: FastMCP) -> None:
@mcp.tool
def my_feature(query: str, limit: int = 10) -> list[str]:
"""One-line description the model will read.
Args:
query: What to look for.
limit: Maximum number of results.
"""
return do_the_work(query, limit=limit)Register it in
src/mcp_server/tools/__init__.py:
from mcp_server.tools.my_feature import register_my_feature_tool
def register_tools(mcp: FastMCP) -> None:
register_my_feature_tool(mcp)Notes:
The docstring is the tool description sent to the model — write it for the model.
Return a Pydantic model (see
schemas/) when the output has structure.Keep the logic in a service; the tool stays a thin adapter.
Use
async defonly when the work is actually I/O-bound (HTTP calls, DB, etc.).
How to create a resource
# src/mcp_server/resources/my_resource.py
from fastmcp import FastMCP
def register_my_resource(mcp: FastMCP) -> None:
@mcp.resource("data://items", mime_type="application/json")
def items() -> list[dict[str, str]]:
"""Static resource: fixed URI."""
return [{"id": "1", "name": "example"}]
@mcp.resource("data://items/{item_id}")
def item(item_id: str) -> dict[str, str]:
"""Resource template: the URI carries a parameter."""
return {"id": item_id, "name": "example"}Then add register_my_resource(mcp) to register_resources in resources/__init__.py.
How to create a prompt
# src/mcp_server/prompts/my_prompt.py
from fastmcp import FastMCP
def register_my_prompt(mcp: FastMCP) -> None:
@mcp.prompt
def review_code(code: str, language: str = "python") -> str:
"""Ask the model to review a snippet."""
return f"Review this {language} code and list concrete issues:\n\n{code}"Then add register_my_prompt(mcp) to register_prompts in prompts/__init__.py.
Adding an external integration
Put the client in services/ (e.g. services/github_service.py), read credentials from
config.py, and keep the tool as a thin wrapper. The service never imports FastMCP, so it
stays unit-testable on its own.
Removing the examples
The echo example is self-contained. To drop it:
rm src/mcp_server/tools/echo.py \
src/mcp_server/resources/server_info.py \
src/mcp_server/prompts/summarize.py \
src/mcp_server/services/echo_service.py \
src/mcp_server/schemas/echo.py \
tests/test_tools_echo.py \
tests/test_services_echo.py \
tests/test_resources_and_prompts.pyThen remove the matching import + call in tools/__init__.py, resources/__init__.py and
prompts/__init__.py, and drop the capability assertions in tests/test_server.py.
Docker
docker build -t mcp-server-template .
docker run --rm -p 8000:8000 mcp-server-templateThe image runs the HTTP transport on port 8000 (MCP_TRANSPORT=http, MCP_HOST=0.0.0.0).
Pass overrides with -e, e.g. docker run --rm -p 8000:8000 -e MCP_SERVER_NAME=my-server ....
MCP client configuration
Claude Code
The repo ships a project-scoped .mcp.json: open the project in Claude Code
and approve the server when prompted. Or register it yourself:
claude mcp add my-server -- uv run --directory /absolute/path/to/mcp-server-template fastmcp run src/mcp_server/server.py:mcpThe bundled .mcp.json uses a relative path, so it assumes the client launches the server
from the project root. If yours doesn't, add --directory /absolute/path after run.
Codex CLI
Codex uses TOML, not JSON. In ~/.codex/config.toml (or a project-scoped
.codex/config.toml):
[mcp_servers.my-server]
command = "uv"
args = ["run", "--directory", "/absolute/path/to/mcp-server-template",
"fastmcp", "run", "src/mcp_server/server.py:mcp"]
[mcp_servers.my-server.env]
MCP_SERVER_NAME = "my-server"Or via CLI:
codex mcp add my-server -- uv run --directory /absolute/path/to/mcp-server-template fastmcp run src/mcp_server/server.py:mcpGeneric STDIO (other clients)
{
"mcpServers": {
"my-server": {
"command": "uv",
"args": [
"run",
"--directory",
"/absolute/path/to/mcp-server-template",
"fastmcp",
"run",
"src/mcp_server/server.py:mcp"
],
"env": {
"MCP_SERVER_NAME": "my-server"
}
}
}
}HTTP (server already running)
{
"mcpServers": {
"my-server": {
"url": "http://localhost:8000/mcp"
}
}
}Codex equivalent:
[mcp_servers.my-server]
url = "http://localhost:8000/mcp"On Windows,
uvmust be resolvable by the client process. It lives in%USERPROFILE%\.localin; if the client can't find it, use the absolute path touv.exeascommand.
License
No license file is included on purpose — add the one your project needs.
Available Tools
1 toolechoEchoA
Echo a message back, optionally repeated.
| Name | Required | Description | Default |
|---|---|---|---|
| times | No | How many times to repeat it (1-10). | |
| message | Yes | Text to echo back. |
Output Schema
| Name | Required | Description |
|---|---|---|
| times | Yes | How many times the message was repeated. |
| result | Yes | The repeated message. |
| message | Yes | The original message. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry behavioral meaning; 'echo back' clearly communicates a non-mutating pass-through operation and 'optionally repeated' adds the only behavioral variant. It does not explicitly state the absence of state changes, but the echo semantics strongly imply it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is eight words long and front-loads the core operation, followed by the optional qualifier. Every word contributes meaning with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a minimal, single-purpose tool with fully documented parameters, no siblings, and an output schema present. The description, combined with the schema, is sufficient for an agent to select and call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the parameter semantics already live in the input schema. The description's 'optionally repeated' maps to the times parameter but adds no range, formatting, or ordering details beyond what the schema already states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the exact operation ('Echo a message back') and the object it operates on (a message), and adds the optional repetition behavior. With no sibling tools to differentiate, this is fully specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context that this tool simply returns a provided message, optionally repeated. There are no siblings or exclusions to document, so the intended use is evident from the purpose statement, though no explicit when/when-not guidance is given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.0- First observed
echo
TDQS
Scored across 1 tool
With only one tool, there is no possibility of ambiguity or misselection. The tool name 'echo' is clear and the single purpose is obvious.
There is only one tool, so no pattern can be established. The name itself is a clean, descriptive verb, but consistency cannot be meaningfully assessed with a single instance.
The server exposes a single trivial tool ('echo'), which is far too thin to serve any meaningful MCP workflow. As per calibration, a single trivial tool warrants the lowest score.
The tool surface is severely incomplete for any practical domain. Even as a template, offering only an echo tool provides essentially no functionality that an agent could build upon.
Maintenance
Related MCP Connectors
Primarily to be used as a template repository for developing MCP servers with FastMCP in Python, P…
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA basic MCP server template that provides a foundation for building custom tools, resources, and prompts. Serves as a starting point for developers to create their own MCP server functionality.-
- FlicenseNot gradedqualityDmaintenanceA foundational template for building MCP servers in Python using Streamable HTTP transport. Provides example implementations of tools, resources, and prompts to help developers create custom MCP integrations for AI assistants.-
- AlicenseNot gradedqualityDmaintenanceA bare-bones FastMCP server template designed to serve as a starting point for building custom Model Context Protocol servers. It provides a foundational structure for implementing tools over HTTP and includes a built-in health check utility.GPL 3.0
- FlicenseNot gradedqualityDmaintenanceA production-ready Python scaffold for building Model Context Protocol (MCP) servers using FastMCP. It provides a structured framework for developers and AI agents to rapidly develop, test, and manage custom tools and workflows.1-