mcpserve
by AmirNaghibi
README.md
# mcpserve
Minimal, decorator-based framework for building [Model Context Protocol](https://modelcontextprotocol.io) (MCP) servers in Python.
**Zero required dependencies.** Define tools and resources with simple decorators, and mcpserve handles the JSON-RPC protocol, parameter inference, and stdio transport.
## Why?
MCP is the emerging standard for connecting AI assistants to external tools and data. But building an MCP server from scratch means implementing JSON-RPC 2.0 framing, capability negotiation, schema generation, and error handling. mcpserve handles all of that so you can focus on your tool logic.
- **Decorator-based** — `@server.tool()` and `@server.resource()` are all you need
- **Type inference** — parameter types and optionality are inferred from Python type hints
- **Async-ready** — supports both sync and async tool handlers
- **Zero dependencies** — stdlib only for the core library (asyncio + json)
- **MCP 2024-11-05** — implements the latest protocol version
## Architecture
```mermaid
graph LR
subgraph "AI Assistant"
A[LLM Client]
end
subgraph "mcpserve"
B[Stdio Transport] --> C[JSON-RPC Router]
C --> D[Method Dispatcher]
D --> E[tools/list]
D --> F[tools/call]
D --> G[resources/list]
D --> H[resources/read]
end
subgraph "Your Code"
I["@server.tool()"]
J["@server.resource()"]
end
A <-->|"stdin/stdout"| B
F --> I
H --> J
```
## Quick Start
```python
from mcpserve import Server
server = Server(name="my-tools", version="1.0.0")
@server.tool()
def add(a: int, b: int) -> str:
"""Add two numbers together."""
return str(a + b)
@server.tool()
def search(query: str, limit: int = 10) -> str:
"""Search for documents matching a query."""
# Your logic here
return f"Found {limit} results for: {query}"
@server.resource(uri="status://health", name="Health Check")
def health():
"""Server health status."""
return "ok"
if __name__ == "__main__":
server.run()
```
That's it. Run your server, point an MCP client at it, and your tools are available to the AI.
## Installation
```bash
pip install mcpserve
```
Or from source:
```bash
git clone https://github.com/AmirNaghibi/mcpserve.git
cd mcpserve
pip install -e .
```
## Usage with Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"my-tools": {
"command": "python",
"args": ["/path/to/your/server.py"]
}
}
}
```
## Features
### Tool Registration
Parameters are automatically inferred from type hints:
```python
@server.tool()
def fetch_url(url: str, timeout: int = 30, follow_redirects: bool = True) -> str:
"""Fetch content from a URL."""
# url: required string
# timeout: optional integer, default 30
# follow_redirects: optional boolean, default True
...
```
Supported types: `str`, `int`, `float`, `bool`, `list`, `dict`
### Async Tools
```python
@server.tool()
async def slow_operation(input: str) -> str:
"""An async tool that does something time-consuming."""
await asyncio.sleep(1)
return f"Processed: {input}"
```
### Resources
Expose data that the AI can read without calling a tool:
```python
@server.resource(uri="docs://readme", name="README", mime_type="text/markdown")
def readme():
with open("README.md") as f:
return f.read()
```
### Structured Results
Return rich results when plain text isn't enough:
```python
from mcpserve import ToolResult
@server.tool()
def query_db(sql: str) -> ToolResult:
try:
rows = db.execute(sql)
return ToolResult.json(rows)
except Exception as e:
return ToolResult.error(f"Query failed: {e}")
```
### Custom Tool Names
```python
@server.tool(name="web_search", description="Search the internet")
def my_internal_function(query: str) -> str:
...
```
## Request Flow
```mermaid
sequenceDiagram
participant Client as AI Client
participant Transport as Stdio Transport
participant Router as JSON-RPC Router
participant Handler as Tool Handler
Client->>Transport: {"jsonrpc":"2.0","id":1,"method":"initialize"}
Transport->>Router: parse + validate
Router-->>Transport: capabilities response
Transport-->>Client: {"jsonrpc":"2.0","id":1,"result":{...}}
Client->>Transport: {"method":"tools/call","params":{"name":"add","arguments":{"a":2,"b":3}}}
Transport->>Router: parse
Router->>Handler: dispatch(add, {a:2, b:3})
Handler-->>Router: "5"
Router-->>Transport: ToolResult
Transport-->>Client: {"result":{"content":[{"type":"text","text":"5"}]}}
```
## API Reference
### `Server(name, version)`
Create a new MCP server.
### `@server.tool(name=None, description=None)`
Register a function as a tool. Name defaults to the function name, description defaults to the docstring.
### `@server.resource(uri, name=None, description=None, mime_type="text/plain")`
Register a function as a resource. The function is called when the client reads the resource URI.
### `ToolResult.text(str)` / `ToolResult.error(str)` / `ToolResult.json(data)`
Factory methods for creating tool results.
### `server.run()`
Start the server on stdio transport.
## Running Tests
```bash
pip install -e ".[dev]"
pytest tests/ -v
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing