gbif-biodiversity-mcp-server

v0.7.3 pre-1.0

Search GBIF species taxonomy, occurrence records, datasets, and publishers via MCP. STDIO or Streamable HTTP.

gbif-biodiversity.caseyjhand.com/mcp
claude mcp add --transport http gbif-biodiversity-mcp-server https://gbif-biodiversity.caseyjhand.com/mcp
codex mcp add gbif-biodiversity-mcp-server --url https://gbif-biodiversity.caseyjhand.com/mcp
{
  "mcpServers": {
    "gbif-biodiversity-mcp-server": {
      "url": "https://gbif-biodiversity.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http gbif-biodiversity-mcp-server https://gbif-biodiversity.caseyjhand.com/mcp
{
  "mcpServers": {
    "gbif-biodiversity-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "https://gbif-biodiversity.caseyjhand.com/mcp"
      ]
    }
  }
}
{
  "mcpServers": {
    "gbif-biodiversity-mcp-server": {
      "type": "http",
      "url": "https://gbif-biodiversity.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://gbif-biodiversity.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2026-07-28","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

13

gbif_match_species

open-world

Match a scientific name against the GBIF backbone taxonomy. Returns the best-matching taxon with full classification and a confidence score (0–100). This is the mandatory first step for any GBIF workflow — it returns the backbone taxonKey required by gbif_search_occurrences, gbif_count_occurrences, and gbif_occurrence_facets. When the queried name is a synonym, taxonKey is the accepted taxon it resolves to and matchedTaxonKey carries the synonym's own key; occurrence counts differ sharply between the two, so pass taxonKey. Below confidence 80, the match should be reviewed. matchType NONE means no usable match was found — try removing the strict flag or broadening the name.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_match_species",
    "arguments": {
      "name": "<name>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Scientific name to match. Examples: \"Parus major\", \"Agaricus bisporus\", \"Homo sapiens\". Fuzzy matching handles minor spelling variations. Common names are not supported — use gbif_search_species for vernacular name searches."
    },
    "strict": {
      "default": false,
      "description": "When true, only return an exact match. When false (default), GBIF applies fuzzy matching — useful for minor spelling variations and abbreviated names.",
      "type": "boolean"
    },
    "kingdom": {
      "description": "Narrow the match to a specific kingdom (e.g., \"Animalia\", \"Plantae\", \"Fungi\") to disambiguate names that appear in multiple kingdoms. Omit the field to match against the whole backbone — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the undisambiguated match, which is indistinguishable from a match that honored the kingdom.",
      "type": "string"
    },
    "rank": {
      "description": "Expected taxonomic rank. Use to avoid matching a genus when you expect a species.",
      "type": "string",
      "enum": [
        "KINGDOM",
        "PHYLUM",
        "CLASS",
        "ORDER",
        "FAMILY",
        "GENUS",
        "SPECIES",
        "SUBSPECIES"
      ]
    }
  },
  "required": [
    "name",
    "strict"
  ],
  "additionalProperties": false
}
view source ↗

gbif_bulk_match_species

open-world

Resolve up to 50 scientific names to GBIF backbone taxon keys in one call — the batch counterpart to gbif_match_species for checklist, inventory, and species-list workflows that would otherwise need one round trip per name. Each name is matched independently and results are returned in input order, one entry per name. A name with no backbone match yields matchType NONE (no taxonKey) instead of failing the batch; a per-name lookup failure yields matchType ERROR carrying that name's error message and, when the failure was classified, a machine-readable reason — the rest of the batch is unaffected, and the call as a whole still succeeds. When a queried name is a synonym, taxonKey is the accepted taxon it resolves to and matchedTaxonKey carries the synonym's own key. Common names are not supported — use gbif_search_species for vernacular searches. Below confidence 80, review the match.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_bulk_match_species",
    "arguments": {
      "names": "<names>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "names": {
      "minItems": 1,
      "maxItems": 50,
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1,
        "description": "A scientific name to match, e.g. \"Panthera leo\"."
      },
      "description": "Scientific names to match against the GBIF backbone. 1–50 per call, matched in parallel."
    },
    "strict": {
      "default": false,
      "description": "When true, require an exact match for every name (no fuzzy matching). When false (default), GBIF applies fuzzy matching to tolerate minor misspellings.",
      "type": "boolean"
    }
  },
  "required": [
    "names",
    "strict"
  ],
  "additionalProperties": false
}
view source ↗

gbif_get_species

open-world

