birdweather-mcp 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- birdweather_mcp-0.2.2/.gitignore +41 -0
- birdweather_mcp-0.2.2/CHANGELOG.md +100 -0
- birdweather_mcp-0.2.2/LICENSE +21 -0
- birdweather_mcp-0.2.2/PKG-INFO +113 -0
- birdweather_mcp-0.2.2/README.md +78 -0
- birdweather_mcp-0.2.2/docs/README.md +15 -0
- birdweather_mcp-0.2.2/docs/assets/demo.svg +46 -0
- birdweather_mcp-0.2.2/docs/assets/header.jpg +0 -0
- birdweather_mcp-0.2.2/docs/clients.md +141 -0
- birdweather_mcp-0.2.2/docs/configuration.md +61 -0
- birdweather_mcp-0.2.2/docs/development.md +97 -0
- birdweather_mcp-0.2.2/docs/installation.md +87 -0
- birdweather_mcp-0.2.2/docs/self-hosting.md +326 -0
- birdweather_mcp-0.2.2/docs/tools.md +593 -0
- birdweather_mcp-0.2.2/docs/troubleshooting.md +34 -0
- birdweather_mcp-0.2.2/pyproject.toml +74 -0
- birdweather_mcp-0.2.2/server.json +53 -0
- birdweather_mcp-0.2.2/src/birdweather_mcp/__init__.py +3 -0
- birdweather_mcp-0.2.2/src/birdweather_mcp/__main__.py +6 -0
- birdweather_mcp-0.2.2/src/birdweather_mcp/auth.py +120 -0
- birdweather_mcp-0.2.2/src/birdweather_mcp/cache.py +119 -0
- birdweather_mcp-0.2.2/src/birdweather_mcp/client.py +1279 -0
- birdweather_mcp-0.2.2/src/birdweather_mcp/queries.py +235 -0
- birdweather_mcp-0.2.2/src/birdweather_mcp/server.py +710 -0
- birdweather_mcp-0.2.2/tests/conftest.py +14 -0
- birdweather_mcp-0.2.2/tests/test_analytics.py +387 -0
- birdweather_mcp-0.2.2/tests/test_auth.py +233 -0
- birdweather_mcp-0.2.2/tests/test_cache.py +138 -0
- birdweather_mcp-0.2.2/tests/test_client.py +424 -0
- birdweather_mcp-0.2.2/tests/test_live.py +85 -0
- birdweather_mcp-0.2.2/tests/test_prompts_resources.py +100 -0
- birdweather_mcp-0.2.2/tests/test_redact.py +103 -0
- birdweather_mcp-0.2.2/tests/test_server.py +192 -0
- birdweather_mcp-0.2.2/tests/test_startup_warnings.py +71 -0
- birdweather_mcp-0.2.2/tests/test_validation.py +136 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
wheels/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
.eggs/
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
ENV/
|
|
15
|
+
|
|
16
|
+
# Test / coverage
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
.mypy_cache/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
|
|
23
|
+
# Env & secrets — never commit tokens
|
|
24
|
+
.env
|
|
25
|
+
.env.*
|
|
26
|
+
!.env.example
|
|
27
|
+
|
|
28
|
+
# Editors / OS
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
31
|
+
*.swp
|
|
32
|
+
.DS_Store
|
|
33
|
+
|
|
34
|
+
# Local reference scripts (not part of the published package)
|
|
35
|
+
reference/
|
|
36
|
+
|
|
37
|
+
# Misc
|
|
38
|
+
*.db
|
|
39
|
+
.uv/
|
|
40
|
+
uv.lock
|
|
41
|
+
dist/
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.2] — 2026-11-14
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- Compare HTTP bearer tokens with `hmac.compare_digest`; reject websocket scopes when `MCP_AUTH_TOKEN` is set; keep only exact `/health` public.
|
|
13
|
+
- Raise `httpx`/`httpcore` loggers to WARNING; install a root logging filter that redacts station and MCP auth tokens (raw and URL-encoded); harden `_redact` and `_err` so unexpected errors never leak token-bearing URLs or tracebacks.
|
|
14
|
+
- Warn at startup when HTTP binds a non-loopback host (or `MCP_ALLOWED_HOSTS=*`) without `MCP_AUTH_TOKEN` (still starts for compatibility).
|
|
15
|
+
- Validate `station_id`, date windows (max 366 days), species query length, granularity/period enums, and allowlisted REST path segments; keep GraphQL values in variables.
|
|
16
|
+
- Run the container as non-root (`USER app`, uid 10001); add `.dockerignore`; tighten compose with `read_only`, `cap_drop: ALL`, `no-new-privileges`, and `/tmp` tmpfs.
|
|
17
|
+
- Pin GitHub Actions to full commit SHAs, set `permissions: contents: read`, `persist-credentials: false` on checkout, and add Dependabot for Actions/pip/Docker.
|
|
18
|
+
- Expand `SECURITY.md` reporting policy, response expectations, and an HTTP hardening checklist; update configuration and self-hosting docs.
|
|
19
|
+
|
|
20
|
+
## [0.2.1] — 2026-11-14
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Documentation split into focused guides under `docs/` (installation, configuration, clients, tools, self-hosting, development, troubleshooting); README is a concise landing page.
|
|
25
|
+
- Homebrew install documented (`brew install pfrederiksen/tap/birdweather-mcp`).
|
|
26
|
+
|
|
27
|
+
## [0.2.0] — 2026-11-14
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- MCP prompts: `daily_station_summary`, `weekly_report`, `species_deep_dive`.
|
|
32
|
+
- MCP resources: `birdweather://station/{station_id}` and `birdweather://station/{station_id}/species`.
|
|
33
|
+
- README demo illustration (`docs/assets/demo.svg`) with placeholder Example Station conversation.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Version bump to 0.2.0 consolidating reliability, packaging, analytics tools, and prompts/resources.
|
|
38
|
+
|
|
39
|
+
## [0.1.4] — 2026-11-14
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Tools: `species_trend`, `first_of_year` / `new_arrivals`, `compare_stations`, `activity_by_hour` (with unit tests and docs/tools.md entries).
|
|
44
|
+
- Detection payloads include `audio_url`, species image/thumbnail URLs, and `spectrogram_url` when the API provides them.
|
|
45
|
+
|
|
46
|
+
## [0.1.3] — 2026-11-14
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- GitHub Actions workflow to publish multi-arch Docker images to `ghcr.io/pfrederiksen/birdweather-mcp` (`latest` + SHA on `main`; semver on version tags).
|
|
51
|
+
- PyPI-oriented packaging metadata and a trusted-publishing release workflow on version tags (configure the publisher on pypi.org; this repo does not publish automatically until that is set up).
|
|
52
|
+
- Optional `MCP_AUTH_TOKEN` bearer auth for HTTP mode (`/health` stays open).
|
|
53
|
+
- `server.json` for MCP registry metadata and [docs/clients.md](docs/clients.md) for Claude Desktop / Cursor / uvx / HTTP configs.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- `docker-compose.yml` and self-hosting / Synology docs prefer pulling the prebuilt GHCR image; build-from-source remains an alternative.
|
|
58
|
+
|
|
59
|
+
## [0.1.2] — 2026-11-14
|
|
60
|
+
|
|
61
|
+
### Added
|
|
62
|
+
|
|
63
|
+
- In-memory TTL response cache for GraphQL and REST (`BIRDWEATHER_CACHE_TTL`, `BIRDWEATHER_CACHE_MAX_ENTRIES`, `BIRDWEATHER_CACHE_ENABLED`).
|
|
64
|
+
- Retries with exponential backoff and jitter on HTTP 429/5xx and timeouts (`BIRDWEATHER_MAX_RETRIES`).
|
|
65
|
+
- `GET /health` liveness endpoint in streamable-HTTP mode (no upstream call); Dockerfile and compose healthchecks use it.
|
|
66
|
+
- GitHub Actions CI: ruff + unit tests on Python 3.11–3.13 (no live tests).
|
|
67
|
+
|
|
68
|
+
## [0.1.1] — 2026-11-14
|
|
69
|
+
|
|
70
|
+
### Fixed
|
|
71
|
+
|
|
72
|
+
- `daily_counts` now aggregates detections into station-local calendar days (GraphQL `dailyDetectionCounts` buckets by UTC and can return an extra day when `from == to`). Rows outside the requested range are omitted; quiet days are included with `total: 0`.
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- `MCP_ALLOWED_HOSTS` for streamable-HTTP Host-header allowlisting (comma-separated, or `*` on trusted networks) to avoid `421 Invalid Host header` when clients use LAN/MagicDNS names.
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- Synology self-hosting docs: off-NAS `linux/arm64` buildx + image import, `env_file` for tokens, LAN bind + MagicDNS, Container Manager / error 2202 notes, free-port and folder-layout examples.
|
|
81
|
+
|
|
82
|
+
## [0.1.0] — 2026-11-14
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- MCP server with GraphQL tools: `get_station`, `station_counts`, `get_weather`, `recent_detections`, `top_species` / `species_list`, `daily_counts`, `species_info`, `search_species`, `search_stations`.
|
|
87
|
+
- Optional `BIRDWEATHER_STATION_TOKEN` for station REST detections and all-time stats (token never logged).
|
|
88
|
+
- Transports: `stdio` (default), `streamable-http`, `sse`.
|
|
89
|
+
- Dockerfile (`python:3.12-slim`, multi-arch friendly) and `docker-compose.yml` for long-lived HTTP hosting.
|
|
90
|
+
- Docs: README, self-hosting guide, tool reference with live samples, contributing, security policy.
|
|
91
|
+
- Pytest suite with mocked unit tests and optional `@pytest.mark.live` smoke (set `BIRDWEATHER_TEST_STATION_ID`).
|
|
92
|
+
|
|
93
|
+
[0.2.2]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.2.2
|
|
94
|
+
[0.2.1]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.2.1
|
|
95
|
+
[0.2.0]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.2.0
|
|
96
|
+
[0.1.4]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.1.4
|
|
97
|
+
[0.1.3]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.1.3
|
|
98
|
+
[0.1.2]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.1.2
|
|
99
|
+
[0.1.1]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.1.1
|
|
100
|
+
[0.1.0]: https://github.com/pfrederiksen/birdweather-mcp/releases/tag/v0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paul Frederiksen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: birdweather-mcp
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Self-hostable MCP server for BirdWeather stations (detections, species, stats).
|
|
5
|
+
Project-URL: Homepage, https://github.com/pfrederiksen/birdweather-mcp
|
|
6
|
+
Project-URL: Documentation, https://github.com/pfrederiksen/birdweather-mcp#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/pfrederiksen/birdweather-mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/pfrederiksen/birdweather-mcp/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/pfrederiksen/birdweather-mcp/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Paul Frederiksen <pfrederiksen@users.noreply.github.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: birdnet,birds,birdweather,mcp,model-context-protocol
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Environment :: Console
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Requires-Dist: httpx>=0.27
|
|
28
|
+
Requires-Dist: mcp<2,>=1.9
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
32
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.17; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="docs/assets/header.jpg" alt="birdweather-mcp" width="100%" />
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
<p align="center"><em>Ask your AI about the birds your station is hearing — detections, species, and stats via MCP.</em></p>
|
|
41
|
+
|
|
42
|
+
<p align="center">
|
|
43
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
|
|
44
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+" /></a>
|
|
45
|
+
<a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-compatible-green.svg" alt="MCP compatible" /></a>
|
|
46
|
+
<a href="docs/self-hosting.md"><img src="https://img.shields.io/badge/docker-ready-2496ED.svg" alt="Docker ready" /></a>
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
# birdweather-mcp
|
|
50
|
+
|
|
51
|
+
Self-hostable [MCP](https://modelcontextprotocol.io) server for [BirdWeather](https://www.birdweather.com). Point Claude, Cursor, or any MCP client at your (or any public) station and ask natural-language questions about what was detected, what’s trending, and what’s nearby.
|
|
52
|
+
|
|
53
|
+
Works out of the box against the **public GraphQL API** — no account required. Optionally set a **station token** for richer REST fields. **The token is never logged or echoed.**
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- **15 MCP tools** — station info, counts, weather, detections, top species, daily series, species trends, new arrivals, station compare, hourly activity, species search/profiles, nearby stations
|
|
58
|
+
- **Prompts & resources** — daily/weekly summaries, species deep-dives, and `birdweather://station/…` URIs
|
|
59
|
+
- **No auth required** for public GraphQL; optional `BIRDWEATHER_STATION_TOKEN` for REST
|
|
60
|
+
- **stdio** (Claude Desktop / Cursor) or **streamable-HTTP** (Docker / Synology / Tailscale)
|
|
61
|
+
- **Token-safe** — redacted in logs and errors ([SECURITY.md](SECURITY.md))
|
|
62
|
+
- **Docker + compose** — prebuilt multi-arch images on GHCR (`ghcr.io/pfrederiksen/birdweather-mcp`)
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
**uvx** (no clone):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uvx birdweather-mcp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Homebrew:**
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
brew install pfrederiksen/tap/birdweather-mcp
|
|
76
|
+
birdweather-mcp
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Docker** (stdio):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
docker pull ghcr.io/pfrederiksen/birdweather-mcp:latest
|
|
83
|
+
docker run --rm -i ghcr.io/pfrederiksen/birdweather-mcp:latest
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Long-lived HTTP on a NAS → [Self-hosting](docs/self-hosting.md). More install paths → [Installation](docs/installation.md).
|
|
87
|
+
|
|
88
|
+
Configure Claude Desktop / Cursor / VS Code → [Clients](docs/clients.md).
|
|
89
|
+
|
|
90
|
+
## Documentation
|
|
91
|
+
|
|
92
|
+
| Guide | Description |
|
|
93
|
+
|-------|-------------|
|
|
94
|
+
| [docs/README.md](docs/README.md) | Index of all guides |
|
|
95
|
+
| [Installation](docs/installation.md) | uvx, pip, Homebrew, Docker/GHCR, from source |
|
|
96
|
+
| [Configuration](docs/configuration.md) | Environment variables, station ID/token |
|
|
97
|
+
| [Clients](docs/clients.md) | Claude Desktop, Cursor, Claude Code, VS Code, HTTP |
|
|
98
|
+
| [Tools](docs/tools.md) | Tools, prompts, and resources |
|
|
99
|
+
| [Self-hosting](docs/self-hosting.md) | Docker, Synology/arm64, LAN/Tailscale, updates |
|
|
100
|
+
| [Development](docs/development.md) | Dev setup, tests, lint, release process |
|
|
101
|
+
| [Troubleshooting](docs/troubleshooting.md) | Common problems and FAQ |
|
|
102
|
+
|
|
103
|
+
## Demo
|
|
104
|
+
|
|
105
|
+
<p align="center">
|
|
106
|
+
<img src="docs/assets/demo.svg" alt="Demo conversation about Example Station 12345" width="100%" />
|
|
107
|
+
</p>
|
|
108
|
+
|
|
109
|
+
Placeholder illustration of asking an MCP client about **Example Station** (`12345`) — not live data.
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
Licensed under [MIT](LICENSE). See also [CHANGELOG.md](CHANGELOG.md), [CONTRIBUTING.md](CONTRIBUTING.md), and [SECURITY.md](SECURITY.md).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/header.jpg" alt="birdweather-mcp" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center"><em>Ask your AI about the birds your station is hearing — detections, species, and stats via MCP.</em></p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License" /></a>
|
|
9
|
+
<a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+" /></a>
|
|
10
|
+
<a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-compatible-green.svg" alt="MCP compatible" /></a>
|
|
11
|
+
<a href="docs/self-hosting.md"><img src="https://img.shields.io/badge/docker-ready-2496ED.svg" alt="Docker ready" /></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
# birdweather-mcp
|
|
15
|
+
|
|
16
|
+
Self-hostable [MCP](https://modelcontextprotocol.io) server for [BirdWeather](https://www.birdweather.com). Point Claude, Cursor, or any MCP client at your (or any public) station and ask natural-language questions about what was detected, what’s trending, and what’s nearby.
|
|
17
|
+
|
|
18
|
+
Works out of the box against the **public GraphQL API** — no account required. Optionally set a **station token** for richer REST fields. **The token is never logged or echoed.**
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
- **15 MCP tools** — station info, counts, weather, detections, top species, daily series, species trends, new arrivals, station compare, hourly activity, species search/profiles, nearby stations
|
|
23
|
+
- **Prompts & resources** — daily/weekly summaries, species deep-dives, and `birdweather://station/…` URIs
|
|
24
|
+
- **No auth required** for public GraphQL; optional `BIRDWEATHER_STATION_TOKEN` for REST
|
|
25
|
+
- **stdio** (Claude Desktop / Cursor) or **streamable-HTTP** (Docker / Synology / Tailscale)
|
|
26
|
+
- **Token-safe** — redacted in logs and errors ([SECURITY.md](SECURITY.md))
|
|
27
|
+
- **Docker + compose** — prebuilt multi-arch images on GHCR (`ghcr.io/pfrederiksen/birdweather-mcp`)
|
|
28
|
+
|
|
29
|
+
## Quickstart
|
|
30
|
+
|
|
31
|
+
**uvx** (no clone):
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uvx birdweather-mcp
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Homebrew:**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
brew install pfrederiksen/tap/birdweather-mcp
|
|
41
|
+
birdweather-mcp
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Docker** (stdio):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
docker pull ghcr.io/pfrederiksen/birdweather-mcp:latest
|
|
48
|
+
docker run --rm -i ghcr.io/pfrederiksen/birdweather-mcp:latest
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Long-lived HTTP on a NAS → [Self-hosting](docs/self-hosting.md). More install paths → [Installation](docs/installation.md).
|
|
52
|
+
|
|
53
|
+
Configure Claude Desktop / Cursor / VS Code → [Clients](docs/clients.md).
|
|
54
|
+
|
|
55
|
+
## Documentation
|
|
56
|
+
|
|
57
|
+
| Guide | Description |
|
|
58
|
+
|-------|-------------|
|
|
59
|
+
| [docs/README.md](docs/README.md) | Index of all guides |
|
|
60
|
+
| [Installation](docs/installation.md) | uvx, pip, Homebrew, Docker/GHCR, from source |
|
|
61
|
+
| [Configuration](docs/configuration.md) | Environment variables, station ID/token |
|
|
62
|
+
| [Clients](docs/clients.md) | Claude Desktop, Cursor, Claude Code, VS Code, HTTP |
|
|
63
|
+
| [Tools](docs/tools.md) | Tools, prompts, and resources |
|
|
64
|
+
| [Self-hosting](docs/self-hosting.md) | Docker, Synology/arm64, LAN/Tailscale, updates |
|
|
65
|
+
| [Development](docs/development.md) | Dev setup, tests, lint, release process |
|
|
66
|
+
| [Troubleshooting](docs/troubleshooting.md) | Common problems and FAQ |
|
|
67
|
+
|
|
68
|
+
## Demo
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<img src="docs/assets/demo.svg" alt="Demo conversation about Example Station 12345" width="100%" />
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
Placeholder illustration of asking an MCP client about **Example Station** (`12345`) — not live data.
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
Licensed under [MIT](LICENSE). See also [CHANGELOG.md](CHANGELOG.md), [CONTRIBUTING.md](CONTRIBUTING.md), and [SECURITY.md](SECURITY.md).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
Guides for installing, configuring, and running [birdweather-mcp](../README.md).
|
|
4
|
+
|
|
5
|
+
| Guide | Description |
|
|
6
|
+
|-------|-------------|
|
|
7
|
+
| [Installation](installation.md) | uvx, pip, Homebrew, Docker/GHCR, from source |
|
|
8
|
+
| [Configuration](configuration.md) | Environment variables, defaults, station ID/token |
|
|
9
|
+
| [Clients](clients.md) | Claude Desktop, Cursor, Claude Code, VS Code, HTTP |
|
|
10
|
+
| [Tools](tools.md) | MCP tools, prompts, and resources |
|
|
11
|
+
| [Self-hosting](self-hosting.md) | Docker, compose, Synology/arm64, LAN/Tailscale |
|
|
12
|
+
| [Development](development.md) | Dev setup, tests, lint, release process |
|
|
13
|
+
| [Troubleshooting](troubleshooting.md) | Common problems and FAQ |
|
|
14
|
+
|
|
15
|
+
Also see [SECURITY.md](../SECURITY.md), [CHANGELOG.md](../CHANGELOG.md), and [CONTRIBUTING.md](../CONTRIBUTING.md).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">birdweather-mcp demo conversation</title>
|
|
3
|
+
<desc id="desc">Example chat asking about Example Station 12345 detections with placeholder data.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
6
|
+
<stop offset="0%" stop-color="#1a2f28"/>
|
|
7
|
+
<stop offset="55%" stop-color="#243f36"/>
|
|
8
|
+
<stop offset="100%" stop-color="#3d5a4c"/>
|
|
9
|
+
</linearGradient>
|
|
10
|
+
<linearGradient id="panel" x1="0" y1="0" x2="0" y2="1">
|
|
11
|
+
<stop offset="0%" stop-color="#f7f3ea"/>
|
|
12
|
+
<stop offset="100%" stop-color="#ebe4d6"/>
|
|
13
|
+
</linearGradient>
|
|
14
|
+
<filter id="soft" x="-10%" y="-10%" width="120%" height="120%">
|
|
15
|
+
<feDropShadow dx="0" dy="8" stdDeviation="12" flood-color="#0b1612" flood-opacity="0.35"/>
|
|
16
|
+
</filter>
|
|
17
|
+
</defs>
|
|
18
|
+
|
|
19
|
+
<rect width="960" height="540" fill="url(#bg)"/>
|
|
20
|
+
<!-- soft foliage shapes -->
|
|
21
|
+
<circle cx="70" cy="480" r="120" fill="#2f4a3f" opacity="0.55"/>
|
|
22
|
+
<circle cx="900" cy="40" r="140" fill="#2a4439" opacity="0.45"/>
|
|
23
|
+
<circle cx="820" cy="500" r="90" fill="#4a6b58" opacity="0.25"/>
|
|
24
|
+
|
|
25
|
+
<text x="48" y="52" fill="#d8e5dd" font-family="Georgia, 'Times New Roman', serif" font-size="28" font-weight="700">birdweather-mcp</text>
|
|
26
|
+
<text x="48" y="78" fill="#a9c0b4" font-family="ui-sans-serif, system-ui, sans-serif" font-size="13">Ask your AI what station 12345 has been hearing</text>
|
|
27
|
+
|
|
28
|
+
<rect x="48" y="108" width="864" height="380" rx="18" fill="url(#panel)" filter="url(#soft)"/>
|
|
29
|
+
|
|
30
|
+
<!-- user bubble -->
|
|
31
|
+
<rect x="420" y="140" width="460" height="64" rx="14" fill="#2f5d4a"/>
|
|
32
|
+
<text x="440" y="168" fill="#f4faf6" font-family="ui-sans-serif, system-ui, sans-serif" font-size="15">What has Example Station been hearing this week?</text>
|
|
33
|
+
<text x="440" y="188" fill="#c5ddd2" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12">station_id 12345 · last 7 days</text>
|
|
34
|
+
|
|
35
|
+
<!-- assistant bubble -->
|
|
36
|
+
<rect x="80" y="230" width="620" height="210" rx="14" fill="#ffffff" stroke="#d7cfbf"/>
|
|
37
|
+
<text x="100" y="258" fill="#1f2a24" font-family="Georgia, 'Times New Roman', serif" font-size="16" font-weight="700">Assistant</text>
|
|
38
|
+
<text x="100" y="286" fill="#334038" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14">Called top_species + station_counts for Example Station.</text>
|
|
39
|
+
<text x="100" y="312" fill="#334038" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14">• House Finch — 273 detections</text>
|
|
40
|
+
<text x="100" y="334" fill="#334038" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14">• Anna's Hummingbird — 51</text>
|
|
41
|
+
<text x="100" y="356" fill="#334038" font-family="ui-sans-serif, system-ui, sans-serif" font-size="14">• ~497 detections · 31 species</text>
|
|
42
|
+
<text x="100" y="386" fill="#5b6b62" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12">Timezone America/Los_Angeles · placeholder sample data</text>
|
|
43
|
+
<text x="100" y="412" fill="#2f5d4a" font-family="ui-sans-serif, system-ui, sans-serif" font-size="12">soundscape.url available on recent_detections when present</text>
|
|
44
|
+
|
|
45
|
+
<text x="48" y="520" fill="#8eaa9b" font-family="ui-sans-serif, system-ui, sans-serif" font-size="11">Demo illustration · no live station data</text>
|
|
46
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# MCP client configurations
|
|
2
|
+
|
|
3
|
+
Configure Claude Desktop, Cursor, and other MCP clients to talk to birdweather-mcp over **stdio** (local process), **uvx** / Homebrew, or **streamable-HTTP** (self-hosted Docker / NAS).
|
|
4
|
+
|
|
5
|
+
Use placeholder station id `12345` in prompts. Never put a real `BIRDWEATHER_STATION_TOKEN` or `MCP_AUTH_TOKEN` in committed files. Env reference: [configuration.md](configuration.md). Install paths: [installation.md](installation.md).
|
|
6
|
+
|
|
7
|
+
## stdio via uvx (recommended for desktop clients)
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"birdweather": {
|
|
13
|
+
"command": "uvx",
|
|
14
|
+
"args": ["birdweather-mcp"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
From git:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"mcpServers": {
|
|
25
|
+
"birdweather": {
|
|
26
|
+
"command": "uvx",
|
|
27
|
+
"args": ["--from", "git+https://github.com/pfrederiksen/birdweather-mcp", "birdweather-mcp"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## stdio via Homebrew
|
|
34
|
+
|
|
35
|
+
After `brew install pfrederiksen/tap/birdweather-mcp`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"birdweather": {
|
|
41
|
+
"command": "birdweather-mcp"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Optional env:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"birdweather": {
|
|
53
|
+
"command": "uvx",
|
|
54
|
+
"args": ["birdweather-mcp"],
|
|
55
|
+
"env": {
|
|
56
|
+
"BIRDWEATHER_STATION_TOKEN": ""
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## stdio via local checkout
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"birdweather": {
|
|
69
|
+
"command": "uv",
|
|
70
|
+
"args": ["run", "--directory", "/absolute/path/to/birdweather-mcp", "birdweather-mcp"]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Claude Desktop
|
|
77
|
+
|
|
78
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or the Windows equivalent. Use either uvx block above under `mcpServers`.
|
|
79
|
+
|
|
80
|
+
## Cursor
|
|
81
|
+
|
|
82
|
+
Project `.cursor/mcp.json` or Cursor MCP settings — same `mcpServers` shape as Claude Desktop.
|
|
83
|
+
|
|
84
|
+
## Claude Code
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
claude mcp add birdweather -- uvx birdweather-mcp
|
|
88
|
+
# or from git:
|
|
89
|
+
claude mcp add birdweather -- uvx --from git+https://github.com/pfrederiksen/birdweather-mcp birdweather-mcp
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## VS Code
|
|
93
|
+
|
|
94
|
+
`.vscode/mcp.json`:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"servers": {
|
|
99
|
+
"birdweather": {
|
|
100
|
+
"type": "stdio",
|
|
101
|
+
"command": "uvx",
|
|
102
|
+
"args": ["birdweather-mcp"]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## streamable-HTTP (Docker / Synology / LAN)
|
|
109
|
+
|
|
110
|
+
Run the server (see [self-hosting.md](self-hosting.md)), then point the client at `/mcp`:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"birdweather": {
|
|
116
|
+
"url": "http://127.0.0.1:8000/mcp"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
If `MCP_AUTH_TOKEN` is set on the server, send a bearer header (exact client support varies):
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcpServers": {
|
|
127
|
+
"birdweather": {
|
|
128
|
+
"url": "http://127.0.0.1:8000/mcp",
|
|
129
|
+
"headers": {
|
|
130
|
+
"Authorization": "Bearer <your-mcp-auth-token>"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`GET /health` stays unauthenticated for probes. Prefer LAN / Tailscale over public exposure — see [SECURITY.md](../SECURITY.md).
|
|
138
|
+
|
|
139
|
+
## Registry metadata
|
|
140
|
+
|
|
141
|
+
[`server.json`](../server.json) describes this server for the official MCP registry (PyPI + GHCR package entries). Publishing to the registry is a separate step after PyPI/GHCR packages exist.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
All settings are environment variables (or CLI flags that mirror the transport trio). Copy [`.env.example`](../.env.example) for a local template. **Never commit real tokens.**
|
|
4
|
+
|
|
5
|
+
## Environment variables
|
|
6
|
+
|
|
7
|
+
| Variable | Required | Default | Description |
|
|
8
|
+
|----------|----------|---------|-------------|
|
|
9
|
+
| `BIRDWEATHER_STATION_TOKEN` | No | unset | Station API token for REST detections / all-time stats. **Never logged.** Without it, public GraphQL only. |
|
|
10
|
+
| `MCP_TRANSPORT` | No | `stdio` | `stdio`, `streamable-http`, or `sse`. Overridable with `--transport`. |
|
|
11
|
+
| `MCP_HOST` | No | `127.0.0.1` | Bind host for HTTP transports. Overridable with `--host`. Inside Docker use `0.0.0.0` and restrict with host port publish (prefer `127.0.0.1` or LAN-only on the host). Non-loopback bind without `MCP_AUTH_TOKEN` logs a SECURITY warning. |
|
|
12
|
+
| `MCP_PORT` | No | `8000` | Bind port for HTTP transports. Overridable with `--port`. |
|
|
13
|
+
| `MCP_ALLOWED_HOSTS` | No | unset (loopback only) | Comma-separated Host header patterns for streamable-HTTP DNS-rebinding protection (e.g. `nas.example.local:*,192.168.1.10:*`). Unset = FastMCP loopback defaults. `*` = allow any host (**trusted networks only**; warns if `MCP_AUTH_TOKEN` is unset). Fixes `421 Invalid Host header` for MagicDNS/LAN names. |
|
|
14
|
+
| `MCP_AUTH_TOKEN` | No | unset | When set, HTTP `/mcp` requires `Authorization: Bearer …` (constant-time compare). Exact `/health` stays open; websockets are rejected. Strongly recommended for non-loopback HTTP. Never logged. |
|
|
15
|
+
| `BIRDWEATHER_CACHE_TTL` | No | `60` | In-memory upstream cache TTL in seconds. Set `0` to disable. |
|
|
16
|
+
| `BIRDWEATHER_CACHE_MAX_ENTRIES` | No | `256` | Max cached GraphQL/REST responses. |
|
|
17
|
+
| `BIRDWEATHER_CACHE_ENABLED` | No | enabled when TTL > 0 | Set `false` / `0` / `no` / `off` to disable caching; `true` / `1` / `yes` / `on` to force on. |
|
|
18
|
+
| `BIRDWEATHER_MAX_RETRIES` | No | `5` | Retries with exponential backoff + jitter on HTTP 429/5xx and timeouts (minimum effective value `1`). |
|
|
19
|
+
| `BIRDWEATHER_TEST_STATION_ID` | No | unset | Public station id for optional live pytest (`pytest -m live`). Prefer shell-only; do not commit a personal id. |
|
|
20
|
+
|
|
21
|
+
### Logging
|
|
22
|
+
|
|
23
|
+
The server logs to stderr at **INFO** with format `%(levelname)s %(name)s: %(message)s`. There is no log-level env var today. Station and MCP auth tokens are **never** written to logs or tool/error payloads (raw and URL-encoded forms are redacted via a logging filter; `httpx`/`httpcore` are set to WARNING so request URLs are not printed). See [SECURITY.md](../SECURITY.md).
|
|
24
|
+
|
|
25
|
+
### Health endpoint
|
|
26
|
+
|
|
27
|
+
In `streamable-http` mode, `GET /health` returns `{"status":"ok","version":"..."}` with **no** upstream BirdWeather call. Used by Docker/compose healthchecks. Unaffected by `MCP_AUTH_TOKEN` (exact path only — `/health/../mcp` is not public).
|
|
28
|
+
|
|
29
|
+
## Finding a station ID and token
|
|
30
|
+
|
|
31
|
+
**Station ID** (public, numeric — docs use `12345`):
|
|
32
|
+
|
|
33
|
+
1. Open your station on [app.birdweather.com](https://app.birdweather.com) (web) or in the BirdWeather app.
|
|
34
|
+
2. Check the URL: `https://app.birdweather.com/stations/<id>` — that number is `station_id`.
|
|
35
|
+
3. Or search by name/coords with the `search_stations` tool once the server is running.
|
|
36
|
+
|
|
37
|
+
**Station token** (secret — optional):
|
|
38
|
+
|
|
39
|
+
1. In the BirdWeather app or web UI, open **your** station’s settings.
|
|
40
|
+
2. Find the **API / token** section (used for `https://app.birdweather.com/api/v1/stations/<token>/...`).
|
|
41
|
+
3. Copy into `BIRDWEATHER_STATION_TOKEN` in MCP config or `.env` — never commit it.
|
|
42
|
+
|
|
43
|
+
Without a token, all GraphQL tools still work for any public station. With a token, `get_station` can include all-time REST stats and `recent_detections` prefers REST (extra fields).
|
|
44
|
+
|
|
45
|
+
## CLI flags
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
birdweather-mcp --transport streamable-http --host 127.0.0.1 --port 8000
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
| Flag | Env equivalent |
|
|
52
|
+
|------|----------------|
|
|
53
|
+
| `--transport` | `MCP_TRANSPORT` |
|
|
54
|
+
| `--host` | `MCP_HOST` |
|
|
55
|
+
| `--port` | `MCP_PORT` |
|
|
56
|
+
|
|
57
|
+
## Related
|
|
58
|
+
|
|
59
|
+
- [Clients](clients.md) — where to put env in Claude / Cursor / HTTP
|
|
60
|
+
- [Self-hosting](self-hosting.md) — Docker, Synology, `MCP_ALLOWED_HOSTS`
|
|
61
|
+
- [Troubleshooting](troubleshooting.md)
|