Verifiable, release-aware protein evidence workflows over UniProt and linked scientific sources.
This MCP server provides verifiable, release-aware protein evidence workflows over UniProt and linked scientific sources. It is positioned for bioinformatics and protein-focused workflows, covering protein sequences and related areas such as protein–protein interaction and protein structure/structure prediction.
🛠️ Key Features
Verifiable protein evidence workflows
Release-aware processing
UniProt integration
Access to linked scientific sources
🚀 Use Cases
Protein evidence workflows using UniProt
Protein-sequence exploration
Protein–protein interaction analysis
Protein structure and protein-structure-prediction workflows
Drug and drug-target interaction–related research
Drug discovery and drug repurposing context
⚡ Developer Benefits
MCP server for standardized tool access
Topic coverage across genomics and bioinformatics
Developer visibility into release status via documented release awareness
⚠️ Limitations
Scope is centered on UniProt and linked scientific sources; no other data sources are indicated.
Produce verifiable, release-aware protein evidence packages from UniProt and linked scientific sources.
Use this MCP server to find proteins, assemble protein, target, and variant evidence, and keep a checkable record of where each result came from. Each successful response records the UniProt release, retrieval time, resolved source URL, and a SHA-256 digest. uniprot_provenance_verify can later determine whether the upstream record is unchanged or has drifted.
The complete tool catalog remains available for specialized research workflows.
Every answer this server returns is traceable to a primary-source URL and
a content hash you can re-compute yourself. The walkthrough below is a real
run against the live server (UniProt release 2026_01), independently
confirmed against the UniProt REST API.
Question. What is the function of human p53 (UniProt P04637), what
heritable cancer syndrome is it associated with, and is the R175H mutation
a documented disease variant?
Answer, with its provenance footer (verbatim from the server):
Function.Cellular tumor antigen p53 (gene TP53, Homo sapiens,
393 aa). "Multifunctional transcription factor that induces cell cycle
arrest, DNA repair or apoptosis... Acts as a tumor suppressor in many tumor
types."
Disease.Li-Fraumeni syndrome (acronym LFS, UniProt disease id
DI-01904, OMIM 151623) — "an autosomal dominant familial cancer
syndrome... Four types of cancers account for 80% of tumors occurring in
TP53 germline mutation carriers."
Variant.R175H — "in LFS; germline mutation and in sporadic cancers;
somatic mutation; does not induce SNAI1 degradation; reduces interaction
with ZNF385A; dbSNP:rs28934578."
3. Confirm the values against the primary source — no server in the loop:
bash
curl -s -H "Accept: application/json" https://rest.uniprot.org/uniprotkb/P04637 -o p53.json
# UniProt release served (matches the footer):
curl -sI -H "Accept: application/json" https://rest.uniprot.org/uniprotkb/P04637 | grep -i x-uniprot-release
# -> X-UniProt-Release: 2026_01
python - <<'PY'
import json, hashlib
d = json.load(open("p53.json", encoding="utf-8"))
print("gene :", d["genes"][0]["geneName"]["value"]) # TP53print("protein :", d["proteinDescription"]["recommendedName"]["fullName"]["value"]) # Cellular tumor antigen p53print("organism :", d["organism"]["scientificName"], "| length", d["sequence"]["length"]) # Homo sapiens | 393for c in d["comments"]:
if c.get("commentType") == "DISEASE" and c["disease"].get("acronym") == "LFS":
x = c["disease"]
print("disease :", x["diseaseId"], "| OMIM", x["diseaseCrossReference"]["id"]) # Li-Fraumeni syndrome | 151623for f in d["features"]:
if f.get("type") == "Natural variant" and f["location"]["start"]["value"] == 175:
a = f.get("alternativeSequence", {})
if a.get("originalSequence") == "R" and a.get("alternativeSequences") == ["H"]:
print("variant : R175H |", f["description"]) # in LFS; germline mutation ...# The footer SHA-256 is reproducible from these exact bytes (no server):# the server hashes the JSON re-serialized with sorted keys + compact separators.
canonical = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
print("sha-256 :", hashlib.sha256(canonical).hexdigest())
# -> 0040d79bb39e2f7386d55f81071e87858ec2e5c2cd9552e93c3633897f78345e
PY
What this proves: every returned claim is traceable to a primary-source
URL and a content hash. The gene, protein name, disease (with OMIM id), and
variant the server reports all match the live UniProt entry; the footer
SHA-256 is reproducible byte-for-byte from the primary source using a
documented, server-independent recipe. A third party can re-run all three
checks today, or a year from now, without trusting this server.
Note on the hash: the footer SHA-256 is of the canonical UniProt response
body — the JSON re-serialized with sorted keys and compact separators
(json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False)),
so harmless key-order changes within a release do not break verification. A raw
curl | sha256sum of the bytes will therefore differ; apply the same
canonicalization (step 3 above) or use uniprot_provenance_verify.
Installation
Run without installing (recommended):
bash
uvx uniprot-mcp-server
Or install into an environment:
bash
pip install uniprot-mcp-server
Note: There is an unrelated package named uniprot-mcp on PyPI
(different author, 5 tools, MIT). This package is uniprot-mcp-server.
Running pip install uniprot-mcp will install the wrong package silently.
For researchers — where to start
If you are a biomedical researcher visiting this repo, the highest-signal places to look are:
Two artifacts with deliberately different scopes: • Curated atlas (25 entries). TP53, BRCA1, CFTR, HTT, EGFR, BRAF, KRAS, TEM-1 β-lactamase, more — each linking the canonical UniProt accession to MONDO / OMIM / PharmGKB / ARO IDs and the relevant tool sequence. JSON-LD manifest at examples/atlas/atlas.json. • Comprehensive index (11,590 rows). UniProt's curated disease + pathogen surface as two TSVs (comprehensive_index.tsv 7,250 human disease rows, comprehensive_index_pathogens.tsv 4,340 pathogen rows). Each row carries a UniProt disease ID and an OMIM cross-reference where available. MONDO / PharmGKB / ARO mappings exist only in the 25-entry curated atlas, not in the 11,590-row index. SHA-256 reproducibility manifest at examples/atlas/manifest.json. Methodology (how compiled, what's verified, what's community-reviewable) at examples/atlas/METHODOLOGY.md.
Full Claude-Desktop transcripts of clinical-variant interpretation (TP53 R175H), drug-target dossier (BRCA1), provenance verification a year later, pathogen drug-discovery (TEM-1).
Pre-registered 30-prompt benchmark with SHA-256 commitments on main. The 2026-04-26 v1.1.0 run verified 30/30 against live UniProt — transcript at tests/benchmark/run-2026-04-26-v1.1.0/.
One-command verification that the published PyPI wheel was built from this exact repo (cross-checks SHA-256 across PyPI / GitHub Release / SLSA attestation; runs --self-test; re-runs the benchmark live). POSIX + scripts/replicate.ps1 for Windows.
Honest 14-server survey of the bio-MCP space (April 2026) and the specific differentiation this server claims.
Issues / corrections welcome at https://github.com/smaniches/uniprot-mcp/issues. The atlas in particular is community-reviewable — see METHODOLOGY.md for what is machine-verified vs what needs human review.
What makes this different
uniprot-mcp
Vanilla LLM + WebFetch
A typical bio-MCP
Tool surface
41 tools, 8 families
none — caller writes URLs
usually 5–10
Provenance on every response
release • date • URL • SHA-256
none
sometimes URL only
Per-query auditability
uniprot_provenance_verify re-checks any prior response
not possible
not possible
Release pinning
--pin-release=YYYY_MM raises on drift
n/a
n/a
Pre-registered benchmark
30 prompts, SHA-256 committed on main + reproducible verifier
n/a
n/a
Local provenance cache
uniprot_replay_from_cache read primitive (automatic cache write-through is not wired into the request path — see §Provenance & verification)
weekly + on-demand workflow; per-module measurement complete for cache 82 %, proteinchem 92 %, client 70 %; gate currently 0 % (measurement-first), ≥ 95 % is the v1.2.0 target — see docs/MUTATION_SCORES.md
n/a
rare
The provenance + verify chain is, in my 2026-04-26 survey, absent
from every other bio-MCP I could find. A regulated user can take any
prior uniprot-mcp answer and prove — without contacting the author
— that UniProt still returns the same bytes, or detect exactly how the
upstream has drifted. If you find a counter-example I missed, please
file an issue and I will update the comparison.
Tools (41)
Eight endpoint families. All read-only (readOnlyHint: true). All
but uniprot_replay_from_cache interact with at least one upstream
service (openWorldHint: true). No UniProt API key required.
Core UniProtKB (10)
Tool
Purpose
uniprot_get_entry
Full UniProt entry (e.g. P04637 for p53). Function, gene, organism, disease, cross-refs.
Up to 100 entries in one call; invalid accessions filtered client-side.
uniprot_taxonomy_search
Search UniProt taxonomy by organism name.
Controlled vocabularies (4)
Tool
Purpose
uniprot_get_keyword
Keyword by ID (e.g. KW-0007 = Acetylation). Definition, synonyms, GO refs, hierarchy.
uniprot_search_keywords
Free-text keyword search.
uniprot_get_subcellular_location
Subcellular-location term by ID (e.g. SL-0039 = Cell membrane).
uniprot_search_subcellular_locations
Free-text location search.
Sequence archives & clusters (4)
Tool
Purpose
uniprot_get_uniref
UniRef cluster by ID (UniRef50_P04637, UniRef90_P04637, UniRef100_P04637).
uniprot_search_uniref
Cluster search with identity_tier filter (50 / 90 / 100).
uniprot_get_uniparc
Sequence-archive record by UPI (UPI000002ED67).
uniprot_search_uniparc
UniParc full-text search.
Proteomes & literature (4)
Tool
Purpose
uniprot_get_proteome
Proteome by UP ID (UP000005640 = human). Counts, BUSCO score, components.
uniprot_search_proteomes
Filter by organism / type / completeness.
uniprot_get_citation
Citation record by ID (typically a PubMed numeric ID).
uniprot_search_citations
Index search across UniProt citations.
Structured cross-DB resolvers (4)
Gateway-only — no calls leave the UniProt origin. These extract the
relevant cross-references from a UniProt entry and return structured
records (typed lists / objects, not passthrough strings).
Tool
Purpose
uniprot_resolve_pdb
PDB structures: id + method + resolution + chain coverage.
uniprot_resolve_alphafold
AlphaFold model id + EBI viewer URL (model id only — for pLDDT call the dedicated tool below).
uniprot_resolve_interpro
InterPro signatures: id + entry name.
uniprot_resolve_chembl
ChEMBL drug-target id + EBI target-card URL.
Biomedical features (7)
Pure-Python compositions over the entry — no extra origin. The first
four answer per-residue and per-variant questions; the last three are
the v1.1.0 expansion targeting drug discovery, therapeutic-protein
engineering, and pathogen-secretion analysis: each is a filter over the
entry's features array, with a structured grouping by feature type
and an honest empty-set advisory.
Tool
Purpose
uniprot_compute_properties
Derived sequence chemistry from the FASTA: MW / pI / GRAVY / aromaticity / charge / ε₂₈₀.
uniprot_features_at_position
Every feature overlapping a residue position. Critical for variant-effect interpretation.
uniprot_lookup_variant
HGVS-shorthand match (R175H, V600E, R248*) against UniProt's natural-variant features.
uniprot_get_disease_associations
Structured disease records from DISEASE-type comments: name + acronym + UniProt disease ID + OMIM cross-ref + description.
uniprot_get_active_sites
Catalytic and ligand-binding residues: active sites, binding sites, sites, metal binding, DNA binding. The residue-level chemistry of the protein.
uniprot_get_processing_features
Maturation features: signal peptide, propeptide, transit peptide, initiator methionine, chain, peptide. Essential for therapeutic-protein engineering and pathogen-secretion analysis.
The only tools that consult origins outside rest.uniprot.org. Each is documented in PRIVACY.md and in the threat model.
Tool
Origin
Purpose
uniprot_get_alphafold_confidence
alphafold.ebi.ac.uk
pLDDT mean + four-band distribution; lets the agent decide whether to trust the model.
uniprot_resolve_clinvar
eutils.ncbi.nlm.nih.gov
ClinVar significance + condition + review status by gene + optional HGVS shorthand.
uniprot_get_publications
rest.uniprot.org
Pure-Python over the entry's references — listed here because it complements the cross-origin enrichment.
Composition + provenance (5)
Tool
Purpose
uniprot_resolve_orthology
Group orthology cross-references by source DB (KEGG / OMA / OrthoDB / eggNOG / 8 more).
uniprot_get_evidence_summary
Aggregate ECO codes (Evidence and Conclusion Ontology) across an entry and grade them into a 0-100 evidence-confidence score (high / moderate / low / very-low). Distinguishes wet-lab confirmed from inferred-by-similarity from automatic.
Re-fetch a previously recorded URL and compare release tag + canonical response SHA-256. Five verdicts (verified, release_drift, hash_drift, release_and_hash_drift, url_unreachable) each with an advice string.
uniprot_replay_from_cache
Read a cached UniProt response without hitting the upstream. Opt-in via UNIPROT_MCP_CACHE_DIR.
Provenance & verification
Every successful tool response includes a footer like:
A year later, an auditor can call uniprot_provenance_verify with
those exact fields:
code
> uniprot_provenance_verify(
url="https://rest.uniprot.org/uniprotkb/P04637",
release="2026_01",
response_sha256="0040d79bb39e2f7386d55f81071e87858ec2e5c2cd9552e93c3633897f78345e"
)
## Provenance Verification
**Status:** verified
**URL:** https://rest.uniprot.org/uniprotkb/P04637
- ✓ URL resolves (HTTP 200)
- ✓ Release: recorded '2026_01', current '2026_01'
- ✓ Response SHA-256: recorded 0040d79bb39e2f73…, current 0040d79bb39e2f73…
**Advice:** Both checks passed. The recorded provenance is reproducible against the live UniProt API.
If UniProt has moved on, the tool tells you exactly how:
Verdict
Meaning
Advice
verified
Both release and hash match
The provenance is reproducible
release_drift
UniProt released a new version
Pin via the FTP snapshot if you need the historical answer
hash_drift
Same release, body changed
An in-release edit; investigate or re-fetch
release_and_hash_drift
Both moved on
Use a release-specific FTP snapshot
url_unreachable
Endpoint dropped or rate-limited
Retry or report to UniProt
For strict reproducibility, opt into release pinning:
bash
export UNIPROT_PIN_RELEASE=2026_01
uniprot-mcp
# every response is checked against the pinned release;# any drift raises `ReleaseMismatchError`, which the server surfaces# as an agent-actionable error envelope.
For offline replay, uniprot_replay_from_cache(url) reads a
previously-recorded response from a directory pointed at by
UNIPROT_MCP_CACHE_DIR:
bash
export UNIPROT_MCP_CACHE_DIR=~/.uniprot-mcp-cache
uniprot-mcp
# uniprot_replay_from_cache(url) returns the entry at# $UNIPROT_MCP_CACHE_DIR/<sha256(url)>.json if present.
Status note (v1.1.3).uniprot_replay_from_cache is a read
primitive. The cache must currently be populated by an external
process — for example, by the maintainer-provided benchmark capture
script, or by you wrapping httpx calls and writing to the directory
yourself in the documented JSON shape (see src/uniprot_mcp/cache.py).
Automatic cache write-through is not currently wired into the request
path; cache entries must be populated explicitly or by an external
capture workflow.
tests/benchmark/ ships a 30-prompt evaluation (Tier A / B / C × 10)
with SHA-256-committed expected answers on main. The plaintext
expected.jsonl is held local-only until a benchmark run is
published; the cryptographic commitments mean the author cannot
rewrite "correct" answers post-hoc.
Third-party reproducibility path (no seal file required). Re-derive every Tier A / B answer live from UniProt and print it — no expected.jsonl required. This confirms the answers are independently reproducible from the primary source today; it does not recompute the seal (the committed SHA-256 binds a withheld rationale — see below):
bash
python tests/benchmark/verify_against_hashes.py tests/benchmark/expected.hashes.jsonl
# Re-derives all 30 answers live and prints them (informational; exit 0).
Maintainer cryptographic verification path (with the local plaintext seal). The committed digests in expected.hashes.jsonl are sealed over {prompt_id, answer, rationale}; the rationale is deliberately withheld as part of the sealed pre-registration, so the full cryptographic check requires the local expected.jsonl:
bash
python tests/benchmark/verify_answers.py tests/benchmark/expected.jsonl
# OK: all 30 prompts verified against https://rest.uniprot.org
python tests/benchmark/verify.py tests/benchmark/expected.jsonl tests/benchmark/expected.hashes.jsonl
# OK: 30 commitments verified
See tests/benchmark/AUDIT.md for the
per-prompt source attribution and the formal independence statement
(uniprot-mcp was not used during answer authoring).
Install
bash
pip install uniprot-mcp-server # PyPI distribution# or, for a pinned, isolated install:
uvx --from uniprot-mcp-server uniprot-mcp
Why three different names? This is the standard Python packaging pattern, exactly because PyPI's namespace is global and collisions force disambiguation:
Concept
Value
What it is
GitHub repository
smaniches/uniprot-mcp
source code + issue tracker
PyPI distribution
uniprot-mcp-server
what you pip install (the bare uniprot-mcp name was already claimed on PyPI when this project published)
Python module
uniprot_mcp
what you import (PEP-8 underscore form)
Console script + MCP server identity
uniprot-mcp
what you run from the shell and what Claude Desktop sees
Cross-checks that prove the wheel you installed was built from this repo: each release ships a Sigstore signature, SLSA build provenance, and a CycloneDX SBOM, all attached to the v1.1.0 GitHub Release. Run bash scripts/replicate.sh (POSIX) or pwsh scripts/replicate.ps1 (Windows) to verify the full chain end-to-end. Common precedents for the same one-thing-three-names pattern: pillow/PIL, python-dateutil/dateutil, beautifulsoup4/bs4, python-Levenshtein/Levenshtein.
From source:
bash
git clone https://github.com/smaniches/uniprot-mcp.git
cd uniprot-mcp
pip install -e .
To enable uniprot_replay_from_cache reads against a cache directory
you have populated yourself (automatic write-through is not wired into
the request path — see §Provenance & verification):
1. Clinical-variant interpretation packet for TP53 R175H.
code
> What's at residue 175 of P04637? Is R175H a known variant? Pull
> the UniProt and ClinVar evidence and tell me how confident the
> AlphaFold model is at that residue.
→ uniprot_features_at_position("P04637", 175)
→ uniprot_lookup_variant("P04637", "R175H")
→ uniprot_resolve_clinvar("P04637", change="R175H")
→ uniprot_get_alphafold_confidence("P04637")
2. Drug-target dossier in one call.
code
> Give me a complete drug-target characterisation of human BRCA1.
→ uniprot_target_dossier("P38398")
# nine sections, two upstream calls (entry + FASTA), one tool call.
3. Sequence chemistry for buffer choice / expression-system selection.
code
> What's the molecular weight, pI, and hydrophobicity of human insulin?
→ uniprot_compute_properties("P01308")
# MW 11,981 Da, pI 4.93, ε₂₈₀ 24,980 M⁻¹·cm⁻¹ — pure Python on the FASTA.
4. Provenance round-trip — proving an answer is reproducible.
code
> [later, with the provenance footer from a prior session in hand]
> Verify the recorded provenance for P04637.
→ uniprot_provenance_verify(
url="https://rest.uniprot.org/uniprotkb/P04637",
release="2026_01",
response_sha256="0040d79bb39e2f7386d55f81071e87858ec2e5c2cd9552e93c3633897f78345e"
)
5. Replay a previously-cached answer offline (read primitive — see status note below).
bash
# Pre-condition: $UNIPROT_MCP_CACHE_DIR/<sha256(url)>.json already exists,# populated by the maintainer benchmark capture script or an external# wrapper. Automatic cache write-through is not currently wired into the# request path; cache entries must be populated explicitly or by an# external capture workflow.export UNIPROT_MCP_CACHE_DIR=~/sealed-cache
> uniprot_replay_from_cache("https://rest.uniprot.org/uniprotkb/P04637")
Testing
Layer
Path
What
Unit
tests/unit/
Behaviour of every public function.
Property
tests/property/
Hypothesis-driven invariants on regexes + query construction.
956 offline + 44 live integration tests, all green on main (real counts via pytest --collect-only --ignore=tests/integration and pytest --collect-only tests/integration; the offline count includes the v1.1.x mutation-killer files for cache, proteinchem, client, the contract tests for atlas-manifest / version-consistency / changelog-presence, and the coverage-gap test files that restored full coverage). Line + branch coverage is 100.00 % across all seven source files, with the [tool.coverage.report] gate set to fail_under = 100 so CI enforces it. Three branches carry a justified # pragma: no cover for genuinely-unreachable import-time / defensive fallbacks (documented inline and in pyproject.toml). Reproduce locally with pytest tests/unit tests/property tests/client tests/contract --cov=uniprot_mcp --cov-branch --cov-report=term-missing. Mypy (strict), ruff (check + format), bandit (0 issues at any severity), pip-audit (--strict, no known vulnerabilities) all clean. Mutation testing infrastructure ships and is measurement-first: see the per-module table at docs/MUTATION_SCORES.md for the latest matrix-workflow results; the ≥ 95 % gate is the v1.2.0 target, not the current state.
bash
# Fast, offline (CI on every push):
pytest tests/unit tests/property tests/client tests/contract -v
# Live UniProt (opt-in, nightly in CI):
pytest --integration tests/integration -v
# Lint / type-check / security / SCA:
ruff check . && ruff format --check . && mypy src/uniprot_mcp
bandit -r src/uniprot_mcp && pip-audit --strict
Architecture & threat model
docs/THREAT_MODEL.md — twelve STRIDE-shaped
threats, each receipt-anchored to a code path or commit SHA, plus
the cross-origin allowlist policy (§T3b).
AUDIT.md — pre-1.0.1 professional audit, P0/P1
remediations recorded.
docs/RELEASE.md — release runbook covering the
tag → PyPI → MCP Registry → Sigstore → GitHub Release → Zenodo chain,
including the release-verify.yml post-tag verification job and the
one-time setup for the Zenodo + PyPI webhooks.
docs/archive/ — pre-flip planning docs retained
for audit trail (PENDING_V1.md, MERGE_PLAN.md,
RELEASE_AUDIT_v1.1.3.md). Not part of the published docs site;
current operational status lives in README.md, CHANGELOG.md,
and docs/MUTATION_SCORES.md.
mkdocs.yml — Material-themed docs site, deployable to
gh-pages via .github/workflows/docs.yml.
Build locally with pip install -e ".[docs]" && mkdocs serve.
Related MCP servers by the same author
alphafold-sovereign-mcp — Model Context Protocol server that integrates AlphaFold DB with eight additional public biomedical data sources, with a local SQLite knowledge graph (pip install alphafold-sovereign-mcp).
semantic-scholar-mcp — Model Context Protocol server for Semantic Scholar (200M+ academic papers), providing 14 tools for paper search, citation graph traversal, author profiles, and recommendations (pip install s2-mcp-server).
Citation
Cite via CITATION.cff (GitHub renders a "Cite this
repository" button). Always also cite the UniProt Consortium:
The UniProt Consortium. UniProt: the Universal Protein Knowledgebase
in 2025. Nucleic Acids Research (2025).
doi:10.1093/nar/gkae1010
This project is the gateway layer of the planned Topologica
Bio MCP suite. Multi-source orchestration
and tamper-evident provenance ledgers will live in a companion
topologica-bio repository under BUSL-1.1 (Change Date 2030-04-19,
auto-reverts to Apache-2.0). That companion repository is currently
private; this README will be updated with a public link when it ships.
uniprot-mcp itself is and will remain permissively Apache-2.0
regardless of the Topologica Bio side.