Skip to main content
Glama
README.md
# MCP Template

A minimal, production-ready **FastMCP** server template.
Use this as a starting point for building your own MCP (Model Context Protocol) server.

## What This Is

A clean reference implementation that includes:

| Feature | Details |
|---|---|
| Auto-discovery | Drop `.py` files into `src/tools/`, `src/prompts/`, or `src/resources/` — they register automatically |
| YAML config | Base + environment-specific overrides with env-var substitution |
| Auth middleware | Bearer token, toggle on/off via config — no code change needed |
| Graceful shutdown | SIGINT / SIGTERM handlers |
| Request logging | Correlation IDs, per-request timing, level-aware |
| Knowledgebase | YAML-backed topic store, live-reloadable without restart |
| Docker | Dockerfile + docker-compose.yml included |
| 3 example tools | `echo_message`, `greet`, `calculate` |
| 2 KB tools | `search_knowledgebase`, `get_knowledgebase_topic` |
| 3 example prompts | `general_assistant`, `task_helper`, `analysis_mode` |
| 3 example resources | capabilities, quick-reference, server info |

## Project Structure

```
├── src/
│   ├── mcp_app.py                      # FastMCP instance — import `mcp` from here
│   ├── server.py                       # Entry point: Starlette app, middleware, routing
│   ├── tools/
│   │   ├── example_tools.py            # echo_message, greet, calculate
│   │   └── knowledgebase_tools.py      # search_knowledgebase, get_knowledgebase_topic
│   ├── prompts/
│   │   └── example_prompts.py          # general_assistant, task_helper, analysis_mode
│   ├── resources/
│   │   ├── help_resources.py           # help://<name>/capabilities, quick-reference
│   │   └── server_info.py              # data://server/info
│   └── utils/
│       └── logging_middleware.py       # HTTP logging with correlation IDs
├── config/
│   ├── config.py                       # Config loader (YAML + env vars)
│   ├── settings.yaml                   # Base configuration
│   ├── settings.dev.yaml               # Overrides for ENV=dev
│   └── settings.prod.yaml              # Overrides for ENV=prod
├── knowledgebase/
│   ├── loader.py                       # Loads topics.yaml (no caching)
│   └── topics.yaml                     # Knowledgebase topic definitions
├── Dockerfile
├── docker-compose.yml
├── .dockerignore
├── .env.example
├── requirements.txt
├── README.md
└── CLAUDE.md
```

## Installation

```bash
python -m venv .venv

# Windows:
.venv\Scripts\activate
# macOS / Linux:
source .venv/bin/activate

pip install -r requirements.txt
```

## Running Locally

```bash
# Default (port 8000, no auth, INFO logging):
python src/server.py

# Development mode (DEBUG logging):
ENV=dev python src/server.py

# Custom port:
MCP_PORT=9000 python src/server.py

# With authentication:
AUTH_ENABLED=true AUTH_TOKEN=my-secret python src/server.py

# Production mode (auth on, WARNING logging):
ENV=prod AUTH_TOKEN=my-secret python src/server.py
```

Server is available at `http://localhost:8000`.

## Running with Docker

```bash
# Build and start:
docker compose up --build

# With auth enabled:
AUTH_ENABLED=true AUTH_TOKEN=my-secret docker compose up --build

# Detached:
docker compose up -d

# Stop:
docker compose down
```

The container exposes port 8000 (override with `MCP_PORT` env var for the host port).
`knowledgebase/topics.yaml` is bind-mounted so you can update topics without rebuilding the image.

## Configuration

### Environment Variables

| Variable | Default | Description |
|---|---|---|
| `ENV` | `default` | Config file selector: `default`, `dev`, `prod` |
| `MCP_PORT` | `8000` | HTTP listen port |
| `AUTH_ENABLED` | `false` | Enable bearer token auth (`true` / `false`) |
| `AUTH_TOKEN` | — | Bearer token value (required when auth is enabled) |
| `AUTO_DISCOVER` | `true` | Auto-import all tools / prompts / resources |

### YAML Configuration

Edit `config/settings.yaml` for persistent defaults.
Copy `.env.example` to `.env` for local overrides (never commit `.env`).

