A "deep research" agent for the Spanish electricity market. Ask it a question in plain language; it autonomously decides which Spanish grid data to fetch, calls the spanish-grid-mcp server over MCP, iterates as needed, and produces a written analysis with sources.
Two implementations live side by side in this repo:
agent.py— Hand-rolled Claude SDK loop (~150 lines). Deliberately framework-free, showing the raw tool-use pattern.agent_pydantic.py— Built on Pydantic AI. UsesMCPToolsetfor MCP connectivity andstream_textfor live output.streamlit_app.py— Chat UI built onagent_pydantic.py. Renders tool calls, live text, and conversation history in the browser.
Examples of questions it's built for:
- "Why were Spanish day-ahead prices high on 2026-05-20?"
- "How did the wind generation collapse on 2025-11-12 affect prices?"
- "Compare PVPC and wholesale prices for the last 30 days — when is PVPC most divergent?"
Your question
│
├── agent.py (stdio MCP)
│ └── spawns spanish-grid-mcp as subprocess → ESIOS / REE / AEMET
│
├── agent_pydantic.py (stdio or HTTP MCP)
│ ├── stdio: spawns spanish-grid-mcp as subprocess
│ └── HTTP: connects to spanish-grid-mcp at MCP_SERVER_URL
│
└── streamlit_app.py (chat UI → agent_pydantic.py)
└── same transport options as agent_pydantic.py
The agentic part is the loop itself — Claude decides which tool to call next based on what it's learned so far. No hand-coded data-gathering pipeline.
All data comes from the spanish-grid-mcp server, which wraps ESIOS, REE apidatos, and AEMET OpenData behind an MCP interface.
git clone https://github.com/raulrc/spanish-grid-research-agent.git
cd spanish-grid-research-agent
uv sync --extra ui # Streamlit is optional — omit --extra ui for CLI-only
cp .env.example .env
# edit .env: add ANTHROPIC_API_KEY, ESIOS_TOKEN, AEMET_TOKENFor the stdio path (agent.py or agent_pydantic.py without MCP_SERVER_URL), the spanish-grid-mcp package must be importable:
git clone https://github.com/raulrc/spanish-grid-mcp.git ../spanish-grid-mcp
uv pip install -e ../spanish-grid-mcpFor the HTTP path, you only need a running MCP server:
MCP_SERVER_URL=http://localhost:8000/mcp python agent_pydantic.py "..."# Hand-rolled loop (stdio only)
python agent.py "Why were Spanish day-ahead prices high on 2026-05-20?"
# Pydantic AI (stdio)
python agent_pydantic.py "Why were Spanish day-ahead prices high on 2026-05-20?"
# Pydantic AI (HTTP — MCP server running elsewhere)
MCP_SERVER_URL=http://spanish-grid-mcp:8000/mcp python agent_pydantic.py "..."
# Streamlit UI (stdio MCP — spawns subprocess automatically)
streamlit run streamlit_app.py
# Streamlit UI (HTTP MCP — server running elsewhere)
MCP_SERVER_URL=http://localhost:8000/mcp streamlit run streamlit_app.pyAll three entry points stream tool calls live, then produce the analysis.
agent.py — An explicit agent loop with no framework dependency (~150 lines). The while True + tool dispatch is the whole pattern. Uses prompt caching on the system prompt + tool definitions; after the first iteration each subsequent loop step reuses the cached prefix (~10% of full price). Uses adaptive thinking with summarized display — reasoning summaries stream live and each thinking step is cached individually.
agent_pydantic.py — A Pydantic AI implementation that demonstrates the framework's MCP integration, live event streaming, and usage tracking. Switches between stdio and Streamable HTTP based on the MCP_SERVER_URL env var. Uses MCPToolset (the recommended API replacing MCPServerStdio) and stream_text(delta=True) for live output.
streamlit_app.py — A chat UI that wraps run_agent() from agent_pydantic.py with conversation history, live text streaming, tool call log, and usage stats. Uses a background thread to bridge the async agent into Streamlit's sync event loop.
| Env var | Default | Notes |
|---|---|---|
ANTHROPIC_API_KEY |
— | Required. |
MCP_SERVER_URL |
— | HTTP endpoint for the MCP server (e.g. http://host:8000/mcp). If set, connects over Streamable HTTP instead of spawning a subprocess. |
MCP_SERVER_CMD |
python -m spanish_grid_mcp.server |
Command that launches the MCP server subprocess (stdio mode only). |
AGENT_MODEL |
claude-opus-4-7 |
Override to a cheaper model like claude-sonnet-4-6. |
AGENT_MAX_STEPS |
20 |
Hard ceiling on loop iterations (both agents). |
AGENT_MAX_TOKENS |
8192 |
Max output tokens per response (agent_pydantic.py only). Increase if using extended thinking with smaller models. |
ESIOS_TOKEN, AEMET_TOKEN |
— | Forwarded to the MCP server subprocess. |
The spanish-grid-mcp server also accepts --transport streamable-http to run as an HTTP server instead of stdio.
The MCP server is wired to real data from ESIOS, REE apidatos, and AEMET OpenData. The agent can fetch actual prices, demand, generation mix, CO₂ intensity, and weather observations.
get_cross_border_flowsreturns the demand envelope rather than actual interconnection flows — the REE per-border endpoint returns 500 errors.
