Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

io.github.cyanheads/chembl-mcp-server

REMOTE · CHEMBL.CASEYJHAND.COM · 2 COMPONENTS · SCANNED AUG 20

Link compounds to protein targets, rank bioactivity, and look up drug mechanisms and indications.

+4 this week 68 Trust /100
Trust breakdown (6 categories)

How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. How we score →

Endpoint Security66
  • The endpoint's TLS certificate is valid, in date, and uses a strong key. View diagnostics → Pass
  • Authorisation not fully verified: no authorisation is required to call this server, and 7 tool(s) never declared a destructiveHint. The MCP spec treats an absent hint as destructive by default, so we cannot call this surface safe. See how to fix → View diagnostics → Unverified
  • HTTPS is enforced; there's no plaintext access path. View diagnostics → Pass
  • The HSTS (Strict-Transport-Security) header is present. View diagnostics → Pass
  • DNSSEC is configured correctly; the domain's records validate against the full chain to the root. View diagnostics → Pass
Transport & Reachability100
Schema Quality & AI Usability64
  • AI-judged instruction clarity (excellent).Pass
  • Context-footprint check failed: tool/resource definitions use about 2870 tokens (~410/item across 7 items; 7 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
  • Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management23
  • Stability observed for 7 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage100
  • 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
  • 100% of tool parameters carry a description.Pass
  • Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Capabilities100
  • Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Install

Add this component to your MCP client. Where a client-specific snippet is available, pick your client below and copy it straight into your config; otherwise use the connection detail shown.

remote · chembl.caseyjhand.com

# add to Claude Code
claude mcp add --transport http cyanheads-chembl-mcp-server https://chembl.caseyjhand.com/mcp
# ~/.codex/config.toml
[mcp_servers.cyanheads-chembl-mcp-server]
url = "https://chembl.caseyjhand.com/mcp"
// opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "cyanheads-chembl-mcp-server": {
      "type": "remote",
      "url": "https://chembl.caseyjhand.com/mcp",
      "enabled": true
    }
  }
}
# add to OpenClaw
openclaw mcp add cyanheads-chembl-mcp-server --url https://chembl.caseyjhand.com/mcp --transport streamable-http
# ~/.hermes/config.yaml
mcp_servers:
  cyanheads-chembl-mcp-server:
    url: "https://chembl.caseyjhand.com/mcp"
// mcp.json
{
  "mcpServers": {
    "cyanheads-chembl-mcp-server": {
      "type": "http",
      "url": "https://chembl.caseyjhand.com/mcp"
    }
  }
}

The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.

Changelog

Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.

  • 20 Aug 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 20 to 23. That category is still filling its 30-day observation window: 6 days of observed history at the previous scan, 7 at this one. The score rises as the window fills, whether or not the server changes.

  • 18 Aug 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 13 to 17. That category is still filling its 30-day observation window: 4 days of observed history at the previous scan, 5 at this one. The score rises as the window fills, whether or not the server changes.

  • 16 Aug 26 +1

    No change was recorded against any check on this day. Stability & Change Management went from 7 to 10. That category is still filling its 30-day observation window: 2 days of observed history at the previous scan, 3 at this one. The score rises as the window fills, whether or not the server changes.

  • 14 Aug 26 +1
    • Stability: unverified → 0.03 functional
  • 13 Aug 26 64

    First indexed and scored.

Diagnostics

Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.

Captured 20 Aug 2026 · Probed https://chembl.caseyjhand.com/mcp

TLS valid

Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .

Subject Issuer Valid from Valid until Key Signature Serial
CN=caseyjhand.com CN=WE1,O=Google Trust Services,C=US 7 Jul 2026 5 Oct 2026 ECDSA 256 ECDSA-SHA256 5aad900eb2055a0b0ea55912ec19680c
SANs: caseyjhand.com, *.caseyjhand.com
CN=WE1,O=Google Trust Services,C=US (CA) CN=GTS Root R4,O=Google Trust Services LLC,C=US 13 Dec 2023 20 Feb 2029 ECDSA 256 ECDSA-SHA384 7ff31977972c224a76155d13b6d685e3
CN=GTS Root R4,O=Google Trust Services LLC,C=US (CA) CN=GlobalSign Root CA,OU=Root CA,O=GlobalSign nv-sa,C=BE 15 Nov 2023 28 Jan 2028 ECDSA 384 SHA256-RSA 7fe530bf331343bedd821610493d8a1b
DNSSEC secure

