Skip to main content
Glama
HiDiHo01

Frank Energie MCP

by HiDiHo01

Frank Energie MCP

Standalone Model Context Protocol server built with FastMCP and backed exclusively by python-frank-energie.

Development status: The stdio server and MCP registrations exist, but the implementation is not yet production-ready.

Installation

Requires Python 3.14 or newer.

python -m pip install -e ".[dev]"
python -m frank_energie_mcp

The second command starts the stdio MCP transport. It waits for a compatible MCP client; it is not an interactive terminal interface.

Example client configuration (adapt the Python executable to the environment where the package is installed):

{
  "mcpServers": {
    "frank-energie": {
      "command": "python",
      "args": ["-m", "frank_energie_mcp"]
    }
  }
}

Related MCP server: Enapter MCP Server

Windows: local Streamable HTTP

Install Python 3.14 or newer, then open PowerShell:

git clone https://github.com/HiDiHo01/frank-energie-mcp.git
cd frank-energie-mcp
py -3.14 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
$env:FRANK_ENERGIE_MCP_TRANSPORT = "streamable-http"
$env:FRANK_ENERGIE_MCP_HOST = "127.0.0.1"
$env:FRANK_ENERGIE_MCP_PORT = "8000"
python -m frank_energie_mcp

The local MCP endpoint is http://127.0.0.1:8000/mcp. This is an MCP protocol endpoint, not a web page; opening it in a browser is not a valid tool test. Test with an MCP-compatible client. The process must remain running while connected.

Security: The server binds to loopback only and does not provide inbound authentication. Do not expose this endpoint with router port forwarding, a public reverse proxy, or an unauthenticated tunnel. A ChatGPT connection requires a separately configured, authenticated HTTPS transport/tunnel and compatible ChatGPT custom MCP support; neither is established by these commands. Avoid including account credentials in URLs, scripts, or committed files.

Implemented MCP registrations

Kind

Name

Current behavior

Tool

get_charging_cost_breakdown

Summarize the optimal charging schedule with per-interval energy and cost, energy losses, occupied slot minutes, average grid price, and negative-price contribution.

Tool

get_charging_efficiency_analysis

Compare 1–12 charging efficiencies for fixed energy, power and deadline; report grid energy, losses, feasibility, optimal schedule, and estimated costs.

Tool

get_charging_power_analysis

Compare 1–12 charging power levels for a fixed energy target and deadline using published prices; return feasibility, estimated costs, schedules, and difference from cheapest feasible option.

Tool

get_current_prices

Returns current electricity and gas intervals via upstream current_hour

Tool

get_today_prices

Fetches today's market prices

Tool

get_tomorrow_prices

Fetches tomorrow's market prices

Tool

get_average_prices

Averages available normalized total prices for electricity and gas for an optional Amsterdam calendar date and PT15M/PT60M resolution; reports interval count, first/last timestamps, coverage seconds, and completion status.

Tool

get_lowest_price

Lowest available normalized total interval per energy type, with coverage status.

Tool

get_highest_price

Highest available normalized total interval per energy type, with coverage status.

Tool

get_lowest_period

Finds the cheapest uninterrupted window by average normalized total price.

Tool

get_best_start_time

Cheapest contiguous electricity window and optional cost estimate for constant-rate consumption.

Tool

get_highest_period

Finds the most expensive uninterrupted window by average normalized total price.

Tool

get_gas_price

Returns the current gas interval or null when unavailable

Tool

get_account

Requires credentials and a site reference; returns only {"authenticated": true} after a successful account request

Tool

list_sites

Requires credentials; returns only the number of recognized delivery sites

Resource

frank-energie://capabilities

Static description of capabilities

Prompt

summarize_energy_prices

Static instructions for comparing prices

Price requests use the Europe/Amsterdam calendar date. Price tools use the bounded, privacy-safe JSON schema documented in MCP_SPEC.md.

Daily price statistics

Three tools accept an optional day (ISO date, for example 2026-10-25) and resolution (PT15M by default, or PT60M):

  • get_average_prices: arithmetic mean of available normalized total values independently for electricity and gas. It is not duration-weighted.

  • get_lowest_price: lowest total interval for each energy type.

  • get_highest_price: highest total interval for each energy type.

When day is omitted, the tools use the current calendar day in Europe/Amsterdam. They request public prices from python-frank-energie; the MCP layer does not calculate taxes or markups.

Example MCP tool arguments:

{"day": "2026-10-25", "resolution": "PT15M"}

Each result contains date, resolution, electricity and gas. Average results contain average_total, interval_count, first_interval, last_interval, coverage_seconds, expected_seconds, complete and status. Minimum/maximum results contain a price interval (or null), interval_count, coverage_seconds, expected_seconds and status.

