MCP server: One MCP server.

ensembl-mcp-server

@cyanheadsMCP server · External

Look up genes, sequences, variants, homologs, and cross-database xrefs from Ensembl REST.

Biology & genomicsv0.6.2Apache-2.0TypeScript
Freeno charge per call

One address for any MCP client. No sign-in needed on this server.

What it does

6 read1 other

Grouped by tool name. At most: only reads or looks up information.

What it costs

Free

Odel charges nothing per call. The publisher may charge for its own service.

Tools

7 · grouped by tool name · checked 25 Oct 2026

Read · 6Look things up

  • ensembl_list_species
  • ensembl_lookup_gene
  • ensembl_get_sequence
  • ensembl_query_region
  • ensembl_get_homology
  • ensembl_get_xrefs

Other · 1Neither hinted nor named

  • ensembl_predict_variant
Try it in the Inspector
README12 min read

@cyanheads/ensembl-mcp-server

Look up genes, fetch sequences, predict variant consequences, find orthologs, and retrieve cross-database xrefs from Ensembl REST via MCP. STDIO or Streamable HTTP.

7 Tools • 4 Resources • 1 Prompt

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://ensembl.caseyjhand.com/mcp


Overview

Gene, sequence, and variant data for vertebrates and other model organisms from the Ensembl REST API. Look up genes, fetch sequences, predict variant consequences, find orthologs, and cross-reference external databases.

Tools

ToolDescription
ensembl_list_speciesList species supported by Ensembl with display name, common name, assembly, taxon ID, and division
ensembl_lookup_geneResolve a gene by symbol (synonyms and former symbols included) + species or by stable ID to its Ensembl ID, genomic location, biotype, and transcript list
ensembl_get_sequenceFetch the DNA, cDNA, CDS, or protein sequence for a gene, transcript, protein, or genomic region
ensembl_query_regionFind genomic features (genes, transcripts, variants, regulatory elements, exons) overlapping a chromosomal region
ensembl_predict_variantPredict functional consequences of a sequence variant using the Ensembl Variant Effect Predictor (VEP)
ensembl_get_homologyFind orthologs and/or paralogs of a gene across species with percent identity and taxonomy level
ensembl_get_xrefsRetrieve cross-database references for a gene — HGNC, UniProt, EntrezGene, OMIM, RefSeq, Reactome, and others

Resources

ResourceDescription
ensembl://gene/{id}Gene record by stable ID (ENSG…) — location, biotype, description, and transcript list
ensembl://transcript/{id}Transcript record by stable ID (ENST…) — parent gene, location, biotype, canonical flag, and length
ensembl://speciesSupported Ensembl species for the endpoint default division (vertebrates on the default endpoint)
ensembl://species/{division}Supported species in one division (EnsemblVertebrates, EnsemblPlants, EnsemblFungi, EnsemblMetazoa, EnsemblProtists)

The species resources return the first 50 species by internal name; the ensembl_list_species tool pages through the rest and filters by name.

Prompts

PromptDescription
ensembl_gene_dossierStructured workflow for assembling a complete gene profile: symbol → ID + location → sequence → variants → orthologs → xrefs

Capability reference

Ensembl's stable-ID endpoints answer a versioned ID (ENSG00000139618.19) as not found, so every tool and resource that takes an Ensembl stable ID drops a trailing .N from an ENS… ID before the request and returns the current version. HGVS input to ensembl_predict_variant (ENST00000380152.8:c.2T>A) keeps its version, and IDs from other sources (AT3G52430.1) are forwarded unchanged.

ensembl_list_species tool

  • Optional division (EnsemblVertebrates, EnsemblPlants, EnsemblFungi, EnsemblMetazoa, EnsemblProtists; omitted, the endpoint default — vertebrates, ~356 species on GRCh38) and nameContains, a substring match against name, display name, and common name
  • Returns the internal name every other tool expects (homo_sapiens), plus display name, common name, taxon ID, assembly, and division
  • Sorted by internal name and paged by offset (default 0) and max_results (default 50, 0 uncapped); totalCount reports every match, and nextOffset is where the next page starts when more follow

ensembl_lookup_gene tool

  • Exactly one of symbol (+ species, default homo_sapiens), id, ids, or symbols (up to 1,000 submitted items; first 20 distinct trimmed queries processed); expand_transcripts (default false) adds the transcript list with biotype and canonical flag
  • canonicalTranscript identifies the canonical transcript without expansion; transcript-ID lookups also return parentGeneId. Expanded transcripts include maneSelect RefSeq accessions when available (BRCA2: ENST00000380152.8, NM_000059.4)
  • A symbol that is not a current gene name falls back to synonyms and former symbols (HER2 → ERBB2), reported as matchedAlias; a name shared by several genes (MDR1) returns ambiguous with candidates to retry by id
  • A single lookup returns gene (or ambiguous); a batch returns ordered succeeded items carrying the submitted query, failed items with query, reason (not_found, ambiguous with candidates, or unavailable), error, and recovery, and omitted queries past the processing cap. Omitted queries are never requested or reported as failed; resubmit them separately. Errors: not_found, invalid_species, no_input, conflicting_input

