Skip to main content
Glama
michellepellon

mcp-server-template

mcp-server-template

A Python MCP server template with the parts demos leave out: env-driven config, key auth with rotation, JSON logging with request IDs, and a golden-set eval harness. Fork it, replace the example tools, keep the shape.

Quick start

uv sync
uv run mcp-template          # stdio server with seeded demo data
uv run pytest                # unit + integration + e2e + eval contracts

Register it with Claude Desktop or Claude Code:

{
  "mcpServers": {
    "template": { "command": "uv", "args": ["run", "mcp-template"] }
  }
}

Related MCP server: Streamable HTTP Python MCP Server Template

Why this exists

Most public MCP examples stop at the demo: one file, print statements, no tests. What separates that from something you can deploy is auth, observability, and evals, so those are the parts this template takes seriously.

Auth: a stdio server inherits the trust of whatever launched it, but the moment you expose streamable-http you need token verification and a rotation story. auth.py does constant-time key verification with a two-slot rotation window.

Observability: when a tool call fails inside an agent loop, you need to know which call, with what arguments, and how long it ran. Every call gets a request ID and a JSON log line on stderr. stdout belongs to the protocol.

Evals: tools drift. The harness in evals/ replays golden request/response contracts against the server in-process, so a behavior change fails CI before a client notices.

Layout

src/mcp_template/
├── server.py        # FastMCP entrypoint; DuckDB lifecycle via lifespan
├── config.py        # pydantic-settings, MCP_TEMPLATE_* env vars
├── log.py           # JSON lines to stderr, request-id contextvar
├── auth.py          # static key verifier, rotation window
└── tools/
    ├── registry.py       # registration + per-call instrumentation
    └── example_query.py  # REPLACE-ME: guarded read-only DuckDB query tool
evals/
├── goldens/         # recorded request → expected response contracts
└── test_tool_contracts.py
tests/               # unit, in-process integration, stdio subprocess e2e

Config is all environment variables (MCP_TEMPLATE_*; the full list is in config.py). Unsafe combinations fail at startup: requiring auth with no keys configured is a ValueError, not a silent pass-through.

Adding a tool

  1. Write the handler under tools/ with its unit tests.

  2. Register it in register_all (tools/registry.py), wrapped in _instrument(...) so it logs like the rest.

  3. Record a golden contract in evals/goldens/.

  4. Delete example_query.py once you have real tools.

The example tool is worth reading before you delete it: single-statement SELECT/WITH validation before execution, parameter binding instead of string interpolation, a hard row cap with an explicit truncated flag, and database errors surfaced as tool errors rather than crashes. The guard is defense in depth; production should also run against a read-only connection or replica.

Tests

Four layers, all under uv run pytest:

Layer

Where

What it proves

Unit

tests/test_*.py

each module's behavior in isolation

Integration

tests/test_server_inprocess.py

tools over the real protocol, in-process memory streams

End-to-end

tests/test_stdio_e2e.py

a spawned subprocess speaking stdio MCP

Eval contracts

evals/

golden request/response stability across changes

The eval layer is the one that pays for itself: change a tool and the contract diff tells you whether clients will notice.

Deploying

The Dockerfile builds a slim non-root image with locked dependencies. Inject MCP_TEMPLATE_AUTH_KEYS from your secret manager; never bake keys into the image. For streamable-http exposure, wire StaticKeyVerifier.verify into your HTTP layer or terminate auth at a reverse proxy.

License

MIT

Available Tools

2 tools
describe_tablesA

List queryable tables and their columns

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden of disclosing behavior. The verb 'List' implies a read-only, non-mutating operation, and 'queryable tables' scopes what is returned, but no details about permissions, side effects, or edge cases are provided.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with no wasted words. It is appropriately sized for a parameterless metadata-listing tool and immediately states the tool's core functionality.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has no parameters, has an output schema, and the sibling is run_query, the description is largely adequate for selection and invocation. It could benefit from explicitly stating that it is a safe, schema-discovery step before running queries, but that omission is not critical.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so there is no parameter semantics to document. The description appropriately focuses on what the tool returns instead.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb, 'List', and a clear resource, 'queryable tables and their columns'. It clearly conveys that this tool exposes metadata rather than executing queries, which distinguishes it from its sibling run_query.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no explicit guidance on when to use this tool versus run_query. While the name and description imply it should be used to discover available tables and columns before querying, this is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

run_queryA

Run a read-only SQL query with bound parameters

ParametersJSON Schema
NameRequiredDescriptionDefault
sqlYes
paramsNo
max_rowsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description discloses the important read-only nature of the operation, which is a meaningful behavioral trait especially given there are no annotations. It also mentions bound parameters, implying parameterized execution, but it does not elaborate on query restrictions, max_rows behavior, errors, or potential overhead.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence with no unnecessary words. It conveys the primary action and the key constraint efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The output schema covers return values, and the parameters are relatively simple, which reduces the burden on the description. However, the description omits guidance on alternatives and leaves max_rows semantics implicit, making it minimally complete but with clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate for the lack of parameter docs. It only refers to 'bound parameters', which loosely maps to the params argument, but it does not explain the parameter placeholder format or the purpose of max_rows. The sql parameter is self-evident but not elaborated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Run') and resource ('SQL query'), and adds the key constraints 'read-only' and 'bound parameters' that distinguish it from sibling describe_tables, which is about table metadata rather than data queries.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool is for arbitrary read-only SQL execution, which gives a general sense of when to use it. However, it does not explicitly contrast with describe_tables or state when one should be preferred over the other, leaving the boundary to inference.

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. Dates show when Glama detected each change.

  1. 2 tool updatesv0.1.0
    • First observeddescribe_tables
    • First observedrun_query

TDQS

A3.8/5.0
Disambiguation5/5

run_query executes SQL statements while describe_tables returns schema metadata. The two tools have no functional overlap and are easy to distinguish.

Naming Consistency5/5

Both tools follow the same lowercase verb_noun pattern: run_query and describe_tables. Naming is predictable and consistent.

Tool Count3/5

Two tools is on the low end of reasonable for a focused read-only database server. The set is coherent but feels thin, leaving little flexibility for more advanced exploration.

Completeness4/5

The pair covers the core need of read-only SQL querying with schema discovery support. Minor gaps exist, such as no ability to inspect detailed table statistics or relationships, but agents can generally accomplish their main tasks.

Maintenance

ActivitySlowing
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A production-ready Python template for building MCP servers with enterprise features including registry integration, configuration management, structured logging, and extensible patterns for tools, resources, and prompts.
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    A starter template for building MCP servers in Python using the streamable HTTP transport protocol. Provides a foundation with the MCP Python SDK and example configuration to quickly develop custom MCP servers.
    2
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A production-ready template for building Model Context Protocol (MCP) servers in Python, using Docker Compose for containerized development and CI/CD.
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    A production-ready template for developing Model Context Protocol (MCP) servers using Python and FastMCP.
    Apache 2.0

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/michellepellon/mcp-server-template'

If you have feedback or need assistance with the MCP directory API, please join our Discord server