# io.github.cyanheads/inaturalist-mcp-server

> Search iNaturalist sightings, identification threads, phenology, and look-alike species.

- name: io.github.cyanheads/inaturalist-mcp-server
- version: 0.2.2
- connection class: R0 (autonomous)
- trust: 94/100
- quality: 81/100 (good)
- owner: not claimed
- descriptor: https://api.protogrid.dev/v1/servers/io.github.cyanheads%2Finaturalist-mcp-server
- tools: https://api.protogrid.dev/v1/servers/io.github.cyanheads%2Finaturalist-mcp-server/tools

**An agent can connect right now, no human step.** Remote endpoint, no authentication, reachable on the last probe.

## Connection (mcpServers)

```json
{
  "mcpServers": {
    "inaturalist-mcp-server": {
      "type": "http",
      "url": "https://inaturalist.caseyjhand.com/mcp"
    }
  }
}
```


## Trust

- hygiene: 100
- liveness: 100
- freshness: 100
- provenance: 80
- drivers: +repository +namespace-matches-repo +reachable +uptime-100% +updated-12d-ago
- Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed.

## Quality 81/100 (good)

### protocol: 88 (weight 25)

- pass `protocol.modern`: supports 2026-07-28 (server/discover)
- pass `protocol.stateless`: answers without a session
- pass `protocol.transport`: streamable HTTP
- warn `protocol.list_ttl`: tools/list declares ttlMs 0, so clients re-fetch it on every use

### authorization: n/a (weight 15)

- n/a `auth.prm`, `auth.as_metadata`, `auth.cimd`: no remote uses OAuth
- n/a `auth.secret_in_url`: no templated URL

### tool hygiene: 79 (weight 30)

- pass `tools.descriptions`: every tool has a description
- pass `tools.description_length`: descriptions are concise
- pass `tools.schemas`: every tool has an object input schema
- pass `tools.annotations`: 10 of 10 tools declare readOnlyHint or destructiveHint
- fail `tools.directory_hints`: no tool declares all four of readOnlyHint, destructiveHint, idempotentHint and openWorldHint; missing: inaturalist_resolve_name (destructiveHint, idempotentHint); inaturalist_search_observations (destructiveHint, idempotentHint); inaturalist_list_reference (destructiveHint, idempotentHint); …
- warn `tools.token_cost`: about 8,749 tokens to load every tool
- pass `tools.api_dump`: tools are not a one-to-one API dump

### stability: 75 (weight 15)

- warn `stability.changes`: 7 tool changes in 30 days
- pass `stability.rug_pull`: no tool changed its meaning under the same name

### dependencies: n/a (weight 15)

- n/a `deps.known_vulns`, `deps.mcp_sdk_version`, `deps.resolvable`: deps.dev does not know @cyanheads/inaturalist-mcp-server@0.2.2, @cyanheads/inaturalist-mcp-server@0.2.2