Validation of chembl.caseyjhand.com. Secure

Zone DS Keys Algorithms Outcome
. trust_anchor 20326, 38696 8, 8 Verified
com. present 19718 13 Verified
caseyjhand.com. present 2371 13 Verified
chembl.caseyjhand.com. Verified address RRset verified with the apex keys
Authentication No authorisation required

The endpoint answered without asking for a token. Anyone who knows the URL can reach it.

Result No authorisation required
HTTP status 200
Header Value
strict-transport-security max-age=63072000; includeSubDomains; preload
x-content-type-options nosniff
Transports 2 probes
Transport URL Outcome Status Location
streamable-http https://chembl.caseyjhand.com/mcp Verified 200
http (plaintext) http://chembl.caseyjhand.com/mcp HTTPS enforced 301 https://chembl.caseyjhand.com/mcp
MCP tools · 7 exposed · ~2,496 tokens

The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability.

Tool Tokens
chembl_dataframe_describe ~92

List the tables and columns staged on a canvas by chembl_get_bioactivities — inspect before calling chembl_dataframe_query to write correct SQL. Returns each table with its row count, kind (table | view), and column names + types. Requires CANVAS_PROVIDER_TYPE=duckdb.

NameTypeReqDescription
canvas_idstringyesCanvas ID returned by chembl_get_bioactivities (spilled: true).
NameTypeReqDescription
tablesarrayyesTables and views staged on the canvas.

No examples provided.

chembl_dataframe_query ~296

Run a read-only SQL SELECT over the bioactivity rows chembl_get_bioactivities spilled to a canvas — rank, group, dedupe, and aggregate across the FULL set, not the inline preview. Reference each staged table by the name chembl_get_bioactivities returned — bioactivities for its potency_ranked view, bioactivities_null_potency for null_potency; discover the staged tables and their columns with chembl_dataframe_describe. Compute honest aggregates here (e.g. SELECT molecule_chembl_id, MEDIAN(pchembl_value) AS med FROM bioactivities WHERE standard_type = 'IC50' GROUP BY 1 ORDER BY 2 DESC). Two independent bounds apply, each reported on its own field: truncated is true when the SQL result exceeded the canvas row cap, and rendered_rows says how many of the returned rows the markdown table holds once its character budget is reached (below row_count on a wide or long result). Page past either bound with SQL LIMIT/OFFSET — append e.g. LIMIT 500 OFFSET 500 and re-call; offsets reach rows beyond the canvas row cap. Requires CANVAS_PROVIDER_TYPE=duckdb.

NameTypeReqDescription
canvas_idstringyesCanvas ID returned by chembl_get_bioactivities (spilled: true).
sqlstringyesA read-only SELECT against the staged tables. Reference tables by the names chembl_get_bioactivities returned.
NameTypeReqDescription
rendered_rowsnumberyesHow many of those rows the markdown table in content[] holds. Below row_count when the rendered table reached its character budget — a rendering bound, INDEPENDENT of truncated: a response can be tru…
row_countnumberyesNumber of rows materialized in this response.
rowsarrayyesResult rows (capped at the canvas row limit). Each row is a column→value map.
truncatedbooleanyesTrue when the SQL result exceeded the canvas row cap and was truncated — the engine bounding the result set itself, not the rendering. Independent of rendered_rows; page past it with LIMIT/OFFSET.

No examples provided.

chembl_get_assay ~135

Assay provenance behind a bioactivity row: description, type (binding / functional / ADMET / toxicity), the target it measures, organism, and ChEMBL's 1–9 confidence score (9 = direct assay on the protein target, lower = homologous or indirect). Supply assay_chembl_id from a chembl_get_bioactivities row. Call this to judge whether two measurements are comparable before ranking them together.