Fetch a single backbone taxon by its GBIF taxon key. Returns full classification, authorship, taxonomic status, vernacular name, descendant count, and publication reference. Use after gbif_match_species when you need the complete record rather than the match summary. When taxonomicStatus is SYNONYM, acceptedKey and accepted fields identify the accepted taxon. The extinct field is absent (not false) on most records — only present on explicitly flagged taxa.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_get_species",
    "arguments": {
      "taxonKey": "<taxonKey>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "taxonKey": {
      "type": "number",
      "description": "GBIF backbone taxon key from gbif_match_species or another taxonomy tool."
    }
  },
  "required": [
    "taxonKey"
  ],
  "additionalProperties": false
}
view source ↗

gbif_search_species

open-world

Search or browse the GBIF backbone taxonomy. Accepts scientific name fragments, rank filters, and higher-taxon constraints. Useful for exploring what species exist under a higher taxon (e.g., "list all families of Coleoptera"), for simple name-fragment searches, or when gbif_match_species returns too narrow a result. kingdom, family, and genus scope the browse to a higher taxon: each is resolved to its backbone key before the search runs, so the narrowest one supplied is what scopes, an alternative name resolves to the taxon it is a synonym of, and a name that matches no backbone taxon at that rank fails rather than returning the whole index. Names are capitalized as GBIF writes them ("Paridae", not "paridae") and are matched exactly, not fuzzily. Paginated — use limit and offset to walk through results.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_search_species",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "q": {
      "description": "Name fragment to search for. Matches scientific and vernacular names. Omit the field to browse without a name term — a blank or whitespace-only value is rejected rather than sent, because GBIF answers a blank one with the whole 46,623,754-name index and a whitespace-only one with nothing, and neither is the search a caller who filled the field was asking for.",
      "type": "string"
    },
    "rank": {
      "description": "Filter to a specific taxonomic rank.",
      "type": "string",
      "enum": [
        "KINGDOM",
        "PHYLUM",
        "CLASS",
        "ORDER",
        "FAMILY",
        "GENUS",
        "SPECIES",
        "SUBSPECIES"
      ]
    },
    "kingdom": {
      "description": "Scope the search to a kingdom, by name — \"Animalia\", \"Plantae\", \"Fungi\". Resolved to its backbone key before the search runs, and matched exactly: capitalize it as GBIF writes it, since \"animalia\" resolves to nothing. Supplied alongside family or genus it disambiguates that name rather than scoping on its own — \"Prunella\" alone names both a bird genus and a plant genus and resolves to neither. Omit the field to browse every kingdom; a blank or whitespace-only value is rejected rather than dropped.",
      "type": "string"
    },
    "family": {
      "description": "Scope the search to a family, by name — \"Paridae\", \"Fagaceae\". Resolved to its backbone key before the search runs, so an alternative family name lands on the taxon it is a synonym of (\"Compositae\" scopes to Asteraceae). Matched exactly and capitalized as GBIF writes it; a name that is not a backbone family fails rather than being ignored. Supplied with genus, it must be that genus's own family. Omit the field to browse every family; a blank or whitespace-only value is rejected rather than dropped.",
      "type": "string"
    },
    "genus": {
      "description": "Scope the search to a genus, by name — \"Quercus\", \"Parus\". Resolved to its backbone key before the search runs, and it is the narrowest of the three, so it is what scopes when kingdom or family is supplied too. Matched exactly and capitalized as GBIF writes it; a name shared across kingdoms (\"Prunella\", \"Oenanthe\") resolves only when kingdom is supplied with it. Omit the field to browse every genus; a blank or whitespace-only value is rejected rather than dropped.",
      "type": "string"
    },
    "isExtinct": {
      "description": "Filter to extinct (true) or extant (false) taxa.",
      "type": "boolean"
    },
    "datasetKey": {
      "description": "Scope to a specific checklist dataset UUID (8-4-4-4-12 hex). Omit the field to search the GBIF backbone — an empty string is rejected rather than read as no scope, because GBIF answers a blank datasetKey with the unfiltered backbone result.",
      "type": "string"
    },
    "limit": {
      "default": 20,
      "description": "Number of records to return (default 20, max 1000).",
      "type": "number",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset.",
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

gbif_get_species_classification

open-world

Return the parent chain for a taxon — from kingdom (or domain) down to the immediate parent of the queried taxon — as an ordered array. Each entry has its rank, canonical name, and taxon key. The array is returned root-first (kingdom → phylum → class → … → immediate parent of the queried taxon); the queried taxon itself is not included — call gbif_get_species for its own record. Useful for building taxonomic trees or understanding placement without navigating the backbone level-by-level.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_get_species_classification",
    "arguments": {
      "taxonKey": "<taxonKey>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "taxonKey": {
      "type": "number",
      "description": "GBIF backbone taxon key from gbif_match_species or another taxonomy tool."
    }
  },
  "required": [
    "taxonKey"
  ],
  "additionalProperties": false
}
view source ↗

