[
  {
    "id": "chemistry",
    "displayName": "Chemistry",
    "description": "Small-molecule chemistry via PubChem, ChEBI, Rhea and BindingDB.",
    "useWhen": "Use when a question needs authoritative small-molecule chemistry data \u2014 PubChem compound properties (formula, weight, SMILES/InChI, IUPAC name), CID resolution and 2D similarity search, bioassay and GHS safety summaries; ChEBI ontology entities, roles and relations; Rhea enzyme reactions (by ChEBI participant, EC number, or equation text); or BindingDB binding affinities (Ki/Kd/IC50/EC50) by protein target or compound. Sourced from PubChem, ChEBI, Rhea and BindingDB.",
    "sources": [
      "PubChem",
      "ChEBI",
      "Rhea",
      "BindingDB"
    ],
    "termsUrl": "https://www.ncbi.nlm.nih.gov/home/about/policies/",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "pubchem_search_compounds",
        "connector": "chemistry",
        "description": "Resolve a chemical identifier (name, SMILES, InChIKey, or CID) to PubChem CIDs, optionally with core computed properties for the top hits.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "Identifier matching `namespace`, e.g. \"aspirin\", a SMILES, or an InChIKey."
            },
            "namespace": {
              "type": "string",
              "enum": [
                "name",
                "smiles",
                "inchikey",
                "cid"
              ],
              "default": "name"
            },
            "max_cids": {
              "type": "integer",
              "default": 25,
              "minimum": 1,
              "maximum": 100
            },
            "with_properties": {
              "type": "boolean",
              "default": true
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ \"query\": str, \"namespace\": str, \"n_cids_total\": int, \"truncated\": bool, \"cids\": [int], \"properties\": [ {...} ] }` \u2014 `n_cids_total` is the full match count, `cids` capped at `max_cids`. `cids` is `[]` when nothing matches (not an error). `properties` rows use PubChem 2025 field names (CID, MolecularFormula, MolecularWeight, `SMILES` = isomeric, `ConnectivitySMILES` = stereo-stripped, InChI, InChIKey, IUPACName, XLogP, ExactMass, TPSA, Charge, H-bond/rotatable-bond/heavy-atom counts).",
        "example": "const result = await host.mcp(\"chemistry\", \"pubchem_search_compounds\", {\"query\": \"aspirin\", \"max_cids\": 25})",
        "required": [
          "query"
        ]
      },
      {
        "id": "pubchem_get_compounds",
        "connector": "chemistry",
        "description": "Full computed-property records for a batch of PubChem CIDs, with optional capped synonym lists.",
        "input": {
          "type": "object",
          "properties": {
            "cids": {
              "type": "array",
              "items": {
                "type": "integer"
              },
              "minItems": 1,
              "maxItems": 50
            },
            "include_synonyms": {
              "type": "boolean",
              "default": false
            },
            "max_synonyms": {
              "type": "integer",
              "default": 30
            }
          },
          "required": [
            "cids"
          ]
        },
        "returns": "`{ \"n_requested\": int, \"duplicates\": [int], \"records\": [ {...} ], \"not_found\": [int] }` \u2014 one record per distinct CID (repeats disclosed in `duplicates`, first-occurrence order). Each record carries CID, MolecularFormula, MolecularWeight, SMILES (isomeric), ConnectivitySMILES, InChI, InChIKey, IUPACName, XLogP, ExactMass, TPSA, Charge, HBondDonorCount, HBondAcceptorCount, RotatableBondCount, HeavyAtomCount \u2014 plus `synonyms`/`n_synonyms_total`/`synonyms_truncated` when `include_synonyms`. CIDs the API does not know appear in `not_found`.",
        "example": "const result = await host.mcp(\"chemistry\", \"pubchem_get_compounds\", {\"cids\": [2244, 2519], \"include_synonyms\": false})",
        "required": [
          "cids"
        ]
      },
      {
        "id": "pubchem_similarity_search",
        "connector": "chemistry",
        "description": "2D Tanimoto similarity search over all of PubChem for a query SMILES (synchronous fastsimilarity_2d route, no job polling).",
        "input": {
          "type": "object",
          "properties": {
            "smiles": {
              "type": "string"
            },
            "threshold": {
              "type": "integer",
              "default": 90,
              "minimum": 1,
              "maximum": 100
            },
            "max_records": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 200
            },
            "with_properties": {
              "type": "boolean",
              "default": false
            }
          },
          "required": [
            "smiles"
          ]
        },
        "returns": "`{ \"smiles\": str, \"threshold\": int, \"n_cids\": int, \"may_be_truncated\": bool, \"cids\": [int], \"properties\": [ {...} ] }` \u2014 CIDs in upstream relevance order (the query compound is usually first). `threshold` is percent Tanimoto. The API does not report an uncapped total, so `may_be_truncated` is true exactly when the cap was filled. `properties` (when `with_properties`) covers the first 10 hits.",
        "example": "const result = await host.mcp(\"chemistry\", \"pubchem_similarity_search\", {\"smiles\": \"CC(=O)OC1=CC=CC=C1C(=O)O\", \"threshold\": 90})",
        "required": [
          "smiles"
        ]
      },
      {
        "id": "pubchem_get_bioassay_summary",
        "connector": "chemistry",
        "description": "Bioassay activity summary for one PubChem compound \u2014 which assays tested it, against which targets, with what outcome and potency.",
        "input": {
          "type": "object",
          "properties": {
            "cid": {
              "type": "integer"
            },
            "active_only": {
              "type": "boolean",
              "default": false
            },
            "max_rows": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          },
          "required": [
            "cid"
          ]
        },
        "returns": "`{ \"cid\": int, \"active_only\": bool, \"n_rows_total\": int, \"truncated\": bool, \"rows\": [ {...} ] }` \u2014 each row maps the upstream columns: AID, SID, CID, \"Activity Outcome\" (Active/Inactive/Unspecified/Inconclusive), \"Target Accession\", \"Target GeneID\", \"Activity Value [uM]\", \"Activity Name\", \"Assay Name\", \"Assay Type\", \"PubMed ID\". Filtering by `active_only` happens BEFORE the cap, so `n_rows_total` is the true (filtered) count. `rows` is `[]` for compounds with no assay data (not an error).",
        "example": "const result = await host.mcp(\"chemistry\", \"pubchem_get_bioassay_summary\", {\"cid\": 2244, \"active_only\": true})",
        "required": [
          "cid"
        ]
      },
      {
        "id": "pubchem_get_safety",
        "connector": "chemistry",
        "description": "GHS safety classification for one PubChem compound (PUG-View 'GHS Classification' heading), aggregated across reporting sources.",
        "input": {
          "type": "object",
          "properties": {
            "cid": {
              "type": "integer"
            }
          },
          "required": [
            "cid"
          ]
        },
        "returns": "`{ \"cid\": int, \"found\": bool, \"ghs\": {...} | null }` \u2014 `ghs` is null when PubChem has no GHS section. Otherwise `{ cid, record_title, signals (e.g. [\"Danger\"]), pictograms (e.g. [\"Flammable\", \"Irritant\"]), hazard_statements (H-codes with occurrence percentages), precautionary_statement_codes, notes, n_source_references }`. Percentages in the hazard text show how many indexed sources report each hazard.",
        "example": "const result = await host.mcp(\"chemistry\", \"pubchem_get_safety\", {\"cid\": 702})",
        "required": [
          "cid"
        ]
      },
      {
        "id": "chebi_search",
        "connector": "chemistry",
        "description": "Full-text search over ChEBI entities (names, synonyms, formulae, InChIKeys).",
        "input": {
          "type": "object",
          "properties": {
            "term": {
              "type": "string"
            },
            "max_results": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            },
            "page": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          "required": [
            "term"
          ]
        },
        "returns": "`{ \"term\": str, \"page\": int, \"size\": int, \"api_total\": int, \"number_pages\": int, \"results\": [ {...} ] }` \u2014 `api_total` is ChEBI's own hit count (further pages exist iff `api_total > page*size`). Each result: chebi_accession, name, definition, stars (3 = manually curated), formula, charge, mass, monoisotopic_mass, smiles, inchikey, relevance.",
        "example": "const result = await host.mcp(\"chemistry\", \"chebi_search\", {\"term\": \"caffeine\", \"max_results\": 20})",
        "required": [
          "term"
        ]
      },
      {
        "id": "chebi_get_entity",
        "connector": "chemistry",
        "description": "Full ChEBI entity record: names, structure, chemical data, roles and cross-references.",
        "input": {
          "type": "object",
          "properties": {
            "chebi_id": {
              "type": "string"
            },
            "max_synonyms": {
              "type": "integer",
              "default": 30
            },
            "max_xrefs": {
              "type": "integer",
              "default": 50
            }
          },
          "required": [
            "chebi_id"
          ]
        },
        "returns": "`{ chebi_accession, name, definition, stars, formula, charge, mass, monoisotopic_mass, smiles, inchi, inchikey, iupac_names, synonyms, n_synonyms_total, synonyms_truncated, secondary_ids, xrefs ({type, accession, source, url}), n_xrefs_total, xrefs_truncated, roles ({chebi_accession, name, definition}), modified_on, is_released }` \u2014 accepts `CHEBI:27732` or bare `27732`; secondary (merged) ids resolve to the primary record. Ontology parents/children live in `chebi_get_ontology`. Unknown ids throw a not-found error.",
        "example": "const result = await host.mcp(\"chemistry\", \"chebi_get_entity\", {\"chebi_id\": \"CHEBI:27732\"})",
        "required": [
          "chebi_id"
        ]
      },
      {
        "id": "chebi_get_ontology",
        "connector": "chemistry",
        "description": "Ontology relations of a ChEBI entity \u2014 what it IS (outgoing: is a / has role / conjugate acid...) and what points AT it (incoming: children/derivatives).",
        "input": {
          "type": "object",
          "properties": {
            "chebi_id": {
              "type": "string"
            },
            "relation_type": {
              "type": "string",
              "description": "Optional exact filter, e.g. \"is a\", \"has role\", \"has part\"."
            },
            "max_relations": {
              "type": "integer",
              "default": 100
            }
          },
          "required": [
            "chebi_id"
          ]
        },
        "returns": "`{ chebi_accession, name, relation_type_filter, outgoing_relations, n_outgoing_total, outgoing_truncated, incoming_relations, n_incoming_total, incoming_truncated }` \u2014 each relation `{ relation_type, init_chebi_id, init_name, final_chebi_id, final_name }` reads \"init --relation--> final\" (for outgoing, init is this entity; for incoming, final is this entity). `max_relations` caps per direction.",
        "example": "const result = await host.mcp(\"chemistry\", \"chebi_get_ontology\", {\"chebi_id\": \"CHEBI:27732\", \"relation_type\": \"has role\"})",
        "required": [
          "chebi_id"
        ]
      },
      {
        "id": "rhea_search_reactions",
        "connector": "chemistry",
        "description": "Search Rhea master reactions by equation text, participant ChEBI id, or EC number (query type auto-detected).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "A ChEBI id (participant), a full EC number (enzyme reactions), or free text matched against the equation."
            },
            "limit": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 500
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ \"query\": str, \"query_type\": \"chebi\"|\"ec\"|\"text\", \"api_total\": int, \"n_returned\": int, \"truncated\": bool, \"reactions\": [ { \"rhea_id\": str, \"equation\": str, \"status\": \"Approved\"|\"Preliminary\"|\"Obsolete\" } ] }` \u2014 reactions ordered by rhea_id; `api_total` (a companion COUNT query) is the true match count. A ChEBI id matches reactions with that participant; a full EC matches enzyme reactions (partial ECs like \"2.1.1.-\" are rejected); anything else is a case-insensitive substring match on equation text.",
        "example": "const result = await host.mcp(\"chemistry\", \"rhea_search_reactions\", {\"query\": \"caffeine\", \"limit\": 50})",
        "required": [
          "query"
        ]
      },
      {
        "id": "rhea_get_reaction",
        "connector": "chemistry",
        "description": "Full record for one Rhea reaction: equation, participants with ChEBI ids and stoichiometry, EC links, direction family and literature.",
        "input": {
          "type": "object",
          "properties": {
            "rhea_id": {
              "type": "string"
            }
          },
          "required": [
            "rhea_id"
          ]
        },
        "returns": "`{ rhea_id, equation, status, is_transport, is_chemically_balanced, ec_numbers, pubmed_ids, directional_reactions, bidirectional_reaction, left_side, right_side }` \u2014 each side lists participants `{ compound_accession, name, coefficient }` (coefficient \"1\", \"2\", ... or symbolic \"N\"/\"2n\"). Accepts `RHEA:10280` or bare `10280`; unknown ids throw a not-found error.",
        "example": "const result = await host.mcp(\"chemistry\", \"rhea_get_reaction\", {\"rhea_id\": \"10280\"})",
        "required": [
          "rhea_id"
        ]
      },
      {
        "id": "bindingdb_ligands_by_target",
        "connector": "chemistry",
        "description": "Measured binding affinities (Ki/Kd/IC50/EC50) of all BindingDB ligands against one protein target, by UniProt accession.",
        "input": {
          "type": "object",
          "properties": {
            "uniprot": {
              "type": "string"
            },
            "affinity_cutoff_nm": {
              "type": "number",
              "default": 10000
            },
            "max_rows": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          },
          "required": [
            "uniprot"
          ]
        },
        "returns": "`{ \"uniprot\": str, \"affinity_cutoff_nm\": num, \"n_rows_total\": int, \"truncated\": bool, \"rows\": [ { target_name, monomer_id, smiles, affinity_type, affinity, pmid, doi } ] }` \u2014 only measurements with value <= `affinity_cutoff_nm`. The full match set is downloaded and counted, so `n_rows_total` is the true count; rows are capped at `max_rows`, sorted by (affinity_type, numeric affinity ascending). `affinity` is a STRING (may carry `>`/`<`, in nM). No hits returns `n_rows_total=0`.",
        "example": "const result = await host.mcp(\"chemistry\", \"bindingdb_ligands_by_target\", {\"uniprot\": \"P00533\", \"affinity_cutoff_nm\": 100})",
        "required": [
          "uniprot"
        ]
      },
      {
        "id": "bindingdb_targets_by_compound",
        "connector": "chemistry",
        "description": "Protein targets with measured affinities for compounds 2D-similar to a query SMILES \u2014 \"what does this molecule (or its close analogs) bind?\".",
        "input": {
          "type": "object",
          "properties": {
            "smiles": {
              "type": "string"
            },
            "similarity": {
              "type": "number",
              "default": 0.85,
              "minimum": 0.5,
              "maximum": 1
            },
            "max_rows": {
              "type": "integer",
              "default": 100,
              "minimum": 1,
              "maximum": 1000
            }
          },
          "required": [
            "smiles"
          ]
        },
        "returns": "`{ \"smiles\": str, \"similarity\": num, \"api_hit_count\": int, \"n_rows_total\": int, \"truncated\": bool, \"rows\": [ { monomer_id, smiles, ligand_name, target_name, species, affinity_type, affinity, tanimoto } ] }` \u2014 `smiles` per row is the matched analog. `api_hit_count` is the upstream matching-compound count (not row-for-row comparable with `n_rows_total`, which counts per-measurement rows). Rows capped at `max_rows`, sorted by (target_name, affinity_type, numeric affinity). `affinity` is a STRING (may carry `>`/`<`, in nM). No hits returns `n_rows_total=0`.",
        "example": "const result = await host.mcp(\"chemistry\", \"bindingdb_targets_by_compound\", {\"smiles\": \"CC(=O)OC1=CC=CC=C1C(=O)O\", \"similarity\": 0.85})",
        "required": [
          "smiles"
        ]
      }
    ]
  },
  {
    "id": "literature",
    "displayName": "Literature Graph",
    "description": "Literature metadata and relationships through OpenAlex, arXiv, Crossref and DataCite.",
    "useWhen": "Find papers and authors, inspect citations or DOI update relationships, and discover dataset/software DOI records. OpenAlex requires its configured API key; the Crossref and DataCite methods use public metadata.",
    "sources": [
      "OpenAlex",
      "arXiv",
      "Crossref",
      "DataCite"
    ],
    "termsUrl": "https://docs.openalex.org/additional-help/terms",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "openalex_search_works",
        "connector": "literature",
        "description": "Search OpenAlex scholarly works (all disciplines, ~250M records) with year/type/OA/venue filters. Args: query (free-text over title+abstract+fulltext; optional if a filter is set), year_from, year_to (inclusive years), work_type (article/review/preprint/book-chapter/dataset/dissertation), open_access_only, venue (S-id, openalex.org URL, ISSN, or a plain name resolved to the top sources hit \u2014 surfaced in venue_resolved; pass an exact ID to skip resolution), sort (relevance default / cited_by_count / publication_date), max_records (default 50, hard ceiling 500; pages of 200), include_abstracts (reconstructed from the inverted index, but ONLY for verified-open licenses \u2014 cc-by/cc-by-sa/cc0/public-domain; others get abstract=null + abstract_policy note + abstract_license; adds bulk). Returns {query, filters, sort, api_total, n_records_returned, records_truncated, records}; each record is the lean work shape (openalex_id, doi, pmid, title, publication_year/date, type, language, is_retracted, authors[...], source{...}, biblio, cited_by_count, fwci, referenced_works_count, open_access{...}, best_oa_pdf_url, primary_topic, keywords).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "year_from": {
              "type": "integer"
            },
            "year_to": {
              "type": "integer"
            },
            "work_type": {
              "type": "string"
            },
            "open_access_only": {
              "type": "boolean"
            },
            "venue": {
              "type": "string"
            },
            "sort": {
              "type": "string",
              "enum": [
                "relevance",
                "cited_by_count",
                "publication_date"
              ],
              "default": "relevance"
            },
            "max_records": {
              "type": "integer",
              "default": 50
            },
            "include_abstracts": {
              "type": "boolean",
              "default": false
            }
          }
        },
        "returns": "{query, filters (applied filter object), venue_resolved? , sort, api_total (meta.count), n_records_returned, records_truncated (api_total > returned), records[]} \u2014 each record the lean work shape.",
        "example": "const result = await host.mcp(\"literature\", \"openalex_search_works\", {\"query\": \"CRISPR base editing\", \"year_from\": 2020, \"open_access_only\": true, \"sort\": \"cited_by_count\", \"max_records\": 25})",
        "required": []
      },
      {
        "id": "openalex_get_work",
        "connector": "literature",
        "description": "Fetch one OpenAlex work in full \u2014 metadata, abstract (reconstructed from the inverted index, license-gated as in openalex_search_works), OA locations, referenced_works (outgoing W-ids \u2014 hydrate with openalex_references) and counts_by_year. Args: work_id (W-id, openalex.org URL, bare DOI, or doi.org URL). DOI lookups resolve via the claimant filter; when several works share one DOI the most-cited is selected and doi_claimants + doi_resolution_note are included. Raises not-found for unknown IDs/DOIs.",
        "input": {
          "type": "object",
          "properties": {
            "work_id": {
              "type": "string"
            }
          },
          "required": [
            "work_id"
          ]
        },
        "returns": "The lean work shape plus {abstract, abstract_license?, abstract_policy?, referenced_works (W-ids), counts_by_year, doi_claimants?, doi_resolution_note?}.",
        "example": "const result = await host.mcp(\"literature\", \"openalex_get_work\", {\"work_id\": \"W2741809807\"})",
        "required": [
          "work_id"
        ]
      },
      {
        "id": "openalex_citations",
        "connector": "literature",
        "description": "List works that CITE a given work (incoming citations) via OpenAlex's citation graph. Args: work_id (W-id/URL/DOI \u2014 DOIs cost one extra resolution request), sort (cited_by_count default / publication_date / relevance), max_records (default 50, ceiling 500), include_abstracts. Returns {work_id, api_total (the true citing-work count), n_records_returned, records_truncated, records} (lean work records).",
        "input": {
          "type": "object",
          "properties": {
            "work_id": {
              "type": "string"
            },
            "sort": {
              "type": "string",
              "enum": [
                "cited_by_count",
                "publication_date",
                "relevance"
              ],
              "default": "cited_by_count"
            },
            "max_records": {
              "type": "integer",
              "default": 50
            },
            "include_abstracts": {
              "type": "boolean",
              "default": false
            }
          },
          "required": [
            "work_id"
          ]
        },
        "returns": "{work_id (resolved W-id), api_total, n_records_returned, records_truncated, records[], doi_claimants?, doi_resolution_note?}.",
        "example": "const result = await host.mcp(\"literature\", \"openalex_citations\", {\"work_id\": \"W2741809807\", \"sort\": \"cited_by_count\", \"max_records\": 50})",
        "required": [
          "work_id"
        ]
      },
      {
        "id": "openalex_references",
        "connector": "literature",
        "description": "List the works a given work CITES (outgoing references), hydrated to full metadata in reference-list order. Args: work_id (W-id/URL/DOI), max_records (default 100, ceiling 500; hydration batched 50/request). Returns {work_id, n_references, n_records_returned, records_truncated, references_not_hydrated (IDs OpenAlex has no record for \u2014 never silently dropped), reference_ids (ALL outgoing W-ids), records}.",
        "input": {
          "type": "object",
          "properties": {
            "work_id": {
              "type": "string"
            },
            "max_records": {
              "type": "integer",
              "default": 100
            }
          },
          "required": [
            "work_id"
          ]
        },
        "returns": "{work_id, n_references, n_records_returned, records_truncated, references_not_hydrated[], reference_ids[] (all outgoing W-ids, order preserved), records[]}.",
        "example": "const result = await host.mcp(\"literature\", \"openalex_references\", {\"work_id\": \"W2741809807\", \"max_records\": 100})",
        "required": [
          "work_id"
        ]
      },
      {
        "id": "openalex_search_authors",
        "connector": "literature",
        "description": "Search OpenAlex author profiles by name. Args: query (matches display name + alternatives; expect homonyms \u2014 check affiliations/topics/ORCID), max_records (default 25, ceiling 500). Returns {query, api_total, n_records_returned, records_truncated, records}; each record {author_id, name, orcid, works_count, cited_by_count, h_index, i10_index, affiliations[{institution, years}], last_known_institutions, top_topics}. Use author_id with openalex_get_author.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "max_records": {
              "type": "integer",
              "default": 25
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "{query, api_total, n_records_returned, records_truncated, records[]} \u2014 each record the lean author shape.",
        "example": "const result = await host.mcp(\"literature\", \"openalex_search_authors\", {\"query\": \"Jennifer Doudna\", \"max_records\": 25})",
        "required": [
          "query"
        ]
      },
      {
        "id": "openalex_get_author",
        "connector": "literature",
        "description": "Fetch one OpenAlex author profile plus their top-cited works. Args: author_id (A-id, openalex.org URL, or ORCID; CAVEAT: OpenAlex's ORCID pointer can resolve to a sparse duplicate \u2014 prefer the A-id from openalex_search_authors), works_sample (default 10, max 200; 0 skips the extra request). Returns the author record plus counts_by_year, top_works_total (true total works count) and top_works (lean work records by citations).",
        "input": {
          "type": "object",
          "properties": {
            "author_id": {
              "type": "string"
            },
            "works_sample": {
              "type": "integer",
              "default": 10
            }
          },
          "required": [
            "author_id"
          ]
        },
        "returns": "The lean author shape plus {counts_by_year, top_works_total, top_works[] (lean work records by citations)}.",
        "example": "const result = await host.mcp(\"literature\", \"openalex_get_author\", {\"author_id\": \"A5023888391\", \"works_sample\": 10})",
        "required": [
          "author_id"
        ]
      },
      {
        "id": "openalex_venue_info",
        "connector": "literature",
        "description": "Look up journals/repositories ('sources') in OpenAlex \u2014 OA status, DOAJ listing, APC, citation metrics. Args: venue (exact S-id, openalex.org URL, or ISSN for a single record; anything else is a name search), max_records (default 10, ceiling 500; name-search only). Returns: exact -> one source record + counts_by_year; name search -> {query, api_total, n_records_returned, records_truncated, records}. Source record: {source_id, display_name, type, issn_l, issn, host_organization, country_code, homepage_url, is_oa, is_in_doaj, is_core, apc_usd, works_count, cited_by_count, h_index, two_year_mean_citedness, first/last_publication_year, top_topics}.",
        "input": {
          "type": "object",
          "properties": {
            "venue": {
              "type": "string"
            },
            "max_records": {
              "type": "integer",
              "default": 10
            }
          },
          "required": [
            "venue"
          ]
        },
        "returns": "Exact id -> the lean source shape plus {counts_by_year}. Name search -> {query, api_total, n_records_returned, records_truncated, records[]}.",
        "example": "const result = await host.mcp(\"literature\", \"openalex_venue_info\", {\"venue\": \"Nature\", \"max_records\": 10})",
        "required": [
          "venue"
        ]
      },
      {
        "id": "arxiv_search",
        "connector": "literature",
        "description": "Search arXiv preprints (physics, math, CS, stats, q-bio, ...) via the official Atom API. Args: query (arXiv query string; plain terms search all fields, field prefixes ti:/au:/abs: and booleans AND/OR/ANDNOT work; optional if category or a date range is set), category (arXiv code AND-ed in, e.g. q-bio.GN, cs.LG, stat.ML), date_from / date_to (submission date YYYY-MM-DD, inclusive), start (0-based paging offset; the API paces ~3s between requests \u2014 page politely), max_results (default 25, max 100 per call), sort_by (relevance default / submittedDate / lastUpdatedDate), sort_order (descending default / ascending). Returns {search_query (the exact query sent), api_total (arXiv's total match count), start_index, n_records_returned, records_truncated, sort_by, sort_order, records}; each record {arxiv_id, version, id_versioned, title, abstract, authors, published, updated, primary_category, categories, doi, journal_ref, comment, abs_url, pdf_url}. doi/journal_ref appear only after journal publication. Malformed queries raise an error (arXiv's HTTP-200 error feed is detected, never returned as data).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "category": {
              "type": "string"
            },
            "date_from": {
              "type": "string"
            },
            "date_to": {
              "type": "string"
            },
            "start": {
              "type": "integer",
              "default": 0
            },
            "max_results": {
              "type": "integer",
              "default": 25
            },
            "sort_by": {
              "type": "string",
              "enum": [
                "relevance",
                "submittedDate",
                "lastUpdatedDate"
              ],
              "default": "relevance"
            },
            "sort_order": {
              "type": "string",
              "enum": [
                "descending",
                "ascending"
              ],
              "default": "descending"
            }
          },
          "required": []
        },
        "returns": "`{ search_query, api_total, start_index, n_records_returned, records_truncated, sort_by, sort_order, records: [ { arxiv_id, version, id_versioned, title, abstract, authors, published, updated, primary_category, categories, doi, journal_ref, comment, abs_url, pdf_url } ] }` \u2014 `records_truncated` flags more matches beyond this page; `records` is `[]` when nothing matches.",
        "example": "const result = await host.mcp(\"literature\", \"arxiv_search\", {\"query\": \"ti:transformer\", \"category\": \"cs.LG\", \"max_results\": 10})",
        "required": []
      },
      {
        "id": "arxiv_get_papers",
        "connector": "literature",
        "description": "Batch-fetch arXiv paper metadata (incl. abstracts) by ID \u2014 one paced request for up to 100 papers. Args: arxiv_ids (up to 100 IDs in any common form \u2014 2103.14030, versioned 2103.14030v2, old-style q-bio/0601001, arXiv:-prefixed, or abs/pdf URLs; unversioned IDs resolve to the latest version). Returns {n_requested, n_found, duplicates (inputs that resolved to an already-returned paper), not_found (unknown AND malformed IDs \u2014 arXiv silently skips unknowns and rejects whole batches over malformed ones; this tool does neither), records} \u2014 records in requested order, same shape as arxiv_search records. Withdrawn papers still return metadata (check comment for withdrawal notes).",
        "input": {
          "type": "object",
          "properties": {
            "arxiv_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "arxiv_ids"
          ]
        },
        "returns": "`{ n_requested, n_found, duplicates: [str], not_found: [str], records: [ ...same shape as arxiv_search records ] }` \u2014 `records` in requested (deduped) order; `not_found` lists requested ids with no matching record.",
        "example": "const result = await host.mcp(\"literature\", \"arxiv_get_papers\", {\"arxiv_ids\": [\"2103.14030\", \"1706.03762v5\"]})",
        "required": [
          "arxiv_ids"
        ]
      },
      {
        "id": "crossref_get_work",
        "connector": "literature",
        "description": "Retrieve publisher-deposited bibliographic metadata for a Crossref DOI. Accepts a bare DOI, doi: prefix or doi.org URL. No API key required. A DOI registered elsewhere may return HTTP 404; use datacite_get_record for DataCite DOIs.",
        "input": {
          "type": "object",
          "properties": {
            "doi": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048
            }
          },
          "required": [
            "doi"
          ],
          "additionalProperties": false
        },
        "returns": "{ doi, title:string[], authors:[...], publisher:string|null, type:string|null, published:object|null, container_title:string[], url:string|null, license:[...], source_url }. Optional upstream bibliographic fields are null or empty arrays, not inferred.",
        "example": "const result = await host.mcp(\"literature\", \"crossref_get_work\", {\"doi\": \"10.1038/nature12968\"})",
        "required": [
          "doi"
        ]
      },
      {
        "id": "crossref_get_updates",
        "connector": "literature",
        "description": "Check Crossref-deposited corrections, retractions and other update relationships for a DOI. updated_by points to notices updating this work; update_to points to works that this DOI updates. Preserves publisher/Retraction Watch source labels and other relation types. No API key required. An empty result is not evidence that a paper is reliable or has never been retracted.",
        "input": {
          "type": "object",
          "properties": {
            "doi": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048
            }
          },
          "required": [
            "doi"
          ],
          "additionalProperties": false
        },
        "returns": "{ doi, updated_by:[{DOI,type,source?,label?,updated?,...}], update_to:[{DOI,type,source?,label?,updated?,...}], relation:object, source_url, coverage_note }. Arrays contain only deposited metadata; retain duplicate DOI notices with different sources. Does not assign an inferred retraction status.",
        "example": "const result = await host.mcp(\"literature\", \"crossref_get_updates\", {\"doi\": \"10.1038/nature12968\"})",
        "required": [
          "doi"
        ]
      },
      {
        "id": "datacite_search_records",
        "connector": "literature",
        "description": "Find public DataCite dataset/software DOI records by query and/or related DOI. query uses DataCite OpenSearch syntax; related_doi searches deposited links to a paper or other DOI; verify the exact identifier and relationship direction in related_identifiers. resource_type defaults to dataset; use software for code. Returns one bounded page (1-100 records, default 20); pass next_page with the same filters and page_size to continue. Page-number retrieval is limited to the first 10,000 records; narrow the query beyond that. Metadata and landing URLs do not guarantee downloadable files or reuse permission.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2000
            },
            "related_doi": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048
            },
            "resource_type": {
              "type": "string",
              "enum": [
                "dataset",
                "software"
              ],
              "default": "dataset"
            },
            "page_size": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            },
            "page": {
              "type": "integer",
              "minimum": 1,
              "maximum": 10000,
              "default": 1
            }
          },
          "anyOf": [
            {
              "properties": {
                "query": {}
              },
              "required": [
                "query"
              ]
            },
            {
              "properties": {
                "related_doi": {}
              },
              "required": [
                "related_doi"
              ]
            }
          ],
          "additionalProperties": false
        },
        "returns": "{ api_total, n_returned, page, page_size, next_page:number|null, records_truncated, records:[{ doi, titles:[{title,...}], creators:[{name?,...}], publisher:string|object|null, publication_year:number|null, resource_type:{resourceTypeGeneral?,...}, url:string|null, rights:[...], related_identifiers:[{relatedIdentifier?,relatedIdentifierType?,relationType?,...}], version:string|null }], source_url }. records_truncated compares this page to the total, including preceding pages. next_page=null at the end or the 10,000-record window; consult api_total and narrow the query if needed.",
        "example": "const result = await host.mcp(\"literature\", \"datacite_search_records\", {\"query\": \"climate\", \"resource_type\": \"dataset\", \"page_size\": 5})",
        "required": []
      },
      {
        "id": "datacite_get_record",
        "connector": "literature",
        "description": "Retrieve a public DataCite DOI record, including dataset/software identity, creators, rights, version, landing URL and registered publication/data relationships. Accepts a bare DOI, doi: prefix or doi.org URL. No API key required. Metadata does not guarantee access to files; HTTP 404 may mean the DOI is private, unknown or registered elsewhere.",
        "input": {
          "type": "object",
          "properties": {
            "doi": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2048
            }
          },
          "required": [
            "doi"
          ],
          "additionalProperties": false
        },
        "returns": "{ record:{ doi, titles:[{title,...}], creators:[{name?,...}], publisher:string|object|null, publication_year:number|null, resource_type:{resourceTypeGeneral?,...}, url:string|null, rights:[...], related_identifiers:[{relatedIdentifier?,relatedIdentifierType?,relationType?,...}], version:string|null }, source_url }. Preserves upstream relatedIdentifierType and relationType; absent optional fields are null or empty arrays.",
        "example": "const result = await host.mcp(\"literature\", \"datacite_get_record\", {\"doi\": \"10.14454/qdd3-ps68\"})",
        "required": [
          "doi"
        ]
      }
    ]
  },
  {
    "id": "molecule",
    "displayName": "Molecule Viewer",
    "description": "Validate and preview 2D molecular structures and reactions (OpenChemLib). Backs the .mol/.sdf/.smi/.smiles/.rxn artifact viewer.",
    "useWhen": "Use when the user provides or wants to inspect a chemical structure \u2014 validating or normalizing a SMILES or MDL molfile, computing a molecular formula / weight / heavy-atom count, or turning a structure into a previewable 2D depiction. The paired viewer also renders MDL reaction (.rxn) files. Self-contained: pass a SMILES or molfile directly, no other connector required. Sourced from OpenChemLib (offline, in-app).",
    "sources": [
      "OpenChemLib"
    ],
    "requiresNcbi": false,
    "tools": [
      {
        "id": "render_molecule",
        "connector": "molecule",
        "description": "Validate and normalize a 2D chemical structure with OpenChemLib. Pass a `smiles` string or a `molfile` (MDL molblock); returns a canonical molfile plus formula, molecular weight and heavy-atom count. Save the returned `molfile` as a .mol artifact (write_artifact_file) to preview it, or use `preview_molecule` to do both in one call.",
        "input": {
          "type": "object",
          "properties": {
            "smiles": {
              "type": "string",
              "description": "A SMILES string, e.g. \"CC(=O)Oc1ccccc1C(=O)O\"."
            },
            "molfile": {
              "type": "string",
              "description": "An MDL molfile (V2000/V3000 molblock)."
            },
            "filename": {
              "type": "string",
              "description": "Optional base name for the saved artifact filename, e.g. \"aspirin\"."
            }
          }
        },
        "returns": "`{ \"valid\": bool, \"molfile\": str, \"smiles\": str, \"formula\": str, \"molecular_weight\": float, \"heavy_atom_count\": int, \"filename_suggestion\": str }` on success. On an unparseable structure: `{ \"valid\": false, \"error\": str }`. `molfile` is the canonical MDL molblock; `smiles` is the canonical SMILES; `molecular_weight` is the average (relative) weight; `heavy_atom_count` excludes implicit hydrogens.",
        "example": "const result = await host.mcp(\"molecule\", \"render_molecule\", {\"smiles\": \"CC(=O)Oc1ccccc1C(=O)O\", \"filename\": \"aspirin\"})",
        "required": []
      },
      {
        "id": "preview_molecule",
        "connector": "molecule",
        "description": "Validate a 2D chemical structure and open it in the preview panel in one call. Pass a `smiles` or a `molfile`; the structure is saved as a canonical .mol artifact this turn and rendered read-only with OpenChemLib. Returns the saved artifact id. Call it during an assistant turn (the file is attached to the current turn).",
        "input": {
          "type": "object",
          "properties": {
            "smiles": {
              "type": "string",
              "description": "A SMILES string, e.g. \"CC(=O)Oc1ccccc1C(=O)O\"."
            },
            "molfile": {
              "type": "string",
              "description": "An MDL molfile (V2000/V3000 molblock)."
            },
            "filename": {
              "type": "string",
              "description": "Optional base name for the saved artifact filename, e.g. \"aspirin\"."
            }
          }
        },
        "returns": "`{ \"valid\": bool, \"artifact_id\": str, \"version_id\": str, \"version_number\": int, \"filename\": str, \"smiles\": str, \"formula\": str, \"molecular_weight\": float, \"heavy_atom_count\": int }` on success. `artifact_id` identifies the stable Artifact lineage; `version_id` identifies the immutable saved Version. On an unparseable structure: `{ \"valid\": false, \"error\": str }`. The saved .mol artifact opens automatically in the preview panel.",
        "example": "const result = await host.mcp(\"molecule\", \"preview_molecule\", {\"smiles\": \"CC(=O)Oc1ccccc1C(=O)O\", \"filename\": \"aspirin\"})",
        "required": []
      }
    ]
  },
  {
    "id": "pubmed",
    "displayName": "PubMed",
    "aliases": [
      "NCBI literature",
      "PMC",
      "Europe PMC"
    ],
    "description": "Biomedical literature via NCBI E-utilities, the PMC ID Converter and Europe PMC \u2014 search, metadata, related articles, citation lookup, ID conversion, full text and copyright.",
    "useWhen": "Use to search the biomedical literature and retrieve article metadata (authors, abstract, DOIs, MeSH), find related/similar articles, resolve citations to PMIDs, convert between PMID/PMCID/DOI, fetch open-access full text from PubMed Central, or check copyright/license status. Sourced from PubMed (NCBI), PMC and Europe PMC.",
    "sources": [
      "PubMed",
      "PMC",
      "Europe PMC"
    ],
    "termsUrl": "https://www.ncbi.nlm.nih.gov/home/about/policies/",
    "requiresNcbi": true,
    "group": "directory",
    "tools": [
      {
        "id": "search_articles",
        "connector": "pubmed",
        "description": "Search PubMed (biomedical & life-sciences literature via NCBI esearch) for articles matching a query. Returns the total match count plus a page of PMIDs. Supports PubMed field tags ([Title], [Author], [Journal], [MeSH Terms], ...), Boolean operators, date filtering and sort. PubMed does not index physics / CS / math / pure-chemistry papers.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "PubMed query (keywords, field tags, Boolean)."
            },
            "max_results": {
              "type": "integer",
              "default": 20
            },
            "retstart": {
              "type": "integer",
              "default": 0
            },
            "sort": {
              "type": "string",
              "enum": [
                "relevance",
                "pub_date",
                "author",
                "journal_name",
                "title"
              ]
            },
            "date_from": {
              "type": "string",
              "description": "YYYY, YYYY/MM or YYYY/MM/DD."
            },
            "date_to": {
              "type": "string",
              "description": "YYYY, YYYY/MM or YYYY/MM/DD."
            },
            "datetype": {
              "type": "string",
              "enum": [
                "pdat",
                "edat",
                "mdat"
              ],
              "default": "pdat"
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ \"pmids\": [str], \"total_count\": int, \"returned_count\": int, \"query\": str, \"query_translation\": str|null, \"has_more\": bool }` \u2014 `pmids` is one page (`max_results`, default 20) starting at `retstart`; `total_count` is the full PubMed match count. Feed PMIDs to `get_article_metadata`.",
        "example": "const result = await host.mcp(\"pubmed\", \"search_articles\", {\"query\": \"CRISPR gene editing\", \"max_results\": 10})",
        "required": [
          "query"
        ]
      },
      {
        "id": "get_article_metadata",
        "connector": "pubmed",
        "description": "Retrieve detailed article metadata from PubMed by PMID (bulk, via efetch): identifiers (pmid/pmc/doi), title, abstract, journal, authors with affiliations, publication date, MeSH terms, article types, language and citation. On every use, cite PubMed and include the returned article DOIs (identifiers.doi) as links.",
        "input": {
          "type": "object",
          "properties": {
            "pmids": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              },
              "description": "One PubMed ID or a list."
            }
          },
          "required": [
            "pmids"
          ]
        },
        "returns": "`{ \"articles\": [ { \"identifiers\": {\"pmid\",\"pmc\"?,\"doi\"?}, \"title\", \"abstract\", \"doi\"?, \"journal\": {\"title\",\"iso_abbreviation\"}, \"authors\": [{\"last_name\"?,\"fore_name\"?,\"initials\"?,\"collective_name\"?,\"affiliations\":[str]}], \"publication_date\": {\"year\"?,\"month\"?,\"day\"?}, \"mesh_terms\": [str], \"article_types\": [str], \"language\", \"citation\": {\"volume\"?,\"issue\"?,\"pages\"?} } ], \"count\": int, \"important_legal_notice\": str }` \u2014 one article per requested PMID present in PubMed (input order).",
        "example": "const result = await host.mcp(\"pubmed\", \"get_article_metadata\", {\"pmids\": [\"35486828\", \"33264437\"]})",
        "required": [
          "pmids"
        ]
      },
      {
        "id": "find_related_articles",
        "connector": "pubmed",
        "description": "Find related PubMed content for one or more source PMIDs via NCBI elink. `pubmed_pubmed` (default) returns similar articles ranked by word-weighted similarity of titles/abstracts/MeSH (NOT citations); `pubmed_pmc` returns full-text PMC links; `pubmed_gene`/`pubmed_protein`/`pubmed_nucleotide` return linked sequence/gene records.",
        "input": {
          "type": "object",
          "properties": {
            "pmids": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              }
            },
            "link_type": {
              "type": "string",
              "enum": [
                "pubmed_pubmed",
                "pubmed_pmc",
                "pubmed_nucleotide",
                "pubmed_protein",
                "pubmed_gene"
              ],
              "default": "pubmed_pubmed"
            },
            "max_results": {
              "type": "integer",
              "description": "Cap linked ids per linkset."
            }
          },
          "required": [
            "pmids"
          ]
        },
        "returns": "`{ \"linksets\": [ { \"dbfrom\": \"pubmed\", \"ids\": [str], \"linksetdbs\": [ { \"dbto\": str, \"linkname\": str, \"links\": [str] } ] } ] }` \u2014 one linkset per input PMID; `links` are related ids, relevance-ranked for `pubmed_pubmed`, truncated to `max_results` when given.",
        "example": "const result = await host.mcp(\"pubmed\", \"find_related_articles\", {\"pmids\": [\"35486828\"], \"link_type\": \"pubmed_pubmed\"})",
        "required": [
          "pmids"
        ]
      },
      {
        "id": "lookup_article_by_citation",
        "connector": "pubmed",
        "description": "Resolve bibliographic citations to PMIDs via NCBI ecitmatch. Each citation supplies some of {journal, year, volume, first_page, author, key}; provide 2-3+ fields for reliable matching. Use when you have a reference list and need PMIDs.",
        "input": {
          "type": "object",
          "properties": {
            "citations": {
              "type": "array",
              "items": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "journal": {
                    "type": "string"
                  },
                  "year": {
                    "type": "integer"
                  },
                  "volume": {
                    "type": "string"
                  },
                  "first_page": {
                    "type": "string"
                  },
                  "author": {
                    "type": "string"
                  },
                  "key": {
                    "type": "string",
                    "description": "Optional caller-side tracking id."
                  }
                }
              }
            }
          },
          "required": [
            "citations"
          ]
        },
        "returns": "`{ \"citations\": [ { \"journal\", \"year\", \"volume\", \"first_page\", \"author\", \"pmid\": str|null, \"key\": str, \"status\"?: \"not_found\"|\"ambiguous\"|..., \"detail\"? } ] }` \u2014 one entry per input citation (order preserved); `pmid` is set on a unique match, otherwise `status`/`detail` explain why.",
        "example": "const result = await host.mcp(\"pubmed\", \"lookup_article_by_citation\", {\"citations\": [{\"journal\": \"Science\", \"year\": 1987, \"volume\": \"235\", \"first_page\": \"182\", \"author\": \"Palmenberg AC\"}]})",
        "required": [
          "citations"
        ]
      },
      {
        "id": "convert_article_ids",
        "connector": "pubmed",
        "description": "Convert between PMID, PMCID and DOI via the NCBI/PMC ID Converter. Homogeneous input ids per call (set `id_type` to match). Commonly used to check whether a PMID has a PMCID (i.e. full text in PMC) before calling get_full_text_article.",
        "input": {
          "type": "object",
          "properties": {
            "ids": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              }
            },
            "id_type": {
              "type": "string",
              "enum": [
                "pmid",
                "pmcid",
                "doi"
              ],
              "default": "pmid"
            }
          },
          "required": [
            "ids"
          ]
        },
        "returns": "`{ \"status\": \"ok\", \"response-date\": str, \"request\": {...}, \"records\": [ { \"pmcid\": str|null, \"pmid\": str|null, \"doi\": str|null, \"requested-id\": str, \"status\"?: \"error\", \"errmsg\"? } ] }` \u2014 one record per input id (order preserved); missing identifiers are explicit null. `status=\"error\"` with \"not found in PMC\" is the normal outcome for a PMID with no PMC deposit.",
        "example": "const result = await host.mcp(\"pubmed\", \"convert_article_ids\", {\"ids\": [\"PMC9046468\"], \"id_type\": \"pmcid\"})",
        "required": [
          "ids"
        ]
      },
      {
        "id": "get_full_text_article",
        "connector": "pubmed",
        "description": "Retrieve open-access full text from PubMed Central via Europe PMC by PMC id (\"PMC12345\" or \"12345\"). Returns structured section text plus the license; when full text is unavailable the reason is reported explicitly (fulltext_status). Only OA-subset articles have retrievable full text. On every use, cite PubMed and include the returned article DOIs as links.",
        "input": {
          "type": "object",
          "properties": {
            "pmc_ids": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              },
              "description": "One PMC ID or a list (max 20)."
            }
          },
          "required": [
            "pmc_ids"
          ]
        },
        "returns": "`{ \"important_legal_notice\": str, \"articles\": [ { \"identifiers\": {\"pmcid\"?,\"pmid\"?,\"doi\"?}, \"title\", \"full_text\": str, \"license\"?, \"doi\"?, \"abstract\"?, \"fulltext_status\"?: str, \"detail\"? } ], \"count\": int }` \u2014 `full_text` is the section text joined by blank lines; `fulltext_status` (present when not \"retrieved\") is one of not_open_access / no_pmcid / xml_not_available / not_found / invalid_id.",
        "example": "const result = await host.mcp(\"pubmed\", \"get_full_text_article\", {\"pmc_ids\": [\"PMC9046468\"]})",
        "required": [
          "pmc_ids"
        ]
      },
      {
        "id": "get_copyright_status",
        "connector": "pubmed",
        "description": "Report copyright and license status per PMID by combining PubMed CopyrightInformation, the PMC ID Converter (PMID -> PMCID/DOI), and the PMC <permissions> block (license type, ALI license URL, copyright statement/year). Use to check open-access reuse rights before reproducing content.",
        "input": {
          "type": "object",
          "properties": {
            "pmids": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "pmids"
          ]
        },
        "returns": "`{ \"results\": [ { \"pmid\", \"pmc_id\": str|null, \"copyright\": {\"statement\",\"year\",\"holder\"}, \"license\": {\"type\",\"url\",\"is_open_access\"}, \"source\": \"pmc\"|\"pubmed\"|\"not_available\", \"checked_sources\": [str], \"available_at\": {\"pubmed_url\",\"pmc_url\"?,\"doi_url\"?} } ], \"count\": int, \"summary\": {\"total_checked\",\"found_in_pubmed\",\"found_in_pmc\",\"not_found\",\"open_access_count\"} }` \u2014 one result per input PMID.",
        "example": "const result = await host.mcp(\"pubmed\", \"get_copyright_status\", {\"pmids\": [\"35891187\", \"34375400\"]})",
        "required": [
          "pmids"
        ]
      }
    ]
  },
  {
    "id": "genes",
    "displayName": "Genes & Ontologies",
    "aliases": [
      "MyGene",
      "mygene.info",
      "UniProt",
      "gene information",
      "gene annotation"
    ],
    "description": "Gene/protein identity and ontology terms \u2014 mygene.info, UniProt, OLS4 ontologies, GO annotations, Reactome pathways.",
    "useWhen": "Use when you need to resolve gene symbols/identifiers (mygene.info), fetch UniProt protein records, look up or search ontology terms (EFO, GO, CL, ChEBI, MONDO via OLS4), retrieve GO annotations for a protein (QuickGO), or map genes to Reactome pathways.",
    "sources": [
      "MyGene",
      "UniProt",
      "OLS",
      "QuickGO",
      "Reactome"
    ],
    "termsUrl": "https://www.uniprot.org/help/license",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "query_genes",
        "connector": "genes",
        "description": "Resolve gene identifiers/symbols via mygene.info (batched, up to 1000 terms/request). Use this to map gene symbols to Ensembl gene IDs, Entrez IDs, names, and any other mygene.info field \u2014 or the reverse (set `scopes` to the namespace of your input terms, e.g. \"entrezgene\", \"ensembl.gene\", \"symbol,alias\"). Args: terms (query terms, e.g. [\"TP53\",\"BRCA1\"]; terms containing commas are not supported); scopes (comma-separated identifier namespaces to match terms against); fields (comma-separated mygene fields to return, or \"all\"); species (common name \"human\"/\"mouse\" or NCBI taxid). Returns {n_input, n_records, not_found, records}. A term matching several genes yields several records (each carries its `query`). Records are deterministically ordered (input order, then _id).",
        "input": {
          "type": "object",
          "properties": {
            "terms": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "scopes": {
              "type": "string"
            },
            "fields": {
              "type": "string",
              "default": "symbol,name,taxid,entrezgene,ensembl.gene"
            },
            "species": {
              "type": "string"
            }
          },
          "required": [
            "terms"
          ]
        },
        "returns": "{n_input, n_records, not_found:[terms with no match], records:[mygene hit objects, each with `query`, `_id` and the requested fields]} \u2014 records ordered by input position of `query`, then `_id`.",
        "example": "const result = await host.mcp(\"genes\", \"query_genes\", {\"terms\": [\"TP53\", \"BRCA1\"], \"scopes\": \"symbol,alias\", \"fields\": \"symbol,name,entrezgene,ensembl.gene\", \"species\": \"human\"})",
        "required": [
          "terms"
        ]
      },
      {
        "id": "list_ontologies",
        "connector": "genes",
        "description": "List ontologies in the EBI Ontology Lookup Service (OLS4). With `ontology_ids` (e.g. [\"efo\",\"cl\",\"chebi\",\"go\",\"mondo\"]): fetch structured metadata records for just those ontologies; unknown IDs are reported in `not_found`. Without: the complete OLS4 catalogue (~250 ontologies, paginated fully and count-verified). Returns: {records:[{ontology_id, title, version, status, num_terms, ...}], not_found:[...]} for an ID list, or {records:[...], total_elements, complete} for the full catalogue.",
        "input": {
          "type": "object",
          "properties": {
            "ontology_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        },
        "returns": "ID list -> {records:[{ontology_id, title, version, status, num_terms, num_properties, num_individuals, preferred_prefix, description, namespace}], not_found:[...]}; full catalogue -> {records:[...], total_elements, complete}.",
        "example": "const result = await host.mcp(\"genes\", \"list_ontologies\", {\"ontology_ids\": [\"efo\", \"go\", \"mondo\"]})",
        "required": []
      },
      {
        "id": "search_ontology_terms",
        "connector": "genes",
        "description": "Search ontology terms by label/synonym across one or more OLS4 ontologies. Typical uses: find an EFO ID for a disease name (ontologies=[\"efo\"]), Cell Ontology terms for a cell type ([\"cl\"]), ChEBI terms for a chemical ([\"chebi\"]), GO terms by name ([\"go\"]) \u2014 or search all ontologies at once. Args: query (term label, synonym, or identifier); ontologies (lowercase IDs to restrict to; None searches every ontology); exact (whole-string match); include_obsolete (default False); max_results (ranked by OLS relevance). Returns {query, total_found, n_returned, truncated, terms:[{curie, iri, label, short_form, ontology, description, type, is_defining_ontology}]}.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "ontologies": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "exact": {
              "type": "boolean",
              "default": false
            },
            "include_obsolete": {
              "type": "boolean",
              "default": false
            },
            "max_results": {
              "type": "integer",
              "default": 20
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "{query, total_found (numFound), n_returned, truncated (total_found > n_returned), terms:[{curie, iri, label, short_form, ontology, description, type, is_defining_ontology}]}.",
        "example": "const result = await host.mcp(\"genes\", \"search_ontology_terms\", {\"query\": \"asthma\", \"ontologies\": [\"efo\"], \"max_results\": 20})",
        "required": [
          "query"
        ]
      },
      {
        "id": "get_ontology_term",
        "connector": "genes",
        "description": "Fetch one ontology term's details, or its complete related-term set. With `relation=None`: full term record (label, synonyms, description, obsolete flag, direct parents). With a relation: the COMPLETE, fully paginated set of related terms \u2014 e.g. relation=\"hierarchicalChildren\" for direct children incl. part_of etc., \"descendants\"/\"hierarchicalDescendants\" for the whole subtree, \"ancestors\"/\"hierarchicalAncestors\", \"parents\", \"children\". Retrieval is count-verified against the API's own total. Args: ontology (lowercase, e.g. \"efo\",\"go\",\"cl\",\"chebi\"); term_id (CURIE \"EFO:0000305\"/\"GO:0006281\" or full IRI); relation (None or one of the listed); include_parents (include direct parent refs when relation is None). Returns: relation=None {curie, iri, label, ontology, short_form, synonyms, description, is_obsolete, has_children, parents}; otherwise {root, relation, total_elements, term_count, terms:[...]}.",
        "input": {
          "type": "object",
          "properties": {
            "ontology": {
              "type": "string"
            },
            "term_id": {
              "type": "string"
            },
            "relation": {
              "type": "string",
              "enum": [
                "parents",
                "children",
                "ancestors",
                "descendants",
                "hierarchicalParents",
                "hierarchicalChildren",
                "hierarchicalAncestors",
                "hierarchicalDescendants"
              ]
            },
            "include_parents": {
              "type": "boolean",
              "default": false
            }
          },
          "required": [
            "ontology",
            "term_id"
          ]
        },
        "returns": "relation=None -> {curie, iri, label, ontology, short_form, synonyms, description, is_obsolete, has_children, parents?}; with a relation -> {root, relation, total_elements, term_count, terms:[{curie, iri, label, short_form, ontology, has_children}]}.",
        "example": "const result = await host.mcp(\"genes\", \"get_ontology_term\", {\"ontology\": \"go\", \"term_id\": \"GO:0006281\", \"relation\": \"children\"})",
        "required": [
          "ontology",
          "term_id"
        ]
      },
      {
        "id": "get_go_annotations",
        "connector": "genes",
        "description": "Retrieve GO annotations for a UniProt gene product from QuickGO (complete, count-verified). Args: uniprot_accession (e.g. \"P04637\", prefix optional); aspect (omit for all aspects, or one of biological_process/molecular_function/cellular_component); evidence (None/all, a preset \"experimental_manual\"=manually-assigned experimental evidence, \"automatic_iea\"=electronic/IEA, or an explicit ECO code like \"ECO:0000314\"; three-letter GO evidence codes like IDA/IEA are NOT accepted \u2014 QuickGO silently ignores goEvidence, filter must use ECO codes); taxon_id (optional NCBI taxon, e.g. 9606); include_term_names (hydrate each record with GO term name/aspect/obsolete via one batched ontology lookup); max_records (cap on records; full set still retrieved and summarized; `truncated` flags the cap). Returns {gene_product, total_annotations, n_records, complete, truncated, distinct_go_ids (across ALL annotations), records:[{go_id, go_aspect, qualifier, go_evidence, eco_id, reference, assigned_by, date, ...}]}.",
        "input": {
          "type": "object",
          "properties": {
            "uniprot_accession": {
              "type": "string"
            },
            "aspect": {
              "type": "string",
              "enum": [
                "biological_process",
                "molecular_function",
                "cellular_component"
              ],
              "description": "Restrict to one GO aspect; omit for all aspects."
            },
            "evidence": {
              "type": "string",
              "description": "Evidence filter: a preset (\"experimental_manual\" = manually-assigned experimental, \"automatic_iea\" = electronic/IEA) or an explicit ECO code (e.g. \"ECO:0000314\"); omit for all. Three-letter GO codes (IDA/IEA) are not accepted \u2014 filter by ECO code."
            },
            "taxon_id": {
              "type": "integer"
            },
            "include_term_names": {
              "type": "boolean",
              "default": false
            },
            "max_records": {
              "type": "integer",
              "default": 200
            }
          },
          "required": [
            "uniprot_accession"
          ]
        },
        "returns": "{gene_product, total_annotations (numberOfHits), n_records, complete (full set retrieved), truncated (records capped), distinct_go_ids:[...], records:[{go_id, go_aspect, qualifier, go_evidence, eco_id, reference, assigned_by, date, taxon_id, symbol, with_from, go_name?, go_obsolete?}]}.",
        "example": "const result = await host.mcp(\"genes\", \"get_go_annotations\", {\"uniprot_accession\": \"P04637\", \"aspect\": \"molecular_function\", \"evidence\": \"experimental_manual\"})",
        "required": [
          "uniprot_accession"
        ]
      },
      {
        "id": "get_uniprot_entries",
        "connector": "genes",
        "description": "Fetch UniProtKB records for a list of accessions (batched OR-queries, not per-accession). Three modes: `fields` given \u2192 token-lean tabular retrieval of just those UniProt fields (e.g. [\"accession\",\"id\",\"protein_name\",\"gene_names\",\"organism_name\",\"length\",\"sequence\"]); `format` is ignored. format=\"fasta\" \u2192 per-accession FASTA sequences. format=\"txt\" \u2192 per-accession full UniProt flat-file text (complete annotation; can be very large \u2014 prefer `fields`). Args: accessions (e.g. [\"P04637\",\"P38398\"]); format (\"fasta\"/\"txt\", ignored when `fields` given); fields (optional UniProt REST field names for tabular mode). Returns: fields mode {accessions, fields, n_records, records:[{<column>:value}]}; fasta/txt mode {accessions, format, n_found, missing, records:{accession:text}} \u2014 `missing` lists accessions UniProt returned no record for.",
        "input": {
          "type": "object",
          "properties": {
            "accessions": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "format": {
              "type": "string",
              "enum": [
                "fasta",
                "txt"
              ]
            },
            "fields": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "accessions"
          ]
        },
        "returns": "fields mode {accessions, fields, n_records, records:[{<column>:value}]} (columns are the UniProt TSV headers); fasta/txt mode {accessions, format, n_found, missing:[...], records:{accession:text}}.",
        "example": "const result = await host.mcp(\"genes\", \"get_uniprot_entries\", {\"accessions\": [\"P04637\", \"P38398\"], \"fields\": [\"accession\", \"id\", \"protein_name\", \"gene_names\", \"organism_name\", \"length\"]})",
        "required": [
          "accessions"
        ]
      },
      {
        "id": "map_reactome_pathways",
        "connector": "genes",
        "description": "Map gene symbols or UniProt accessions to Reactome pathways (AnalysisService token workflow). Args: identifiers (gene symbols if id_type=\"symbol\", UniProt accessions if \"uniprot\"; no duplicates); id_type (\"symbol\"/\"uniprot\"); species (default \"Homo sapiens\"); resource (AnalysisService molecule-resource view \"TOTAL\" default; \"UNIPROT\" restricts to protein-level mappings); include_disease (service default True); compact (True \u2192 per-identifier low-level pathways only {stId,name,species} + reactome release version; False \u2192 full deterministic result: per-identifier complete pathway sets with entity/reaction statistics (p-values, FDR, found/total) and batch summary incl. identifiers_not_found). Returns: compact {tool, reactome_version, id_type, species, n_input, genes:{identifier:{found, n_lowlevel_pathways, pathways}}}; full adds per-pathway statistics and batch_summary.",
        "input": {
          "type": "object",
          "properties": {
            "identifiers": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "id_type": {
              "type": "string",
              "enum": [
                "symbol",
                "uniprot"
              ]
            },
            "species": {
              "type": "string",
              "default": "Homo sapiens"
            },
            "resource": {
              "type": "string",
              "default": "TOTAL"
            },
            "include_disease": {
              "type": "boolean",
              "default": true
            },
            "compact": {
              "type": "boolean",
              "default": true
            }
          },
          "required": [
            "identifiers",
            "id_type"
          ]
        },
        "returns": "compact {tool, reactome_version, id_type, species, resource, include_disease, n_input, genes:{identifier:{found, n_lowlevel_pathways, pathways:[{stId,name,species}]}}}; full replaces each pathways[] with full stats {stId,name,species,low_level,in_disease,entities:{total,found,ratio,p_value,fdr},reactions:{total,found,ratio}} and adds batch_summary {n_input, n_found, n_not_found, identifiers_not_found, distinct_lowlevel_pathways, batch_pathways_found}.",
        "example": "const result = await host.mcp(\"genes\", \"map_reactome_pathways\", {\"identifiers\": [\"TP53\", \"EGFR\", \"BRCA1\"], \"id_type\": \"symbol\"})",
        "required": [
          "identifiers",
          "id_type"
        ]
      }
    ]
  },
  {
    "id": "genomes",
    "displayName": "Genomes",
    "description": "Genome annotation, variants, homology, sequence and browser tracks \u2014 Ensembl REST and the UCSC Genome Browser.",
    "useWhen": "Use when you need Ensembl gene/transcript annotation, cross-references, VEP variant consequences, orthologues/paralogues, sequence, or region overlaps \u2014 or UCSC Genome Browser tracks, track data, conservation scores, TFBS clusters and chromosome sizes.",
    "sources": [
      "Ensembl",
      "UCSC"
    ],
    "termsUrl": "https://www.ensembl.org/info/about/legal/disclaimer.html",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "ensembl_lookup",
        "connector": "genomes",
        "description": "Look up an Ensembl gene/transcript/protein by stable ID or a gene by symbol; returns the core annotation record (location, biotype, canonical transcript, description). Args: query (Ensembl stable ID ENSG.../ENST.../ENSP..., versioned accepted; or a gene symbol/alias like BRAF \u2014 true stable IDs [ENS + optional species code + feature letter + >=6-digit block, or LRG_N] route to the ID endpoint; everything else, incl. symbols starting with \"ENS\" like ENSA, to the symbol endpoint); species (Ensembl species name for symbol lookups, default homo_sapiens; ignored for stable IDs); expand (include the child feature tree \u2014 a gene's transcripts/exons/translation; default off). Returns {found, query, species, record}; record is null when nothing matches, else the upstream lookup dict \u2014 for a gene {id, display_name, description, biotype, object_type, seq_region_name, start, end, strand, assembly_name, canonical_transcript, version, ...} with 1-based inclusive coordinates.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "species": {
              "type": "string",
              "default": "homo_sapiens"
            },
            "expand": {
              "type": "boolean",
              "default": false
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "{found, query, species, record} \u2014 record is the upstream lookup dict (1-based inclusive coords) or null when nothing matches.",
        "example": "const result = await host.mcp(\"genomes\", \"ensembl_lookup\", {\"query\": \"BRAF\"})",
        "required": [
          "query"
        ]
      },
      {
        "id": "ensembl_xrefs",
        "connector": "genomes",
        "description": "External cross-references of an Ensembl stable ID \u2014 the bridge from Ensembl gene/transcript IDs to HGNC, NCBI (EntrezGene), UniProt, OMIM, RefSeq, Expression Atlas and others. Args: stable_id (ENSG.../ENST..., versioned accepted); external_db (optional exact upstream database-name filter, e.g. HGNC, EntrezGene, Uniprot_gn, MIM_GENE, RefSeq_mRNA; omit for all). Returns {stable_id, external_db, n_xrefs, xrefs} \u2014 the COMPLETE list (never truncated), sorted by (dbname, primary_id); each row {dbname, db_display_name, primary_id, display_id, description, synonyms, info_type}. Unknown IDs return n_xrefs:0.",
        "input": {
          "type": "object",
          "properties": {
            "stable_id": {
              "type": "string"
            },
            "external_db": {
              "type": "string"
            }
          },
          "required": [
            "stable_id"
          ]
        },
        "returns": "{stable_id, external_db, n_xrefs, xrefs:[{dbname, db_display_name, primary_id, display_id, description, synonyms, info_type}]} sorted by (dbname, primary_id); unknown id -> n_xrefs:0.",
        "example": "const result = await host.mcp(\"genomes\", \"ensembl_xrefs\", {\"stable_id\": \"ENSG00000157764\", \"external_db\": \"HGNC\"})",
        "required": [
          "stable_id"
        ]
      },
      {
        "id": "ensembl_vep_variant",
        "connector": "genomes",
        "description": "Predict variant consequences with Ensembl VEP \u2014 most-severe-first summary of the (often huge) per-transcript consequence list. Pass EITHER variant_id OR region+allele. Args: variant_id (dbSNP rsID rs7412, COSMIC COSV..., or HGMD ID); region (GRCh38 1-based inclusive chrom:start-end, e.g. 7:140753336-140753336; SNV start==end; insertion start=end+1; explicit strand suffix :1/:-1 accepted); allele (variant allele on forward strand for the region route, e.g. T or - for deletion); species (default homo_sapiens); max_consequences (cap on returned per-transcript rows, default 25; full count in n_transcript_consequences, rows kept are most severe HIGH>MODERATE>LOW>MODIFIER; transcript_consequences_truncated flags the cap). Returns {query, n_results, results:[{input, assembly_name, seq_region_name, start, end, strand, allele_string, most_severe_consequence, genes:[{gene_id, gene_symbol, worst_impact, n_transcripts}], n_transcript_consequences, transcript_consequences_truncated, transcript_consequences:[...], n_regulatory_feature_consequences, n_motif_feature_consequences, colocated_variants:[...]}]}. Unknown rsIDs raise with the upstream message.",
        "input": {
          "type": "object",
          "properties": {
            "variant_id": {
              "type": "string"
            },
            "region": {
              "type": "string"
            },
            "allele": {
              "type": "string"
            },
            "species": {
              "type": "string",
              "default": "homo_sapiens"
            },
            "max_consequences": {
              "type": "integer",
              "default": 25
            }
          }
        },
        "returns": "{query, n_results, results[]} \u2014 each result the most-severe-first VEP summary (per-transcript rows sorted HIGH>MODERATE>LOW>MODIFIER and capped, plus per-gene worst impact and colocated variants).",
        "example": "const result = await host.mcp(\"genomes\", \"ensembl_vep_variant\", {\"variant_id\": \"rs7412\", \"max_consequences\": 25})",
        "required": []
      },
      {
        "id": "ensembl_homology",
        "connector": "genomes",
        "description": "Orthologues or paralogues of a gene from Ensembl Compara (condensed rows \u2014 no alignments/sequences). Args: gene_symbol (resolved to a stable ID in `species` first; pass exactly one of gene_symbol/gene_id); gene_id (ENSG...); homology_type (orthologues default/paralogues/projections); target_species (restrict to one species); target_taxon (NCBI taxon subtree, e.g. 9443 Primates; combinable with target_species, OR semantics); species (source species, default homo_sapiens); max_homologies (row cap default 200; n_total carries the complete count, homologies_truncated flags the cap). Returns {gene_id, gene_symbol, species, homology_type, target_species, target_taxon, n_total, homologies_truncated, homologies}; rows sorted by (species,id) {type, species, id, protein_id, taxonomy_level, method_link_type}. Quirk: the /homology/symbol route stalls \u2014 this tool always resolves symbols itself and queries by stable ID.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string"
            },
            "gene_id": {
              "type": "string"
            },
            "homology_type": {
              "type": "string",
              "enum": [
                "orthologues",
                "paralogues",
                "projections"
              ],
              "default": "orthologues"
            },
            "target_species": {
              "type": "string"
            },
            "target_taxon": {
              "type": "integer"
            },
            "species": {
              "type": "string",
              "default": "homo_sapiens"
            },
            "max_homologies": {
              "type": "integer",
              "default": 200
            }
          }
        },
        "returns": "{gene_id, gene_symbol, species, homology_type, target_species, target_taxon, n_total, homologies_truncated, homologies:[{type, species, id, protein_id, taxonomy_level, method_link_type}]} sorted by (species,id).",
        "example": "const result = await host.mcp(\"genomes\", \"ensembl_homology\", {\"gene_symbol\": \"BRAF\", \"target_species\": \"mus_musculus\"})",
        "required": []
      },
      {
        "id": "ensembl_sequence",
        "connector": "genomes",
        "description": "Fetch sequence from Ensembl \u2014 by stable ID (gene/transcript/protein) or by genomic region. Pass EITHER stable_id OR region. Args: stable_id (ENSG.../ENST.../ENSP..., versioned accepted); region (1-based inclusive chrom:start..end or chrom:start-end, GRCh38 for human, max 10Mb); species (for region route, default homo_sapiens; ignored for stable IDs); seq_type (ID route: genomic default/cdna/cds/protein \u2014 protein only for ENST/ENSP; ignored for regions which always return genomic); max_bytes (payload guard default 400000 \u2014 larger sequences have `seq` omitted; length/sha256/metadata always returned; re-call with larger max_bytes for full text). Returns {found, query, seq_type, id, description, molecule, length, sha256, seq} \u2014 length in the unit implied by molecule (bases for dna, residues for protein); seq replaced by seq_omitted when capped; found:false with null fields for unknown stable IDs; malformed/oversized regions raise with the upstream message.",
        "input": {
          "type": "object",
          "properties": {
            "stable_id": {
              "type": "string"
            },
            "region": {
              "type": "string"
            },
            "species": {
              "type": "string",
              "default": "homo_sapiens"
            },
            "seq_type": {
              "type": "string",
              "enum": [
                "genomic",
                "cdna",
                "cds",
                "protein"
              ],
              "default": "genomic"
            },
            "max_bytes": {
              "type": "integer",
              "default": 400000
            }
          }
        },
        "returns": "{found, query, seq_type, id, description, molecule, length, sha256, seq} \u2014 seq replaced by seq_omitted:true when byte length exceeds max_bytes; found:false with null fields for unknown stable IDs.",
        "example": "const result = await host.mcp(\"genomes\", \"ensembl_sequence\", {\"stable_id\": \"ENSP00000288602\", \"seq_type\": \"protein\"})",
        "required": []
      },
      {
        "id": "ensembl_overlap_region",
        "connector": "genomes",
        "description": "List Ensembl features overlapping a genomic region \u2014 genes, transcripts, regulatory features (enhancers/promoters), repeats, variants, karyotype bands. Args: region (1-based inclusive chrom:start-end GRCh38, e.g. 7:140719327-140925199; upstream rejects spans >5Mb \u2014 split larger); feature (gene default/transcript/exon/cds/regulatory/motif/repeat/variation/structural_variation/band/simple/misc); species (default homo_sapiens); max_features (row cap default 500; n_total carries the complete overlap count, features_truncated flags the cap). Returns {region, species, feature, n_total, features_truncated, features} sorted by (start,id). Row shape varies \u2014 genes {id, external_name, biotype, description, start, end, strand, canonical_transcript, ...}; regulatory {id, description, start, end, extended_start/end, ...}. Empty regions return n_total:0.",
        "input": {
          "type": "object",
          "properties": {
            "region": {
              "type": "string"
            },
            "feature": {
              "type": "string",
              "enum": [
                "gene",
                "transcript",
                "exon",
                "cds",
                "regulatory",
                "motif",
                "repeat",
                "variation",
                "structural_variation",
                "band",
                "simple",
                "misc"
              ],
              "default": "gene"
            },
            "species": {
              "type": "string",
              "default": "homo_sapiens"
            },
            "max_features": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "region"
          ]
        },
        "returns": "{region, species, feature, n_total, features_truncated, features[]} sorted by (start,id); row shape varies by feature type. Empty regions return n_total:0.",
        "example": "const result = await host.mcp(\"genomes\", \"ensembl_overlap_region\", {\"region\": \"7:140719327-140925199\", \"feature\": \"gene\"})",
        "required": [
          "region"
        ]
      },
      {
        "id": "ucsc_list_tracks",
        "connector": "genomes",
        "description": "List data tracks available in a UCSC Genome Browser assembly (leaf tracks only \u2014 the queryable ones), optionally filtered. Args: genome (hg38 default/hg19/mm39/danRer11/... ~220 assemblies); filter_text (case-insensitive substring over name/short/long label, e.g. phyloP, TFBS, ClinVar; omit to list everything \u2014 hg38 has ~24k leaf tracks, you almost always want a filter); max_tracks (row cap default 200; n_total carries the full match count, tracks_truncated flags the cap). Returns {genome, filter_text, n_total, tracks_truncated, tracks} sorted by track name; each row {track, short_label, long_label, type, group, parent}. Use `track` with ucsc_track_data. Quirk: first call per genome downloads the full ~17MB listing and caches it for the process.",
        "input": {
          "type": "object",
          "properties": {
            "genome": {
              "type": "string",
              "default": "hg38"
            },
            "filter_text": {
              "type": "string"
            },
            "max_tracks": {
              "type": "integer",
              "default": 200
            }
          }
        },
        "returns": "{genome, filter_text, n_total, tracks_truncated, tracks:[{track, short_label, long_label, type, group, parent}]} \u2014 leaf tracks only, sorted by track name; n_total is the full match count, tracks_truncated flags the max_tracks cap.",
        "example": "const result = await host.mcp(\"genomes\", \"ucsc_list_tracks\", {\"genome\": \"hg38\", \"filter_text\": \"phyloP\", \"max_tracks\": 50})",
        "required": []
      },
      {
        "id": "ucsc_track_data",
        "connector": "genomes",
        "description": "Fetch raw rows of any UCSC Genome Browser track in a region \u2014 the generic escape hatch behind ucsc_conservation / ucsc_tfbs_clusters (gene tracks, ClinVar, GWAS catalog, CpG islands, repeats, ...). Args: track (name from ucsc_list_tracks, e.g. knownGene, cpgIslandExt, clinvarMain); chrom (chr-prefixed, chr7/chrX \u2014 UCSC requires the prefix); start (0-based half-open; an Ensembl 1-based start is start-1 here); end (exclusive); genome (default hg38); max_rows (API maxItemsOutput, default 1000; truncated reflects the API's own maxItemsLimit flag). Returns {genome, track, chrom, start, end, track_type, items_returned, truncated, rows} \u2014 rows in upstream shape (BED-like {chrom, chromStart, chromEnd, name, score, ...}; wiggle {start, end, value}). Unknown tracks raise. Quirk: for some huge tracks the API caps output itself and points at dataDownloadUrl \u2014 echoed when present.",
        "input": {
          "type": "object",
          "properties": {
            "track": {
              "type": "string"
            },
            "chrom": {
              "type": "string"
            },
            "start": {
              "type": "integer"
            },
            "end": {
              "type": "integer"
            },
            "genome": {
              "type": "string",
              "default": "hg38"
            },
            "max_rows": {
              "type": "integer",
              "default": 1000
            }
          },
          "required": [
            "track",
            "chrom",
            "start",
            "end"
          ]
        },
        "returns": "{genome, track, chrom, start, end, track_type, items_returned, truncated, rows, dataDownloadUrl?} \u2014 rows in the upstream shape; truncated reflects the API maxItemsLimit flag; dataDownloadUrl echoed when the API caps a huge track itself.",
        "example": "const result = await host.mcp(\"genomes\", \"ucsc_track_data\", {\"track\": \"cpgIslandExt\", \"chrom\": \"chr7\", \"start\": 140700000, \"end\": 140800000, \"genome\": \"hg38\"})",
        "required": [
          "track",
          "chrom",
          "start",
          "end"
        ]
      },
      {
        "id": "ucsc_conservation",
        "connector": "genomes",
        "description": "Evolutionary conservation summary for a region from UCSC phyloP / phastCons tracks (base-wise scores over multi-species alignments). Args: chrom (chr-prefixed); start (0-based half-open); end (exclusive; span capped at 100000 bp \u2014 split larger); genome (default hg38); track (default phyloP100way; positive=conserved, negative=fast-evolving; alternatives hg38 phastCons100way, phyloP30way, phastCons30way, phyloP447way, phyloP470way; hg19 phyloP100wayAll/phastCons100way); include_values (also return per-base {start,end,value} rows capped at max_values, values_truncated flags the cap; default false = summary only); max_values (per-base cap default 2000). Returns {genome, track, chrom, start, end, span_bp, n_bases_covered, coverage_fraction, mean, min, max} (+values, values_truncated when requested). Stats weighted by each row's base span, clipped to window; uncovered bases lower coverage_fraction, not zero-scored. Non-score tracks raise; an upstream-truncated row list also raises.",
        "input": {
          "type": "object",
          "properties": {
            "chrom": {
              "type": "string"
            },
            "start": {
              "type": "integer"
            },
            "end": {
              "type": "integer"
            },
            "genome": {
              "type": "string",
              "default": "hg38"
            },
            "track": {
              "type": "string",
              "default": "phyloP100way"
            },
            "include_values": {
              "type": "boolean",
              "default": false
            },
            "max_values": {
              "type": "integer",
              "default": 2000
            }
          },
          "required": [
            "chrom",
            "start",
            "end"
          ]
        },
        "returns": "{genome, track, chrom, start, end, span_bp, n_bases_covered, coverage_fraction, mean, min, max, values?, values_truncated?} \u2014 stats are base-span-weighted and clipped to the window; uncovered bases lower coverage_fraction rather than count as zero.",
        "example": "const result = await host.mcp(\"genomes\", \"ucsc_conservation\", {\"chrom\": \"chr7\", \"start\": 140753330, \"end\": 140753380, \"track\": \"phyloP100way\"})",
        "required": [
          "chrom",
          "start",
          "end"
        ]
      },
      {
        "id": "ucsc_tfbs_clusters",
        "connector": "genomes",
        "description": "ENCODE transcription-factor binding site clusters overlapping a region (ChIP-seq peak clusters across hundreds of cell types) \u2014 which TFs bind where. Args: chrom (chr-prefixed); start (0-based half-open); end (exclusive); genome (hg38 default track encRegTfbsClustered ENCODE 3, or hg19 wgEncodeRegTfbsClusteredV3; other assemblies raise); max_rows (API maxItemsOutput default 1000; truncated reflects maxItemsLimit). Returns {genome, track, chrom, start, end, items_returned, truncated, n_factors, factors, clusters} \u2014 clusters sorted by (chromStart,name) {name (TF symbol e.g. CTCF), chrom, chromStart, chromEnd, score (0-1000), sourceCount (supporting experiments)}; factors is the distinct TF list. Score>=~600 and high sourceCount ~ robust binding.",
        "input": {
          "type": "object",
          "properties": {
            "chrom": {
              "type": "string"
            },
            "start": {
              "type": "integer"
            },
            "end": {
              "type": "integer"
            },
            "genome": {
              "type": "string",
              "default": "hg38"
            },
            "max_rows": {
              "type": "integer",
              "default": 1000
            }
          },
          "required": [
            "chrom",
            "start",
            "end"
          ]
        },
        "returns": "{genome, track, chrom, start, end, items_returned, truncated, n_factors, factors:[...], clusters:[{name, chrom, chromStart, chromEnd, score, sourceCount}]} \u2014 clusters sorted by (chromStart,name); factors is the distinct TF list.",
        "example": "const result = await host.mcp(\"genomes\", \"ucsc_tfbs_clusters\", {\"chrom\": \"chr7\", \"start\": 140699000, \"end\": 140760000, \"genome\": \"hg38\"})",
        "required": [
          "chrom",
          "start",
          "end"
        ]
      },
      {
        "id": "ucsc_chrom_sizes",
        "connector": "genomes",
        "description": "Chromosome/contig names and sizes of a UCSC assembly \u2014 for validating coordinates and iterating regions. Args: genome (default hg38); filter_text (case-insensitive substring on the name, e.g. chr1; omit for all \u2014 hg38 has 711 sequences, mostly alt/random/unplaced; primary chromosomes sort first); max_chroms (row cap default 100; n_total carries the full post-filter count, chroms_truncated flags the cap). Returns {genome, filter_text, chrom_count (assembly-wide from the API), n_total, chroms_truncated, chromosomes:[{name, size_bp}]} sorted by size descending.",
        "input": {
          "type": "object",
          "properties": {
            "genome": {
              "type": "string",
              "default": "hg38"
            },
            "filter_text": {
              "type": "string"
            },
            "max_chroms": {
              "type": "integer",
              "default": 100
            }
          }
        },
        "returns": "{genome, filter_text, chrom_count, n_total, chroms_truncated, chromosomes:[{name, size_bp}]} \u2014 chrom_count is the assembly-wide total from the API; n_total is the post-filter count; sorted by size descending.",
        "example": "const result = await host.mcp(\"genomes\", \"ucsc_chrom_sizes\", {\"genome\": \"hg38\", \"filter_text\": \"chr1\", \"max_chroms\": 25})",
        "required": []
      }
    ]
  },
  {
    "id": "variants",
    "displayName": "Variants",
    "aliases": [
      "gnomAD",
      "ClinVar",
      "dbSNP",
      "genetic variant"
    ],
    "description": "Human genetic variants \u2014 gnomAD population frequencies/constraint, ClinVar records/search (direct NCBI), dbSNP, structural and mitochondrial variants.",
    "useWhen": "Use when you need human genetic-variant data \u2014 gnomAD population allele frequencies, gene constraint (pLI/LOEUF), structural or mitochondrial variants, and build liftover; ClinVar clinical significance (gnomAD mirror or direct NCBI search/records by accession or rsID); or dbSNP RefSNP records and region lookups.",
    "sources": [
      "gnomAD",
      "ClinVar",
      "dbSNP"
    ],
    "termsUrl": "https://www.ncbi.nlm.nih.gov/clinvar/docs/maintenance_use/",
    "requiresNcbi": true,
    "tools": [
      {
        "id": "get_variant",
        "connector": "variants",
        "description": "Look up one gnomAD short variant by ID and return its population frequencies. `variant_id` is `chrom-pos-ref-alt` on the dataset's reference build (GRCh38 for r3/r4, GRCh37 for r2.1/ExAC), e.g. `19-44908822-C-T` (APOE rs7412); use `search_variants` to resolve an rsID first.",
        "input": {
          "type": "object",
          "properties": {
            "variant_id": {
              "type": "string"
            },
            "dataset": {
              "type": "string",
              "enum": [
                "gnomad_r4",
                "gnomad_r4_non_ukb",
                "gnomad_r3",
                "gnomad_r3_controls_and_biobanks",
                "gnomad_r3_non_cancer",
                "gnomad_r3_non_neuro",
                "gnomad_r3_non_topmed",
                "gnomad_r3_non_v2",
                "gnomad_r2_1",
                "gnomad_r2_1_controls",
                "gnomad_r2_1_non_cancer",
                "gnomad_r2_1_non_neuro",
                "gnomad_r2_1_non_topmed",
                "exac"
              ],
              "default": "gnomad_r4"
            }
          },
          "required": [
            "variant_id"
          ]
        },
        "returns": "`{ found: bool, variant_id: str, dataset: str, variant: null | { variant_id, dataset, reference_genome, chrom, pos, ref, alt, rsids: [str], exome: { ac, an, af, homozygote_count, hemizygote_count, filters }|null, genome: {...}|null } }`. `exome`/`genome` are null where the dataset has no such call set (e.g. r3 is genome-only).",
        "example": "const result = await host.mcp(\"variants\", \"get_variant\", {\"variant_id\": \"19-44908822-C-T\", \"dataset\": \"gnomad_r4\"})",
        "required": [
          "variant_id"
        ]
      },
      {
        "id": "search_variants",
        "connector": "variants",
        "description": "Search gnomAD for variant IDs matching a query string (an rsID like `rs7412`, a variant ID, or a prefix). Use this to resolve rsIDs to `chrom-pos-ref-alt` IDs for `get_variant`.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "dataset": {
              "type": "string",
              "enum": [
                "gnomad_r4",
                "gnomad_r4_non_ukb",
                "gnomad_r3",
                "gnomad_r3_controls_and_biobanks",
                "gnomad_r3_non_cancer",
                "gnomad_r3_non_neuro",
                "gnomad_r3_non_topmed",
                "gnomad_r3_non_v2",
                "gnomad_r2_1",
                "gnomad_r2_1_controls",
                "gnomad_r2_1_non_cancer",
                "gnomad_r2_1_non_neuro",
                "gnomad_r2_1_non_topmed",
                "exac"
              ],
              "default": "gnomad_r4"
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ query: str, dataset: str, n_matches: int, variant_ids: [str] }` \u2014 `variant_ids` sorted. Empty list when nothing matches.",
        "example": "const result = await host.mcp(\"variants\", \"search_variants\", {\"query\": \"rs7412\", \"dataset\": \"gnomad_r4\"})",
        "required": [
          "query"
        ]
      },
      {
        "id": "gene_variants",
        "connector": "variants",
        "description": "List ALL gnomAD short variants in a gene (complete listing \u2014 can be thousands of rows for large genes). Pass exactly one of `gene_symbol` (HGNC symbol, e.g. `APOE`) or `gene_id` (Ensembl gene ID, e.g. `ENSG00000130203`).",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string"
            },
            "gene_id": {
              "type": "string"
            },
            "dataset": {
              "type": "string",
              "enum": [
                "gnomad_r4",
                "gnomad_r4_non_ukb",
                "gnomad_r3",
                "gnomad_r3_controls_and_biobanks",
                "gnomad_r3_non_cancer",
                "gnomad_r3_non_neuro",
                "gnomad_r3_non_topmed",
                "gnomad_r3_non_v2",
                "gnomad_r2_1",
                "gnomad_r2_1_controls",
                "gnomad_r2_1_non_cancer",
                "gnomad_r2_1_non_neuro",
                "gnomad_r2_1_non_topmed",
                "exac"
              ],
              "default": "gnomad_r4"
            }
          }
        },
        "returns": "`{ gene_id: str|null, symbol: str, chrom: str, start: int, stop: int, dataset: str, n_variants: int, variants: [ { variant_id, pos, ref, alt, rsids: [str], exome: { ac, an, af }|null, genome: {...}|null } ] }`, rows sorted by (pos, variant_id). Unknown gene returns `gene_id: null`, an echoed `gene_query`, and an empty `variants` list.",
        "example": "const result = await host.mcp(\"variants\", \"gene_variants\", {\"gene_symbol\": \"APOE\", \"dataset\": \"gnomad_r4\"})",
        "required": []
      },
      {
        "id": "gene_constraint",
        "connector": "variants",
        "description": "gnomAD gene constraint metrics: pLI, observed/expected LoF-missense-synonymous counts with oe ratios + 90% CI bounds, and per-class z-scores. Use to judge a gene's intolerance to loss-of-function (pLI >= 0.9 or oe_lof_upper (LOEUF) < 0.6 ~ LoF-intolerant). Pass exactly one of `gene_symbol` (e.g. `TP53`) or `gene_id`.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string"
            },
            "gene_id": {
              "type": "string"
            }
          }
        },
        "returns": "`{ found: bool, gene_id: str|null, symbol: str, canonical_transcript_id: str, chrom: str, start: int, stop: int, strand: str, constraint: { exp_lof, obs_lof, oe_lof, oe_lof_lower, oe_lof_upper, exp_mis, obs_mis, oe_mis, oe_mis_lower, oe_mis_upper, exp_syn, obs_syn, oe_syn, oe_syn_lower, oe_syn_upper, pli, lof_z, mis_z, syn_z }|null }`. Unknown gene returns `found: false`, an echoed `gene_query`, and `constraint: null`.",
        "example": "const result = await host.mcp(\"variants\", \"gene_constraint\", {\"gene_symbol\": \"TP53\"})",
        "required": []
      },
      {
        "id": "region_variants",
        "connector": "variants",
        "description": "List ALL gnomAD short variants in a genomic region (max 1 Mb \u2014 split larger regions into consecutive windows). `chrom` is a chromosome name without `chr` prefix (`1`-`22`, `X`, `Y`); `start`/`stop` are 1-based inclusive and `stop - start` must be <= 1,000,000. The dataset determines the reference build of the coordinates (GRCh38 for r3/r4).",
        "input": {
          "type": "object",
          "properties": {
            "chrom": {
              "type": "string"
            },
            "start": {
              "type": "integer"
            },
            "stop": {
              "type": "integer"
            },
            "dataset": {
              "type": "string",
              "enum": [
                "gnomad_r4",
                "gnomad_r4_non_ukb",
                "gnomad_r3",
                "gnomad_r3_controls_and_biobanks",
                "gnomad_r3_non_cancer",
                "gnomad_r3_non_neuro",
                "gnomad_r3_non_topmed",
                "gnomad_r3_non_v2",
                "gnomad_r2_1",
                "gnomad_r2_1_controls",
                "gnomad_r2_1_non_cancer",
                "gnomad_r2_1_non_neuro",
                "gnomad_r2_1_non_topmed",
                "exac"
              ],
              "default": "gnomad_r4"
            }
          },
          "required": [
            "chrom",
            "start",
            "stop"
          ]
        },
        "returns": "`{ chrom: str, start: int, stop: int, dataset: str, n_variants: int, variants: [...] }` with the same lean variant rows as `gene_variants`, sorted by (pos, variant_id).",
        "example": "const result = await host.mcp(\"variants\", \"region_variants\", {\"chrom\": \"1\", \"start\": 55039475, \"stop\": 55064852, \"dataset\": \"gnomad_r4\"})",
        "required": [
          "chrom",
          "start",
          "stop"
        ]
      },
      {
        "id": "liftover_variant",
        "connector": "variants",
        "description": "Map a variant ID between reference builds (GRCh37 <-> GRCh38) using gnomAD's liftover table. `variant_id` is `chrom-pos-ref-alt` on `source_build`. The route is directional: a GRCh38 ID passed with `source_build=GRCh37` returns zero results, not an error.",
        "input": {
          "type": "object",
          "properties": {
            "variant_id": {
              "type": "string"
            },
            "source_build": {
              "type": "string",
              "enum": [
                "GRCh37",
                "GRCh38"
              ],
              "default": "GRCh37"
            }
          },
          "required": [
            "variant_id"
          ]
        },
        "returns": "`{ source_variant_id: str, source_build: str, n_results: int, results: [ { source: { variant_id, reference_genome }, liftover: { variant_id, reference_genome }, datasets: [str] } ] }`, sorted by liftover variant_id. `n_results: 0` when the ID does not lift over in that direction.",
        "example": "const result = await host.mcp(\"variants\", \"liftover_variant\", {\"variant_id\": \"1-55516888-G-GA\", \"source_build\": \"GRCh37\"})",
        "required": [
          "variant_id"
        ]
      },
      {
        "id": "clinvar_variants",
        "connector": "variants",
        "description": "List ClinVar variants in a gene as mirrored by gnomAD, with clinical significance, review status and gold stars. The output pins gnomAD's ClinVar snapshot via `clinvar_release_date`. Pass exactly one of `gene_symbol` (e.g. `BRCA1`) or `gene_id`.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string"
            },
            "gene_id": {
              "type": "string"
            }
          }
        },
        "returns": "`{ gene_id: str|null, symbol: str, clinvar_release_date: str, n_variants: int, variants: [ { variant_id, clinvar_variation_id, clinical_significance, gold_stars, review_status, major_consequence, pos, transcript_id, in_gnomad } ] }`, sorted by (pos, variant_id). Unknown gene returns `gene_id: null`, an echoed `gene_query`, and an empty `variants` list.",
        "example": "const result = await host.mcp(\"variants\", \"clinvar_variants\", {\"gene_symbol\": \"BRCA1\"})",
        "required": []
      },
      {
        "id": "structural_variants",
        "connector": "variants",
        "description": "List gnomAD structural variants (deletions, duplications, insertions, inversions, CNVs...) overlapping a gene. Pass exactly one of `gene_symbol` (e.g. `TP53`) or `gene_id`. `dataset` is an SV pin \u2014 `gnomad_sv_r4` (default, GRCh38) or `gnomad_sv_r2_1` (GRCh37); SV IDs are release-specific.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string"
            },
            "gene_id": {
              "type": "string"
            },
            "dataset": {
              "type": "string",
              "enum": [
                "gnomad_sv_r4",
                "gnomad_sv_r2_1"
              ],
              "default": "gnomad_sv_r4"
            }
          }
        },
        "returns": "`{ gene_id: str|null, symbol: str, dataset: str, n_variants: int, variants: [...] }`; rows carry SV `variant_id`, `type`, position/length, allele counts/frequencies, `filters`, and per-gene `consequence`/`major_consequence`, sorted by variant_id. Unknown gene returns `gene_id: null`, an echoed `gene_query`, and an empty list.",
        "example": "const result = await host.mcp(\"variants\", \"structural_variants\", {\"gene_symbol\": \"TP53\", \"dataset\": \"gnomad_sv_r4\"})",
        "required": []
      },
      {
        "id": "get_structural_variant",
        "connector": "variants",
        "description": "Look up one gnomAD structural variant by its release-specific SV ID (e.g. `DEL_CHR17_599B1512` in gnomad_sv_r4). IDs do NOT carry across releases \u2014 `dataset` (`gnomad_sv_r4` default, or `gnomad_sv_r2_1`) must match the release the ID came from.",
        "input": {
          "type": "object",
          "properties": {
            "sv_id": {
              "type": "string"
            },
            "dataset": {
              "type": "string",
              "enum": [
                "gnomad_sv_r4",
                "gnomad_sv_r2_1"
              ],
              "default": "gnomad_sv_r4"
            }
          },
          "required": [
            "sv_id"
          ]
        },
        "returns": "`{ found: bool, sv_id: str, dataset: str, structural_variant: null | { variant_id, chrom, pos, end, chrom2, pos2, type, length, ac, an, af, homozygote_count, hemizygote_count, filters, qual, consequences: [ { consequence, genes: [str] } ], algorithms: [str], evidence: [str], dataset } }`. Null when not found.",
        "example": "const result = await host.mcp(\"variants\", \"get_structural_variant\", {\"sv_id\": \"DEL_CHR17_A5250EA9\", \"dataset\": \"gnomad_sv_r4\"})",
        "required": [
          "sv_id"
        ]
      },
      {
        "id": "mitochondrial_variants",
        "connector": "variants",
        "description": "List gnomAD mitochondrial variants with heteroplasmy-aware counts (`ac_het`, `ac_hom`, `max_heteroplasmy`) for a mitochondrial gene OR a chrM coordinate window. Pass a gene (`gene_symbol` like `MT-TL1`, or `gene_id`) OR a region (`region_start` + `region_stop`), not both.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string"
            },
            "gene_id": {
              "type": "string"
            },
            "region_start": {
              "type": "integer"
            },
            "region_stop": {
              "type": "integer"
            },
            "dataset": {
              "type": "string",
              "enum": [
                "gnomad_r4",
                "gnomad_r4_non_ukb",
                "gnomad_r3",
                "gnomad_r3_controls_and_biobanks",
                "gnomad_r3_non_cancer",
                "gnomad_r3_non_neuro",
                "gnomad_r3_non_topmed",
                "gnomad_r3_non_v2",
                "gnomad_r2_1",
                "gnomad_r2_1_controls",
                "gnomad_r2_1_non_cancer",
                "gnomad_r2_1_non_neuro",
                "gnomad_r2_1_non_topmed",
                "exac"
              ],
              "default": "gnomad_r4"
            }
          }
        },
        "returns": "`{ gene_id+symbol | region: \"M:start-stop\", dataset: str, n_variants: int, variants: [ { variant_id, pos, ac_het, ac_hom, an, max_heteroplasmy, filters } ] }`, sorted by (pos, variant_id). Unknown gene returns `gene_id: null`, an echoed `gene_query`, and an empty list.",
        "example": "const result = await host.mcp(\"variants\", \"mitochondrial_variants\", {\"gene_symbol\": \"MT-TL1\", \"dataset\": \"gnomad_r4\"})",
        "required": []
      },
      {
        "id": "clinvar_search",
        "connector": "variants",
        "description": "Search ClinVar directly (live NCBI, not gnomAD's snapshot) and return matching variation records with clinical significance, review status and gold stars. Requires a contact email (Settings \u2192 Credentials \u2192 Literature access \u2192 Contact email) per NCBI E-utilities usage policy. Args: query (a ClinVar Entrez query \u2014 free text like \"TP53 R175H\" or an HGVS string works, and fielded terms compose with AND/OR/NOT, e.g. BRCA1[gene], pathogenic[CLIN_SIG], \"Lynch syndrome\"[dis], single_nucleotide_variant[Type of variation]; an rsID also works but clinvar_variant_by_rsid returns fuller records), max_records (page cap 1-200, default 50). The match TOTAL is always reported; when total > max_records the list is a capped prefix (ClinVar relevance/recency order) and truncated is true. NCBI E-utilities intermittently return HTTP 500 under load \u2014 retry once a few seconds later if that surfaces.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "max_records": {
              "type": "integer",
              "default": 50
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ term, total, n_returned, truncated, missing_uids, records }` \u2014 `total` is the true ClinVar match count (may exceed the returned list); `truncated` flags a capped page; `missing_uids` lists matched IDs whose summary doc NCBI dropped (rare, transient \u2014 distinct from truncation; retry to recover). Each record: `{ variation_id, accession (VCV), accession_version, title, obj_type, variant_type, canonical_spdi, cdna_change, protein_change, rsids, other_xrefs, genes, molecular_consequences, locations (GRCh38+GRCh37), allele_frequencies, germline_classification, clinical_impact_classification, oncogenicity_classification (each: description, review_status, gold_stars 0-4, last_evaluated, fda_recognized_database, conditions with ontology xrefs; null when ClinVar has no classification on that axis), n_submissions (SCV count), supporting_submissions }`. When no contact email is set, returns `{ error: \"contact_email_required\", message }` instead.",
        "example": "const result = await host.mcp(\"variants\", \"clinvar_search\", {\"query\": \"BRCA1 pathogenic[CLIN_SIG]\", \"max_records\": 50})",
        "required": [
          "query"
        ]
      },
      {
        "id": "clinvar_get_records",
        "connector": "variants",
        "description": "Fetch full ClinVar records for a batch of VCV/RCV accessions or bare variation IDs. Requires a contact email (Settings \u2192 Credentials \u2192 Literature access \u2192 Contact email) per NCBI E-utilities usage policy. Args: accessions (up to 50 identifiers, mixed forms accepted \u2014 VCV000045122 (versioned VCV000045122.3 ok; resolved locally, free), RCV000019428 (each RCV costs one extra esearch), or a bare ClinVar variation ID (45122). rsIDs are rejected \u2014 use clinvar_variant_by_rsid. An RCV (one variant-condition pair) resolves to its parent VCV variation record). Never silently drops an input.",
        "input": {
          "type": "object",
          "properties": {
            "accessions": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "accessions"
          ]
        },
        "returns": "`{ n_requested, n_unique, n_duplicate_skipped, records, not_found, missing_uids, not_processed }`. Records carry the full shape documented in `clinvar_search` plus `requested_as` (which input(s) mapped to the record), sorted by variation_id. `not_found` lists unknown accessions (definitive absence \u2014 RCVs that esearch proves unknown); `missing_uids` lists inputs whose summary NCBI dropped or error-flagged (for a just-resolved RCV this is a transient drop \u2014 the record EXISTS, retry; for a VCV/numeric input it is a transient drop OR a nonexistent id \u2014 retry to disambiguate, never conclude absence from one call); `not_processed` lists RCVs skipped because the per-call time budget ran out (re-request just those \u2014 VCV/numeric inputs always resolve, they never land there). When no contact email is set, returns `{ error: \"contact_email_required\", message }` instead.",
        "example": "const result = await host.mcp(\"variants\", \"clinvar_get_records\", {\"accessions\": [\"VCV000045122\", \"RCV000019428\", \"45123\"]})",
        "required": [
          "accessions"
        ]
      },
      {
        "id": "clinvar_variant_by_rsid",
        "connector": "variants",
        "description": "All ClinVar variation records that reference a dbSNP rsID, with full classifications (an rsID can map to several VCVs \u2014 one per alternate allele, e.g. rs121913529 covers KRAS G12D/G12V/G12A). Requires a contact email (Settings \u2192 Credentials \u2192 Literature access \u2192 Contact email) per NCBI E-utilities usage policy. Args: rsid (dbSNP reference SNP ID, e.g. rs7412; case-insensitive, must match rs<digits>), max_records (cap 1-200, default 50). total always carries the true match count and truncated flags a capped listing; total == 0 means ClinVar has no record for the rsID.",
        "input": {
          "type": "object",
          "properties": {
            "rsid": {
              "type": "string",
              "description": "dbSNP rsID, e.g. rs7412 (case-insensitive)."
            },
            "max_records": {
              "type": "integer",
              "default": 50
            }
          },
          "required": [
            "rsid"
          ]
        },
        "returns": "`{ rsid, total, n_returned, truncated, missing_uids, records }` with the full record shape documented in `clinvar_search` (review status, gold stars, last-evaluated dates, SCV counts \u2014 the fields gnomAD\u2019s ClinVar mirror lacks). Records come in ClinVar relevance order; `missing_uids` lists matches whose summary NCBI dropped (transient). `total == 0` means ClinVar has no record for the rsID. When no contact email is set, returns `{ error: \"contact_email_required\", message }` instead.",
        "example": "const result = await host.mcp(\"variants\", \"clinvar_variant_by_rsid\", {\"rsid\": \"rs121913529\", \"max_records\": 50})",
        "required": [
          "rsid"
        ]
      },
      {
        "id": "dbsnp_get_rsids",
        "connector": "variants",
        "description": "Canonical dbSNP RefSNP records for a batch of rsIDs: GRCh38+GRCh37 placements, alleles, gene context, per-study allele frequencies, and ClinVar cross-references. Requires a contact email (Settings \u2192 Credentials \u2192 Literature access \u2192 Contact email) per NCBI E-utilities usage policy; without one the tool returns {error: 'contact_email_required', message}. Args: rsids (up to 20 rs<digits>, case-insensitive) \u2014 each costs one paced NCBI Variation Services request, so large batches take ~1 s per rsID. Returns {n_requested, records, not_found (rs numbers dbSNP doesn't know), not_processed (rsIDs skipped when the wall-clock budget ran out \u2014 re-request just those)}. Each record: {rsid, status, create_date, last_update_date, last_update_build_id, n_citations, citations_pmids (capped at 20; citations_truncated flags the cap), variant_type, mane_select_ids, placements, alleles}. status is 'live', 'merged' (record instead carries merged_into \u2014 re-query those rsIDs) or 'no_data' (withdrawn/unsupported). placements give 1-based chromosome coordinates with ref/alts per assembly (GRCh38 first, is_primary true). Each alt-allele entry: {allele, ref, spdi (0-based interbase), hgvs, frequencies: [{study, study_version, allele_count, total_count, af}] (ALFA, 1000Genomes, TOPMED, gnomAD...), clinvar: [{rcv_accession, clinical_significances, review_status, last_evaluated_date, disease_names}], genes: [{symbol, gene_id, name, orientation, consequences (SO terms), mane_select: [{transcript_hgvs, protein_spdi}]}]}.",
        "input": {
          "type": "object",
          "properties": {
            "rsids": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Up to 20 rsIDs (rs<digits>, case-insensitive), e.g. [\"rs7412\", \"rs429358\"]."
            }
          },
          "required": [
            "rsids"
          ]
        },
        "returns": "`{ n_requested: int, records: [ { rsid, status ('live'|'merged'|'no_data'), create_date, last_update_date, last_update_build_id, n_citations, citations_pmids, citations_truncated, merged_into?, variant_type?, mane_select_ids?, placements?: [{assembly, assembly_full, seq_id, chrom, position, ref, alts, is_primary}], alleles?: [{allele, ref, spdi, hgvs, frequencies, clinvar, genes}] } ], not_found: [str], not_processed: [str] }` \u2014 not_found are rs numbers dbSNP doesn't know; not_processed are rsIDs skipped when the time budget ran out. Empty/blank input yields all-empty lists (never an error).",
        "example": "const result = await host.mcp(\"variants\", \"dbsnp_get_rsids\", {\"rsids\": [\"rs7412\", \"rs429358\"]})",
        "required": [
          "rsids"
        ]
      },
      {
        "id": "dbsnp_search_by_region",
        "connector": "variants",
        "description": "List dbSNP rsIDs in a genomic window (esearch db=snp positional index \u2014 NCBI Variation Services has no region endpoint). Requires a contact email (Settings \u2192 Credentials \u2192 Literature access \u2192 Contact email) per NCBI E-utilities usage policy; without one the tool returns {error: 'contact_email_required', message}. Args: chrom (1-22, X, Y or MT; 'chr' prefix tolerated), start (1-based inclusive), stop (inclusive; span capped at 1 Mb \u2014 split larger regions into consecutive windows; dense regions hold many thousands of rsIDs per kb, so keep windows small or raise max_rsids), assembly (which positional index \u2014 'GRCh38' default -> [CPOS], or 'GRCh37' -> [CPOS_GRCH37]; coordinates must be on the chosen assembly), max_rsids (listing cap 1-1000, default 200). Returns {chrom, start, stop, assembly, term (the exact Entrez query used), total (the API's own count), n_returned, truncated, rsids}. truncated is true when total > n_returned \u2014 the list is then a prefix in Entrez default order (descending rs number), never a silent truncation. Feed rsIDs (<= 20 at a time) to dbsnp_get_rsids for full records.",
        "input": {
          "type": "object",
          "properties": {
            "chrom": {
              "type": "string",
              "description": "Chromosome 1-22, X, Y or MT ('chr' prefix tolerated)."
            },
            "start": {
              "type": "integer",
              "description": "Window start, 1-based inclusive."
            },
            "stop": {
              "type": "integer",
              "description": "Window end, inclusive; span capped at 1 Mb."
            },
            "assembly": {
              "type": "string",
              "enum": [
                "GRCh38",
                "GRCh37"
              ],
              "default": "GRCh38"
            },
            "max_rsids": {
              "type": "integer",
              "default": 200
            }
          },
          "required": [
            "chrom",
            "start",
            "stop"
          ]
        },
        "returns": "`{ chrom: str, start: int, stop: int, assembly: str, term: str, total: int, n_returned: int, truncated: bool, rsids: [str] }` \u2014 total is esearch's own match count; truncated is true when total > n_returned (rsids is then a capped prefix in descending-rs-number order).",
        "example": "const result = await host.mcp(\"variants\", \"dbsnp_search_by_region\", {\"chrom\": \"19\", \"start\": 44905000, \"stop\": 44910000, \"assembly\": \"GRCh38\"})",
        "required": [
          "chrom",
          "start",
          "stop"
        ]
      }
    ]
  },
  {
    "id": "clinical-trials",
    "displayName": "Clinical Trials",
    "description": "Clinical trials from ClinicalTrials.gov \u2014 search, details, sponsors, investigators, endpoints, and eligibility.",
    "useWhen": "Use for ClinicalTrials.gov: search trials by condition/intervention/sponsor/location/status/phase, fetch full details by NCT id, find trials by sponsor, discover investigators and sites, analyze trial endpoints, or match patients by eligibility.",
    "sources": [
      "ClinicalTrials.gov"
    ],
    "termsUrl": "https://clinicaltrials.gov/about-site/terms-conditions",
    "requiresNcbi": false,
    "group": "directory",
    "tools": [
      {
        "id": "search_trials",
        "connector": "clinical-trials",
        "description": "PRIMARY search over ClinicalTrials.gov. Filter by condition, intervention, sponsor, location, status (e.g. [\"RECRUITING\"]), phase ([\"PHASE1\"..\"PHASE4\"]) and study_type. condition/intervention/sponsor/location accept Essie query syntax (boolean AND/OR/NOT, \"quoted phrases\", grouping, automatic synonyms). Page with page_token; set count_total for the total match count. advanced_query merges a raw Essie expression into filter.advanced.",
        "input": {
          "type": "object",
          "properties": {
            "condition": {
              "type": "string"
            },
            "intervention": {
              "type": "string"
            },
            "sponsor": {
              "type": "string"
            },
            "location": {
              "type": "string"
            },
            "status": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "NOT_YET_RECRUITING",
                  "RECRUITING",
                  "ENROLLING_BY_INVITATION",
                  "ACTIVE_NOT_RECRUITING",
                  "COMPLETED",
                  "SUSPENDED",
                  "TERMINATED",
                  "WITHDRAWN",
                  "AVAILABLE",
                  "NO_LONGER_AVAILABLE",
                  "TEMPORARILY_NOT_AVAILABLE",
                  "APPROVED_FOR_MARKETING",
                  "WITHHELD",
                  "UNKNOWN"
                ]
              }
            },
            "phase": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "EARLY_PHASE1",
                  "PHASE1",
                  "PHASE2",
                  "PHASE3",
                  "PHASE4",
                  "NA"
                ]
              }
            },
            "study_type": {
              "type": "string",
              "enum": [
                "INTERVENTIONAL",
                "OBSERVATIONAL",
                "EXPANDED_ACCESS"
              ]
            },
            "advanced_query": {
              "type": "string"
            },
            "page_size": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 1000
            },
            "page_token": {
              "type": "string"
            },
            "count_total": {
              "type": "boolean",
              "default": false
            }
          }
        },
        "returns": "`{ count, total (only when count_total, else null), next_page_token, items: [ { nct_id, title, status, phase (array|null), conditions, interventions, sponsor, enrollment, start_date, primary_completion_date, locations_count, study_type } ] }`.",
        "example": "const result = await host.mcp(\"clinical-trials\", \"search_trials\", {\"condition\": \"lung cancer\", \"status\": [\"RECRUITING\"], \"phase\": [\"PHASE3\"], \"count_total\": true, \"page_size\": 10})",
        "required": []
      },
      {
        "id": "get_trial_details",
        "connector": "clinical-trials",
        "description": "Get comprehensive details for one trial by NCT id (format \"NCT\" + 8 digits; a bare number is prefixed, case-insensitive). Returns full eligibility criteria, study design, primary/secondary/other endpoints, all locations, sponsor and collaborators, dates, enrollment, and a results link.",
        "input": {
          "type": "object",
          "properties": {
            "nct_id": {
              "type": "string"
            }
          },
          "required": [
            "nct_id"
          ]
        },
        "returns": "Found: `{ found: true, trial: { nct_id, title, brief_title, acronym, status, phase, study_type, conditions, interventions, sponsor, collaborators, enrollment, start_date, primary_completion_date, completion_date, brief_summary, detailed_description, eligibility_criteria, minimum_age, maximum_age, sex, healthy_volunteers (\"Yes\"/\"No\"), primary_outcomes, secondary_outcomes, other_outcomes, locations, url, has_results } }`. Missing/invalid id: `{ found: false, nct_id, error }`.",
        "example": "const result = await host.mcp(\"clinical-trials\", \"get_trial_details\", {\"nct_id\": \"NCT03661411\"})",
        "required": [
          "nct_id"
        ]
      },
      {
        "id": "search_by_sponsor",
        "connector": "clinical-trials",
        "description": "Find trials sponsored by a company or organization (partial name match, e.g. \"Pfizer\" matches \"Pfizer Inc\"). Optionally narrow by condition, phase and status. Set count_total for the total number of trials by the sponsor. Page with page_token.",
        "input": {
          "type": "object",
          "properties": {
            "sponsor_name": {
              "type": "string"
            },
            "condition": {
              "type": "string"
            },
            "phase": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "EARLY_PHASE1",
                  "PHASE1",
                  "PHASE2",
                  "PHASE3",
                  "PHASE4",
                  "NA"
                ]
              }
            },
            "status": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "NOT_YET_RECRUITING",
                  "RECRUITING",
                  "ENROLLING_BY_INVITATION",
                  "ACTIVE_NOT_RECRUITING",
                  "COMPLETED",
                  "SUSPENDED",
                  "TERMINATED",
                  "WITHDRAWN",
                  "AVAILABLE",
                  "NO_LONGER_AVAILABLE",
                  "TEMPORARILY_NOT_AVAILABLE",
                  "APPROVED_FOR_MARKETING",
                  "WITHHELD",
                  "UNKNOWN"
                ]
              }
            },
            "page_size": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 1000
            },
            "page_token": {
              "type": "string"
            },
            "count_total": {
              "type": "boolean",
              "default": false
            }
          },
          "required": [
            "sponsor_name"
          ]
        },
        "returns": "Same shape as search_trials: `{ count, total, next_page_token, items: [ trial summaries ] }`.",
        "example": "const result = await host.mcp(\"clinical-trials\", \"search_by_sponsor\", {\"sponsor_name\": \"Pfizer\", \"phase\": [\"PHASE3\"], \"count_total\": true})",
        "required": [
          "sponsor_name"
        ]
      },
      {
        "id": "search_investigators",
        "connector": "clinical-trials",
        "description": "Find principal investigators and research sites by condition, institution, location or investigator_name. institution filters on the site facility and takes precedence over location; investigator_name searches OverallOfficialName and ResponsiblePartyInvestigatorFullName. Returns site contacts (names, roles, affiliations, facilities, cities) with their trial NCT ids. page_size caps how many trials are scanned.",
        "input": {
          "type": "object",
          "properties": {
            "condition": {
              "type": "string"
            },
            "institution": {
              "type": "string"
            },
            "location": {
              "type": "string"
            },
            "investigator_name": {
              "type": "string"
            },
            "status": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "NOT_YET_RECRUITING",
                  "RECRUITING",
                  "ENROLLING_BY_INVITATION",
                  "ACTIVE_NOT_RECRUITING",
                  "COMPLETED",
                  "SUSPENDED",
                  "TERMINATED",
                  "WITHDRAWN",
                  "AVAILABLE",
                  "NO_LONGER_AVAILABLE",
                  "TEMPORARILY_NOT_AVAILABLE",
                  "APPROVED_FOR_MARKETING",
                  "WITHHELD",
                  "UNKNOWN"
                ]
              }
            },
            "page_size": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 1000
            }
          }
        },
        "returns": "`{ count, trials_analyzed, investigators: [ { name, role, affiliation, facility, location, nct_id, study_title, condition } ] }`. Deduplicated per trial by (name, role, nct_id).",
        "example": "const result = await host.mcp(\"clinical-trials\", \"search_investigators\", {\"condition\": \"Alzheimer\", \"institution\": \"Mayo Clinic\", \"page_size\": 20})",
        "required": []
      },
      {
        "id": "analyze_endpoints",
        "connector": "clinical-trials",
        "description": "Analyze primary/secondary/other outcome measures (endpoints). Provide ONLY nct_id (single-trial mode) OR condition (aggregate mode across trials); if both are given, nct_id takes precedence. Aggregate mode may be narrowed by phase and start_date_after (YYYY-MM-DD) and scans up to page_size trials. Returns the endpoint lists plus the most common measure names across the analyzed trials.",
        "input": {
          "type": "object",
          "properties": {
            "nct_id": {
              "type": "string"
            },
            "condition": {
              "type": "string"
            },
            "phase": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "EARLY_PHASE1",
                  "PHASE1",
                  "PHASE2",
                  "PHASE3",
                  "PHASE4",
                  "NA"
                ]
              }
            },
            "start_date_after": {
              "type": "string"
            },
            "page_size": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "maximum": 1000
            }
          }
        },
        "returns": "`{ trials_analyzed, nct_id (or null), condition (or null), primary_endpoints, secondary_endpoints, other_endpoints, common_measures: [str] }`. Each endpoint is `{ measure, time_frame, description, type }`; common_measures is the 20 most frequent measure names.",
        "example": "const result = await host.mcp(\"clinical-trials\", \"analyze_endpoints\", {\"nct_id\": \"NCT03661411\"})",
        "required": []
      },
      {
        "id": "search_by_eligibility",
        "connector": "clinical-trials",
        "description": "Patient-trial matching. DEFAULTS to RECRUITING trials unless status is set. min_age/max_age are the PATIENT's age (\"65 Years\", \"6 Months\") and match trials whose age window admits the patient; sex matches trials accepting that sex or all comers; eligibility_keywords searches the inclusion/exclusion criteria text (e.g. \"HbA1c > 8\", \"BRCA mutation\", \"ECOG 0-1\"). At least one of condition, eligibility_keywords, min_age, max_age or sex is required. Page with page_token.",
        "input": {
          "type": "object",
          "properties": {
            "condition": {
              "type": "string"
            },
            "eligibility_keywords": {
              "type": "string"
            },
            "min_age": {
              "type": "string"
            },
            "max_age": {
              "type": "string"
            },
            "sex": {
              "type": "string",
              "enum": [
                "ALL",
                "MALE",
                "FEMALE"
              ]
            },
            "status": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "NOT_YET_RECRUITING",
                  "RECRUITING",
                  "ENROLLING_BY_INVITATION",
                  "ACTIVE_NOT_RECRUITING",
                  "COMPLETED",
                  "SUSPENDED",
                  "TERMINATED",
                  "WITHDRAWN",
                  "AVAILABLE",
                  "NO_LONGER_AVAILABLE",
                  "TEMPORARILY_NOT_AVAILABLE",
                  "APPROVED_FOR_MARKETING",
                  "WITHHELD",
                  "UNKNOWN"
                ]
              }
            },
            "page_size": {
              "type": "integer",
              "default": 10,
              "minimum": 1,
              "maximum": 1000
            },
            "page_token": {
              "type": "string"
            }
          }
        },
        "returns": "Same shape as search_trials: `{ count, total (always null \u2014 count_total is not exposed here), next_page_token, items: [ trial summaries ] }`.",
        "example": "const result = await host.mcp(\"clinical-trials\", \"search_by_eligibility\", {\"condition\": \"diabetes\", \"min_age\": \"65 Years\", \"sex\": \"FEMALE\"})",
        "required": []
      }
    ]
  },
  {
    "id": "clinical-genomics",
    "displayName": "Clinical Genomics",
    "aliases": [
      "ClinGen",
      "CIViC",
      "Open Targets"
    ],
    "description": "Clinical genomics knowledge bases: ClinGen curations, CIViC clinical evidence, and the Open Targets Platform.",
    "useWhen": "Use when you need clinical interpretation of genes and variants \u2014 ClinGen gene-disease validity, dosage sensitivity, clinical actionability, and expert-panel (VCEP) variant pathogenicity classifications; CIViC clinical evidence, assertions, molecular profiles, diseases, and therapies for a gene or variant in cancer; or Open Targets target-disease association scores, a disease's known drugs/associated targets, a drug's mechanism of action, and arbitrary Open Targets GraphQL. Sourced from ClinGen, CIViC, and the Open Targets Platform.",
    "sources": [
      "ClinGen",
      "CIViC",
      "Open Targets"
    ],
    "termsUrl": "https://platform-docs.opentargets.org/licence",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "clingen_gene_validity",
        "connector": "clinical-genomics",
        "description": "ClinGen gene-disease validity curations (how strong the evidence is that variation in a gene causes a disease: Definitive/Strong/Moderate/Limited/Disputed/Refuted/No Known Disease Relationship). Omit gene to list all 3,600+ curations.",
        "input": {
          "type": "object",
          "properties": {
            "gene": {
              "type": "string"
            }
          }
        },
        "returns": "`{ \"total\": int, \"records\": [ { \"gene_symbol\": str, \"hgnc_id\": str, \"disease_label\": str, \"mondo_id\": str, \"moi\": str, \"sop\": str, \"classification\": str, \"expert_panel\": str, \"affiliate_id\": str, \"animal_model_only\": bool, \"assertion_id\": str } ], \"source\": str }` \u2014 records filtered to the gene (exact, case-insensitive) or the full table when gene is omitted.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"clingen_gene_validity\", {\"gene\": \"BRCA2\"})",
        "required": []
      },
      {
        "id": "clingen_dosage_sensitivity",
        "connector": "clinical-genomics",
        "description": "ClinGen dosage sensitivity curations: haploinsufficiency and triplosensitivity assertions for genes (and optionally ISCA genomic/CNV regions). A gene symbol or an ISCA region id filters exactly; omit for the full table.",
        "input": {
          "type": "object",
          "properties": {
            "gene": {
              "type": "string"
            },
            "include_regions": {
              "type": "boolean",
              "default": false
            }
          }
        },
        "returns": "`{ \"total\": int, \"records\": [ { \"record_type\": \"gene\"|\"region\", \"symbol\": str, \"id\": str, \"cytoband\": str, \"grch37\": str, \"grch38\": str, \"haploinsufficiency\": { \"code\": str, \"label\": str }|null, \"triplosensitivity\": {...}|null, \"haplo_disease\": str, \"haplo_mondo\": str, \"triplo_disease\": str, \"triplo_mondo\": str, \"omim\": str, \"morbid\": str } ], \"source\": str }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"clingen_dosage_sensitivity\", {\"gene\": \"TP53\"})",
        "required": []
      },
      {
        "id": "clingen_actionability",
        "connector": "clinical-genomics",
        "description": "ClinGen clinical actionability curations: for disorders associated with a gene, whether early intervention in pre-symptomatic carriers is actionable (intervention/outcome pairs with severity, likelihood, effectiveness, nature-of-intervention component scores and the total score). Gene filter matches any member of multi-gene topics.",
        "input": {
          "type": "object",
          "properties": {
            "gene": {
              "type": "string"
            },
            "context": {
              "type": "string",
              "enum": [
                "adult",
                "pediatric",
                "both"
              ],
              "default": "both"
            }
          }
        },
        "returns": "`{ \"adult\"?: { \"total\": int, \"records\": [...] }, \"pediatric\"?: { \"total\": int, \"records\": [...] }, \"source\": str }` \u2014 one block per requested context; each record has doc_id, genes, disease, outcome, intervention, severity, likelihood, nature_of_intervention, effectiveness, overall_score, release/release_date.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"clingen_actionability\", {\"gene\": \"BRCA1\", \"context\": \"adult\"})",
        "required": []
      },
      {
        "id": "clingen_variant_classifications",
        "connector": "clinical-genomics",
        "description": "ClinGen Evidence Repository (ERepo) expert-panel variant pathogenicity classifications (VCEP interpretations under ACMG criteria). Provide EXACTLY ONE of gene (HGNC symbol), caid (ClinGen canonical allele id, e.g. CA114360), or hgvs (e.g. NM_000277.2:c.1222C>T). Complete retrieval (matchLimit=none).",
        "input": {
          "type": "object",
          "properties": {
            "gene": {
              "type": "string"
            },
            "caid": {
              "type": "string"
            },
            "hgvs": {
              "type": "string"
            }
          }
        },
        "returns": "`{ \"total\": int, \"records\": [ { \"interpretation_id\": str, \"uuid\": str, \"caid\": str, \"clinvar_variation_id\": str, \"gene_symbol\": str, \"gene_ncbi_id\": str, \"condition_id\": str, \"condition_label\": str, \"hgvs\": [str], \"evidence_links\": [str], \"published_date\": str, \"guidelines\": [ { \"guideline\": str, \"guideline_id\": str, \"outcome\": str, \"agents\": [ { \"agent_id\": str, \"affiliation\": str, \"outcome\": str, \"evidence_codes_met\": [str], \"evidence_codes_not_met\": [str] } ] } ] } ], \"query\": { <gene|caid|hgvs>: str }, \"source\": str }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"clingen_variant_classifications\", {\"gene\": \"BRCA1\"})",
        "required": []
      },
      {
        "id": "civic_search_genes",
        "connector": "clinical-genomics",
        "description": "Find CIViC gene records by exact Entrez symbol (e.g. \"BRAF\"). Fully paginated, count-verified. Use the returned CIViC gene id with civic_gene_variants.",
        "input": {
          "type": "object",
          "properties": {
            "entrez_symbol": {
              "type": "string"
            }
          },
          "required": [
            "entrez_symbol"
          ]
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ { \"id\": int, \"name\": str, \"entrezId\": int, \"fullName\": str, \"featureAliases\": [str], \"description\": str, \"link\": str } ], \"query\": { \"mode\": \"search_genes\", \"entrez_symbol\": str } }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_search_genes\", {\"entrez_symbol\": \"BRAF\"})",
        "required": [
          "entrez_symbol"
        ]
      },
      {
        "id": "civic_gene_variants",
        "connector": "clinical-genomics",
        "description": "All variants of one CIViC gene (by CIViC gene id), fully paginated \u2014 complete even for genes with hundreds of variants. Sorted by variant id.",
        "input": {
          "type": "object",
          "properties": {
            "gene_id": {
              "type": "integer"
            }
          },
          "required": [
            "gene_id"
          ]
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ { \"id\": int, \"name\": str, \"link\": str, \"variantAliases\": [str], \"variantTypes\": [{ \"id\": int, \"name\": str, \"soid\": str }], \"feature\": { \"id\": int, \"name\": str }, \"singleVariantMolecularProfileId\": int, \"alleleRegistryId\"?: str, \"clinvarIds\"?: [str], \"hgvsDescriptions\"?: [str], \"coordinates\"?: {...} } ], \"query\": {...} }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_gene_variants\", {\"gene_id\": 5})",
        "required": [
          "gene_id"
        ]
      },
      {
        "id": "civic_get_variant",
        "connector": "clinical-genomics",
        "description": "One CIViC variant by its CIViC variant id (aliases, variant types, feature/gene linkage, coordinates for gene variants). Returns found=false if absent.",
        "input": {
          "type": "object",
          "properties": {
            "variant_id": {
              "type": "integer"
            }
          },
          "required": [
            "variant_id"
          ]
        },
        "returns": "`{ \"query\": { \"mode\": \"variant\", \"id\": int }, \"found\": bool, \"record\": { \"id\": int, \"name\": str, \"variantTypes\": [...], \"feature\": {...}, \"coordinates\"?: {...}, ... }|null }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_get_variant\", {\"variant_id\": 12})",
        "required": [
          "variant_id"
        ]
      },
      {
        "id": "civic_search_variants",
        "connector": "clinical-genomics",
        "description": "Search CIViC variants by name substring (e.g. \"V600\"), optionally scoped to a CIViC gene id. Fully paginated; sorted by variant id.",
        "input": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            },
            "gene_id": {
              "type": "integer"
            }
          },
          "required": [
            "name"
          ]
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ <variant record> ], \"query\": { \"mode\": \"search_variants\", \"name\": str, \"gene_id\": int|null } }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_search_variants\", {\"name\": \"V600\", \"gene_id\": 5})",
        "required": [
          "name"
        ]
      },
      {
        "id": "civic_get_evidence_item",
        "connector": "clinical-genomics",
        "description": "One CIViC evidence item by id: clinical significance of a molecular profile in a disease/therapy context (evidence level A-E, type, direction, significance, rating, disease, therapies, source). Returns found=false if absent.",
        "input": {
          "type": "object",
          "properties": {
            "evidence_id": {
              "type": "integer"
            }
          },
          "required": [
            "evidence_id"
          ]
        },
        "returns": "`{ \"query\": { \"mode\": \"evidenceItem\", \"id\": int }, \"found\": bool, \"record\": { \"id\": int, \"evidenceLevel\": str, \"evidenceType\": str, \"evidenceDirection\": str, \"significance\": str, \"evidenceRating\": int, \"disease\": {...}, \"therapies\": [...], \"molecularProfile\": {...}, \"source\": {...}, ... }|null }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_get_evidence_item\", {\"evidence_id\": 1409})",
        "required": [
          "evidence_id"
        ]
      },
      {
        "id": "civic_search_evidence",
        "connector": "clinical-genomics",
        "description": "Search CIViC evidence items by any combination of filters; fully paginated, count-verified, sorted by ascending evidence id. Enum filters take CIViC GraphQL enum values verbatim (evidence_level \"A\"..\"E\"; evidence_type PREDICTIVE|PROGNOSTIC|DIAGNOSTIC|PREDISPOSING|ONCOGENIC|FUNCTIONAL; evidence_direction SUPPORTS|DOES_NOT_SUPPORT; status ACCEPTED|SUBMITTED|REJECTED|ALL). Provide at least one filter \u2014 no filters walks the entire 10k+ corpus.",
        "input": {
          "type": "object",
          "properties": {
            "disease_name": {
              "type": "string"
            },
            "therapy_name": {
              "type": "string"
            },
            "evidence_level": {
              "type": "string"
            },
            "evidence_type": {
              "type": "string"
            },
            "evidence_direction": {
              "type": "string"
            },
            "significance": {
              "type": "string"
            },
            "variant_origin": {
              "type": "string"
            },
            "evidence_rating": {
              "type": "integer"
            },
            "status": {
              "type": "string"
            },
            "molecular_profile_name": {
              "type": "string"
            },
            "molecular_profile_id": {
              "type": "integer"
            },
            "variant_id": {
              "type": "integer"
            },
            "disease_id": {
              "type": "integer"
            },
            "therapy_id": {
              "type": "integer"
            },
            "phenotype_id": {
              "type": "integer"
            },
            "source_id": {
              "type": "integer"
            },
            "assertion_id": {
              "type": "integer"
            }
          }
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ <evidence record> ], \"query\": { \"mode\": \"search_evidence\", \"filters\": {...} } }` \u2014 records sorted by ascending evidence id.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_search_evidence\", {\"disease_name\": \"melanoma\", \"evidence_level\": \"A\"})",
        "required": []
      },
      {
        "id": "civic_get_assertion",
        "connector": "clinical-genomics",
        "description": "One CIViC assertion by id: an expert-curated summary claim (AMP/ASCO/CAP tier, ACMG/ClinGen codes, FDA companion-test flags) aggregating evidence for a molecular profile in a disease/therapy context. Returns found=false if absent.",
        "input": {
          "type": "object",
          "properties": {
            "assertion_id": {
              "type": "integer"
            }
          },
          "required": [
            "assertion_id"
          ]
        },
        "returns": "`{ \"query\": { \"mode\": \"assertion\", \"id\": int }, \"found\": bool, \"record\": { \"id\": int, \"assertionType\": str, \"assertionDirection\": str, \"significance\": str, \"ampLevel\": str, \"summary\": str, \"acmgCodes\": [...], \"clingenCodes\": [...], \"disease\": {...}, \"therapies\": [...], \"evidenceItemsCount\": int, ... }|null }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_get_assertion\", {\"assertion_id\": 7})",
        "required": [
          "assertion_id"
        ]
      },
      {
        "id": "civic_search_assertions",
        "connector": "clinical-genomics",
        "description": "Search CIViC assertions by any combination of filters; fully paginated, count-verified, sorted by ascending assertion id. assertion_type PREDICTIVE|PROGNOSTIC|DIAGNOSTIC|PREDISPOSING|ONCOGENIC; assertion_direction SUPPORTS|DOES_NOT_SUPPORT; amp_level e.g. TIER_I_LEVEL_A; status ACCEPTED|SUBMITTED|REJECTED|ALL. No filters walks the full corpus.",
        "input": {
          "type": "object",
          "properties": {
            "disease_name": {
              "type": "string"
            },
            "therapy_name": {
              "type": "string"
            },
            "assertion_type": {
              "type": "string"
            },
            "assertion_direction": {
              "type": "string"
            },
            "significance": {
              "type": "string"
            },
            "amp_level": {
              "type": "string"
            },
            "status": {
              "type": "string"
            },
            "molecular_profile_name": {
              "type": "string"
            },
            "molecular_profile_id": {
              "type": "integer"
            },
            "variant_id": {
              "type": "integer"
            },
            "variant_name": {
              "type": "string"
            },
            "disease_id": {
              "type": "integer"
            },
            "therapy_id": {
              "type": "integer"
            },
            "phenotype_id": {
              "type": "integer"
            },
            "evidence_id": {
              "type": "integer"
            },
            "summary": {
              "type": "string"
            }
          }
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ <assertion record> ], \"query\": { \"mode\": \"search_assertions\", \"filters\": {...} } }` \u2014 records sorted by ascending assertion id.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_search_assertions\", {\"disease_name\": \"melanoma\"})",
        "required": []
      },
      {
        "id": "civic_get_molecular_profile",
        "connector": "clinical-genomics",
        "description": "One CIViC molecular profile by id (variant combination that evidence/assertions attach to), incl. parsed name, score, and component variants. Returns found=false if absent.",
        "input": {
          "type": "object",
          "properties": {
            "mp_id": {
              "type": "integer"
            }
          },
          "required": [
            "mp_id"
          ]
        },
        "returns": "`{ \"query\": { \"mode\": \"molecularProfile\", \"id\": int }, \"found\": bool, \"record\": { \"id\": int, \"name\": str, \"rawName\": str, \"molecularProfileScore\": float, \"isComplex\": bool, \"isMultiVariant\": bool, \"molecularProfileAliases\": [str], \"variants\": [{ \"id\": int, \"name\": str, \"feature\": {...} }], \"evidenceCountsByStatus\": {...} }|null }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_get_molecular_profile\", {\"mp_id\": 12})",
        "required": [
          "mp_id"
        ]
      },
      {
        "id": "civic_search_molecular_profiles",
        "connector": "clinical-genomics",
        "description": "Search CIViC molecular profiles by name substring (e.g. \"BRAF V600E\"). Fully paginated; sorted by id.",
        "input": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            }
          },
          "required": [
            "name"
          ]
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ <molecular profile record> ], \"query\": { \"mode\": \"search_molecular_profiles\", \"name\": str } }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_search_molecular_profiles\", {\"name\": \"BRAF V600E\"})",
        "required": [
          "name"
        ]
      },
      {
        "id": "civic_search_diseases",
        "connector": "clinical-genomics",
        "description": "Search CIViC disease records by name substring (e.g. \"melanoma\"). Returns DOIDs + display names; fully paginated; sorted by id.",
        "input": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            }
          },
          "required": [
            "name"
          ]
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ { \"id\": int, \"name\": str, \"displayName\": str, \"doid\": str, \"diseaseUrl\": str, \"diseaseAliases\": [str], \"link\": str } ], \"query\": { \"mode\": \"search_diseases\", \"name\": str } }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_search_diseases\", {\"name\": \"melanoma\"})",
        "required": [
          "name"
        ]
      },
      {
        "id": "civic_search_therapies",
        "connector": "clinical-genomics",
        "description": "Search CIViC therapy records by name substring (e.g. \"vemurafenib\"). Returns NCIt ids + names; fully paginated; sorted by id.",
        "input": {
          "type": "object",
          "properties": {
            "name": {
              "type": "string"
            }
          },
          "required": [
            "name"
          ]
        },
        "returns": "`{ \"total_count\": int, \"pages_fetched\": int, \"records\": [ { \"id\": int, \"name\": str, \"ncitId\": str, \"therapyUrl\": str, \"therapyAliases\": [str], \"link\": str } ], \"query\": { \"mode\": \"search_therapies\", \"name\": str } }`.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"civic_search_therapies\", {\"name\": \"vemurafenib\"})",
        "required": [
          "name"
        ]
      },
      {
        "id": "open_targets_graphql",
        "connector": "clinical-genomics",
        "description": "Run an arbitrary GraphQL query against the Open Targets Platform API (targets, diseases, drugs, target-disease association scores, evidence, tractability, safety, known drugs). Introspection queries work for schema discovery. Note knownDrugs was renamed to drugAndClinicalCandidates upstream.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "variables": {
              "type": "object"
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ \"data\": {...}|null, \"attempts\": int, \"errors\"?: [ { \"message\": str } ] }` \u2014 the raw GraphQL data payload; transient HTTP-200 \"Internal server error\" responses are retried up to 3 attempts before being surfaced in errors.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"open_targets_graphql\", {\"query\": \"query($id: String!){ target(ensemblId: $id){ approvedSymbol associatedDiseases{ count } } }\", \"variables\": {\"id\": \"ENSG00000157764\"}})",
        "required": [
          "query"
        ]
      },
      {
        "id": "open_targets_disease_drugs",
        "connector": "clinical-genomics",
        "description": "Known/investigational drugs for a disease (Open Targets Platform) \u2014 wraps Disease.drugAndClinicalCandidates. efo_id is a disease ontology id (EFO/MONDO/etc., e.g. \"MONDO_0004992\").",
        "input": {
          "type": "object",
          "properties": {
            "efo_id": {
              "type": "string"
            },
            "size": {
              "type": "integer",
              "default": 25
            }
          },
          "required": [
            "efo_id"
          ]
        },
        "returns": "`{ \"id\": str, \"name\": str, \"drugAndClinicalCandidates\": { \"count\": int, \"rows\": [ { \"id\": str, \"maxClinicalStage\": str, \"drug\": { \"id\": str, \"name\": str, \"drugType\": str } } ] } }` (rows capped at `size`, default 25), or `{ \"errors\": [...] }` on GraphQL error / unknown id.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"open_targets_disease_drugs\", {\"efo_id\": \"MONDO_0004992\", \"size\": 25})",
        "required": [
          "efo_id"
        ]
      },
      {
        "id": "open_targets_disease_targets",
        "connector": "clinical-genomics",
        "description": "Top associated targets for a disease, ranked by Open Targets overall association score \u2014 wraps Disease.associatedTargets. efo_id is a disease ontology id (EFO/MONDO/etc.).",
        "input": {
          "type": "object",
          "properties": {
            "efo_id": {
              "type": "string"
            },
            "size": {
              "type": "integer",
              "default": 25
            }
          },
          "required": [
            "efo_id"
          ]
        },
        "returns": "`{ \"id\": str, \"name\": str, \"associatedTargets\": { \"count\": int, \"rows\": [ { \"score\": float, \"target\": { \"id\": str, \"approvedSymbol\": str } } ] } }` (up to `size` rows, default 25), or `{ \"errors\": [...] }` on GraphQL error / unknown id.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"open_targets_disease_targets\", {\"efo_id\": \"MONDO_0004992\", \"size\": 25})",
        "required": [
          "efo_id"
        ]
      },
      {
        "id": "open_targets_drug",
        "connector": "clinical-genomics",
        "description": "Drug details by ChEMBL id (Open Targets Platform) \u2014 name, type, maximum clinical stage, and mechanisms of action (target + action type). chembl_id e.g. \"CHEMBL1201583\".",
        "input": {
          "type": "object",
          "properties": {
            "chembl_id": {
              "type": "string"
            }
          },
          "required": [
            "chembl_id"
          ]
        },
        "returns": "`{ \"id\": str, \"name\": str, \"drugType\": str, \"maximumClinicalStage\": str, \"mechanismsOfAction\": { \"rows\": [ { \"mechanismOfAction\": str, \"actionType\": str, \"targets\": [ { \"id\": str, \"approvedSymbol\": str } ] } ] } }`, or `{ \"errors\": [...] }` on GraphQL error / unknown id.",
        "example": "const result = await host.mcp(\"clinical-genomics\", \"open_targets_drug\", {\"chembl_id\": \"CHEMBL1201583\"})",
        "required": [
          "chembl_id"
        ]
      }
    ]
  },
  {
    "id": "structures",
    "displayName": "Structures & Interactions",
    "description": "Structures and molecular interactions \u2014 PDB structures, AlphaFold predictions, EMDB cryo-EM entries, Complex Portal complexes, IntAct interaction networks.",
    "useWhen": "Use when you need a macromolecular 3D structure or a molecular interaction \u2014 experimental PDB entries (search, summaries, polymer entities, ligands), AlphaFold predicted models, EMDB cryo-EM metadata/validation, curated Complex Portal complexes, or IntAct binary interactions and networks.",
    "sources": [
      "PDB",
      "AlphaFold",
      "EMDB",
      "Complex Portal",
      "IntAct"
    ],
    "termsUrl": "https://www.rcsb.org/pages/usage-policy",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "emdb_get_entries",
        "connector": "structures",
        "description": "Fetch structured metadata records for EMDB cryo-EM 3D map entries. Accepts accessions as 'EMD-1234', 'emd-1234' or '1234'. Each record carries title, structure determination method (singleParticle / helical / tomography / subtomogramAveraging / electronCrystallography), resolution in Angstrom (null for entries with no reported resolution, e.g. raw tomograms) and the resolution method, deposition/release dates, sample and macromolecule/supramolecule names, fitted PDB model IDs (empty list when no model is fitted), primary citation (journal, year, first author, DOI, PMID), map dimensions and voxel size, and status. Obsolete entries report is_obsolete=true plus superseded_by accessions. Unknown accessions come back as {\"emdb_id\", \"error\": \"not_found\"} \u2014 never silently dropped. Metadata only; map volumes are never downloaded.",
        "input": {
          "type": "object",
          "properties": {
            "emdb_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "emdb_ids"
          ]
        },
        "returns": "{n_requested, records:[{emdb_id, title, status, is_obsolete, superseded_by, obsolete_date, method, aggregation_state, resolution_angstrom, resolution_method, deposition_date, header_release_date, map_release_date, update_date, sample_name, macromolecule_names, supramolecule_names, fitted_pdb_ids, has_fitted_model, citation:{title, journal, year, published, doi, pmid, first_author, author_count}, map:{file, size_kbytes, dimensions:{col,row,sec}, voxel_size_angstrom:{x,y,z:{value,units}}}} | {emdb_id, error:\"not_found\"}]}.",
        "example": "const result = await host.mcp(\"structures\", \"emdb_get_entries\", {\"emdb_ids\": [\"EMD-11638\", \"emd-3061\", \"1234\"]})",
        "required": [
          "emdb_ids"
        ]
      },
      {
        "id": "emdb_search_entries",
        "connector": "structures",
        "description": "Search EMDB with a Solr-style query; complete paged retrieval of compact rows. Query examples: 'title:\"apoferritin\" AND resolution:[0 TO 1.5]', 'structure_determination_method:\"singleParticle\"', 'current_status:\"REL\" AND release_date:[2024-01-01T00:00:00Z TO *]'. Args: query (Solr query string); max_rows (row cap, default 1000). Returns num_found_released (the API's own released-entry count from the facet route \u2014 ground truth), rows_retrieved, rows_by_status (REL vs OBS \u2014 the search route returns obsolete entries too but they are NOT counted as released), released_complete (true iff every released match was retrieved; false means max_rows truncated the sweep or the counts disagree), and records: compact per-entry rows (emdb_id, title, resolution, structure_determination_method, current_status, release_date, fitted_pdbs) sorted by EMD accession.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "max_rows": {
              "type": "integer",
              "default": 1000
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "{query, num_found_released (facet-route released count), rows_retrieved (REL+OBS, deduped), rows_by_status (e.g. {\"OBS\":19,\"REL\":890}), released_complete, records:[{emdb_id, title, resolution, structure_determination_method, fitted_pdbs, current_status, release_date}], max_rows}.",
        "example": "const result = await host.mcp(\"structures\", \"emdb_search_entries\", {\"query\": \"title:\\\"apoferritin\\\" AND resolution:[0 TO 1.5]\", \"max_rows\": 500})",
        "required": [
          "query"
        ]
      },
      {
        "id": "emdb_get_entry_section",
        "connector": "structures",
        "description": "Fetch one detailed metadata section for EMDB entries. Sections: 'publications' \u2014 primary citation with complete ordered author list, auxiliary citations, external references (PMID/DOI/ISSN/CSD); 'map' \u2014 file, format, data type, dimensions, voxel spacing, origin, axis order, cell, voxel statistics, contour levels, symmetry; 'sample' \u2014 per-macromolecule records (type, molecular weight, copies, EC number, source organism + NCBI taxid, sequence cross-refs) and per-supramolecule records; 'imaging' \u2014 microscope, voltage, electron source, detector, dose, imaging modes, defocus range, magnification, Cs, cryogen, grid/buffer/vitrification conditions (one record per microscopy session \u2014 entries can carry several). Args: emdb_ids (accession list, any of EMD-1234/emd-1234/1234); section (one of publications/map/sample/imaging). Unknown accessions are reported with \"error\": \"not_found\". Use emdb_get_entries first when you only need the headline record.",
        "input": {
          "type": "object",
          "properties": {
            "emdb_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "section": {
              "type": "string",
              "enum": [
                "publications",
                "map",
                "sample",
                "imaging"
              ]
            }
          },
          "required": [
            "emdb_ids",
            "section"
          ]
        },
        "returns": "{n_requested, section, records:[<section record> | {emdb_id, error:\"not_found\"}]}. Section record shapes: publications -> {emdb_id, primary_citation, secondary_citations}; map -> {emdb_id, file, format, dimensions, pixel_spacing_angstrom, cell, statistics, contour_levels, space_group, ...}; sample -> {emdb_id, name, macromolecules[...], supramolecules[...]}; imaging -> {emdb_id, method, microscopy[...], specimen_preparations[...]}.",
        "example": "const result = await host.mcp(\"structures\", \"emdb_get_entry_section\", {\"emdb_ids\": [\"EMD-11638\"], \"section\": \"imaging\"})",
        "required": [
          "emdb_ids",
          "section"
        ]
      },
      {
        "id": "emdb_get_validation",
        "connector": "structures",
        "description": "Fetch numeric validation-analysis metrics for EMDB entries. Per entry (from the EMDB /analysis route): Q-score, atom inclusion, recommended/predicted/rawmap contour levels, model/mask volumes, model-map ratio, surface metrics \u2014 where the validation pipeline has computed them. available_blocks lists every block the validation service returned; sparse payloads (tomograms, model-free or historical entries) yield explicit nulls. Entries with no validation analysis report has_validation_analysis=false \u2014 never silently dropped.",
        "input": {
          "type": "object",
          "properties": {
            "emdb_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "emdb_ids"
          ]
        },
        "returns": "{n_requested, records:[{emdb_id, has_validation_analysis, resolution_angstrom, qscore_average, atom_inclusion_average, available_blocks:[...], recommended_contour_level, predicated_contour_level, rawmap_contour_level, model_map_ratio, model_volume, mask_volume, surfaces, surface_ratio, feature_assessment, relion_mask_coverage}]} \u2014 scalar blocks are the raw numeric objects or null; unknown accessions get has_validation_analysis=false + error:\"not_found\".",
        "example": "const result = await host.mcp(\"structures\", \"emdb_get_validation\", {\"emdb_ids\": [\"EMD-11638\", \"EMD-3061\"]})",
        "required": [
          "emdb_ids"
        ]
      },
      {
        "id": "complexportal_get_complexes",
        "connector": "structures",
        "description": "Fetch curated Complex Portal records by CPX accession. Each record: complex AC, recommended/systematic names + synonyms, species and taxid, participant list with stoichiometry (min/max copies), biological role and interactor type, evidence ECO code, GO annotations, and cross-references \u2014 the manually curated description of a stable macromolecular complex. Records come back in input order; unknown accessions are listed in `not_found` rather than silently dropped. For binary interaction *evidence* (who binds whom in which experiment) use the intact_* tools instead.",
        "input": {
          "type": "object",
          "properties": {
            "complex_acs": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "complex_acs"
          ]
        },
        "returns": "{n_requested, records:[{complex_ac, intact_ac, name, systematic_name, synonyms:[...], species_name, taxid, predicted_complex, evidence:{eco_code, description, confidence_score}, participants:[{identifier, name, description, interactor_type, interactor_type_mi, biological_role, biological_role_mi, stoichiometry_min, stoichiometry_max, stoichiometry_raw}], go_annotations:[{go_id, aspect, term}], cross_references:[{database, identifier, qualifier, description}], functions:[...], complex_assemblies:[...], release_dates:[...]}], not_found:[...]}. Records preserve the de-duplicated input order.",
        "example": "const result = await host.mcp(\"structures\", \"complexportal_get_complexes\", {\"complex_acs\": [\"CPX-2158\", \"CPX-2419\"]})",
        "required": [
          "complex_acs"
        ]
      },
      {
        "id": "complexportal_search_by_participant",
        "connector": "structures",
        "description": "Search Complex Portal for complexes containing a molecule. `accession` is a participant accession \u2014 UniProt (e.g. 'P04637'), ChEBI, or RNAcentral. With participants_only=true (default) the search is field-qualified (pxref:<accession>) so only complexes that actually contain the molecule as a curated participant are returned; with false the bare accession is matched as free text too (descriptions, names), which over-reports but can catch mentions. All result pages are retrieved and the row count is verified against the service-reported total (total_reported == total_retrieved, or the call fails loudly). Hits are compact records (complex_ac, name, species, interactors) sorted by complex accession; fetch full detail with complexportal_get_complexes.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string"
            },
            "participants_only": {
              "type": "boolean",
              "default": true
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "{query_accession, solr_query, total_reported, total_retrieved, complexes:[{complex_ac, name, species_name, taxid, predicted_complex, interactors:[{identifier, name, interactor_type, stoichiometry_raw}]}]}. complexes are sorted by CPX accession (numeric); total_reported == total_retrieved is enforced or the call throws.",
        "example": "const result = await host.mcp(\"structures\", \"complexportal_search_by_participant\", {\"accession\": \"P69905\", \"participants_only\": true})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "intact_fetch_interactions",
        "connector": "structures",
        "description": "Retrieve ALL IntAct binary interactions matching a query, MI-score filtered. `query` is a UniProt accession (e.g. 'P04637'), gene symbol, free text, or any IntAct Solr query. Retrieval is a complete paginated sweep verified against the server-reported total (n_records == total_elements, or the call FAILS LOUDLY \u2014 silent truncation is impossible). min_mi_score/max_mi_score filter server-side on the IntAct MI confidence score (0.45 is a common medium-confidence floor); interactor_species filters by species name or taxid (e.g. [\"Homo sapiens\"] or [\"9606\"]). Records are slim and structured: interactor pair (IntAct ACs, database identifiers, molecule names, species/taxids), interaction type, detection method (+MI id), experimental roles, host organism, MI score, PubMed id, first author, source database \u2014 sorted by DESCENDING MI score. Output lists at most max_records_returned records (records_truncated=true when the full verified sweep was larger; n_records always reports the true total). Large queries (e.g. CFTR ~10k interactions) take a while \u2014 narrow with min_mi_score or species when possible.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "min_mi_score": {
              "type": "number",
              "default": 0
            },
            "max_mi_score": {
              "type": "number",
              "default": 1
            },
            "interactor_species": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "max_records_returned": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ query, min_mi_score, max_mi_score, total_elements, n_records, records_truncated, n_records_returned, records: [{ interaction_ac, binary_interaction_id, ac_a, ac_b, id_a, id_b, id_a_database, id_b_database, molecule_a, molecule_b, species_a, species_b, taxid_a, taxid_b, interaction_type, interaction_type_mi, detection_method, detection_method_mi, experimental_role_a, experimental_role_b, host_organism, expansion_method, mi_score, negative, pubmed_id, first_author, source_database }] }`. `n_records` is the true count-verified total; `records` is capped at max_records_returned (records_truncated=true when the sweep was larger), sorted by descending mi_score. `records` is `[]` when nothing matches. Throws when the sweep count fails to verify.",
        "example": "const result = await host.mcp(\"structures\", \"intact_fetch_interactions\", {\"query\": \"P04637\", \"min_mi_score\": 0.45, \"interactor_species\": [\"Homo sapiens\"], \"max_records_returned\": 200})",
        "required": [
          "query"
        ]
      },
      {
        "id": "intact_get_interactor",
        "connector": "structures",
        "description": "Resolve a molecule to its IntAct interactor record(s). `query` is a UniProt accession, gene symbol, or IntAct interactor AC (e.g. 'EBI-7090529'). Returns ALL matching interactor records with an explicit n_matches \u2014 a UniProt accession can resolve to the canonical protein plus chain/isoform interactors, and this tool never silently picks one. Each record: interactor_ac, preferred_identifier, name, species, taxid, interactor_type, and the interaction_count seen by IntAct (useful for sizing an intact_fetch_interactions sweep).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ query, n_matches, interactors: [{ interactor_ac, preferred_identifier, name, species, taxid, interactor_type, interaction_count }] }` \u2014 all matches, sorted by interactor_ac. `interactors` is `[]` and `n_matches` is 0 when nothing resolves.",
        "example": "const result = await host.mcp(\"structures\", \"intact_get_interactor\", {\"query\": \"P04637\"})",
        "required": [
          "query"
        ]
      },
      {
        "id": "intact_get_interaction_details",
        "connector": "structures",
        "description": "Full curated detail for ONE IntAct interaction AC (e.g. 'EBI-15635490'). Returns interaction type, host organism, detection method, publication, cross-references, annotations, kinetic/affinity parameters and confidences, plus per-participant records (identifier, species, biological and experimental role, participant detection methods) unless include_participants=false. Get interaction ACs from intact_fetch_interactions records (the interaction_ac field). Unknown ACs return { interaction_ac, error: 'not_found' }.",
        "input": {
          "type": "object",
          "properties": {
            "interaction_ac": {
              "type": "string"
            },
            "include_participants": {
              "type": "boolean",
              "default": true
            }
          },
          "required": [
            "interaction_ac"
          ]
        },
        "returns": "`{ interaction_ac, short_label, type: {name, mi}, detection_method: {name, mi}, host_organism, negative, publication: {pubmed_id, title, journal, publication_date, authors}, xrefs: [{database, database_mi, identifier, qualifier}], annotations: [{topic, topic_mi, description}], parameters, confidences, participants?: [{participant_ac, short_label, identifier, identifier_database, description, type, species, taxid, biological_role, experimental_role, detection_methods}], n_participants? }`. Unknown AC -> `{ interaction_ac, error: 'not_found' }`.",
        "example": "const result = await host.mcp(\"structures\", \"intact_get_interaction_details\", {\"interaction_ac\": \"EBI-15635490\", \"include_participants\": true})",
        "required": [
          "interaction_ac"
        ]
      },
      {
        "id": "intact_build_network",
        "connector": "structures",
        "description": "Build a depth-1 IntAct interaction network around seed proteins. `seed_accessions` are UniProt accessions. Step 1: a complete, count-verified MI-score-filtered interaction sweep per seed. Step 2: the partners of every seed edge plus the seeds form the node set. Step 3: partner-partner edges are only discoverable by querying the partners themselves, so up to max_interactors_expanded partners are queried (most-connected first, ties by identifier) and edges with BOTH endpoints inside the node set are kept. The expansion block reports exactly which partners were / were not expanded (expansion.complete=false means more partner-partner edges may exist). Output: nodes, edges (with MI score, detection method, PubMed id), per-seed sweep stats. Keep seeds few and min_mi_score >= 0.45 \u2014 every expansion is a full paginated sweep.",
        "input": {
          "type": "object",
          "properties": {
            "seed_accessions": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "min_mi_score": {
              "type": "number",
              "default": 0.45
            },
            "max_interactors_expanded": {
              "type": "integer",
              "default": 25
            },
            "interactor_species": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "seed_accessions"
          ]
        },
        "returns": "`{ seeds, min_mi_score, n_nodes, nodes: [id...], n_edges, edges: [slim_record + {origin}], seed_sweeps: {seed: {total_elements, n_records}}, expansion: {max_interactors_expanded, n_partners, expanded: [id...], not_expanded: [id...], complete} }`. `nodes` are sorted identifiers; `edges` are sorted by descending mi_score; `expansion.complete=false` means partner-partner edges beyond the cap may exist.",
        "example": "const result = await host.mcp(\"structures\", \"intact_build_network\", {\"seed_accessions\": [\"P04637\", \"Q00987\"], \"min_mi_score\": 0.45, \"max_interactors_expanded\": 25})",
        "required": [
          "seed_accessions"
        ]
      },
      {
        "id": "pdb_search_structures",
        "connector": "structures",
        "description": "Search RCSB PDB entries by attribute filters; paged, capped + flagged. All filters AND together; at least one is required. `text` is a full-text relevance query ('p53 DNA binding domain'); `organism` is an exact source-organism lineage name ('Homo sapiens' \u2014 matches at any lineage level, so 'Eukaryota' works too); `taxonomy_id` an NCBI taxid (9606); `uniprot_accession` finds entries whose polymer entities map to that UniProt ('P04637' -> every p53 structure); `experimental_method` is the PDB vocabulary ('X-RAY DIFFRACTION', 'ELECTRON MICROSCOPY', 'SOLUTION NMR', ... \u2014 case-insensitive, unknown values error with the full list); `max_resolution_angstrom` keeps entries at or below that resolution; `ligand_comp_id` requires a bound nonpolymer component by chem-comp id ('ZN', 'ATP', 'HEM'). include_computed_models=true adds computed structure models (e.g. AlphaFold) to the default experimental-only results. Returns total_count (the API's own match total \u2014 ground truth), n_retrieved, truncated (true iff total_count > n_retrieved; max_rows, 1..1000, caps retrieval), and records [{pdb_id, score}] in relevance order. Identifiers only \u2014 chain to pdb_get_structures for metadata.",
        "input": {
          "type": "object",
          "properties": {
            "text": {
              "type": "string"
            },
            "organism": {
              "type": "string"
            },
            "taxonomy_id": {
              "type": "integer"
            },
            "uniprot_accession": {
              "type": "string"
            },
            "experimental_method": {
              "type": "string"
            },
            "max_resolution_angstrom": {
              "type": "number"
            },
            "ligand_comp_id": {
              "type": "string"
            },
            "include_computed_models": {
              "type": "boolean",
              "default": false
            },
            "max_rows": {
              "type": "integer",
              "default": 100
            }
          }
        },
        "returns": "{total_count (API match total), n_retrieved, truncated (total_count > n_retrieved), max_rows, records:[{pdb_id, score}]} in relevance order; records is [] when nothing matches.",
        "example": "const result = await host.mcp(\"structures\", \"pdb_search_structures\", {\"uniprot_accession\": \"P04637\", \"experimental_method\": \"X-RAY DIFFRACTION\", \"max_rows\": 50})",
        "required": []
      },
      {
        "id": "pdb_get_structures",
        "connector": "structures",
        "description": "Fetch entry-level summaries for PDB entries (batch, max 25 ids). Accepts 4-character PDB ids in any case ('1tup' == '1TUP'; duplicates are de-duplicated). Each record: title, experimental methods, resolution in Angstrom (null for methods without one, e.g. NMR), determination methodology (experimental vs computational), deposit/release/revision dates and status, molecular weight (kDa), assembly and entity counts (protein/DNA/RNA polymer + nonpolymer), bound ligand chem-comp ids, polymer/nonpolymer entity id lists (inputs for pdb_get_entities / pdb_get_ligands), and the primary citation (title, journal, year, authors, PubMed id, DOI). Unknown ids come back as {\"pdb_id\", \"error\": \"not_found\"} \u2014 never silently dropped. Metadata only; coordinate files are never downloaded.",
        "input": {
          "type": "object",
          "properties": {
            "pdb_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "pdb_ids"
          ]
        },
        "returns": "{n_requested, n_unique, n_blank_skipped, n_duplicate_skipped, records:[{pdb_id, title, experimental_methods, resolution_angstrom, ..., polymer_entity_ids, nonpolymer_entity_ids, citation} | {pdb_id, error:\"not_found\"}]}.",
        "example": "const result = await host.mcp(\"structures\", \"pdb_get_structures\", {\"pdb_ids\": [\"1TUP\", \"1tup\", \"6XYZ\"]})",
        "required": [
          "pdb_ids"
        ]
      },
      {
        "id": "pdb_get_entities",
        "connector": "structures",
        "description": "Polymer entity details for one PDB entry, incl. UniProt mappings. With entity_ids=null every polymer entity of the entry is fetched, capped at 25 with truncated=true and n_polymer_entities reporting the entry's true count (large assemblies like ribosomes carry 50+ \u2014 get the full id list from pdb_get_structures' polymer_entity_ids and page with explicit subsets like [\"26\", \"27\"]); with an explicit entity_ids subset the entry total is not fetched, so n_polymer_entities is null; an explicit entity_ids list larger than 25 errors. Each record: description, polymer type (Protein / DNA / RNA), sequence length, mutation count, deposited copies, chain ids (asym + author), source organisms with taxids, UniProt accessions with per-entity sequence coverage (SIFTS), and UniProt-aligned regions (entity-seq vs reference-seq coordinates). Unknown entity ids are listed in not_found; an unknown entry id errors. include_sequences=true adds the canonical one-letter sequence per entity; if the combined sequences exceed max_bytes (default 400000) they are omitted and sequences_omitted explains why \u2014 metadata always survives.",
        "input": {
          "type": "object",
          "properties": {
            "pdb_id": {
              "type": "string"
            },
            "entity_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "include_sequences": {
              "type": "boolean",
              "default": false
            },
            "max_bytes": {
              "type": "integer",
              "default": 400000
            }
          },
          "required": [
            "pdb_id"
          ]
        },
        "returns": "{pdb_id, n_polymer_entities (entry total when entity_ids=null, else null), polymer_entity_ids, truncated, records:[{rcsb_id, entity_id, description, polymer_type, sequence_length, source_organisms, uniprot_ids, reference_sequence_identifiers, uniprot_aligned_regions, sequence?}], not_found:[...], sequences_omitted?}.",
        "example": "const result = await host.mcp(\"structures\", \"pdb_get_entities\", {\"pdb_id\": \"1TUP\", \"include_sequences\": true})",
        "required": [
          "pdb_id"
        ]
      },
      {
        "id": "pdb_get_ligands",
        "connector": "structures",
        "description": "Bound ligands (nonpolymer components) of one PDB entry, with chemistry. Walks the entry's nonpolymer entities and resolves each chemical component: per ligand \u2014 entity id, chem-comp id ('ZN', 'ATP'), description, deposited copy count, author chain ids, and a chem_comp block (name, formula, formula weight, formal charge, component type, InChIKey, stereo SMILES). Waters are not nonpolymer entities in the PDB data model and never appear. Entries with no ligands return ligands: []. n_nonpolymer_entities is the entry's true count; truncated=true when it exceeds max_ligands (clamped to 1..25, which bounds the request budget) \u2014 never silently dropped. Entities/components the data API no longer serves are reported inline with \"error\": \"not_found\" (partial results, not an aborted call). An unknown entry id errors.",
        "input": {
          "type": "object",
          "properties": {
            "pdb_id": {
              "type": "string"
            },
            "max_ligands": {
              "type": "integer",
              "default": 25
            }
          },
          "required": [
            "pdb_id"
          ]
        },
        "returns": "{pdb_id, n_nonpolymer_entities (entry total), n_returned, truncated, ligands:[{entity_id, comp_id, description, n_copies_deposited, auth_asym_ids, chem_comp:{name, formula, formula_weight, inchikey, smiles, ...} | {comp_id, error:\"not_found\"} | null} | {entity_id, comp_id:null, error:\"not_found\", chem_comp:null}]}.",
        "example": "const result = await host.mcp(\"structures\", \"pdb_get_ligands\", {\"pdb_id\": \"1TUP\"})",
        "required": [
          "pdb_id"
        ]
      },
      {
        "id": "alphafold_get_prediction",
        "connector": "structures",
        "description": "AlphaFold DB predicted-structure metadata for one UniProt accession. Returns has_model, n_models and per-model records. A single accession can carry several models (canonical + isoforms like 'P04637-9', and community providers beyond the Google DeepMind monomer pipeline \u2014 provider_id / tool_used identify them). Each model: entry id, UniProt annotation (id, description, gene, organism, taxid, reviewed flags), sequence coordinates and length, global pLDDT (global_plddt, 0-100) plus the fraction of residues per pLDDT confidence bin (very_low <50, low 50-70, confident 70-90, very_high >90), model version info and creation date, and download URLs (cif/bcif/pdb coordinates, PAE JSON + image, per-residue pLDDT JSON, MSA, AlphaMissense CSV where available) \u2014 URLs only, payloads are never downloaded; fetch them yourself if needed. Accessions without a prediction return has_model=false (not an error); malformed identifiers return an explicit `error` field. include_sequence=true adds the model sequence (protein one-letter).",
        "input": {
          "type": "object",
          "properties": {
            "uniprot_accession": {
              "type": "string"
            },
            "include_sequence": {
              "type": "boolean",
              "default": false
            }
          },
          "required": [
            "uniprot_accession"
          ]
        },
        "returns": "`{ uniprot_accession, has_model, n_models, models:[{ model_entity_id, entry_id, provider_id, tool_used, uniprot_accession, uniprot_id, uniprot_description, gene, organism_scientific_name, tax_id, is_uniprot_reviewed, is_reference_proteome, is_complex, sequence_length, uniprot_start, uniprot_end, global_plddt, fraction_plddt:{very_low, low, confident, very_high}, latest_version, all_versions, model_created_date, urls:{cif, bcif, pdb, pae_image, pae_json, plddt_json, msa, alphamissense_csv}, sequence? }] }`. No prediction -> `{ uniprot_accession, has_model:false, n_models:0, models:[] }`; malformed accession -> same shape plus an `error` field. `urls` only carries the keys the API supplied.",
        "example": "const result = await host.mcp(\"structures\", \"alphafold_get_prediction\", {\"uniprot_accession\": \"P04637\"})",
        "required": [
          "uniprot_accession"
        ]
      },
      {
        "id": "alphafold_check_coverage",
        "connector": "structures",
        "description": "Batch AlphaFold DB coverage check (max 40 unique UniProt accessions). Blank entries and duplicates are stripped before the batch cap applies, and disclosed: n_requested == n_unique + n_blank_skipped + n_duplicate_skipped always reconciles. One compact record per unique accession, in input order: has_model, n_models, and the primary (first-listed) model's model_entity_id, latest_version, global_plddt and sequence_length. Accessions with no prediction report has_model=false; malformed ones carry an explicit `error` field \u2014 never silently dropped. Use to triage which proteins of a set have usable predicted structures before pulling full records with alphafold_get_prediction.",
        "input": {
          "type": "object",
          "properties": {
            "uniprot_accessions": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "uniprot_accessions"
          ]
        },
        "returns": "`{ n_requested, n_unique, n_blank_skipped, n_duplicate_skipped, not_processed:[...], records:[{ uniprot_accession, has_model, n_models?, model_entity_id?, latest_version?, global_plddt?, sequence_length? }] }`. n_requested == n_unique + n_blank_skipped + n_duplicate_skipped. No-prediction records carry `has_model:false`; malformed ones add an `error` field. `not_processed` holds accessions dropped for the per-call time budget.",
        "example": "const result = await host.mcp(\"structures\", \"alphafold_check_coverage\", {\"uniprot_accessions\": [\"P04637\", \"P38398\", \"Q9Y6K9\"]})",
        "required": [
          "uniprot_accessions"
        ]
      }
    ]
  },
  {
    "id": "chembl",
    "displayName": "ChEMBL",
    "description": "Bioactive compounds, drugs, targets, bioactivity, and mechanisms via the ChEMBL REST API.",
    "useWhen": "Use for ChEMBL medicinal-chemistry data \u2014 search compounds by name, ChEMBL id, or molecular structure (similarity/substructure); find drugs by therapeutic indication with approval and withdrawal flags; get calculated ADMET / drug-likeness properties for a molecule; retrieve bioactivity measurements (IC50, Ki, EC50, pChEMBL) for compound-target pairs; look up mechanism of action; or search biological targets by gene symbol, name, organism, or type. Sourced from ChEMBL (EBI).",
    "sources": [
      "ChEMBL"
    ],
    "termsUrl": "https://chembl.gitbook.io/chembl-interface-documentation/about",
    "requiresNcbi": false,
    "group": "directory",
    "tools": [
      {
        "id": "compound_search",
        "connector": "chembl",
        "description": "Search ChEMBL chemical compounds by name (default), ChEMBL id, or molecular structure. By name: case-insensitive synonym substring match (falls back to a preferred-name match). By chembl_id: direct record lookup. By smiles: Tanimoto similarity search when similarity_threshold is set, else a substructure search (structure walks are capped and disclose walk_truncated/upstream_total). Optional max_phase filters by clinical stage. Pass at least one of name, chembl_id, or smiles. Use drug_search instead when searching by therapeutic indication.",
        "input": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "name": {
              "type": "string",
              "description": "Compound name or synonym (case-insensitive substring). Primary search criterion."
            },
            "chembl_id": {
              "type": "string",
              "description": "ChEMBL identifier, e.g. 'CHEMBL25' for aspirin."
            },
            "smiles": {
              "type": "string",
              "description": "SMILES structure for similarity/substructure search."
            },
            "similarity_threshold": {
              "type": "integer",
              "minimum": 70,
              "maximum": 100,
              "description": "Similarity cutoff % (70-100). Only with smiles; omit for a substructure search."
            },
            "max_phase": {
              "type": "integer",
              "enum": [
                0,
                1,
                2,
                3,
                4
              ],
              "description": "Filter by clinical phase. 4 = approved."
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 20
            }
          }
        },
        "returns": "`{ count, total (verified upstream total_count), truncated, compounds: [ { molecule_chembl_id, pref_name, molecule_type, max_phase, first_approval, oral, parenteral, topical, black_box_warning, therapeutic_flag, natural_product, withdrawn_flag, molecule_properties: { alogp, aromatic_rings, full_mwt, hba, hbd, heavy_atoms, psa, rtb, ro3_pass, num_ro5_violations, qed_weighted, molecular_formula, mw_freebase, np_likeness_score, med_chem_friendly, molecular_species }, smiles, inchi, inchi_key, synonyms: [str], chirality, score, atc_classifications, molecule_hierarchy, ... } ] }`. Structure searches may add `walk_truncated` + `upstream_total`.",
        "example": "const result = await host.mcp(\"chembl\", \"compound_search\", {\"name\": \"aspirin\", \"limit\": 5})",
        "required": []
      },
      {
        "id": "drug_search",
        "connector": "chembl",
        "description": "Search approved drugs and clinical candidates by therapeutic indication (EFO term, partial match). Joins drug_indication rows to distinct parent molecules, then to molecule records and withdrawal/black-box warnings. only_approved restricts to phase 4. Optional post-filters molecule_chembl_id, drug_name (preferred-name substring), and max_phase (>=) narrow the joined set. Use compound_search for name/id/structure lookups.",
        "input": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "indication": {
              "type": "string",
              "description": "Disease indication, e.g. 'hypertension', 'cancer'. Primary search criterion."
            },
            "drug_name": {
              "type": "string",
              "description": "Filter joined drugs by preferred-name substring."
            },
            "molecule_chembl_id": {
              "type": "string",
              "description": "Filter joined drugs to this parent molecule id."
            },
            "max_phase": {
              "type": "integer",
              "enum": [
                0,
                1,
                2,
                3,
                4
              ],
              "description": "Keep drugs whose max_phase is >= this value."
            },
            "only_approved": {
              "type": "boolean",
              "default": false,
              "description": "Only approved drugs (phase 4)."
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 20
            }
          },
          "required": [
            "indication"
          ]
        },
        "returns": "`{ count, total (distinct parents, or filtered count when a post-filter is set), truncated, indication_query: { term, match_field, only_approved }, total_indication_rows, drugs: [ { molecule_chembl_id, pref_name, molecule_type, max_phase, first_approval, oral, parenteral, therapeutic_flag, black_box_warning (0/1), topical (0/1), withdrawn_flag, molecule_properties, molecule_structures, molecule_synonyms, best_phase_for_ind, efo_terms: [str], indication_rows: [drugind_id], warning_summary: [ { warning_type, warning_class, warning_country, warning_year } ], ... } ] }`.",
        "example": "const result = await host.mcp(\"chembl\", \"drug_search\", {\"indication\": \"hypertension\", \"only_approved\": true, \"limit\": 10})",
        "required": [
          "indication"
        ]
      },
      {
        "id": "get_admet",
        "connector": "chembl",
        "description": "Retrieve ChEMBL calculated molecular properties for drug-likeness / ADMET assessment of one molecule (ALogP, molecular weight, PSA, HBA/HBD, rotatable bonds, aromatic rings, heavy atoms, Rule-of-5 violations, Rule-of-3 pass, QED, molecular formula). These are computed from structure, not experimental measurements.",
        "input": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "molecule_chembl_id": {
              "type": "string",
              "description": "ChEMBL molecule id, e.g. 'CHEMBL941'. Use compound_search first if you only have a name."
            }
          },
          "required": [
            "molecule_chembl_id"
          ]
        },
        "returns": "`{ found: bool, properties: { molecule_chembl_id, alogp, molecular_weight, mw_freebase, psa, hba, hbd, rtb, aromatic_rings, heavy_atoms, num_ro5_violations, ro3_pass, qed_weighted, molecular_formula } | null, message? }`. When the id is unknown, `found` is false, `properties` null, and `message` explains.",
        "example": "const result = await host.mcp(\"chembl\", \"get_admet\", {\"molecule_chembl_id\": \"CHEMBL25\"})",
        "required": [
          "molecule_chembl_id"
        ]
      },
      {
        "id": "get_bioactivity",
        "connector": "chembl",
        "description": "Retrieve ChEMBL bioactivity measurements (IC50, Ki, Kd, EC50, ...) for compound-target interactions. Filter by molecule_chembl_id and/or target_chembl_id, activity_type (standard_type), a pChEMBL floor (min_pchembl), a standard_value range (min_value/max_value), and unit (standard_units). Returns one page ordered by activity_id with a most-potent summary.",
        "input": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "molecule_chembl_id": {
              "type": "string",
              "description": "ChEMBL molecule id, e.g. 'CHEMBL25'."
            },
            "target_chembl_id": {
              "type": "string",
              "description": "ChEMBL target id, e.g. 'CHEMBL240' (hERG)."
            },
            "activity_type": {
              "type": "string",
              "enum": [
                "IC50",
                "EC50",
                "Ki",
                "Kd",
                "AC50",
                "GI50",
                "ED50",
                "Potency"
              ],
              "description": "standard_type to filter on."
            },
            "min_pchembl": {
              "type": "number",
              "minimum": 0,
              "maximum": 14,
              "description": "Minimum pChEMBL value."
            },
            "min_value": {
              "type": "number",
              "description": "Minimum standard_value (in unit)."
            },
            "max_value": {
              "type": "number",
              "description": "Maximum standard_value (in unit)."
            },
            "unit": {
              "type": "string",
              "enum": [
                "nM",
                "uM",
                "mM",
                "pM",
                "M"
              ],
              "description": "standard_units filter."
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 20
            }
          }
        },
        "returns": "`{ count, total (verified upstream total_count), truncated, summary, activities: [ { activity_id, molecule_chembl_id, target_chembl_id, target_pref_name, standard_type, standard_relation, standard_value, standard_units, pchembl_value, assay_chembl_id, assay_type, ligand_efficiency, document_chembl_id, ... 45 keys } ] }`.",
        "example": "const result = await host.mcp(\"chembl\", \"get_bioactivity\", {\"molecule_chembl_id\": \"CHEMBL25\", \"activity_type\": \"IC50\", \"limit\": 10})",
        "required": []
      },
      {
        "id": "get_mechanism",
        "connector": "chembl",
        "description": "Retrieve ChEMBL mechanism-of-action records for approved drugs and clinical candidates. Filter by molecule_chembl_id, target_chembl_id, and/or action_type. When a molecule id yields nothing, retries against the parent molecule so salt-form ids resolve. Returns one page ordered by mec_id with an action-type summary.",
        "input": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "molecule_chembl_id": {
              "type": "string",
              "description": "ChEMBL molecule id, e.g. 'CHEMBL25'."
            },
            "target_chembl_id": {
              "type": "string",
              "description": "ChEMBL target id, e.g. 'CHEMBL1824'."
            },
            "action_type": {
              "type": "string",
              "enum": [
                "INHIBITOR",
                "AGONIST",
                "ANTAGONIST",
                "BLOCKER",
                "MODULATOR",
                "OPENER",
                "ACTIVATOR",
                "POSITIVE ALLOSTERIC MODULATOR",
                "NEGATIVE ALLOSTERIC MODULATOR",
                "PARTIAL AGONIST",
                "INVERSE AGONIST"
              ],
              "description": "Mechanism action type."
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 20
            }
          }
        },
        "returns": "`{ count, total, truncated, summary, mechanisms: [ { mec_id, molecule_chembl_id, mechanism_of_action, target_chembl_id, action_type, direct_interaction (bool), disease_efficacy (bool), mechanism_comment, binding_site_comment, selectivity_comment, molecular_mechanism, max_phase, parent_molecule_chembl_id, mechanism_refs, ... } ] }`.",
        "example": "const result = await host.mcp(\"chembl\", \"get_mechanism\", {\"molecule_chembl_id\": \"CHEMBL25\"})",
        "required": []
      },
      {
        "id": "target_search",
        "connector": "chembl",
        "description": "Search ChEMBL biological targets (proteins, complexes, families, organisms). Filter by target_chembl_id, gene_symbol (exact component-synonym match), target_name (preferred-name substring), organism (substring), and/or target_type. Each result carries its components with UniProt accessions, a gene_symbol, and bounded cross-reference lists.",
        "input": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "target_name": {
              "type": "string",
              "description": "Target name / partial name, e.g. 'kinase'."
            },
            "gene_symbol": {
              "type": "string",
              "description": "Gene symbol (exact), e.g. 'EGFR', 'BRAF'."
            },
            "target_chembl_id": {
              "type": "string",
              "description": "ChEMBL target id, e.g. 'CHEMBL203'."
            },
            "organism": {
              "type": "string",
              "description": "Organism, e.g. 'Homo sapiens'."
            },
            "target_type": {
              "type": "string",
              "enum": [
                "SINGLE PROTEIN",
                "PROTEIN COMPLEX",
                "PROTEIN FAMILY",
                "ORGANISM",
                "TISSUE",
                "CELL-LINE",
                "NUCLEIC-ACID",
                "SUBCELLULAR"
              ],
              "description": "Target type filter."
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 20
            }
          }
        },
        "returns": "`{ count, total (verified upstream total_count), truncated, targets: [ { target_chembl_id, pref_name, target_type, organism, tax_id, species_group_flag, cross_references, score, components: [ { component_id, component_type, accession, component_description, gene_symbol, relationship, target_component_xrefs: [ { xref_id, xref_name, xref_src_db, xref_src_url } ], xrefs_truncated_from? } ] } ] }`.",
        "example": "const result = await host.mcp(\"chembl\", \"target_search\", {\"gene_symbol\": \"EGFR\", \"organism\": \"Homo sapiens\", \"limit\": 5})",
        "required": []
      }
    ]
  },
  {
    "id": "biorxiv",
    "displayName": "bioRxiv",
    "description": "bioRxiv/medRxiv preprints \u2014 search by date/category, metadata by DOI, journal-publication links, funder listings, and platform statistics.",
    "useWhen": "Use when working with bioRxiv or medRxiv preprints \u2014 searching by date range and category (no keyword search), fetching full metadata for a DOI, finding which preprints were published in journals (optionally by publisher DOI prefix), listing preprints by funder (ROR id), or reporting submission/usage statistics over time. Sourced from bioRxiv and medRxiv (funder ids via ROR).",
    "sources": [
      "bioRxiv",
      "medRxiv",
      "ROR"
    ],
    "termsUrl": "https://www.biorxiv.org/about/FAQ",
    "requiresNcbi": false,
    "group": "directory",
    "tools": [
      {
        "id": "get_categories",
        "connector": "biorxiv",
        "description": "List all 27 bioRxiv subject categories and their API-compatible slugs (e.g. \"cancer biology\" -> \"cancer_biology\"). Use before search_preprints to discover valid category values.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ \"success\": true, \"categories\": [ { \"name\": str, \"api_format\": str, \"description\": null } ], \"error\": null }` \u2014 27 entries.",
        "example": "const result = await host.mcp(\"biorxiv\", \"get_categories\", {})",
        "required": []
      },
      {
        "id": "search_preprints",
        "connector": "biorxiv",
        "description": "Search bioRxiv/medRxiv preprints by date and (optionally) category. Use exactly ONE search method: date_from+date_to, recent_days (last N days), or recent_count (N most recent within a 90-day window); with none, the last 60 days. There is NO keyword/text search. cursor paginates. Returns DOI, title, authors, date, category, version, and a 200-char abstract preview.",
        "input": {
          "type": "object",
          "properties": {
            "server": {
              "type": "string",
              "enum": [
                "biorxiv",
                "medrxiv"
              ],
              "default": "biorxiv",
              "description": "'biorxiv' (biological sciences) or 'medrxiv' (medical sciences)"
            },
            "category": {
              "type": "string",
              "enum": [
                "animal behavior and cognition",
                "biochemistry",
                "bioengineering",
                "bioinformatics",
                "biophysics",
                "cancer biology",
                "cell biology",
                "clinical trials",
                "developmental biology",
                "ecology",
                "epidemiology",
                "evolutionary biology",
                "genetics",
                "genomics",
                "immunology",
                "microbiology",
                "molecular biology",
                "neuroscience",
                "paleontology",
                "pathology",
                "pharmacology and toxicology",
                "physiology",
                "plant biology",
                "scientific communication and education",
                "synthetic biology",
                "systems biology",
                "zoology"
              ],
              "description": "Subject category to filter by (see get_categories)"
            },
            "date_from": {
              "type": "string",
              "description": "Start date YYYY-MM-DD (use with date_to)"
            },
            "date_to": {
              "type": "string",
              "description": "End date YYYY-MM-DD (use with date_from)"
            },
            "recent_days": {
              "type": "integer",
              "minimum": 1,
              "description": "Preprints from the last N days"
            },
            "recent_count": {
              "type": "integer",
              "minimum": 1,
              "description": "N most recent within a ~90-day window"
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "cursor": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        },
        "returns": "`{ \"success\": bool, \"results\": [ { \"doi\", \"title\", \"authors\", \"date\", \"category\", \"version\", \"abstract_preview\" } ], \"cursor\": int, \"count\": int, \"total\": int|null, \"error\": null }`. `total` is the window total (null when the route reports none).",
        "example": "const result = await host.mcp(\"biorxiv\", \"search_preprints\", {\"recent_days\": 30, \"category\": \"neuroscience\", \"limit\": 20})",
        "required": []
      },
      {
        "id": "get_preprint",
        "connector": "biorxiv",
        "description": "Get complete metadata for one preprint by DOI (bare \"10.1101/...\" or a full https://doi.org/ URL). Uses the latest version. Returns title, authors, corresponding author + institution, full abstract, category, license, version, JATS XML, funding, published journal DOI (if linked), PDF and web URLs, and version count. Preprints are NOT peer-reviewed.",
        "input": {
          "type": "object",
          "properties": {
            "doi": {
              "type": "string",
              "description": "Preprint DOI, e.g. \"10.1101/339747\""
            },
            "server": {
              "type": "string",
              "enum": [
                "biorxiv",
                "medrxiv"
              ],
              "default": "biorxiv",
              "description": "'biorxiv' (biological sciences) or 'medrxiv' (medical sciences)"
            }
          },
          "required": [
            "doi"
          ]
        },
        "returns": "`{ \"success\": bool, \"preprint\": { \"doi\", \"title\", \"authors\", \"author_corresponding\", \"author_corresponding_institution\", \"date\", \"version\", \"type\", \"category\", \"license\", \"abstract\", \"jatsxml\", \"funding\", \"published_doi\", \"server\", \"pdf_url\", \"web_url\", \"n_versions\" }, \"error\": str|null }`. On an unknown DOI: `success:false`, `preprint:null`, `error` set.",
        "example": "const result = await host.mcp(\"biorxiv\", \"get_preprint\", {\"doi\": \"10.1101/339747\"})",
        "required": [
          "doi"
        ]
      },
      {
        "id": "search_published_preprints",
        "connector": "biorxiv",
        "description": "Find preprints that were later published in peer-reviewed journals (preprint -> journal-article links). Same ONE-OF search methods as search_preprints (date_from+date_to / recent_days / recent_count). include_details=false returns a compact summary. publisher filters by journal DOI prefix (e.g. \"10.1038\" for Nature) via the bioRxiv-only /publisher route.",
        "input": {
          "type": "object",
          "properties": {
            "server": {
              "type": "string",
              "enum": [
                "biorxiv",
                "medrxiv"
              ],
              "default": "biorxiv",
              "description": "'biorxiv' (biological sciences) or 'medrxiv' (medical sciences)"
            },
            "publisher": {
              "type": "string",
              "description": "Publisher DOI prefix, e.g. \"10.1038\" (bioRxiv only)"
            },
            "include_details": {
              "type": "boolean",
              "default": true
            },
            "date_from": {
              "type": "string",
              "description": "Start date YYYY-MM-DD (use with date_to)"
            },
            "date_to": {
              "type": "string",
              "description": "End date YYYY-MM-DD (use with date_from)"
            },
            "recent_days": {
              "type": "integer",
              "minimum": 1
            },
            "recent_count": {
              "type": "integer",
              "minimum": 1
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "cursor": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        },
        "returns": "`{ \"success\": bool, \"results\": [ { \"biorxiv_doi\", ...link fields } ], \"cursor\": int, \"count\": int, \"total\": int|null, \"error\": null }`. With include_details each result carries every upstream field (published_doi, published_journal, preprint_title, dates, etc.); with include_details=false, only the summary subset.",
        "example": "const result = await host.mcp(\"biorxiv\", \"search_published_preprints\", {\"publisher\": \"10.1038\", \"date_from\": \"2024-01-01\", \"date_to\": \"2024-01-05\", \"limit\": 10})",
        "required": []
      },
      {
        "id": "search_by_funder",
        "connector": "biorxiv",
        "description": "Find preprints acknowledging a funder, identified by ROR id (9-char, e.g. \"021nxhr62\" for NIH; a full https://ror.org/ URL is also accepted). Requires an explicit date_from + date_to; funder metadata begins 2025-04-10. Optional category filter. cursor paginates. Same compact result shape as search_preprints.",
        "input": {
          "type": "object",
          "properties": {
            "funder_ror_id": {
              "type": "string",
              "description": "Funder ROR id, e.g. \"021nxhr62\" (NIH)"
            },
            "date_from": {
              "type": "string",
              "description": "Start date YYYY-MM-DD (>= 2025-04-10)"
            },
            "date_to": {
              "type": "string",
              "description": "End date YYYY-MM-DD"
            },
            "server": {
              "type": "string",
              "enum": [
                "biorxiv",
                "medrxiv"
              ],
              "default": "biorxiv",
              "description": "'biorxiv' (biological sciences) or 'medrxiv' (medical sciences)"
            },
            "category": {
              "type": "string",
              "enum": [
                "animal behavior and cognition",
                "biochemistry",
                "bioengineering",
                "bioinformatics",
                "biophysics",
                "cancer biology",
                "cell biology",
                "clinical trials",
                "developmental biology",
                "ecology",
                "epidemiology",
                "evolutionary biology",
                "genetics",
                "genomics",
                "immunology",
                "microbiology",
                "molecular biology",
                "neuroscience",
                "paleontology",
                "pathology",
                "pharmacology and toxicology",
                "physiology",
                "plant biology",
                "scientific communication and education",
                "synthetic biology",
                "systems biology",
                "zoology"
              ],
              "description": "Subject category to filter by (see get_categories)"
            },
            "limit": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 10
            },
            "cursor": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          "required": [
            "funder_ror_id",
            "date_from",
            "date_to"
          ]
        },
        "returns": "`{ \"success\": bool, \"results\": [ { \"doi\", \"title\", \"authors\", \"date\", \"category\", \"version\", \"abstract_preview\" } ], \"cursor\": int, \"count\": int, \"total\": int|null, \"error\": null }`. The funder route reports no total, so `total` is null when results exist; page with cursor until count < limit.",
        "example": "const result = await host.mcp(\"biorxiv\", \"search_by_funder\", {\"funder_ror_id\": \"021nxhr62\", \"date_from\": \"2025-04-10\", \"date_to\": \"2025-05-10\", \"limit\": 10})",
        "required": [
          "funder_ror_id",
          "date_from",
          "date_to"
        ]
      },
      {
        "id": "get_content_statistics",
        "connector": "biorxiv",
        "description": "bioRxiv submission statistics over all history \u2014 new vs revised paper counts per period, with running cumulative totals. interval is \"monthly\" (default) or \"yearly\".",
        "input": {
          "type": "object",
          "properties": {
            "interval": {
              "type": "string",
              "enum": [
                "monthly",
                "yearly"
              ],
              "default": "monthly"
            }
          }
        },
        "returns": "`{ \"success\": bool, \"results\": [ { \"month\"|\"year\", \"new_papers\", \"new_papers_cumulative\", \"revised_papers\", \"revised_papers_cumulative\" } ], \"error\": null }`. Monthly rows carry \"month\" (YYYY-MM); yearly rows carry \"year\".",
        "example": "const result = await host.mcp(\"biorxiv\", \"get_content_statistics\", {\"interval\": \"yearly\"})",
        "required": []
      },
      {
        "id": "get_usage_statistics",
        "connector": "biorxiv",
        "description": "bioRxiv usage/engagement statistics over all history \u2014 abstract views, full-text views, and PDF downloads per period, with running cumulative totals. interval is \"monthly\" (default) or \"yearly\".",
        "input": {
          "type": "object",
          "properties": {
            "interval": {
              "type": "string",
              "enum": [
                "monthly",
                "yearly"
              ],
              "default": "monthly"
            }
          }
        },
        "returns": "`{ \"success\": bool, \"results\": [ { \"month\"|\"year\", \"abstract_views\", \"full_text_views\", \"pdf_downloads\", \"abstract_cumulative\", \"full_text_cumulative\", \"pdf_cumulative\" } ], \"error\": null }`. Monthly rows carry \"month\" (YYYY-MM); yearly rows carry \"year\".",
        "example": "const result = await host.mcp(\"biorxiv\", \"get_usage_statistics\", {\"interval\": \"yearly\"})",
        "required": []
      }
    ]
  },
  {
    "id": "drug-regulatory",
    "displayName": "Drug Regulatory",
    "description": "Drugs@FDA applications, labels, and corpus statistics via openFDA.",
    "useWhen": "Use when you need FDA drug regulatory data \u2014 searching or fetching Drugs@FDA applications (NDA/ANDA/BLA) by brand, generic, ingredient, sponsor, marketing status, or pharmacologic class; aggregate/corpus statistics; generic equivalents of a brand; or product label (SPL) sections such as indications and boxed warnings. Sourced from openFDA (Drugs@FDA + drug labels).",
    "sources": [
      "openFDA"
    ],
    "termsUrl": "https://open.fda.gov/terms/",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "search_drug_applications",
        "connector": "drug-regulatory",
        "description": "Search Drugs@FDA applications (NDA/ANDA/BLA) by any combination of exact-phrase filters (brand, generic, active_ingredient, sponsor, marketing_status, dosage_form, route, pharm_class). generic and pharm_class query the harmonized openfda block (absent on older applications, so silently skipped there). A broad search returns the first max_records with the true total and truncated=true; to page beyond ~26,000 records, narrow with submission_date_from/to.",
        "input": {
          "type": "object",
          "properties": {
            "brand": {
              "type": "string"
            },
            "generic": {
              "type": "string"
            },
            "active_ingredient": {
              "type": "string"
            },
            "sponsor": {
              "type": "string"
            },
            "marketing_status": {
              "type": "string",
              "enum": [
                "Prescription",
                "Over-the-counter",
                "Discontinued",
                "None (Tentative Approval)"
              ]
            },
            "dosage_form": {
              "type": "string"
            },
            "route": {
              "type": "string"
            },
            "pharm_class": {
              "type": "string"
            },
            "pharm_class_type": {
              "type": "string",
              "enum": [
                "epc",
                "moa",
                "cs",
                "pe"
              ],
              "description": "Which openFDA pharmacologic-class facet `pharm_class` matches: epc (established pharmacologic class), moa (mechanism of action), cs (chemical/structural class), pe (physiologic effect)."
            },
            "search_type": {
              "type": "string",
              "enum": [
                "and",
                "or"
              ],
              "default": "and",
              "description": "How to combine the mapped filters: \"and\" (default) or \"or\"."
            },
            "submission_date_from": {
              "type": "string",
              "description": "Earliest submission date (inclusive), YYYY-MM-DD; ANDed onto the query."
            },
            "submission_date_to": {
              "type": "string",
              "description": "Latest submission date (inclusive), YYYY-MM-DD; ANDed onto the query."
            },
            "raw_search": {
              "type": "string",
              "description": "Verbatim openFDA Lucene query; when set, overrides every mapped filter above."
            },
            "max_records": {
              "type": "integer",
              "default": 50
            }
          }
        },
        "returns": "`{ total (API meta count), n_returned, truncated, last_updated, records: [ { application_number, sponsor_name, products: [...], submissions: [...], openfda_generic_name, openfda_pharm_class_epc, openfda_pharm_class_moa, openfda_pharm_class_cs, openfda_pharm_class_pe, openfda_substance_name, openfda_route, openfda_manufacturer_name, openfda_product_type } ] }`. `truncated` is true when fewer than `total` records were returned.",
        "example": "const result = await host.mcp(\"drug-regulatory\", \"search_drug_applications\", {\"generic\": \"ATORVASTATIN CALCIUM\", \"marketing_status\": \"Prescription\", \"max_records\": 25})",
        "required": []
      },
      {
        "id": "get_drug_application",
        "connector": "drug-regulatory",
        "description": "Fetch one Drugs@FDA application by its number (e.g. \"NDA020702\", \"ANDA076543\", \"BLA125514\"). Returns the full record \u2014 sponsor, products (brand, active ingredients + strengths, dosage form, route, marketing status, TE code), complete submissions history, and harmonized openfda fields when present.",
        "input": {
          "type": "object",
          "properties": {
            "application_number": {
              "type": "string"
            }
          },
          "required": [
            "application_number"
          ]
        },
        "returns": "`{ application_number, found (bool), record }`. `record` is the full Drugs@FDA application object (sponsor_name, products, submissions, openfda); it is null and `found` false when the number does not exist.",
        "example": "const result = await host.mcp(\"drug-regulatory\", \"get_drug_application\", {\"application_number\": \"NDA020702\"})",
        "required": [
          "application_number"
        ]
      },
      {
        "id": "count_drug_applications",
        "connector": "drug-regulatory",
        "description": "Aggregate Drugs@FDA bucket counts over one field, optionally narrowed by the same filters as search_drug_applications. count_field accepts friendly names (sponsor_name, application_number, dosage_form, route, marketing_status, te_code, pharm_class_epc/moa/cs/pe) or a raw openFDA field path (append .exact yourself for analyzed fields).",
        "input": {
          "type": "object",
          "properties": {
            "count_field": {
              "type": "string",
              "description": "Field to bucket on \u2014 a friendly name (sponsor_name, application_number, dosage_form, route, marketing_status, te_code, pharm_class_epc/moa/cs/pe) or a raw openFDA field path."
            },
            "brand": {
              "type": "string"
            },
            "generic": {
              "type": "string"
            },
            "active_ingredient": {
              "type": "string"
            },
            "sponsor": {
              "type": "string"
            },
            "marketing_status": {
              "type": "string"
            },
            "dosage_form": {
              "type": "string"
            },
            "route": {
              "type": "string"
            },
            "pharm_class": {
              "type": "string"
            },
            "pharm_class_type": {
              "type": "string",
              "enum": [
                "epc",
                "moa",
                "cs",
                "pe"
              ],
              "description": "Which openFDA pharmacologic-class facet `pharm_class` matches: epc (established pharmacologic class), moa (mechanism of action), cs (chemical/structural class), pe (physiologic effect)."
            },
            "search_type": {
              "type": "string",
              "enum": [
                "and",
                "or"
              ],
              "default": "and",
              "description": "How to combine the mapped filters: \"and\" (default) or \"or\"."
            },
            "submission_date_from": {
              "type": "string",
              "description": "Earliest submission date (inclusive), YYYY-MM-DD; ANDed onto the query."
            },
            "submission_date_to": {
              "type": "string",
              "description": "Latest submission date (inclusive), YYYY-MM-DD; ANDed onto the query."
            },
            "raw_search": {
              "type": "string",
              "description": "Verbatim openFDA Lucene query; when set, overrides every mapped filter above."
            },
            "max_buckets": {
              "type": "integer",
              "default": 100
            }
          },
          "required": [
            "count_field"
          ]
        },
        "returns": "`{ count_field, api_field (resolved openFDA path), n_buckets, bucket_sum, buckets: [ { term, count } ] }` \u2014 buckets are descending by count.",
        "example": "const result = await host.mcp(\"drug-regulatory\", \"count_drug_applications\", {\"count_field\": \"marketing_status\"})",
        "required": [
          "count_field"
        ]
      },
      {
        "id": "get_drug_statistics",
        "connector": "drug-regulatory",
        "description": "Corpus-level Drugs@FDA statistics in one call \u2014 total applications, marketing-status split, top dosage forms and routes (with distinct counts), and top sponsors by application count.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ total_applications, last_updated, marketing_status: [ { term, count } ], dosage_form_top (top 25), dosage_form_distinct, route_top (top 25), route_distinct, sponsor_top (top 25 by application count) }`.",
        "example": "const result = await host.mcp(\"drug-regulatory\", \"get_drug_statistics\", {})",
        "required": []
      },
      {
        "id": "list_pharmacologic_classes",
        "connector": "drug-regulatory",
        "description": "Enumerate pharmacologic classes with their application counts, counted over the harmonized openfda.pharm_class_<type> block. Counts reflect only applications carrying that block.",
        "input": {
          "type": "object",
          "properties": {
            "class_type": {
              "type": "string",
              "enum": [
                "epc",
                "moa",
                "cs",
                "pe"
              ],
              "default": "epc",
              "description": "Pharmacologic-class facet to enumerate: epc (established pharmacologic class), moa (mechanism of action), cs (chemical/structural class), pe (physiologic effect)."
            },
            "max_buckets": {
              "type": "integer",
              "default": 100
            }
          }
        },
        "returns": "`{ class_type, n_classes, classes: [ { term, count } ] }` \u2014 classes descending by count.",
        "example": "const result = await host.mcp(\"drug-regulatory\", \"list_pharmacologic_classes\", {\"class_type\": \"epc\", \"max_buckets\": 50})",
        "required": []
      },
      {
        "id": "get_generic_equivalents",
        "connector": "drug-regulatory",
        "description": "Find generic equivalents of a brand drug: resolve the brand to its reference application(s), extract the exact active-ingredient name set(s), then return every Drugs@FDA application with a product whose active-ingredient set matches (including TE codes and marketing status).",
        "input": {
          "type": "object",
          "properties": {
            "brand": {
              "type": "string"
            }
          },
          "required": [
            "brand"
          ]
        },
        "returns": "`{ brand, reference_applications: [appnums], active_ingredient_sets: [[names]], equivalents: [ full application records ] }`.",
        "example": "const result = await host.mcp(\"drug-regulatory\", \"get_generic_equivalents\", {\"brand\": \"Lipitor\"})",
        "required": [
          "brand"
        ]
      },
      {
        "id": "search_drug_labels",
        "connector": "drug-regulatory",
        "description": "Retrieve FDA drug product labels (SPL) by ingredient/name/route with targeted section extraction. Filters (active_ingredient, generic_name, brand_name, route, product_type) hit the openfda label block; set exact to query the non-analyzed .exact variants. Pass sections to extract raw openFDA label sections instead of the default structured record. raw_search is mutually exclusive with the mapped filters.",
        "input": {
          "type": "object",
          "properties": {
            "active_ingredient": {
              "type": "string"
            },
            "generic_name": {
              "type": "string"
            },
            "brand_name": {
              "type": "string"
            },
            "route": {
              "type": "string"
            },
            "product_type": {
              "type": "string",
              "enum": [
                "HUMAN PRESCRIPTION DRUG",
                "HUMAN OTC DRUG"
              ]
            },
            "exact": {
              "type": "boolean",
              "default": false,
              "description": "Query the non-analyzed `.exact` field variants (exact match instead of tokenized)."
            },
            "raw_search": {
              "type": "string",
              "description": "Verbatim openFDA Lucene query; when set, overrides every mapped filter above."
            },
            "sections": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "openFDA label section names to extract (e.g. [\"boxed_warning\", \"warnings\"]); returns raw section text instead of the default structured record."
            },
            "max_records": {
              "type": "integer",
              "default": 25
            }
          }
        },
        "returns": "Default: `{ search, total (API count), n_returned, truncated, records: [ { identification: { set_id, spl_version, effective_time, brand_name, generic_name, substance_name, manufacturer, route, product_type, application_number }, has_boxed_warning, warning_sections: [str], indications_and_usage } ] }`. When `sections` is given, each record is `{ set_id, brand_name, generic_name, sections: { <name>: text } }`.",
        "example": "const result = await host.mcp(\"drug-regulatory\", \"search_drug_labels\", {\"brand_name\": \"Tylenol\", \"max_records\": 5})",
        "required": []
      }
    ]
  },
  {
    "id": "human-genetics",
    "displayName": "Human Genetics",
    "description": "Human genetic association evidence \u2014 GWAS Catalog, eQTL Catalogue, and PheWeb PheWAS portals (FinnGen, BioBank Japan).",
    "useWhen": "Use when you need human genetic-association evidence \u2014 GWAS Catalog associations/studies/traits for a variant, gene or trait; eQTL Catalogue molecular-QTL datasets and associations; or PheWAS scans (variant- or gene-level) from FinnGen and BioBank Japan PheWeb portals.",
    "sources": [
      "GWAS Catalog",
      "eQTL Catalogue",
      "PheWeb"
    ],
    "termsUrl": "https://www.ebi.ac.uk/gwas/docs/about",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "gwas_associations_for_variant",
        "connector": "human-genetics",
        "description": "GWAS Catalog associations reported for one variant (rsID), most significant first. Args: rs_id (dbSNP rsID e.g. rs7412 APOE or rs699 AGT; must be the catalog's current rsID \u2014 merged/retired IDs may return zero rows rather than an error); max_records (output cap default 500; trait-hub variants can carry 1000+ associations; rows are server-sorted by p-value ascending, so a capped result is the top-signal prefix). Returns {rs_id, api_total, returned, truncated, associations}. api_total is the catalog's own total; truncated flags a capped fetch. Each association row: {association_id, p_value, pvalue_mantissa, pvalue_exponent, pvalue_description, or_value, beta, ci_lower, ci_upper, range, risk_frequency, snp_effect_alleles, rs_ids, locations, mapped_genes, efo_traits:[{efo_id, efo_trait}], bg_efo_traits, reported_trait, multi_snp_haplotype, snp_interaction, study_accession_id, pubmed_id, first_author}. or_value and beta are mutually exclusive per row (binary vs quantitative); p_value of 0.0 means p < ~1e-308 (use mantissa/exponent).",
        "input": {
          "type": "object",
          "properties": {
            "rs_id": {
              "type": "string",
              "description": "dbSNP rsID, e.g. rs7412"
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "rs_id"
          ]
        },
        "returns": "{rs_id, api_total (page.totalElements), returned, truncated (api_total > returned), associations[]} \u2014 each row the shared association shape.",
        "example": "const result = await host.mcp(\"human-genetics\", \"gwas_associations_for_variant\", {\"rs_id\": \"rs7412\", \"max_records\": 100})",
        "required": [
          "rs_id"
        ]
      },
      {
        "id": "gwas_associations_for_gene",
        "connector": "human-genetics",
        "description": "GWAS Catalog associations whose variants are MAPPED to a gene (catalog's Ensembl pipeline mapping, not author-reported), most significant first. Args: gene_symbol (HGNC symbol, exact match, e.g. PCSK9, APOE; case-sensitive upstream \u2014 pass canonical uppercase; intergenic variants map to flanking genes, so rows may sit outside the gene body); max_records (cap default 500; rows server-sorted by p-value ascending). Returns {gene_symbol, api_total, returned, truncated, associations} with the same row shape as gwas_associations_for_variant. A nonexistent symbol returns api_total=0, not an error.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string",
              "description": "HGNC gene symbol, exact match, e.g. PCSK9"
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "gene_symbol"
          ]
        },
        "returns": "{gene_symbol, api_total, returned, truncated, associations[]} \u2014 the shared association row shape.",
        "example": "const result = await host.mcp(\"human-genetics\", \"gwas_associations_for_gene\", {\"gene_symbol\": \"PCSK9\", \"max_records\": 100})",
        "required": [
          "gene_symbol"
        ]
      },
      {
        "id": "gwas_associations_for_trait",
        "connector": "human-genetics",
        "description": "GWAS Catalog associations annotated to one EFO trait, most significant first. Args: efo_id (ontology term short form as used by the catalog, e.g. MONDO_0005010, EFO_0004340, HP_0003124; the catalog migrated many historical EFO ids to MONDO/HP \u2014 resolve current ids with gwas_search_traits first; pass exactly one of efo_id/efo_trait); efo_trait (exact trait LABEL alternative); max_records (cap default 500; rows p-value ascending). Returns {efo_id|efo_trait, api_total, returned, truncated, associations} with the same row shape as gwas_associations_for_variant. An unknown id/label returns api_total=0, not an error.",
        "input": {
          "type": "object",
          "properties": {
            "efo_id": {
              "type": "string",
              "description": "EFO/MONDO/HP short form, e.g. MONDO_0005010"
            },
            "efo_trait": {
              "type": "string",
              "description": "Exact trait label alternative"
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          }
        },
        "returns": "{efo_id|efo_trait, api_total, returned, truncated, associations[]} \u2014 the shared association row shape.",
        "example": "const result = await host.mcp(\"human-genetics\", \"gwas_associations_for_trait\", {\"efo_id\": \"MONDO_0005010\", \"max_records\": 100})",
        "required": []
      },
      {
        "id": "gwas_search_traits",
        "connector": "human-genetics",
        "description": "Search GWAS Catalog EFO trait annotations by label substring \u2014 the entry point for resolving a disease/phenotype name to the ontology ids that gwas_associations_for_trait / gwas_search_studies take. Args: query (case-insensitive substring of the trait label, e.g. \"coronary\" matches coronary artery disorder MONDO_0005010 etc.; the catalog mixes EFO, MONDO, HP and OBA ids \u2014 don't assume an EFO_ prefix); max_records (cap default 500). Returns {query, api_total, returned, truncated, efo_traits}; each row {efo_id, efo_trait, uri} sorted by label. Count-verified against the catalog's own total when not capped.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "Trait label substring, e.g. coronary"
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "{query, api_total, returned, truncated, efo_traits:[{efo_id, efo_trait, uri}]}.",
        "example": "const result = await host.mcp(\"human-genetics\", \"gwas_search_traits\", {\"query\": \"coronary\", \"max_records\": 50})",
        "required": [
          "query"
        ]
      },
      {
        "id": "gwas_search_studies",
        "connector": "human-genetics",
        "description": "Search GWAS Catalog studies by trait annotation or publication. Args: efo_id (ontology short form, e.g. MONDO_0005010, resolve via gwas_search_traits; filters combine AND \u2014 usually pass one); efo_trait (exact trait label alternative); pubmed_id (PubMed ID of the study's publication, e.g. 38714703); max_records (cap default 500). Returns {filters, api_total, returned, truncated, studies}; each study row {accession_id, disease_trait, efo_traits, bg_efo_traits, pubmed_id, initial_sample_size, replication_sample_size, discovery_ancestry, replication_ancestry, genotyping_technologies, platforms, cohort, full_summary_stats_available, imputed, gxe, gxg}. Count-verified against the catalog total when not capped. At least one filter is required (the unfiltered catalog is ~90k studies).",
        "input": {
          "type": "object",
          "properties": {
            "efo_id": {
              "type": "string",
              "description": "EFO/MONDO/HP short form"
            },
            "efo_trait": {
              "type": "string",
              "description": "Exact trait label"
            },
            "pubmed_id": {
              "type": "string",
              "description": "PubMed ID, e.g. 38714703"
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          }
        },
        "returns": "{filters, api_total, returned, truncated, studies[]} \u2014 each study the lean study shape.",
        "example": "const result = await host.mcp(\"human-genetics\", \"gwas_search_studies\", {\"efo_id\": \"MONDO_0005010\", \"max_records\": 50})",
        "required": []
      },
      {
        "id": "gwas_get_study",
        "connector": "human-genetics",
        "description": "Fetch one GWAS Catalog study by its GCST accession. Args: accession_id (study accession, e.g. GCST90841394; listed in every association row as study_accession_id and in study search results). Returns {found, accession_id, study} where study is the same row shape as gwas_search_studies (null when the accession is unknown).",
        "input": {
          "type": "object",
          "properties": {
            "accession_id": {
              "type": "string",
              "description": "Study accession, e.g. GCST90841394"
            }
          },
          "required": [
            "accession_id"
          ]
        },
        "returns": "{found, accession_id, study} \u2014 study is the lean study shape, null when unknown.",
        "example": "const result = await host.mcp(\"human-genetics\", \"gwas_get_study\", {\"accession_id\": \"GCST90841394\"})",
        "required": [
          "accession_id"
        ]
      },
      {
        "id": "gwas_get_variant",
        "connector": "human-genetics",
        "description": "Fetch one GWAS Catalog variant record (position, mapped genes, consequence) by rsID \u2014 lighter than pulling its associations. Args: rs_id (dbSNP rsID e.g. rs7412). Returns {found, rs_id, variant}; variant is {rs_id, merged, functional_class, most_severe_consequence, alleles (e.g. \"C/T (forward)\"), mapped_genes, locations:[{chromosome, position, region}], last_update_date} \u2014 positions GRCh38 \u2014 or null when the rsID is not in the catalog. merged=1 means the rsID was merged into another record upstream.",
        "input": {
          "type": "object",
          "properties": {
            "rs_id": {
              "type": "string",
              "description": "dbSNP rsID, e.g. rs7412"
            }
          },
          "required": [
            "rs_id"
          ]
        },
        "returns": "{found, rs_id, variant} \u2014 variant is the lean variant shape, null when not in catalog.",
        "example": "const result = await host.mcp(\"human-genetics\", \"gwas_get_variant\", {\"rs_id\": \"rs7412\"})",
        "required": [
          "rs_id"
        ]
      },
      {
        "id": "eqtl_list_datasets",
        "connector": "human-genetics",
        "description": "List eQTL Catalogue datasets (one dataset = one study x tissue/cell type x quantification method). Args: study_label (exact study name, e.g. GTEx, Alasoo_2018, BLUEPRINT); tissue_label (exact tissue/cell-type label, e.g. liver, macrophage, LCL \u2014 lowercase in the catalogue); quant_method (ge=gene expression, exon, tx, txrev, microarray, leafcutter, aptamer=plasma protein; for conventional gene-level eQTLs use ge); max_records (cap default 1000; the full unfiltered catalogue is ~760 datasets). Returns {filters, returned, truncated, datasets} sorted by dataset_id; each {dataset_id (QTD...), study_id (QTS...), study_label, sample_group, tissue_id, tissue_label, condition_label, quant_method, sample_size}. The API publishes no total count; truncated=false proves the listing is complete.",
        "input": {
          "type": "object",
          "properties": {
            "study_label": {
              "type": "string"
            },
            "tissue_label": {
              "type": "string"
            },
            "quant_method": {
              "type": "string"
            },
            "max_records": {
              "type": "integer",
              "default": 1000
            }
          }
        },
        "returns": "{filters (applied filter object), returned, truncated (returned == cap; a short page proves the listing complete), datasets:[{dataset_id, study_id, study_label, sample_group, tissue_id, tissue_label, condition_label, quant_method, sample_size}]} sorted by dataset_id.",
        "example": "const result = await host.mcp(\"human-genetics\", \"eqtl_list_datasets\", {\"study_label\": \"Alasoo_2018\", \"quant_method\": \"ge\"})",
        "required": []
      },
      {
        "id": "eqtl_associations",
        "connector": "human-genetics",
        "description": "Molecular-QTL association rows from one eQTL Catalogue dataset, filtered by gene, variant or region. Args: dataset_id (QTD accession from eqtl_list_datasets, e.g. QTD000266); gene_id (unversioned Ensembl gene ID e.g. ENSG00000130203 APOE; at least one of gene_id/rsid/variant/pos is required); rsid (dbSNP rsID); variant (eQTL Catalogue variant string chr19_44908822_C_T, chr-prefixed underscore GRCh38); pos (genomic window chromosome:start-end GRCh38 no chr prefix, e.g. 19:44900000-44920000); nlog10p_min (significance floor: only rows with -log10(p) >= this, applied upstream); max_records (cap default 1000 = one page). Returns {dataset_id, filters, returned, truncated, associations}; each row {molecular_trait_id, gene_id, variant, rsid, chromosome, position, ref, alt, type, beta, se, pvalue, nlog10p, maf, ac, an, r2, median_tpm}. Rows cover ONLY the cis window the dataset tested (\u00b11 Mb of each gene); empty means \"not tested / not present\". No total count is published: truncated=false proves exhaustion, truncated=true means the cap was hit.",
        "input": {
          "type": "object",
          "properties": {
            "dataset_id": {
              "type": "string"
            },
            "gene_id": {
              "type": "string"
            },
            "rsid": {
              "type": "string"
            },
            "variant": {
              "type": "string"
            },
            "pos": {
              "type": "string"
            },
            "nlog10p_min": {
              "type": "number"
            },
            "max_records": {
              "type": "integer",
              "default": 1000
            }
          },
          "required": [
            "dataset_id"
          ]
        },
        "returns": "{dataset_id, filters (applied filter object incl. nlog10p_min), returned, truncated (returned == cap), associations:[{molecular_trait_id, gene_id, variant, rsid, chromosome, position, ref, alt, type, beta, se, pvalue, nlog10p, maf, ac, an, r2, median_tpm}]}.",
        "example": "const result = await host.mcp(\"human-genetics\", \"eqtl_associations\", {\"dataset_id\": \"QTD000266\", \"gene_id\": \"ENSG00000130203\", \"nlog10p_min\": 2})",
        "required": [
          "dataset_id"
        ]
      },
      {
        "id": "phewas_instances",
        "connector": "human-genetics",
        "description": "List the public PheWeb PheWAS portals this server can query, with genome build and capability registry. Returns {instances:{key:{label, base_url, genome_build, capabilities, notes}}}. capabilities name the endpoints each instance exposes: variant (phewas_variant), gene (phewas_finngen_gene), phenotypes (phewas_list_phenotypes), autocomplete (phewas_search_phenotypes). NOTE the build split: FinnGen R12 variant IDs are GRCh38; BioBank Japan (pheweb.jp) is GRCh37/hg19 \u2014 liftover coordinates before cross-querying.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "{instances:{key:{label, base_url, genome_build, capabilities, notes}}}.",
        "example": "const result = await host.mcp(\"human-genetics\", \"phewas_instances\", {})",
        "required": []
      },
      {
        "id": "phewas_variant",
        "connector": "human-genetics",
        "description": "PheWAS for one variant: its association statistics against every phenotype in a biobank PheWeb portal, most significant first. Args: instance (finngen FinnGen R12 GRCh38, or bbj BioBank Japan GRCh37; variant coords MUST be on the instance's build); variant (chrom-pos-ref-alt, :/_ separators and chr prefix tolerated, e.g. 19-44908822-C-T APOE rs7412 GRCh38/finngen or 1-55505647-G-T PCSK9 rs11591147 GRCh37/bbj); max_phenos (cap default 200; FinnGen returns ~2470 rows; sorted by p-value ascending before capping). Returns {instance, genome_build, variant, variant_meta, total, returned, truncated, phenotypes}; variant_meta {chrom, pos, ref, alt, rsids, nearest_genes, gnomad (FinnGen only)}. Each phenotype row {phenocode, phenostring, category, pval, mlogp, beta, sebeta, af|maf, maf_case, maf_control, n_cases, n_controls, n_samples} (unpublished fields null; BBJ rows have af, FinnGen rows have maf triplets + mlogp). Unknown variants raise a not-found error.",
        "input": {
          "type": "object",
          "properties": {
            "instance": {
              "type": "string",
              "enum": [
                "finngen",
                "bbj"
              ]
            },
            "variant": {
              "type": "string"
            },
            "max_phenos": {
              "type": "integer",
              "default": 200
            }
          },
          "required": [
            "instance",
            "variant"
          ]
        },
        "returns": "{instance, genome_build, variant (normalized chrom-pos-ref-alt), variant_meta{chrom, pos, ref, alt, rsids[], nearest_genes[], gnomad|null}, total, returned, truncated, phenotypes[]}.",
        "example": "const result = await host.mcp(\"human-genetics\", \"phewas_variant\", {\"instance\": \"finngen\", \"variant\": \"19-44908822-C-T\", \"max_phenos\": 50})",
        "required": [
          "instance",
          "variant"
        ]
      },
      {
        "id": "phewas_finngen_gene",
        "connector": "human-genetics",
        "description": "Gene-level PheWAS from FinnGen R12: for every disease endpoint, the best-associated variant in the gene region, most significant first. Args: gene_symbol (HGNC symbol e.g. PCSK9, APOE; unknown symbols raise a not-found error); max_phenos (cap default 200; FinnGen has ~2470 endpoints, one row each; sorted by p-value ascending before capping). Returns {instance:\"finngen\", genome_build:\"GRCh38\", gene_symbol, total, returned, truncated, phenotypes}; each row is the phewas_variant row shape plus variant:{chrom, pos, ref, alt, varid, rsids} \u2014 the top variant for that endpoint in this gene's region (region != gene body; PheWeb pads gene boundaries). Most rows are null results (pval~1) \u2014 the per-endpoint BEST variant is still reported; filter by pval yourself for significant hits.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string"
            },
            "max_phenos": {
              "type": "integer",
              "default": 200
            }
          },
          "required": [
            "gene_symbol"
          ]
        },
        "returns": "{instance:\"finngen\", genome_build:\"GRCh38\", gene_symbol, total, returned, truncated, phenotypes[<phewas_variant row> + variant:{chrom, pos, ref, alt, varid, rsids[]}]}.",
        "example": "const result = await host.mcp(\"human-genetics\", \"phewas_finngen_gene\", {\"gene_symbol\": \"PCSK9\", \"max_phenos\": 50})",
        "required": [
          "gene_symbol"
        ]
      },
      {
        "id": "phewas_list_phenotypes",
        "connector": "human-genetics",
        "description": "Complete phenotype (disease endpoint) catalogue of a PheWeb instance, with case/control counts. Args: instance (currently only finngen exposes this endpoint; BBJ does not \u2014 use phewas_search_phenotypes there); max_records (cap default 3000 > FinnGen's ~2470 endpoints, so the default returns the complete catalogue). Returns {instance, total, returned, truncated, phenotypes} sorted by phenocode; each row {phenocode (e.g. \"T2D\"), phenostring, category, num_cases, num_controls, num_gw_significant (count of genome-wide-significant loci for that endpoint)}.",
        "input": {
          "type": "object",
          "properties": {
            "instance": {
              "type": "string",
              "enum": [
                "finngen"
              ],
              "default": "finngen"
            },
            "max_records": {
              "type": "integer",
              "default": 3000
            }
          }
        },
        "returns": "{instance, total, returned, truncated, phenotypes[{phenocode, phenostring, category, num_cases, num_controls, num_gw_significant}]} sorted by phenocode.",
        "example": "const result = await host.mcp(\"human-genetics\", \"phewas_list_phenotypes\", {\"instance\": \"finngen\", \"max_records\": 3000})",
        "required": []
      },
      {
        "id": "phewas_search_phenotypes",
        "connector": "human-genetics",
        "description": "Search a PheWeb instance's phenotypes (and entities) by name \u2014 the entry point for resolving a disease name to a phenocode. Args: query (free-text phenotype query e.g. \"diabetes\", \"asthma\"; matches phenotype names/codes; some instances also match gene names and rsIDs); instance (finngen default or bbj \u2014 both expose autocomplete); max_records (cap default 500; autocomplete responses are short lists, rarely capped). Returns {instance, query, total, returned, truncated, matches}; each match {display, phenocode, url}. Use the phenocode with phewas_list_phenotypes rows or the instance website; BBJ display strings embed the code in parentheses.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "instance": {
              "type": "string",
              "enum": [
                "finngen",
                "bbj"
              ],
              "default": "finngen"
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "{instance, query, total, returned, truncated, matches[{display, phenocode, url}]}.",
        "example": "const result = await host.mcp(\"human-genetics\", \"phewas_search_phenotypes\", {\"query\": \"diabetes\", \"instance\": \"finngen\"})",
        "required": [
          "query"
        ]
      }
    ]
  },
  {
    "id": "expression",
    "displayName": "Expression",
    "description": "Human tissue expression and eQTLs via the GTEx Portal.",
    "useWhen": "Use for GTEx tissue expression and eQTL evidence \u2014 listing tissue sites or dataset releases, resolving gene symbols to versioned GENCODE ids, median or per-sample expression (TPM) by tissue, top-expressed genes per tissue, sample/donor metadata, and cis-eQTLs (eGenes, single-tissue, multi-tissue METASOFT, or on-the-fly calculation) for a gene or variant. Sourced from GTEx.",
    "sources": [
      "GTEx"
    ],
    "termsUrl": "https://gtexportal.org/home/license",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "gtex_tissue_sites",
        "connector": "expression",
        "description": "List all tissue sites with metadata for a pinned GTEx release (54 in gtex_v8): sample counts, eGene/sGene counts, colour codes, and UBERON ontology ids.",
        "input": {
          "type": "object",
          "properties": {
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          }
        },
        "returns": "`{ \"total\": int, \"tissues\": [ { \"tissue_site_detail_id\": str, \"tissue_site_detail\": str, \"tissue_site\": str, \"abbreviation\": str, \"color_hex\": str, \"color_rgb\": str, \"egene_count\": int, \"sgene_count\": int, \"expressed_gene_count\": int, \"rnaseq_sample_count\": int, \"eqtl_sample_count\": int, \"ontology_id\": str } ] }` \u2014 `total` is the API-verified row count (54 for gtex_v8).",
        "example": "const result = await host.mcp(\"expression\", \"gtex_tissue_sites\", {\"dataset_id\": \"gtex_v8\"})",
        "required": []
      },
      {
        "id": "gtex_dataset_info",
        "connector": "expression",
        "description": "List all GTEx dataset releases with metadata: datasetId, GENCODE version, genome build, dbSNP build, and sample/subject/tissue counts.",
        "input": {
          "type": "object",
          "properties": {
            "dataset_id": {
              "type": "string"
            },
            "organization_name": {
              "type": "string"
            }
          }
        },
        "returns": "`[ { \"dataset_id\": str, \"display_name\": str, \"gencode_version\": str, \"genome_build\": str, \"dbsnp_build\": int, \"organization\": str, \"rnaseq_sample_count\": int, \"rnaseq_and_genotype_sample_count\": int, \"subject_count\": int, \"eqtl_subject_count\": int, \"eqtl_tissue_count\": int, \"tissue_count\": int, \"description\": str } ]` \u2014 one row per release (e.g. gtex_v7, gtex_v8).",
        "example": "const result = await host.mcp(\"expression\", \"gtex_dataset_info\", {})",
        "required": []
      },
      {
        "id": "gtex_sample_info",
        "connector": "expression",
        "description": "Sample and donor metadata for a pinned GTEx release, optionally filtered by tissue_site_detail_id, data_type (e.g. RNASEQ, WGS), or subject_id. Paged and count-verified; an unfiltered call matches tens of thousands of samples, so filter or set max_samples.",
        "input": {
          "type": "object",
          "properties": {
            "tissue_site_detail_id": {
              "type": "string"
            },
            "data_type": {
              "type": "string"
            },
            "subject_id": {
              "type": "string"
            },
            "max_samples": {
              "type": "integer"
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          }
        },
        "returns": "`{ \"total\": int, \"returned\": int, \"truncated\": bool, \"samples\": [ { \"sample_id\": str, \"subject_id\": str, \"tissue_site_detail_id\": str, \"tissue_site_detail\": str, \"data_type\": str, \"sex\": str, \"age_bracket\": str, \"hardy_scale\": int, \"ischemic_time\": int, \"rin\": float, \"autolysis_score\": int, \"pathology_notes\": str, \"uberon_id\": str } ] }` \u2014 `total` is the API-verified match count; `truncated` is true when capped by max_samples.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_sample_info\", {\"tissue_site_detail_id\": \"Liver\", \"data_type\": \"RNASEQ\", \"max_samples\": 100})",
        "required": []
      },
      {
        "id": "gtex_resolve_genes",
        "connector": "expression",
        "description": "Resolve gene symbols or unversioned Ensembl ids to versioned GENCODE ids for a pinned release, e.g. GAPDH -> ENSG00000111640.14. Feed the ids to the expression / eQTL tools.",
        "input": {
          "type": "object",
          "properties": {
            "genes": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "genes"
          ]
        },
        "returns": "`{ \"total\": int, \"genes\": [ { \"gene_symbol\": str, \"gencode_id\": str, \"ensembl_id\": str, \"gencode_version\": str, \"genome_build\": str, \"chromosome\": str, \"start\": int, \"end\": int, \"strand\": str, \"entrez_gene_id\": int, \"gene_type\": str, \"description\": str } ] }` \u2014 one record per matched reference gene; unmatched inputs are simply absent.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_resolve_genes\", {\"genes\": [\"GAPDH\", \"BRCA2\"]})",
        "required": [
          "genes"
        ]
      },
      {
        "id": "gtex_median_expression",
        "connector": "expression",
        "description": "Median gene expression (TPM) for one or more VERSIONED GENCODE ids across tissues (omit tissues for all). Paged and count-verified over (gene, tissue) rows.",
        "input": {
          "type": "object",
          "properties": {
            "gencode_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "tissue_site_detail_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "gencode_ids"
          ]
        },
        "returns": "`{ \"total\": int, \"returned\": int, \"rows\": [ { \"gencode_id\": str, \"gene_symbol\": str, \"tissue_site_detail_id\": str, \"median_tpm\": float, \"unit\": str } ] }` \u2014 one row per (gene, tissue); `total` is the API-verified row count.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_median_expression\", {\"gencode_ids\": [\"ENSG00000111640.14\"]})",
        "required": [
          "gencode_ids"
        ]
      },
      {
        "id": "gtex_expression_summary",
        "connector": "expression",
        "description": "Summarize a gene\u2019s expression across ALL tissues ranked by descending median TPM. Accepts a symbol or Ensembl id and auto-resolves it to a versioned GENCODE id first.",
        "input": {
          "type": "object",
          "properties": {
            "gene": {
              "type": "string"
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "gene"
          ]
        },
        "returns": "`{ \"gene\": { gene reference record }, \"total_tissues\": int, \"tissues\": [ { \"tissue_site_detail_id\": str, \"median_tpm\": float, \"unit\": str } ] }` \u2014 tissues sorted by descending median TPM. Raises if the gene is not in the GTEx reference.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_expression_summary\", {\"gene\": \"GAPDH\"})",
        "required": [
          "gene"
        ]
      },
      {
        "id": "gtex_gene_expression",
        "connector": "expression",
        "description": "Sample-level (not aggregated) expression TPM arrays for one VERSIONED GENCODE id, per tissue (omit tissues for all). Returns the full per-sample TPM array and n_samples for each tissue.",
        "input": {
          "type": "object",
          "properties": {
            "gencode_id": {
              "type": "string"
            },
            "tissue_site_detail_ids": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "gencode_id"
          ]
        },
        "returns": "`[ { \"tissue_site_detail_id\": str, \"gencode_id\": str, \"gene_symbol\": str, \"unit\": str, \"n_samples\": int, \"expression\": [float] } ]` \u2014 one entry per tissue; `expression` is the raw per-sample TPM array.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_gene_expression\", {\"gencode_id\": \"ENSG00000111640.14\", \"tissue_site_detail_ids\": [\"Whole_Blood\"]})",
        "required": [
          "gencode_id"
        ]
      },
      {
        "id": "gtex_top_expressed_genes",
        "connector": "expression",
        "description": "Top-n genes by median TPM in one tissue, using the API-side ranking. filter_mt_gene (default true) drops mitochondrial genes from the ranking.",
        "input": {
          "type": "object",
          "properties": {
            "tissue_site_detail_id": {
              "type": "string"
            },
            "n": {
              "type": "integer",
              "default": 100
            },
            "filter_mt_gene": {
              "type": "boolean",
              "default": true
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "tissue_site_detail_id"
          ]
        },
        "returns": "`{ \"tissue_site_detail_id\": str, \"total_genes_in_ranking\": int, \"returned\": int, \"genes\": [ { \"gencode_id\": str, \"gene_symbol\": str, \"median_tpm\": float, \"unit\": str } ] }` \u2014 genes in rank order; `total_genes_in_ranking` is the full ranking size (~56k).",
        "example": "const result = await host.mcp(\"expression\", \"gtex_top_expressed_genes\", {\"tissue_site_detail_id\": \"Whole_Blood\", \"n\": 20})",
        "required": [
          "tissue_site_detail_id"
        ]
      },
      {
        "id": "gtex_eqtl_genes",
        "connector": "expression",
        "description": "All eGenes (genes with \u22651 significant cis-eQTL) for a tissue. Walked page-by-page and count-verified (e.g. Pancreas gtex_v8 = 9,660). max_genes caps how many rows are returned.",
        "input": {
          "type": "object",
          "properties": {
            "tissue_site_detail_id": {
              "type": "string"
            },
            "max_genes": {
              "type": "integer"
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "tissue_site_detail_id"
          ]
        },
        "returns": "`{ \"total\": int, \"returned\": int, \"truncated\": bool, \"genes\": [ { \"gencode_id\": str, \"gene_symbol\": str, \"empirical_p_value\": float, \"p_value\": float, \"p_value_threshold\": float, \"q_value\": float, \"log2_allelic_fold_change\": float } ] }` \u2014 `total` is exact even when truncated by max_genes.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_eqtl_genes\", {\"tissue_site_detail_id\": \"Pancreas\", \"max_genes\": 100})",
        "required": [
          "tissue_site_detail_id"
        ]
      },
      {
        "id": "gtex_single_tissue_eqtls",
        "connector": "expression",
        "description": "Significant single-tissue cis-eQTL associations for a gene and/or a variant (precomputed). Provide gencode_id and/or variant_id; tissue_site_detail_id optionally narrows. Paged and count-verified.",
        "input": {
          "type": "object",
          "properties": {
            "gencode_id": {
              "type": "string"
            },
            "variant_id": {
              "type": "string"
            },
            "tissue_site_detail_id": {
              "type": "string"
            },
            "max_results": {
              "type": "integer"
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          }
        },
        "returns": "`{ \"total\": int, \"returned\": int, \"truncated\": bool, \"eqtls\": [ { \"gencode_id\": str, \"gene_symbol\": str, \"variant_id\": str, \"snp_id\": str, \"chromosome\": str, \"pos\": int, \"tissue_site_detail_id\": str, \"p_value\": float, \"nes\": float } ] }` \u2014 precomputed significant associations.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_single_tissue_eqtls\", {\"gencode_id\": \"ENSG00000111640.14\"})",
        "required": []
      },
      {
        "id": "gtex_multi_tissue_eqtls",
        "connector": "expression",
        "description": "Multi-tissue cis-eQTL meta-analysis (METASOFT) for a VERSIONED GENCODE id. variant_id optionally narrows to one variant. Returns per-variant rows with per-tissue m-values, NES, p-values, and SEs.",
        "input": {
          "type": "object",
          "properties": {
            "gencode_id": {
              "type": "string"
            },
            "variant_id": {
              "type": "string"
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "gencode_id"
          ]
        },
        "returns": "`{ \"total\": int, \"returned\": int, \"variants\": [ { \"gencode_id\": str, \"variant_id\": str, \"meta_p\": float, \"tissues\": { <tissue_site_detail_id>: { \"m_value\": float, \"nes\": float, \"p_value\": float, \"se\": float } } } ] }` \u2014 one row per variant tested for the gene; `total` is the variant count.",
        "example": "const result = await host.mcp(\"expression\", \"gtex_multi_tissue_eqtls\", {\"gencode_id\": \"ENSG00000111640.14\"})",
        "required": [
          "gencode_id"
        ]
      },
      {
        "id": "gtex_calculate_eqtl",
        "connector": "expression",
        "description": "Calculate an eQTL on the fly for any gene-variant pair in one tissue, including non-significant pairs. Returns p-value, NES, t-statistic, MAF, and the per-sample genotype/expression arrays.",
        "input": {
          "type": "object",
          "properties": {
            "gencode_id": {
              "type": "string"
            },
            "variant_id": {
              "type": "string"
            },
            "tissue_site_detail_id": {
              "type": "string"
            },
            "dataset_id": {
              "type": "string",
              "default": "gtex_v8"
            }
          },
          "required": [
            "gencode_id",
            "variant_id",
            "tissue_site_detail_id"
          ]
        },
        "returns": "`{ \"gencode_id\": str, \"gene_symbol\": str, \"variant_id\": str, \"tissue_site_detail_id\": str, \"p_value\": float, \"nes\": float, \"t_statistic\": float, \"maf\": float, \"hom_ref_count\": int, \"het_count\": int, \"hom_alt_count\": int, \"n_samples\": int, \"samples\": [ { \"genotype\": float, \"expression\": float } ] }` \u2014 `samples` sorted by (genotype, expression).",
        "example": "const result = await host.mcp(\"expression\", \"gtex_calculate_eqtl\", {\"gencode_id\": \"ENSG00000111640.14\", \"variant_id\": \"chr12_6452899_G_A_b38\", \"tissue_site_detail_id\": \"Whole_Blood\"})",
        "required": [
          "gencode_id",
          "variant_id",
          "tissue_site_detail_id"
        ]
      }
    ]
  },
  {
    "id": "protein-annotation",
    "displayName": "Protein Annotation",
    "description": "Protein domain architecture, family/clan membership, expression atlas and interaction networks via InterPro/Pfam, the Human Protein Atlas and STRING.",
    "useWhen": "Use when you need protein annotation \u2014 a protein's complete InterPro/Pfam domain architecture, entry/family/clan search and detail, member proteins or proteomes of a Pfam family, Human Protein Atlas per-gene expression (tissue/subcellular/pathology/blood/brain) and bulk search, or STRING id mapping, interaction networks and homology similarity. Sourced from InterPro, Pfam, the Human Protein Atlas and STRING.",
    "sources": [
      "InterPro",
      "Pfam",
      "Human Protein Atlas",
      "STRING"
    ],
    "termsUrl": "https://string-db.org/cgi/access?footer_active_subpage=licensing",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "get_domain_architecture",
        "connector": "protein-annotation",
        "description": "Complete InterPro domain architecture for one or more UniProt proteins (all matching entries, member-DB signatures, fragment coordinates), with pagination verified against the API count.",
        "input": {
          "type": "object",
          "properties": {
            "accessions": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "UniProt accessions, e.g. [\"P04637\"]."
            }
          },
          "required": [
            "accessions"
          ]
        },
        "returns": "`{ \"summaries\": { <accession>: { \"protein\": str, \"protein_length\": int, \"entry_count\": int, \"entries\": [ { \"accession\": str, \"name\": str, \"type\": str, \"member_db_signatures\": [ { \"database\": str, \"accession\": str, \"name\": str } ], \"locations\": [ { \"fragments\": [ { \"start\": int, \"end\": int } ], ... } ] } ] } }, \"stats\": { \"http_requests\": int, \"bytes_downloaded\": int } }` \u2014 entries sorted by (type, accession); default location fields (model/score/representative, fragment dc_status \"CONTINUOUS\") are omitted.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_domain_architecture\", {\"accessions\": [\"P04637\"]})",
        "required": [
          "accessions"
        ]
      },
      {
        "id": "search_interpro_entries",
        "connector": "protein-annotation",
        "description": "Keyword search over InterPro or member-database entries (Pfam, SMART, PROSITE, PANTHER, CDD), complete cursor walk verified against the API count.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "Free-text keyword, e.g. \"kinase\". Optional if go_term is given."
            },
            "entry_type": {
              "type": "string",
              "description": "family | domain | repeat | homologous_superfamily | conserved_site | active_site | binding_site | ptm"
            },
            "source_db": {
              "type": "string",
              "default": "interpro",
              "description": "interpro (default) or a member DB: pfam, smart, prosite, panther, cdd."
            },
            "go_term": {
              "type": "string",
              "description": "GO identifier filter, e.g. \"GO:0004672\"."
            }
          }
        },
        "returns": "`{ \"count\": int, \"results\": [ { \"accession\": str, \"name\": str, \"type\": str, \"source_database\": str, \"integrated\": str|null, \"member_db_signatures\"?: [...], \"go_terms\"?: [...] } ] }` \u2014 rows sorted by accession; `count` is the API total.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"search_interpro_entries\", {\"query\": \"kinase\", \"source_db\": \"pfam\"})",
        "required": []
      },
      {
        "id": "get_interpro_entry",
        "connector": "protein-annotation",
        "description": "Detail record for an InterPro entry (IPRxxxxxx) or Pfam family (PFxxxxx) \u2014 route chosen by accession prefix.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string",
              "description": "InterPro (IPRxxxxxx) or Pfam (PFxxxxx) accession."
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"accession\": str, \"name\": { \"name\": str, \"short\": str|null }, \"type\": str, \"source_database\": str, \"integrated\": str|null, \"hierarchy\": ..., \"set_info\": ..., \"member_db_signatures\"?: [...], \"go_terms\"?: [...], \"n_literature_refs\"?: int }` \u2014 a Pfam family's clan appears under `set_info`.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_interpro_entry\", {\"accession\": \"IPR000719\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "search_pfam_clans",
        "connector": "protein-annotation",
        "description": "Keyword search over Pfam clans (InterPro sets, accessions CLxxxx).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "Free-text keyword; omit to list all clans."
            }
          }
        },
        "returns": "`{ \"count\": int, \"results\": [ { \"accession\": str, \"name\": str, \"source_database\": str } ] }` \u2014 rows sorted by accession; an empty upstream result yields count 0.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"search_pfam_clans\", {\"query\": \"kinase\"})",
        "required": []
      },
      {
        "id": "get_pfam_clan",
        "connector": "protein-annotation",
        "description": "Pfam clan detail including the complete sorted member-family list.",
        "input": {
          "type": "object",
          "properties": {
            "clan_accession": {
              "type": "string",
              "description": "Clan accession, e.g. \"CL0016\"."
            }
          },
          "required": [
            "clan_accession"
          ]
        },
        "returns": "`{ \"accession\": str, \"name\": str, \"source_database\": str, \"member_count\": int, \"members\": [ { \"accession\": str, \"name\": str, \"short_name\": str, \"type\": str } ] }` \u2014 members sorted by accession.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_pfam_clan\", {\"clan_accession\": \"CL0016\"})",
        "required": [
          "clan_accession"
        ]
      },
      {
        "id": "get_pfam_family_proteins",
        "connector": "protein-annotation",
        "description": "Member proteins of a Pfam family (complete count-verified walk or count only). Use count_only for very large families.",
        "input": {
          "type": "object",
          "properties": {
            "pfam_accession": {
              "type": "string",
              "description": "Pfam family accession, e.g. \"PF00069\"."
            },
            "reviewed_only": {
              "type": "boolean",
              "default": false,
              "description": "Restrict to reviewed (Swiss-Prot) proteins."
            },
            "tax_id": {
              "type": "integer",
              "description": "Restrict to an NCBI taxon, e.g. 9606 for human."
            },
            "count_only": {
              "type": "boolean",
              "default": false,
              "description": "Return only the match count \u2014 REQUIRED for very large families (e.g. unfiltered PF00069)."
            }
          },
          "required": [
            "pfam_accession"
          ]
        },
        "returns": "`{ \"count\": int, \"results\": [ { \"accession\": str, \"name\": str, \"source_database\": str, \"length\": int, \"tax_id\": int, \"organism\": str } ] | null }` \u2014 `results` is null in count_only mode; otherwise sorted by accession.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_pfam_family_proteins\", {\"pfam_accession\": \"PF00069\", \"count_only\": true})",
        "required": [
          "pfam_accession"
        ]
      },
      {
        "id": "get_pfam_family_proteomes",
        "connector": "protein-annotation",
        "description": "Proteomes containing members of a Pfam family. count_only defaults true \u2014 the upstream proteome cursor pagination is defective for deep walks.",
        "input": {
          "type": "object",
          "properties": {
            "pfam_accession": {
              "type": "string",
              "description": "Pfam family accession, e.g. \"PF00069\"."
            },
            "count_only": {
              "type": "boolean",
              "default": true,
              "description": "Default true (reliable count). Set false only for small families."
            }
          },
          "required": [
            "pfam_accession"
          ]
        },
        "returns": "`{ \"count\": int, \"results\": [ { \"accession\": str, \"name\": str, \"is_reference\": bool, \"taxonomy\": ... } ] | null }` \u2014 `results` is null in count_only mode; a full walk raises if upstream pagination is defective.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_pfam_family_proteomes\", {\"pfam_accession\": \"PF00069\"})",
        "required": [
          "pfam_accession"
        ]
      },
      {
        "id": "get_protein_atlas_gene",
        "connector": "protein-annotation",
        "description": "Human Protein Atlas per-gene record (release 25.x): tissue/subcellular/pathology/blood/brain expression and antibody info. Accepts an Ensembl gene ID or a gene symbol.",
        "input": {
          "type": "object",
          "properties": {
            "gene": {
              "type": "string",
              "description": "Ensembl gene ID (\"ENSG00000141510\") or gene symbol (\"TP53\")."
            },
            "full": {
              "type": "boolean",
              "default": false,
              "description": "False (default) returns a grouped summary; true returns HPA's complete raw record."
            }
          },
          "required": [
            "gene"
          ]
        },
        "returns": "`full=false`: `{ \"identity\": {...}, \"tissue_expression\": {...}, \"single_cell_expression\": {...}, \"blood_expression\": {...}, \"brain_expression\": {...}, \"cancer_expression\": {...}, \"subcellular\": {...}, \"antibody\": {...}, \"pathology\": { \"prognostics\": { <cancer>: ... } } }`. `full=true`: HPA's complete raw ~119-key per-gene record.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_protein_atlas_gene\", {\"gene\": \"TP53\"})",
        "required": [
          "gene"
        ]
      },
      {
        "id": "search_protein_atlas",
        "connector": "protein-annotation",
        "description": "Column-selected bulk search over the Human Protein Atlas (search_download).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "Free-text search (gene symbol, description keyword, ...)."
            },
            "columns": {
              "type": "string",
              "default": "g,gs,eg,gd,up,chr,chrp,scl",
              "description": "Comma-separated HPA column codes: g=Gene, gs=synonym, eg=Ensembl, gd=description, up=Uniprot, chr=Chromosome, chrp=Position, scl=Subcellular location, ab=Antibody, pc=Protein class, di=Disease."
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`[ { <HPA field name>: value } ]` \u2014 a list of row dicts keyed by the human-readable field names selected via `columns`; `[]` when nothing matches.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"search_protein_atlas\", {\"query\": \"kinase\"})",
        "required": [
          "query"
        ]
      },
      {
        "id": "map_string_ids",
        "connector": "protein-annotation",
        "description": "Map gene symbols/aliases to STRING protein identifiers (v12.0). Every input symbol is either mapped or listed in unmapped \u2014 the two partition the input.",
        "input": {
          "type": "object",
          "properties": {
            "symbols": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Gene symbols or aliases, e.g. [\"TP53\", \"PD-1\"]."
            },
            "species": {
              "type": "integer",
              "default": 9606,
              "description": "NCBI taxonomy ID (9606 = human)."
            }
          },
          "required": [
            "symbols"
          ]
        },
        "returns": "`{ \"string_version\": { \"string_version\": str, \"stable_address\": str }, \"species\": int, \"mapped\": [ { \"query\": str, \"string_id\": str, \"preferred_name\": str, \"ncbi_taxon_id\": int } ], \"unmapped\": [ str ] }`.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"map_string_ids\", {\"symbols\": [\"TP53\", \"BRCA1\", \"EGFR\"]})",
        "required": [
          "symbols"
        ]
      },
      {
        "id": "get_string_network",
        "connector": "protein-annotation",
        "description": "STRING protein-protein interaction network for a gene list (v12.0) at a confidence threshold. Maps symbols first (unmapped reported), then retrieves nodes, edges, summary and provenance.",
        "input": {
          "type": "object",
          "properties": {
            "symbols": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Gene symbols, e.g. [\"TP53\", \"BRCA1\", \"EGFR\"]."
            },
            "species": {
              "type": "integer",
              "default": 9606,
              "description": "NCBI taxonomy ID (9606 = human)."
            },
            "required_score": {
              "type": "integer",
              "default": 700,
              "description": "Minimum combined score 0-1000 (400 medium, 700 high, 900 highest)."
            }
          },
          "required": [
            "symbols"
          ]
        },
        "returns": "`{ \"tool\", \"tool_version\", \"query\", \"string_version\", \"nodes\": [ { \"query\", \"name\", \"string_id\", \"degree\" } ], \"unmapped\": [ str ], \"edges\": [ { \"a\": str, \"b\": str, \"score\": float, \"evidence\": { <channel>: float } } ], \"summary\": { node/edge counts, score stats }, \"provenance\": {...} }` \u2014 edges deterministically ordered; isolated nodes visible (degree 0).",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_string_network\", {\"symbols\": [\"TP53\", \"BRCA1\", \"EGFR\"], \"required_score\": 700})",
        "required": [
          "symbols"
        ]
      },
      {
        "id": "get_string_similarity_scores",
        "connector": "protein-annotation",
        "description": "Smith-Waterman protein similarity bitscores among a gene set (STRING /homology). Sparse: pairs absent from STRING's data are not listed (absence means no recorded similarity, not zero).",
        "input": {
          "type": "object",
          "properties": {
            "symbols": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Gene symbols in the source species."
            },
            "species": {
              "type": "integer",
              "default": 9606,
              "description": "NCBI taxonomy ID (9606 = human)."
            }
          },
          "required": [
            "symbols"
          ]
        },
        "returns": "`{ \"species\": int, \"mapped\": [...], \"unmapped\": [ str ], \"n_pairs\": int, \"n_self\": int, \"pairs\": [ { \"id_a\": str, \"id_b\": str, \"taxon_a\": int, \"taxon_b\": int, \"bitscore\": float, \"self\": bool, \"name_a\": str, \"name_b\": str } ] }` \u2014 one record per reported unordered pair (incl. self-scores), id_a <= id_b.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_string_similarity_scores\", {\"symbols\": [\"TP53\", \"MDM2\", \"MDM4\"]})",
        "required": [
          "symbols"
        ]
      },
      {
        "id": "get_string_best_similarity_hits",
        "connector": "protein-annotation",
        "description": "Best homology hit per input protein in a target species (STRING /homology_best). target_species=null asks for the best hit across all species.",
        "input": {
          "type": "object",
          "properties": {
            "symbols": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "Gene symbols in the source species."
            },
            "species": {
              "type": "integer",
              "default": 9606,
              "description": "Source NCBI taxonomy ID (9606 = human)."
            },
            "target_species": {
              "type": "integer",
              "description": "Target NCBI taxonomy ID; omit for best hit across all species."
            }
          },
          "required": [
            "symbols"
          ]
        },
        "returns": "`{ \"species\": int, \"species_b\": int|null, \"mapped\": [...], \"unmapped\": [ str ], \"n_hits\": int, \"hits\": [ { \"query_id\": str, \"query_name\": str, \"query_taxon\": int, \"hit_id\": str, \"hit_taxon\": int, \"bitscore\": float } ] }` \u2014 one best-hit record per query protein, sorted by query STRING ID.",
        "example": "const result = await host.mcp(\"protein-annotation\", \"get_string_best_similarity_hits\", {\"symbols\": [\"TP53\"], \"target_species\": 10090})",
        "required": [
          "symbols"
        ]
      }
    ]
  },
  {
    "id": "cancer-models",
    "displayName": "Cancer Models",
    "description": "Cancer genomics study records via the cBioPortal REST API.",
    "useWhen": "Use when you need cancer genomics data from cBioPortal \u2014 listing or looking up cancer studies (cancer type, sample counts, citation), the mutations of a gene in a study (recurrent protein changes, mutation types), a gene's mutation frequency across several studies, discrete copy-number alterations (deletions/amplifications) of a gene, or a study's clinical attributes and survival endpoints.",
    "sources": [
      "cBioPortal"
    ],
    "termsUrl": "https://www.cbioportal.org/faq",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "cbioportal_list_studies",
        "connector": "cancer-models",
        "description": "List cBioPortal cancer studies, optionally filtered by a free-text keyword (name/description/cancer type) and/or an exact cancer-type id; returns study id, name, cancer type, reference genome, citation, and per-data-type sample counts.",
        "input": {
          "type": "object",
          "properties": {
            "keyword": {
              "type": "string",
              "description": "Free-text match on name/description/cancer type"
            },
            "cancer_type_id": {
              "type": "string",
              "description": "Exact cancer-type id filter (client-side), e.g. brca, difg"
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          }
        },
        "returns": "`{ \"keyword\": str|null, \"cancer_type_id\": str|null, \"api_total_for_keyword\": int, \"count\": int, \"truncated\": bool, \"studies\": [ { \"study_id\": str, \"name\": str, \"description\": str, \"cancer_type_id\": str, \"cancer_type\": str, \"reference_genome\": str, \"pmid\": str, \"citation\": str, \"sequenced_sample_count\": int, \"cna_sample_count\": int, \"structural_variant_count\": int } ] }` \u2014 `api_total_for_keyword` is every study the keyword matched (before the `cancer_type_id` filter); `count` is after it; `studies` are sorted by `study_id`, capped at `max_records` (default 500), and `truncated` is true when `count` exceeds the cap.",
        "example": "const result = await host.mcp(\"cancer-models\", \"cbioportal_list_studies\", {\"keyword\": \"glioma\"})",
        "required": []
      },
      {
        "id": "cbioportal_get_study",
        "connector": "cancer-models",
        "description": "Get a cBioPortal cancer study by id: metadata, per-data-type sample counts, true sample/patient counts (from the study collections, not the display field), and its molecular profiles.",
        "input": {
          "type": "object",
          "properties": {
            "study_id": {
              "type": "string"
            }
          },
          "required": [
            "study_id"
          ]
        },
        "returns": "`{ \"study_id\": str, \"name\": str, \"description\": str, \"cancer_type\": str, \"cancer_type_id\": str, \"reference_genome\": str, \"pmid\": str, \"citation\": str, \"public\": bool, \"groups\": str, \"import_date\": str, \"sample_count\": int, \"patient_count\": int, \"sequenced_sample_count\": int, \"cna_sample_count\": int, \"mrna_rnaseq_v2_sample_count\": int, \"rppa_sample_count\": int, \"structural_variant_count\": int, \"treatment_count\": int, ..., \"molecular_profiles\": [ { \"molecular_profile_id\": str, \"alteration_type\": str, \"datatype\": str, \"name\": str, \"description\": str } ] }` \u2014 `sample_count`/`patient_count` are the real collection sizes; `molecular_profiles` are sorted by id. Unknown study id throws \"Study not found\".",
        "example": "const result = await host.mcp(\"cancer-models\", \"cbioportal_get_study\", {\"study_id\": \"msk_impact_2017\"})",
        "required": [
          "study_id"
        ]
      },
      {
        "id": "cbioportal_mutations_in_gene",
        "connector": "cancer-models",
        "description": "All mutations of one gene (HUGO symbol) in a cBioPortal study, with recurrence aggregates: total mutations, mutated-sample count, mutation-type and protein-change distributions, and the most recurrent protein changes.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string",
              "description": "HUGO gene symbol, e.g. KRAS, IDH1"
            },
            "study_id": {
              "type": "string"
            },
            "max_records": {
              "type": "integer",
              "default": 100
            }
          },
          "required": [
            "gene_symbol",
            "study_id"
          ]
        },
        "returns": "`{ \"gene\": { \"symbol\": str, \"entrez_gene_id\": int }, \"study_id\": str, \"molecular_profile_id\": str, \"total_mutations\": int, \"mutated_sample_count\": int, \"mutation_type_counts\": { str: int }, \"distinct_protein_changes\": int, \"top_protein_changes\": { str: int }, \"truncated\": bool, \"mutations\": [ { \"sample_id\": str, \"patient_id\": str, \"protein_change\": str, \"mutation_type\": str, \"mutation_status\": str, \"chromosome\": str, \"start_position\": int, \"end_position\": int, \"reference_allele\": str, \"variant_allele\": str, \"variant_type\": str, \"ncbi_build\": str, \"protein_pos_start\": int, \"protein_pos_end\": int, \"tumor_alt_count\": int, \"tumor_ref_count\": int, \"refseq_mrna_id\": str } ] }` \u2014 aggregates cover every mutation; `mutations` are sorted by genomic position and capped at `max_records` (default 100), with `truncated` set when they exceed it. `top_protein_changes` holds the 25 most recurrent. Unknown gene throws \"Gene not found\"; a study without mutation data throws, listing the alteration types it does have.",
        "example": "const result = await host.mcp(\"cancer-models\", \"cbioportal_mutations_in_gene\", {\"gene_symbol\": \"IDH1\", \"study_id\": \"difg_msk_2023\"})",
        "required": [
          "gene_symbol",
          "study_id"
        ]
      },
      {
        "id": "cbioportal_mutation_frequency",
        "connector": "cancer-models",
        "description": "Mutation frequency of one gene across several cBioPortal studies (1\u201312): mutated-sample fraction of the sequenced cohort per study, ranked most-frequent first.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string",
              "description": "HUGO gene symbol, e.g. KRAS, IDH1"
            },
            "study_ids": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "minItems": 1,
              "maxItems": 12
            }
          },
          "required": [
            "gene_symbol",
            "study_ids"
          ]
        },
        "returns": "`{ \"gene\": { \"symbol\": str, \"entrez_gene_id\": int }, \"count\": int, \"frequencies\": [ { \"study_id\": str, \"study_name\": str, \"molecular_profile_id\": str, \"mutation_count\": int, \"mutated_samples\": int, \"sequenced_samples\": int, \"frequency\": float } ], \"unknown_studies\": [ str ], \"no_mutation_data\": [ str ] }` \u2014 `frequencies` are sorted by descending `frequency` (4-dp, null when the study reports 0 sequenced samples); ids the API does not know go to `unknown_studies`, studies without a mutation profile / sample list to `no_mutation_data`. At most 12 ids are considered. Unknown gene throws \"Gene not found\".",
        "example": "const result = await host.mcp(\"cancer-models\", \"cbioportal_mutation_frequency\", {\"gene_symbol\": \"KRAS\", \"study_ids\": [\"msk_impact_2017\", \"difg_msk_2023\"]})",
        "required": [
          "gene_symbol",
          "study_ids"
        ]
      },
      {
        "id": "cbioportal_cna_in_gene",
        "connector": "cancer-models",
        "description": "Discrete copy-number alterations of one gene in a cBioPortal study, filtered by event type (deep deletion / amplification by default), with the full per-sample alteration distribution.",
        "input": {
          "type": "object",
          "properties": {
            "gene_symbol": {
              "type": "string",
              "description": "HUGO gene symbol, e.g. KRAS, IDH1"
            },
            "study_id": {
              "type": "string"
            },
            "event_type": {
              "type": "string",
              "enum": [
                "HOMDEL_AND_AMP",
                "HOMDEL",
                "AMP",
                "GAIN",
                "HETLOSS",
                "DIPLOID",
                "ALL"
              ],
              "default": "HOMDEL_AND_AMP"
            },
            "max_records": {
              "type": "integer",
              "default": 100
            }
          },
          "required": [
            "gene_symbol",
            "study_id"
          ]
        },
        "returns": "`{ \"gene\": { \"symbol\": str, \"entrez_gene_id\": int }, \"study_id\": str, \"molecular_profile_id\": str, \"event_type\": str, \"total_events\": int, \"altered_sample_count\": int, \"alteration_counts\": { str: int }, \"truncated\": bool, \"events\": [ { \"sample_id\": str, \"patient_id\": str, \"alteration\": int, \"alteration_label\": str } ] }` \u2014 `alteration_counts` is the complete per-label distribution over the gene (deep_deletion/shallow_deletion/diploid/gain/amplification); `total_events`/`events` cover only rows matching `event_type` (default HOMDEL_AND_AMP), sorted by sample id and capped at `max_records`. Unknown gene throws \"Gene not found\"; a study without discrete CNA throws, listing its alteration types.",
        "example": "const result = await host.mcp(\"cancer-models\", \"cbioportal_cna_in_gene\", {\"gene_symbol\": \"CDKN2A\", \"study_id\": \"msk_impact_2017\"})",
        "required": [
          "gene_symbol",
          "study_id"
        ]
      },
      {
        "id": "cbioportal_clinical_attributes",
        "connector": "cancer-models",
        "description": "Clinical attributes defined in a cBioPortal study (patient- and sample-level fields), highlighting survival endpoints and whether overall-survival data is present.",
        "input": {
          "type": "object",
          "properties": {
            "study_id": {
              "type": "string"
            },
            "max_records": {
              "type": "integer",
              "default": 200
            }
          },
          "required": [
            "study_id"
          ]
        },
        "returns": "`{ \"study_id\": str, \"total_attributes\": int, \"patient_level_count\": int, \"sample_level_count\": int, \"survival_attributes\": [ str ], \"has_overall_survival\": bool, \"truncated\": bool, \"attributes\": [ { \"attribute_id\": str, \"display_name\": str, \"description\": str, \"datatype\": \"STRING\"|\"NUMBER\", \"level\": \"patient\"|\"sample\", \"priority\": int } ] }` \u2014 `survival_attributes` are every OS_/DFS_/PFS_/DSS_ attribute id; `has_overall_survival` is true when both OS_STATUS and OS_MONTHS exist; `attributes` are sorted by id and capped at `max_records` (default 200).",
        "example": "const result = await host.mcp(\"cancer-models\", \"cbioportal_clinical_attributes\", {\"study_id\": \"brca_tcga_pan_can_atlas_2018\"})",
        "required": [
          "study_id"
        ]
      }
    ]
  },
  {
    "id": "rna",
    "displayName": "RNA",
    "description": "Non-coding RNA family data (metadata, alignments, models, structures) via Rfam.",
    "useWhen": "Use for non-coding RNA families from Rfam (accession or family id, e.g. RF00005 / tRNA): family metadata (RNA type, seed/full counts, gathering/trusted/noise cutoffs, clan); the seed alignment (Stockholm or FASTA); the Infernal covariance model; the seed phylogenetic tree; full-region hits across sequence databases; PDB structure mappings; accession<->id conversion; and single-sequence cmscan search against all Rfam models.",
    "sources": [
      "Rfam"
    ],
    "termsUrl": "https://docs.rfam.org/en/latest/",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "get_family",
        "connector": "rna",
        "description": "Rfam family metadata for an accession (RF00005) or family id (tRNA) \u2014 both resolve. Flattened record plus the full upstream JSON in \"raw\".",
        "input": {
          "type": "object",
          "properties": {
            "family": {
              "type": "string"
            }
          },
          "required": [
            "family"
          ]
        },
        "returns": "`{ \"rfam_acc\": str, \"rfam_id\": str, \"description\": str, \"comment\": str, \"clan_acc\": str|null, \"clan_id\": str|null, \"rna_type\": str, \"structure_source\": str, \"num_seed\": int, \"num_full\": int, \"num_species\": int, \"gathering_cutoff\": float, \"trusted_cutoff\": float, \"noise_cutoff\": float, \"release_number\": str, \"release_date\": str, \"raw\": {\u2026} }` \u2014 flattened metadata; `raw` is the complete upstream `rfam` record. Absent fields are `null`/`undefined`.",
        "example": "const result = await host.mcp(\"rna\", \"get_family\", {\"family\": \"RF00005\"})",
        "required": [
          "family"
        ]
      },
      {
        "id": "get_seed_alignment",
        "connector": "rna",
        "description": "Seed alignment of an Rfam family in Stockholm (default, with consensus secondary-structure line) or aligned gapped FASTA.",
        "input": {
          "type": "object",
          "properties": {
            "family": {
              "type": "string"
            },
            "fmt": {
              "type": "string",
              "enum": [
                "stockholm",
                "fasta"
              ],
              "default": "stockholm"
            },
            "max_bytes": {
              "type": "integer",
              "default": 400000
            }
          },
          "required": [
            "family"
          ]
        },
        "returns": "`{ \"family\": str, \"format\": str, \"num_sequences\": int, \"sequence_names\": [str], \"sha256\": str, \"alignment\": str }` \u2014 when the alignment exceeds `max_bytes` (default 400000) \"alignment\" is dropped and replaced by \"alignment_omitted\"/\"size_bytes\"; metadata, counts and sha256 are always present. Raise `max_bytes` to force the full body.",
        "example": "const result = await host.mcp(\"rna\", \"get_seed_alignment\", {\"family\": \"RF00162\", \"fmt\": \"stockholm\"})",
        "required": [
          "family"
        ]
      },
      {
        "id": "get_covariance_model",
        "connector": "rna",
        "description": "Infernal covariance model (CM file) of an Rfam family, usable directly with cmsearch/cmscan, plus parsed header fields.",
        "input": {
          "type": "object",
          "properties": {
            "family": {
              "type": "string"
            },
            "max_bytes": {
              "type": "integer",
              "default": 400000
            }
          },
          "required": [
            "family"
          ]
        },
        "returns": "`{ \"family\": str, \"header\": { \"NAME\": str, \"ACC\": str, \"STATES\": int, \"CLEN\": int, \"W\": int, \u2026 }, \"size_bytes\": int, \"sha256\": str, \"cm\": str }` \u2014 when the CM exceeds `max_bytes` (default 400000) \"cm\" is dropped for \"cm_omitted\"; header, size_bytes and sha256 are always present.",
        "example": "const result = await host.mcp(\"rna\", \"get_covariance_model\", {\"family\": \"RF00162\"})",
        "required": [
          "family"
        ]
      },
      {
        "id": "get_tree",
        "connector": "rna",
        "description": "Seed phylogenetic tree of an Rfam family (NHX/Newick text).",
        "input": {
          "type": "object",
          "properties": {
            "family": {
              "type": "string"
            }
          },
          "required": [
            "family"
          ]
        },
        "returns": "`{ \"family\": str, \"num_leaf_labels\": int, \"sha256\": str, \"tree\": str }` \u2014 `tree` is NHX/Newick text; `num_leaf_labels` counts labelled leaves.",
        "example": "const result = await host.mcp(\"rna\", \"get_tree\", {\"family\": \"RF00162\"})",
        "required": [
          "family"
        ]
      },
      {
        "id": "get_sequence_regions",
        "connector": "rna",
        "description": "All full-region hits of an Rfam family across sequence databases (parsed TSV). Check num_full via get_family first \u2014 rfam.org 403s this route for very large families (e.g. RF00005).",
        "input": {
          "type": "object",
          "properties": {
            "family": {
              "type": "string"
            }
          },
          "required": [
            "family"
          ]
        },
        "returns": "`{ \"family\": str, \"declared_count\": int|null, \"num_regions\": int, \"regions\": [ { \"sequence_accession\": str, \"bits_score\": str, \"region_start\": str, \"region_end\": str, \"sequence_description\": str, \"species\": str, \"ncbi_tax_id\": str } ] }` \u2014 `declared_count` is the server's own \"# found N regions\" header. Very large families surface an HTTP 403 error as-is.",
        "example": "const result = await host.mcp(\"rna\", \"get_sequence_regions\", {\"family\": \"RF00162\"})",
        "required": [
          "family"
        ]
      },
      {
        "id": "get_structure_mapping",
        "connector": "rna",
        "description": "PDB residue-level structure mappings of an Rfam family, deterministically sorted.",
        "input": {
          "type": "object",
          "properties": {
            "family": {
              "type": "string"
            }
          },
          "required": [
            "family"
          ]
        },
        "returns": "`{ \"family\": str, \"num_mappings\": int, \"num_pdb_ids\": int, \"pdb_ids\": [str], \"mapping\": [ { \"pdb_id\": str, \"chain\": str, \"pdb_start\": int, \"pdb_end\": int, \"cm_start\": int, \"cm_end\": int, \"bit_score\": float, \"evalue_score\": str, \"rfam_acc\": str } ] }` \u2014 rows sorted by (pdb_id, chain, pdb_start, pdb_end, cm_start); exact per-row fields follow upstream. `mapping` is `[]` when no structures exist.",
        "example": "const result = await host.mcp(\"rna\", \"get_structure_mapping\", {\"family\": \"RF00162\"})",
        "required": [
          "family"
        ]
      },
      {
        "id": "accession_to_id",
        "connector": "rna",
        "description": "Convert an Rfam accession to its family id (e.g. RF00005 -> \"tRNA\").",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"accession\": str, \"rfam_id\": str }` \u2014 echoes the input accession and its resolved family id.",
        "example": "const result = await host.mcp(\"rna\", \"accession_to_id\", {\"accession\": \"RF00005\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "id_to_accession",
        "connector": "rna",
        "description": "Convert an Rfam family id to its accession (e.g. \"tRNA\" -> RF00005).",
        "input": {
          "type": "object",
          "properties": {
            "family_id": {
              "type": "string"
            }
          },
          "required": [
            "family_id"
          ]
        },
        "returns": "`{ \"rfam_id\": str, \"accession\": str }` \u2014 echoes the input id and its resolved RF##### accession. Throws when no accession resolves.",
        "example": "const result = await host.mcp(\"rna\", \"id_to_accession\", {\"family_id\": \"tRNA\"})",
        "required": [
          "family_id"
        ]
      },
      {
        "id": "search_sequence",
        "connector": "rna",
        "description": "Search a single RNA/DNA sequence against all Rfam covariance models (async cmscan): submit a multipart sequence file to batch.rfam.org and poll until done. The whole call is capped at 600 seconds, including submission and polling. Submission is not automatically retried. Cancellation stops local polling; the provider retains job results for one week. Upstream failures are surfaced as errors.",
        "input": {
          "type": "object",
          "properties": {
            "sequence": {
              "type": "string"
            },
            "max_wait_s": {
              "type": "number",
              "default": 300
            },
            "poll_interval_s": {
              "type": "number",
              "default": 5
            }
          },
          "required": [
            "sequence"
          ]
        },
        "returns": "`{ \"job_id\": str, \"num_hits\": int, \"families\": [str], \"hits\": { family_id: [ { e-value, score, alignment blocks, \u2026 } ] }, \"search_sequence\": str }` \u2014 hits grouped by matching family id. Surfaces upstream HTTP, invalid-response and timeout errors; no local fallback.",
        "example": "const result = await host.mcp(\"rna\", \"search_sequence\", {\"sequence\": \"GGUUCCGGGAAGGCAGCAGGUGGAAACCUGCCA\"})",
        "required": [
          "sequence"
        ]
      }
    ]
  },
  {
    "id": "omics-archives",
    "displayName": "Omics Archives",
    "description": "Omics data archives \u2014 expression (ArrayExpress, GEO), metabolomics (MetaboLights), metagenomics (MGnify) and proteomics (PRIDE).",
    "useWhen": "Use when finding or looking up omics datasets across the major archives \u2014 functional-genomics / expression experiments in ArrayExpress (BioStudies) or NCBI GEO series (by keyword, organism, assay, or accession, with per-sample metadata); metabolomics studies and data files in MetaboLights (MTBLS); metagenomics studies and analyses in MGnify (MGYS, by free text or biome lineage); or proteomics projects and proteins in PRIDE Archive (PXD, by keyword/organism/instrument/disease, or protein\u2194project). Sourced from ArrayExpress, GEO, MetaboLights, MGnify and PRIDE.",
    "sources": [
      "ArrayExpress",
      "GEO",
      "MetaboLights",
      "MGnify",
      "PRIDE"
    ],
    "termsUrl": "https://www.ebi.ac.uk/about/terms-of-use",
    "requiresNcbi": true,
    "tools": [
      {
        "id": "arrayexpress_search_experiments",
        "connector": "omics-archives",
        "description": "Search ArrayExpress functional-genomics experiments (BioStudies) with complete, totalHits-verified retrieval; filters (query, organism, study_type, technology, release-date range, extra facets) combine with AND.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "Free text (BioStudies/Lucene syntax)"
            },
            "organism": {
              "type": "string",
              "description": "e.g. 'Homo sapiens'"
            },
            "study_type": {
              "type": "string",
              "description": "e.g. 'ChIP-seq'"
            },
            "technology": {
              "type": "string",
              "description": "e.g. 'sequencing assay', 'array assay'"
            },
            "released_after": {
              "type": "string",
              "description": "Inclusive ISO date YYYY-MM-DD"
            },
            "released_before": {
              "type": "string",
              "description": "Inclusive ISO date YYYY-MM-DD"
            },
            "extra_facets": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "max_records": {
              "type": "integer",
              "default": 50
            }
          }
        },
        "returns": "`{ \"total_hits\": int, \"is_total_exact\": bool, \"truncated\": bool, \"params\": {...}, \"records\": [ { \"accession\": str, \"title\": str, \"release_date\": str, \"files\": int, \"links\": int, \"is_public\": bool } ] }` \u2014 every match is walked and the unique count verified against `total_hits`; `records` is capped at `max_records` (default 50) with `truncated=true`, `total_hits` still the full count. Sorted release_date desc, accession asc.",
        "example": "const result = await host.mcp(\"omics-archives\", \"arrayexpress_search_experiments\", {\"organism\": \"Homo sapiens\", \"study_type\": \"ChIP-seq\", \"max_records\": 50})",
        "required": []
      },
      {
        "id": "arrayexpress_get_experiment",
        "connector": "omics-archives",
        "description": "Fetch one ArrayExpress experiment (BioStudies) as a flattened analyst record \u2014 study type, organisms, assay/sample counts, designs/factors, authors, publications, protocols, array designs, and file summary.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string",
              "description": "e.g. 'E-MTAB-5061'"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"accession\", \"title\", \"release_date\", \"study_type\", \"organisms\": [str], \"description\", \"assay_count\", \"sample_count\", \"technology\", \"assay_by_molecule\", \"experimental_designs\": [str], \"experimental_factors\": [str], \"authors\": [{name,email,role,affiliations}], \"submitter_organizations\": [str], \"publications\": [{accno,title,authors,doi,status}], \"protocol_count\", \"protocol_types\": [str], \"array_designs\": [str], \"file_count\", \"files_by_type\": {type:count}, \"total_file_bytes\", \"links\": [{target,type}] }` \u2014 absent attributes are undefined.",
        "example": "const result = await host.mcp(\"omics-archives\", \"arrayexpress_get_experiment\", {\"accession\": \"E-MTAB-5061\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "arrayexpress_get_experiment_files",
        "connector": "omics-archives",
        "description": "List every file of an ArrayExpress experiment (name, size, type, format, description) with download URLs, plus the /info endpoint file count carried alongside for comparison.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string",
              "description": "e.g. 'E-MTAB-5061'"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"accession\", \"n_files\": int, \"files\": [ { \"path\", \"size_bytes\", \"type\", \"format\", \"description\", \"download_url\" } ], \"info_reported_file_count\": int, \"http_link\", \"ftp_link\", \"rel_path\" }` \u2014 files sorted by path.",
        "example": "const result = await host.mcp(\"omics-archives\", \"arrayexpress_get_experiment_files\", {\"accession\": \"E-MTAB-5061\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "arrayexpress_get_experiment_samples",
        "connector": "omics-archives",
        "description": "Fetch per-sample SDRF annotation rows for an ArrayExpress experiment (MAGE-TAB headers verbatim, repeats suffixed #2/#3). Experiments with no SDRF return {\"error\":\"no_sdrf\"}.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string",
              "description": "e.g. 'E-MTAB-5061'"
            },
            "max_rows_returned": {
              "type": "integer",
              "default": 200
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"accession\", \"sdrf_file\", \"sdrf_size_bytes\", \"headers\": [str], \"n_samples\": int, \"samples\": [ {header: value} ], \"n_samples_returned\": int, \"rows_truncated\": bool }` \u2014 `n_samples` is the true total; `samples` capped at `max_rows_returned` (default 200). No SDRF yields `{ \"accession\", \"error\": \"no_sdrf\", \"n_samples\": 0, \"samples\": [], ... }`.",
        "example": "const result = await host.mcp(\"omics-archives\", \"arrayexpress_get_experiment_samples\", {\"accession\": \"E-MTAB-5061\", \"max_rows_returned\": 200})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "geo_search_series",
        "connector": "omics-archives",
        "description": "Search NCBI GEO DataSets (db=gds) and return series-level records (trimmed esummary docs). `term` is full E-utilities syntax; add gse[ETYP] to restrict to series.",
        "input": {
          "type": "object",
          "properties": {
            "term": {
              "type": "string",
              "description": "E-utilities query, e.g. 'asthma AND gse[ETYP]'"
            },
            "retmax": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "term"
          ]
        },
        "returns": "`{ \"term\": str, \"count\": int, \"retrieved\": int, \"complete\": bool, \"records\": [ { \"accession\", \"title\", \"summary\", \"gdstype\", \"taxon\", \"n_samples\", \"pdat\", \"ftplink\", \"bioproject\", \"pubmedids\", \"samples\": [{accession,title}], ... } ] }` \u2014 `count` is esearch's own total (may exceed `retrieved` when > retmax); records sorted by accession.",
        "example": "const result = await host.mcp(\"omics-archives\", \"geo_search_series\", {\"term\": \"asthma AND gse[ETYP]\", \"retmax\": 20})",
        "required": [
          "term"
        ]
      },
      {
        "id": "geo_get_series",
        "connector": "omics-archives",
        "description": "Fetch structured metadata for GEO series (GSE accessions) with samples included \u2014 series title/summary/design, platforms, samples with characteristics and library info, and supplementary-file URLs. Data tables are never downloaded.",
        "input": {
          "type": "object",
          "properties": {
            "accessions": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "GSE accessions, e.g. ['GSE131907']"
            }
          },
          "required": [
            "accessions"
          ]
        },
        "returns": "`{ \"n_requested\": int (accessions requested, before de-duplication), \"records\": [ { \"accession\", \"title\", \"organism\": [str], \"series_type\": [str], \"status\", \"submission_date\", \"last_update_date\", \"summary\", \"overall_design\", \"pubmed_ids\": [str], \"platforms\": [str], \"n_samples\": int, \"samples\": [ {accession,title,organism,characteristics:[{tag,value}],library_strategy,instrument_model,...} ], \"supplementary_files\": {series:[str],samples:{acc:[str]},ftp_root}, \"esummary\": {...} } ] }` \u2014 records ordered by accession.",
        "example": "const result = await host.mcp(\"omics-archives\", \"geo_get_series\", {\"accessions\": [\"GSE131907\"]})",
        "required": [
          "accessions"
        ]
      },
      {
        "id": "metabolights_list_studies",
        "connector": "omics-archives",
        "description": "List every public MetaboLights study accession (numerically sorted) with the API's own reported count. There is no server-side study search \u2014 filter fetched candidates by title/descriptor instead.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ \"accessions\": [str], \"count\": int, \"reported_count\": int }` \u2014 full accession list (MTBLS1, MTBLS2, ...); `count` and `reported_count` should agree.",
        "example": "const result = await host.mcp(\"omics-archives\", \"metabolights_list_studies\", {})",
        "required": []
      },
      {
        "id": "metabolights_get_studies",
        "connector": "omics-archives",
        "description": "Fetch structured metadata for MetaboLights studies (MTBLSxxx) from the parsed ISA payload \u2014 title, status, years, organisms, assays, factors, descriptors, sample count, protocols; optional per-sample table. Unknown/private accessions go in not_found.",
        "input": {
          "type": "object",
          "properties": {
            "accessions": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "e.g. ['MTBLS1']"
            },
            "include_samples": {
              "type": "boolean",
              "default": false
            },
            "max_sample_rows_returned": {
              "type": "integer",
              "default": 200
            }
          },
          "required": [
            "accessions"
          ]
        },
        "returns": "`{ \"n_requested\": int, \"records\": [ { \"accession\", \"title\", \"description\", \"study_status\", \"release_year\", \"submission_year\", \"organisms\": [{organism,organism_part}], \"organism_names\": [str], \"assays\": [{assay_number,measurement,technology,platform,filename}], \"assay_count\", \"technologies\": [str], \"factors\": [str], \"descriptors\": [str], \"sample_count\", \"protocols\": [{name,description}], \"sample_table\"? } ], \"not_found\": [str] }` \u2014 records sorted by numeric accession.",
        "example": "const result = await host.mcp(\"omics-archives\", \"metabolights_get_studies\", {\"accessions\": [\"MTBLS1\"], \"include_samples\": false})",
        "required": [
          "accessions"
        ]
      },
      {
        "id": "metabolights_get_study_files",
        "connector": "omics-archives",
        "description": "Complete file inventory for a public MetaboLights study \u2014 the top-level study folder (ISA-Tab, MAF, folder entries) and, by default, the recursive FILES data folder.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string",
              "description": "e.g. 'MTBLS1'"
            },
            "include_data_files": {
              "type": "boolean",
              "default": true
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"accession\", \"latest_version\", \"study_folder\": [ {file,type,status,directory} ], \"n_study_folder_entries\": int, \"metadata_files\": [str], \"data_files\"?: [str], \"n_data_files\"?: int }` \u2014 sorted deterministically; volatile timestamps dropped.",
        "example": "const result = await host.mcp(\"omics-archives\", \"metabolights_get_study_files\", {\"accession\": \"MTBLS1\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "metabolights_search_data_files",
        "connector": "omics-archives",
        "description": "Glob search over a MetaboLights study's raw-data folder (FILES tree). `pattern` is a filename glob (e.g. '*.mzML', '*.raw'); omit it to list every data file.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string",
              "description": "e.g. 'MTBLS1'"
            },
            "pattern": {
              "type": "string",
              "description": "filename glob, e.g. '*.mzML'"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"accession\", \"pattern\": str|null, \"file_match\": true, \"folder_match\": false, \"files\": [str], \"n_files\": int }` \u2014 relative paths under the study folder (FILES/...), sorted.",
        "example": "const result = await host.mcp(\"omics-archives\", \"metabolights_search_data_files\", {\"accession\": \"MTBLS1\", \"pattern\": \"*.zip\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "mgnify_search_studies",
        "connector": "omics-archives",
        "description": "Find MGnify metagenomics studies by free text OR biome lineage (provide exactly one). Full listing is paginated to completion and count-verified against the API.",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string",
              "description": "free text, e.g. 'coral'"
            },
            "biome_lineage": {
              "type": "string",
              "description": "GOLD-style lineage, e.g. 'root:Engineered:Wastewater' (includes sub-lineages)"
            }
          }
        },
        "returns": "`{ \"spec\": {...}, \"count\": int, \"pages_fetched\": int, \"records\": [ { \"accession\", \"secondary_accession\", \"bioproject\", \"study_name\", \"biome_lineages\": [str], \"samples_count\", \"centre_name\", \"data_origination\", \"is_private\", \"last_update\" } ] }` \u2014 `count == records.length` (verified); records sorted by MGYS accession.",
        "example": "const result = await host.mcp(\"omics-archives\", \"mgnify_search_studies\", {\"query\": \"coral\"})",
        "required": []
      },
      {
        "id": "mgnify_get_studies",
        "connector": "omics-archives",
        "description": "Fetch structured records for MGnify studies (MGYS accessions). With include_analyses, each study also carries its complete analyses listing plus by-pipeline/by-experiment breakdowns. Unknown accessions go in missing.",
        "input": {
          "type": "object",
          "properties": {
            "accessions": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "e.g. ['MGYS00000410']"
            },
            "include_analyses": {
              "type": "boolean",
              "default": false
            }
          },
          "required": [
            "accessions"
          ]
        },
        "returns": "`{ \"studies\": [ { \"accession\", \"secondary_accession\", \"bioproject\", \"study_name\", \"biome_lineages\": [str], \"samples_count\", \"centre_name\", \"data_origination\", \"is_private\", \"last_update\", \"analyses_total\"?, \"analyses_by_pipeline_version\"?, \"analyses_by_experiment_type\"? } ], \"missing\": [str], \"analyses\"?: {acc: [record]} }` \u2014 studies sorted by accession.",
        "example": "const result = await host.mcp(\"omics-archives\", \"mgnify_get_studies\", {\"accessions\": [\"MGYS00000410\"], \"include_analyses\": false})",
        "required": [
          "accessions"
        ]
      },
      {
        "id": "mgnify_get_study_analyses",
        "connector": "omics-archives",
        "description": "List ALL analyses of one MGnify study (complete, count-verified pagination) \u2014 one record per MGYA analysis with pipeline version, experiment type, status, and run/assembly/sample accessions.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string",
              "description": "MGYS accession, e.g. 'MGYS00000410'"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ \"study_accession\", \"analyses_count\": int, \"analyses\": [ { \"analysis_accession\", \"study_accession\", \"pipeline_version\", \"experiment_type\", \"analysis_status\", \"run_accession\", \"assembly_accession\", \"sample_accession\", \"instrument_platform\" } ] }` \u2014 `analyses_count` is the API total (retrieval verified against it); sorted by MGYA accession.",
        "example": "const result = await host.mcp(\"omics-archives\", \"mgnify_get_study_analyses\", {\"accession\": \"MGYS00000410\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "pride_search_projects",
        "connector": "omics-archives",
        "description": "Search PRIDE Archive proteomics projects (complete, api_total-verified retrieval); filters (keyword, organism, instrument, disease, extra_filters) combine with AND. Sorted by accession ASC \u2014 a bounded walk is a stable prefix.",
        "input": {
          "type": "object",
          "properties": {
            "keyword": {
              "type": "string",
              "description": "free text, e.g. 'phosphoproteome'"
            },
            "organism": {
              "type": "string",
              "description": "exact PRIDE facet, e.g. 'Homo sapiens (human)'"
            },
            "instrument": {
              "type": "string",
              "description": "exact PRIDE facet, e.g. 'Orbitrap Fusion Lumos'"
            },
            "disease": {
              "type": "string",
              "description": "exact PRIDE facet, e.g. 'Covid-19'"
            },
            "extra_filters": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "max_records_returned": {
              "type": "integer",
              "default": 50
            }
          }
        },
        "returns": "`{ \"spec\": {...}, \"filter\": str|null, \"api_total\": int, \"complete\": bool, \"pages_fetched\": int, \"n_records_returned\": int, \"records_truncated\": bool, \"records\": [ { \"accession\", \"title\", \"organisms\": [str], \"diseases\": [str], \"instruments\": [str], \"experiment_types\": [str], \"quantification_methods\": [str], \"submission_date\", \"publication_date\", \"submitters\": [str], \"lab_pis\": [str], \"references\": [{pubmed_id,doi,reference_line}], ... } ] }` \u2014 `api_total` is the true count; `records` capped at `max_records_returned` (default 50) with `records_truncated`.",
        "example": "const result = await host.mcp(\"omics-archives\", \"pride_search_projects\", {\"keyword\": \"phosphoproteome\", \"organism\": \"Homo sapiens (human)\", \"max_records_returned\": 50})",
        "required": []
      },
      {
        "id": "pride_get_projects",
        "connector": "omics-archives",
        "description": "Fetch full metadata for PRIDE projects by accession (e.g. PXD010154) \u2014 the same normalized record shape as pride_search_projects, so the two are directly comparable. Unknown accessions go in not_found.",
        "input": {
          "type": "object",
          "properties": {
            "accessions": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "description": "e.g. ['PXD010154']"
            }
          },
          "required": [
            "accessions"
          ]
        },
        "returns": "`{ \"n_requested\": int, \"records\": [ { \"accession\", \"title\", \"organisms\": [str], \"organism_parts\": [str], \"diseases\": [str], \"instruments\": [str], \"experiment_types\": [str], \"quantification_methods\": [str], \"keywords\": [str], \"submission_date\", \"publication_date\", \"submitters\": [str], \"lab_pis\": [str], \"references\": [{pubmed_id,doi,reference_line}], \"source\": \"detail\" } ], \"not_found\": [str] }` \u2014 records sorted by accession.",
        "example": "const result = await host.mcp(\"omics-archives\", \"pride_get_projects\", {\"accessions\": [\"PXD010154\"]})",
        "required": [
          "accessions"
        ]
      },
      {
        "id": "pride_search_project_proteins",
        "connector": "omics-archives",
        "description": "List protein evidence rows for one PRIDE affinity-proteomics project (paged to exhaustion). NOTE: only affinity-proteomics projects are served here; for classic MS (PXD) projects use pride_find_projects_for_protein instead.",
        "input": {
          "type": "object",
          "properties": {
            "project_accession": {
              "type": "string",
              "description": "PRIDE project accession"
            },
            "keyword": {
              "type": "string",
              "description": "server-side filter (accession, gene, or protein name)"
            }
          },
          "required": [
            "project_accession"
          ]
        },
        "returns": "`{ \"project_accession\", \"keyword\": str|null, \"n_proteins\": int, \"proteins\": [ { \"protein_accession\", \"protein_name\", \"gene\", \"project_count\" } ] }` \u2014 sorted by protein accession; empty for MS-only projects.",
        "example": "const result = await host.mcp(\"omics-archives\", \"pride_search_project_proteins\", {\"project_accession\": \"PXD010154\"})",
        "required": [
          "project_accession"
        ]
      },
      {
        "id": "pride_find_projects_for_protein",
        "connector": "omics-archives",
        "description": "Find PRIDE projects containing a protein (MS-archive direction). `protein_accession` is a UniProt accession (e.g. P04637). Feed the returned project accessions to pride_get_projects for full metadata.",
        "input": {
          "type": "object",
          "properties": {
            "protein_accession": {
              "type": "string",
              "description": "UniProt accession, e.g. 'P04637'"
            }
          },
          "required": [
            "protein_accession"
          ]
        },
        "returns": "`{ \"query_accession\", \"n_records\": int, \"records\": [ { \"protein_accession\", \"n_projects\": int, \"projects\": [str] } ] }` \u2014 project lists sorted.",
        "example": "const result = await host.mcp(\"omics-archives\", \"pride_find_projects_for_protein\", {\"protein_accession\": \"P04637\"})",
        "required": [
          "protein_accession"
        ]
      }
    ]
  },
  {
    "id": "cellguide",
    "displayName": "CellGuide",
    "description": "Cell-type identity, marker genes, source datasets, and tissues via CELLxGENE CellGuide.",
    "useWhen": "Use for cell-type biology from CELLxGENE CellGuide \u2014 searching cell types by name/synonym, or (by Cell Ontology id or name) getting identity/description, computational or canonical marker genes, contributing source datasets/publications, and the anatomical tissues a cell type is found in.",
    "sources": [
      "CELLxGENE"
    ],
    "termsUrl": "https://cellxgene.cziscience.com/",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "get_cell_type_info",
        "connector": "cellguide",
        "description": "CellGuide (CELLxGENE) cell-type info by Cell Ontology id or name: name, synonyms, ontology description, and curated/GPT description.",
        "input": {
          "type": "object",
          "properties": {
            "cell_type": {
              "type": "string",
              "description": "Cell Ontology id (CL:0000622, CL_0000622, or 0000622) or a cell-type name/synonym (e.g. 'acinar cell')"
            }
          },
          "required": [
            "cell_type"
          ]
        },
        "returns": "`{ \"id\": str, \"name\": str, \"synonyms\": [ str ], \"ontologyDescription\": str, \"description\": str, \"descriptionSource\": str, \"references\": [ ... ] }` \u2014 `descriptionSource` is `validated`, `gpt`, or `none` (with `description` empty). Returns `{ \"error\": str }` when the cell type is not found.",
        "example": "const result = await host.mcp(\"cellguide\", \"get_cell_type_info\", {\"cell_type\": \"acinar cell\"})",
        "required": [
          "cell_type"
        ]
      },
      {
        "id": "search_cell_types",
        "connector": "cellguide",
        "description": "Search CellGuide cell types by free text over name and synonyms (the CDN has no search endpoint, so celltype_metadata.json is filtered client-side).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "limit": {
              "type": "integer",
              "default": 25
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "`{ \"result\": [ { \"id\": str, \"name\": str, \"synonyms\": [ str ], \"ontology_description\": str } ] }` \u2014 cell types whose name or a synonym contains `query` (case-insensitive), capped at `limit` (default 25).",
        "example": "const result = await host.mcp(\"cellguide\", \"search_cell_types\", {\"query\": \"T cell\", \"limit\": 25})",
        "required": [
          "query"
        ]
      },
      {
        "id": "get_marker_genes",
        "connector": "cellguide",
        "description": "CellGuide marker genes for a cell type (id or name): computational (data-derived, scored) or canonical (literature-curated).",
        "input": {
          "type": "object",
          "properties": {
            "cell_type": {
              "type": "string"
            },
            "marker_type": {
              "type": "string",
              "enum": [
                "computational",
                "canonical"
              ],
              "default": "computational"
            },
            "limit": {
              "type": "integer",
              "default": 25
            }
          },
          "required": [
            "cell_type"
          ]
        },
        "returns": "`{ \"id\": str, \"name\": str, \"markerType\": str, \"returned\": int, \"markerGenes\": [ ... ] }`. Computational items: `{ \"symbol\": str, \"name\": str, \"geneId\": str, \"markerScore\": float, \"specificity\": float, \"meanExpression\": float, \"percentExpressing\": float, \"groupbyDims\": { ... } }` sorted by `markerScore` desc. Canonical items: `{ \"symbol\": str, \"name\": str, \"tissue\": str, \"publication\": str, \"publicationTitle\": str }`. Capped at `limit` (default 25); an empty list means no markers are curated/computed for this cell type. `{ \"error\": str }` when the cell type is not found.",
        "example": "const result = await host.mcp(\"cellguide\", \"get_marker_genes\", {\"cell_type\": \"CL:0000084\", \"marker_type\": \"computational\", \"limit\": 25})",
        "required": [
          "cell_type"
        ]
      },
      {
        "id": "get_source_data",
        "connector": "cellguide",
        "description": "CellGuide source datasets and publications contributing to a cell type (id or name): collection name/url, publication, and the tissues/diseases/organisms each covers.",
        "input": {
          "type": "object",
          "properties": {
            "cell_type": {
              "type": "string"
            }
          },
          "required": [
            "cell_type"
          ]
        },
        "returns": "`{ \"id\": str, \"name\": str, \"count\": int, \"sources\": [ { \"collectionName\": str, \"collectionUrl\": str, \"publicationUrl\": str, \"publicationTitle\": str, \"tissues\": [ { \"id\": str, \"label\": str } ], \"diseases\": [ { \"id\": str, \"label\": str } ], \"organisms\": [ { \"id\": str, \"label\": str } ] } ] }` \u2014 empty `sources` when no source collections exist. `{ \"error\": str }` when the cell type is not found.",
        "example": "const result = await host.mcp(\"cellguide\", \"get_source_data\", {\"cell_type\": \"CL:0000622\"})",
        "required": [
          "cell_type"
        ]
      },
      {
        "id": "get_cell_tissues",
        "connector": "cellguide",
        "description": "Anatomical tissues where a cell type (id or name) is observed, aggregated (deduplicated) across CellGuide source collections.",
        "input": {
          "type": "object",
          "properties": {
            "cell_type": {
              "type": "string"
            }
          },
          "required": [
            "cell_type"
          ]
        },
        "returns": "`{ \"id\": str, \"name\": str, \"count\": int, \"tissues\": [ { \"id\": str, \"label\": str } ] }` \u2014 unique UBERON tissues (by ontology term id) the cell type appears in, sorted by label. `{ \"error\": str }` when the cell type is not found.",
        "example": "const result = await host.mcp(\"cellguide\", \"get_cell_tissues\", {\"cell_type\": \"T cell\"})",
        "required": [
          "cell_type"
        ]
      }
    ]
  },
  {
    "id": "regulation",
    "displayName": "Regulation",
    "description": "Gene-regulation functional genomics \u2014 ENCODE experiments/biosamples/files, JASPAR TF binding profiles, and UniBind ChIP-seq TFBS.",
    "useWhen": "Use when you need gene-regulation / functional-genomics data \u2014 ENCODE experiments (ChIP-seq, ATAC-seq, ...), biosamples and data files (complete, count-verified searches by assay/target/organism/format, or a record by accession); JASPAR transcription-factor binding profiles (PFM by versioned matrix id, version history, filtered profile catalog by species/collection, and the species/taxa/collections/releases listings); or UniBind high-confidence TF binding sites (search ChIP-seq datasets, per-model TFBS detail with BED/FASTA URLs, and TFBS overlapping a genomic region). Sourced from ENCODE, JASPAR and UniBind.",
    "sources": [
      "ENCODE",
      "JASPAR",
      "UniBind"
    ],
    "termsUrl": "https://www.encodeproject.org/about/data-use-policy/",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "encode_search_experiments",
        "connector": "regulation",
        "description": "Search ENCODE functional-genomics experiments (ChIP-seq, ATAC-seq, ...). Filters: assay_title (e.g. \"TF ChIP-seq\"), target (protein label, e.g. \"CTCF\"), organism (scientific name), status (default \"released\"), date_released_before (ISO date \u2014 a closed window), plus arbitrary portal field filters via extra_filters. The full result set is paged and count-verified; `accessions` lists every match, at most max_rows row summaries are returned.",
        "input": {
          "type": "object",
          "properties": {
            "assay_title": {
              "type": "string"
            },
            "target": {
              "type": "string"
            },
            "organism": {
              "type": "string"
            },
            "status": {
              "type": "string",
              "default": "released"
            },
            "date_released_before": {
              "type": "string"
            },
            "extra_filters": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "max_rows": {
              "type": "integer",
              "default": 100
            }
          }
        },
        "returns": "`{ total (exact), returned, truncated, accessions: [every matching accession, sorted], experiments: [ report rows: accession, assay_title, assay_term_name, target.label, biosample_ontology.term_name, status, date_released, lab.title ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"encode_search_experiments\", {\"target\": \"CTCF\", \"assay_title\": \"TF ChIP-seq\", \"max_rows\": 50})",
        "required": []
      },
      {
        "id": "encode_search_biosamples",
        "connector": "regulation",
        "description": "Search ENCODE biosamples (cell lines, tissues, primary cells). Filters: term_name (ontology term, e.g. \"K562\"), classification (\"cell line\", \"tissue\", ...), organism (scientific name), status (default \"released\"), date_created_before (ISO date), plus arbitrary portal field filters via extra_filters. Complete, count-verified: `accessions` is the full match list, at most max_rows row summaries are returned.",
        "input": {
          "type": "object",
          "properties": {
            "term_name": {
              "type": "string"
            },
            "classification": {
              "type": "string"
            },
            "organism": {
              "type": "string"
            },
            "status": {
              "type": "string",
              "default": "released"
            },
            "date_created_before": {
              "type": "string"
            },
            "extra_filters": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "max_rows": {
              "type": "integer",
              "default": 100
            }
          }
        },
        "returns": "`{ total, returned, truncated, accessions: [...], biosamples: [ report rows: accession, biosample_ontology.term_name/classification, organism.scientific_name, status, lab.title, summary, date_created ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"encode_search_biosamples\", {\"term_name\": \"K562\", \"classification\": \"cell line\", \"max_rows\": 25})",
        "required": []
      },
      {
        "id": "encode_list_files",
        "connector": "regulation",
        "description": "List ENCODE data files by format / assay / biosample. Filters: file_format (\"fastq\", \"bam\", \"bigWig\", \"bed\", ...), assay_term_name (the ontology term e.g. \"ChIP-seq\" \u2014 NOT the display assay_title like \"TF ChIP-seq\", which matches nothing; pass titles via extra_filters={\"assay_title\": ...}), biosample_term_name (e.g. \"K562\"), status (default \"released\"), date_created_before, plus arbitrary portal field filters via extra_filters. File queries match millions of rows unfiltered \u2014 always combine several filters. Complete + count-verified; at most max_rows row summaries returned.",
        "input": {
          "type": "object",
          "properties": {
            "file_format": {
              "type": "string"
            },
            "assay_term_name": {
              "type": "string"
            },
            "biosample_term_name": {
              "type": "string"
            },
            "status": {
              "type": "string",
              "default": "released"
            },
            "date_created_before": {
              "type": "string"
            },
            "extra_filters": {
              "type": "object",
              "additionalProperties": {
                "type": "string"
              }
            },
            "max_rows": {
              "type": "integer",
              "default": 100
            }
          }
        },
        "returns": "`{ total, returned, truncated, accessions: [...], files: [ report rows: accession, file_format, output_type, assay_term_name, assembly, dataset, status, file_size, date_created ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"encode_list_files\", {\"file_format\": \"bed\", \"assay_term_name\": \"ChIP-seq\", \"biosample_term_name\": \"K562\", \"extra_filters\": {\"output_type\": \"peaks\", \"assembly\": \"GRCh38\"}, \"max_rows\": 50})",
        "required": []
      },
      {
        "id": "encode_get_experiment",
        "connector": "regulation",
        "description": "Get one ENCODE experiment by accession (e.g. \"ENCSR000AKP\"). Returns a stable-field record: assay, target, biosample ontology + summary, description, lab, award project, release/submission dates, assemblies, replicate counts, replication type, dbxrefs, DOI and uuid. Volatile portal fields (audits, analyses, internal status) are excluded.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ record_type: \"experiment\", accession, status, assay_term_name, assay_title, target_label, biosample_term_name, biosample_classification, biosample_summary, description, lab, award_project, date_released, date_submitted, assembly: [...], bio_replicate_count, tech_replicate_count, replication_type, dbxrefs: [...], doi, uuid }`.",
        "example": "const result = await host.mcp(\"regulation\", \"encode_get_experiment\", {\"accession\": \"ENCSR000AKP\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "encode_get_file",
        "connector": "regulation",
        "description": "Get one ENCODE file by accession (e.g. \"ENCFF002JUR\"). Returns a stable-field record: format, output type/category, assay, assembly, parent dataset, biological replicates, file size, md5sums, run type, read length, lab, creation date, download href and uuid.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ record_type: \"file\", accession, status, file_format, file_format_type, output_type, output_category, assay_term_name, assembly, dataset, biological_replicates: [...], file_size, md5sum, content_md5sum, run_type, read_length, lab, date_created, href, uuid }`.",
        "example": "const result = await host.mcp(\"regulation\", \"encode_get_file\", {\"accession\": \"ENCFF002JUR\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "encode_get_biosample",
        "connector": "regulation",
        "description": "Get one ENCODE biosample by accession (e.g. \"ENCBS013JZP\"). Returns a stable-field record: ontology term + classification, organism, summary/description, source, donor, treatments, genetic modifications, life stage, age, sex, lab, creation date, status and uuid.",
        "input": {
          "type": "object",
          "properties": {
            "accession": {
              "type": "string"
            }
          },
          "required": [
            "accession"
          ]
        },
        "returns": "`{ record_type: \"biosample\", accession, status, term_name, classification, organism, donor, source, lab, summary, life_stage, age_display, sex, treatments: [...], genetic_modifications: [...], date_created, uuid }`.",
        "example": "const result = await host.mcp(\"regulation\", \"encode_get_biosample\", {\"accession\": \"ENCBS013JZP\"})",
        "required": [
          "accession"
        ]
      },
      {
        "id": "jaspar_get_matrix",
        "connector": "regulation",
        "description": "Get one JASPAR TF binding profile by VERSIONED matrix id (e.g. \"MA0002.2\"). Returns the full record: position frequency matrix (pfm), TF name/class/family, species, data type, literature references (pubmed/medline), sequence logo URL. Requires a versioned id (\"MA0002.2\", not \"MA0002\") \u2014 use jaspar_matrix_versions to enumerate versions. Versioned matrices are immutable, so results are reproducible.",
        "input": {
          "type": "object",
          "properties": {
            "matrix_id": {
              "type": "string"
            }
          },
          "required": [
            "matrix_id"
          ]
        },
        "returns": "`{ matrix_id, name, base_id, version, collection, pfm: { A:[...], C:[...], G:[...], T:[...] }, class, family, species: [ { tax_id, name } ], pubmed_ids, uniprot_ids, tax_group, type, sequence_logo, versions_url, sites_url, ... }` \u2014 the full immutable JASPAR record.",
        "example": "const result = await host.mcp(\"regulation\", \"jaspar_get_matrix\", {\"matrix_id\": \"MA0002.2\"})",
        "required": [
          "matrix_id"
        ]
      },
      {
        "id": "jaspar_matrix_versions",
        "connector": "regulation",
        "description": "List all versions of a JASPAR base matrix id (e.g. \"MA0002\"). Returns every released version with its matrix_id, name, collection and URL \u2014 count-verified. Use to pin an exact version before jaspar_get_matrix, or to track how a profile changed across releases. A versioned id (\"MA0002.2\") is accepted and reduced to its base.",
        "input": {
          "type": "object",
          "properties": {
            "base_id": {
              "type": "string"
            }
          },
          "required": [
            "base_id"
          ]
        },
        "returns": "`{ count, results: [ { matrix_id, name, base_id, version, collection, sequence_logo, url } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"jaspar_matrix_versions\", {\"base_id\": \"MA0002\"})",
        "required": [
          "base_id"
        ]
      },
      {
        "id": "jaspar_list_matrices",
        "connector": "regulation",
        "description": "Search/list JASPAR TF binding profiles (the full profile catalog). Filters (all optional): collection (\"CORE\", \"UNVALIDATED\"), tax_group (\"vertebrates\", \"plants\", ...), tax_id (NCBI taxonomy id, e.g. 9606 for human \u2014 this is how you filter by species; enumerate ids with jaspar_list_species), name (exact TF name, e.g. \"FOXA1\"), search (free text), version=\"latest\" (restrict to latest versions only). The full filtered catalog is paginated and count-verified; at most max_rows summary rows are returned.",
        "input": {
          "type": "object",
          "properties": {
            "collection": {
              "type": "string"
            },
            "tax_group": {
              "type": "string"
            },
            "tax_id": {
              "type": "integer"
            },
            "name": {
              "type": "string"
            },
            "search": {
              "type": "string"
            },
            "version": {
              "type": "string"
            },
            "max_rows": {
              "type": "integer",
              "default": 1000
            }
          }
        },
        "returns": "`{ count (exact), returned, truncated, matrices: [ { matrix_id, name, base_id, version, collection, sequence_logo, url } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"jaspar_list_matrices\", {\"tax_id\": 9606, \"collection\": \"CORE\", \"version\": \"latest\", \"max_rows\": 200})",
        "required": []
      },
      {
        "id": "jaspar_list_species",
        "connector": "regulation",
        "description": "List all species with JASPAR profiles (NCBI tax_id + name); count-verified full listing. Use the tax_id values to filter jaspar_list_matrices (e.g. 9606 = Homo sapiens, 10090 = Mus musculus).",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ count, results: [ { tax_id, species, url, matrix_url } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"jaspar_list_species\", {})",
        "required": []
      },
      {
        "id": "jaspar_list_taxa",
        "connector": "regulation",
        "description": "List all JASPAR taxonomic groups (vertebrates, plants, fungi, insects, ...); count-verified full listing. Use the group names as the tax_group filter of jaspar_list_matrices.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ count, results: [ { name, url } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"jaspar_list_taxa\", {})",
        "required": []
      },
      {
        "id": "jaspar_list_collections",
        "connector": "regulation",
        "description": "List all JASPAR collections (CORE, UNVALIDATED, ...); count-verified full listing. Use the collection names as the collection filter of jaspar_list_matrices (CORE = curated, non-redundant profiles).",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ count, results: [ { name, url } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"jaspar_list_collections\", {})",
        "required": []
      },
      {
        "id": "jaspar_list_releases",
        "connector": "regulation",
        "description": "List all JASPAR database releases (year, release number, active flag); count-verified full listing. Record the active release when selecting motifs for reproducibility, or check release history before comparing results across JASPAR versions.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ count, results: [ { year, release_number, pubmed_id, website, active, url } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"jaspar_list_releases\", {})",
        "required": []
      },
      {
        "id": "unibind_search_tfbs",
        "connector": "regulation",
        "description": "Search UniBind ChIP-seq datasets with high-confidence TFBS predictions (unibind.uio.no, 2021 release; direct TF-DNA interactions from ~10k datasets across 9 species). Each dataset is one (experiment, cell type, TF) triple. Filters (all optional, AND-combined, exact-match unless noted): tf_name (gene symbol, e.g. \"CTCF\"), cell_line (verbose UniBind title \u2014 prefer `search` for fuzzy matching), species (scientific name), collection (\"Robust\" = best-model / high confidence, or \"Permissive\"), jaspar_id (versioned, e.g. \"MA0139.1\"), search (free text). `total` is the API's exact count; at most max_rows rows are returned (a stable prefix).",
        "input": {
          "type": "object",
          "properties": {
            "tf_name": {
              "type": "string"
            },
            "cell_line": {
              "type": "string"
            },
            "species": {
              "type": "string"
            },
            "collection": {
              "type": "string",
              "enum": [
                "Robust",
                "Permissive"
              ]
            },
            "jaspar_id": {
              "type": "string"
            },
            "search": {
              "type": "string"
            },
            "max_rows": {
              "type": "integer",
              "default": 200
            }
          }
        },
        "returns": "`{ total (exact), returned, truncated, datasets: [ { tf_id (key for unibind_get_dataset), tf_name, total_peaks (ChIP-seq peak count, NOT TFBS count), identifier, cell_line } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"unibind_search_tfbs\", {\"tf_name\": \"CTCF\", \"collection\": \"Robust\", \"max_rows\": 50})",
        "required": []
      },
      {
        "id": "unibind_get_dataset",
        "connector": "regulation",
        "description": "Get one UniBind dataset's detail: per-model TFBS counts + file URLs. tf_id is the dataset key \"<identifier>.<cell_line>.<TF>\" as returned by unibind_search_tfbs (e.g. \"ENCSR000AUE.A549_lung_carcinoma.CTCF\"). Returns the TF name, source identifiers (ENCODE/GEO/GTRD), cell lines, biological conditions, JASPAR matrix ids, ChIP-seq peak count, and one row per TFBS prediction model (DAMO/PWM/...) with total_tfbs, score/distance thresholds, adjusted CentriMo p-value, and direct BED/FASTA download URLs \u2014 use those URLs (not an MCP call) to retrieve the complete site list.",
        "input": {
          "type": "object",
          "properties": {
            "tf_id": {
              "type": "string"
            }
          },
          "required": [
            "tf_id"
          ]
        },
        "returns": "`{ tf_id, tf_name, identifiers: [...], cell_lines: [...], biological_conditions: [...], jaspar_ids: [...], prediction_models: [...], total_peaks, n_models, models: [ { prediction_model, jaspar_id, jaspar_version, total_tfbs, score_threshold, distance_threshold, adj_centrimo_pvalue, bed_url, fasta_url } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"unibind_get_dataset\", {\"tf_id\": \"ENCSR000AUE.A549_lung_carcinoma.CTCF\"})",
        "required": [
          "tf_id"
        ]
      },
      {
        "id": "unibind_tfbs_in_region",
        "connector": "regulation",
        "description": "TF binding sites overlapping a genomic region (UniBind 2021 maps), served via the UCSC hubApi against UniBind's registered public track hubs (UniBind's own REST API has no region endpoint). Coordinates are 0-based half-open. genome: UCSC assembly \u2014 Robust hub: hg38, mm10, ce11, dm6, danRer11, sacCer3, rn6, araTha1; Permissive adds spo2 (no hg19 \u2014 lift first). chrom: with \"chr\" prefix. start/end: interval, end-start <= 1,000,000 bp. HONEST-CAP: at most 20,000 items are scanned per call; region_scan_complete=false means the region has more sites than were scanned (narrow the window) and, with tf_name set, matches may be missing. n_matching counts scanned sites passing the filter; returned/truncated describe the max_sites cap.",
        "input": {
          "type": "object",
          "properties": {
            "genome": {
              "type": "string"
            },
            "chrom": {
              "type": "string"
            },
            "start": {
              "type": "integer"
            },
            "end": {
              "type": "integer"
            },
            "tf_name": {
              "type": "string"
            },
            "collection": {
              "type": "string",
              "enum": [
                "Robust",
                "Permissive"
              ],
              "default": "Robust"
            },
            "max_sites": {
              "type": "integer",
              "default": 2000
            }
          },
          "required": [
            "genome",
            "chrom",
            "start",
            "end"
          ]
        },
        "returns": "`{ genome, chrom, start, end, collection, tf_name_filter, items_scanned, region_scan_complete, n_matching, returned, truncated, sites: [ { chrom, start, end, strand, dataset, cell_line, tf_name, jaspar_matrix } ] }`.",
        "example": "const result = await host.mcp(\"regulation\", \"unibind_tfbs_in_region\", {\"genome\": \"hg38\", \"chrom\": \"chr1\", \"start\": 1000000, \"end\": 1010000, \"collection\": \"Robust\"})",
        "required": [
          "genome",
          "chrom",
          "start",
          "end"
        ]
      }
    ]
  },
  {
    "id": "research-resources",
    "displayName": "Research Resources",
    "description": "Funding-opportunity search (Grants.gov) and antibody catalog lookups (Antibody Registry).",
    "useWhen": "Use when you need U.S. federal funding opportunities from Grants.gov (search by keyword, opportunity number, CFDA/ALN, agency such as NIH/NSF/FDA, status, eligibility, or funding category \u2014 complete, count-verified, with facet breakdowns) or research antibodies from the Antibody Registry (full-text search by target/name/catalog, lookup by RRID/accession, exact catalog-number matching, and registry statistics \u2014 with RRID, vendor, target, clone, and species). Sourced from Grants.gov and the Antibody Registry.",
    "sources": [
      "Grants.gov",
      "Antibody Registry"
    ],
    "termsUrl": "https://www.antibodyregistry.org/",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "search_grants",
        "connector": "research-resources",
        "description": "Search Grants.gov funding opportunities via the search2 API (complete, count-verified retrieval). At least one criterion is required (keyword, opportunity_number, aln/CFDA, agencies, eligibilities, funding_categories, or funding_instruments). opportunity_statuses defaults to [\"forecasted\",\"posted\"] (current opportunities); add \"closed\"/\"archived\" for historical ones. agencies takes codes like [\"HHS-NIH11\"] (NIH), [\"HHS-FDA\"], [\"NSF\"]. Set count_only for just the hit count + facets; max_records caps returned records (the walk still retrieves the complete set and flags truncated).",
        "input": {
          "type": "object",
          "properties": {
            "keyword": {
              "type": "string"
            },
            "opportunity_number": {
              "type": "string"
            },
            "aln": {
              "type": "string"
            },
            "agencies": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "opportunity_statuses": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "forecasted",
                  "posted",
                  "closed",
                  "archived"
                ]
              }
            },
            "eligibilities": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "funding_categories": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "funding_instruments": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "count_only": {
              "type": "boolean",
              "default": false
            },
            "max_records": {
              "type": "integer",
              "default": 100
            },
            "include_facets": {
              "type": "boolean",
              "default": true
            }
          }
        },
        "returns": "`{ hit_count, n_returned, truncated, records: [ { id, number, title, agencyCode, agency, oppStatus, openDate, closeDate, docType, cfdaList } ], facets? }` \u2014 records are the raw search2 hits (verbatim, sorted by oppNum). `truncated` is true when `hit_count` exceeds the returned count. `facets` (when include_facets) holds oppStatusOptions/agencies/eligibilities/fundingCategories/fundingInstruments/dateRangeOptions value counts. With `count_only`, records is [] and n_returned 0.",
        "example": "const result = await host.mcp(\"research-resources\", \"search_grants\", {\"keyword\": \"cancer\", \"agencies\": [\"HHS-NIH11\"], \"max_records\": 25})",
        "required": []
      },
      {
        "id": "search_antibodies",
        "connector": "research-resources",
        "description": "Full-text search the Antibody Registry (antibodyregistry.org, ~3.2M records). Token-based matching against antibody name/target/catalog text (\"TP53\" and \"p53\" are different queries). With page omitted, all pages are walked up to max_records or the anonymous depth cap (rows beyond offset 500 need authentication upstream, flagged as anonymous_limit_hit \u2014 never silently dropped). Pass a 1-based page for single-page retrieval (page*page_size must stay <= 500).",
        "input": {
          "type": "object",
          "properties": {
            "query": {
              "type": "string"
            },
            "page": {
              "type": "integer"
            },
            "page_size": {
              "type": "integer",
              "default": 100
            },
            "max_records": {
              "type": "integer",
              "default": 500
            }
          },
          "required": [
            "query"
          ]
        },
        "returns": "Walk mode (page omitted): `{ query, total_elements, retrieved, unique_ab_ids, complete, truncated_at_max_records, anonymous_limit_hit, items: [ { abId, abName, abTarget, catalogNum, vendorName, cloneId, sourceOrganism, targetSpecies, ... } ] }`. `total_elements` counts index rows (not unique antibodies). Single-page mode (page given): `{ query, page, total_elements, retrieved, complete, items }`.",
        "example": "const result = await host.mcp(\"research-resources\", \"search_antibodies\", {\"query\": \"CD4\", \"max_records\": 100})",
        "required": [
          "query"
        ]
      },
      {
        "id": "get_antibody",
        "connector": "research-resources",
        "description": "Fetch Antibody Registry detail record(s) for one antibody accession / RRID. Accepts a plain number (\"3643095\"), \"AB_3643095\", or \"RRID:AB_3643095\". The upstream route is list-valued (an accession can map to several curated records, e.g. multi-vendor duplicates). A nonexistent id yields record_count 0, not an error.",
        "input": {
          "type": "object",
          "properties": {
            "antibody_id": {
              "type": "string"
            }
          },
          "required": [
            "antibody_id"
          ]
        },
        "returns": "`{ ab_id (numeric), rrid (\"AB_<id>\"), record_count, records: [ full antibody records ] }`. `record_count` is 0 (with records []) when the accession has no records.",
        "example": "const result = await host.mcp(\"research-resources\", \"get_antibody\", {\"antibody_id\": \"RRID:AB_3643095\"})",
        "required": [
          "antibody_id"
        ]
      },
      {
        "id": "find_antibodies_by_catalog",
        "connector": "research-resources",
        "description": "Find antibodies by vendor catalog number (exact, case-insensitive). Implemented as a full-text search plus client-side exact matching on the catalog number (or its listed alternatives), because the upstream column-filter route returns HTTP 500 for every key. Pass an optional vendor name (exact, case-insensitive) to further narrow the matches.",
        "input": {
          "type": "object",
          "properties": {
            "catalog_number": {
              "type": "string"
            },
            "vendor": {
              "type": "string"
            },
            "page_size": {
              "type": "integer",
              "default": 100
            }
          },
          "required": [
            "catalog_number"
          ]
        },
        "returns": "`{ catalog_num, vendor, match_count, search_total_elements, matches: [ full antibody records ] }`. `search_total_elements` is the underlying full-text hit count; `matches` are the exact catalog-number matches.",
        "example": "const result = await host.mcp(\"research-resources\", \"find_antibodies_by_catalog\", {\"catalog_number\": \"ab32572\"})",
        "required": [
          "catalog_number"
        ]
      },
      {
        "id": "get_antibody_registry_stats",
        "connector": "research-resources",
        "description": "Antibody Registry statistics: total antibody count and last-update date. Returns the upstream /api/datainfo payload.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "`{ total (registry size), lastupdate (YYYY-MM-DD) }` \u2014 the upstream /api/datainfo payload.",
        "example": "const result = await host.mcp(\"research-resources\", \"get_antibody_registry_stats\", {})",
        "required": []
      }
    ]
  },
  {
    "id": "biomart",
    "displayName": "BioMart",
    "description": "Ensembl BioMart attribute queries and identifier translation.",
    "useWhen": "Use when you need Ensembl BioMart data \u2014 browsing the marts \u2192 datasets \u2192 attributes/filters hierarchy, running attribute queries (get_data) for a dataset with filters, or translating gene/transcript identifiers between attribute types (e.g. HGNC symbol \u2192 Ensembl gene ID).",
    "sources": [
      "Ensembl BioMart"
    ],
    "termsUrl": "https://www.ensembl.org/info/about/legal/disclaimer.html",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "list_marts",
        "connector": "biomart",
        "description": "List available Ensembl BioMart marts (databases). BioMart organizes data as MART -> DATASET -> ATTRIBUTES/FILTERS; a mart name feeds list_datasets.",
        "input": {
          "type": "object",
          "properties": {}
        },
        "returns": "CSV string with header `name,display_name,description` \u2014 one row per mart (e.g. `ENSEMBL_MART_ENSEMBL,Ensembl Genes 116,...`). Header-only when the registry lists no marts.",
        "example": "const result = await host.mcp(\"biomart\", \"list_marts\", {})",
        "required": []
      },
      {
        "id": "list_datasets",
        "connector": "biomart",
        "description": "List the datasets available in a given mart (e.g. hsapiens_gene_ensembl for human genes). A dataset name feeds the attribute/filter/query tools.",
        "input": {
          "type": "object",
          "properties": {
            "mart": {
              "type": "string"
            }
          },
          "required": [
            "mart"
          ]
        },
        "returns": "CSV string with header `name,display_name,description` \u2014 one row per dataset (`description` is the assembly/version, e.g. `GRCh38.p14`).",
        "example": "const result = await host.mcp(\"biomart\", \"list_datasets\", {\"mart\": \"ENSEMBL_MART_ENSEMBL\"})",
        "required": [
          "mart"
        ]
      },
      {
        "id": "list_common_attributes",
        "connector": "biomart",
        "description": "List the commonly used attributes for a dataset (a curated high-signal subset). Use this before list_all_attributes to pick attributes for get_data. `mart` is accepted for signature parity but ignored; the query keys off `dataset`.",
        "input": {
          "type": "object",
          "properties": {
            "mart": {
              "type": "string"
            },
            "dataset": {
              "type": "string"
            }
          },
          "required": [
            "mart",
            "dataset"
          ]
        },
        "returns": "CSV string with header `name,display_name,description` \u2014 the subset of the dataset\u2019s attributes that are commonly used identifiers/annotations.",
        "example": "const result = await host.mcp(\"biomart\", \"list_common_attributes\", {\"mart\": \"ENSEMBL_MART_ENSEMBL\", \"dataset\": \"hsapiens_gene_ensembl\"})",
        "required": [
          "mart",
          "dataset"
        ]
      },
      {
        "id": "list_all_attributes",
        "connector": "biomart",
        "description": "List all attributes available for a dataset, minus homologs and microarray probes (which are bulky and rarely needed). Can be large; prefer list_common_attributes first. `mart` is accepted for signature parity but ignored; the query keys off `dataset`.",
        "input": {
          "type": "object",
          "properties": {
            "mart": {
              "type": "string"
            },
            "dataset": {
              "type": "string"
            }
          },
          "required": [
            "mart",
            "dataset"
          ]
        },
        "returns": "CSV string with header `name,display_name,description` \u2014 every attribute except the homologs page and microarray-probe attributes.",
        "example": "const result = await host.mcp(\"biomart\", \"list_all_attributes\", {\"mart\": \"ENSEMBL_MART_ENSEMBL\", \"dataset\": \"hsapiens_gene_ensembl\"})",
        "required": [
          "mart",
          "dataset"
        ]
      },
      {
        "id": "list_filters",
        "connector": "biomart",
        "description": "List the filters available for a dataset. Filters narrow a get_data query (e.g. chromosome_name, biotype) and are passed to get_data as a filters dict. `mart` is accepted for signature parity but ignored; the query keys off `dataset`.",
        "input": {
          "type": "object",
          "properties": {
            "mart": {
              "type": "string"
            },
            "dataset": {
              "type": "string"
            }
          },
          "required": [
            "mart",
            "dataset"
          ]
        },
        "returns": "CSV string with header `name,description` \u2014 one row per filter name and its human-readable label.",
        "example": "const result = await host.mcp(\"biomart\", \"list_filters\", {\"mart\": \"ENSEMBL_MART_ENSEMBL\", \"dataset\": \"hsapiens_gene_ensembl\"})",
        "required": [
          "mart",
          "dataset"
        ]
      },
      {
        "id": "get_data",
        "connector": "biomart",
        "description": "Run a BioMart query: retrieve the requested attributes for a dataset, optionally narrowed by filters. This is the main data-retrieval tool. `mart` is accepted for signature parity but ignored; the query keys off `dataset`.",
        "input": {
          "type": "object",
          "properties": {
            "mart": {
              "type": "string"
            },
            "dataset": {
              "type": "string"
            },
            "attributes": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "filters": {
              "type": "object"
            }
          },
          "required": [
            "mart",
            "dataset",
            "attributes"
          ]
        },
        "returns": "CSV string whose header row is the requested attributes, followed by one row per matching record (in BioMart order). Header-only when nothing matches.",
        "example": "const result = await host.mcp(\"biomart\", \"get_data\", {\"mart\": \"ENSEMBL_MART_ENSEMBL\", \"dataset\": \"hsapiens_gene_ensembl\", \"attributes\": [\"ensembl_gene_id\", \"external_gene_name\", \"chromosome_name\"], \"filters\": {\"chromosome_name\": \"Y\", \"biotype\": \"protein_coding\"}})",
        "required": [
          "mart",
          "dataset",
          "attributes"
        ]
      },
      {
        "id": "get_translation",
        "connector": "biomart",
        "description": "Translate a single identifier from one attribute type to another (e.g. an HGNC symbol to an Ensembl gene ID) within a dataset. `mart` is accepted for signature parity but ignored; the query keys off `dataset`.",
        "input": {
          "type": "object",
          "properties": {
            "mart": {
              "type": "string"
            },
            "dataset": {
              "type": "string"
            },
            "from_attr": {
              "type": "string"
            },
            "to_attr": {
              "type": "string"
            },
            "target": {
              "type": "string"
            }
          },
          "required": [
            "mart",
            "dataset",
            "from_attr",
            "to_attr",
            "target"
          ]
        },
        "returns": "The translated identifier as a string, or a `No translation found ...` message string when the source id has no mapping.",
        "example": "const result = await host.mcp(\"biomart\", \"get_translation\", {\"mart\": \"ENSEMBL_MART_ENSEMBL\", \"dataset\": \"hsapiens_gene_ensembl\", \"from_attr\": \"hgnc_symbol\", \"to_attr\": \"ensembl_gene_id\", \"target\": \"TP53\"})",
        "required": [
          "mart",
          "dataset",
          "from_attr",
          "to_attr",
          "target"
        ]
      },
      {
        "id": "batch_translate",
        "connector": "biomart",
        "description": "Translate many identifiers from one attribute type to another in a single query \u2014 more efficient than repeated get_translation calls. `mart` is accepted for signature parity but ignored; the query keys off `dataset`.",
        "input": {
          "type": "object",
          "properties": {
            "mart": {
              "type": "string"
            },
            "dataset": {
              "type": "string"
            },
            "from_attr": {
              "type": "string"
            },
            "to_attr": {
              "type": "string"
            },
            "targets": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          },
          "required": [
            "mart",
            "dataset",
            "from_attr",
            "to_attr",
            "targets"
          ]
        },
        "returns": "`{ \"translations\": { <input>: <translated> }, \"not_found\": [str], \"found_count\": int, \"not_found_count\": int }` \u2014 `translations` maps each resolved input id to its target id.",
        "example": "const result = await host.mcp(\"biomart\", \"batch_translate\", {\"mart\": \"ENSEMBL_MART_ENSEMBL\", \"dataset\": \"hsapiens_gene_ensembl\", \"from_attr\": \"hgnc_symbol\", \"to_attr\": \"ensembl_gene_id\", \"targets\": [\"TP53\", \"BRCA1\", \"BRCA2\"]})",
        "required": [
          "mart",
          "dataset",
          "from_attr",
          "to_attr",
          "targets"
        ]
      }
    ]
  },
  {
    "id": "zinc",
    "displayName": "ZINC",
    "description": "ZINC22 purchasable chemical space (CartBlanche22) \u2014 compound lookup by ZINC id, SMILES exact/similarity search, supplier-code resolution, random sampling, 3D structure locations for docking.",
    "useWhen": "Use when you need purchasable small molecules from ZINC22 \u2014 look up compounds by ZINC id, search by SMILES (exact or analog/similarity), resolve vendor catalog codes, draw a random compound sample, or locate docking-ready 3D structures. Sourced from ZINC22 / CartBlanche22.",
    "sources": [
      "ZINC"
    ],
    "termsUrl": "https://zinc.docking.org/",
    "requiresNcbi": false,
    "tools": [
      {
        "id": "zinc_search_by_id",
        "connector": "zinc",
        "description": "Look up purchasable compounds in ZINC22/ZINC20 by ZINC identifier \u2014 answers \"what is this compound and who sells it\". Batched: pass up to 100 ids in one call rather than many single-id calls. Async upstream (submit + poll); can take up to timeout_s seconds.",
        "input": {
          "type": "object",
          "properties": {
            "zinc_ids": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              },
              "description": "One or more ZINC ids, e.g. ZINC000000000012 (max 100 per call)."
            },
            "max_results": {
              "type": "integer",
              "default": 50,
              "description": "Response bound (hard cap 500)."
            },
            "timeout_s": {
              "type": "number",
              "default": 25,
              "description": "Overall submit->result budget in seconds (clamped 5-55)."
            }
          },
          "required": [
            "zinc_ids"
          ]
        },
        "returns": "`{ \"query\", \"total_available\", \"returned_count\", \"truncated\", \"source_counts\": { \"zinc22\": int, ... }, \"records\": [ { \"zinc_id\", \"smiles\", \"tranche_name\", \"catalogs\", \"source\", \"tranche_properties\": { \"heavy_atoms\", \"logp\" } } ] }` \u2014 records capped at `max_results` (default 50, cap 500); `truncated` true when more were available. `source` is \"zinc22\"/\"zinc20\"; ids with no match simply have no record.",
        "example": "const result = await host.mcp(\"zinc\", \"zinc_search_by_id\", {\"zinc_ids\": [\"ZINC000000000012\"]})",
        "required": [
          "zinc_ids"
        ]
      },
      {
        "id": "zinc_search_by_smiles",
        "connector": "zinc",
        "description": "Search ZINC22's purchasable chemical space by structure \u2014 answers \"what purchasable compounds look like this SMILES\". This is BOTH the exact-match and the analog-discovery (similarity) tool: CartBlanche22 exposes one structure-search endpoint whose `dist` parameter spans exact through diverse, so there is deliberately no separate similarity-search tool. The slowest ZINC query \u2014 raise `dist` gradually rather than starting loose.",
        "input": {
          "type": "object",
          "properties": {
            "smiles": {
              "type": "string",
              "description": "Query SMILES string (sent verbatim as a form field)."
            },
            "dist": {
              "type": "integer",
              "default": 0,
              "description": "Tanimoto DISTANCE 0-10 (a distance, not a percent similarity): 0 = exact match, 1-3 = close analogs, 4-6 = moderate, 7-10 = diverse (looser = slower, many more hits)."
            },
            "adist": {
              "type": "integer",
              "description": "Anonymous-graph distance 0-10 (scaffold-shaped tolerance); defaults to dist."
            },
            "max_results": {
              "type": "integer",
              "default": 50,
              "description": "Response bound (hard cap 500)."
            },
            "timeout_s": {
              "type": "number",
              "default": 25,
              "description": "Overall submit->result budget in seconds (clamped 5-55)."
            }
          },
          "required": [
            "smiles"
          ]
        },
        "returns": "The standard bounded shape (`query`, `total_available`, `returned_count`, `truncated`, `source_counts`, `records`) with records as in `zinc_search_by_id`. `query` echoes the resolved `{ smiles, dist, adist }`.",
        "example": "const result = await host.mcp(\"zinc\", \"zinc_search_by_smiles\", {\"smiles\": \"CC(=O)Oc1ccccc1C(=O)O\", \"dist\": 2})",
        "required": [
          "smiles"
        ]
      },
      {
        "id": "zinc_search_by_supplier",
        "connector": "zinc",
        "description": "Resolve vendor catalog numbers to ZINC compounds \u2014 answers \"which ZINC substance is this supplier code, and what's its structure\". Batched: up to 100 supplier codes per call. Async upstream (submit + poll).",
        "input": {
          "type": "object",
          "properties": {
            "supplier_codes": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              },
              "description": "One or more vendor catalog codes, e.g. MCULE-2311834287 (max 100 per call)."
            },
            "max_results": {
              "type": "integer",
              "default": 50,
              "description": "Response bound (hard cap 500)."
            },
            "timeout_s": {
              "type": "number",
              "default": 25,
              "description": "Overall submit->result budget in seconds (clamped 5-55)."
            }
          },
          "required": [
            "supplier_codes"
          ]
        },
        "returns": "The standard bounded shape; records additionally carry `supplier_code` alongside `zinc_id`/`smiles`/`catalogs`/`tranche_name`/`tranche_properties`.",
        "example": "const result = await host.mcp(\"zinc\", \"zinc_search_by_supplier\", {\"supplier_codes\": [\"MCULE-2311834287\"]})",
        "required": [
          "supplier_codes"
        ]
      },
      {
        "id": "zinc_random_sample",
        "connector": "zinc",
        "description": "Draw a random sample of purchasable compounds from ZINC22 \u2014 for building screening decks, property baselines, or decoy sets. `count` doubles as this tool's `max_results`; re-calling draws a fresh sample. Async upstream (submit + poll).",
        "input": {
          "type": "object",
          "properties": {
            "count": {
              "type": "integer",
              "default": 50,
              "description": "Sample size; doubles as max_results (default 50, hard cap 500)."
            },
            "subset": {
              "type": "string",
              "description": "Optional predefined property filter: fragment (MW < 250), lead-like (MW 250-350, logP <= 3.5), drug-like (MW 350-500, Lipinski), lugs (curated). Other upstream subset names pass through verbatim."
            },
            "timeout_s": {
              "type": "number",
              "default": 25,
              "description": "Overall submit->result budget in seconds (clamped 5-55)."
            }
          },
          "required": []
        },
        "returns": "The standard bounded shape with records as in `zinc_search_by_id` (random order). `query` echoes `{ count, subset, known_subsets }`.",
        "example": "const result = await host.mcp(\"zinc\", \"zinc_random_sample\", {\"count\": 25, \"subset\": \"lead-like\"})",
        "required": []
      },
      {
        "id": "zinc_get_3d",
        "connector": "zinc",
        "description": "Locate docking-ready 3D structures for ZINC compounds. ZINC22 ships pre-generated 3D conformers (DOCK .db2.gz, .mol2.gz, .sdf.gz) in its file repository, organized by tranche \u2014 this tool resolves each id to its tranche and returns the repository locations to download from for docking prep (DOCK6, AutoDock Vina, etc.). Max 50 ids per call (3D retrieval is per-compound work). Async upstream (submit + poll).",
        "input": {
          "type": "object",
          "properties": {
            "zinc_ids": {
              "type": [
                "string",
                "array"
              ],
              "items": {
                "type": "string"
              },
              "description": "ZINC ids to prepare, e.g. ZINC000000000012 (max 50 per call)."
            },
            "timeout_s": {
              "type": "number",
              "default": 25,
              "description": "Overall lookup budget in seconds (clamped 5-55)."
            }
          },
          "required": [
            "zinc_ids"
          ]
        },
        "returns": "`{ \"query\", \"returned_count\", \"structures\", \"repository_note\" }`; each structure carries `zinc_id`, `found`, `smiles`, `source`, `tranche_name` + `tranche_properties`, and (when the tranche decodes) `download`: `{ repository, tranche_path_pattern: \"zinc-22*/H##/<tranche>/\", formats }`. Sub-release directories (zinc-22a, zinc-22b, \u2026) must be browsed for exact file names \u2014 the repository has no per-compound fetch URL.",
        "example": "const result = await host.mcp(\"zinc\", \"zinc_get_3d\", {\"zinc_ids\": [\"ZINC000000000012\"]})",
        "required": [
          "zinc_ids"
        ]
      }
    ]
  }
]