NameTypeReqDescription
assay_chembl_idstringyesChEMBL assay ID from a bioactivity row's assay_chembl_id, e.g. "CHEMBL674637".
NameTypeReqDescription
assay_chembl_idstringyesThe ChEMBL assay ID queried.
assay_typeyesAssay type code: B=binding, F=functional, A=ADMET, T=toxicity, P=physicochemical, U=unclassified. Null when absent.
assay_type_descriptionyesHuman-readable assay type, e.g. "Binding". Null when absent.
confidence_descriptionyesHuman-readable confidence description, e.g. "Direct single protein target assigned". Null when absent.
confidence_scoreyesChEMBL confidence score, 1–9 (9 = direct single-protein assay; lower = homologous/indirect). Null when unscored.
descriptionyesAssay description text. Null when absent.
organismyesAssay organism. Null when unspecified.
target_chembl_idyesChEMBL target ID the assay measures — chain to chembl_search_targets/chembl_get_bioactivities. Null when unassigned.

No examples provided.

chembl_get_bioactivities ~878

The flagship compound↔target bioactivity bridge: measurements for a molecule (target deconvolution / selectivity), a target (lead finding), or both together (how potently one compound hits one target). Supply at least one of molecule_chembl_id (from chembl_search_molecules) or target_chembl_id (from chembl_search_targets) — supplying both narrows to that compound–target pair, supplying neither is an error. Filter by standard_type (IC50/Ki/EC50/…), minimum potency pchembl_value_min, assay_type, and organism. Not every measurement has a derivable pchembl_value, so potency_view picks which side of that split you get: the default "potency_ranked" returns the measurements that have one, most potent first (ChEMBL sorts the rest first otherwise, which is why they are not merged), and "null_potency" returns exactly the measurements that have none. totalCount is the honest full match count across both views either way. Mixing measurement types (IC50 vs Ki) is a scientific error — set standard_type to compare like with like. A popular target carries tens of thousands of rows: results spill to a DataCanvas table (call chembl_dataframe_describe for its columns, then chembl_dataframe_query for honest aggregates across the staged set), while an inline preview answers the immediate question. Each view stages its own table (bioactivities / bioactivities_null_potency), so running both against one canvas_id lets a UNION ALL rebuild the full set. The staged table is capped at CHEMBL_MAX_SPILL_ROWS; when the cap is hit, truncated is true and the table is a bounded slice, not the complete view. The inline rows are always capped at limit, so compare that against totalCount before treating them as the whole answer. Spilling the rest requires CANVAS_PROVIDER_TYPE=duckdb; without it the inline preview is all there is.

