reactome-db-mcp
# reactome-db-mcp
> An [MCP](https://modelcontextprotocol.io) server that gives coding agents **direct SQL access to a locally hosted Reactome database** — schema discovery, a guarded read-only `query()`, and ergonomic helpers over the full GKB relational schema, all from the chat.
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io)
[](LICENSE)
Built with the official Python MCP SDK ([FastMCP](https://github.com/modelcontextprotocol/python-sdk)) over stdio. It talks straight to MySQL with pure-Python **PyMySQL** (run via `asyncio.to_thread`), so there is no build toolchain to install.
### `reactome-db-mcp` vs [`reactome-mcp`](../reactome-mcp)
They are **complementary** — register both:
| | `reactome-mcp` (REST) | `reactome-db-mcp` (this) |
|---|---|---|
| Backend | Reactome REST + Analysis web services | local `reactome_local` MySQL DB |
| Needs | internet; zero setup | the local DB up (MySQL + dump loaded) |
| Strength | curated, pre-joined objects; enrichment p-values/FDR; always current | fast, arbitrary joins + reverse navigation over the raw 242-table schema |
| Use it for | "what does Reactome say about X", enrichment | bespoke relational queries the REST API never exposed |
Tool names are deliberately distinct (`get_object` vs `get_entry`, `search_by_name` vs `search`, …) so both can be registered without ambiguity.
---
## Prerequisites — the local database
This server needs a running MySQL with the Reactome `gk_current` dump loaded as `reactome_local`, reachable by a read-only user. On this machine that is already set up:
- **MySQL 9.6** (Homebrew), database `reactome_local` (~990 MB, 242 MyISAM tables, Reactome release 96).
- Read-only user `ro_user@localhost`, **empty password**, `SELECT`-only.
If `mysqld` isn't running (it is **not** registered with `brew services`, so it won't survive a reboot):
```bash
brew services start mysql # reliable auto-start, or: /opt/homebrew/opt/mysql/bin/mysqld_safe &
```
To rebuild the DB from scratch, see [`../reactome_local_build`](../reactome_local_build) (keeps `gk_current.sql.gz`).
---
## Quickstart
Requires [`uv`](https://github.com/astral-sh/uv) (which manages Python ≥ 3.12 for you).
```bash
cd reactome-db-mcp
uv sync # install runtime deps (mcp, PyMySQL)
uv run reactome-db-mcp # boots the server on stdio (Ctrl+C to exit)
```
Then register it with your agent (below) and ask something like:
> *"Using the reactome-db tools, look up R-HSA-69278, then list its child events in order."*
---
## Register with your agent
Standard stdio MCP server launched with `uv run reactome-db-mcp`. Run from inside the cloned directory.
**Claude Code**
```bash
claude mcp add reactome-db -- uv --directory "$PWD" run reactome-db-mcp
```
**Codex**
```bash
codex mcp add reactome-db -- uv --directory "$PWD" run reactome-db-mcp
```
**Cursor / any other MCP client** — point it at the same stdio command; a ready-to-edit example lives in [`.mcp.json`](.mcp.json):
```json
{
"mcpServers": {
"reactome-db": {
"type": "stdio",
"command": "uv",
"args": ["run", "--directory", "/absolute/path/to/reactome-db-mcp", "reactome-db-mcp"],
"env": { "REACTOME_DB_NAME": "reactome_local" }
}
}
}
```
---
## Tools
All tools are async and return JSON-shaped dicts, degrading to `{"error": ...}` rather than raising on bad input or a down database.
### Schema discovery
| Tool | Signature | Purpose |
|---|---|---|
| `schema_overview` | `()` | Primer on the class-table-inheritance model + key joins + data release. **Read first.** Also the `schema://reactome` resource. |
| `list_tables` | `(filter=None, limit=500)` | Tables with exact row counts + engine; `filter` is a name substring (e.g. `_2_`). |
| `describe_table` | `(table)` | Columns of a table: type, nullability, key, default. |
### Guarded raw SQL
| Tool | Signature | Purpose |
|---|---|---|
| `query` | `(sql, max_rows=200)` | Run **one** read-only statement (`SELECT`/`WITH`/`SHOW`/`DESCRIBE`/`EXPLAIN`). Auto-`LIMIT`, per-query timeout, result/cell truncation. |
### Ergonomic helpers (sugar over the common joins)
| Tool | Signature | Purpose |
|---|---|---|
| `get_object` | `(id)` | Assemble a full object (stable id **or** DB_ID) across its inheritance chain. |
| `search_by_name` | `(query, classes=None, limit=25, anchored=False)` | `_displayName` search; `anchored=True` is fast, wildcard is a ~1-2s scan. |
| `get_pathway_events` | `(id)` | Ordered child events of a pathway (via `Pathway_2_hasEvent`). |
| `get_participants` | `(id)` | Inputs / outputs / catalysts of a reaction-like event. |
| `get_referrers` | `(id, attribute=None, limit=50)` | Reverse lookup — which objects point at this one (curated/best-effort). |
**Design notes**
- **Read-only by construction.** The connection is a `SELECT`-only user (writes fail at the server), PyMySQL rejects stacked statements by default, and `query()` additionally checks that the statement is a single read. The string check is for clear errors, not as the security boundary — the grant is.
- **Stable ids vs DB_IDs.** Helpers accept either `R-HSA-69278` or the numeric `DB_ID`. The `'R-HSA-...'` text lives in `StableIdentifier`, joined via `DatabaseObject.stableIdentifier`.
- **Size-guarded.** Every result is row-capped (`max_rows`, hard ceiling 5000) and long text cells are truncated; `truncated` flags either.
- **Point-in-time data.** This is a dump (release reported by `schema_overview`). For the always-current Reactome, use the sibling `reactome-mcp`.
### Configuration (env vars)
| Var | Default | Meaning |
|---|---|---|
| `REACTOME_DB_HOST` / `REACTOME_DB_PORT` | `127.0.0.1` / `3306` | MySQL endpoint |
| `REACTOME_DB_USER` / `REACTOME_DB_PASSWORD` | `ro_user` / `` (empty) | credentials |
| `REACTOME_DB_NAME` | `reactome_local` | database |
| `REACTOME_DB_POOL_SIZE` | `4` | max pooled connections |
| `REACTOME_DB_MAX_ROWS` | `200` | default row cap |
| `REACTOME_DB_STMT_TIMEOUT_MS` | `15000` | per-query timeout |
| `REACTOME_DB_MAX_CELL_CHARS` | `2000` | per-cell text cap |
---
## Example prompts
1. **Lookup + drill-down** — *"Look up R-HSA-69278 with the reactome-db tools and list its child events in order."*
2. **Raw SQL** — *"Run `SELECT _class, COUNT(*) FROM DatabaseObject GROUP BY _class ORDER BY 2 DESC LIMIT 10` and summarize."*
3. **Reverse navigation** — *"Which pathways and reactions reference the object named 'CYCB:CDK1'? Use get_referrers."*
4. **Schema spelunking** — *"Call schema_overview, then describe the Pathway and Pathway_2_hasEvent tables."*
---
## Development
```bash
uv sync --extra dev # pytest, pytest-asyncio
uv run pytest # safety-layer suite runs fully offline (no DB)
# opt-in live-DB integration tests (needs reactome_local up):
REACTOME_DB_MCP_INTEGRATION=1 uv run pytest
```
Smoke-test the live server:
```bash
uv run reactome-db-mcp # console script
uv run python -m reactome_db_mcp # module entry point
uv run python server.py # source-checkout shim
uv run mcp dev src/reactome_db_mcp/server.py # MCP Inspector dev UI
```
```
reactome-db-mcp/
├── src/reactome_db_mcp/
│ ├── server.py # FastMCP app, lifespan pool, all tools + resource
│ ├── db.py # PyMySQL pool + read-only safety layer (pure, tested)
│ └── schema.py # curated schema primer + class-inheritance map
├── server.py # source-checkout compatibility shim
├── tests/ # offline safety-layer suite + opt-in DB integration tests
├── pyproject.toml # uv-managed project
├── .mcp.json # example stdio MCP config
└── PLAN.md # design & build record (verified 2026-06-22)
```
---
## Acknowledgements
Powered by [Reactome](https://reactome.org), a free, open-source, open-access, curated and peer-reviewed pathway database. Please cite Reactome when publishing work that uses this data — see <https://reactome.org/cite>.
This project is not affiliated with or endorsed by the Reactome team.
## License
[MIT](LICENSE) © Tien Comlekoglu
TDQS
Scored across 9 tools
Each tool has a distinct purpose: schema exploration (list_tables, describe_table, schema_overview), object retrieval (get_object, search_by_name), relationship queries (get_participants, get_pathway_events, get_referrers), and raw SQL (query). No overlapping functionality.
Most tools follow a consistent verb_noun pattern (e.g., describe_table, get_object, list_tables). However, 'query' and 'schema_overview' are outliers—'query' is a bare verb and 'schema_overview' is noun_noun—but the overall pattern is mostly adhered to.
With 9 tools, the server is well-scoped for a biological database MCP server. It provides essential operations without bloat, covering schema exploration, object retrieval, relationship navigation, and custom queries.
The tool set covers the primary interactions with Reactome: exploring schema, fetching objects, searching by name, retrieving participants and pathway events, reverse lookups, and arbitrary SQL queries. No obvious gaps for typical workflows.