Zero-dependency MCP server implementation
Project description
zeromcp
Minimal MCP server implementation in pure Python.
A lightweight, handcrafted implementation of the Model Context Protocol focused on what most users actually need: exposing tools with clean Python type annotations.
Features
- ✨ Zero dependencies - Pure Python, standard library only
- 🎯 Type-safe - Native Python type annotations for everything
- 🚀 Fast - Minimal overhead, maximum performance
- 🛠️ Handcrafted - Written by a human, verified against the spec
- 🌐 HTTP/SSE transport - Streamable responses
- 📡 Stdio transport - For legacy clients
- 📦 Tiny - Less than 1,000 lines of code
Installation
pip install zeromcp
Or with uv:
uv add zeromcp
Quick Start
from typing import Annotated
from zeromcp import McpServer
mcp = McpServer("my-server")
@mcp.tool
def greet(
name: Annotated[str, "Name to greet"],
age: Annotated[int | None, "Age of person"] = None
) -> str:
"""Generate a greeting message"""
if age:
return f"Hello, {name}! You are {age} years old."
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.serve("127.0.0.1", 8000)
Then manually test your MCP server with the inspector:
npx -y @modelcontextprotocol/inspector
Once things are working you can configure the mcp.json:
{
"mcpServers": {
"my-server": {
"type": "http",
"url": "http://127.0.0.1/mcp"
}
}
}
Stdio Transport
For MCP clients that only support stdio transport:
from zeromcp import McpServer
mcp = McpServer("my-server")
@mcp.tool
def greet(name: str) -> str:
"""Generate a greeting"""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.stdio()
Then configure in mcp.json (different for every client):
{
"mcpServers": {
"my-server": {
"command": "python",
"args": ["path/to/server.py"]
}
}
}
Type Annotations
zeromcp uses native Python Annotated types for schema generation:
from typing import Annotated, Optional, TypedDict, NotRequired
class GreetingResponse(TypedDict):
message: Annotated[str, "Greeting message"]
name: Annotated[str, "Name that was greeted"]
age: Annotated[NotRequired[int], "Age if provided"]
@mcp.tool
def greet(
name: Annotated[str, "Name to greet"],
age: Annotated[Optional[int], "Age of person"] = None
) -> GreetingResponse:
"""Generate a greeting message"""
if age is not None:
return {
"message": f"Hello, {name}! You are {age} years old.",
"name": name,
"age": age
}
return {
"message": f"Hello, {name}!",
"name": name
}
Union Types
Tools can accept multiple input types:
from typing import Annotated, TypedDict
class StructInfo(TypedDict):
name: Annotated[str, "Structure name"]
size: Annotated[int, "Structure size in bytes"]
fields: Annotated[list[str], "List of field names"]
@mcp.tool
def struct_get(
names: Annotated[list[str], "Array of structure names"]
| Annotated[str, "Single structure name"]
) -> list[StructInfo]:
"""Retrieve structure information by names"""
return [
{
"name": name,
"size": 128,
"fields": ["field1", "field2", "field3"]
}
for name in (names if isinstance(names, list) else [names])
]
Error Handling
from zeromcp import McpToolError
@mcp.tool
def divide(
numerator: Annotated[float, "Numerator"],
denominator: Annotated[float, "Denominator"]
) -> float:
"""Divide two numbers"""
if denominator == 0:
raise McpToolError("Division by zero")
return numerator / denominator
Supported clients
The following clients have been tested:
- Claude Code
- Claude Desktop (stdio only)
- Visual Studio Code
- Roo Code / Cline / Kilo Code
- LM Studio
- Jan
- Gemini CLI
- Cursor
- Windsurf
- Zed (stdio only)
- Warp
Note: generally the /mcp endpoint is preferred, but not all clients support it correctly.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zeromcp-1.0.0.tar.gz.
File metadata
- Download URL: zeromcp-1.0.0.tar.gz
- Upload date:
- Size: 13.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b1d0e189db32048cc37a01b03c7fc5f88328354e46cc5fef38329453291fe6e2
|
|
| MD5 |
c95ee55deac4c48dd7f02b8b791645a5
|
|
| BLAKE2b-256 |
15bb2399a2798ff2965e1a191728a8c6d8b40bbf942961677fc988ce7196de4e
|
File details
Details for the file zeromcp-1.0.0-py3-none-any.whl.
File metadata
- Download URL: zeromcp-1.0.0-py3-none-any.whl
- Upload date:
- Size: 10.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.7.17
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fd115d7f7984559d964af0e729b61d73b33f72379332d5eac8bb43fea9e2320
|
|
| MD5 |
633dbb71c68dab2e4f6471cafab2f3e1
|
|
| BLAKE2b-256 |
d058195cafaa6fa1b2a586329746fecd5bd56bf59c0b6d0a108b64d5dd5c820a
|