gbif_get_species_children

open-world

List direct children of a backbone taxon — genera within a family, species within a genus, subspecies within a species. Paginated. Use gbif_match_species to get the taxonKey first, then iterate with offset for large groups.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_get_species_children",
    "arguments": {
      "taxonKey": "<taxonKey>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "taxonKey": {
      "type": "number",
      "description": "GBIF backbone taxon key from gbif_match_species or another taxonomy tool."
    },
    "limit": {
      "default": 20,
      "description": "Number of children to return (default 20, max 1000).",
      "type": "number",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset.",
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "taxonKey",
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

gbif_search_occurrences

open-world

Search 3.9B+ GBIF occurrence records with Darwin Core filters. Use taxonKey from gbif_match_species for reliable results — it resolves synonyms automatically. Accepts country (uppercase ISO 3166-1 alpha-2, where the record was observed), publishingCountry (the publishing organization's country — a different question), stateProvince, bounding box (decimalLatitude/decimalLongitude ranges), WKT polygon geometry, year range, month, basis of record, coordinate filter, and dataset key. Returns sightings only by default — GBIF also indexes absence records (surveys that looked and found nothing), and occurrenceStatus controls whether they are included. Pagination is capped at offset+limit=100,001 and GBIF offers no cursor or scroll, so a larger result set is covered only by partitioning it — facet it by DATASET_KEY with gbif_occurrence_facets and search each datasetKey separately. This server cannot download a result set in bulk; that needs the GBIF Download API with a GBIF.org account, or the GBIF snapshot on AWS Open Data.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_search_occurrences",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "taxonKey": {
      "description": "GBIF backbone taxon key from gbif_match_species. Preferred over scientificName — matches all synonyms automatically. Matches the given taxon and all descendant taxa (subspecies, varieties, etc.).",
      "type": "number"
    },
    "scientificName": {
      "description": "Scientific name filter. Less precise than taxonKey — does not match synonyms. Use taxonKey from gbif_match_species for reliable results. Supplying both does not narrow the search: GBIF combines the two taxon filters with OR, so the result is the union of the two, not their intersection. Omit the field to search every name — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered result set.",
      "type": "string"
    },
    "country": {
      "description": "ISO 3166-1 alpha-2 code, uppercase, of where the occurrence was recorded (e.g., \"GB\", \"US\", \"DE\", \"SE\"). Not the publisher's country — that is publishingCountry, and the two disagree on most records. Lowercase and alpha-3 forms (\"gb\", \"USA\") match nothing upstream, which is why only the uppercase two-letter form is accepted here. Take a value from a COUNTRY facet on gbif_occurrence_facets; an uppercase pair GBIF does not know (\"XX\") is rejected upstream by name.",
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "publishingCountry": {
      "description": "ISO 3166-1 alpha-2 code, uppercase, of the organization that published the record — not where the occurrence was observed, which is country. The two differ constantly: of 60,290,950 records observed in GB, 1,548,928 were published by US organizations. Take a value from a PUBLISHING_COUNTRY facet on gbif_occurrence_facets. Lowercase and alpha-3 forms (\"us\", \"USA\") match nothing upstream, which is why only the uppercase two-letter form is accepted here.",
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "stateProvince": {
      "description": "State, province, or first-level administrative division, matched as a verbatim string — exact and case-sensitive. GBIF stores what each dataset recorded without normalizing it, so there is no vocabulary to guess from: \"England\", \"England - Greater London\", and \"Greater London\" are three distinct values, and \"england\" is none of them. Take one from a STATE_PROVINCE facet on gbif_occurrence_facets scoped the same way and pass it back unchanged — an unmatched value returns zero records rather than an error. Omit the field to search every state or province — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered result set. Records carrying no stateProvince match no value, so this cannot partition a scope.",
      "type": "string"
    },
    "decimalLatitude": {
      "description": "Latitude range as \"min,max\" (e.g., \"47.0,48.5\"). Decimal degrees, WGS84. Combine with decimalLongitude for a bounding box. Omit the field to leave latitude unbounded — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered result set.",
      "type": "string"
    },
    "decimalLongitude": {
      "description": "Longitude range as \"min,max\" (e.g., \"8.0,9.5\"). Decimal degrees, WGS84. Combine with decimalLatitude for a bounding box. Omit the field to leave longitude unbounded — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered result set.",
      "type": "string"
    },
    "geometry": {
      "description": "WKT polygon for geographic filtering (e.g., POLYGON((8 47, 9 47, 9 48, 8 48, 8 47))). Coordinates are longitude latitude. Takes precedence over decimalLatitude/decimalLongitude. Omit the field to search everywhere — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered result set.",
      "type": "string"
    },
    "year": {
      "description": "Year or year range. Single year: \"2024\". Range: \"2020,2024\". Filters by observation year. Both endpoints inclusive. Omit the field to search every year — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered result set.",
      "type": "string"
    },
    "month": {
      "description": "Calendar month (1–12). Useful for seasonal distribution queries.",
      "type": "number",
      "minimum": 1,
      "maximum": 12
    },
    "basisOfRecord": {
      "description": "Filter by how the occurrence was recorded. HUMAN_OBSERVATION covers citizen science. PRESERVED_SPECIMEN covers natural history collections.",
      "type": "string",
      "enum": [
        "HUMAN_OBSERVATION",
        "MACHINE_OBSERVATION",
        "PRESERVED_SPECIMEN",
        "LIVING_SPECIMEN",
        "MATERIAL_SAMPLE",
        "MATERIAL_CITATION",
        "OCCURRENCE",
        "LITERATURE"
      ]
    },
    "hasCoordinate": {
      "description": "When true, return only georeferenced records (those with coordinates). When false, return ONLY records without coordinates. Omit the parameter entirely to include all records regardless of coordinate presence.",
      "type": "boolean"
    },
    "isInCluster": {
      "description": "Filter to records flagged as likely duplicates (true) or exclude them (false). Omit to include all. Note: GBIF does not expose a cluster identifier — only the membership flag. To de-duplicate, set isInCluster: false to exclude all clustered records.",
      "type": "boolean"
    },
    "coordinateUncertaintyInMeters": {
      "description": "Filter by coordinate uncertainty radius in meters. Range format: \"min,max\" (e.g., \"0,1000\" for sub-kilometer precision). Both endpoints inclusive. Omit the field to accept any uncertainty — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered result set.",
      "type": "string"
    },
    "datasetKey": {
      "description": "Restrict results to a single dataset by its GBIF dataset UUID (8-4-4-4-12 hex). Obtain one from gbif_search_datasets, gbif_get_dataset, a DATASET_KEY facet (gbif_occurrence_facets), or the datasetKey field on an occurrence record. Omit the field to search every dataset — an empty string is rejected rather than read as no filter, because GBIF answers a blank datasetKey with the unfiltered result set.",
      "type": "string"
    },
    "occurrenceStatus": {
      "default": "PRESENT",
      "description": "Presence/absence filter. Defaults to PRESENT: an ABSENT record documents a survey that looked for the taxon and did not find it, so including one would read as a sighting of the opposite. Use ANY for both (GBIF's own default), or ABSENT for non-observations alone.",
      "type": "string",
      "enum": [
        "PRESENT",
        "ABSENT",
        "ANY"
      ]
    },
    "iucnRedListCategory": {
      "description": "Restrict to records whose taxon carries this IUCN Red List category: CR Critically Endangered, EN Endangered, VU Vulnerable, NT Near Threatened, LC Least Concern, DD Data Deficient, EX Extinct, EW Extinct in the Wild, CD Conservation Dependent. Records with no category are excluded when this is set.",
      "type": "string",
      "enum": [
        "CR",
        "EN",
        "VU",
        "NT",
        "LC",
        "DD",
        "EX",
        "EW",
        "CD"
      ]
    },
    "limit": {
      "default": 20,
      "description": "Number of records to return (default 20, max 300).",
      "type": "number",
      "minimum": 1,
      "maximum": 300
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset. GBIF serves offset+limit up to 100,001 and rejects anything past it, with no cursor or scroll to continue from. To reach a result set larger than that, split it into per-datasetKey searches using a DATASET_KEY facet from gbif_occurrence_facets — gap-free and high-cardinality, unlike YEAR, which leaves undated records in no bucket — rather than paging deeper.",
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "occurrenceStatus",
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

gbif_count_occurrences

open-world

Count occurrences matching a taxon + location filter without fetching records. Use for quick totals ("how many Aves records in Sweden?") or before deciding whether to paginate a full search. Accepts taxonKey, country (uppercase ISO 3166-1 alpha-2), publishingCountry, stateProvince, isGeoreferenced, datasetKey, year, occurrenceStatus, and iucnRedListCategory. Counts sightings only by default, matching gbif_search_occurrences — GBIF also indexes absence records, and for some taxa they are the overwhelming majority. A count above 100,001 is the signal to partition rather than page: gbif_search_occurrences cannot reach past that offset, so split the query by DATASET_KEY via gbif_occurrence_facets and search each dataset separately.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_count_occurrences",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "taxonKey": {
      "description": "GBIF backbone taxon key from gbif_match_species. Matches the given taxon and all descendant taxa (subspecies, varieties, etc.).",
      "type": "number"
    },
    "country": {
      "description": "ISO 3166-1 alpha-2 code, uppercase, of where the occurrence was recorded (e.g., \"GB\", \"US\"). Not the publisher's country — that is publishingCountry, and the two disagree on most records. Lowercase and alpha-3 forms (\"gb\", \"USA\") match nothing upstream, which is why only the uppercase two-letter form is accepted here. Take a value from a COUNTRY facet on gbif_occurrence_facets; an uppercase pair GBIF does not know (\"XX\") is rejected upstream by name.",
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "publishingCountry": {
      "description": "ISO 3166-1 alpha-2 code, uppercase, of the organization that published the record — not where the occurrence was observed, which is country. The two differ constantly: of 60,290,950 records observed in GB, 1,548,928 were published by US organizations. Take a value from a PUBLISHING_COUNTRY facet on gbif_occurrence_facets. Lowercase and alpha-3 forms (\"us\", \"USA\") match nothing upstream, which is why only the uppercase two-letter form is accepted here.",
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "stateProvince": {
      "description": "State, province, or first-level administrative division, matched as a verbatim string — exact and case-sensitive. GBIF stores what each dataset recorded without normalizing it, so there is no vocabulary to guess from: \"England\", \"England - Greater London\", and \"Greater London\" are three distinct values, and \"england\" is none of them. Take one from a STATE_PROVINCE facet on gbif_occurrence_facets scoped the same way and pass it back unchanged — an unmatched value counts zero rather than erroring. Omit the field to count across every state or province — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered total.",
      "type": "string"
    },
    "isGeoreferenced": {
      "description": "When true, count only georeferenced records. When false, count only non-georeferenced records.",
      "type": "boolean"
    },
    "datasetKey": {
      "description": "Filter to a specific dataset UUID (8-4-4-4-12 hex) from gbif_search_datasets. Omit the field to count across every dataset — an empty string is rejected rather than read as no filter, because GBIF answers a blank datasetKey with the unfiltered total. The result is not the recordCount the dataset tools and the gbif://dataset/{datasetKey} resource report for the same key: that figure spans every occurrenceStatus, while this count applies occurrenceStatus below, PRESENT by default.",
      "type": "string"
    },
    "year": {
      "description": "Year or year range (e.g., \"2024\" or \"2020,2024\"). Both endpoints inclusive. Omit the field to count across every year — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered total.",
      "type": "string"
    },
    "occurrenceStatus": {
      "default": "PRESENT",
      "description": "Presence/absence filter. Defaults to PRESENT: an ABSENT record documents a survey that looked for the taxon and did not find it, so counting one inflates the total with the opposite of a sighting. Use ANY for both (GBIF's own default), or ABSENT for non-observations alone. Matches the gbif_search_occurrences default, so the two tools agree.",
      "type": "string",
      "enum": [
        "PRESENT",
        "ABSENT",
        "ANY"
      ]
    },
    "iucnRedListCategory": {
      "description": "Count only records whose taxon carries this IUCN Red List category: CR Critically Endangered, EN Endangered, VU Vulnerable, NT Near Threatened, LC Least Concern, DD Data Deficient, EX Extinct, EW Extinct in the Wild, CD Conservation Dependent. Records with no category are excluded when this is set.",
      "type": "string",
      "enum": [
        "CR",
        "EN",
        "VU",
        "NT",
        "LC",
        "DD",
        "EX",
        "EW",
        "CD"
      ]
    }
  },
  "required": [
    "occurrenceStatus"
  ],
  "additionalProperties": false
}
view source ↗

gbif_get_occurrence

open-world

Fetch a single occurrence record by its GBIF occurrence key. Returns the complete Darwin Core record — all coordinates, administrative geography (GADM levels 0–3), dates, collections metadata, collector identifiers, conservation status, media links, and quality issue flags. Check occurrenceStatus before reading the record as a sighting: ABSENT means a survey looked for the taxon and did not find it. Use the occurrence key from gbif_search_occurrences results.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_get_occurrence",
    "arguments": {
      "occurrenceKey": "<occurrenceKey>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "occurrenceKey": {
      "type": "number",
      "description": "GBIF occurrence key from gbif_search_occurrences results."
    }
  },
  "required": [
    "occurrenceKey"
  ],
  "additionalProperties": false
}
view source ↗

gbif_occurrence_facets

open-world

Aggregate occurrence counts across a dimension (COUNTRY, STATE_PROVINCE, YEAR, BASIS_OF_RECORD, DATASET_KEY, KINGDOM_KEY, etc.). Returns one page of facet values ranked by count descending — the top facetLimit at facetOffset 0, a later slice of the same ranking past that. No record payloads returned. Core tool for distribution analysis and trend queries: "which countries have the most records for this species?", "how has observation volume changed since 2010?". Scope the aggregation with taxonKey, country (uppercase ISO 3166-1 alpha-2), publishingCountry, stateProvince, year, geometry, basisOfRecord, datasetKey, occurrenceStatus, or iucnRedListCategory filters. Also the way to split a result set too large for gbif_search_occurrences to page (offset+limit caps at 100,001): facet by DATASET_KEY, then search each datasetKey on its own. Aggregates sightings only by default, matching gbif_search_occurrences and gbif_count_occurrences; to measure the presence/absence split itself, pass facet OCCURRENCE_STATUS with occurrenceStatus ANY.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_occurrence_facets",
    "arguments": {
      "facet": "<facet>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "facet": {
      "type": "string",
      "enum": [
        "BASIS_OF_RECORD",
        "COUNTRY",
        "STATE_PROVINCE",
        "YEAR",
        "DATASET_KEY",
        "KINGDOM_KEY",
        "PHYLUM_KEY",
        "CLASS_KEY",
        "ORDER_KEY",
        "FAMILY_KEY",
        "GENUS_KEY",
        "SPECIES_KEY",
        "PUBLISHING_COUNTRY",
        "MONTH",
        "OCCURRENCE_STATUS",
        "IUCN_RED_LIST_CATEGORY"
      ],
      "description": "Dimension to aggregate by (e.g., COUNTRY, YEAR, BASIS_OF_RECORD, SPECIES_KEY, OCCURRENCE_STATUS, IUCN_RED_LIST_CATEGORY). DATASET_KEY is the dimension to split on when a result set is too large to page: every occurrence carries exactly one datasetKey, so its buckets sum to totalOccurrences with no gap and no overlap, and it has the cardinality to cut a large scope into pageable pieces. BASIS_OF_RECORD and PUBLISHING_COUNTRY are gap-free too and both have a matching filter on the occurrence tools, so either can drive a further split of a bucket still too large — but on that same scope they return 9 and 41 buckets against DATASET_KEY's 550, so neither replaces it as the first cut. A dimension a record can lack silently drops that record: faceting one 60,290,950-record scope by YEAR returned 224 buckets summing to 59,407,400, leaving 883,550 undated records in no bucket at all, and MONTH, STATE_PROVINCE, and SPECIES_KEY lose records the same way — stateProvince included, even though the occurrence tools can now filter on it. Sums are comparable only across the same occurrenceStatus scope."
    },
    "taxonKey": {
      "description": "Backbone taxon key to scope the aggregation. Matches the given taxon and all descendant taxa (subspecies, varieties, etc.).",
      "type": "number"
    },
    "country": {
      "description": "ISO 3166-1 alpha-2 code, uppercase, of where the occurrence was recorded, to scope to one country. Not the publisher's country — that is publishingCountry, and the two disagree on most records. Scope to one country, or pass back a value this tool returned under facet COUNTRY to drill into that bucket. Lowercase and alpha-3 forms (\"gb\", \"USA\") match nothing upstream, which is why only the uppercase two-letter form is accepted here.",
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "publishingCountry": {
      "description": "ISO 3166-1 alpha-2 code, uppercase, of the organization that published the record — not where the occurrence was observed, which is country. Scope to one publisher country, or pass back a value this tool returned under facet PUBLISHING_COUNTRY to drill into that bucket. Lowercase and alpha-3 forms (\"us\", \"USA\") match nothing upstream, which is why only the uppercase two-letter form is accepted here.",
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "stateProvince": {
      "description": "State, province, or first-level administrative division, matched as a verbatim string — exact and case-sensitive. Pass back a value this tool returned under facet STATE_PROVINCE rather than a guessed one: GBIF stores what each dataset recorded without normalizing it, so \"England\", \"England - Greater London\", and \"Greater London\" are three distinct values, \"england\" is none of them, and an unmatched value aggregates zero records rather than erroring. Omit the field to aggregate across every state or province — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered aggregation.",
      "type": "string"
    },
    "year": {
      "description": "Year or year range (e.g., \"2020,2024\") to scope the aggregation. Both endpoints inclusive. Omit the field to aggregate across every year — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered aggregation.",
      "type": "string"
    },
    "basisOfRecord": {
      "description": "Scope to a specific basis of record.",
      "type": "string",
      "enum": [
        "HUMAN_OBSERVATION",
        "MACHINE_OBSERVATION",
        "PRESERVED_SPECIMEN",
        "LIVING_SPECIMEN",
        "MATERIAL_SAMPLE",
        "MATERIAL_CITATION",
        "OCCURRENCE",
        "LITERATURE"
      ]
    },
    "geometry": {
      "description": "WKT polygon to scope the aggregation to a geographic area (e.g., POLYGON((8 47, 9 47, 9 48, 8 48, 8 47))). Coordinates are longitude latitude. Omit the field to aggregate everywhere — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered aggregation.",
      "type": "string"
    },
    "datasetKey": {
      "description": "Scope the aggregation to a single dataset by its GBIF dataset UUID (8-4-4-4-12 hex). Obtain one from gbif_search_datasets, gbif_get_dataset, a DATASET_KEY facet, or the datasetKey field on an occurrence record. Omit the field to aggregate across every dataset — an empty string is rejected rather than read as no scope, because GBIF answers a blank datasetKey with the unfiltered aggregation.",
      "type": "string"
    },
    "occurrenceStatus": {
      "default": "PRESENT",
      "description": "Presence/absence scope. Defaults to PRESENT so the aggregation counts sightings, not the surveys that looked and found nothing, and agrees with gbif_count_occurrences on the same filters. Use ANY for both — required to see both buckets when facet is OCCURRENCE_STATUS — or ABSENT for non-observations alone.",
      "type": "string",
      "enum": [
        "PRESENT",
        "ABSENT",
        "ANY"
      ]
    },
    "iucnRedListCategory": {
      "description": "Scope to records whose taxon carries this IUCN Red List category: CR Critically Endangered, EN Endangered, VU Vulnerable, NT Near Threatened, LC Least Concern, DD Data Deficient, EX Extinct, EW Extinct in the Wild, CD Conservation Dependent. Leave unset and facet on IUCN_RED_LIST_CATEGORY to see the whole distribution instead.",
      "type": "string",
      "enum": [
        "CR",
        "EN",
        "VU",
        "NT",
        "LC",
        "DD",
        "EX",
        "EW",
        "CD"
      ]
    },
    "facetLimit": {
      "default": 10,
      "description": "Maximum number of facet values to return (default 10, max 100).",
      "type": "number",
      "minimum": 1,
      "maximum": 100
    },
    "facetOffset": {
      "default": 0,
      "description": "Zero-based offset into the ranked facet values, for paging past the first facetLimit values on high-cardinality dimensions like DATASET_KEY. Advance by facetLimit to fetch the next page (0, then facetLimit, then 2×facetLimit, …).",
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "facet",
    "occurrenceStatus",
    "facetLimit",
    "facetOffset"
  ],
  "additionalProperties": false
}
view source ↗

gbif_search_datasets

open-world

Search GBIF datasets by keyword, type, publishing country (uppercase ISO 3166-1 alpha-2), publishing organization, or hosting organization. The two organization filters answer different questions — publishingOrg matches the organization whose data it is, hostingOrg the organization whose installation serves it — and an organization key from gbif_search_publishers usually wants publishingOrg. Returns dataset title, description, license, record count, and DOI. Use to find the source dataset behind a set of records, or to explore what data collections are available for a taxon, country, or organization.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_search_datasets",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "q": {
      "description": "Free-text search across dataset title and description. Omit the field to browse without a term — a blank or whitespace-only value is rejected rather than sent, because GBIF answers a blank one with all 123,527 indexed datasets and a whitespace-only one with none, and neither is the search a caller who filled the field was asking for.",
      "type": "string"
    },
    "type": {
      "description": "Filter by dataset type. OCCURRENCE for observation records, CHECKLIST for species lists.",
      "type": "string",
      "enum": [
        "OCCURRENCE",
        "CHECKLIST",
        "METADATA",
        "SAMPLING_EVENT"
      ]
    },
    "publishingCountry": {
      "description": "ISO 3166-1 alpha-2 code, uppercase, of the organization that published the dataset (e.g., \"GB\", \"US\", \"DE\", \"SE\"). Lowercase and alpha-3 forms (\"gb\", \"GBR\") match nothing upstream, which is why only the uppercase two-letter form is accepted here — unlike the country filter on gbif_search_publishers, which resolves either form. Take a value from a PUBLISHING_COUNTRY facet on gbif_occurrence_facets; an uppercase pair GBIF does not assign (\"XX\") is rejected upstream by name.",
      "type": "string",
      "pattern": "^[A-Z]{2}$"
    },
    "publishingOrg": {
      "description": "UUID (8-4-4-4-12 hex, lowercase — GBIF matches these two keys case-sensitively, so an upper-cased rendering of a real key matches nothing) of the organization that published the dataset — the organization whose data it is, and the question a key from gbif_search_publishers is usually asking. Not the organization that serves it, which is hostingOrg and matches a different set: Butterfly Conservation (0d72dd7f-6f05-46af-85c2-8b6e77ce5534) publishes 3 datasets and hosts none, while the National Biodiversity Network (07f617d0-c688-11d8-bf62-b8a03c50a862) hosts 984 — those 3 among them — and publishes 1. Supplied together the two filters are intersected, not combined, so the same key in both fields returns only what that organization both published and serves.",
      "type": "string"
    },
    "hostingOrg": {
      "description": "UUID (8-4-4-4-12 hex, lowercase — matched case-sensitively, as publishingOrg is) of the organization whose installation serves the dataset — not the organization that published it, which is publishingOrg. Most organizations publish through an installation someone else runs, so a key from gbif_search_publishers matches nothing here for them: of the first 25 GB organizations the registry lists, all 25 host no datasets while 13 publish one or two. Supplied together the two filters are intersected, not combined.",
      "type": "string"
    },
    "limit": {
      "default": 20,
      "description": "Number of datasets to return (default 20, max 1000).",
      "type": "number",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset.",
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

gbif_get_dataset

open-world

Fetch full dataset metadata by UUID key — title, description, citation text, contacts, license, DOI, record count, numConstituents (sub-datasets), and temporal/geographic coverage. Use after gbif_search_datasets or when an occurrence record's datasetKey needs provenance detail. Contacts are capped by contactLimit (default 10); contactsTotal and contactsReturned report the full count.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_get_dataset",
    "arguments": {
      "datasetKey": "<datasetKey>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "datasetKey": {
      "type": "string",
      "description": "Dataset UUID (8-4-4-4-12 hex) from gbif_search_datasets or an occurrence record."
    },
    "contactLimit": {
      "default": 10,
      "description": "Maximum number of contacts to include (default 10, max 100). Set to 0 to omit contact detail while still reporting contactsTotal — useful when citation, license, and record count are all you need from a high-contact dataset like eBird.",
      "type": "integer",
      "minimum": 0,
      "maximum": 100
    }
  },
  "required": [
    "datasetKey",
    "contactLimit"
  ],
  "additionalProperties": false
}
view source ↗