ensembl_get_sequence tool

  • A stable ID (ENSG…/ENST…/ENSP…) or a region (species:chr:start-end, or chr:start-end with species; at most 10,000,000 bases); type is genomic (default), cdna, cds, or protein — cdna and cds from a transcript ID only, protein from a transcript or protein ID; expand_5prime/expand_3prime add flanking bases
  • Regions take a :1/:-1 strand suffix (1:1000000-1000020:-1 returns the reverse complement); the mitochondrion is MT in vertebrates (mitochondrion_genome in Drosophila, MtDNA in C. elegans), and M/chrM are rejected as invalid_region where they would not resolve to it
  • Returns a window set by offset (default 0) and max_length (default 10000, 0 uncapped); length is the full sequence length, and truncated/nextOffset say where to resume
  • Errors: not_found, type_mismatch (also a protein ID asked for genomic/cdna/cds, or a non-coding transcript asked for protein/cds), invalid_species, missing_species, invalid_region (also an id with a colon that is not a valid region)

ensembl_query_region tool

  • region as species:chr:start-end, or chr:start-end with species (at most 5,000,000 bases; reversed coordinates are rejected; mitochondrion naming as in ensembl_get_sequence); feature defaults to ["gene"] and also accepts transcript, variation, regulatory, exon; optional case-sensitive biotype filter (protein_coding, lncRNA). An empty filtered result suggests removing the filter without inferring the locus is intergenic
  • max_results caps the list (default 100, 0 uncapped) while totalCount reports the true count; assemblyName names the coordinates' assembly, and exon rows carry parentId and rank. Errors: invalid_region, invalid_species, missing_species

ensembl_predict_variant tool

  • variant as HGVS (transcript-relative or genomic), region+allele (chr:start:end:strand/allele), or a dbSNP rsID
  • Returns the most severe consequence, per-transcript impact (HIGH/MODERATE/LOW/MODIFIER), and colocated known variants with clinical significance. Errors: invalid_notation, not_found, invalid_species
  • Requests HGVS, canonical and MANE annotations on every input route. Consequences retain variantAllele, optional canonical/maneSelect, HGVS and available SIFT/PolyPhen prediction-score pairs; records retain alleleString
  • Colocated clinicalSignificance is aggregate, not a classification of the submitted allele. clinicalSignificanceByAllele preserves explicit per-allele terms and clinicalSignificanceReferenceAllele identifies their reference allele; absent annotations remain omitted
  • max_transcript_consequences and max_pubmed_ids_per_variant (default 10 each, 0 uncapped; include_all_colocated_pubmed lifts the PubMed cap) bound the result, while transcriptConsequencesTotal and pubmedTotal report the full counts

ensembl_get_homology tool

  • Exactly one of symbol (+ species, default homo_sapiens) or id; type is orthologues (default), paralogues, or all; optional target_species filter
  • max_results caps the list (default 25, 0 uncapped) while totalCount reports the true count. Errors: not_found, invalid_species (unknown species or target_species), no_input, conflicting_input
  • Each row retains optional targetProteinId for ensembl_get_sequence with type:"protein". Optional targetSymbol comes from a lookup of the returned rows' distinct gene IDs, in chunks of at most 1,000, with one five-second budget bounded by the call's remaining deadline and no retries. Missing names or failed lookups retain every primary homolog with a notice; cancellation still aborts

ensembl_get_xrefs tool

  • id (ENSG…/ENST…/ENSP…) required; optional dbname filter (e.g. HGNC, Uniprot_gn, EntrezGene, MIM_GENE, RefSeq_mRNA, Reactome_gene for gene pathways, Reactome for translation pathways, GO)
  • Default all_levels:false queries the requested object's level. Set all_levels:true to include a gene's transcript and translation references, independently of dbname; for example, gene-level RefSeq_mRNA may need this or a transcript ID. Empty lists remain successful with totalCount:0. Errors: not_found

ensembl://gene/{id} resource

  • id is a gene stable ID (ENSG…); a version suffix is optional and dropped
  • Returns location, biotype, description, and transcript list. Errors: not_found

ensembl://transcript/{id} resource

  • id is a transcript stable ID (ENST…); a version suffix is optional and dropped
  • Returns parent gene, location, biotype, canonical flag, and length. Errors: not_found

ensembl://species resource

  • No parameters — covers the endpoint default division (vertebrates, ~356 species on the default GRCh38 endpoint)
  • Returns the first 50 species by internal name with totalCount, shown, and truncated; a truncated body's notice points to ensembl_list_species (offset, max_results) for the rest
  • For a named division, read ensembl://species/{division} instead

ensembl://species/{division} resource

  • division required: EnsemblVertebrates, EnsemblPlants, EnsemblFungi, EnsemblMetazoa, or EnsemblProtists
  • Returns that division's first 50 species by internal name — internal name, display name, assembly, taxon ID, and division — with totalCount, shown, and truncated; a truncated body's notice points to ensembl_list_species (division, offset, max_results) for the rest

