protein-mcp-server
蛋白質結構(PDB、UniProt)
文件
@cyanheads/protein-mcp-server
Federated protein structure & annotation across experimental (PDB) and predicted (AlphaFold) models via MCP. STDIO or Streamable HTTP.
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.
Capability reference
protein_search_structures tool
- At least one of
query,sequence(an mmseqs2 search;min_identity/max_evalueapply only with it),organism,method, ormax_resolution;content_typeisexperimental,predicted, orall(default); up to 100 hits per page (default 25) vialimit/start - Each hit names its
sourceand a chainable entryid(sequence hits addentityId); experimental hits carry title, method, resolution, and organism, AlphaFold models theiruniprotAccession - Optional
facets(method,organism,polymer_type,resolution,release_year,molecular_weight) add a flat breakdown capped atPROTEIN_FACET_BUCKET_CAPbuckets per dimension; for non-sequence searches,protein_analyze_collectionreaches the long tail
protein_get_structure tool
source: experimental(default) takes PDB entry IDs and theAF_*/MA_*computed-model IDs search returns (these come back assource: predicted);predictedtakes UniProt accessions for AlphaFold models with pLDDT/PAE;best_availabletakes UniProt accessions and returns the highest-resolution experimental structure, else the best prediction. Up toPROTEIN_MAX_BATCH_IDSIDs per call (default 25)- Unresolved IDs land in
failed[], andrequested/processedshow IDs dropped past the cap; the call fails asall_failedwhen nothing resolves andmixed_id_typeswhen the IDs don't fitsource. Experimental-source records addpolymerEntities,ligands,molecularWeight, andreleaseDate, andcoordinateUrlslists only files that exist include_coordsinlines coordinate text within a response budget; an over-budget batch returns anoverflowsize outline to re-call withsections: [ids], and a lone oversized file is withheld in favor of itscoordinateUrls
protein_find_similar tool
by: sequenceruns a synchronous RCSB mmseqs2 search fromsequence,pdb_id, oruniprot, filtered bymax_evalue/min_identity;by: structureruns an async Foldseek search frompdb_idoruniprotagainstpdb100+afdb50unlessdatabasessays otherwise. A field only the other mode reads fails asmode_mismatched_field; up to 100 hits per page (default 25)- Hits name their
sourceand, for structure hits, thedatabase, ranked byscoreacross all searched databases; responses reporttotalCountandnextStart - A structure job still running at the poll budget returns
status: computingwith aticketId; re-call withticket_idto resume it or re-page a finished job. A multichain structure is one query per chain: each response coversquery(0-based, default 0) out ofqueryCount
protein_track_ligands tool
modeisfind_ligand(query: a name or formula),structures_with_ligand(comp_id), orbinding_site(pdb_id, optionalcomp_id); a missing mode input fails asmissing_param.limitis 1–100 (default 25), andstartpages the last two modesfind_ligandreturns component IDs with formula, weight, SMILES, and InChIKey, ranked bydepositionCount, withtotalCountandcandidatesConsideredshowing when the candidate pool was cut short;structures_with_ligandlists entries highest resolution firstbinding_sitereturns 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 apdb_idwith an optional label-namespacechain;methodistm-align(default),fatcat-rigid, orfatcat-flexible;referenceisfirst(default) orall_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]-orderedmodeledResiduesandcoverage; TM-score is normalized bya's length, so reversing a pair changes it - A computing pair returns its job
uuid; re-call with{ a, b, uuid }inresume[]to poll it instead of resubmitting. A resume under a differentmethodor for another pair fails asresume_method_mismatchorresume_job_mismatch
protein_analyze_collection tool
group_bytakes one ofmethod,organism,polymer_type,resolution,release_year,molecular_weight, or two distinct ones for a cross-tab; scope withquery,organism,method,max_resolution, andcontent_type(defaultexperimental).intervalsets a numeric bin width forresolution/molecular_weightor"year"forrelease_year- Returns
totalandbucketsReturned; buckets carrylabelandcount(numeric bins addrangeFrom/rangeTo), and every dimension reportsmissingValueCount bucket_limit(1–500, defaultPROTEIN_FACET_BUCKET_CAP) caps each dimension level separately, so a cross-tab can returnbucket_limit × (1 + bucket_limit)buckets;truncatedandnoticename the capped positions
protein_get_annotations tool
- A
uniprotaccession, or apdb_idresolved through its UniProt cross-reference (authorchainpicks one accession);includeisfeatures,domains,variants, orall(default);limitcaps 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
truncatedand are named innotice; failures are typed asmissing_identifier,invalid_accession,no_uniprot_mapping, orchain_not_found
pdb://{entry_id} resource
entry_idis a PDB entry ID (e.g.4HHB); returnsapplication/jsonwith title, methods, resolution, organisms, ligands, andpolymerEntitiescarrying bothauthAsymIdsandlabelAsymIds- Same data as
protein_get_structurewithsource: experimental
af://{uniprot} resource
uniprotis a UniProt accession or an AlphaFold DB entry ID (e.g.AF-P69905-F1); returnsapplication/jsonwithmeanPlddt,confidenceBuckets,cifUrl/pdbUrl/bcifUrl, andmodelVersion- Same data as
protein_get_structurewithsource: 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-pairuuid) instead of blocking - Chain namespaces stay separate:
authAsymIdsfeedprotein_get_annotationschain,labelAsymIdsfeedprotein_compare_structureschain
Agent-friendly output:
- Provenance: every hit names its
source(experimental/predicted) and the engine or database behind it, andprotein_get_structureandprotein_get_annotationscarry anattributionblock (see Upstream data licensing) - Graceful partial failure:
failed[]rows and per-pairstatuskeep one bad ID or pair from failing the request - Paging and advisories: paged tools return
totalCountandnextStart, astartpast the end is flagged innoticeseparately from zero matches, and every advisory joins into that onenotice
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
- Clone the repository:
git clone https://github.com/cyanheads/protein-mcp-server.git
- Navigate into the directory:
cd protein-mcp-server
- Install dependencies:
bun install
- 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/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Export new definitions from the barrels in
src/mcp-server/*/definitions/index.tsand register them in thecreateApp()arrays insrc/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.