gbif_search_publishers

open-world

Search organizations registered with GBIF by name fragment or country. Returns organization key, title, and country — sufficient to chain into gbif_search_datasets as publishingOrg for the datasets an organization published, or as hostingOrg for the ones its own installation serves, or to understand who publishes data for a region. publishingOrg is the usual chain: most organizations publish through an installation someone else runs, so hostingOrg matches nothing for them.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "gbif_search_publishers",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "q": {
      "description": "Name fragment to search for. Matches organization names. Omit the field to browse without a term — a blank or whitespace-only value is rejected rather than sent, because the registry answers either with all 3,561 registered organizations.",
      "type": "string"
    },
    "country": {
      "description": "ISO 3166-1 country code to filter organizations by country. The alpha-2 form (\"GB\") is canonical; unlike the country codes on the occurrence tools and gbif_search_datasets, this one also resolves the alpha-3 form (\"GBR\") and is case-insensitive, because the registry endpoint matches the parsed country rather than the string. A value GBIF cannot parse as a country errors rather than returning an empty list. Omit the field to search every country — an empty string is rejected rather than read as no filter, because the registry answers a blank country with all 3,561 organizations.",
      "type": "string"
    },
    "limit": {
      "default": 20,
      "description": "Number of organizations to return (default 20, max 1000).",
      "type": "number",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Pagination offset.",
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

Resources

2

Taxon record from the GBIF backbone — classification, authorship, synonymy status, vernacular name. Stable URI for caching and injection as context.

uri gbif://species/{taxonKey} mime application/json

Dataset metadata — title, description, citation, license, contacts, coverage. Stable URI for provenance context. Use the dataset UUID from gbif_search_datasets or an occurrence record's datasetKey field.

uri gbif://dataset/{datasetKey} mime application/json