aeso-mcp 0.2.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.
- aeso_mcp-0.2.0/LICENSE +21 -0
- aeso_mcp-0.2.0/PKG-INFO +325 -0
- aeso_mcp-0.2.0/README.md +289 -0
- aeso_mcp-0.2.0/pyproject.toml +144 -0
- aeso_mcp-0.2.0/pyproject.toml.orig +137 -0
- aeso_mcp-0.2.0/src/aeso_mcp/__init__.py +7 -0
- aeso_mcp-0.2.0/src/aeso_mcp/__main__.py +89 -0
- aeso_mcp-0.2.0/src/aeso_mcp/app.py +81 -0
- aeso_mcp-0.2.0/src/aeso_mcp/config.py +341 -0
- aeso_mcp-0.2.0/src/aeso_mcp/data/__init__.py +2 -0
- aeso_mcp-0.2.0/src/aeso_mcp/data/glossary.md +54 -0
- aeso_mcp-0.2.0/src/aeso_mcp/errors.py +58 -0
- aeso_mcp-0.2.0/src/aeso_mcp/http_runtime.py +375 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/__init__.py +6 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/errors.py +57 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/prompts/__init__.py +18 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/prompts/market.py +102 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/resources/__init__.py +22 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/resources/capabilities.py +88 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/resources/datasets.py +116 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/resources/glossary.py +39 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/resources/methodology.py +206 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/server.py +71 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/tools/__init__.py +14 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/tools/analytics.py +106 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/tools/grid.py +141 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/tools/market.py +123 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/tools/market_power.py +63 -0
- aeso_mcp-0.2.0/src/aeso_mcp/mcp/tools/operations.py +189 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/__init__.py +106 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/analytics.py +172 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/assets.py +43 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/common.py +125 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/generation.py +100 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/grid.py +99 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/market_power.py +72 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/operations.py +325 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/prices.py +67 -0
- aeso_mcp-0.2.0/src/aeso_mcp/models/transmission.py +78 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/__init__.py +18 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/aeso_apim.py +301 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/base.py +76 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/capabilities.py +91 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/csd.py +92 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/gridstatus.py +669 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/http.py +202 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/operations.py +564 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/public_reports.py +527 -0
- aeso_mcp-0.2.0/src/aeso_mcp/providers/public_reports_http.py +168 -0
- aeso_mcp-0.2.0/src/aeso_mcp/py.typed +1 -0
- aeso_mcp-0.2.0/src/aeso_mcp/server.py +6 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/__init__.py +18 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/analytics.py +540 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/assets.py +64 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/cache.py +220 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/grid.py +138 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/market.py +646 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/market_power.py +136 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/operations.py +875 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/transmission.py +128 -0
- aeso_mcp-0.2.0/src/aeso_mcp/services/ttl.py +29 -0
- aeso_mcp-0.2.0/src/aeso_mcp/timeutil.py +206 -0
aeso_mcp-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Brandon Choi
|
|
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.
|
aeso_mcp-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aeso-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Agent-native MCP server for Alberta electricity market data via official AESO APIs
|
|
5
|
+
Keywords: aeso,alberta,electricity,energy,mcp,model-context-protocol,grid
|
|
6
|
+
Author: Brandon Choi
|
|
7
|
+
Author-email: Brandon Choi <bchoi0824@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Dist: beautifulsoup4>=4.12.0,<5
|
|
18
|
+
Requires-Dist: fastmcp==4.0.0b3
|
|
19
|
+
Requires-Dist: gridstatus>=0.36.0,<0.37
|
|
20
|
+
Requires-Dist: httpx>=0.28.1,<1
|
|
21
|
+
Requires-Dist: pandas>=2.2.0,<3
|
|
22
|
+
Requires-Dist: pydantic>=2.11.0,<3
|
|
23
|
+
Requires-Dist: pydantic-settings>=2.8.0,<3
|
|
24
|
+
Requires-Dist: tenacity>=9.0.0,<10
|
|
25
|
+
Requires-Dist: tzdata>=2025.1
|
|
26
|
+
Requires-Dist: duckdb>=1.2.0,<2 ; extra == 'analytics'
|
|
27
|
+
Requires-Dist: pyarrow>=19.0.0,<22 ; extra == 'analytics'
|
|
28
|
+
Requires-Python: >=3.13
|
|
29
|
+
Project-URL: Homepage, https://github.com/bchoi-qwe/aeso-mcp
|
|
30
|
+
Project-URL: Documentation, https://github.com/bchoi-qwe/aeso-mcp#readme
|
|
31
|
+
Project-URL: Repository, https://github.com/bchoi-qwe/aeso-mcp
|
|
32
|
+
Project-URL: Issues, https://github.com/bchoi-qwe/aeso-mcp/issues
|
|
33
|
+
Project-URL: Changelog, https://github.com/bchoi-qwe/aeso-mcp/blob/main/CHANGELOG.md
|
|
34
|
+
Provides-Extra: analytics
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# aeso-mcp
|
|
38
|
+
|
|
39
|
+
<!-- mcp-name: io.github.bchoi-qwe/aeso-mcp -->
|
|
40
|
+
|
|
41
|
+
**Agent-native, strongly typed access and analytics for Alberta's electricity market using official AESO data.**
|
|
42
|
+
|
|
43
|
+
> Independent open-source project. **Not affiliated with or endorsed by the Alberta Electric System Operator (AESO).**
|
|
44
|
+
|
|
45
|
+
## What it is
|
|
46
|
+
|
|
47
|
+
`aeso-mcp` is a Model Context Protocol (MCP) server that exposes Alberta electricity-market observations and deterministic analytics to AI clients. It is designed for energy analysts, researchers, developers, journalists, market participants, and AI agents that need reliable, structured AESO data—not a thin REST decorator layer.
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- Typed MCP tools with Pydantic inputs/outputs and structured results
|
|
52
|
+
- Current market snapshot combining price, load, generation, interchange, and reserves
|
|
53
|
+
- Paginated historical Pool Price, System Marginal Price, load, and generation retrieval
|
|
54
|
+
- Authenticated APIM reports for merit order, commitments, capability/outages, interties,
|
|
55
|
+
metered volumes, and operating-reserve offer control
|
|
56
|
+
- Deterministic analytics: compact history summaries, period comparison, event detection,
|
|
57
|
+
condition evidence, forecast accuracy, and transparent supply-tightness indicators
|
|
58
|
+
- One complete server package and startup path: `AESO_API_KEY` is always required; there is no
|
|
59
|
+
reduced credential-free server mode
|
|
60
|
+
- Query bounds, cache provenance, completeness metadata, upstream `Retry-After` handling, and
|
|
61
|
+
secret-safe machine-readable errors
|
|
62
|
+
- Hardened HTTP transport with Host/Origin validation, optional bearer authentication, rate and
|
|
63
|
+
concurrency limits, request-size bounds, probes, and correlation IDs
|
|
64
|
+
- Reusable MCP prompts plus glossary, capability, dataset, and methodology resources
|
|
65
|
+
|
|
66
|
+
## Implemented datasets
|
|
67
|
+
|
|
68
|
+
| Dataset | Tool | Notes |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| Market snapshot | `get_market_snapshot` | Current cohesive view |
|
|
71
|
+
| Pool Price | `get_pool_prices` | Hourly CAD/MWh |
|
|
72
|
+
| System Marginal Price | `get_system_marginal_prices` | Minute-level CAD/MWh |
|
|
73
|
+
| Alberta Internal Load | `get_load` | MW; optional forecast |
|
|
74
|
+
| Generation / fuel mix | `get_generation` | Current all fuels; historical wind/solar |
|
|
75
|
+
| Interchange | `get_interchange` | Current path flows MW |
|
|
76
|
+
| Operating reserves | `get_reserves` | Current MW indicators |
|
|
77
|
+
| Generator outages | `get_outages` | Hourly outage capacity by fuel/technology |
|
|
78
|
+
| AIES capacity/outages | `get_generation_capacity` | Hourly MC, AC, operating, and mothball outage MW |
|
|
79
|
+
| Load outage forecast | `get_load_outage_forecast` | Hourly forecast MW |
|
|
80
|
+
| Energy Merit Order | `get_energy_merit_order` | Historical blocks; 60-day publication delay |
|
|
81
|
+
| Unit commitments | `get_unit_commitments` | Generating-unit commitment directives |
|
|
82
|
+
| Intertie capability | `get_intertie_capability` | Import/export ATC, TTC, margins, gross offers |
|
|
83
|
+
| Intertie capability outages | `get_intertie_outages` | Outages affecting interties/flowgates |
|
|
84
|
+
| Metered volumes | `get_metered_volumes` | Hourly MWh by asset; optional ID filters |
|
|
85
|
+
| OR offer control | `get_operating_reserve_offer_control` | Historical reserve offer blocks; 60-day delay |
|
|
86
|
+
| Approved Tx outages | `get_approved_transmission_outages` | AESO-approved planned transmission outages |
|
|
87
|
+
| Long-range Tx outages | `get_long_range_transmission_outages` | Tentative ~24-month significant outages |
|
|
88
|
+
| MCSINR | `get_monthly_cumulative_net_revenue` | Cumulative net revenue vs offer-cap trigger |
|
|
89
|
+
| Secondary offer limit | `get_secondary_offer_price_limit` | Whether secondary offer cap is in effect |
|
|
90
|
+
| Assets | `get_assets` | Registry with filters |
|
|
91
|
+
|
|
92
|
+
Analytics: `summarize_market_history`, `assess_supply_tightness`, `compare_market_periods`,
|
|
93
|
+
`find_price_events`, `explain_market_conditions`, and `compare_forecast_to_actual`.
|
|
94
|
+
|
|
95
|
+
## Architecture
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
MCP clients
|
|
99
|
+
|
|
|
100
|
+
v
|
|
101
|
+
FastMCP adapter (aeso_mcp/mcp)
|
|
102
|
+
|
|
|
103
|
+
v
|
|
104
|
+
Domain services (market, grid, assets, operations, analytics, transmission, market power)
|
|
105
|
+
|
|
|
106
|
+
+---------------------+------------------------+
|
|
107
|
+
| | |
|
|
108
|
+
v v v
|
|
109
|
+
GridStatus provider Direct AESO APIM Public-reports client
|
|
110
|
+
| | |
|
|
111
|
+
+----------+----------+ |
|
|
112
|
+
| |
|
|
113
|
+
v v
|
|
114
|
+
AESO APIM gateway ets.aeso.ca
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Domain code does not depend on FastMCP. Framework changes should stay in `aeso_mcp/mcp/`.
|
|
118
|
+
Both upstream clients are implementation details of this single server: the APIM key is sent only
|
|
119
|
+
to `apimgw.aeso.ca` and is never sent to the allow-listed `ets.aeso.ca` report host.
|
|
120
|
+
|
|
121
|
+
## Requirements
|
|
122
|
+
|
|
123
|
+
- Python 3.13+
|
|
124
|
+
- AESO APIM API key from [developer-apim.aeso.ca](https://developer-apim.aeso.ca/)
|
|
125
|
+
- [`uv`](https://docs.astral.sh/uv/) recommended
|
|
126
|
+
|
|
127
|
+
## Installation
|
|
128
|
+
|
|
129
|
+
See [LIMITATIONS.md](LIMITATIONS.md) for an honest gap inventory.
|
|
130
|
+
|
|
131
|
+
### From PyPI (recommended)
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
export AESO_API_KEY=your-key
|
|
135
|
+
uvx aeso-mcp
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### From GitHub
|
|
139
|
+
|
|
140
|
+
To run the current repository version directly:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
export AESO_API_KEY=your-key
|
|
144
|
+
uvx --from git+https://github.com/bchoi-qwe/aeso-mcp.git aeso-mcp
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Development
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
git clone https://github.com/bchoi-qwe/aeso-mcp.git
|
|
151
|
+
cd aeso-mcp
|
|
152
|
+
uv sync --group dev
|
|
153
|
+
cp .env.example .env # set AESO_API_KEY
|
|
154
|
+
uv run aeso-mcp
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Docker
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
docker build -t aeso-mcp .
|
|
161
|
+
docker run --rm -e AESO_API_KEY=your-key -p 8000:8000 aeso-mcp
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Obtaining an AESO API key
|
|
165
|
+
|
|
166
|
+
1. Register at the [AESO developer portal](https://developer-apim.aeso.ca/)
|
|
167
|
+
2. Subscribe to the AESO public API product
|
|
168
|
+
3. Copy the primary/secondary subscription key
|
|
169
|
+
4. Set `AESO_API_KEY` in your environment (never commit it)
|
|
170
|
+
|
|
171
|
+
Missing credentials produce an actionable startup error. The key is never returned through MCP tools or logged.
|
|
172
|
+
|
|
173
|
+
## Example MCP client configuration
|
|
174
|
+
|
|
175
|
+
### Cursor / Claude Desktop style (stdio)
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"mcpServers": {
|
|
180
|
+
"aeso": {
|
|
181
|
+
"command": "uvx",
|
|
182
|
+
"args": ["aeso-mcp"],
|
|
183
|
+
"env": {
|
|
184
|
+
"AESO_API_KEY": "your-key"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### HTTP transport
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
uv run aeso-mcp --transport http --host 127.0.0.1 --port 8000
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
HTTP always validates Host and Origin. For a remotely reachable deployment, explicitly set
|
|
198
|
+
`AESO_MCP_HTTP_ALLOWED_HOSTS` and `AESO_MCP_HTTP_ALLOWED_ORIGINS`; set
|
|
199
|
+
`AESO_MCP_HTTP_BEARER_TOKEN` to require bearer authentication. `/healthz` and `/readyz` contain no
|
|
200
|
+
market data or secrets. See [.env.example](.env.example) for all bounded runtime settings.
|
|
201
|
+
|
|
202
|
+
## Example prompts
|
|
203
|
+
|
|
204
|
+
- What is Alberta's current grid situation?
|
|
205
|
+
- What is the current pool price?
|
|
206
|
+
- Show Alberta pool prices over the last 24 hours.
|
|
207
|
+
- Compare today's pool prices with yesterday's.
|
|
208
|
+
- Which hours had the highest prices this week?
|
|
209
|
+
- How much wind and solar are producing right now?
|
|
210
|
+
- What happened during the largest price spike this week?
|
|
211
|
+
- Explain the evidence associated with today's price increase.
|
|
212
|
+
|
|
213
|
+
## Tools
|
|
214
|
+
|
|
215
|
+
| Tool | Purpose |
|
|
216
|
+
| --- | --- |
|
|
217
|
+
| `get_market_snapshot` | Current market overview |
|
|
218
|
+
| `get_pool_prices` | Hourly Pool Price history |
|
|
219
|
+
| `get_system_marginal_prices` | Minute-level SMP history |
|
|
220
|
+
| `get_load` | Alberta Internal Load |
|
|
221
|
+
| `get_generation` | Fuel mix / renewable history |
|
|
222
|
+
| `get_interchange` | Intertie flows |
|
|
223
|
+
| `get_reserves` | Operating reserve indicators |
|
|
224
|
+
| `get_outages` | Hourly generator outage capacity by fuel |
|
|
225
|
+
| `get_generation_capacity` | AIES capability and outage grouping by fuel |
|
|
226
|
+
| `get_load_outage_forecast` | Hourly load-outage forecast |
|
|
227
|
+
| `get_energy_merit_order` | Historical energy merit-order blocks |
|
|
228
|
+
| `get_unit_commitments` | Generating-unit commitment directives |
|
|
229
|
+
| `get_intertie_capability` | Intertie/flowgate ATC, TTC, and margins |
|
|
230
|
+
| `get_intertie_outages` | Outages affecting intertie capability |
|
|
231
|
+
| `get_metered_volumes` | Metered energy by asset |
|
|
232
|
+
| `get_operating_reserve_offer_control` | Historical reserve offer-control blocks |
|
|
233
|
+
| `get_approved_transmission_outages` | Approved planned transmission outages |
|
|
234
|
+
| `get_long_range_transmission_outages` | Tentative long-range transmission outages |
|
|
235
|
+
| `get_assets` | Asset registry |
|
|
236
|
+
| `get_monthly_cumulative_net_revenue` | Current MCSINR publication |
|
|
237
|
+
| `get_secondary_offer_price_limit` | Current secondary offer-cap status |
|
|
238
|
+
| `compare_market_periods` | Aggregate period comparison |
|
|
239
|
+
| `find_price_events` | High-price event detection |
|
|
240
|
+
| `explain_market_conditions` | Structured evidence (not causal prose) |
|
|
241
|
+
| `compare_forecast_to_actual` | AIL forecast vs actual accuracy |
|
|
242
|
+
| `summarize_market_history` | Compact hourly/daily/weekly/monthly price and load summaries |
|
|
243
|
+
| `assess_supply_tightness` | Transparent reserve-adjusted supply-margin screening |
|
|
244
|
+
|
|
245
|
+
All tools are read-only, non-destructive, and network-dependent.
|
|
246
|
+
|
|
247
|
+
## Resources
|
|
248
|
+
|
|
249
|
+
| URI | Content |
|
|
250
|
+
| --- | --- |
|
|
251
|
+
| `aeso://glossary` | Market terminology |
|
|
252
|
+
| `aeso://datasets` | Dataset catalog |
|
|
253
|
+
| `aeso://methodology/pool-price` | Pool Price interpretation |
|
|
254
|
+
| `aeso://methodology/system-marginal-price` | SMP interpretation |
|
|
255
|
+
| `aeso://capabilities` | Complete tool, prompt, and resource surface |
|
|
256
|
+
| `aeso://methodology/{dataset}` | Dataset-specific interpretation and caveats |
|
|
257
|
+
|
|
258
|
+
Prompts: `daily_market_brief`, `investigate_price_event`, and `compare_market_days`.
|
|
259
|
+
|
|
260
|
+
## Data semantics
|
|
261
|
+
|
|
262
|
+
- **Timezone**: `America/Edmonton` (AESO market time). DST days may have 23 or 25 local hours.
|
|
263
|
+
- **Intervals**: Explicit `interval_start` / `interval_end` (half-open ranges in requests).
|
|
264
|
+
- **Units**: Pool Price / SMP → CAD/MWh; load / generation / interchange / reserves → MW.
|
|
265
|
+
- **Status**: Metadata includes `actual` / `forecast` / etc. Forecasts are never implied to be settled actuals.
|
|
266
|
+
- **Finality**: Operational feeds may be preliminary; do not assume final settlement.
|
|
267
|
+
- **Completeness**: Metadata reports available/missing series and expected/missing observations
|
|
268
|
+
where the source cadence is known. Optional enrichment failures are surfaced as partial or
|
|
269
|
+
degraded results with warnings.
|
|
270
|
+
- **Pagination**: Raw price, SMP, load, generation, and operational reports return `page` metadata
|
|
271
|
+
with `next_offset`. Use `summarize_market_history` before retrieving long raw series.
|
|
272
|
+
- **Cache timing**: `retrieved_at` identifies the upstream fetch; `served_at`, `cache_hit`, and
|
|
273
|
+
`cache_age` identify when and how the response was served.
|
|
274
|
+
|
|
275
|
+
## Development
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
uv sync --group dev
|
|
279
|
+
uv run ruff check src tests
|
|
280
|
+
uv run pyright src
|
|
281
|
+
uv run pytest tests/unit tests/contract tests/mcp --cov=aeso_mcp
|
|
282
|
+
uv build
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Optional live tests:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
AESO_API_KEY=... uv run pytest tests/integration -m integration
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
MCP Inspector:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
# Prefer the console entrypoint; or point Inspector at:
|
|
295
|
+
# uv run aeso-mcp
|
|
296
|
+
npx @modelcontextprotocol/inspector uv run aeso-mcp
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Tests
|
|
300
|
+
|
|
301
|
+
- `tests/unit` — time, bounds, analytics, config
|
|
302
|
+
- `tests/contract` — AESO APIM fixtures via `respx`
|
|
303
|
+
- `tests/mcp` — tool/resource discovery and structured outputs
|
|
304
|
+
- `tests/integration` — opt-in live AESO calls
|
|
305
|
+
|
|
306
|
+
## Security
|
|
307
|
+
|
|
308
|
+
See [SECURITY.md](SECURITY.md). Highlights: no arbitrary URL/shell/SQL tools, host allow-list, secret hygiene, bounded queries, stderr logging for stdio.
|
|
309
|
+
|
|
310
|
+
## Roadmap
|
|
311
|
+
|
|
312
|
+
- Optional DuckDB/Parquet historical analytics store
|
|
313
|
+
- Broader forecast vs actual tools
|
|
314
|
+
|
|
315
|
+
## Contributing
|
|
316
|
+
|
|
317
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
318
|
+
|
|
319
|
+
## License
|
|
320
|
+
|
|
321
|
+
MIT — see [LICENSE](LICENSE).
|
|
322
|
+
|
|
323
|
+
## Disclaimer
|
|
324
|
+
|
|
325
|
+
This project is an independent open-source interface to publicly documented AESO APIs. It is **not** an official AESO product and is **not affiliated with or endorsed by AESO**. Market data may be preliminary or incomplete; verify critical decisions against official AESO publications.
|
aeso_mcp-0.2.0/README.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# aeso-mcp
|
|
2
|
+
|
|
3
|
+
<!-- mcp-name: io.github.bchoi-qwe/aeso-mcp -->
|
|
4
|
+
|
|
5
|
+
**Agent-native, strongly typed access and analytics for Alberta's electricity market using official AESO data.**
|
|
6
|
+
|
|
7
|
+
> Independent open-source project. **Not affiliated with or endorsed by the Alberta Electric System Operator (AESO).**
|
|
8
|
+
|
|
9
|
+
## What it is
|
|
10
|
+
|
|
11
|
+
`aeso-mcp` is a Model Context Protocol (MCP) server that exposes Alberta electricity-market observations and deterministic analytics to AI clients. It is designed for energy analysts, researchers, developers, journalists, market participants, and AI agents that need reliable, structured AESO data—not a thin REST decorator layer.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Typed MCP tools with Pydantic inputs/outputs and structured results
|
|
16
|
+
- Current market snapshot combining price, load, generation, interchange, and reserves
|
|
17
|
+
- Paginated historical Pool Price, System Marginal Price, load, and generation retrieval
|
|
18
|
+
- Authenticated APIM reports for merit order, commitments, capability/outages, interties,
|
|
19
|
+
metered volumes, and operating-reserve offer control
|
|
20
|
+
- Deterministic analytics: compact history summaries, period comparison, event detection,
|
|
21
|
+
condition evidence, forecast accuracy, and transparent supply-tightness indicators
|
|
22
|
+
- One complete server package and startup path: `AESO_API_KEY` is always required; there is no
|
|
23
|
+
reduced credential-free server mode
|
|
24
|
+
- Query bounds, cache provenance, completeness metadata, upstream `Retry-After` handling, and
|
|
25
|
+
secret-safe machine-readable errors
|
|
26
|
+
- Hardened HTTP transport with Host/Origin validation, optional bearer authentication, rate and
|
|
27
|
+
concurrency limits, request-size bounds, probes, and correlation IDs
|
|
28
|
+
- Reusable MCP prompts plus glossary, capability, dataset, and methodology resources
|
|
29
|
+
|
|
30
|
+
## Implemented datasets
|
|
31
|
+
|
|
32
|
+
| Dataset | Tool | Notes |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| Market snapshot | `get_market_snapshot` | Current cohesive view |
|
|
35
|
+
| Pool Price | `get_pool_prices` | Hourly CAD/MWh |
|
|
36
|
+
| System Marginal Price | `get_system_marginal_prices` | Minute-level CAD/MWh |
|
|
37
|
+
| Alberta Internal Load | `get_load` | MW; optional forecast |
|
|
38
|
+
| Generation / fuel mix | `get_generation` | Current all fuels; historical wind/solar |
|
|
39
|
+
| Interchange | `get_interchange` | Current path flows MW |
|
|
40
|
+
| Operating reserves | `get_reserves` | Current MW indicators |
|
|
41
|
+
| Generator outages | `get_outages` | Hourly outage capacity by fuel/technology |
|
|
42
|
+
| AIES capacity/outages | `get_generation_capacity` | Hourly MC, AC, operating, and mothball outage MW |
|
|
43
|
+
| Load outage forecast | `get_load_outage_forecast` | Hourly forecast MW |
|
|
44
|
+
| Energy Merit Order | `get_energy_merit_order` | Historical blocks; 60-day publication delay |
|
|
45
|
+
| Unit commitments | `get_unit_commitments` | Generating-unit commitment directives |
|
|
46
|
+
| Intertie capability | `get_intertie_capability` | Import/export ATC, TTC, margins, gross offers |
|
|
47
|
+
| Intertie capability outages | `get_intertie_outages` | Outages affecting interties/flowgates |
|
|
48
|
+
| Metered volumes | `get_metered_volumes` | Hourly MWh by asset; optional ID filters |
|
|
49
|
+
| OR offer control | `get_operating_reserve_offer_control` | Historical reserve offer blocks; 60-day delay |
|
|
50
|
+
| Approved Tx outages | `get_approved_transmission_outages` | AESO-approved planned transmission outages |
|
|
51
|
+
| Long-range Tx outages | `get_long_range_transmission_outages` | Tentative ~24-month significant outages |
|
|
52
|
+
| MCSINR | `get_monthly_cumulative_net_revenue` | Cumulative net revenue vs offer-cap trigger |
|
|
53
|
+
| Secondary offer limit | `get_secondary_offer_price_limit` | Whether secondary offer cap is in effect |
|
|
54
|
+
| Assets | `get_assets` | Registry with filters |
|
|
55
|
+
|
|
56
|
+
Analytics: `summarize_market_history`, `assess_supply_tightness`, `compare_market_periods`,
|
|
57
|
+
`find_price_events`, `explain_market_conditions`, and `compare_forecast_to_actual`.
|
|
58
|
+
|
|
59
|
+
## Architecture
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
MCP clients
|
|
63
|
+
|
|
|
64
|
+
v
|
|
65
|
+
FastMCP adapter (aeso_mcp/mcp)
|
|
66
|
+
|
|
|
67
|
+
v
|
|
68
|
+
Domain services (market, grid, assets, operations, analytics, transmission, market power)
|
|
69
|
+
|
|
|
70
|
+
+---------------------+------------------------+
|
|
71
|
+
| | |
|
|
72
|
+
v v v
|
|
73
|
+
GridStatus provider Direct AESO APIM Public-reports client
|
|
74
|
+
| | |
|
|
75
|
+
+----------+----------+ |
|
|
76
|
+
| |
|
|
77
|
+
v v
|
|
78
|
+
AESO APIM gateway ets.aeso.ca
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Domain code does not depend on FastMCP. Framework changes should stay in `aeso_mcp/mcp/`.
|
|
82
|
+
Both upstream clients are implementation details of this single server: the APIM key is sent only
|
|
83
|
+
to `apimgw.aeso.ca` and is never sent to the allow-listed `ets.aeso.ca` report host.
|
|
84
|
+
|
|
85
|
+
## Requirements
|
|
86
|
+
|
|
87
|
+
- Python 3.13+
|
|
88
|
+
- AESO APIM API key from [developer-apim.aeso.ca](https://developer-apim.aeso.ca/)
|
|
89
|
+
- [`uv`](https://docs.astral.sh/uv/) recommended
|
|
90
|
+
|
|
91
|
+
## Installation
|
|
92
|
+
|
|
93
|
+
See [LIMITATIONS.md](LIMITATIONS.md) for an honest gap inventory.
|
|
94
|
+
|
|
95
|
+
### From PyPI (recommended)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
export AESO_API_KEY=your-key
|
|
99
|
+
uvx aeso-mcp
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### From GitHub
|
|
103
|
+
|
|
104
|
+
To run the current repository version directly:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
export AESO_API_KEY=your-key
|
|
108
|
+
uvx --from git+https://github.com/bchoi-qwe/aeso-mcp.git aeso-mcp
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Development
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git clone https://github.com/bchoi-qwe/aeso-mcp.git
|
|
115
|
+
cd aeso-mcp
|
|
116
|
+
uv sync --group dev
|
|
117
|
+
cp .env.example .env # set AESO_API_KEY
|
|
118
|
+
uv run aeso-mcp
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Docker
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
docker build -t aeso-mcp .
|
|
125
|
+
docker run --rm -e AESO_API_KEY=your-key -p 8000:8000 aeso-mcp
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Obtaining an AESO API key
|
|
129
|
+
|
|
130
|
+
1. Register at the [AESO developer portal](https://developer-apim.aeso.ca/)
|
|
131
|
+
2. Subscribe to the AESO public API product
|
|
132
|
+
3. Copy the primary/secondary subscription key
|
|
133
|
+
4. Set `AESO_API_KEY` in your environment (never commit it)
|
|
134
|
+
|
|
135
|
+
Missing credentials produce an actionable startup error. The key is never returned through MCP tools or logged.
|
|
136
|
+
|
|
137
|
+
## Example MCP client configuration
|
|
138
|
+
|
|
139
|
+
### Cursor / Claude Desktop style (stdio)
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"mcpServers": {
|
|
144
|
+
"aeso": {
|
|
145
|
+
"command": "uvx",
|
|
146
|
+
"args": ["aeso-mcp"],
|
|
147
|
+
"env": {
|
|
148
|
+
"AESO_API_KEY": "your-key"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### HTTP transport
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv run aeso-mcp --transport http --host 127.0.0.1 --port 8000
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
HTTP always validates Host and Origin. For a remotely reachable deployment, explicitly set
|
|
162
|
+
`AESO_MCP_HTTP_ALLOWED_HOSTS` and `AESO_MCP_HTTP_ALLOWED_ORIGINS`; set
|
|
163
|
+
`AESO_MCP_HTTP_BEARER_TOKEN` to require bearer authentication. `/healthz` and `/readyz` contain no
|
|
164
|
+
market data or secrets. See [.env.example](.env.example) for all bounded runtime settings.
|
|
165
|
+
|
|
166
|
+
## Example prompts
|
|
167
|
+
|
|
168
|
+
- What is Alberta's current grid situation?
|
|
169
|
+
- What is the current pool price?
|
|
170
|
+
- Show Alberta pool prices over the last 24 hours.
|
|
171
|
+
- Compare today's pool prices with yesterday's.
|
|
172
|
+
- Which hours had the highest prices this week?
|
|
173
|
+
- How much wind and solar are producing right now?
|
|
174
|
+
- What happened during the largest price spike this week?
|
|
175
|
+
- Explain the evidence associated with today's price increase.
|
|
176
|
+
|
|
177
|
+
## Tools
|
|
178
|
+
|
|
179
|
+
| Tool | Purpose |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| `get_market_snapshot` | Current market overview |
|
|
182
|
+
| `get_pool_prices` | Hourly Pool Price history |
|
|
183
|
+
| `get_system_marginal_prices` | Minute-level SMP history |
|
|
184
|
+
| `get_load` | Alberta Internal Load |
|
|
185
|
+
| `get_generation` | Fuel mix / renewable history |
|
|
186
|
+
| `get_interchange` | Intertie flows |
|
|
187
|
+
| `get_reserves` | Operating reserve indicators |
|
|
188
|
+
| `get_outages` | Hourly generator outage capacity by fuel |
|
|
189
|
+
| `get_generation_capacity` | AIES capability and outage grouping by fuel |
|
|
190
|
+
| `get_load_outage_forecast` | Hourly load-outage forecast |
|
|
191
|
+
| `get_energy_merit_order` | Historical energy merit-order blocks |
|
|
192
|
+
| `get_unit_commitments` | Generating-unit commitment directives |
|
|
193
|
+
| `get_intertie_capability` | Intertie/flowgate ATC, TTC, and margins |
|
|
194
|
+
| `get_intertie_outages` | Outages affecting intertie capability |
|
|
195
|
+
| `get_metered_volumes` | Metered energy by asset |
|
|
196
|
+
| `get_operating_reserve_offer_control` | Historical reserve offer-control blocks |
|
|
197
|
+
| `get_approved_transmission_outages` | Approved planned transmission outages |
|
|
198
|
+
| `get_long_range_transmission_outages` | Tentative long-range transmission outages |
|
|
199
|
+
| `get_assets` | Asset registry |
|
|
200
|
+
| `get_monthly_cumulative_net_revenue` | Current MCSINR publication |
|
|
201
|
+
| `get_secondary_offer_price_limit` | Current secondary offer-cap status |
|
|
202
|
+
| `compare_market_periods` | Aggregate period comparison |
|
|
203
|
+
| `find_price_events` | High-price event detection |
|
|
204
|
+
| `explain_market_conditions` | Structured evidence (not causal prose) |
|
|
205
|
+
| `compare_forecast_to_actual` | AIL forecast vs actual accuracy |
|
|
206
|
+
| `summarize_market_history` | Compact hourly/daily/weekly/monthly price and load summaries |
|
|
207
|
+
| `assess_supply_tightness` | Transparent reserve-adjusted supply-margin screening |
|
|
208
|
+
|
|
209
|
+
All tools are read-only, non-destructive, and network-dependent.
|
|
210
|
+
|
|
211
|
+
## Resources
|
|
212
|
+
|
|
213
|
+
| URI | Content |
|
|
214
|
+
| --- | --- |
|
|
215
|
+
| `aeso://glossary` | Market terminology |
|
|
216
|
+
| `aeso://datasets` | Dataset catalog |
|
|
217
|
+
| `aeso://methodology/pool-price` | Pool Price interpretation |
|
|
218
|
+
| `aeso://methodology/system-marginal-price` | SMP interpretation |
|
|
219
|
+
| `aeso://capabilities` | Complete tool, prompt, and resource surface |
|
|
220
|
+
| `aeso://methodology/{dataset}` | Dataset-specific interpretation and caveats |
|
|
221
|
+
|
|
222
|
+
Prompts: `daily_market_brief`, `investigate_price_event`, and `compare_market_days`.
|
|
223
|
+
|
|
224
|
+
## Data semantics
|
|
225
|
+
|
|
226
|
+
- **Timezone**: `America/Edmonton` (AESO market time). DST days may have 23 or 25 local hours.
|
|
227
|
+
- **Intervals**: Explicit `interval_start` / `interval_end` (half-open ranges in requests).
|
|
228
|
+
- **Units**: Pool Price / SMP → CAD/MWh; load / generation / interchange / reserves → MW.
|
|
229
|
+
- **Status**: Metadata includes `actual` / `forecast` / etc. Forecasts are never implied to be settled actuals.
|
|
230
|
+
- **Finality**: Operational feeds may be preliminary; do not assume final settlement.
|
|
231
|
+
- **Completeness**: Metadata reports available/missing series and expected/missing observations
|
|
232
|
+
where the source cadence is known. Optional enrichment failures are surfaced as partial or
|
|
233
|
+
degraded results with warnings.
|
|
234
|
+
- **Pagination**: Raw price, SMP, load, generation, and operational reports return `page` metadata
|
|
235
|
+
with `next_offset`. Use `summarize_market_history` before retrieving long raw series.
|
|
236
|
+
- **Cache timing**: `retrieved_at` identifies the upstream fetch; `served_at`, `cache_hit`, and
|
|
237
|
+
`cache_age` identify when and how the response was served.
|
|
238
|
+
|
|
239
|
+
## Development
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
uv sync --group dev
|
|
243
|
+
uv run ruff check src tests
|
|
244
|
+
uv run pyright src
|
|
245
|
+
uv run pytest tests/unit tests/contract tests/mcp --cov=aeso_mcp
|
|
246
|
+
uv build
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Optional live tests:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
AESO_API_KEY=... uv run pytest tests/integration -m integration
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
MCP Inspector:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Prefer the console entrypoint; or point Inspector at:
|
|
259
|
+
# uv run aeso-mcp
|
|
260
|
+
npx @modelcontextprotocol/inspector uv run aeso-mcp
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Tests
|
|
264
|
+
|
|
265
|
+
- `tests/unit` — time, bounds, analytics, config
|
|
266
|
+
- `tests/contract` — AESO APIM fixtures via `respx`
|
|
267
|
+
- `tests/mcp` — tool/resource discovery and structured outputs
|
|
268
|
+
- `tests/integration` — opt-in live AESO calls
|
|
269
|
+
|
|
270
|
+
## Security
|
|
271
|
+
|
|
272
|
+
See [SECURITY.md](SECURITY.md). Highlights: no arbitrary URL/shell/SQL tools, host allow-list, secret hygiene, bounded queries, stderr logging for stdio.
|
|
273
|
+
|
|
274
|
+
## Roadmap
|
|
275
|
+
|
|
276
|
+
- Optional DuckDB/Parquet historical analytics store
|
|
277
|
+
- Broader forecast vs actual tools
|
|
278
|
+
|
|
279
|
+
## Contributing
|
|
280
|
+
|
|
281
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
MIT — see [LICENSE](LICENSE).
|
|
286
|
+
|
|
287
|
+
## Disclaimer
|
|
288
|
+
|
|
289
|
+
This project is an independent open-source interface to publicly documented AESO APIs. It is **not** an official AESO product and is **not affiliated with or endorsed by AESO**. Market data may be preliminary or incomplete; verify critical decisions against official AESO publications.
|