pollenbrief
Integrates with Google Pollen API and Geocoding API to geocode addresses, retrieve daily pollen forecasts, and build threshold-based pollen briefs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pollenbriefpollen brief for 100 Example Ave, 3 days, alert me when grass hits High"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
pollenbrief-mcp
A small MCP server and CLI that turns a Google Pollen API forecast into an agent-ready daily brief with
alert thresholds you set (for example GRASS>=4), per day and per pollen type or plant, as compact JSON
and Markdown.
The problem
Allergy-sensitive people and outdoor operations teams (landscaping, field crews, events) need to know which of the next few days cross their pollen threshold at a given place. The Google Pollen API returns a detailed forecast per day, pollen type and plant on the Universal Pollen Index (UPI, 0 to 5), but an agent still has to geocode the place, page through the response and apply thresholds itself. This lab does that in one tool call.
Related MCP server: Atmospore
Quickstart
bun install --frozen-lockfile
# Keyless demo: synthetic data in the documented response shape (fictional address, invented values)
bun run src/cli.ts brief --address "100 Example Ave, Sampletown" --profile grass-allergy --fixtures
bun run src/cli.ts brief --at 47.25,8.5 --days 3 --alert "GRASS>=3" --format json --fixtures
# Live: put GOOGLE_MAPS_API_KEY in .env (see .env.example), then
bun run src/cli.ts brief --address "<your address>" --alert "GRASS>=4" --alert "BIRCH>=High: keep windows closed"
bun run src/cli.ts brief --at 40.71,-74.01 --profile outdoor-crew --exit-on-alert # exit 3 when a rule fires
# MCP server (stdio)
bun run src/cli.ts serve # live
bun run src/cli.ts serve --fixtures # keyless demoQuote rules in the shell: an unquoted GRASS>=4 is a redirect to a file named =4.
MCP client config (Claude Code .mcp.json, Cursor mcp.json):
{
"mcpServers": {
"pollenbrief": {
"command": "bun",
"args": ["run", "/path/to/pollenbrief-mcp/src/cli.ts", "serve"],
"env": { "GOOGLE_MAPS_API_KEY": "${GOOGLE_MAPS_API_KEY}", "POLLENBRIEF_BUDGET": "20" }
}
}
}The key's Cloud project must have the Pollen API (pollen.googleapis.com) enabled, and the Geocoding API for
address lookups. Restrict the key to those APIs.
Tools
Tool | What it does | Google calls |
|
| 1 Geocoding v4 call for an address, plus 1 |
| Rule grammar, valid targets, UPI levels, bundled profiles | none |
| Budget used/remaining, refused calls | none |
CLI: pollenbrief brief | profiles | serve. Exit codes: 0 ok, 2 error, 3 alert (with --exit-on-alert), 64 usage.
Rules and profiles
TARGET>=LEVEL[: action]. TARGET is GRASS, TREE, WEED, a plant code from the API's Plant enum (ALDER,
ASH, BIRCH, COTTONWOOD, ELM, MAPLE, OLIVE, JUNIPER, OAK, PINE, CYPRESS_PINE, HAZEL,
GRAMINALES, RAGWEED, MUGWORT), or ANY_TYPE, ANY_PLANT, ANY. LEVEL is 0-5 or a UPI category name
(None, Very Low, Low, Moderate, High, Very High).
A rule alerts on a day when that entry's UPI value is at least LEVEL, and puts the day on watch when the
value is exactly one below. Entries the API returns without indexInfo (out of season or a low count) never fire;
the brief adds a note when a rule cannot fire at all in the forecast.
Bundled profiles in profiles/: grass-allergy, tree-allergy, outdoor-crew, ragweed-season. The CLI also
accepts a path to your own profile JSON; the MCP tool accepts bundled names only.
Output
JSON (brief): location, rules, summary (status alert/watch/clear, alertDays, watchDays,
firstAlert, peak, text, phrasedBy), daily[] (types, plants, alerts, watch, advice), notes,
attribution, source. Markdown: a status line, a day table (Grass / Tree / Weed / Status / Alerts), a plants
table, alerts grouped by day with your action text and the API's health recommendations, and the attribution line.
A full sample is in reports/pollenbrief-mcp.md.
phrase: true (CLI --phrase) asks Gemini to reword summary.text. Gemini receives only dates, codes, UPI values,
categories and rule ids, never the API's free text or your action notes. A reply containing any number that is not
in that input is rejected and the template wording stays. Without GEMINI_API_KEY, or on HTTP 402, the template is
used and phrasedBy says why.
Measured
Keyless run of the real MCP server (in-memory MCP client) over fixtures/synthetic/: invented values in the
response shape documented in Google's API reference. Byte counts depend on the invented values, so read them as
illustrative. "MCP = service" checks that the tool's JSON and Markdown equal the service output for the same input.
Produced by bun run report; full output in reports/pollenbrief-mcp.md and reports/pollenbrief-mcp.json.
Scenario | Input | Days | Upstream calls | Pollen response bytes (synthetic) | Brief JSON bytes | Markdown bytes | Alert days | Watch days | Status | MCP = service |
levels only | address, no rules | 5 | 2 | 11782 | 2679 | 1235 | 0 | 0 | clear | true |
grass-allergy | address, profile | 5 | 2 | 11782 | 4861 | 2764 | 2 | 2 | alert | true |
tree-allergy | address, profile | 5 | 2 | 11782 | 5032 | 2995 | 3 | 1 | alert | true |
outdoor-crew | address, profile | 5 | 2 | 11782 | 6020 | 3530 | 4 | 1 | alert | true |
ragweed-season | address, profile (weeds out of season) | 5 | 2 | 11782 | 3379 | 1851 | 0 | 0 | clear | true |
calm point | lat/lng, 3 days, GRASS>=3 | 3 | 1 | 4628 | 1569 | 974 | 0 | 1 | watch | true |
Pagination:
pageSize=2over 5 days took 3 pages / 4 upstream calls (geocode included) and produced the same days as one page: true.plantsDescription: the brief asks forfalseby default; the same synthetic forecast with descriptions is 25437 bytes vs 11782 (invented description text, so the ratio is illustrative).403 SERVICE_DISABLED (synthetic copy of the error shape): tool error google_PERMISSION_DENIED, reason SERVICE_DISABLED.
Budget 2, two address briefs (2 calls each): ok, budget_exceeded.
Gemini mocked 402:
template (gemini gemini-3.1-flash-lite skipped: HTTP 402 RESOURCE_EXHAUSTED).Gemini mocked reply with invented numbers:
template (gemini gemini-3.1-flash-lite skipped: rejected: numbers not in the input (7, 10)).ragweed-season note: "WEED has no index in this forecast (out of season or a low count), so WEED>=3 cannot fire".
Live status (2026-10-21)
Calls made with our Maps Platform key in project dev-test-461117 while building this lab:
Call | Result |
Geocoding API v4 | HTTP 200; |
Pollen API | HTTP 403 |
Gemini | HTTP 402 |
So no real Pollen API response has been observed by this lab, and no Gemini-written brief has been produced.
The Pollen path is tested only against synthetic responses built from the reference. When the API is enabled,
bun run record saves one real response to the gitignored .recorded-local/, and tests/live then checks it
against src/schema.ts.
What we checked in Google's docs
Checked on 2026-10-21 (the policies page showed "Last updated 2026-10-20 UTC").
Item | What the docs say | Source |
Endpoint |
| |
Parameters |
| same |
Response |
| same |
Index |
| same |
UPI categories | 0 None, 1 Very Low, 2 Low, 3 Moderate, 4 High, 5 Very High | |
Omitted fields |
| |
Pagination |
| same |
Geocoding v4 |
| |
"Agents lack a small MCP for this" | Not verified: market claim from the idea, not measured | none |
The forecast guide's examples pass the key as a key= query parameter. This lab sends it in the
X-Goog-Api-Key header instead; the Pollen API accepted that header far enough to answer SERVICE_DISABLED for our
project, but we have not seen a successful response with it.
Attribution and usage requirements
From the Pollen API policies:
Attribution. Data shown as is, without modifications, uses the Google Maps logo or text attribution. Content that aggregates or interprets the data (a custom score, thresholds, statuses) is attributed with "Includes data from Google Maps". The page also lists the older string "Source: Includes pollen data from Google" and says Google will publish timelines for moving existing "Google" attributions to "Google Maps". This brief interprets the data, so every JSON brief carries
"attribution": "Includes data from Google Maps"and every Markdown brief ends with that line. Keep it next to the numbers wherever you show them. The text attribution rules also cover font, size, color and placement, and ask fortranslate="no"in HTML.No caching. Content pre-fetching, caching or storage is generally prohibited (place IDs excepted). This server keeps nothing between calls, and the repo commits no Google responses.
Maps. If you put Pollen results on a map, it must be a Google Map.
Terms. Your app needs public Terms of Use and a Privacy Policy that meet Google's requirements; use is governed by your agreement with Google, and EEA billing addresses get different terms.
Limits
Pollen responses unverified live. See Live status: the Pollen API is not enabled in our project. Synthetic fixtures follow the reference field by field (
src/types.ts, strict check insrc/schema.ts), but real responses may carry fields or value formats we have not seen.Not medical advice. The brief repeats the API's health recommendations and your own action text; it adds no advice of its own. The UPI is Google's index, not a pollen count.
Thresholds are integer UPI levels. No per-hour data (the API gives one value per day) and no
>/<=operators.Budget scope. One upstream call budget per process / MCP connection (default 20), not per key or per day. Use Cloud quotas and budget alerts for hard limits. No prices are quoted here.
Keyless mode answers only the demo inputs in
fixtures/synthetic/INDEX.json; anything else returnsgoogle_NO_FIXTURE. Fixture output is never a real forecast and says so in the Markdown footer.Gemini guard is numeric only. It rejects invented numbers, not a wrong pairing of real ones.
Not included: any Pollen API endpoint other than
forecast:lookup, multi-location batches, scheduling or notifications (use--exit-on-alertfrom cron or CI), and storage of past briefs.
Development
bun run check # install, skills layout, strict YAML, biome, tsc, unit + e2e tests, live tests (skip
# cleanly), report, bun audit, actionlint (if installed), gitleaks
bun test tests/unit tests/e2e
bun test tests/live # needs keys; prints "skipped: ..." otherwise (POLLENBRIEF_SKIP_LIVE=1 forces skip)
bun run fixtures:synthetic # regenerate fixtures/synthetic from src/synthetic.ts
bun run record # live: save real responses to gitignored .recorded-local/ for the shape checkNot affiliated
Not affiliated with, endorsed by, or sponsored by Google LLC. Google Maps Platform, Pollen API and Gemini are trademarks of Google LLC.
Built by SkilledDragon LLC. Questions: hello@skilleddragon.com. Apache-2.0 (see LICENSE and NOTICE).
This server cannot be deployed
Maintenance
Related MCP Connectors
Access Pollinations models and API capabilities through agent tools.
SEO & marketing toolkit for AI agents: GA4, Search Console, AdSense, GTM, PageSpeed, Trends.
NOAA weather for AI agents: conditions, forecasts, observed history. 3 tools, free, no key.
31US NWS weather alerts and 5-day forecasts for AI agents. $0.01 per call.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceExposes public weather and climate data through a standardized API, allowing AI agents to retrieve current conditions, 7-day forecasts, and historical data. It enables weather-aware automation and data enrichment for conversational agents and travel planning.-
- AlicenseAqualityDmaintenancePer-species pollen forecasts at any point on Earth, seven days ahead. Free tier covers casual use.41MIT
- FlicenseAqualityDmaintenanceProvides real-time pollen level alerts and hayfever mitigation tips using the Ambee API.2-
- FlicenseAqualityCmaintenanceEnables AI assistants to retrieve current weather conditions and multi-day forecasts for any location, with optional natural-language summaries.1-