Ensembl MCP Server README

The full upstream README, mirrored for reference. Install config, tool schemas, adoption signals and our own overview live on the Ensembl MCP Server listing page.

@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
  • A target_species outside the comparison Ensembl runs for the source species (a fly gene to homo_sapiens) is answered from Ensembl's pan-taxonomic comparison when it holds both species, with a notice saying so
  • 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), target_not_compared (a target outside the source species' comparison that the pan-taxonomic comparison cannot answer), 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, Uniprot/SWISSPROT for the reviewed UniProt accession on the translation, 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
  • Each reference carries how Ensembl assigned it (infoType: DIRECT, DEPENDENT, PROJECTION, …; infoText: its source, such as UniProt or the ortholog a projection came from) and its evidence codes (linkageTypes, GO evidence such as IDA or IEA) when supplied, so GO and Projected GO rows, and experimental and electronic annotations, stay distinct on both surfaces

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 model-organism 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, disease, pathway, and structure servers; the ensembl_gene_dossier prompt chains 6 of the 7 tools

Getting started

Connect to the hosted instance, no install:

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

Or run it locally:

Terminal
claude mcp add ensembl-mcp-server -- npx -y @cyanheads/ensembl-mcp-server

For other clients, add this to the MCP config file:

config.json
{
  "mcpServers": {
    "ensembl-mcp-server": {
      "command": "npx",
      "args": ["-y", "@cyanheads/ensembl-mcp-server"]
    }
  }
}

Ensembl REST is public, so no API key is needed. Set ENSEMBL_BASE_URL to https://grch37.rest.ensembl.org to query the GRCh37 assembly instead of GRCh38. For Docker or a source build, see Running the server.

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

Requires Bun v1.4.0 or higher (or Node.js v24+).

  • Clone and install:

    sh
    git clone https://github.com/cyanheads/ensembl-mcp-server.git
    cd ensembl-mcp-server && bun install
    cp .env.example .env   # optional: set ENSEMBL_BASE_URL for GRCh37 or a mirror
    
  • 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

Terminal
docker run --rm -p 3010:3010 ghcr.io/cyanheads/ensembl-mcp-server:latest

The image defaults to HTTP transport, stateless session mode, and logs to /var/log/ensembl-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/ensembl-mcp-server:latest"]. To build locally, docker build -t 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.