Skip to main content
Glama
mcp-server-template

mcp-server-template

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+

  • uv

Related MCP server: Python MCP Server Template

Installation

uv sync

That 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 .env

Running

STDIO (default — for local clients like Claude Desktop)

uv run fastmcp run src/mcp_server/server.py:mcp

HTTP

uv run fastmcp run src/mcp_server/server.py:mcp \
  --transport http \
  --host 0.0.0.0 \
  --port 8000

Using the settings from .env

The installed entry point reads MCP_TRANSPORT, MCP_HOST and MCP_PORT:

uv run mcp-server

Tests

uv run pytest

Tests 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 .    # format

Project 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.md

The 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

MCP_SERVER_NAME

mcp-server-template

Name advertised to clients

MCP_TRANSPORT

stdio

stdio, http or sse

MCP_HOST

127.0.0.1

HTTP host

MCP_PORT

8000

HTTP port

MCP_LOG_LEVEL

INFO

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

  1. 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)
  1. 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 def only 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.py

Then 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-template

The 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:mcp

The 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:mcp

Generic 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, uv must be resolvable by the client process. It lives in %USERPROFILE%\.localin; if the client can't find it, use the absolute path to uv.exe as command.

License

No license file is included on purpose — add the one your project needs.

Available Tools

1 tool
echoEchoA

Echo a message back, optionally repeated.

Inputs: message (required), times. Returns: Structured output returned by the ``echo`` tool.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 1 tool

    Disambiguation5/5

    With only one tool, there is no possibility of ambiguity or misselection. The tool name 'echo' is clear and the single purpose is obvious.

    Naming Consistency3/5

    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.

    Tool Count1/5

    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.

    Completeness1/5

    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

    ActivityStale
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      A 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.
      -
    • F
      license
      Not graded
      quality
      D
      maintenance
      A 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.
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      A 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
    • F
      license
      Not graded
      quality
      D
      maintenance
      A 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
      -