MGnify MCP Server
# MGnify MCP Server
An MCP (Model Context Protocol) server exposing the [MGnify v2 (EBI Metagenomics) API](https://www.ebi.ac.uk/metagenomics/api/v2/) as **23 typed tools** an LLM can call directly. Ships with stdio transport for desktop clients and Streamable HTTP transport for Kubernetes deployments, plus health/metrics endpoints and an end-to-end evaluation harness.
## What it gives you
- **23 tools** covering biomes, studies, super-studies, samples, runs, analyses, typed annotations (taxonomies + pfams), genome catalogues, genomes, publications.
- **Two transports**: `stdio` (Claude Desktop, IDE clients) and HTTP Streamable at `/mcp` (web/k8s).
- **Production endpoints**: `/healthz` (liveness), `/readyz` (verifies upstream MGnify), `/metrics` (Prometheus text — per-tool call/error counters).
- **Resilient client**: retries on 5xx, surfaces 429 with `Retry-After`, clamps page sizes; passes through v2's clean `{count, items[]}` shape unchanged.
- **K8s manifests**: Deployment with proper probes, HPA, PDB, ConfigMap, optional Secret, Ingress with streaming-friendly nginx annotations, kustomize entrypoint.
- **Evaluation harness**: 10 verifiable questions, a fast fact-check script, and an LLM-driven runner that measures pass rate, tool calls, latency, tokens.
- **Perf benchmark** for capacity planning (p50/p95/p99 latency, throughput).
## Quick start (local stdio)
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
cp .env.example .env
python scripts/smoke_test.py # confirm upstream API works
mgnify-mcp # stdio MCP server
```
Add it to Claude Desktop's `claude_desktop_config.json`:
```json
{
"mcpServers": {
"mgnify": {
"command": "/abs/path/to/.venv/bin/mgnify-mcp",
"env": { "MG_BASE_URL": "https://www.ebi.ac.uk/metagenomics/api/v2" }
}
}
}
```
## Quick start (HTTP / Docker)
```bash
docker compose up --build
curl http://127.0.0.1:8173/healthz
curl http://127.0.0.1:8173/readyz
curl http://127.0.0.1:8173/metrics
```
MCP clients connect to `http://127.0.0.1:8173/mcp` over Streamable HTTP.
## Kubernetes deployment
```bash
# 1. Build & push image
make build push REGISTRY=your-registry TAG=v0.2.0
# 2. Point the deployment at it
( cd k8s && kustomize edit set image your-registry/mgnify-mcp=your-registry/mgnify-mcp:v0.2.0 )
# 3. Deploy
kubectl apply -k k8s/
kubectl rollout status deployment/mgnify-mcp
# 4. Verify
kubectl port-forward svc/mgnify-mcp 8173:80 &
curl http://127.0.0.1:8173/healthz
```
Manifests included: `deployment.yaml` (probes + non-root + read-only FS), `service.yaml`, `ingress.yaml` (streaming-friendly), `configmap.yaml`, `hpa.yaml` (CPU+memory autoscale 2→8), `pdb.yaml` (`minAvailable: 1`), `secret.yaml` (optional MG_API_KEY).
See `DEPLOYMENT.md` for the long-form runbook.
## Measuring effectiveness
Two complementary checks.
### 1. Fact check (fast, no LLM)
Confirms the 10 expected answers still match upstream MGnify. Run this on a schedule (CI cron) to catch silent staleness.
```bash
python evals/check_facts.py
```
### 2. End-to-end LLM evaluation
Drives a real Claude model against the running MCP server. The model must answer each question by composing tool calls. This is the headline quality metric — if a capable model can't reliably answer, the tools or descriptions are wrong.
```bash
export ANTHROPIC_API_KEY=sk-...
pip install -e '.[eval]'
# Server must be running on HTTP transport
python evals/run_eval.py \
--server-url http://127.0.0.1:8173/mcp \
--model claude-opus-4-7 \
--report eval-report.json
```
Reports pass rate, average latency per question, average tool calls used, and tokens consumed. Treat <90% pass rate as a regression.
### 3. Latency / throughput benchmark
For capacity planning before bumping replicas or HPA targets:
```bash
python scripts/perf_test.py \
--server-url http://127.0.0.1:8173/mcp \
--tool mgnify_list_studies \
--concurrency 20 --requests 200
```
Reports p50/p95/p99 latency, throughput (req/s), error rate. Most latency comes from the upstream MGnify API (~1–2s typical), so set readiness/liveness timeouts accordingly.
## Tool catalogue
Biomes (1) · Studies (5) · Super-studies (2) · Samples (3) · Runs (3) · Analyses (3) · Genome catalogues & genomes (4) · Publications (2). All named `mgnify_*` with Pydantic-validated inputs.
List them at runtime:
```bash
python -c "import asyncio; from mgnify_mcp.server import mcp; \
print('\n'.join(t.name for t in asyncio.run(mcp.list_tools())))"
```
## Environment variables
| Var | Default | Purpose |
|---|---|---|
| `MG_BASE_URL` | `https://www.ebi.ac.uk/metagenomics/api/v2` | Upstream MGnify API |
| `MG_API_KEY` | _(empty)_ | Optional bearer token |
| `MCP_TRANSPORT` | `stdio` | `stdio` or `http` |
| `MCP_HOST` | `0.0.0.0` | HTTP bind host |
| `MCP_PORT` | `8173` | HTTP bind port |
| `MCP_CORS_ORIGINS` | `*` | Comma-separated allowed origins |
| `LOG_LEVEL` | `info` | uvicorn log level |
## Endpoints
- `POST /mcp` — Streamable HTTP MCP transport
- `GET /healthz` — Liveness (always 200 if the process is alive)
- `GET /readyz` — Readiness (verifies upstream MGnify reachable)
- `GET /metrics` — Prometheus text (`mgnify_mcp_tool_calls_total`, `mgnify_mcp_tool_errors_total`, `mgnify_mcp_uptime_seconds`)
## Project layout
```
mgnify_mcp/ # the server package
server.py # FastMCP server + FastAPI wrapper
mgnify_client.py # HTTP client with retries against MGnify v2 OpenAPI surface
schemas.py # Pydantic input schemas
evals/ # evaluation harness
questions.xml # 10 verifiable Q&A pairs
check_facts.py # direct-API fact check (no LLM)
run_eval.py # LLM-driven E2E eval
scripts/
smoke_test.py # quick upstream-API sanity check
perf_test.py # latency / throughput benchmark
build-and-push.sh, deploy.sh
k8s/ # Deployment, Service, Ingress, HPA, PDB, ConfigMap, Secret
```
## License
See `LICENSE` (if present) or the repository hosting service.
TDQS
Scored across 23 tools
Every tool targets a distinct entity or relationship: list/get variants for studies, samples, runs, analyses, genomes, catalogues, publications, biomes, and super-studies. Where multiple tools list analyses, their scoping (all, by study, by run) is explicit in descriptions, so an agent should not confuse them.
The set consistently uses the mgnify_ prefix and snake_case, with list_X/get_X for most resources. The relational endpoints (mgnify_study_analyses, mgnify_sample_runs) and the single mgnify_analysis_annotations deviate from the list/get convention, but the pattern is still predictable.
23 tools is above the typical sweet spot, but the domain spans many entity types and each tool maps to a distinct API operation. It is slightly heavy but not bloated.
The read-only domain is covered end-to-end: every core entity has a list and get tool, and relationship tools allow traversal from super-studies/studies to samples to runs to analyses, plus genome catalogues and publications. No obvious dead ends or missing operations for the stated browse/search purpose.