Skip to content

starter-series/python-mcp-server-starter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Python MCP Server Starter

Python + OIDC PyPI Publishing + CI/CD.

Build your MCP server. One-click publish. Zero secrets needed.

CI License: MIT PyPI version

English | 한국어


Part of Starter Series — Stop explaining CI/CD to your AI every time. Clone and start.

Docker Deploy · Discord Bot · Telegram Bot · Browser Extension · Electron App · npm Package · React Native · VS Code Extension · MCP Server (TS) · MCP Server (Python) · Cloudflare Pages


What You Get

  • MCP SDKmcp (FastMCP) with stdio transport
  • Python 3.11+ — Type hints, async/await, hatchling build
  • All three MCP primitives — Tools, Resources, and Prompts with working examples
  • Safety Annotations — readOnly/destructive/idempotent hints on every tool
  • Validated Prompts — pydantic @validate_call rejects bad args before the handler runs
  • Response Helpersok() and err() for consistent tool responses
  • Config — Environment variable parsing pattern
  • CI — gitleaks, ruff, license compliance, pytest (3.11/3.12/3.13)
  • CD — OIDC trusted publishing to PyPI (zero secrets needed)
  • Dependabot — Automated dependency + GitHub Actions updates

Quick Start

Via create-starter (recommended):

npx @starter-series/create my-mcp-server --template mcp-server-python
cd my-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'

Or clone directly:

git clone https://github.com/starter-series/python-mcp-server-starter my-mcp-server
cd my-mcp-server
python -m venv .venv && source .venv/bin/activate
pip install -e '.[dev]'

Adding Tools

Tool names must be globally unique across all MCP servers a client connects to. Prefix with your module name (e.g., mymodule_action instead of action).

Inline (simple)

Add directly to src/my_mcp_server/server.py:

@mcp.tool(
    annotations=ToolAnnotations(
        readOnlyHint=True,
        destructiveHint=False,
        idempotentHint=True,
        openWorldHint=False,
    ),
)
async def your_tool(input: str) -> str:
    """What your tool does.

    Args:
        input: Input parameter.
    """
    return f"Processed: {input}"

Modular (recommended for larger servers)

Create src/my_mcp_server/tools/your_tool.py:

from mcp.server.fastmcp import FastMCP
from mcp.types import ToolAnnotations


def register(mcp: FastMCP) -> None:
    @mcp.tool(
        annotations=ToolAnnotations(
            readOnlyHint=True,
            destructiveHint=False,
            idempotentHint=True,
        ),
    )
    async def your_tool(input: str) -> str:
        """What your tool does."""
        return f"Processed: {input}"

Then in server.py:

from my_mcp_server.tools.your_tool import register
register(mcp)

Adding Resources

Resources expose read-only data to the client at a stable URI (contrast with Tools, which perform actions).

See src/my_mcp_server/resources/server_info.py for the example. Pattern:

from mcp.server.fastmcp import FastMCP


def register(mcp: FastMCP) -> None:
    @mcp.resource(
        "info://your/resource",
        name="your-resource",
        description="What this resource exposes.",
        mime_type="application/json",
    )
    async def your_resource() -> str:
        return "..."  # str, bytes, or JSON-serializable object

Then in server.py:

from my_mcp_server.resources.your_resource import register as register_your_resource
register_your_resource(mcp)

Adding Prompts

Prompts are reusable, parameterized message templates. Arguments are validated via pydantic before the handler runs.

See src/my_mcp_server/prompts/code_review.py for the example. Pattern:

from typing import Annotated, Literal

from mcp.server.fastmcp import FastMCP
from mcp.server.fastmcp.prompts.base import UserMessage
from pydantic import Field, validate_call


@validate_call
def your_prompt(
    mode: Literal["short", "long"],
    topic: Annotated[str, Field(min_length=1)],
) -> list[UserMessage]:
    return [UserMessage(content=f"Write a {mode} note about {topic}.")]


def register(mcp: FastMCP) -> None:
    mcp.prompt(name="your-prompt", title="Your Prompt")(your_prompt)

Configuration

Environment variables:

Variable Default Description
MCP_DEBUG false Enable debug logging
LOG_LEVEL INFO Log level (DEBUG/INFO/WARNING/ERROR)

Add your own in server.py.

Testing Locally

# Run tests
pytest -v

# Lint
ruff check .

# Run the server (stdio)
python -m my_mcp_server

CI/CD

CI (runs on every push/PR)

Check Tool
Secret scanning gitleaks
Large file detection find (>5 MB)
License compliance pip-licenses (blocks GPL/AGPL)
Lint + format ruff
Tests pytest (Python 3.11, 3.12, 3.13)

CD (publish to PyPI)

  1. Bump version in pyproject.toml
  2. Go to Actions → Publish to PyPI → Run workflow
  3. OIDC handles auth — no PYPI_TOKEN secret needed

Setup: PyPI OIDC trusted publishing docs

Project Structure

src/my_mcp_server/
├── __init__.py          # Version
├── __main__.py          # python -m entry point
├── server.py            # FastMCP server + inline tools + helpers
├── tools/
│   ├── __init__.py
│   └── greet.py          # Example modular tool
├── resources/
│   ├── __init__.py
│   └── server_info.py    # Example resource (info://server/status)
└── prompts/
    ├── __init__.py
    └── code_review.py    # Example prompt (validated args)
tests/
├── test_tools.py         # Tool tests
├── test_server_info.py   # Resource tests
└── test_code_review.py   # Prompt tests
.github/
├── workflows/
│   ├── ci.yml            # Lint, test, security
│   ├── cd.yml            # PyPI OIDC publish
│   ├── stale.yml         # Stale issue management
│   └── maintenance.yml   # Weekly health check
└── dependabot.yml        # Dependency updates

Scripts

pip install -e ".[dev]"   # Install with dev deps
python -m my_mcp_server   # Run server
pytest -v                 # Run tests
ruff check .              # Lint
ruff format .             # Format

License

MIT

About

Python MCP server starter template — FastMCP, ruff, pytest, CI/CD ready

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages