An MCP server over the GTEx Portal v2 API, serving GTEx tissue expression — median and individual-sample TPM, top genes per tissue, gene and transcript annotation — to AI assistants over Streamable HTTP. It also serves a REST API from the same process.
Important
Research use only. Not clinical decision support. Do not use for diagnosis, treatment, triage, or patient management.
The GTEx Portal v2 API is public and needs no key, but it is shaped for the Portal's own
UI rather than for a model. /expression/medianGeneExpression requires versioned
GENCODE IDs (ENSG00000012048.23) — a gene symbol is not a valid argument — so every
question starts with a separate resolution call whose GENCODE version and genome build
must line up with the expression call that follows. Tissues are addressed by internal
tissueSiteDetailId codes, and "where is this gene expressed?" spans several paginated
endpoints that return wide, untyped JSON.
Worse, the release you query decides the annotation: gtex_v8 is GENCODE v26 and
gtex_v10 is GENCODE v39, so an ID resolved against the wrong one silently returns
nothing.
gtex-link answers that question in one call. Symbols are auto-resolved to GENCODE IDs
in the release backing the dataset you asked for; tissue expression comes back ranked
and compact by default; every result with a _meta frame (all but fetch and
get_server_capabilities) stamps provenance naming the release actually queried plus the
required citation, and the tools with an obvious next step add _meta.next_commands so a
model chains without guessing; and a token-bucket limiter keeps the whole fleet inside
GTEx's request budget.
Hosted — nothing to install, no data to build:
claude mcp add --transport http gtex-link https://gtex-link.genefoundry.org/mcpRun it yourself (Python 3.12+, uv). There is no data bundle and no ingest step — the server proxies the live GTEx Portal API:
uv sync --group dev
make dev # unified REST + MCP on 127.0.0.1:8000
curl http://127.0.0.1:8000/api/healthThe MCP endpoint is http://127.0.0.1:8000/mcp. Streamable HTTP is the only transport —
there is no stdio, and gtex-link serve --transport http is REST-only and serves no
/mcp; MCP clients need the default unified transport. See
deployment.md.
| Tool | Purpose |
|---|---|
search |
Natural-language gene search; returns result documents (id/title/url) |
fetch |
Full gene detail for an id returned by search |
search_genes |
Search the GTEx Portal gene catalog by symbol or partial match |
get_gene_information |
Gene detail for a GENCODE ID or symbol |
get_transcript_information |
Transcript annotations for a GENCODE ID |
get_median_expression_levels |
Median expression (TPM) per tissue |
get_individual_expression_data |
Individual-sample expression (TPM) |
get_top_expressed_genes_by_tissue |
Top expressed genes for a tissue |
get_server_capabilities |
Discover tools, datasets, tissues, limits, and workflows |
search / fetch are the OpenAI deep-research / Apps SDK contract and are retained
verbatim — a documented exception to the canonical-verb rule of the GeneFoundry
Tool-Naming Standard v1.
Namespace. Leaf tool names are unprefixed, per Tool-Naming Standard v1. Behind the
genefoundry-router gateway, which
mounts this server with mount(namespace="gtex"), they surface as gtex_<tool> —
get_gene_information becomes gtex_get_gene_information. A leaf-level gtex_ prefix
would double-prefix to gtex_gtex_…, so do not add one.
| Source | GTEx Portal v2 API — https://gtexportal.org/api/v2/, public, no authentication |
| Datasets | gtex_v8 (GENCODE v26, the default), gtex_v10 (v39), gtex_snrnaseq_pilot (v26) — pick one with the dataset_id argument on the expression tools |
| Provenance | On every tool that has a _meta frame, _meta.gtex_release names the release the data actually came from: it follows dataset_id, and a dataset-scoped call also reports the _meta.gencode_version its gene IDs were resolved against. Tools taking no dataset_id report the server default (gtex_v8); fetch and get_server_capabilities carry no _meta at all (data.md) |
| Refresh | None to run: no bundle, no mirror, no ingest. Calls proxy the live API behind a TTL cache, so freshness tracks the Portal |
| Rate limit | Token bucket, 5 req/s with burst 10 — upstream courtesy, not a local throttle. Do not raise it casually |
| Data licence | GTEx Portal terms. Only open-access GTEx data are reachable here; protected individual-level genotype data are dbGaP-controlled and are not exposed |
Required citation — returned verbatim in _meta.recommended_citation (on every tool that
carries a _meta frame) and at the gtex://citations resource:
GTEx Consortium. The GTEx Consortium atlas of genetic regulatory effects across human tissues. Science. 2020;369(6509):1318-1330. doi:10.1126/science.aaz1776
More: data.md.
- Data & provenance — dataset, refresh model, licence, citation, response modes, and error codes.
- Configuration — every
GTEX_LINK_*variable, the MCP tool profiles, and the Host/Origin request boundary. - Deployment — transports, the CLI, Docker and Compose overlays, the proxy boundary, and health checks.
- Architecture — the layering, the REST surface, and why REST and MCP are deliberately not symmetric.
- Conventions — code style, test data standards, and upstream pagination.
- GTEx Portal API reference — the upstream endpoint catalogue, generated from the OpenAPI spec.
- AGENTS.md — engineering conventions for humans and coding agents.
- SECURITY.md — the trust boundary and how to report a vulnerability.
- CHANGELOG.md — release history.
Read AGENTS.md first — it is the engineering guide. make ci-local is the
definition-of-done gate: format, lint, line budget, README standard, type check, and
tests. It must be green before handoff.
Code: MIT. Data: GTEx Portal data remain subject to the GTEx Portal's terms and carry the citation requirement above.