MCP server: One MCP server.

Protein

@cyanheadsMCP server · External

MCP Server for 3D protein structural data retrieval & analysis from RCSB PDB, PDBe, and UniProt.

Biology & genomicsv1.0.3Apache-2.0TypeScript
Freeno charge per call

Runs on your machine from its package; the steps are below.

What it does

  1. Reads
  2. Writes
  3. High impact

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.

Runs locally over stdio

This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).

npmprotein-mcp-serverv1.0.3

Run

npx -y protein-mcp-server

MCP client config

{
  "mcpServers": {
    "protein": {
      "command": "npx",
      "args": [
        "-y",
        "protein-mcp-server"
      ]
    }
  }
}
npmprotein-mcp-serverv1.0.3

Run

npx -y protein-mcp-server

MCP client config

{
  "mcpServers": {
    "protein": {
      "command": "npx",
      "args": [
        "-y",
        "protein-mcp-server"
      ]
    }
  }
}
README12 min read

@cyanheads/protein-mcp-server

Federated protein structure & annotation across experimental (PDB) and predicted (AlphaFold) models via MCP. STDIO or Streamable HTTP.

7 Tools • 2 Resources

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

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


Overview

Experimental (PDB) and predicted (AlphaFold) protein structures behind one surface. Search, fetch, align, and annotate structures and their ligands across RCSB, AlphaFold DB, 3D-Beacons, UniProt, InterPro, and Foldseek. No API key required.

Tools

ToolDescription
protein_search_structuresSearch experimental and predicted structures by text, sequence, or organism/method/resolution, with optional facets
protein_get_structureFetch metadata and coordinate URLs for experimental, predicted, or best-available structures
protein_find_similarFind sequence homologs (RCSB mmseqs2) or fold homologs (Foldseek)
protein_track_ligandsResolve ligands to component IDs, find structures containing one, or map its binding site
protein_compare_structuresAlign structures with TM-align or jFATCAT, against a reference or all pairs
protein_analyze_collectionProfile the PDB as counts, histograms, timelines, and cross-tabs
protein_get_annotationsFetch UniProt features and variants plus InterPro domains with GO terms

Resources

ResourceDescription
pdb://{entry_id}Experimental structure summary for a PDB entry
af://{uniprot}AlphaFold DB predicted-structure summary for a UniProt accession

Both resources are also reachable through protein_get_structure, for clients that don't surface resources.

Capability reference

protein_search_structures tool

  • At least one of query, sequence (an mmseqs2 search; min_identity / max_evalue apply only with it), organism, method, or max_resolution; content_type is experimental, predicted, or all (default); up to 100 hits per page (default 25) via limit / start
  • A sequence needs at least 10 residues; whitespace is stripped and case normalized, and an invalid one fails as invalid_sequence. Each hit names its source and an entry id you can pass to other tools
  • Optional facets (method, organism, polymer_type, resolution, release_year, molecular_weight) add a breakdown capped at PROTEIN_FACET_BUCKET_CAP buckets per dimension

protein_get_structure tool

  • source: experimental (default) takes PDB entry IDs and the AF_* / MA_* computed-model IDs search returns (these come back as source: predicted); predicted takes UniProt accessions for AlphaFold models with pLDDT/PAE; best_available takes UniProt accessions and returns the highest-resolution experimental structure, else the best prediction. Up to PROTEIN_MAX_BATCH_IDS IDs per call (default 25)
  • Unresolved IDs land in failed[], and requested / processed show IDs dropped past the cap; the call fails as all_failed when nothing resolves and mixed_id_types when the IDs don't fit source. Records include coordinateUrls for the files that exist
  • include_coords inlines coordinate text within a size budget; an over-budget batch returns an overflow outline to re-call with sections: [ids]

protein_find_similar tool

  • by: sequence runs a synchronous RCSB mmseqs2 search from sequence, pdb_id, or uniprot, filtered by max_evalue / min_identity; by: structure runs an async Foldseek search from pdb_id or uniprot against pdb100 + afdb50 unless databases says otherwise. A field only the other mode reads fails as mode_mismatched_field; up to 100 hits per page (default 25)
  • Sequences follow the same rules as protein_search_structures, and a direct sequence wins over identifiers. Hits name their source (and database for structure hits), ranked by score; responses report totalCount and nextStart
  • A structure job still running at the poll budget returns status: computing with a ticketId; re-call with ticket_id to resume it. A multichain structure is searched one chain at a time via query (0-based, default 0) out of queryCount

