mcp-starter
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
MCP Server Python Starter
A clean starting point for building your own MCP server in Python. Clone it, swap the example tools for yours, and you've got something you can actually ship.
What this is
MCP (the Model Context Protocol) is an open standard that lets AI apps like Claude call your code, so the model can look up your data or take actions in your systems. An MCP server is the small program that exposes those capabilities, and this repo is a template for writing one well.
If you're new to the idea, I wrote a plain-English intro: What is an MCP server?
I put this together because most MCP examples stop at "hello world." That's fine for a demo. But the first time you put one in front of a real team, you need config, logging that doesn't break stdio, input validation, tests, and a way to deploy it. That's what's here.
Related MCP server: mcp-docs-server
What's included
The official
mcpPython SDK (v2), usingMCPServer. That's the decorator-based API that used to be calledFastMCP.Two example tools.
search_docsis a read-only lookup with pagination over a small bundled dataset.draft_support_tickettakes a pydantic model as input, so bad input gets a clear error before your code even runs.A resource template,
docs://{slug}, that returns a doc as Markdown.A prompt template,
explain_doc, that shows up as a reusable prompt in clients that support them.Config from environment variables with pydantic-settings (
MCP_STARTER_*, or a.envfile).JSON logs to stderr. With the stdio transport, stdout is the protocol channel, so one stray
print()can break the connection. Ruff'sT20rule is turned on to catch those.Two transports: stdio (the default, for local clients) and streamable HTTP (for running it as a service).
Tests that talk to the server through the SDK's in-memory client, plus a smoke test that launches it over real stdio.
Ruff, Pyright in strict mode, GitHub Actions CI, and a slim Dockerfile that runs as a non-root user.
The sample data is a made-up help center for a made-up product. It's only there so the tools have something to search. There are no network calls and no secrets.
Quick start
You'll need uv and Python 3.11 or newer.
git clone https://github.com/BonnieDooneConsulting/mcp-server-python-starter.git
cd mcp-server-python-starter
uv syncRun it over stdio (this is what Claude Code and Claude Desktop use):
uv run mcp-starterIt'll sit there waiting for a client on stdin. That's expected. Press Ctrl+C to stop it.
Or run it over HTTP:
uv run mcp-starter --transport streamable-http --port 8000
# the MCP endpoint is http://127.0.0.1:8000/mcpWant to poke at it in a browser first? The MCP Inspector is handy for that:
npx @modelcontextprotocol/inspector uv run mcp-starterConfiguration
Every setting can come from a flag, an environment variable, or a .env file. Flags win.
Variable | Flag | Default |
|
|
|
|
|
|
|
|
|
|
| |
|
|
|
|
| |
|
|
Connecting to Claude Code
Point Claude Code at your checkout:
claude mcp add mcp-starter -- uv --directory /absolute/path/to/mcp-server-python-starter run mcp-starterIf you're running the HTTP transport instead:
claude mcp add --transport http mcp-starter http://127.0.0.1:8000/mcpRun claude mcp list to check that it connected, then ask Claude something like "search the help docs for webhooks."
Connecting to Claude Desktop
Open Settings, go to Developer, and click Edit Config. That opens claude_desktop_config.json. Add this:
{
"mcpServers": {
"mcp-starter": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/mcp-server-python-starter",
"run",
"mcp-starter"
]
}
}
}Restart Claude Desktop. If it can't find uv, use the full path from which uv as the command. The desktop app doesn't always see the same PATH your terminal does, and it's a common reason a server shows up as failed.
Adding your own tool
Tools live in src/mcp_starter/tools.py, inside register(). Type hints become the input schema, the docstring becomes the description the model reads, and a pydantic return type becomes structured output. Here's a small one:
from typing import Annotated
from pydantic import BaseModel, Field
class WordCount(BaseModel):
words: int
characters: int
def register(server: MCPServer, store: DocStore) -> None:
...
@server.tool(annotations=ToolAnnotations(read_only_hint=True))
def count_words(
text: Annotated[str, Field(min_length=1, max_length=10_000)],
) -> WordCount:
"""Count the words and characters in a piece of text."""
return WordCount(words=len(text.split()), characters=len(text))A few habits that'll save you pain later:
Write the docstring for the model, not for you. It's how the model decides when to call your tool, so say what it does and what it returns.
Raise
ToolErrorfor failures you expect, like "that customer doesn't exist." The message goes straight back to the model, so make it something it can act on. Any other exception gets hidden behind a generic error and logged with a traceback, which is what you want for real bugs.Keep tools small and specific. A few focused tools are easier for the model to pick between than one big tool with a
modeargument.Add a test in
tests/test_server.py. Theclientfixture hands you a connected client, so most tests are a few lines.
If you outgrow one file, split tools into a package and call each module's register() from build_server() in server.py.
Running tests
uv run pytest # tests, all in-memory
uv run ruff check . # lint
uv run ruff format --check . # formatting
uv run pyright # type check (strict)
uv run python scripts/smoke_stdio.py # launch over real stdio and list toolsCI runs all of these on every push to main and every pull request, on Python 3.11 through 3.14.
Not using uv? A plain virtualenv works too:
python3 -m venv .venv
.venv/bin/pip install -e '.[dev]'
.venv/bin/pytestRunning in Docker
The image runs the HTTP transport on port 8000 as a non-root user.
docker build -t mcp-starter .
docker run --rm -p 8000:8000 mcp-starterOne heads-up before you put it on the internet: this template doesn't include auth. Inside the container it binds to 0.0.0.0, and the SDK only turns on DNS rebinding protection by default when it's bound to localhost. Put it behind something that handles auth, or wire up the SDK's auth support, before you expose it.
Project layout
.
├── src/mcp_starter/
│ ├── cli.py # entry point: parses flags, sets up logging, picks a transport
│ ├── server.py # build_server(): creates the MCPServer and registers everything
│ ├── config.py # Settings, loaded from MCP_STARTER_* env vars
│ ├── logging_setup.py # JSON logs to stderr
│ ├── store.py # tiny read-only data layer over the bundled JSON
│ ├── tools.py # search_docs, draft_support_ticket
│ ├── resources.py # docs://{slug}
│ ├── prompts.py # explain_doc
│ └── data/docs.json # sample data
├── tests/ # pytest, using the SDK's in-memory client
├── scripts/smoke_stdio.py
├── Dockerfile
├── .github/workflows/ci.yml
├── pyproject.toml # deps plus ruff, pyright, and pytest config
└── uv.lockContributing
Issues and pull requests are welcome. Please run the checks in Running tests before opening a PR. If you're planning something big, open an issue first so we can talk it through.
License
MIT. See LICENSE.
Built by Bonnie Doone Consulting. Need a custom MCP server for your team? Let's talk.
This server cannot be deployed
Maintenance
Related MCP Connectors
Augments MCP Server - A comprehensive framework documentation provider for Claude Code
MCP server for querying Forkast documentation
Read-only MCP server for the OrchestKit docs: full-text search + Markdown fetch. No auth.
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceGeneric MCP server that exposes Markdown documentation to LLMs, enabling them to search and answer questions about any software documentation.MIT
- FlicenseNot gradedqualityFmaintenanceServes Markdown documentation as an MCP server, enabling AI agents to answer questions from your content. Supports local, npm, and Cloudflare deployment with reusable prompts and resources.106 npm3-
- FlicenseNot gradedqualityBmaintenanceLocal documentation search server for AI models using hybrid retrieval (phrase, keyword, vector). Provides MCP tools to search and fetch documentation from bundled or custom doc sets without any external API keys.-
- FlicenseNot gradedqualityCmaintenanceA local MCP server providing read-only search and retrieval over the Claude documentation corpus via SQLite FTS5, with tools for searching, fetching documents, outlines, and listing sources without network I/O.-