NameTypeReqDescription
assay_typestringRestrict to an assay type code: "B" (binding), "F" (functional), "A" (ADMET), "T" (toxicity).
canvas_idstringOptional canvas ID from a prior call to reuse the same canvas. Each potency_view re-stages its own table, so a second query of the SAME view REPLACES (overwrites) its prior rows — it does not append…
limitintegerMaximum rows in the inline preview. Defaults to the server default (25). The full set still spills to the canvas.
molecule_chembl_idstringChEMBL molecule ID (from chembl_search_molecules), e.g. "CHEMBL941". Supply this, target_chembl_id, or both — both narrows to that compound–target pair.
organismstringRestrict to a target organism, e.g. "Homo sapiens" (case-insensitive exact match).
pchembl_value_minnumberMinimum pchembl_value (−log10 molar potency), e.g. 7 keeps sub-100 nM activities. Only valid on the potency_ranked view — the null_potency rows have no pchembl_value to compare against.
potency_viewstringWhich side of the pchembl_value presence split to retrieve. "potency_ranked" (default) returns the measurements that have a derivable pchembl_value, most potent first. "null_potency" returns exactly…
standard_typestringRestrict to one measurement type, e.g. "IC50", "Ki", "EC50". Set this to compare potencies validly.
target_chembl_idstringChEMBL target ID (from chembl_search_targets), e.g. "CHEMBL203". Supply this, molecule_chembl_id, or both — both narrows to that compound–target pair.
NameTypeReqDescription
activitiesarrayyesBioactivity rows for the selected potency_view — the inline preview, or the full view when it fit without spilling.
appliedFiltersobjectyesFilters as the server parsed them.
canvasDisabledbooleanyesTrue when CANVAS_PROVIDER_TYPE is not duckdb, so large sets could not spill — the inline rows are a capped preview, not the full set.
canvas_idyesCanvas ID holding the staged table — pass to chembl_dataframe_describe to list its columns, then to chembl_dataframe_query to run SQL over them. Null when canvas is disabled or nothing spilled.
noticestringGuidance when no measurements matched, or how to SQL the spilled set.
potency_viewstringyesWhich view these rows came from: "potency_ranked" = measurements with a derivable pchembl_value; "null_potency" = measurements with none. Re-call with the other value to reach the rest of totalCount.
spilledbooleanyesTrue when the view exceeded the preview and was staged on the canvas.
staged_row_countyesRows actually registered on the canvas table. Null when nothing spilled. Below the view total when truncated is true.
table_nameyesCanvas table name holding the staged rowset, and the FROM target for chembl_dataframe_query SQL — "bioactivities" for potency_ranked, "bioactivities_null_potency" for null_potency. Null when not spil…
totalCountnumberyesTotal matching measurements upstream — the honest full count spanning BOTH potency views, before any preview cap. The staged/preview rows are the selected view of this.
truncatedbooleanyesTrue when the CHEMBL_MAX_SPILL_ROWS cap was hit before the upstream view was exhausted — the staged table is a bounded slice, NOT the complete view, so aggregates over it are a sample. Narrow the fil…

No examples provided.

chembl_get_drug_info ~237

Pharmacology for a drug (molecule): mechanism(s) of action, the molecular target(s) it acts on, action type (inhibitor / agonist / …), first-approval year, and clinical indications with the max phase reached for each. Supply molecule_chembl_id (from chembl_search_molecules). Distinct from the openfda server's label/adverse-event view — this is the curated mechanism-and-indication record. A mechanism's target_chembl_id chains into chembl_get_bioactivities for compounds hitting the same target. Each list carries its own retrieval state: an empty mechanisms or indications array means the molecule has none recorded only when the matching mechanisms_status / indications_status is "complete" — "failed" means the upstream request was rejected and the array says nothing about the molecule, and "truncated" means the page cap bounded the list at fewer rows than the matching *_total_count.

NameTypeReqDescription
molecule_chembl_idstringyesChEMBL molecule ID (from chembl_search_molecules), e.g. "CHEMBL939" for gefitinib.
NameTypeReqDescription
first_approvalyesYear of first approval, e.g. 2003. Null when unapproved or unknown.
indicationsarrayyesClinical indications. Empty is authoritative only when indications_status is "complete".
indications_statusstringyesRetrieval state of the indication list. "complete" = every row ChEMBL records is present, so an empty array is a fact about the molecule. "truncated" = the single-request page cap bounded the list, s…
indications_total_countyesTotal indication rows ChEMBL holds for this molecule (upstream page_meta.total_count). Exceeds the returned array length exactly when the status is "truncated". Null when the fetch failed — the count…
max_phaseyesMax clinical phase across indications: 4 = marketed. Null when unknown.
mechanismsarrayyesMechanisms of action. Empty is authoritative only when mechanisms_status is "complete".
mechanisms_statusstringyesRetrieval state of the mechanism list. "complete" = every row ChEMBL records is present, so an empty array is a fact about the molecule. "truncated" = the single-request page cap bounded the list, so…
mechanisms_total_countyesTotal mechanism rows ChEMBL holds for this molecule (upstream page_meta.total_count). Exceeds the returned array length exactly when the status is "truncated". Null when the fetch failed — the count…
molecule_chembl_idstringyesThe ChEMBL molecule ID queried.
noticestringDisclosure of anything the two lists do not say for themselves: a list whose upstream fetch failed (so its emptiness means nothing), a list the page cap bounded, or — only when both lists came back c…
pref_nameyesPreferred drug name, e.g. "GEFITINIB". Null when unnamed.