protein_track_ligands tool

  • mode is find_ligand (query: a name or formula), structures_with_ligand (comp_id), or binding_site (pdb_id, optional comp_id); a missing mode input fails as missing_param. limit is 1–100 (default 25), and start pages the last two modes
  • find_ligand returns component IDs with formula, weight, SMILES, and InChIKey, ranked by depositionCount, with totalCount and candidatesConsidered showing when the candidate pool was cut short; structures_with_ligand lists entries highest resolution first
  • binding_site returns pocket residues with contact distances, numbered in both label (asymId, seqId) and author (authAsymId, authSeqId) namespaces; it works on experimental structures only

protein_compare_structures tool

  • 2 structures up to PROTEIN_MAX_COMPARE_STRUCTURES (default 10, max 25), each a pdb_id with an optional label-namespace chain; method is tm-align (default), fatcat-rigid, or fatcat-flexible; reference is first (default) or all_pairs; timeout_s (5–120) sets the per-pair poll budget
  • Each pair row has a status (complete, computing, failed), tmScore, rmsd, alignedResidues, modeledResidues, and coverage; TM-score is normalized by a's length, so reversing a pair changes it. similarityScore and sequenceSimilarity appear when the upstream supplies them
  • A computing pair returns its job uuid; re-call with { a, b, uuid } in resume[] to poll it instead of resubmitting. A mismatched method or pair fails as resume_method_mismatch or resume_job_mismatch

protein_analyze_collection tool

  • group_by takes one of method, organism, polymer_type, resolution, release_year, molecular_weight, or two distinct ones for a cross-tab; scope with query, organism, method, max_resolution, and content_type (default experimental). interval sets a numeric bin width for resolution / molecular_weight or "year" for release_year
  • Returns total and bucketsReturned; buckets carry label and count (numeric bins add rangeFrom / rangeTo), and every dimension reports missingValueCount
  • bucket_limit (1–500, default PROTEIN_FACET_BUCKET_CAP) caps each dimension separately; truncated and notice say where results were cut

protein_get_annotations tool

  • A uniprot accession, or a pdb_id resolved through its UniProt cross-reference (author chain picks one accession); include is features, domains, variants, or all (default); limit pages each class separately (1–200, default 50), with independent zero-based features_offset, variants_offset, and domains_offset (default 0)
  • Returns UniProt features and natural variants plus InterPro domains with GO terms; a PDB entry mapping to several accessions uses the lowest author chain and lists the rest in ambiguity
  • Each requested class carries featuresTotal, variantsTotal, or domainsTotal; a page with more records sets truncated and names the next offset in notice
  • Failures are typed as missing_identifier, invalid_accession, no_uniprot_mapping, or chain_not_found

pdb://{entry_id} resource

  • entry_id is a PDB entry ID (e.g. 4HHB); returns application/json with title, methods, resolution, organisms, ligands, and polymerEntities carrying both authAsymIds and labelAsymIds
  • Same data as protein_get_structure with source: experimental

af://{uniprot} resource

  • uniprot is a UniProt accession or an AlphaFold DB entry ID (e.g. AF-P69905-F1); returns application/json with meanPlddt, confidenceBuckets, cifUrl / pdbUrl / bcifUrl, and modelVersion
  • Same data as protein_get_structure with source: predicted

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.

PDB / AlphaFold-specific:

  • Search, fetch, and comparison treat experimental (PDB) and predicted (AlphaFold DB, ModelArchive, 3D-Beacons providers) structures the same way
  • Keyless across RCSB, AlphaFold DB, 3D-Beacons, UniProt, InterPro, and Foldseek
  • Collection analytics run on RCSB's facet engine and return bucket counts, not matching entries
  • Foldseek and alignment jobs poll for up to PROTEIN_ASYNC_POLL_TIMEOUT_MS (default 30 s), then hand back a resume handle (ticketId, per-pair uuid) instead of blocking
  • Chain namespaces stay separate: authAsymIds feed protein_get_annotations chain, labelAsymIds feed protein_compare_structures chain

Agent-friendly output:

  • Provenance: every hit names its source (experimental / predicted) and the engine or database behind it, and protein_get_structure and protein_get_annotations carry an attribution block (see Upstream data licensing)
  • Graceful partial failure: failed[] rows and per-pair status keep one bad ID or pair from failing the request
  • Paging and advisories: paged tools return totalCount and nextStart, a start past the end is flagged in notice separately from zero matches, and every advisory joins into that one notice

