Skip to main content
Glama

gbif-biodiversity-mcp-server

Search Occurrences

gbif_search_occurrences
Read-onlyIdempotent

Search 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, gadmGid (a GADM administrative-area code GBIF derives from coordinates), bounding box (decimalLatitude/decimalLongitude ranges), WKT polygon geometry, year range, month, basis of record, coordinate filter, dataset key, catalogNumber, institutionCode, recordedBy (collector), and mediaType. 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. BASIS_OF_RECORD and PUBLISHING_COUNTRY are gap-free second cuts for a dataset bucket still over the cap; YEAR, MONTH, STATE_PROVINCE, and SPECIES_KEY lose records lacking that dimension. Repeat every biological filter on the facet call and each partition search, including the same occurrenceStatus, and compare sub-split sums against the bucket total. Bulk retrieval is outside this server: use the asynchronous GBIF Download API with a GBIF.org account or the monthly GBIF Parquet snapshot on AWS Open Data.

Input Schema

NameRequiredDescriptionDefault
yearNoYear 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.
limitNoNumber of records to return (default 20, max 300).
monthNoCalendar month (1–12). Useful for seasonal distribution queries.
offsetNoPagination 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.
countryNoISO 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.
gadmGidNoGADM administrative-area GID at any level: country (ESP), state or province (ESP.1_1), county or district (ESP.1.5_1), or municipality (ESP.1.6.3_1). GBIF derives a record's GIDs from its coordinates, so records without coordinates match none, and the unit can differ from the verbatim stateProvince the record carries. Matched exactly and case-sensitively, and a GID holds only uppercase letters, digits, dots, and underscores — any other character is rejected. Take one from a GADM_LEVEL_0_GID, GADM_LEVEL_1_GID, GADM_LEVEL_2_GID, or GADM_LEVEL_3_GID facet on gbif_occurrence_facets, or from the gadm field of gbif_get_occurrence; a well-formed GID GBIF does not hold matches zero records rather than erroring. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.
geometryNoWKT polygon for geographic filtering (e.g., POLYGON((8 47, 9 47, 9 48, 8 48, 8 47))). Coordinates are longitude latitude. All supplied spatial filters intersect: decimalLatitude and decimalLongitude remain active and restrict results to the overlap. 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.
taxonKeyNoGBIF 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.).
mediaTypeNoOnly records carrying at least one media item of this type: StillImage (photographs, scans), MovingImage (video), Sound (audio), or InteractiveResource. Records without media are excluded when this is set.
datasetKeyNoRestrict 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.
recordedByNoCollector or observer, matched against each name in the record's recordedBy list (GBIF splits it on "|") exactly and case-sensitively: "Charles Darwin" and "charles darwin" differ, and one person written two ways ("Charles Darwin", "Darwin, C.") is two values. Take a value from a RECORDED_BY facet on gbif_occurrence_facets scoped the same way and pass it back unchanged; an unmatched value matches zero records rather than erroring. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.
isInClusterNoFilter 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.
basisOfRecordNoFilter by how the occurrence was recorded. HUMAN_OBSERVATION covers citizen science. PRESERVED_SPECIMEN covers natural history collections.
catalogNumberNoCatalog number a collection assigned to the specimen or record (e.g. "L.1592880"), matched as the whole value, case-insensitively. Not unique across collections — one number can name records in several datasets, so pair it with institutionCode or datasetKey to single one out. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.
hasCoordinateNoWhen 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.
stateProvinceNoState, 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.
scientificNameNoScientific 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.
decimalLatitudeNoLatitude 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.
institutionCodeNoCode of the institution holding the record (e.g. "K", "CLO"), matched as the whole value, case-insensitively — part of a code or of a multi-word code matches nothing. An INSTITUTION_CODE facet on gbif_occurrence_facets lists the codes in a scope, lowercased, and this filter takes them unchanged. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.
decimalLongitudeNoLongitude 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.
occurrenceStatusNoPresence/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.PRESENT
publishingCountryNoISO 3166-1 alpha-2 code, uppercase, of the organization that published the record — distinct from where the occurrence was observed, which is country. 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.
iucnRedListCategoryNoRestrict 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.
coordinateUncertaintyInMetersNoFilter 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.

