Skip to main content
Glama
cyanheads

protein-mcp-server

by cyanheads

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, all keyless. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.

Tools

Tool

Description

protein_search_structures

Search experimental and predicted structures by text, sequence, or organism/method/resolution, with optional facets

protein_get_structure

Fetch metadata and coordinate URLs for experimental, predicted, or best-available structures

protein_find_similar

Find sequence homologs (RCSB mmseqs2) or fold homologs (Foldseek)

protein_track_ligands

Resolve ligands to component IDs, find structures containing one, or map its binding site

protein_compare_structures

Align structures with TM-align or jFATCAT, against a reference or all pairs

protein_analyze_collection

Profile the PDB as counts, histograms, timelines, and cross-tabs

protein_get_annotations

Fetch UniProt features and variants plus InterPro domains with GO terms

Resources

Resource

Description

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.

Related MCP server: UniProt MCP Server

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

  • Each hit names its source and a chainable entry id (sequence hits add entityId); experimental hits carry title, method, resolution, and organism, AlphaFold models their uniprotAccession

  • Optional facets (method, organism, polymer_type, resolution, release_year, molecular_weight) add a flat breakdown capped at PROTEIN_FACET_BUCKET_CAP buckets per dimension; for non-sequence searches, protein_analyze_collection reaches the long tail


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. Experimental-source records add polymerEntities, ligands, molecularWeight, and releaseDate, and coordinateUrls lists only files that exist

  • include_coords inlines coordinate text within a response budget; an over-budget batch returns an overflow size outline to re-call with sections: [ids], and a lone oversized file is withheld in favor of its coordinateUrls


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)

  • Hits name their source and, for structure hits, the database, ranked by score across all searched databases; 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 or re-page a finished job. A multichain structure is one query per chain: each response covers 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, and [a, b]-ordered modeledResidues and coverage; TM-score is normalized by a's length, so reversing a pair changes it

  • A computing pair returns its job uuid; re-call with { a, b, uuid } in resume[] to poll it instead of resubmitting. A resume under a different method or for another 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 level separately, so a cross-tab can return bucket_limit × (1 + bucket_limit) buckets; truncated and notice name the capped positions


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 caps each class separately (1–200, default 50)

  • Returns UniProt features and natural variants plus InterPro domains with GO terms; a PDB entry mapping to several accessions defaults to the lowest author chain and lists the rest in ambiguity

  • Capped classes set truncated and are named 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:

{
  "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.

{
  "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):

{
  "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:

{
  "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:

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:

git clone https://github.com/cyanheads/protein-mcp-server.git
  1. Navigate into the directory:

cd protein-mcp-server
  1. Install dependencies:

bun install
  1. Configure environment (optional):

cp .env.example .env
# every variable has a working default

Configuration

Every variable is optional.

Variable

Description

Default

PROTEIN_ASYNC_POLL_TIMEOUT_MS

Poll budget for an async alignment or Foldseek job before it returns computing (min 1000).

30000

PROTEIN_MAX_BATCH_IDS

Max IDs per protein_get_structure call (1–100).

25

PROTEIN_MAX_COMPARE_STRUCTURES

Max structures per protein_compare_structures call (2–25).

10

PROTEIN_FACET_BUCKET_CAP

Buckets per dimension for protein_search_structures facets, and the protein_analyze_collection default (1–500).

50

PROTEIN_FANOUT_CONCURRENCY

Max concurrent upstream requests for per-ID and per-pair fan-out (1–16).

5

MCP_TRANSPORT_TYPE

Transport: stdio or http.

stdio

MCP_HTTP_PORT

HTTP server port.

3010

MCP_SESSION_MODE

HTTP session mode: stateless, stateful, or auto. Overrides the stateless mode the server declares in code.

stateless

MCP_AUTH_MODE

Authentication: none, jwt, or oauth.

none

MCP_LOG_LEVEL

Log level (RFC 5424).

info

OTEL_ENABLED

Enable 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:

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

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

Docker

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

Directory

Purpose

src/index.ts

createApp() entry point: registers tools and resources, inits the provider services.

src/config

Server-specific environment variable parsing and validation with Zod.

src/mcp-server/tools

Tool definitions (*.tool.ts) and shared schemas (_schemas.ts).

src/mcp-server/resources

Resource definitions (*.resource.ts).

src/services

Upstream 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.

Source

Contributes to

License

RCSB PDB

protein_get_structure — experimental records

CC0 1.0 Universal

AlphaFold DB

protein_get_structure — predicted models

CC BY 4.0

ModelArchive

protein_get_structure — MA_* computed models

CC BY 4.0

SWISS-MODEL

protein_get_structure — best_available models

CC BY-SA 4.0

BFVD

protein_get_structure — best_available models

CC BY 4.0

UniProt

protein_get_annotations

CC BY 4.0

InterPro

protein_get_annotations — domain/family data

CC0 1.0 Universal

GO

protein_get_annotations — GO terms

CC 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:

bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    A Model Context Protocol server that enhances language models with protein structure analysis capabilities, enabling detailed active site analysis and disease-related protein searches through established protein databases.
    2
    19
    -
  • F
    license
    A
    quality
    F
    maintenance
    A Model Context Protocol (MCP) server that provides access to the Protein Data Bank (PDB) - the worldwide repository of information about the 3D structures of proteins, nucleic acids, and complex assemblies.
    5
    27
    -