Skip to main content
Glama
muhammadwaqasmbd

mcp-server-template

mcp-server-template

A production-shaped starting point for an MCP server.

The quickstart in the MCP docs gets you a working tool in ten lines. This is what you end up adding over the following three weeks, once that tool is being called by something you do not control.

@mcp.tool()
def add(a: int, b: int) -> int:
    return a + b          # fine on a laptop

What is missing there is not features. It is what happens when the tool hangs, raises, returns a novel, or gets called forty times at once — and what the model is allowed to see when it does.


The problem this solves

An MCP tool's caller is a language model, which changes the engineering.

  • A model cannot read a stack trace, but it will cheerfully repeat one to your user. So a leaked traceback is both useless and a disclosure.

  • A model has its own deadline. A tool that hangs does not produce a slow answer; it produces a dead conversation.

  • A model cannot tell a truncated result from a complete one. Silently overflowing its context does not raise an error — it degrades the answer, and you find out from a customer.

  • A model will retry if you let it. So "not found" and "upstream is down" have to be different answers, or it will hammer a service over a record that was never there.

Every one of those is handled once, in one place, so that a tool added on a Friday afternoon inherits the same protections as the one written carefully on day one.


Related MCP server: Graft

What you get

Per-tool timeout

Real cancellation, not a warning afterwards. Returns a retryable error the model can act on

Concurrency ceiling

Bounded parallel execution, so a burst cannot stampede whatever your tools call

Error boundary

Declared errors reach the caller; unexpected ones become internal_error with no detail, and the traceback goes to the log

Secret redaction

Applied to logs and outbound messages, because keys escape through interpolated exception strings more often than through code

Visible truncation

Oversized results are cut with a marker, never silently

Correlation ids

One id per call, in the log and in the error the user can quote back to you

Structured logs on stderr

stdout belongs to the protocol — a stray print() corrupts the stream

Fail-fast config

Bad settings stop the server at boot, not on the first request

Offline tests

The suite runs on a train. No live keys, no network


Quickstart

git clone https://github.com/muhammadwaqasmbd/mcp-server-template
cd mcp-server-template
make install
make test
make run          # stdio, ready for a desktop MCP client

Serve it over the network instead:

TRANSPORT=streamable-http PORT=8000 python -m mcp_server_template

Point a desktop client at it

{
  "mcpServers": {
    "template": {
      "command": "python",
      "args": ["-m", "mcp_server_template"],
      "cwd": "/absolute/path/to/mcp-server-template"
    }
  }
}

Adding your own tool

Write the function. Nothing else.

# src/mcp_server_template/tools/orders.py
from ..errors import InvalidInput, UpstreamUnavailable

async def cancel_order(order_id: str) -> dict:
    """Cancel an order. Returns the order's new state."""
    if not order_id.strip():
        raise InvalidInput("order_id must not be empty")     # model can fix this
    ...
    raise UpstreamUnavailable("order service timed out")     # model may retry

Register it behind the guard:

mcp.tool(name="cancel_order", description="Cancel an order by id.")(
    guard.wrap(orders.cancel_order)
)

It now has the timeout, the ceiling, the error boundary, the truncation and the logging. You wrote none of that.

Raise InvalidInput when the model can fix it. Raise UpstreamUnavailable when retrying might work. Return normally for outcomes that are simply false — a missing record is an answer, not a failure.


Architecture

server.py      the ONLY module that imports the MCP SDK
   │
   ├── guard.py          timeout · concurrency · error boundary · truncation · timing
   ├── errors.py         what a model is allowed to see, and secret redaction
   ├── observability.py  JSON logs on stderr, correlation ids
   ├── config.py         validated once at boot, immutable thereafter
   └── tools/            plain functions. No protocol knowledge. No decorators

The dependency arrow points one way: tools know nothing about MCP, and the guard knows nothing about your tools. That is why the tests run in milliseconds without a server, and why an SDK change touches exactly one file.


What this deliberately does not do

Being honest about the edges is more useful than a longer feature list.

  • No authentication. Over stdio the OS boundary is the security boundary. If you expose it over HTTP, put real auth in front — the SDK supports it, and wiring it here would imply a threat model you have not chosen yet.

  • No retry logic inside tools. The guard reports whether a failure is retryable; deciding to retry belongs to the caller, which has the context and the budget.

  • No rate limiting per caller. The concurrency ceiling bounds total work, not per-identity fairness. If you need that, you need identity first.

  • No persistence, queue or scheduler. A tool server that quietly became a job runner is a distributed system nobody designed.

  • No streaming partial results. Worth adding for long-running tools; left out because it complicates the error boundary and most tools do not need it.


Testing

make test

The suite is deliberately about failure, not coverage. It asserts that a hung tool is cancelled, that an unexpected exception cannot leak its message, that oversized output is truncated visibly, that the concurrency ceiling holds under ten simultaneous calls, and that a blocking sync tool does not starve the event loop.


Licence

MIT — see LICENSE.

Built by Muhammad Waqas, who spends most of his time on agent systems in regulated industries, where a confident wrong answer is a reportable incident.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    A production-grade, extensible Python template for building Model Context Protocol servers with support for Streamable HTTP and stdio transports. It provides a structured framework for implementing tools, resources, and prompts with built-in authentication, observability, and background task management.
    11
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables building agent-ready APIs that expose tools as both HTTP and MCP endpoints from a single server definition, with automatic OpenAPI, discovery docs, and interactive API reference.
    5
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    A production-ready foundation for building secure, observable MCP servers with built-in authentication, rate limiting, and reference tools like database-query and semantic-search.
    15
    78
    MIT

View all related MCP servers

Related MCP Connectors

  • Hosted MCP server for LLM cost estimation, model comparison, and budget-aware routing.

  • MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.

  • An MCP server for Arcjet - the runtime security platform that ships with your AI code.

View all MCP Connectors

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/muhammadwaqasmbd/mcp-server-template'

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