Public Hosted Server: https://pubchem.caseyjhand.com/mcp
Overview
Chemical compound and bioassay data from PubChem's PUG REST and PUG View APIs. Search compounds by identifier, formula, or structure; fetch physicochemical properties, safety data, bioactivity, interactions, cross-references, and 3D structures; find bioassays by biological target. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
Tools
| Tool | Description |
|---|---|
pubchem_search_compounds | Search for compounds by name, SMILES, InChIKey, formula, substructure, superstructure, or 2D similarity. |
pubchem_get_compound_details | Get physicochemical properties, descriptions, synonyms, drug-likeness, and classification for compounds by CID. |
pubchem_get_compound_image | Fetch a 2D structure diagram (PNG) for a compound by CID. |
pubchem_get_compound_3d_structure | Fetch a 3D conformer (atomic coordinates and bonds) for a compound by CID, as parsed JSON or raw SDF. |
pubchem_get_compound_xrefs | Get external database cross-references (PubMed, patents, genes, proteins, etc.). |
pubchem_get_compound_safety | Get GHS hazard classification and safety data for one or more compounds by CID (batch). |
pubchem_get_bioactivity | Get a compound's bioactivity profile: assay results, targets, and activity values; filter by outcome or molecular target. |
pubchem_get_compound_interactions | Get drug-drug, drug-food, and chemical-target interactions for a compound by CID. |
pubchem_search_assays | Find bioassays by biological target (gene symbol, protein, Gene ID, UniProt accession). |
pubchem_get_summary | Get summaries for PubChem entities: assays, genes, proteins, taxonomy. |
Resources
Compound and assay records are also exposed as URI-templated resources, backed by the same client methods as the tools; many MCP clients are tool-only and never surface resources.
| Resource | Description |
|---|---|
pubchem://compound/{cid} | Core physicochemical properties (JSON). |
pubchem://compound/{cid}/safety | GHS hazard classification (JSON). |
pubchem://compound/{cid}/image | 2D structure diagram (PNG). |
pubchem://compound/{cid}/xrefs | External cross-references (JSON). |
pubchem://compound/{cid}/bioactivity | Bioassay activity profile (JSON). |
pubchem://assay/{aid} | BioAssay summary (JSON). |
Capability reference
pubchem_search_compounds tool
- Five search strategies: identifier (name/SMILES/InChIKey, batched 1-25), formula (Hill notation, optional
allowOtherElements), substructure/superstructure containment, or 2D Tanimoto similarity (threshold 70-100, default 90) - Caps at 200 CIDs per page (default 20);
offsetpages to a ceiling of 10,000 — identifier lookups resolve every match up front so paging is free, while formula/structure/similarity searches cost more upstream per deep page - Optional
propertieshydration avoids a follow-uppubchem_get_compound_detailscall - Identifier mode reports
unresolvedIdentifiersfor inputs that resolved to no CID, plus notices when multiple inputs collide on one CID - Reports an exact
totalFoundwhen the full match set was observed, or atotalFoundAtLeastfloor when a bounded upstream search saturated
pubchem_get_compound_details tool
- Up to 100 CIDs per call; 27 available properties, defaulting to a core set of 14 (formula, weight, IUPAC name, SMILES forms, InChIKey, XLogP, TPSA, H-bond/rotatable-bond counts, heavy atom count, charge, complexity)
- Optional textual descriptions, paged via
descriptionOffset/maxDescriptions(default 3, up to 20) — fetched only for the first 10 CIDs in the batch, remaining CIDs listed inskippedCids - Optional synonyms for every found CID, paged via
synonymOffset/maxSynonyms(default 20, up to 100) - Optional drug-likeness assessment (Lipinski Rule of Five + Veber rules), computed from the returned properties at no extra latency
- Optional pharmacological classification (FDA classes/mechanisms, MeSH classes, ATC codes) — same 10-CID fan-out cap as descriptions
- Per-CID
found: falsedistinguishes a nonexistent CID from a real compound PubChem simply has no data for
pubchem_get_compound_image tool
- Single CID;
sizeis"small"(100x100) or"large"(300x300, default) - Returns base64-encoded PNG plus width/height
- Typed
cid_not_founderror when PubChem has no record for the CID
pubchem_get_compound_3d_structure tool
- Single CID;
format="json"(default) returns parsed atoms (element + x/y/z) and bonds,format="sdf"returns the raw V2000 SDF text maxAtoms/maxBondscap the JSON preview (default 200 each);atomCount/bondCountalways report the full totals, with any capping disclosed via enrichmentincludeRawSdfbypasses the default 500-line cap on the raw SDF text- Optional
includeAlternateConformerIdslists conformer IDs beyond the default - Typed
no_3d_structureerror when PubChem has no computed 3D coordinates (large molecules, mixtures, some salts)
pubchem_get_compound_xrefs tool
- Single CID; one or more
xrefTypes— string IDs (RegistryID,RNfor CAS numbers,PatentID) and numeric IDs (PubMedID,GeneID,ProteinGI,TaxonomyID) - Paged per type:
maxPerTypeup to 500 (default 50), with the sameoffsetapplied across every requested type - Each type reports its own
totalAvailableandtruncatedflag - Empty-result notice distinguishes "this compound has none of the requested types" from a possibly-mistyped CID
pubchem_get_compound_safety tool
- Batch of 1-25 CIDs
- Returns GHS signal word, pictograms, hazard statements (H-codes), and precautionary statements (P-codes), with source attribution
- Per-CID
status:ok,no_ghs_data(compound exists, no deposited classification), orcid_not_found(no PubChem record at all) — kept distinct so a bad CID never reads as "no hazards on file" - Precautionary statements carry a
decodedflag — false for codes needing label-specific fill text or outside the decoder table; the code itself is still authoritative
pubchem_get_bioactivity tool
- Single CID; filter by
outcomeFilter(active/inactive/all, defaultall) and/ortargetGeneId/targetAccession - Caps at 100 results per page (default 20);
offsetreaches the rest - Reports
totalAssays/activeCount/inactiveCountfor the whole compound, plusfilteredCount/returnedCountfor the current page - Notices distinguish "no bioactivity data at all" from "the filter excluded everything" from "offset past the end"
pubchem_get_compound_interactions tool
- Single CID; one or more
kinds—drug-drug(DrugBank),drug-food,target(binding/activity from BindingDB, ChEMBL, and others); default["drug-drug"] maxEntriesper kind per page (1-50, default 10);offsetcounts source records rather than returned entries, capped at 2,147,483,646- Each kind pages independently —
paging[]reports per-kindtotalRecords/nextOffset/truncated; the top-levelnextOffsetis populated only when exactly one requested kind still has records left - A kind that fails to retrieve is named in
failedKindswithout failing the kinds that succeeded
pubchem_search_assays tool
- Search by
targetType:genesymbol/proteinname(text),geneid(NCBI Gene ID),proteinaccession(UniProt) - Caps at 200 AIDs per page (default 50);
offsetpages to the total found - Rejects a blank
targetQueryand a non-numericgeneidquery before the upstream call - Reports
totalFoundacross all pages and distinguishes "no match" from "offset past the end"
pubchem_get_summary tool
entityType:assay(AID),gene(NCBI Gene ID),protein(UniProt accession), ortaxonomy(Tax ID); up to 10 identifiers per call- Per-identifier
foundflag; populated fields depend onentityType(taxonomy includes an orderedlineage, gene includessymbol/taxonomy) - Notice reports how many identifiers were not found and which ID type
entityTypeexpects
pubchem://compound/{cid} resource
- Core physicochemical properties (the same default 14-property set as
pubchem_get_compound_details), asapplication/json - Throws a typed not-found when the CID doesn't exist in PubChem
- Use
pubchem_get_compound_detailsto select specific properties or add descriptions, synonyms, drug-likeness, and classification
pubchem://compound/{cid}/safety resource
- GHS hazard classification as
application/json status(ok/no_ghs_data/cid_not_found) is the only signal distinguishing a bad CID from a compound with no deposited classification — a resource read has no notice surface
pubchem://compound/{cid}/image resource
- 2D structure diagram, 300x300 PNG, returned as a base64 blob
- Use
pubchem_get_compound_imagefor the 100x100 size option
pubchem://compound/{cid}/xrefs resource
- Focused default set —
RN(CAS),RegistryID,PubMedID— up to 25 IDs per type, asapplication/json - Use
pubchem_get_compound_xrefsfor the full set of xref types, a higher per-type cap, and offset paging
pubchem://compound/{cid}/bioactivity resource
- Up to 25 assays as
application/json, plustotalAssays/activeCountfor the whole compound - Use
pubchem_get_bioactivityto filter by outcome or target, raise the cap, or page with offset
pubchem://assay/{aid} resource
- BioAssay summary as
application/json— name, description, source, protocol, substance counts - Throws a typed not-found when the AID doesn't exist
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.
PubChem-specific:
- Covers both PUG REST (search, properties, cross-references, safety, bioactivity, interactions) and PUG View (textual descriptions, pharmacological classification) endpoints
- Rate-limited client (5 req/s) with automatic request queuing, and retry with exponential backoff on 5xx errors and network failures
- Hand-rolled V2000 SDF parser for 3D conformer atoms and bonds; drug-likeness (Lipinski/Veber) computed from already-fetched properties, adding no extra latency
- All tools are read-only and idempotent — no API keys required, PubChem's API is freely accessible
Agent-friendly output:
- Discriminated output contracts — per-CID
status(ok/no_ghs_data/cid_not_found) andfoundflags let callers branch on data instead of matching an error string - Graceful partial failure — batch tools return per-item results alongside
unresolvedIdentifiers,skippedCids, andfailedKindsrather than failing the whole call - Response shaping — truncation disclosure (
truncated,shown/cap,nextOffset) on every capped list, plus atotalFoundAtLeastfloor in place of a count when an upstream search saturates - Typed error reasons — validation and not-found failures declare a
reason(e.g.cid_not_found,missing_identifier_args,invalid_cid_query) with actionable recovery text, not generic messages
Getting started
Public Hosted Instance
A public instance is available at https://pubchem.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
json{ "mcpServers": { "pubchem-mcp-server": { "type": "streamable-http", "url": "https://pubchem.caseyjhand.com/mcp" } } }
Self-Hosted / Local
Add the following to your MCP client configuration file.
json{ "mcpServers": { "pubchem-mcp-server": { "type": "stdio", "command": "bunx", "args": ["@cyanheads/pubchem-mcp-server@latest"], "env": { "MCP_TRANSPORT_TYPE": "stdio" } } } }
Or with npx (no Bun required):
json{ "mcpServers": { "pubchem-mcp-server": { "type": "stdio", "command": "npx", "args": ["-y", "@cyanheads/pubchem-mcp-server@latest"], "env": { "MCP_TRANSPORT_TYPE": "stdio" } } } }
Or with Docker:
json{ "mcpServers": { "pubchem-mcp-server": { "type": "stdio", "command": "docker", "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/pubchem-mcp-server:latest"] } } }
For Streamable HTTP, set the transport and start the server:
shMCP_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 API keys required — PubChem's API is freely accessible.
Installation
- Clone the repository:
shgit clone https://github.com/cyanheads/pubchem-mcp-server.git
- Navigate into the directory:
shcd pubchem-mcp-server
- Install dependencies:
shbun install
- Configure environment (optional):
shcp .env.example .env # edit .env to override transport, session mode, storage, or logging defaults
Configuration
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_HTTP_HOST | Host for HTTP server. | 127.0.0.1 |
MCP_SESSION_MODE | stateless, stateful, or auto. PubChem needs no multi-round-trip input, so the server declares stateless; the example and Docker set it to match. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
STORAGE_PROVIDER_TYPE | Storage backend. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for 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:
shbun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
shdocker build -t pubchem-mcp-server . docker run --rm -p 3010:3010 pubchem-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/pubchem-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/resources and inits the PubChem client. |
src/mcp-server/tools/definitions/ | Tool definitions (*.tool.ts). |
src/mcp-server/resources/definitions/ | Resource definitions (*.resource.ts). |
src/services/pubchem/ | PubChem API client — rate limiting, retry, and response/SDF parsing. |
scripts/ | Build, clean, devcheck, and tree generation scripts. |
tests/ | Unit and integration tests. |
Development guide
See CLAUDE.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 - Wrap external API calls: validate the raw PubChem response → normalize to a domain type → return the output schema; never fabricate missing fields
- Register new tools and resources in the
index.tsbarrel files
Contributing
Issues are welcome. Run checks before submitting:
shbun run devcheck bun run test
License
Apache-2.0 — see LICENSE for details.