- tools: 10, about 8,749 tokens to load them all
- tool set hash: 4707ac679083979d7692e8cfd5c91472
- tools last changed: 2026-09-24T03:39:13.436Z
- badge: [![protogrid quality](https://protogrid.dev/badge/io.github.cyanheads/inaturalist-mcp-server.svg)](https://protogrid.dev/servers/io.github.cyanheads/inaturalist-mcp-server)
- Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed.

### Tool changes

- 2026-09-24 `inaturalist_get_similar_species` changed (description, inputSchema, outputSchema)
  - before: List the taxa this one is most often misidentified as, ranked by how many times identifiers made the correction — the field-identification check before committing to a look-alike. Scope it to an area in exactly one form (place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box) to see the confusion set a specific region actually produces, or leave the area off for the global set. Resolve the organism name to a taxon id with inaturalist_resolve_name first.
  - after: List the taxa this one is most often misidentified as, ranked by how many times identifiers made the correction — the field-identification check before committing to a look-alike. Scope it to an area in exactly one form (place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box) to see the confusion set a specific region actually produces, or leave the area off for the global set. The taxon must be a genus or finer (genus, species, or below) — upstream keeps no confusion set for a family, order, or anything coarser. Resolve the organism name to a taxon id with inaturalist_resolve_name first.
- 2026-09-24 `inaturalist_get_leaderboard` changed (outputSchema)
- 2026-09-24 `inaturalist_get_histogram` changed (description, inputSchema, outputSchema)
  - before: Build a phenology histogram for a taxon in an area — which months, weeks, or years it is recorded in. The default month_of_year interval answers "when does this bloom or appear here" in twelve buckets; the absolute intervals (year, month, week, day, hour) bucket real dates and upstream applies a default start date to them. An area is given in exactly one form: place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box. Omit taxon_id to chart every taxon in the area. Defaults to research-grade, wild-only records and echoes those defaults back.
  - after: Build a phenology histogram for a taxon in an area — which months, weeks, or years it is recorded in. The default month_of_year interval answers "when does this bloom or appear here" in twelve buckets; the absolute intervals (year, month, week, day, hour) bucket real dates and upstream applies a default start date to them. An area is given in exactly one form: place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box. Omit taxon_id to chart every taxon in the area. Narrow to one life stage or reproductive state with an annotation pair (term_id and term_value_id, e.g. Life Stage = Larva, or Flowers and Fruits = Flowers), or to broad groups with iconic_taxa. Defaults to research-grade, wild-only records and echoes those defaults back.
- 2026-09-24 `inaturalist_get_species_counts` changed (description, inputSchema, outputSchema)
  - before: Rank the distinct species recorded in an area and period, most-observed first — the "what lives here" answer, without paging through individual sightings. An area is given in exactly one form: place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box. Narrow to a clade by passing taxon_id, e.g. the birds of a park. Defaults to research-grade, wild-only records and echoes those defaults back. For the most active people rather than the most recorded species, use inaturalist_get_leaderboard.
  - after: Rank the distinct species recorded in an area and period, most-observed first — the "what lives here" answer, without paging through individual sightings. An area is given in exactly one form: place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box. Narrow to a clade by passing taxon_id, e.g. the birds of a park, or to one observer or one project by user_id, user_login, or project_id. Defaults to research-grade, wild-only records and echoes those defaults back. For the most active people rather than the most recorded species, use inaturalist_get_leaderboard.
- 2026-09-24 `inaturalist_get_observation` changed (description, inputSchema, outputSchema)
  - before: Fetch up to 10 observations by id with their community identification thread — who identified what, whether each identification agrees, and the consensus taxon the community landed on. The whole batch costs one upstream request, so resolving ten ids here is far cheaper than ten separate lookups. A missing id is reported per id in unresolved rather than failing the batch; the call fails only when nothing resolved.
  - after: Fetch up to 10 observations by id with their community identification thread — who identified what, whether each identification agrees, and the consensus taxon the community landed on. The whole batch costs one upstream request, so resolving ten ids here is far cheaper than ten separate lookups. Records come back in the requested order. A missing id is reported per id in unresolved rather than failing the batch; the call fails only when nothing resolved. Long threads and long observation-field lists are cut to fit one response budget shared across the batch — up to 40 identifications, 40 comments, and 40 filled fields for a single id, 4 of each per record for ten — and every record reports the size of each array beside what it kept.
- 2026-09-24 `inaturalist_search_observations` changed (description, inputSchema, outputSchema)
  - before: Search georeferenced wildlife sightings by area, date, taxon, quality grade, annotation, and conservation status. Returns a projected record per sighting with coordinates, licence, first photo, and identification counts. An area is given in exactly one form — place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box — and defaults to research-grade, wild-only records, which are echoed back on every call. Identifications and comments are deliberately not expandable here (one thread is 28 KB); fetch them for specific records with inaturalist_get_observation. Results past 10,000 need the cursor from the previous page rather than a higher page number.
  - after: Search georeferenced wildlife sightings by area, date, taxon, quality grade, annotation, conservation status, observer, project, and licence. Returns a projected record per sighting with coordinates, licence, first photo, and identification counts. An area is given in exactly one form — place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box — and defaults to research-grade, wild-only records, which are echoed back on every call. Identifications and comments are deliberately not expandable here (one thread is 28 KB); fetch them for specific records with inaturalist_get_observation. page walks the first 10,000 results under any ordering; past 10,000, order by id descending (order_by "id", order "desc") and pass each page’s next_cursor as cursor.
- 2026-09-24 `inaturalist_list_reference` changed (outputSchema)

Full history: https://api.protogrid.dev/v1/servers/io.github.cyanheads%2Finaturalist-mcp-server/changes

## Dependencies

- npm @cyanheads/inaturalist-mcp-server@0.2.2: unknown
- npm @cyanheads/inaturalist-mcp-server@0.2.2: unknown

Dependency graphs from deps.dev; advisories from OSV.dev, including the GitHub Advisory Database and the PyPI Advisory Database; all CC BY 4.0.

## Remotes

- https://inaturalist.caseyjhand.com/mcp (streamable-http, auth none, reachable true, uptime30d 1)

## Packages

- npm @cyanheads/inaturalist-mcp-server@0.2.2
- npm @cyanheads/inaturalist-mcp-server@0.2.2

## Tools (10)

- `inaturalist_find_places`: Resolve a place name to a place id, or list the places containing a map area. Pass q to match a place-name PREFIX, or all four of nelat, nelng, swlat and swlng to list every place covering that box — exactly one of the two, never both. Each result carries the bounding box, place type, and containment chain an area search needs. Place geometry is stripped: a single nearby response carries 247 KB of boundary polygons upstream, none of which reaches the caller.
- `inaturalist_get_histogram`: Build a phenology histogram for a taxon in an area — which months, weeks, or years it is recorded in. The default month_of_year interval answers "when does this bloom or appear here" in twelve buckets; the absolute intervals (year, month, week, day, hour) bucket real dates and upstream applies a default start date to them. An area is given in exactly one form: place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box. Omit taxon_id to chart every taxon in the area. Narrow to one life stage or reproductive state with an annotation pair (term_id and term_value_id, e.g. Life Stage = Larva, or Flowers and Fruits = Flowers), or to broad groups with iconic_taxa. Defaults to research-grade, wild-only records and echoes those defaults back.
- `inaturalist_get_leaderboard`: Rank the most active observers or identifiers for an area, period, and taxon — who knows this place or this group. kind selects which: observers are ranked by how many observations they recorded, identifiers by how many identifications they made. An area is given in exactly one form: place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box. Both endpoints rank only the top 500 entries, so page multiplied by per_page must stay at or below 500 — narrow the area, period, or taxon to bring someone further down into reach. For the most-recorded species rather than the most active people, use inaturalist_get_species_counts.
- `inaturalist_get_observation`: Fetch up to 10 observations by id with their community identification thread — who identified what, whether each identification agrees, and the consensus taxon the community landed on. The whole batch costs one upstream request, so resolving ten ids here is far cheaper than ten separate lookups. Records come back in the requested order. A missing id is reported per id in unresolved rather than failing the batch; the call fails only when nothing resolved. Long threads and long observation-field lists are cut to fit one response budget shared across the batch — up to 40 identifications, 40 comments, and 40 filled fields for a single id, 4 of each per record for ten — and every record reports the size of each array beside what it kept.
- `inaturalist_get_similar_species`: List the taxa this one is most often misidentified as, ranked by how many times identifiers made the correction — the field-identification check before committing to a look-alike. Scope it to an area in exactly one form (place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box) to see the confusion set a specific region actually produces, or leave the area off for the global set. The taxon must be a genus or finer (genus, species, or below) — upstream keeps no confusion set for a family, order, or anything coarser. Resolve the organism name to a taxon id with inaturalist_resolve_name first.
- `inaturalist_get_species_counts`: Rank the distinct species recorded in an area and period, most-observed first — the "what lives here" answer, without paging through individual sightings. An area is given in exactly one form: place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box. Narrow to a clade by passing taxon_id, e.g. the birds of a park, or to one observer or one project by user_id, user_login, or project_id. Defaults to research-grade, wild-only records and echoes those defaults back. For the most active people rather than the most recorded species, use inaturalist_get_leaderboard.
- `inaturalist_get_taxon`: Fetch a taxon profile: the taxonomic path, per-authority conservation listings, the encyclopedia summary, the photo gallery, immediate children, and observation counts. Resolve a name to a taxon id with inaturalist_resolve_name first. The upstream record is 95 KB for a common species, so it is projected before anything else happens; a taxon that still overflows comes back as an outline of its sections with their byte sizes, and naming those sections in a re-call returns only those. The valid section names are summary, taxonomy, children, conservation, photos, and encyclopedia.
- `inaturalist_list_reference`: Decode the vocabularies the other iNaturalist tools take as input: annotation attributes and values, quality grades, license codes, taxonomic ranks, iconic taxa, and IUCN conservation-status codes. An unrecognized filter value is not rejected upstream — it silently returns nothing — so read the codes here before filtering. Note that the conservation codes are the normalised csi search filter; a taxon record’s own conservation_statuses[].status is authority-specific free text and reads differently. With topic controlled_terms and a taxon_id, the response also carries which annotations identifiers have actually recorded for that taxon, with counts.
- `inaturalist_resolve_name`: Resolve a common or scientific name to a taxon id, or a place, project, or observer name to its id. Returns ranked candidates carrying the identifiers every other tool takes. A miss is a result rather than a failure: found comes back false with guidance naming why. Taxon lookup matches a name PREFIX, not words inside a name, so "monarch butterfly" misses where "monarch" hits.
- `inaturalist_search_observations`: Search georeferenced wildlife sightings by area, date, taxon, quality grade, annotation, conservation status, observer, project, and licence. Returns a projected record per sighting with coordinates, licence, first photo, and identification counts. An area is given in exactly one form — place_id, the lat/lng/radius triple in kilometres, or a four-corner bounding box — and defaults to research-grade, wild-only records, which are echoed back on every call. Identifications and comments are deliberately not expandable here (one thread is 28 KB); fetch them for specific records with inaturalist_get_observation. page walks the first 10,000 results under any ordering; past 10,000, order by id descending (order_by "id", order "desc") and pass each page’s next_cursor as cursor.