status is empty (no intervals), incomplete (partial day) or complete (the entire local calendar day is covered). Missing prices are never filled in; the average of an empty series is null. Overlapping, duplicate or out-of-day intervals are rejected. Coverage is measured in elapsed seconds, including 23- and 25-hour daylight-saving transition days. Minimum and maximum ties select the first interval in the upstream list. For exact response shapes, see MCP_SPEC.md.

Consecutive price periods

get_lowest_period and get_highest_period accept a required period_minutes and optional day (default: today in Europe/Amsterdam) and resolution (default: PT15M; also PT60M).

For example, to find the cheapest four consecutive hours today, call get_lowest_period with:

{"period_minutes": 240, "resolution": "PT15M"}

A four-hour window requires 16 consecutive 15-minute intervals, or 4 consecutive 60-minute intervals with PT60M. The duration must be positive and divisible by the chosen resolution (15 or 60 minutes). The selected window minimizes or maximizes the arithmetic mean of normalized total prices; it is calculated separately for electricity and gas. Each result includes period (or null if no uninterrupted window exists), available_interval_count, coverage_seconds, expected_seconds, and status. The period includes start, end, average_total, interval_count, and unit.

Only fully contained, contiguous windows are considered; gaps are not filled. The search uses the entire requested calendar day and does not restrict today's result to future intervals. Equal averages prefer the earliest window. See MCP_SPEC.md.

Electricity cost estimation

calculate_energy_cost estimates cost from the normalized total electricity price for a requested fully covered time interval. Example arguments:

{
  "start": "2026-06-15T00:00:00+02:00",
  "end": "2026-06-15T02:00:00+02:00",
  "consumption_kwh": 2.0,
  "resolution": "PT15M"
}

The tool weights partial price intervals by elapsed seconds and assumes a constant rate of energy consumption. It rejects missing or overlapping price coverage, and supports at most two consecutive Amsterdam dates.

Best electricity start time

get_best_start_time accepts period_minutes, optional day and resolution, and optional consumption_kwh (finite and non-negative). For example:

{"period_minutes": 240, "resolution": "PT15M", "consumption_kwh": 8}

The tool uses the existing cheapest consecutive-window algorithm for electricity. It returns start/end timestamps, average normalized total price and, when consumption is supplied, an estimated cost assuming a constant consumption rate over the window. It searches the entire requested day, including past intervals, and does not promise a future start. Missing complete windows produce null start/end/cost fields.

Authentication

Account and delivery-site tools use the existing upstream python-frank-energie login method. Configure the server process with FRANK_ENERGIE_USERNAME and FRANK_ENERGIE_PASSWORD; set FRANK_ENERGIE_SITE_REFERENCE for get_account. Both credentials must be provided together. Missing credentials or site references cause descriptive errors before an account request is sent. Avoid putting credentials in repository files, command-line arguments, logs or screenshots. The server currently logs in for each managed upstream session and closes the client after each request. Token persistence, renewal across requests and credential rotation have not been validated.

Architecture

MCP client
    |
FastMCP stdio server (server.py)
    |
FrankEnergieService (library.py)
    |
FrankEnergieLibraryBridge (managed async client)
    |
python-frank-energie
    |
Frank Energie API

API communication, authentication and price calculations belong in the upstream library, not in the MCP server.

Validation

python -m ruff check .
python -m ruff format --check .
python -m mypy src tests
python -m pytest -q

Tests use mock clients; they do not require production credentials. These commands are documented checks, not a claim that CI currently passes.

Known limitations

  • Environment-based login and explicit site-reference selection are implemented; persistent token lifecycle and operational validation remain pending.

  • Normalized price responses and generic error mapping have unit coverage; real upstream and MCP transport interoperability still require validation.

  • End-to-end MCP client interoperability has not been validated.

See ARCHITECTURE.md, MCP_SPEC.md, AI_POLICY.md and SECURITY.md. Do not commit credentials or personal account data.

Production deployment gates

Local Windows (stdio): Install into a dedicated Python 3.14 virtual environment, run the four validation commands above, and configure the MCP client to launch the virtual-environment Python executable. Keep credentials in the process environment or a protected secret store. Do not store them in client configuration committed to Git.

External ChatGPT access: Not production-ready yet. The built-in Streamable HTTP listener is loopback-only and unauthenticated. Do not expose it publicly or treat a TLS tunnel alone as sufficient authentication. A production deployment needs an HTTPS ingress with validated client authentication and authorization, restricted access to the upstream loopback service, rate limiting, request/body size limits, TLS certificate management, and documented token rotation. Verify that the intended ChatGPT MCP connector supports the chosen authentication flow before deployment. A reverse proxy or tunnel must not forward unauthenticated public requests to /mcp.