### Logging Level

Set `logging.level` in `config/settings.yaml` to `DEBUG`, `INFO`, `WARNING`, or `ERROR`.
Override per environment in `settings.dev.yaml` / `settings.prod.yaml`.

## Authentication

Disabled by default. To enable:

1. Set `AUTH_ENABLED=true` env var **or** `security.authentication.enabled: true` in YAML
2. Set `AUTH_TOKEN=<your-token>` env var **or** `security.authentication.token` in YAML

All MCP requests then require:
```
Authorization: Bearer <your-token>
```

Health-check paths (`/healthz`, `/health`, `/version`) are always unauthenticated.

## How Auto-Discovery Works

On startup `src/server.py` calls `import_submodules()` for the `tools`, `resources`, and
`prompts` packages. This uses `pkgutil.iter_modules()` to find every non-private `.py` file,
imports it, and the `@mcp.tool()` / `@mcp.resource()` / `@mcp.prompt()` decorators register
with the global `mcp` instance in `src/mcp_app.py`.

- Files starting with `_` are **skipped** automatically — use this to temporarily disable a module.
- To disable entirely: `AUTO_DISCOVER=false`, then import modules manually in `src/server.py`.

## How to Add a New Tool

```python
# src/tools/my_tool.py
from mcp_app import mcp

@mcp.tool(name="my_tool", description="Does something useful.")
def my_tool(input: str) -> dict:
    return {"result": input.upper()}
```

Restart the server — no other changes needed.

## How to Add a New Prompt

```python
# src/prompts/my_prompt.py
from mcp_app import mcp

@mcp.prompt(name="my_prompt", description="A focused system prompt.")
def my_prompt(context: str = "") -> str:
    return f"You are a helpful assistant. Context: {context}"
```

## How to Add a New Resource

```python
# src/resources/my_resource.py
from mcp_app import mcp

@mcp.resource("data://my-domain/my-resource")
def my_resource() -> dict:
    return {"data": "your payload here"}
```

## How to Add Knowledgebase Topics

Edit `knowledgebase/topics.yaml` — **no server restart needed**:

```yaml
topics:
  my_topic:
    title: "My Topic"
    tags: [example]
    content: |
      Topic content goes here.
```

## How to Test

```bash
# Health check:
curl http://localhost:8000/healthz

# Version:
curl http://localhost:8000/version

# With auth:
curl -H "Authorization: Bearer my-secret" http://localhost:8000/healthz
```

Connect with any MCP-compatible client (Claude Desktop, MCP Inspector, etc.).

## Getting Started with AI Agents

This template is designed to be cloned and extended with the help of AI coding agents
(Claude Code, Amazon Q, Cursor, etc.).

### Claude Code (CLI)

```bash
git clone <this-repo> my-mcp-server
cd my-mcp-server
claude
```

Claude Code **automatically reads `CLAUDE.md`** at the project root — no extra setup needed.
Just describe what you want to build.

### Amazon Q (IDE)

Open the project in your IDE with the Amazon Q plugin installed.
The `.amazonq/rules/mcp-conventions.md` file is auto-loaded into every chat session.
Describe what you want to build — Q will follow the template conventions.

### Cursor / Windsurf / Other IDEs

Reference the conventions file explicitly:
```
@CLAUDE.md Build me an MCP server that does X, Y, Z.
```

### Starter Prompts

See **[STARTER_PROMPTS.md](STARTER_PROMPTS.md)** for ready-to-paste prompts you can give
any AI agent to scaffold a new MCP server from this template.

---

## FastMCP Notes

Targets **FastMCP >= 2.0.0**. Key patterns:

| Pattern | Where |
|---|---|
| `FastMCP(name="...")` | `src/mcp_app.py` |
| `mcp.http_app(transport="streamable-http")` | `src/server.py` |
| `async with mcp_http_app.lifespan(app)` | `src/server.py` lifespan |
| `@mcp.tool()` / `@mcp.resource()` / `@mcp.prompt()` | tools / resources / prompts |

If you upgrade to a new FastMCP major version, verify these four patterns first.
See `CLAUDE.md` for full architecture details and coding rules.