AI assistants (MCP)
Connect Claude, ChatGPT, Cursor or Claude Code to your Joulo account with the Joulo MCP server
The Joulo MCP server lets an AI assistant read your own Joulo data: your chargers, your charging sessions, your energy totals, your ERE position, your energy contract and today's and tomorrow's electricity prices. You can then ask questions such as "how much did I charge in September?" or "when is power cheapest tonight?" in plain language.
The server speaks the Model Context Protocol. Any assistant that supports remote MCP servers can use it.
https://joulo.nl/mcpThe tools only read. An assistant cannot change your account, start or stop a session, or touch your chargers through this server.
Connect your assistant
Claude
- Open Settings → Connectors and choose Add custom connector.
- Enter
https://joulo.nl/mcpas the URL. - Choose Connect. Claude opens joulo.nl. Sign in and allow access.
ChatGPT
ChatGPT adds a custom MCP server through developer mode. OpenAI offers it on paid plans and often renames the menus, so check the OpenAI help center if a step looks different.
- Open Settings → Apps. Under Advanced settings, turn on Developer mode. On a Business or Enterprise workspace, an admin enables it first.
- Choose Create app. Enter
Jouloas the name andhttps://joulo.nl/mcpas the URL, and choose OAuth as the authentication. - ChatGPT opens joulo.nl. Sign in and allow access.
- In a new chat, open + → Developer mode and select Joulo.
ChatGPT can ask you to confirm a tool call before it runs.
Claude Code
claude mcp add --transport http joulo https://joulo.nl/mcpThen run /mcp in Claude Code and choose joulo to sign in.
Other assistants
Add a custom connector or remote MCP server with the URL https://joulo.nl/mcp. The assistant registers itself, sends you to joulo.nl to sign in, and stores the token. For Cursor, add this to .cursor/mcp.json:
{
"mcpServers": {
"joulo": { "url": "https://joulo.nl/mcp" }
}
}With a personal API token
A client that cannot sign in with OAuth can send a personal API token as a header instead:
claude mcp add --transport http joulo https://joulo.nl/mcp --header "Authorization: Bearer YOUR_TOKEN"A personal token gives the assistant every tool. Partner API keys and CPO tokens do not work here.
Example: what did I earn this year?
Ask the question the way you would ask a person. This example uses ChatGPT and the sample data from the API reference.
You: How much have I earned with Joulo this year, and what is still on its way?
ChatGPT picks the tool that answers money questions and calls it:
{ "name": "get_ere_position", "arguments": { "year": 2026 } }Joulo returns your position for 2026. An excerpt:
{
"compliance_year": 2026,
"effective_fee_pct": 20,
"paid": { "ere": 120, "net_eur": 44.35 },
"payable": { "ere": 40, "net_eur": 14.78 },
"reserved": { "ere": 60, "net_eur": 22.12 },
"unsold": { "ere": 92.45, "forecast_net_eur": 33.36 },
"pending_sessions": 2,
"pending_forecast_net_eur": 3,
"total_expected_eur": 117.61
}ChatGPT: So far this year Joulo paid you € 44.35 for 120 ERE. Another € 14.78 is ready for your next quarterly payout, and € 22.12 is reserved in a sale. The 92 ERE that are not sold yet are worth about € 33 at today's price. Two recent sessions, worth about € 3, are still being checked. For 2026 you can expect about € 118 in total, after your 20% service fee.
The assistant answers in the language you write in. Ask in Dutch and you get Dutch.
A forecast of € 0 while the quarters show production means a step of your registration is still open. The position then carries blocked, with the step in reason (registration, mandate, ean, review or iban) and the ERE and euro amount waiting on it. The tool description tells the assistant to read that field, so it answers "387 ERE wait until you confirm that this station sits behind your connection" instead of "€ 0".
More questions to try
| Question | Tools the assistant uses |
|---|---|
| How many kWh did I charge in September? | list_charging_sessions |
| Which of my sessions last month did not count for ERE, and why? | list_charging_sessions, list_chargers |
| Is my charging station charging right now? | list_chargers |
| Show my kWh per month this year as a table. | get_energy_totals |
| What service fee do I pay? | get_account |
| What do I still have to do before Joulo pays me? | get_registration_status |
| When is power cheapest tomorrow? | get_price_curve |
| What does a kWh cost me tonight after my ERE? | get_price_curve |
| Which supplier and contract does Joulo have for me? | get_household |
| What do Joulo customers get per kWh? | get_ere_market |
| I need 30 kWh by 7:00. When should I charge? | find_cheapest_window |
| What did charging cost me in September, and what would smart charging have saved? | get_charging_costs |
| How do I connect my Alfen? | get_connection_guide |
| Does my charger need a MID meter? | read_kennisbank_article |
| How do I charge without a card again? | get_feature_guide |
Sign-in and permissions
The first time you connect, joulo.nl shows a consent screen. It names the assistant, says that Joulo has not reviewed it, and lists what it may read. The name on that screen is the name the assistant gave itself. Only allow access if you just started the connection yourself.
| Scope | What the assistant may read |
|---|---|
profile:read | Your name, email address and service fee, your energy contract, your household answers, your electricity prices and your charging costs |
locations:read | The postcode and city of your addresses. Never your street or house number. |
chargers:read | Your chargers and their status |
sessions:read | Your charging sessions |
energy:read | Your energy totals and ERE |
position:read | Your ERE reserve, sales, payouts and forecast, and where your registration stands |
To stop the access, open your dashboard at Developer → API → Connected apps and revoke the assistant. Its tokens stop working on the next request.
Tools
| Tool | Scope | Returns |
|---|---|---|
get_account | profile:read | Name, email, effective service fee and the indicative ERE price behind the estimates. Same data as GET /me. |
list_chargers | chargers:read | Your chargers with their brand and model, whether Joulo hears from each one, whether each one counts for ERE, and any session that charges right now. Same data as GET /chargers. |
list_charging_sessions | sessions:read | Sessions, newest first, filtered by period and charger, 100 per page at most. Same data as GET /sessions. |
get_energy_totals | energy:read | Lifetime and monthly kWh, ERE and session counts. Same data as GET /energy. |
get_ere_position | position:read | Reserved, sold, payable and paid ERE for one year, with the forecast per quarter. Same data as GET /ere-position. |
get_registration_status | position:read | Per step what is done and what is still open before Joulo can sell your ERE: a charger that counts, the form, the mandate, the EAN, Joulo's review and the IBAN. Same data as GET /registration-status. |
get_price_curve | profile:read | The electricity price of one day per hour or per 15 minutes: spot, the price on a dynamic contract with your supplier, your own price, and the price after ERE. Same data as GET /prices. Shows a chart in assistants that support MCP Apps or ChatGPT apps. |
get_household | profile:read | Your energy contract and your household answers. Same data as GET /household. |
list_locations | locations:read | The postcode and city of each of your addresses. Same data as GET /locations. |
find_cheapest_window | profile:read | When to charge a number of kWh most cheaply before a deadline: the cheapest slots, the cheapest stretch without a pause, and starting now, each with its cost. Same data as GET /prices/cheapest-window. |
get_charging_costs | profile:read | What charging cost over a period on your contract, what smart charging would have cost, and the ERE against it. Same data as GET /costs. |
get_ere_market | none | The one indicative ERE price for what is not sold yet, plus the customer average and the sales log as history. Same data as GET /ere-market. |
read_kennisbank_article | none | One article from the public kennisbank, with its live figures. Same data as GET /kennisbank-article. |
get_connection_guide | none | Joulo's manual for connecting a charger of one brand, with the link to it on joulo.nl/docs. Same data as GET /connection-guide. |
get_feature_guide | none | How a feature of the Joulo dashboard or app works: where it is, what it does, and what only the Joulo team can do. Same data as GET /feature-guide. |
list_charging_sessions takes from and to as an ISO 8601 date or date-time. A bare date means the start or the end of that day in Amsterdam, so from: 2026-09-01, to: 2026-09-30 covers all of September.
get_price_curve takes day (today, tomorrow or a date) and resolution (60 or 15 minutes). Tomorrow's prices exist after the daily publication in the early afternoon. Before that, the tool says when they come.
Every estimate uses one ERE price: the indicative price per ERE that your dashboard forecast uses. Realized sale prices appear only as history, in get_ere_market. A blocked step in get_ere_position comes with a link to the article that explains it and to the dashboard tab where you complete it.
The assistant sees only the tools your scopes allow. One exception: a connection made before locations:read existed still sees list_locations. The first call asks you to allow that scope on joulo.nl. Every tool call counts toward the same rate limit as a REST call.
For client developers
- Transport: Streamable HTTP. Send each JSON-RPC message with
POST. The server answers withapplication/jsonand keeps no session, soGETandDELETEreturn405. - Step-up: a call to a tool behind a scope added after you connected (now only
locations:read) answers403withWWW-Authenticate: Bearer error="insufficient_scope", scope="…". Thescopeparameter lists every scope the token holds plus the missing one, so request that whole set. In a JSON-RPC batch the same call returns a tool error instead. - Widgets:
get_price_curvecarries a UI resource. MCP Apps hosts read_meta.ui.resourceUri(ui://joulo/price-curve.html,text/html;profile=mcp-app). ChatGPT readsopenai/outputTemplate(ui://joulo/price-curve.skybridge.html,text/html+skybridge). Both serve the same HTML, which loads nothing from the network and reads the tool'sstructuredContent. The server lists both underresources/list. - Protocol versions:
2025-11-25,2025-06-18,2025-03-26and2024-11-05. - Discovery: a request without a valid token returns
401withWWW-Authenticate: Bearer resource_metadata="https://joulo.nl/.well-known/oauth-protected-resource/mcp". The authorization server metadata is athttps://joulo.nl/.well-known/oauth-authorization-server. - Client registration:
POST https://api.joulo.nl/functions/v1/oauth-register(RFC 7591). A client registers as public (token_endpoint_auth_method: none) unless it asks for a secret. A self-registered client can only hold the read scopes above. Redirect URIs must usehttps,httponlocalhost, or an app scheme. A client that nobody authorizes within 30 days is deleted. - Authorization: the code flow with PKCE (
S256). See OAuth2. Without ascopeparameter, a self-registered client gets all of its scopes, and the user decides on the consent screen.