ensembl_gene_dossier prompt

  • Arguments: gene_symbol required; species optional (default homo_sapiens)
  • Sequences a 7-step workflow: resolve the gene → fetch the protein sequence → find variants in the locus → predict variant consequences → find cross-species orthologs → get external database IDs → synthesize the dossier

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

Ensembl-specific:

  • Keyless REST API — no API key required; Ensembl REST is fully public at 55,000 req/hr
  • Retries 429 responses (honoring Retry-After) and transient 5xx errors
  • Upstream calls time out at 180 s per attempt and 240 s per call, so a stalled Ensembl fails instead of hanging
  • Batch lookups use POST /lookup/id and POST /lookup/symbol/{species} to avoid N+1 round trips
  • GRCh37 legacy support via ENSEMBL_BASE_URL — point the entire server at https://grch37.rest.ensembl.org for clinical workflows on the older assembly
  • All coordinate-bearing responses echo the assembly name so agents never see a bare genomic position without assembly context

Agent-friendly output:

  • ensembl_get_sequence returns bounded windows (10,000 characters by default) with the full length and a nextOffset to continue
  • ensembl_list_species is the discovery step for internal species names, 50 per page with a nextOffset
  • Xref IDs from ensembl_get_xrefs are described as inputs for protein and literature servers; the ensembl_gene_dossier prompt chains 6 of the 7 tools

Getting started

Public Hosted Instance

A public instance is available at https://ensembl.caseyjhand.com/mcp — no installation required. It serves Streamable HTTP; each client takes its own config shape.

For Claude Code:

sh
claude mcp add --transport http ensembl-mcp-server https://ensembl.caseyjhand.com/mcp

For Cline (cline_mcp_settings.json):

json
{
  "mcpServers": {
    "ensembl-mcp-server": {
      "type": "streamableHttp",
      "url": "https://ensembl.caseyjhand.com/mcp"
    }
  }
}

For Cursor (mcp.json):

json
{
  "mcpServers": {
    "ensembl-mcp-server": {
      "url": "https://ensembl.caseyjhand.com/mcp"
    }
  }
}

Claude Desktop adds remote servers under Settings → Connectors.

Self-Hosted / Local

Add the following to your MCP client configuration file.

json
{
  "mcpServers": {
    "ensembl-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/ensembl-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

json
{
  "mcpServers": {
    "ensembl-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/ensembl-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

json
{
  "mcpServers": {
    "ensembl-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_TRANSPORT_TYPE=stdio",
        "ghcr.io/cyanheads/ensembl-mcp-server:latest"
      ]
    }
  }
}

For Streamable HTTP, set the transport and start the server:

sh
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key required — Ensembl REST is fully public.

Installation

  1. Clone the repository:
sh
git clone https://github.com/cyanheads/ensembl-mcp-server.git
  1. Navigate into the directory:
sh
cd ensembl-mcp-server
  1. Install dependencies:
sh
bun install
  1. Configure environment:
sh
cp .env.example .env
# edit .env if you need to override ENSEMBL_BASE_URL (e.g. for GRCh37)

Configuration

All configuration is validated at startup via Zod schemas in src/config/server-config.ts.

VariableDescriptionDefault
ENSEMBL_BASE_URLEnsembl REST API base URL. Override for GRCh37 (https://grch37.rest.ensembl.org) or a local mirror.https://rest.ensembl.org
MCP_TRANSPORT_TYPETransport: stdio or httpstdio
MCP_HTTP_PORTHTTP server port3010
MCP_HTTP_ENDPOINT_PATHHTTP endpoint path/mcp
MCP_SESSION_MODEHTTP session mode: auto, stateful, or statelessstateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauthnone
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.)info
LOGS_DIRDirectory for log files (Node.js only)<project-root>/logs
LOG_TOOL_FAILURE_PAYLOADSLog each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted.false
OTEL_ENABLEDEnable OpenTelemetryfalse

See .env.example for the full list of optional overrides.

Running the server

Local development

  • Build and run:

    sh
    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
    
  • Run checks and tests:

    sh
    bun run devcheck   # Lint, format, typecheck, security
    bun run test       # Vitest test suite
    bun run lint:mcp   # Validate MCP definitions against spec
    

Docker

sh
docker build -t ensembl-mcp-server .
docker run --rm -p 3010:3010 ensembl-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/ensembl-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers tools/resources/prompts and inits services
src/configServer-specific environment variable parsing and validation with Zod
src/mcp-server/toolsTool definitions (*.tool.ts) — 7 tools
src/mcp-server/resourcesResource definitions (*.resource.ts) — gene, transcript, species
src/mcp-server/promptsPrompt definitions (*.prompt.ts) — gene dossier workflow
src/services/ensemblEnsembl REST API client — HTTP, rate-limit handling, retry, error normalization
tests/Unit and integration tests mirroring src/

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools and resources in the createApp() arrays in src/index.ts
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.

Similar modules

Browse Biology & genomics