# iNaturalist MCP Documentation

## Overview

iNaturalist MCP is a **read-only** [Model Context Protocol](https://modelcontextprotocol.io/) server powered by [Laravel MCP](https://github.com/laravel/mcp). It queries the live iNaturalist API (`api.inaturalist.org/v1`) with Redis caching and a global rate budget.

Agents (Claude.ai, Claude Desktop, Claude mobile, Cursor) can:

- Resolve taxa and places to iNaturalist IDs
- Search observations (always includes `quality_grade` and `captive`)
- Fetch observation counts by quality grade
- List species counts for a place or bbox
- Fetch observation histograms

This service is **independent** of `naturalist.netviper.com`. Counts are never mixed with the naturalist mirror.

## Endpoint

**Production (port 443, after root Apache/FPM install):**

```
https://inat.netviper.com/mcp
```

**Interim (live now, no root required):**

```
https://inat.netviper.com:8443/mcp
```

Transport: HTTP POST (JSON-RPC / Streamable HTTP). Authenticated; not public.

Interim stack: systemd user services `inat-mcp-app` + `inat-mcp-caddy` (Caddy TLS on :8443 → Laravel on 127.0.0.1:8765). To move to :443:

```bash
sudo bash /var/www/inat.netviper.com/deploy/install-root.sh
# then set APP_URL=https://inat.netviper.com and disable the interim Caddy unit
systemctl --user disable --now inat-mcp-caddy.service inat-mcp-app.service
```

## Authentication

**OAuth 2.1 (recommended for Claude.ai)** via Laravel Passport + `Mcp::oauthRoutes()`. Clients discover metadata, register dynamically (DCR), and complete authorization-code + PKCE. The user must be **approved** (admins bypass approval). There is **no public registration** — create users with:

```bash
php artisan user:create you@example.com "Your Name" --password='...' --approved
# or admin:
php artisan user:create admin@example.com "Admin" --password='...' --admin
```

Leave Claude.ai **Advanced settings blank** (DCR supported).

**Personal access tokens** for Cursor / CLI:

```bash
php artisan api:generate-token {user_email} --name="MCP Agent"
```

Send on every MCP request:

```
Authorization: Bearer {your-token-here}
```

Discovery endpoints (public):

- `GET /.well-known/oauth-protected-resource/mcp`
- `GET /.well-known/oauth-authorization-server`
- `POST /oauth/register` (public clients; redirect hosts allowlisted)

Redirect allowlist includes `https://claude.ai`, `https://claude.com`, localhost, and custom schemes `claude`, `cursor`, `vscode`.

**Never** forwards the client bearer token to iNaturalist. Upstream calls are unauthenticated read-only requests with a custom User-Agent.

## Claude.ai custom connector

1. Settings → Connectors → Add custom connector
2. Paste `https://inat.netviper.com:8443/mcp` (interim) or `https://inat.netviper.com/mcp` (after Apache install)
3. Leave Advanced settings blank
4. Sign in when prompted and click **Authorize**
5. Use an approved account

The connector should sync to Claude Desktop and the iOS app automatically.

## Tools

| Tool | Purpose |
|------|---------|
| `resolve-taxon` | Name or numeric ID → `taxon_id`, rank, common name |
| `search-places` | Place name → `place_id` |
| `search-observations` | Observation records (`quality_grade`, `captive` included) |
| `observation-counts` | Totals by quality grade (research / needs_id / casual / all) |
| `species-counts` | Taxa in a `place_id` or bbox |
| `observation-histogram` | Temporal distribution |

Resource: `inat://overview`

## Response contract

Every successful tool payload includes:

- `source`: always `inaturalist-live`
- `endpoint` and `query`
- `fetched_at`
- `cache.hit` / `cache.ttl_remaining`
- `rate_limit.used_today` / `rate_limit.daily_limit`
- `truncated`
- `total_results` and `results`

Zero records (`total_results: 0`) are distinct from failures (`isError: true`).

## Rate limits and cache

- Global upstream budget: **50 req/min**, soft stop at **9,500 / 10,000 day**
- Cache TTLs: taxa/places 7d; counts/histograms/species_counts 6h; observations 1h (shorter for empty results)
- Redis cache uses `REDIS_CACHE_DB=2` (isolated from naturalist queues on DB 0)

## Regression check

New Mexico *Senticolis triaspis*: `place_id` **9**, taxon_id from `resolve-taxon` (typically **28713**).

Expected: **4** total — **3** research, **1** casual/captive.

## Ops

```bash
# Create approved user
php artisan user:create email@example.com "Name" --approved --password='...'

# Mint PAT
php artisan api:generate-token email@example.com --name=Cursor

# Deploy Apache/FPM (requires root once)
sudo bash /var/www/inat.netviper.com/deploy/install-root.sh
```

## TODO(cimd)

MCP 2026-07-28 deprecated DCR in favour of Client ID Metadata Documents. DCR remains for ≥12 months; add CIMD before that window closes.