No examples provided.

chembl_search_molecules ~470

Discovery entry point for compounds. Find by name / ChEMBL ID / InChIKey with the default search_type=name (supply query), or run a structure search with search_type exact | similarity | substructure (supply structure as a SMILES). At least one of query or structure is required, and structure is required for the three structure modes. Returns ChEMBL ID, preferred name, canonical SMILES, formula, MW, AlogP, Lipinski violations, QED, and max clinical phase on every row; only search_type=similarity adds a Tanimoto similarity percent. Chain molecule_chembl_id into chembl_get_bioactivities or chembl_get_drug_info. A capped result carries nextCursor — pass it back as cursor with the same filters to read the next page.

NameTypeReqDescription
cursorstringOpaque continuation token from a previous call's nextCursor — resumes where that page ended. Omit for the first page. Re-send the same query/structure/filters that minted it (only limit may change; i…
limitintegerMaximum molecules to return. Defaults to the server default (25) when omitted.
max_phase_minintegerFor search_type=name, restrict to compounds at or above this max clinical phase (e.g. 4 for marketed drugs only).
querystringSearch text for search_type=name — a drug name, ChEMBL ID, or InChIKey, e.g. "imatinib" or "CHEMBL25".
search_typestringname = text lookup (query); exact = exact structure match; similarity = Tanimoto ≥ threshold; substructure = contains the structure. All structure modes need `structure`.
similarity_thresholdintegerMinimum Tanimoto similarity percent for search_type=similarity (40–100; ChEMBL rejects below 40). Ignored for other modes.
structurestringSMILES string for structure search, e.g. "CC(=O)Oc1ccccc1C(=O)O". Required when search_type is exact/similarity/substructure.
NameTypeReqDescription
capnumberyesThe limit that was applied.
moleculesarrayyesMatching compounds (up to the limit).
nextCursorstringOpaque token for the next page — pass it back as cursor with the same filters. Absent when this page is the last one.
noticestringGuidance when nothing matched — echoes the query and suggests how to broaden.
shownnumberyesNumber of molecules returned.
totalCountnumberyesTotal compounds matching before the limit was applied.
truncatedbooleanyesTrue when the result was capped at the limit.

No examples provided.

chembl_search_targets ~388

Resolve a protein/gene/UniProt accession to the ChEMBL target ID that chembl_get_bioactivities needs for the target→leads workflow. Supply at least one of accession (UniProt, e.g. P00533), gene_symbol (e.g. EGFR), or query (free-text name); filter further by organism and target_type. Returns each target with its type, organism, and component UniProt accessions + gene symbols. A UniProt accession from the uniprot/protein server is the most precise input. A capped result carries nextCursor — pass it back as cursor with the same filters to read the next page.

NameTypeReqDescription
accessionstringUniProt accession of a target component, e.g. "P00533". The most precise resolver — from the uniprot/protein server.
cursorstringOpaque continuation token from a previous call's nextCursor — resumes where that page ended. Omit for the first page. Re-send the same accession/gene_symbol/query/filters that minted it (only limit m…
gene_symbolstringGene symbol of a target component, e.g. "EGFR" (case-insensitive exact match).
limitintegerMaximum targets to return. Defaults to the server default (25) when omitted.
organismstringRestrict to a source organism, e.g. "Homo sapiens" (case-insensitive exact match).
querystringFree-text name match against the target preferred name, e.g. "kinase" or "growth factor receptor".
target_typestringRestrict to a target class, e.g. "SINGLE PROTEIN" or "PROTEIN COMPLEX".
NameTypeReqDescription
capnumberyesThe limit that was applied.
nextCursorstringOpaque token for the next page — pass it back as cursor with the same filters. Absent when this page is the last one.
noticestringGuidance when no target matched — echoes the filters and suggests how to broaden.
shownnumberyesNumber of targets returned.
targetsarrayyesMatching targets (up to the limit).
totalCountnumberyesTotal targets matching the filters before the limit was applied.
truncatedbooleanyesTrue when the result was capped at the limit.

No examples provided.