captain-drawdown-mcp 0.1.0__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.
- captain_drawdown_mcp-0.1.0/.gitignore +3 -0
- captain_drawdown_mcp-0.1.0/PKG-INFO +55 -0
- captain_drawdown_mcp-0.1.0/README.md +42 -0
- captain_drawdown_mcp-0.1.0/captain_drawdown_mcp/__init__.py +2 -0
- captain_drawdown_mcp-0.1.0/captain_drawdown_mcp/__main__.py +4 -0
- captain_drawdown_mcp-0.1.0/captain_drawdown_mcp/data.py +111 -0
- captain_drawdown_mcp-0.1.0/captain_drawdown_mcp/server.py +87 -0
- captain_drawdown_mcp-0.1.0/pyproject.toml +24 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: captain-drawdown-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for Captain Drawdown's open carbon dioxide removal data: companies, researchers, shops.
|
|
5
|
+
Project-URL: Homepage, https://captaindrawdown.com/api/
|
|
6
|
+
Project-URL: Data, https://captaindrawdown.com/data/
|
|
7
|
+
Author-email: Captain Drawdown / Carbon Drawdown Initiative <info@carbon-drawdown.de>
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: CDR,carbon removal,climate,mcp,open data
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: mcp<2,>=1.2
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# captain-drawdown-mcp
|
|
15
|
+
|
|
16
|
+
An MCP server that gives Claude, ChatGPT, Cursor and other MCP clients direct access to Captain Drawdown's open data on carbon dioxide removal (CDR):
|
|
17
|
+
|
|
18
|
+
- **Companies**: 969 published CDR companies with pathway, country, liveliness tier, EU SME size class, registries and evidence dates (CC BY-NC-SA 4.0).
|
|
19
|
+
- **Researchers**: 95,000+ researchers with CDR papers since 2021, aggregated by country and pathway, plus top-100 lists per pathway (CC BY 4.0).
|
|
20
|
+
- **Shops**: shops where individuals buy certified removal, with prices (CC BY 4.0).
|
|
21
|
+
|
|
22
|
+
It reads the public static API at `https://captaindrawdown.com/api/v1/` (no key, CORS open) and caches responses for six hours. Every result carries `version`, `as_of`, `license` and `cite_as`.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install captain-drawdown-mcp # or: uvx captain-drawdown-mcp
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Claude Desktop / Claude Code (`claude mcp add captain-drawdown -- captain-drawdown-mcp`) or `claude_desktop_config.json`:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{"mcpServers": {"captain-drawdown": {"command": "captain-drawdown-mcp"}}}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Cursor (`.cursor/mcp.json`): same `command`. Any client that speaks MCP over stdio works.
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
| Tool | What it returns |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `stats()` | headline numbers of the newest release |
|
|
43
|
+
| `search_companies(query, country_code, pathway, size_class, region, limit)` | matching companies with slugs |
|
|
44
|
+
| `company(slug)` | one company |
|
|
45
|
+
| `countries()`, `pathways()` | companies per country / per pathway |
|
|
46
|
+
| `researchers_by_country()`, `researchers_by_pathway()` | listed researchers with the calibrated estimate |
|
|
47
|
+
| `top_researchers(pathway, region, limit)` | top 100 worldwide or top 50 in Europe for a pathway |
|
|
48
|
+
| `shops()` | shops with prices |
|
|
49
|
+
| `cite()` | citation and licences |
|
|
50
|
+
|
|
51
|
+
## Data and licences
|
|
52
|
+
|
|
53
|
+
Pages: https://captaindrawdown.com/data/ · API: https://captaindrawdown.com/api/ · Corrections: info@carbon-drawdown.de. The server code is MIT; the data keeps its own licences (companies CC BY-NC-SA 4.0 because the directory is seeded from Grant Faber's list; researchers and shops CC BY 4.0).
|
|
54
|
+
|
|
55
|
+
Captain Drawdown is a project of the Carbon Drawdown Initiative (Fürth, Germany).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# captain-drawdown-mcp
|
|
2
|
+
|
|
3
|
+
An MCP server that gives Claude, ChatGPT, Cursor and other MCP clients direct access to Captain Drawdown's open data on carbon dioxide removal (CDR):
|
|
4
|
+
|
|
5
|
+
- **Companies**: 969 published CDR companies with pathway, country, liveliness tier, EU SME size class, registries and evidence dates (CC BY-NC-SA 4.0).
|
|
6
|
+
- **Researchers**: 95,000+ researchers with CDR papers since 2021, aggregated by country and pathway, plus top-100 lists per pathway (CC BY 4.0).
|
|
7
|
+
- **Shops**: shops where individuals buy certified removal, with prices (CC BY 4.0).
|
|
8
|
+
|
|
9
|
+
It reads the public static API at `https://captaindrawdown.com/api/v1/` (no key, CORS open) and caches responses for six hours. Every result carries `version`, `as_of`, `license` and `cite_as`.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install captain-drawdown-mcp # or: uvx captain-drawdown-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Claude Desktop / Claude Code (`claude mcp add captain-drawdown -- captain-drawdown-mcp`) or `claude_desktop_config.json`:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{"mcpServers": {"captain-drawdown": {"command": "captain-drawdown-mcp"}}}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Cursor (`.cursor/mcp.json`): same `command`. Any client that speaks MCP over stdio works.
|
|
24
|
+
|
|
25
|
+
## Tools
|
|
26
|
+
|
|
27
|
+
| Tool | What it returns |
|
|
28
|
+
|---|---|
|
|
29
|
+
| `stats()` | headline numbers of the newest release |
|
|
30
|
+
| `search_companies(query, country_code, pathway, size_class, region, limit)` | matching companies with slugs |
|
|
31
|
+
| `company(slug)` | one company |
|
|
32
|
+
| `countries()`, `pathways()` | companies per country / per pathway |
|
|
33
|
+
| `researchers_by_country()`, `researchers_by_pathway()` | listed researchers with the calibrated estimate |
|
|
34
|
+
| `top_researchers(pathway, region, limit)` | top 100 worldwide or top 50 in Europe for a pathway |
|
|
35
|
+
| `shops()` | shops with prices |
|
|
36
|
+
| `cite()` | citation and licences |
|
|
37
|
+
|
|
38
|
+
## Data and licences
|
|
39
|
+
|
|
40
|
+
Pages: https://captaindrawdown.com/data/ · API: https://captaindrawdown.com/api/ · Corrections: info@carbon-drawdown.de. The server code is MIT; the data keeps its own licences (companies CC BY-NC-SA 4.0 because the directory is seeded from Grant Faber's list; researchers and shops CC BY 4.0).
|
|
41
|
+
|
|
42
|
+
Captain Drawdown is a project of the Carbon Drawdown Initiative (Fürth, Germany).
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Data access for the Captain Drawdown MCP server: reads the static JSON API
|
|
2
|
+
(https://captaindrawdown.com/api/v1/) and filters in memory. No mcp import
|
|
3
|
+
here so the logic is testable without the SDK."""
|
|
4
|
+
import json
|
|
5
|
+
import time
|
|
6
|
+
from typing import Callable, Optional
|
|
7
|
+
from urllib.request import Request, urlopen
|
|
8
|
+
|
|
9
|
+
API_BASE = "https://captaindrawdown.com/api/v1"
|
|
10
|
+
CACHE_TTL_S = 6 * 3600
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _http_get(url: str) -> dict:
|
|
14
|
+
req = Request(url, headers={"User-Agent": "captain-drawdown-mcp/0.1 (+https://captaindrawdown.com/api/)"})
|
|
15
|
+
with urlopen(req, timeout=30) as r:
|
|
16
|
+
return json.loads(r.read().decode("utf-8"))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Api:
|
|
20
|
+
"""Small cached client. `fetch` is injectable for tests."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, base: str = API_BASE, fetch: Optional[Callable[[str], dict]] = None,
|
|
23
|
+
ttl_s: int = CACHE_TTL_S):
|
|
24
|
+
self.base = base.rstrip("/")
|
|
25
|
+
self.fetch = fetch or _http_get
|
|
26
|
+
self.ttl = ttl_s
|
|
27
|
+
self._cache = {}
|
|
28
|
+
|
|
29
|
+
def get(self, path: str) -> dict:
|
|
30
|
+
now = time.time()
|
|
31
|
+
hit = self._cache.get(path)
|
|
32
|
+
if hit and now - hit[0] < self.ttl:
|
|
33
|
+
return hit[1]
|
|
34
|
+
data = self.fetch(f"{self.base}/{path.lstrip('/')}")
|
|
35
|
+
self._cache[path] = (now, data)
|
|
36
|
+
return data
|
|
37
|
+
|
|
38
|
+
# ── tools ──────────────────────────────────────────────────────────
|
|
39
|
+
def stats(self) -> dict:
|
|
40
|
+
return self.get("stats.json")
|
|
41
|
+
|
|
42
|
+
def company(self, slug: str) -> dict:
|
|
43
|
+
slug = (slug or "").strip().lower()
|
|
44
|
+
if not slug:
|
|
45
|
+
return {"error": "slug required"}
|
|
46
|
+
try:
|
|
47
|
+
return self.get(f"companies/{slug}.json")
|
|
48
|
+
except Exception:
|
|
49
|
+
return {"error": f"unknown slug {slug!r}; use search_companies to find the slug"}
|
|
50
|
+
|
|
51
|
+
def search_companies(self, query: str = "", country_code: str = "", pathway: str = "",
|
|
52
|
+
size_class: str = "", region: str = "", limit: int = 25) -> dict:
|
|
53
|
+
env = self.get("companies.json")
|
|
54
|
+
rows = env.get("data", [])
|
|
55
|
+
q = (query or "").strip().lower()
|
|
56
|
+
cc = (country_code or "").strip().upper()
|
|
57
|
+
pw = (pathway or "").strip().lower()
|
|
58
|
+
sc = (size_class or "").strip().lower()
|
|
59
|
+
rg = (region or "").strip().lower()
|
|
60
|
+
out = []
|
|
61
|
+
for c in rows:
|
|
62
|
+
if q and q not in f"{c.get('name', '')} {c.get('description', '')} {c.get('pathway_primary', '')}".lower():
|
|
63
|
+
continue
|
|
64
|
+
if cc and (c.get("country_code") or "").upper() != cc:
|
|
65
|
+
continue
|
|
66
|
+
if pw and pw not in (c.get("pathway_primary") or "").lower():
|
|
67
|
+
continue
|
|
68
|
+
if sc and sc not in (c.get("size_class") or "").lower():
|
|
69
|
+
continue
|
|
70
|
+
if rg and rg not in (c.get("region") or "").lower():
|
|
71
|
+
continue
|
|
72
|
+
out.append({k: c.get(k) for k in ("name", "slug", "country", "country_code", "region",
|
|
73
|
+
"pathway_primary", "liveliness_tier", "size_class",
|
|
74
|
+
"url", "profile_url")})
|
|
75
|
+
limit = max(1, min(int(limit or 25), 200))
|
|
76
|
+
return {"version": env.get("version"), "as_of": env.get("as_of"), "license": env.get("license"),
|
|
77
|
+
"cite_as": env.get("cite_as"), "matches": len(out), "data": out[:limit]}
|
|
78
|
+
|
|
79
|
+
def countries(self) -> dict:
|
|
80
|
+
return self.get("countries.json")
|
|
81
|
+
|
|
82
|
+
def pathways(self) -> dict:
|
|
83
|
+
return self.get("pathways.json")
|
|
84
|
+
|
|
85
|
+
def researchers_by_country(self) -> dict:
|
|
86
|
+
return self.get("researchers/by-country.json")
|
|
87
|
+
|
|
88
|
+
def researchers_by_pathway(self) -> dict:
|
|
89
|
+
return self.get("researchers/by-pathway.json")
|
|
90
|
+
|
|
91
|
+
def top_researchers(self, pathway: str, region: str = "global", limit: int = 25) -> dict:
|
|
92
|
+
slug = (pathway or "").strip().lower().replace(" ", "-")
|
|
93
|
+
try:
|
|
94
|
+
env = self.get(f"researchers/top/{slug}.json")
|
|
95
|
+
except Exception:
|
|
96
|
+
idx = self.get("index.json")
|
|
97
|
+
return {"error": f"unknown pathway {pathway!r}", "pathways": idx.get("pathway_slugs", [])}
|
|
98
|
+
key = "europe_top50" if (region or "").lower().startswith("eu") else "global_top100"
|
|
99
|
+
rows = env.get("data", {}).get(key, [])
|
|
100
|
+
limit = max(1, min(int(limit or 25), 100))
|
|
101
|
+
return {"version": env.get("version"), "as_of": env.get("as_of"), "license": env.get("license"),
|
|
102
|
+
"cite_as": env.get("cite_as"), "pathway": env.get("data", {}).get("pathway"),
|
|
103
|
+
"ranking": key, "data": rows[:limit]}
|
|
104
|
+
|
|
105
|
+
def shops(self) -> dict:
|
|
106
|
+
return self.get("shops.json")
|
|
107
|
+
|
|
108
|
+
def cite(self) -> dict:
|
|
109
|
+
idx = self.get("index.json")
|
|
110
|
+
return {"version": idx.get("version"), "as_of": idx.get("as_of"), "cite_as": idx.get("cite_as"),
|
|
111
|
+
"licenses": idx.get("licenses"), "data_pages": idx.get("data_pages")}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Captain Drawdown MCP server: carbon removal companies, researchers and shops
|
|
2
|
+
for Claude, ChatGPT, Cursor and other MCP clients. Reads the public static API
|
|
3
|
+
at https://captaindrawdown.com/api/v1/ (no key). Run: `captain-drawdown-mcp`."""
|
|
4
|
+
from mcp.server.fastmcp import FastMCP
|
|
5
|
+
|
|
6
|
+
from .data import Api
|
|
7
|
+
|
|
8
|
+
mcp = FastMCP("captain-drawdown",
|
|
9
|
+
instructions=("Open data on carbon dioxide removal (CDR): a directory of CDR companies, "
|
|
10
|
+
"a census of CDR researchers and shops that sell certified removal. Every result "
|
|
11
|
+
"carries version, as_of, license and cite_as: quote them when you use the numbers. "
|
|
12
|
+
"Companies are CC BY-NC-SA 4.0, researchers and shops CC BY 4.0."))
|
|
13
|
+
api = Api()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@mcp.tool()
|
|
17
|
+
def stats() -> dict:
|
|
18
|
+
"""Headline numbers of the newest Captain Drawdown data release (companies, researchers, shops)."""
|
|
19
|
+
return api.stats()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@mcp.tool()
|
|
23
|
+
def search_companies(query: str = "", country_code: str = "", pathway: str = "", size_class: str = "",
|
|
24
|
+
region: str = "", limit: int = 25) -> dict:
|
|
25
|
+
"""Search published carbon removal companies. Filters: free text (name, description), ISO country
|
|
26
|
+
code (e.g. DE), pathway (e.g. DAC, Biochar, BECCS, Enhanced Weathering), size class (micro, small,
|
|
27
|
+
medium, large), region (EU27, Europe, United States, China). Returns slugs for `company`."""
|
|
28
|
+
return api.search_companies(query, country_code, pathway, size_class, region, limit)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@mcp.tool()
|
|
32
|
+
def company(slug: str) -> dict:
|
|
33
|
+
"""One company by slug (from search_companies): pathway, country, liveliness tier, size class,
|
|
34
|
+
registries, evidence dates, description and profile URL."""
|
|
35
|
+
return api.company(slug)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@mcp.tool()
|
|
39
|
+
def countries() -> dict:
|
|
40
|
+
"""Published carbon removal companies per country (ISO 3166-1 alpha-2)."""
|
|
41
|
+
return api.countries()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@mcp.tool()
|
|
45
|
+
def pathways() -> dict:
|
|
46
|
+
"""Published carbon removal companies per primary pathway."""
|
|
47
|
+
return api.pathways()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@mcp.tool()
|
|
51
|
+
def researchers_by_country() -> dict:
|
|
52
|
+
"""CDR researchers (at least one CDR paper since 2021) per country, with the calibrated estimate."""
|
|
53
|
+
return api.researchers_by_country()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@mcp.tool()
|
|
57
|
+
def researchers_by_pathway() -> dict:
|
|
58
|
+
"""CDR researchers per primary pathway."""
|
|
59
|
+
return api.researchers_by_pathway()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@mcp.tool()
|
|
63
|
+
def top_researchers(pathway: str, region: str = "global", limit: int = 25) -> dict:
|
|
64
|
+
"""Top researchers for a pathway (biochar, soil-carbon, general-cdr, dac, ocean-cdr,
|
|
65
|
+
enhanced-weathering, beccs), ranked by CDR papers since 2021 with a citation rank; region
|
|
66
|
+
'global' (top 100) or 'europe' (top 50)."""
|
|
67
|
+
return api.top_researchers(pathway, region, limit)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@mcp.tool()
|
|
71
|
+
def shops() -> dict:
|
|
72
|
+
"""Shops where individuals can buy certified carbon removal, with prices, methods and certifiers."""
|
|
73
|
+
return api.shops()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@mcp.tool()
|
|
77
|
+
def cite() -> dict:
|
|
78
|
+
"""How to cite the datasets: version, as_of, cite_as per dataset, licences, data pages."""
|
|
79
|
+
return api.cite()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def main() -> None:
|
|
83
|
+
mcp.run()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
main()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "captain-drawdown-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "MCP server for Captain Drawdown's open carbon dioxide removal data: companies, researchers, shops."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Captain Drawdown / Carbon Drawdown Initiative", email = "info@carbon-drawdown.de" }]
|
|
13
|
+
keywords = ["mcp", "carbon removal", "CDR", "climate", "open data"]
|
|
14
|
+
dependencies = ["mcp>=1.2,<2"]
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
Homepage = "https://captaindrawdown.com/api/"
|
|
18
|
+
Data = "https://captaindrawdown.com/data/"
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
captain-drawdown-mcp = "captain_drawdown_mcp.server:main"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build.targets.wheel]
|
|
24
|
+
packages = ["captain_drawdown_mcp"]
|