Release blockers: Verify all CI checks on the exact release commit; validate real MCP tool calls over both transports; test upstream timeouts, authentication failures, missing data, and time-zone boundaries; and document a rollback procedure. The legacy module review is complete: unused placeholders were removed, while models.py and tools/__init__.py remain for import compatibility. Do not describe the service as production-ready until the remaining checks pass.

Price threshold periods

Use get_price_threshold_periods to find contiguous intervals where electricity or gas normalized total prices are strictly below or above a finite threshold. Example: threshold=0.0, comparison="below", resolution="PT15M" finds negative-price windows. Missing price bins do not join separate periods.

Negative price periods

Use get_negative_price_periods(day=None, resolution="PT15M") to identify consecutive periods with a strictly negative normalized total price for electricity and gas. Zero-price bins and missing intervals do not count as negative. The tool reuses the existing threshold analyzer and supports PT15M and PT60M.

Daily price trend

Call get_price_trend(day=None, resolution="PT15M") to compare the first and last available normalized total prices for electricity and gas on a local date. The result classifies rising, falling, stable, or unknown (fewer than two intervals), and includes coverage metadata. This is an endpoint trend, not a prediction or statistical regression.

Daily price volatility

Use get_price_volatility(day=None, resolution="PT15M") for per-energy all-in price spread, population standard deviation, and mean absolute change between contiguous intervals. Missing intervals are not bridged; the response includes coverage status and the number of adjacent pairs.

Compare calendar days

Use compare_days(first_day, second_day, resolution="PT15M") to compare observed average, minimum and maximum normalized all-in electricity and gas prices. Absolute and percentage differences are relative to the first day; percentages are null for a zero baseline. Both days retain separate DST-aware coverage and completeness metadata.

Cheapest split periods

Call get_best_split_periods(total_duration_minutes=240, block_duration_minutes=60, day=..., resolution="PT15M") to find the globally cheapest non-overlapping blocks, which need not be adjacent. Results include block times, mean all-in prices, availability and DST-aware coverage. The reported total_cost_per_unit assumes a constant load of one unit per hour; actual appliance costs depend on consumption.

Battery savings (single-cycle estimate)

Use calculate_battery_savings(capacity_kwh=1, charge_power_kw=4, discharge_power_kw=4, charge_efficiency=0.95, discharge_efficiency=0.95) to estimate a profitable charge-then-discharge cycle using public all-in electricity prices. This is not a multi-interval battery scheduling model: each phase fits within one price interval. The calculation assumes enough local demand to use discharged energy, excludes export payments and battery wear, and returns no estimate when daily prices are incomplete.

Daily price statistics

Use get_price_statistics(day="2026-06-15", resolution="PT15M") for duration-weighted mean, median, percentiles, population variance, standard deviation, extrema, negative/zero counts and coverage for electricity and gas. Percentiles use duration-weighted nearest rank; incomplete days are marked rather than filled with invented prices.

Today and tomorrow summary

Use get_price_forecast_summary(day="2026-06-15", resolution="PT15M", period_minutes=60) to compare observed all-in prices and cheapest/dearest contiguous windows for two consecutive local dates. The tool does not predict unpublished prices; it reports missing intervals and incomplete coverage explicitly.

Charging schedule

Use get_optimal_charging_schedule to plan fixed-power charging from published electricity prices. Supply energy (kWh), maximum grid power (kW), timezone-aware start and deadline, efficiency, and whether charging may be interrupted. Missing price intervals never become synthetic estimates. This tool does not control chargers.

Compare charging strategies

Use compare_charging_strategies to compare immediate, least-cost contiguous, and least-cost interruptible charging within a timezone-aware window. It reports estimated costs and savings versus immediate charging without inventing missing prices.

Charging deadline analysis

Use get_charging_deadline_analysis to compare the cost of charging before multiple deadlines. The tool reuses get_optimal_charging_schedule, reports a plan, required grid energy, required charging slots, occupied charging minutes (when a feasible published-price schedule exists), and the additional cost relative to the cheapest feasible deadline. It does not invent missing prices or control a charger.

Use get_charging_start_time_analysis to compare up to twelve timezone-aware start times against a fixed deadline (within 48 hours). It reports feasible charging schedules, estimated costs, grid energy, charging duration, the cheapest start, and savings against the most expensive feasible start. Missing prices never produce invented costs.

Related MCP Connectors

Related MCP Servers