Getting started

Public Hosted Instance

A public instance is available at https://protein.caseyjhand.com/mcp, no installation required. Point any MCP client at it via Streamable HTTP:

json
{
  "mcpServers": {
    "protein-mcp-server": {
      "type": "streamable-http",
      "url": "https://protein.caseyjhand.com/mcp"
    }
  }
}

Self-Hosted / Local

Add the following to your MCP client configuration file. No API key is needed.

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

Or with npx (no Bun required):

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

Or with Docker:

json
{
  "mcpServers": {
    "protein-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/protein-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 accounts or API keys: every upstream is public.

Installation

  1. Clone the repository:
sh
git clone https://github.com/cyanheads/protein-mcp-server.git
  1. Navigate into the directory:
sh
cd protein-mcp-server
  1. Install dependencies:
sh
bun install
  1. Configure environment (optional):
sh
cp .env.example .env
# every variable has a working default

Configuration

Every variable is optional.

VariableDescriptionDefault
PROTEIN_ASYNC_POLL_TIMEOUT_MSPoll budget for an async alignment or Foldseek job before it returns computing (min 1000).30000
PROTEIN_MAX_BATCH_IDSMax IDs per protein_get_structure call (1–100).25
PROTEIN_MAX_COMPARE_STRUCTURESMax structures per protein_compare_structures call (2–25).10
PROTEIN_FACET_BUCKET_CAPBuckets per dimension for protein_search_structures facets, and the protein_analyze_collection default (1–500).50
PROTEIN_FANOUT_CONCURRENCYMax concurrent upstream requests for per-ID and per-pair fan-out (1–16).5
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto. Overrides the stateless mode the server declares in code.stateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (RFC 5424).info
OTEL_ENABLEDEnable OpenTelemetry.false

Each upstream's base URL can also be pointed at a mirror: RCSB_SEARCH_BASE_URL, RCSB_DATA_BASE_URL, RCSB_FILES_BASE_URL, RCSB_MODELS_BASE_URL, RCSB_ALIGNMENT_BASE_URL, BEACONS_BASE_URL, ALPHAFOLD_BASE_URL, MODELARCHIVE_BASE_URL, FOLDSEEK_BASE_URL, UNIPROT_BASE_URL, INTERPRO_BASE_URL. See .env.example for their defaults and 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 protein-mcp-server .
docker run --rm -p 3010:3010 protein-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/protein-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 and resources, inits the provider services.
src/configServer-specific environment variable parsing and validation with Zod.
src/mcp-server/toolsTool definitions (*.tool.ts) and shared schemas (_schemas.ts).
src/mcp-server/resourcesResource definitions (*.resource.ts).
src/servicesUpstream clients: rcsb/ (search, data, facets), alphafold/, beacons/ (3D-Beacons), uniprot/ (UniProt, InterPro, GO), alignment/ (RCSB Structural Comparison), foldseek/, and shared/ HTTP, concurrency, identifier, and attribution helpers.
tests/Unit and integration tests mirroring src/.

Development guide

See CLAUDE.md/AGENTS.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
  • Export new definitions from the barrels in src/mcp-server/*/definitions/index.ts and register them 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

Upstream data licensing

Structure and annotation data comes from public databases, each under its own license. protein_get_structure and protein_get_annotations return an attribution block with the license, citation, and homepage of every source that contributed to that response. CC BY and CC BY-SA sources require attribution on redistribution; CC0 sources ask only for citation.

SourceContributes toLicense
RCSB PDBprotein_get_structure — experimental recordsCC0 1.0 Universal
AlphaFold DBprotein_get_structure — predicted modelsCC BY 4.0
ModelArchiveprotein_get_structure — MA_* computed modelsCC BY 4.0
SWISS-MODELprotein_get_structure — best_available modelsCC BY-SA 4.0
BFVDprotein_get_structure — best_available modelsCC BY 4.0
UniProtprotein_get_annotationsCC BY 4.0
InterProprotein_get_annotations — domain/family dataCC0 1.0 Universal
GOprotein_get_annotations — GO termsCC BY 4.0

best_available models arrive through 3D-Beacons, so the block credits the provider that actually supplied the model; a provider with no curated entry gets a See provider terms fallback instead of a guessed license. InterPro and GO are credited separately, each only when it contributes. This table covers upstream data; the server's own code is licensed separately (see License).

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