Connects Claude to the Global Biodiversity Information Facility's 2.4 billion occurrence records and backbone taxonomy. Ships with 12 tools covering species name matching, taxonomic lookups, occurrence search with Darwin Core filters (country, bounding box, year, basis of record), dataset metadata, and publisher search. Match a species name to get a taxonKey, then query occurrences by geography or time. Fetch full classification chains, browse child taxa, or aggregate counts by facets like country or year. Useful for ecological research, biodiversity analysis, and any workflow that needs authoritative taxonomic resolution or specimen data. Available over stdio or streamable HTTP, with a public hosted endpoint at gbif.caseyjhand.com.
Search GBIF species taxonomy, occurrence records, datasets, and publishers via MCP. STDIO or Streamable HTTP.
12 tools for working with GBIF species taxonomy, occurrence records, datasets, and publishers:
| Tool | Description |
|---|---|
gbif_match_species | Match a species name against the GBIF backbone taxonomy — returns taxonKey, confidence score, and full classification |
gbif_get_species | Fetch a single backbone taxon by key — full classification, authorship, synonymy, vernacular name, descendant count |
gbif_search_species | Search or browse the GBIF backbone taxonomy by name fragment, rank, kingdom, family, or genus |
gbif_get_species_classification | Return the complete parent chain for a taxon — root-first ordered array from kingdom to immediate parent |
gbif_get_species_children | List direct children of a backbone taxon — genera within a family, species within a genus |
gbif_search_occurrences | Search 2.4B+ GBIF occurrence records with Darwin Core filters — country, bounding box, WKT geometry, year, month, basis of record |
gbif_count_occurrences | Count occurrences matching a filter without fetching records — fast single-number response |
gbif_get_occurrence | Fetch a single occurrence record by key — full Darwin Core record with GADM geography, media, and quality flags |
gbif_occurrence_facets | Aggregate occurrence counts by a dimension — country, year, basis of record, dataset, kingdom, and more |
gbif_search_datasets | Search GBIF datasets by keyword, type, country, or publishing organization |
gbif_get_dataset | Fetch full dataset metadata by UUID — title, description, citation, contacts, license, DOI, coverage |
gbif_search_publishers | Search GBIF-registered publishing organizations by name fragment or country |
gbif_match_speciesMatch a scientific or common name against the GBIF backbone taxonomy.
strict: true for exact-only matchingtaxonKey — the backbone key required by gbif_search_occurrences, gbif_count_occurrences, and gbif_occurrence_facetsmatchType NONE indicates no usable match — try removing strict mode or broadening the namegbif_get_speciesFetch a complete taxon record by GBIF backbone key.
taxonomicStatus: ACCEPTED, SYNONYM, DOUBTFUL — when SYNONYM, acceptedKey and accepted identify the current namenumDescendants and numOccurrences for scope at a glanceextinct field present only when explicitly flagged — not false on unlabeled taxapublishedIn carries the original description citation when availablegbif_search_speciesSearch or browse the GBIF backbone taxonomy.
isExtinct filter for extinct vs. extant taxadatasetKey (omit for the GBIF backbone)gbif_get_species_classificationReturn the full parent chain for a taxon as an ordered array.
gbif_get_species_childrenList direct children of a backbone taxon.
gbif_search_occurrencesSearch 2.4B+ GBIF occurrence records with full Darwin Core filtering.
taxonKey from gbif_match_species for reliable results — resolves synonyms automatically; scientificName filter does notcountry (ISO 3166-1 alpha-2), bounding box (decimalLatitude/decimalLongitude ranges as "min,max"), or WKT polygon (geometry)year as single year or range, month (1–12) for seasonal queriesbasisOfRecord enum: HUMAN_OBSERVATION, PRESERVED_SPECIMEN, MACHINE_OBSERVATION, and morehasCoordinate to require or exclude georeferenced recordsgbif_occurrence_facets for aggregate analysis beyond thisgbif_count_occurrencesCount occurrences matching a filter without fetching any records.
/occurrence/count endpoint — fast single-number responsetaxonKey, country, isGeoreferenced, datasetKey, yeargbif_get_occurrenceFetch a single occurrence record by GBIF occurrence key.
gbif_occurrence_facetsAggregate occurrence counts across a dimension.
COUNTRY, YEAR, BASIS_OF_RECORD, DATASET_KEY, KINGDOM_KEY, PHYLUM_KEY, CLASS_KEY, ORDER_KEY, FAMILY_KEY, GENUS_KEY, SPECIES_KEY, PUBLISHING_COUNTRY, MONTHtaxonKey, country, year, geometry, or basisOfRecord filtersgbif_search_datasetsSearch GBIF datasets by keyword, type, country, or publishing organization.
OCCURRENCE, CHECKLIST, METADATA, SAMPLING_EVENT), publishing country, hosting organization UUIDhostingOrg from gbif_search_publishers to scope to datasets from one organizationgbif_get_datasetFetch full dataset metadata by UUID.
numConstituents for aggregate datasets (e.g. iNaturalist, eBird)gbif_search_datasets or when an occurrence record's datasetKey needs provenance detailgbif_search_publishersSearch organizations registered with GBIF.
gbif_search_datasets with hostingOrg| Type | Name | Description |
|---|---|---|
| Resource | gbif://species/{taxonKey} | Taxon record from the GBIF backbone — classification, authorship, synonymy status, vernacular name |
| Resource | gbif://dataset/{datasetKey} | Dataset metadata — title, description, citation, license, contacts, coverage |
Built on @cyanheads/mcp-ts-core:
none, jwt, oauth)in-memory, filesystem, Supabase, Cloudflare KV/R2/D1GBIF-specific:
gbif_match_species as the entry point — resolves synonyms to backbone taxon keys used throughoutpaginationNote — redirects to facet aggregation before hitting the ~100,000 row limitAgent-friendly output:
gbif_match_species is the mandatory first step — all downstream tools document which key they expectwhen documentation per toolAdd the following to your MCP client configuration file.
{
"mcpServers": {
"gbif-biodiversity-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/gbif-biodiversity-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"gbif-biodiversity-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/gbif-biodiversity-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"gbif-biodiversity-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/gbif-biodiversity-mcp-server:latest"]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
git clone https://github.com/cyanheads/gbif-biodiversity-mcp-server.git
cd gbif-biodiversity-mcp-server
bun install
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
| Variable | Description | Default |
|---|---|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http | stdio |
MCP_HTTP_PORT | HTTP server port | 3010 |
MCP_HTTP_ENDPOINT_PATH | HTTP endpoint path where the MCP server is mounted | /mcp |
MCP_PUBLIC_URL | Public origin override for TLS-terminating reverse-proxy deployments | none |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.) | info |
MCP_GC_PRESSURE_INTERVAL_MS | Opt-in Bun-only forced-GC pressure loop (ms). Try 60000 if RSS grows under sustained HTTP load. | 0 (disabled) |
LOGS_DIR | Directory for log files (Node.js only) | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1 | in-memory |
GBIF_BASE_URL | GBIF API base URL override | https://api.gbif.org/v1 |
GBIF_REQUEST_TIMEOUT_MS | HTTP request timeout in milliseconds | 10000 |
OTEL_ENABLED | Enable OpenTelemetry | false |
Build and run the production version:
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more
bun run test # Runs the test suite
| Directory | Purpose |
|---|---|
src/mcp-server/tools | Tool definitions (*.tool.ts). Twelve tools across species taxonomy, occurrences, datasets, and publishers. |
src/mcp-server/resources | Resource definitions. Species and dataset stable-URI resources. |
src/services/gbif | GBIF REST API service layer — client, request handling, type definitions. |
src/config | Server-specific environment variable parsing and validation with Zod. |
tests/ | Unit and integration tests, mirroring the src/ structure. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for logging, ctx.state for storagecreateApp() arraysIssues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
GBIF_API_KEYGBIF API key for higher rate limits. Sent as HTTP Basic Auth username.
MCP_LOG_LEVELdefault: infoSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
MCP_HTTP_HOSTdefault: 127.0.0.1The hostname for the HTTP server.
MCP_HTTP_PORTdefault: 3010The port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHdefault: /mcpThe endpoint path for the MCP server.
MCP_AUTH_MODEdefault: noneAuthentication mode to use: 'none', 'jwt', or 'oauth'.
com.mcparmory/google-search
io.github.pipeworx-io/brave-search
marcopesani/mcp-server-serper
brave/brave-search-mcp-server
com.mcparmory/google-search-console
acamolese/google-search-console-mcp