mcp-server-template
Provides tools for executing read-only SQL queries on DuckDB databases with parameter binding, row limits, and truncated flags.
Click on "Install 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-templatequery all records from the example table"
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
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 contractsRegister 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 e2eConfig 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
Write the handler under
tools/with its unit tests.Register it in
register_all(tools/registry.py), wrapped in_instrument(...)so it logs like the rest.Record a golden contract in
evals/goldens/.Delete
example_query.pyonce 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 |
| each module's behavior in isolation |
Integration |
| tools over the real protocol, in-process memory streams |
End-to-end |
| a spawned subprocess speaking stdio MCP |
Eval contracts |
| 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 toolsdescribe_tablesA
List queryable tables and their columns
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | ||
| params | No | ||
| max_rows | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
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.
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.
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.
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.
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.
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.
2 tool updates
v0.1.0- First observed
describe_tables - First observed
run_query
TDQS
run_query executes SQL statements while describe_tables returns schema metadata. The two tools have no functional overlap and are easy to distinguish.
Both tools follow the same lowercase verb_noun pattern: run_query and describe_tables. Naming is predictable and consistent.
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.
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
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
Primarily to be used as a template repository for developing MCP servers with FastMCP in Python, P…
An MCP server that provides Javelin Standalone Guardrails
- ArcjetOAuthcom.arcjet
An MCP server for Arcjet - the runtime security platform that ships with your AI code.
MCP server for Modern Treasury — payment orders, transactions, counterparties and ledgers.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA 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
- FlicenseNot gradedqualityDmaintenanceA 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-
- AlicenseNot gradedqualityCmaintenanceA production-ready template for building Model Context Protocol (MCP) servers in Python, using Docker Compose for containerized development and CI/CD.MIT
- AlicenseNot gradedqualityCmaintenanceA production-ready template for developing Model Context Protocol (MCP) servers using Python and FastMCP.Apache 2.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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