Output Schema

NameRequiredDescriptionDefault
errorNoPresent when the call failed. Absent on success.
limitNoRecords returned in this page.
scopeNoEffective supplied biological filters, with occurrenceStatus always included; excludes pagination and facet controls.
noticeNoGuidance when results are empty, paging overshot, a read limit was clamped, the match is larger than the pagination cap can reach, or a presence/absence filter narrowed the result. Absent only when none applies.
offsetNoCurrent pagination offset.
totalCountNoTotal matching occurrences before pagination.
occurrencesNoOccurrence records matching the filters.
endOfRecordsNoTrue when there are no more results after this page.
occurrenceStatusNoThe presence/absence filter applied upstream — PRESENT, ABSENT, or ANY when no filter was sent. Says what totalCount and the returned records cover.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed16 schema fields changed
    • addedInput schema / properties / catalogNumber
      Added value: +{
      +  "description": "Catalog number a collection assigned to the specimen or record (e.g. \"L.1592880\"), matched as the whole value, case-insensitively. Not unique across collections — one number can name records in several datasets, so pair it with institutionCode or datasetKey to single one out. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.",
      +  "type": "string"
      +}
    • addedInput schema / properties / gadmGid
      Added value: +{
      +  "description": "GADM administrative-area GID at any level: country (ESP), state or province (ESP.1_1), county or district (ESP.1.5_1), or municipality (ESP.1.6.3_1). GBIF derives a record's GIDs from its coordinates, so records without coordinates match none, and the unit can differ from the verbatim stateProvince the record carries. Matched exactly and case-sensitively, and a GID holds only uppercase letters, digits, dots, and underscores — any other character is rejected. Take one from a GADM_LEVEL_0_GID, GADM_LEVEL_1_GID, GADM_LEVEL_2_GID, or GADM_LEVEL_3_GID facet on gbif_occurrence_facets, or from the gadm field of gbif_get_occurrence; a well-formed GID GBIF does not hold matches zero records rather than erroring. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.",
      +  "type": "string"
      +}
    • addedInput schema / properties / institutionCode
      Added value: +{
      +  "description": "Code of the institution holding the record (e.g. \"K\", \"CLO\"), matched as the whole value, case-insensitively — part of a code or of a multi-word code matches nothing. An INSTITUTION_CODE facet on gbif_occurrence_facets lists the codes in a scope, lowercased, and this filter takes them unchanged. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.",
      +  "type": "string"
      +}
    • addedInput schema / properties / limit / maximum
      Added value: +9007199254740991
    • changedInput schema / properties / limit / type
      Previous value: -"number"New value: +"integer"
    • addedInput schema / properties / mediaType
      Added value: +{
      +  "description": "Only records carrying at least one media item of this type: StillImage (photographs, scans), MovingImage (video), Sound (audio), or InteractiveResource. Records without media are excluded when this is set.",
      +  "enum": [
      +    "StillImage",
      +    "MovingImage",
      +    "Sound",
      +    "InteractiveResource"
      +  ],
      +  "type": "string"
      +}
    • changedInput schema / properties / month / type
      Previous value: -"number"New value: +"integer"
    • addedInput schema / properties / offset / maximum
      Added value: +9007199254740991
    • changedInput schema / properties / offset / type
      Previous value: -"number"New value: +"integer"
    • addedInput schema / properties / recordedBy
      Added value: +{
      +  "description": "Collector or observer, matched against each name in the record's recordedBy list (GBIF splits it on \"|\") exactly and case-sensitively: \"Charles Darwin\" and \"charles darwin\" differ, and one person written two ways (\"Charles Darwin\", \"Darwin, C.\") is two values. Take a value from a RECORDED_BY facet on gbif_occurrence_facets scoped the same way and pass it back unchanged; an unmatched value matches zero records rather than erroring. Omit the field to leave it unfiltered — a blank or whitespace-only value is rejected rather than dropped, because GBIF answers one with the unfiltered scope.",
      +  "type": "string"
      +}
    • addedInput schema / properties / taxonKey / maximum
      Added value: +9007199254740991
    • addedInput schema / properties / taxonKey / minimum
      Added value: +0
    • changedInput schema / properties / taxonKey / type
      Previous value: -"number"New value: +"integer"
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `pagination_cap_exceeded`: offset + limit exceeds 100,001, the deepest page GBIF serves. `invalid_filter`: A filter value is unusable — any filter supplied blank or whitespace-only, a datasetKey that is not an 8-4-4-4-12 hex UUID, a two-letter country or publishingCountry code GBIF does not know, or a WKT geometry or range GBIF rejects. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `pagination_cap_exceeded`: offset + limit exceeds 100,001, the deepest page GBIF serves. `invalid_filter`: A filter value is unusable — a string filter other than country and publishingCountry supplied blank or whitespace-only (a blank country or publishingCountry fails earlier, at its uppercase pattern, as -32602 invalid_arguments), a datasetKey that is not an 8-4-4-4-12 hex UUID, a gadmGid holding a character other than an uppercase letter, digit, dot, or underscore, a two-letter country or publishingCountry code GBIF does not know, or a WKT geometry or range GBIF rejects. Other values are possible when a failure originates below the handler."
    • addedOutput schema / properties / occurrences / items / properties / catalogNumber
      Added value: +{
      +  "description": "Catalog number the holding collection assigned — the value the catalogNumber filter matches. May be absent.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / occurrences / items / properties / institutionCode
      Added value: +{
      +  "description": "Code of the institution holding the record — the value the institutionCode filter matches. May be absent.",
      +  "type": "string"
      +}
  2. Changed3 schema fields changed
    • removedInput schema / properties / limit / maximum
      Removed value: -300
    • changedInput schema / properties / publishingCountry / description
      Previous value: -"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."New value: +"ISO 3166-1 alpha-2 code, uppercase, of the organization that published the record — distinct from where the occurrence was observed, which is country. 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."
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when results are empty, paging overshot, the match is larger than the pagination cap can reach, or a presence/absence filter narrowed the result. Absent only when none applies."New value: +"Guidance when results are empty, paging overshot, a read limit was clamped, the match is larger than the pagination cap can reach, or a presence/absence filter narrowed the result. Absent only when none applies."
  3. Changed4 schema fields changed
    • changedInput schema / properties / basisOfRecord / enum
      Previous value: -[
      -  "HUMAN_OBSERVATION",
      -  "MACHINE_OBSERVATION",
      -  "PRESERVED_SPECIMEN",
      -  "LIVING_SPECIMEN",
      -  "MATERIAL_SAMPLE",
      -  "MATERIAL_CITATION",
      -  "OCCURRENCE",
      -  "LITERATURE"
      -]New value: +[
      +  "HUMAN_OBSERVATION",
      +  "MACHINE_OBSERVATION",
      +  "PRESERVED_SPECIMEN",
      +  "LIVING_SPECIMEN",
      +  "MATERIAL_SAMPLE",
      +  "MATERIAL_CITATION",
      +  "OCCURRENCE",
      +  "LITERATURE",
      +  "FOSSIL_SPECIMEN",
      +  "OBSERVATION",
      +  "UNKNOWN"
      +]
    • changedInput schema / properties / geometry / description
      Previous value: -"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."New value: +"WKT polygon for geographic filtering (e.g., POLYGON((8 47, 9 47, 9 48, 8 48, 8 47))). Coordinates are longitude latitude. All supplied spatial filters intersect: decimalLatitude and decimalLongitude remain active and restrict results to the overlap. 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."
    • changedOutput schema / anyOf
      Previous value: -[
      -  {
      -    "not": {
      -      "required": [
      -        "error"
      -      ]
      -    },
      -    "required": [
      -      "occurrences",
      -      "totalCount",
      -      "offset",
      -      "limit",
      -      "endOfRecords",
      -      "occurrenceStatus"
      -    ]
      -  },
      -  {
      -    "required": [
      -      "error"
      -    ]
      -  }
      -]New value: +[
      +  {
      +    "not": {
      +      "required": [
      +        "error"
      +      ]
      +    },
      +    "required": [
      +      "occurrences",
      +      "scope",
      +      "totalCount",
      +      "offset",
      +      "limit",
      +      "endOfRecords",
      +      "occurrenceStatus"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / scope
      Added value: +{
      +  "additionalProperties": {
      +    "type": [
      +      "string",
      +      "number",
      +      "boolean"
      +    ]
      +  },
      +  "description": "Effective supplied biological filters, with occurrenceStatus always included; excludes pagination and facet controls.",
      +  "propertyNames": {
      +    "type": "string"
      +  },
      +  "type": "object"
      +}
  4. Changed6 schema fields changed
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedInput schema / additionalProperties
      Added value: +false
    • changedOutput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedOutput schema / anyOf
      Added value: +[
      +  {
      +    "not": {
      +      "required": [
      +        "error"
      +      ]
      +    },
      +    "required": [
      +      "occurrences",
      +      "totalCount",
      +      "offset",
      +      "limit",
      +      "endOfRecords",
      +      "occurrenceStatus"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / error
      Added value: +{
      +  "additionalProperties": {},
      +  "description": "Present when the call failed. Absent on success.",
      +  "properties": {
      +    "code": {
      +      "description": "JSON-RPC error code for this failure.",
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "data": {
      +      "additionalProperties": {},
      +      "properties": {
      +        "reason": {
      +          "description": "Machine-readable failure mode. Declared by this tool: `pagination_cap_exceeded`: offset + limit exceeds 100,001, the deepest page GBIF serves. `invalid_filter`: A filter value is unusable — any filter supplied blank or whitespace-only, a datasetKey that is not an 8-4-4-4-12 hex UUID, a two-letter country or publishingCountry code GBIF does not know, or a WKT geometry or range GBIF rejects. Other values are possible when a failure originates below the handler.",
      +          "examples": [
      +            "pagination_cap_exceeded",
      +            "invalid_filter"
      +          ],
      +          "type": "string"
      +        },
      +        "recovery": {
      +          "additionalProperties": {},
      +          "description": "Actionable next step for the caller.",
      +          "properties": {
      +            "hint": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "hint"
      +          ],
      +          "type": "object"
      +        },
      +        "retryable": {
      +          "description": "Whether retrying may succeed.",
      +          "type": "boolean"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "message": {
      +      "description": "Human-readable description of what went wrong.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "code",
      +    "message"
      +  ],
      +  "type": "object"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "occurrences",
      -  "totalCount",
      -  "offset",
      -  "limit",
      -  "endOfRecords",
      -  "occurrenceStatus"
      -]
  5. Changed8 schema fields changed
    • changedInput schema / properties / coordinateUncertaintyInMeters / description
      Previous value: -"Filter by coordinate uncertainty radius in meters. Range format: \"min,max\" (e.g., \"0,1000\" for sub-kilometer precision). Both endpoints inclusive."New value: +"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."
    • changedInput schema / properties / datasetKey / description
      Previous value: -"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."New value: +"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."
    • changedInput schema / properties / decimalLatitude / description
      Previous value: -"Latitude range as \"min,max\" (e.g., \"47.0,48.5\"). Decimal degrees, WGS84. Combine with decimalLongitude for a bounding box."New value: +"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."
    • changedInput schema / properties / decimalLongitude / description
      Previous value: -"Longitude range as \"min,max\" (e.g., \"8.0,9.5\"). Decimal degrees, WGS84. Combine with decimalLatitude for a bounding box."New value: +"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."
    • changedInput schema / properties / geometry / description
      Previous value: -"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."New value: +"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."
    • changedInput schema / properties / scientificName / description
      Previous value: -"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."New value: +"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."
    • changedInput schema / properties / stateProvince / description
      Previous value: -"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. Records carrying no stateProvince match no value, so this cannot partition a scope."New value: +"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."
    • changedInput schema / properties / year / description
      Previous value: -"Year or year range. Single year: \"2024\". Range: \"2020,2024\". Filters by observation year. Both endpoints inclusive."New value: +"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."
  6. Changed2 schema fields changed
    • changedInput schema / properties / country / description
      Previous value: -"ISO 3166-1 alpha-2 code 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."New value: +"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."
    • addedInput schema / properties / country / pattern
      Added value: +"^[A-Z]{2}$"
  7. Changed5 schema fields changed
    • changedInput schema / properties / country / description
      Previous value: -"ISO 3166-1 alpha-2 country code (e.g., \"GB\", \"US\", \"DE\", \"SE\")."New value: +"ISO 3166-1 alpha-2 code 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."
    • changedInput schema / properties / offset / description
      Previous value: -"Pagination offset. GBIF serves offset+limit up to 100,001 and rejects anything past it — use gbif_occurrence_facets for aggregate analysis beyond that."New value: +"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."
    • addedInput schema / properties / publishingCountry
      Added value: +{
      +  "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.",
      +  "pattern": "^[A-Z]{2}$",
      +  "type": "string"
      +}
    • addedInput schema / properties / stateProvince
      Added value: +{
      +  "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. Records carrying no stateProvince match no value, so this cannot partition a scope.",
      +  "type": "string"
      +}
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when results are empty, paging overshot, or a presence/absence filter narrowed the result. Absent only when none applies."New value: +"Guidance when results are empty, paging overshot, the match is larger than the pagination cap can reach, or a presence/absence filter narrowed the result. Absent only when none applies."
  8. Changed10 schema fields changed
    • addedInput schema / properties / iucnRedListCategory
      Added value: +{
      +  "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.",
      +  "enum": [
      +    "CR",
      +    "EN",
      +    "VU",
      +    "NT",
      +    "LC",
      +    "DD",
      +    "EX",
      +    "EW",
      +    "CD"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / occurrenceStatus
      Added value: +{
      +  "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.",
      +  "enum": [
      +    "PRESENT",
      +    "ABSENT",
      +    "ANY"
      +  ],
      +  "type": "string"
      +}
    • changedInput schema / properties / scientificName / description
      Previous value: -"Scientific name filter. Less precise than taxonKey — does not match synonyms. Use taxonKey from gbif_match_species for reliable results."New value: +"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."
    • changedOutput schema / properties / notice / description
      Previous value: -"Guidance when results are empty or paging overshot. Absent on successful result pages."New value: +"Guidance when results are empty, paging overshot, or a presence/absence filter narrowed the result. Absent only when none applies."
    • addedOutput schema / properties / occurrenceStatus
      Added value: +{
      +  "description": "The presence/absence filter applied upstream — PRESENT, ABSENT, or ANY when no filter was sent. Says what totalCount and the returned records cover.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / occurrences / items / properties / eventTime
      Added value: +{
      +  "description": "Time of day of the observation, with seconds and UTC offset (e.g. 20:15:00+01:00) — the offset eventDate omits when it carries a local time. May be absent.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / occurrences / items / properties / iucnRedListCategory
      Added value: +{
      +  "description": "IUCN Red List category of the taxon — CR, EN, VU, NT, LC, DD, EX, EW, or CD. May be absent.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / occurrences / items / properties / occurrenceStatus
      Added value: +{
      +  "description": "PRESENT when the record asserts the taxon was there, ABSENT when it documents a survey that looked and did not find it. An ABSENT record is not a sighting. May be absent.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / occurrences / items / properties / taxonomicStatus
      Added value: +{
      +  "description": "Status of the identification carried on this record — ACCEPTED, PROVISIONALLY_ACCEPTED, SYNONYM, DOUBTFUL, and so on. Says whether the occurrence was filed under an accepted name or a synonym. May be absent.",
      +  "type": "string"
      +}
    • changedOutput schema / required
      Previous value: -[
      -  "occurrences",
      -  "totalCount",
      -  "offset",
      -  "limit",
      -  "endOfRecords"
      -]New value: +[
      +  "occurrences",
      +  "totalCount",
      +  "offset",
      +  "limit",
      +  "endOfRecords",
      +  "occurrenceStatus"
      +]
  9. Changed2 schema fields changed
    • changedInput schema / properties / datasetKey / description
      Previous value: -"Restrict results to a single dataset by its GBIF dataset UUID. Obtain one from gbif_search_datasets, gbif_get_dataset, a DATASET_KEY facet (gbif_occurrence_facets), or the datasetKey field on an occurrence record."New value: +"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."
    • changedInput schema / properties / offset / description
      Previous value: -"Pagination offset. GBIF caps offset+limit at approximately 100,000 — use gbif_occurrence_facets for aggregate analysis beyond this."New value: +"Pagination offset. GBIF serves offset+limit up to 100,001 and rejects anything past it — use gbif_occurrence_facets for aggregate analysis beyond that."
  10. Changed2 schema fields changed
    • addedInput schema / properties / datasetKey
      Added value: +{
      +  "description": "Restrict results to a single dataset by its GBIF dataset UUID. Obtain one from gbif_search_datasets, gbif_get_dataset, a DATASET_KEY facet (gbif_occurrence_facets), or the datasetKey field on an occurrence record.",
      +  "type": "string"
      +}
    • changedInput schema / properties / hasCoordinate / description
      Previous value: -"When true, return only georeferenced records. When false, include records without coordinates."New value: +"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."
  11. Changed1 schema field changed
    • addedOutput schema / properties / notice
      Added value: +{
      +  "description": "Guidance when results are empty or paging overshot. Absent on successful result pages.",
      +  "type": "string"
      +}
  12. Changed4 schema fields changed
    • addedInput schema / properties / coordinateUncertaintyInMeters
      Added value: +{
      +  "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"
      +}
    • changedInput schema / properties / isInCluster / description
      Previous value: -"Filter to records flagged as likely duplicates (true) or exclude them (false). Omit to include all."New value: +"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."
    • changedInput schema / properties / taxonKey / description
      Previous value: -"GBIF backbone taxon key from gbif_match_species. Preferred over scientificName — matches all synonyms automatically."New value: +"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.)."
    • changedInput schema / properties / year / description
      Previous value: -"Year or year range. Single year: \"2024\". Range: \"2020,2024\". Filters by observation year."New value: +"Year or year range. Single year: \"2024\". Range: \"2020,2024\". Filters by observation year. Both endpoints inclusive."
  13. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and openWorldHint, but the description adds operational behavior that matters: default exclusion of absence records, the 100,001 offset+limit cap with no cursor/scroll, and the required partitioning workflow via datasetKey facets. It also explains that filters must be repeated across partition searches and that sums should be reconciled.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose and then moves through filters, default behavior, pagination limits, partitioning strategy, and bulk alternatives. It is dense but appropriately sized for a 24-parameter tool. The long filter enumeration borders on redundancy with the schema, and bullet-like structure could improve scanability, keeping it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex 24-parameter search tool with an output schema, the description covers the necessary operational context: what it returns by default, how absence records behave, the hard pagination limit, how to partition large result sets, and when to leave the server for bulk downloads. Nothing critical for correct invocation appears missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already carries most parameter detail. The description still adds value by surfacing high-impact distinctions in the main text, notably country versus publishingCountry, taxonKey versus scientificName, and the default occurrenceStatus behavior. It does not add much syntax beyond the schema, but it organizes the key semantic traps for the agent.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: "Search GBIF occurrence records with Darwin Core filters." It names relevant siblings (gbif_match_species, gbif_occurrence_facets) and clearly scopes the tool as occurrence retrieval rather than species matching, dataset lookup, or counting. An agent can distinguish its purpose without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit routing guidance: use taxonKey from gbif_match_species for synonym-resolving searches, use gbif_occurrence_facets to partition result sets beyond the pagination cap, and use the external GBIF Download API or Parquet snapshot for bulk retrieval. It also states when occurrenceStatus defaults to PRESENT versus when ANY or ABSENT is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.