@cyanheads/gbif-biodiversity-mcp-server

v0.2.3 pre-1.0

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

@cyanheads/gbif-biodiversity-mcp-server
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": [
        "@cyanheads/gbif-biodiversity-mcp-server@latest"
      ]
    }
  }
}
{
  "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: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

12

gbif_match_species

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 resolves synonyms and returns the backbone taxonKey required by gbif_search_occurrences, gbif_count_occurrences, and gbif_occurrence_facets. 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.",
      "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_get_species

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

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. 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.",
      "type": "string"
    },
    "rank": {
      "description": "Filter to a specific taxonomic rank.",
      "type": "string",
      "enum": [
        "KINGDOM",
        "PHYLUM",
        "CLASS",
        "ORDER",
        "FAMILY",
        "GENUS",
        "SPECIES",
        "SUBSPECIES"
      ]
    },
    "kingdom": {
      "description": "Scope search to a kingdom (e.g., \"Animalia\", \"Plantae\").",
      "type": "string"
    },
    "family": {
      "description": "Scope search to a family name.",
      "type": "string"
    },
    "genus": {
      "description": "Scope search to a genus name.",
      "type": "string"
    },
    "isExtinct": {
      "description": "Filter to extinct (true) or extant (false) taxa.",
      "type": "boolean"
    },
    "datasetKey": {
      "description": "Scope to a specific checklist dataset UUID. Omit to search the GBIF backbone.",
      "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

Return the complete parent chain for a taxon — from kingdom (or domain) down to the taxon itself — as an ordered array. Each entry has its rank, canonical name, and taxon key. The array is returned root-first (kingdom → phylum → class → … → parent of given taxon). 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

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 2.4B+ GBIF occurrence records with Darwin Core filters. Use taxonKey from gbif_match_species for reliable results — it resolves synonyms automatically. Accepts country (ISO 3166-1 alpha-2), bounding box (decimalLatitude/decimalLongitude ranges), WKT polygon geometry, year range, month, basis of record, and coordinate filter. Pagination is capped at approximately offset+limit=100,000 — use gbif_occurrence_facets for aggregate counts across large result sets.

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.",
      "type": "string"
    },
    "country": {
      "description": "ISO 3166-1 alpha-2 country code (e.g., \"GB\", \"US\", \"DE\", \"SE\").",
      "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.",
      "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.",
      "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.",
      "type": "string"
    },
    "year": {
      "description": "Year or year range. Single year: \"2024\". Range: \"2020,2024\". Filters by observation year. Both endpoints inclusive.",
      "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. When false, include records without coordinates.",
      "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.",
      "type": "string"
    },
    "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 caps offset+limit at approximately 100,000 — use gbif_occurrence_facets for aggregate analysis beyond this.",
      "type": "number",
      "minimum": 0
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

gbif_count_occurrences

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, isGeoreferenced, datasetKey, and year.

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 country code (e.g., \"GB\", \"US\").",
      "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.",
      "type": "string"
    },
    "year": {
      "description": "Year or year range (e.g., \"2024\" or \"2020,2024\"). Both endpoints inclusive.",
      "type": "string"
    }
  },
  "additionalProperties": false
}
view source ↗

gbif_get_occurrence

Fetch a single occurrence record by its GBIF occurrence key. Returns the complete Darwin Core record — all coordinates, administrative geography (GADM), dates, collections metadata, collector identifiers, media links, and quality issue flags. Use the occurrence key from gbif_search_occurrences results to fetch full detail.

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

Aggregate occurrence counts across a dimension (COUNTRY, STATE_PROVINCE, YEAR, BASIS_OF_RECORD, DATASET_KEY, KINGDOM_KEY, etc.). Returns the top-N facet values ranked by count — 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, year, geometry, or basisOfRecord filters.

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"
      ],
      "description": "Dimension to aggregate by (e.g., COUNTRY, YEAR, BASIS_OF_RECORD, SPECIES_KEY)."
    },
    "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 country code to scope to one country.",
      "type": "string"
    },
    "year": {
      "description": "Year or year range (e.g., \"2020,2024\") to scope the aggregation. Both endpoints inclusive.",
      "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.",
      "type": "string"
    },
    "facetLimit": {
      "default": 10,
      "description": "Maximum number of facet values to return (default 10, max 100).",
      "type": "number",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": [
    "facet",
    "facetLimit"
  ],
  "additionalProperties": false
}
view source ↗

gbif_search_datasets

Search GBIF datasets by keyword, type, country, or publishing organization. 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.",
      "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 country code of the publishing organization.",
      "type": "string"
    },
    "hostingOrg": {
      "description": "UUID of the hosting organization. From gbif_search_publishers results.",
      "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

Fetch full dataset metadata by UUID key — title, description, citation text, contacts, license, DOI, numConstituents (sub-datasets), and temporal/geographic coverage. Use after gbif_search_datasets or when an occurrence record's datasetKey needs provenance detail.

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 from gbif_search_datasets or an occurrence record."
    }
  },
  "required": [
    "datasetKey"
  ],
  "additionalProperties": false
}
view source ↗

gbif_search_publishers

Search organizations registered with GBIF by name fragment or country. Returns organization key, title, and country — sufficient to chain into gbif_search_datasets with hostingOrg, or to understand who publishes data for a region.

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.",
      "type": "string"
    },
    "country": {
      "description": "ISO 3166-1 alpha-2 country code to filter organizations by country.",
      "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