Skip to main content
Glama
wyattlindsey

plant-intel-mcp

by wyattlindsey
README.md
# plant-intel

An MCP server that gives coding agents structured plant data for garden
planning: species care profiles, companion checks that explain themselves, and
frost-date planting windows derived from ten years of observed weather.

**Live demo: [rootwise](https://rootwise-deploy.vercel.app)** — an AI gardening
advisor built on this server, showing every tool call and source it uses
([source](https://github.com/wyattlindsey/rootwise)).

```
companion_check("tomato", "potato")
  -> bad: both are Solanaceae. Same-family crops share soilborne diseases and
     compete for the same rotation slot, so plant them apart and rotate the bed.
```

## Why an MCP server for this

A model asked about garden planning will happily produce plausible spacing
numbers and companion-planting advice. Most of it is folklore, and none of it
is traceable. Three things make this worth a server rather than a prompt:

1. **The data is real and cited.** Every record says where it came from and
   under what licence.
2. **The gaps are visible.** When an upstream tier withholds a field, the
   response says so. When no source publishes a number, it stays null with an
   explanation instead of being filled in.
3. **Verdicts carry mechanisms.** `companion_check` never says "these are good
   together" because a gardening blog said so. It says *why*, from evidence in
   the care data, and admits when it has no evidence at all.

## Tools

| Tool | What it does |
| --- | --- |
| `search_plants` | Search the species catalogue by name; returns candidates with ids. |
| `plant_details` | Care profile: sun, water, hardiness zones, mature height, edibility, toxicity to people and pets, known pests. |
| `companion_check` | Whether two plants belong near each other, with every mechanism behind the verdict. |
| `planting_window` | Frost envelope, season length, and USDA zone fit for a site, from observed daily minima. |
| `identify_plant` | *Experimental, off by default.* Identify a plant from image URLs. Requires Perenual beta access. |

Every tool returns compact JSON. No HTML, no page dumps.

### `companion_check` returns mechanisms, not folklore

No API in this server's source roster publishes plant antagonists —
Permapeople lists "companion to" links only — so a good/bad/neutral answer
cannot simply be looked up. Rather than restate garden lore, the negative case
is derived from evidence already in the care data:

| Mechanism | Verdict | Reasoning |
| --- | --- | --- |
| `shared-family` | bad | Same botanical family: shared soilborne disease, same rotation slot. |
| `shared-pest` | bad | Overlapping pest susceptibility: co-planting concentrates the pest. |
| `listed-companion` | good | Documented in Permapeople (requires that source to be configured). |

A risk mechanism outranks a positive listing, but **every** matched reason is
returned, including the overridden one, so a mixed pair stays legible. A
`neutral` verdict states plainly that it is an absence of evidence, not
evidence of compatibility.

### `planting_window` will not guess crop timing

Give it coordinates and it derives, from ten whole years of Open-Meteo daily
minima, the median last spring frost, first autumn frost, season length, and
the site's USDA zone — then checks that against the plant's published hardiness
range. Verified against published normals:

| Site | Last frost | First frost | Season | Derived zone |
| --- | --- | --- | --- | --- |
| Minneapolis, MN | 04-28 | 10-28 | 183 days | 4b |
| Portland, OR | 03-17 | 11-25 | 252 days | 8b |
| Honolulu, HI | — | — | — | 13a (frost free) |
| Sydney, AU | — | — | — | 10b (frost free) |

What it will *not* do is invent the crop half. No source in this roster
publishes days-to-maturity or a frost-hardiness class, so transplant and sow-by
dates appear only when you supply `days_to_maturity` and `frost_tolerance`
yourself. That division is deliberate: this server owns the climate half, and
the caller owns the crop half.

Southern-hemisphere sites are read on their own growing season. Grouping by
calendar year splits a southern summer in two and reports a frost-free window
of roughly one day.

## Install

Requires Node 20 or newer and a free Perenual API key.

```bash
npm install -g plant-intel-mcp
```

Or run it without installing: `npx plant-intel-mcp`.

Or run it straight from the repo:

```bash
git clone https://github.com/wyattlindsey/plant-intel-mcp.git && cd plant-intel-mcp && npm install && npm run build
```

### Claude Code

```bash
claude mcp add plant-intel --env PERENUAL_API_KEY=your-key -- npx -y plant-intel-mcp
```

### Claude Desktop

In `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "plant-intel": {
      "command": "npx",
      "args": ["-y", "plant-intel-mcp"],
      "env": {
        "PERENUAL_API_KEY": "your-key"
      }
    }
  }
}
```

### Configuration

| Variable | Required | Purpose |
| --- | --- | --- |
| `PERENUAL_API_KEY` | yes | Species data. [Free key](https://perenual.com/docs/api). |
| `PERMAPEOPLE_KEY_ID` / `PERMAPEOPLE_KEY_SECRET` | no | Adds documented companion listings. [Free, self-service](https://permapeople.org/my/api_keys). |
| `PERENUAL_IDENTIFY_BETA` | no | Set to `1` to register `identify_plant`. Needs Perenual beta access. |
| `PLANT_INTEL_CACHE_DIR` | no | Cache location. Defaults under `XDG_CACHE_HOME` or `~/.cache`. |
| `PLANT_INTEL_CACHE_DISABLED` | no | Set to `1` to disable caching. |

Missing the Perenual key does not stop the server. The tools stay listed and
each call answers with the variable to set and where to get a key.

## Sources, and their limits

Stated plainly, because these constraints shape what the server can honestly
answer.

| Source | Licence | Limits that matter |
| --- | --- | --- |
| [Perenual](https://perenual.com/docs/api) | API terms; **free tier is non-commercial** | **100 requests/day.** Free keys cover species ids 1–3000 only, not the advertised 10,000+ — and in practice most common garden crops sit above that line (tomato is 8759). Search covers the full catalogue; details do not, and an out-of-range request comes back as a 429 that this server translates rather than reporting as throttling. Care guides and hardiness maps are paid. Withheld fields return upgrade prompts, which this server strips. |
| [Permapeople](https://permapeople.org) | **CC BY-SA 4.0** | Optional, off unless configured. Positive companion links only — no antagonists. Its upstream includes PFAF and Kew, whose terms are more restrictive than CC BY-SA implies; check before relying on it commercially. |
| [Open-Meteo](https://open-meteo.com) | CC BY 4.0 | Free, no key. ERA5 reanalysis on a ~9 km grid — **not station data**. Expect frost dates to differ from local normals, especially in hills, valleys, or near water. Every `planting_window` response says so. |

Because of the 100/day ceiling, the server caches aggressively (30 days for
species data, 90 for weather archives) and guards a daily budget, refusing to
spend a request it does not have and naming the reset time when it declines.
Cache hits are never charged against it.

`plant_details` never returns spacing. No source here publishes it, and a null
with a note is more useful than a number nobody stands behind.

## Development

```bash
npm test          # unit and protocol tests, no network
npm run typecheck
npm run build
npm run test:live # opt-in; hits real APIs and spends quota
```

The live suite exists for drift detection. Fixtures were built from published
documentation, so it asserts that every field the mapper reads is still present
upstream -- last confirmed against the live API on 2026-09-08. Its Open-Meteo
tests need no credential and run anywhere.

See [CLAUDE.md](CLAUDE.md) for architecture and conventions.

## Licence

MIT. See [LICENSE](LICENSE).

Data retrieved through this server remains under its own source's terms — see
the table above. In particular, Perenual's free tier is non-commercial, and
Permapeople's data is share-alike.

TDQS

A4.4/5.0

Scored across 4 tools

Disambiguation5/5

Each tool targets a distinct operation: searching plants, retrieving details, computing planting windows, and checking companions. There is no overlap in purpose; the tools chain together via the search_then_detail flow, making selection unambiguous.

Naming Consistency5/5

All tool names follow a consistent pattern: a noun ('plant') modified by a descriptor ('details', 'intel', 'window', 'check'). The names are clear, descriptive, and follow a predictable schema, with no mixed conventions or vague verbs.

Tool Count4/5

Four tools is a lean but coherent set for a plant-intel domain. Each tool covers a distinct need (search, details, planting time, companionship), so none feel redundant. A fifth tool for bulk or batch queries might be additive, but the current count is appropriate for the niche scope.

Completeness4/5

The server covers the primary lifecycle: identify a plant, retrieve its profile, plan its planting window, and assess its compatibility with neighbors. Notable gaps include a missing tool for updating or managing the data (e.g., adding a custom plant or saving a garden plan), but the core research-oriented use case is fully covered.

Maintenance

ActivityMaintained
ResponsivenessNo issues