AetherServe
README.md
# AetherServe
**An operations layer for Model Context Protocol (MCP) servers.**
[](LICENSE)
[](pyproject.toml)
[](tests/)
AetherServe wraps an MCP server built with the official
[Model Context Protocol Python SDK](https://github.com/modelcontextprotocol/python-sdk)
and adds the operational concerns that matter once that server leaves a
notebook and goes into a real deployment: **metrics, health checks, rate
limiting, structured logging, and lifecycle hooks.**
It is a small, dependency-light middleware layer, not a fork of the
protocol or the SDK. You keep writing tools the normal way; AetherServe
observes and governs the calls around them.
---
## Why
The MCP SDK gives you the protocol. It does not give you an answer to
questions every production deployment eventually asks:
- *Which tools are slow, and how slow, at the p50/p95/p99?*
- *Is this server healthy enough to keep receiving traffic?*
- *What stops one client from calling an expensive tool 500 times a second?*
- *How do I correlate a log line with the tool call that produced it?*
- *How do I run code exactly once at startup and exactly once at shutdown?*
AetherServe answers all five with a small, composable API, without asking
you to adopt a heavier framework or change how you write MCP tools.
## Installation
```bash
pip install aetherserve
```
This pulls in the `mcp` package as its only required runtime dependency.
For the bundled examples (which use Starlette/Uvicorn to expose HTTP
health and metrics endpoints):
```bash
pip install "aetherserve[examples]"
```
## Quickstart
```python
from mcp.server.fastmcp import FastMCP
from aetherserve import AetherServer, AetherServeConfig
raw_server = FastMCP("weather-server")
server = AetherServer(raw_server, AetherServeConfig(service_name="weather-server"))
@server.tool()
def get_forecast(city: str) -> str:
return f"Sunny in {city}"
if __name__ == "__main__":
server.run()
```
Every call to `get_forecast` is now timed, counted, rate-limited against
the server's default policy, wrapped in a trace span, and logged with a
correlation id — without a single line of that logic living in
`get_forecast` itself.
## Core Concepts
| Concern | Module | What you get |
|---|---|---|
| Metrics | `aetherserve.observability.metrics` | Counters, gauges, histograms; Prometheus text exposition |
| Logging | `aetherserve.observability.logging_setup` | JSON or text logs, automatic request-id correlation |
| Tracing | `aetherserve.observability.tracing` | Lightweight nested spans, pluggable exporter |
| Health | `aetherserve.health` | Named checks, critical vs. degraded aggregation, HTTP-status mapping |
| Rate limiting | `aetherserve.ratelimit` | Token-bucket limiter, global or per-client scope, per-tool overrides |
| Lifecycle | `aetherserve.hooks.lifecycle` | `startup` / `shutdown` / `before_tool_call` / `after_tool_call` / `tool_call_error` events |
See [`ARCHITECTURE.md`](ARCHITECTURE.md) for how these pieces fit together
around the `AetherServer` wrapper, and [`docs/`](docs/) for a deeper dive
into each subsystem.
## Per-tool rate limits
```python
from aetherserve.ratelimit.policies import RateLimitScope
server.set_rate_limit(
"generate_image",
capacity=2,
refill_rate=0.1, # ~1 token every 10 seconds
scope=RateLimitScope.PER_CLIENT,
)
@server.tool(client_key=lambda client_id, prompt: client_id)
def generate_image(client_id: str, prompt: str) -> str:
...
```
## Health checks
```python
def database_ping() -> None:
connection.execute("SELECT 1") # raises on failure
server.add_health_check("database", database_ping, critical=True)
```
`server.health.run_all()` aggregates every registered check into a single
`healthy` / `degraded` / `unhealthy` report; `aetherserve.health.endpoint.health_response`
maps that straight into an HTTP status code your framework of choice can
return.
## Examples
- [`examples/basic_server.py`](examples/basic_server.py) — the smallest
possible AetherServe server.
- [`examples/rate_limited_server.py`](examples/rate_limited_server.py) —
a stricter, per-client policy on one expensive tool.
- [`examples/full_observability_demo.py`](examples/full_observability_demo.py) —
wiring `/healthz` and `/metrics` into a Starlette app alongside the MCP
tool server.
## Development
```bash
git clone https://github.com/faizansiddiq/aetherserve.git
cd aetherserve
pip install -e ".[dev]"
pytest
mypy src/aetherserve
ruff check .
```
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the full workflow and
[`docs/testing.md`](docs/testing.md) for how the test suite is structured
(including the in-repo fake MCP server used so unit tests don't require a
live protocol connection).
## Relationship to the MCP SDK
AetherServe is **not** a fork, rebrand, or replacement of the official
Model Context Protocol Python SDK. It is an independent project that
takes an `mcp.server.fastmcp.FastMCP` instance as a constructor argument
and wraps it, in the same sense that a WSGI middleware wraps a WSGI app.
Full attribution for the underlying SDK is in [`NOTICE.md`](NOTICE.md).
## License
MIT — see [`LICENSE`](LICENSE). Copyright (c) 2026 Faizan Siddiq.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues