Skip to main content
Glama

Linky — MCP NexAura

Ton agent lit la consommation électrique de la maison (ou du local) du client, chez Enedis, grâce au compteur Linky. Lecture seule, rien d'autre.

Fabriqué par NexAura, publié sous licence MIT.

Ce qu'il fait

Quatre outils, que l'agent choisit tout seul selon la question :

Outil

Il répond à…

conso_veille

« Combien j'ai consommé hier ? » (en kWh)

conso_periode

« Et la semaine dernière, jour par jour ? » (entre deux dates, avec total et moyenne)

courbe_charge

« À quelle heure ça consomme ? » (toutes les 30 minutes, 7 jours au plus)

puissance_max

« Mon abonnement est-il trop grand ? » (puissance la plus forte de chaque jour, en VA)

Related MCP server: data-odre

Ce qu'il lit

La consommation du compteur du client, via Conso API (un service gratuit qui passe par l'accès officiel d'Enedis). Les données d'un jour arrivent le lendemain vers 8 h : l'agent ne peut donc pas voir aujourd'hui, seulement jusqu'à hier. La courbe toutes les 30 minutes ne marche que si le client a activé « enregistrement de la courbe de charge » dans son espace Enedis.

Ce qu'il ne fait jamais

  • Il n'écrit rien, ne modifie rien, ne commande rien (pas de réglage du compteur, pas de changement de contrat).

  • Il n'envoie aucune donnée ailleurs que chez Conso API.

  • Il ne montre ni le jeton ni le numéro du compteur, ni à l'agent, ni dans le journal.

  • Il ne redemande pas deux fois les mêmes données (mémoire de 6 h), pour respecter les règles de Conso API (5 demandes par seconde, 10 000 par heure, partagées entre tous les utilisateurs).

Ce qu'il faut au client

  1. Un compteur Linky avec la collecte activée dans son espace Enedis.

  2. Un jeton Conso API, obtenu sur https://conso.boris.sh après avoir donné son accord chez Enedis (le jeton est personnel ; l'accord se retire à tout moment chez Enedis).

  3. Le numéro du compteur (PRM), 14 chiffres, visible dans l'espace Enedis ou sur une facture.

Installer

Il faut Python 3.10 ou plus.

git clone https://github.com/Nexauraholding/mcp-linky.git
cd mcp-linky
python3 -m venv .venv
.venv/bin/pip install .

Brancher dans Hermès

Dans config.yaml de l'agent :

mcp_servers:
  linky:
    command: /chemin/vers/mcp-linky/.venv/bin/python
    args: ["-m", "nexaura_linky"]
    env:
      LINKY_TOKEN: "le-jeton-conso-api"
      LINKY_PRM: "12345678901234"
      LINKY_MCP_JOURNAL: "/chemin/vers/journal/linky.jsonl"   # facultatif
    tools:
      include: [conso_veille, conso_periode, courbe_charge, puissance_max]

tools.include est facultatif (les quatre outils sont déjà les seuls) ; il sert à retirer un outil, par exemple courbe_charge, sans toucher au code.

Brancher en http, avec un mandat (OAuth 2.1)

Le mode stdio ci-dessus reste le branchement local, sans jeton : c'est ce qu'Hermès fait aujourd'hui. Quand le MCP tourne sur un serveur, il devient une ressource protégée OAuth 2.1, selon la spécification d'autorisation MCP (version courante 2026-07-28) :

  • il publie ses métadonnées de ressource protégée (RFC 9728) : /.well-known/oauth-protected-resource/mcp pour une ressource en /mcp ; une réponse 401 y renvoie le client ;

  • il n'accepte qu'un jeton émis pour lui (audience = son adresse exacte, RFC 8707), signé, non expiré ;

  • la seule portée est linky:read : aucune portée d'écriture n'existe. Un jeton qui n'a que linky:write ou qonto:read reçoit 403 ;

  • le client mène le flux standard (code d'autorisation + PKCE + paramètre resource) : c'est le travail du client MCP, pas du nôtre.

Le mandat

Le mandat est ce jeton court (15 minutes au plus), émis par le serveur d'autorisation selon le flux standard, à partir de l'accord que l'humain a donné. Il dit :

Dans le jeton

Sens

sub

pour qui l'agent agit (l'humain qui a donné son accord)

client_id

quel agent agit (le client OAuth)

scope

sur quoi : linky:read

aud

quel MCP : le sien, et aucun autre

mandat.perimetre

le périmètre : outils (liste d'outils permis), depuis (première date lisible, AAAA-MM-JJ)

mandat.jusqu_a

jusqu'à quand (secondes Unix) : le mandat fini est refusé même si le jeton vit encore

mandat.id

l'identifiant du mandat, écrit dans chaque ligne du journal

Pas d'échange de jetons (RFC 8693) : la spécification MCP de base ne l'emploie pas, et Hermès sait déjà faire le flux standard (voir ci-dessous). (Changement du 27 oct. : la première version prévoyait cet échange.)

Le jeton est refusé (401) s'il est expiré, mal signé, d'un autre émetteur, pour une autre audience, de type autre que at+jwt, plus long que 15 minutes, ou sans mandat, sans agent, sans date de fin ; le mandat fini est refusé de même. Chaque refus est noté au journal (raison courte, jamais le jeton).

Lancer

.venv/bin/pip install ".[http]"
LINKY_MCP_TRANSPORT=http \
LINKY_MCP_ISSUER=https://auth.exemple.fr/realms/nexaura \
LINKY_MCP_RESOURCE=https://linky.exemple.fr/mcp \
LINKY_TOKEN=... LINKY_PRM=... \
.venv/bin/python -m nexaura_linky

Le serveur écoute en local (127.0.0.1:8791) : mets devant lui un proxy en https, et donne l'adresse publique dans LINKY_MCP_HOTES. Un LINKY_MCP_RESOURCE en http n'est accepté que sur la machine locale.

Côté Hermès : rien de maison

Hermès sait déjà faire OAuth 2.1 avec PKCE vers un MCP en http (auth: oauth, voir la documentation MCP d'Hermès) : découverte, identification du client (CIMD, sinon enregistrement dynamique), jeton, renouvellement.

mcp_servers:
  linky:
    url: "https://linky.exemple.fr/mcp"
    auth: oauth
    oauth:
      scope: "linky:read"

Quel serveur d'autorisation ?

Aucun n'est écrit ici : on prend une brique libre. Trois ont été regardés (sources officielles, 27 oct.) :

Keycloak (26.2 et plus)

oidc-provider (panva, Node)

Authlib (Python)

Nature

produit complet, écran d'accord, comptes, journal

bibliothèque certifiée OpenID

boîte à outils

Code + PKCE, jeton JWT (RFC 9068)

oui

oui

oui (9068 listé)

resource (RFC 8707)

non géré (dit dans sa doc) : l'audience se règle par la portée

oui, natif

non listé

Login, accord, comptes

déjà là

à écrire

à écrire

Keycloak évite d'écrire un serveur d'autorisation (écran d'accord, comptes et journal des connexions existent). Ses limites : pas de paramètre resource (on lie l'audience à la portée linky:read par un mappeur d'audience), et pas de fin automatique d'un accord (le mandat jusqu_a et le périmètre viennent d'attributs posés au moment de l'accord). Si ces limites gênent, oidc-provider est le repli.

Les tests utilisent un faux serveur d'autorisation (tests/faux_as.py), qui fait le flux standard.

Le journal

Chaque appel ajoute une ligne dans un fichier local (par défaut ~/.nexaura/journal/linky.jsonl, réglable avec LINKY_MCP_JOURNAL). Une ligne = un appel, lisible par un humain :

{"quand": "2026-10-28T07:31:02+00:00", "service": "linky", "outil": "conso_periode", "parametres": {"debut": "2026-10-20", "fin": "2026-10-26"}, "acces": "lecture", "duree_ms": 412, "resultat": "ok"}

En mode http, la ligne porte aussi mandat (l'identifiant), pour et agent ; en stdio, mandat vaut null. Un appel hors périmètre, ou un jeton refusé, s'écrit resultat: refuse.

En cas d'erreur, resultat vaut erreur et un court texte l'explique. Jamais de jeton ni de PRM : même dans les messages d'erreur, ils sont remplacés par ***.

Les accès dans un fichier privé

Copie secrets.exemple dans un dossier privé, par exemple ~/.nexaura/acces/linky.env, puis protège-le avec chmod 600 ~/.nexaura/acces/linky.env avant d'y mettre tes vrais accès. Le fichier doit t'appartenir et être un fichier ordinaire, sans lien symbolique. Ne le mets jamais dans Git.

Dans le bloc env de la configuration ci-dessus, remplace les accès par ce chemin :

      LINKY_SECRETS_FILE: "~/.nexaura/acces/linky.env"

Écris une ligne CLE=valeur par accès. Les lignes vides et les commentaires commençant par # sont ignorés ; les espaces autour des clés et valeurs et les guillemets simples ou doubles autour des valeurs sont retirés. Seules les clés LINKY_TOKEN et LINKY_PRM sont lues. Une variable déjà présente dans l'environnement garde la priorité, même vide. Si un accès obligatoire manque et que le fichier est absent ou vide, l'erreur indique son chemin.

Variables

Variable

Rôle

LINKY_SECRETS_FILE

Chemin du fichier privé des accès (facultatif, ~ accepté).

LINKY_TOKEN

jeton Conso API (obligatoire)

LINKY_PRM

numéro du compteur, 14 chiffres (obligatoire)

LINKY_MCP_JOURNAL

emplacement du journal (facultatif)

LINKY_BASE_URL

autre adresse d'API, pour les tests (facultatif)

LINKY_MCP_TRANSPORT

stdio (défaut) ou http

LINKY_MCP_ISSUER, LINKY_MCP_RESOURCE

http : serveur d'autorisation, adresse publique de ce MCP (obligatoires)

LINKY_MCP_JWKS_URL

http : adresse des clés publiques (sinon trouvée par RFC 8414)

LINKY_MCP_HOST, LINKY_MCP_PORT, LINKY_MCP_HOTES

http : écoute (127.0.0.1:8791) et noms d'hôte publics

Tests

.venv/bin/pip install ".[test]"   # PyJWT et pytest
.venv/bin/python -m pytest

Les tests utilisent de fausses réponses écrites à la main, une fausse API locale et un faux serveur d'autorisation : aucun appel réel (ni Enedis, ni serveur d'autorisation).

Licence

MIT (voir LICENSE). Les formats de réponse viennent de la documentation publique de Conso API ; aucun code de bokub/linky (GPL-3.0) n'est repris.

Available Tools

4 tools
conso_periodeA

Donne la consommation d'électricité jour par jour, en kWh, entre deux dates incluses, avec le total et la moyenne. Dates au format AAAA-MM-JJ (exemple 2026-05-01). Jusqu'à 366 jours, jusqu'à hier au plus tard. Lecture seule.

NameRequiredDescriptionDefault
finYes
debutYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations at all, the description carries the full burden and does disclose the safety profile ('Lecture seule') plus the range and end-date limits, which are real behavioral boundaries. It omits what happens on invalid or future dates and whether days without data appear as gaps or zeros.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences: purpose and return values first, then format, then limits, then read-only status. Every sentence adds a fact an agent needs, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter read tool with no output schema, the description covers return shape (daily values, total, average), input format and validity limits. The remaining gap is edge-case behavior — missing data days and error responses — but nothing essential to issuing a correct call is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% — the properties are bare strings titled 'Debut' and 'Fin' — so the description must compensate, and it does: date format AAAA-MM-JJ with an example, inclusive boundary semantics, and both range limits. Only the interplay of debut/fin validation errors is left unstated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: daily electricity consumption in kWh between two inclusive dates, plus total and average. That clearly distinguishes it from conso_veille (single day), courbe_charge (load curve) and puissance_max (peak power), though no sibling is named explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete applicability windows: maximum 366 days per call and an end date no later than yesterday. These constraints tell an agent when the call is valid, but no alternative tool is named for out-of-range cases or for single-day queries.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

conso_veilleA

Dit combien d'électricité le foyer a consommé hier, en kWh (kilowattheures). À utiliser pour « combien j'ai consommé hier ? ». Lecture seule.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden: it correctly discloses read-only behavior ('Lecture seule') and the return unit, which is valuable. It says nothing about what happens when yesterday's data is missing or delayed, which matters for an energy-metering tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with what is returned, followed by the usage cue and the read-only note. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a no-param, no-output-schema tool this covers what it returns, the unit, and the trigger. The only gap is how 'hier' is resolved (timezone / data availability window).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4; the description makes clear no user input is needed. There is nothing further to add.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource the household's electricity consumption, with unit (kWh) and time window (yesterday), which distinguishes it implicitly from conso_periode. It stops short of naming the sibling it differs from, so it's clear but not fully differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The quoted user utterance 'combien j'ai consommé hier ?' gives a concrete trigger, and 'hier' implicitly routes away from conso_periode. However, no alternative tool is named and no when-not condition is given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

courbe_chargeA

Donne la consommation toutes les 30 minutes (puissance moyenne en watts et énergie en kWh). Sans dates : hier. 7 jours au plus. Dates au format AAAA-MM-JJ, incluses. Marche seulement si le client a activé l'enregistrement de la courbe de charge chez Enedis. Lecture seule.

NameRequiredDescriptionDefault
finNo
debutNo

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it declares the tool is read-only ('Lecture seule'), states the prerequisite Enedis activation, and defines the default and maximum date range. It does not cover error behavior when recording is inactive, but the core behavioral constraints are disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short, front-loaded with the core output, and each sentence adds actionable information: format, default, limit, prerequisite, and read-only status. No waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-optional-parameter tool with no output schema, the description explains what is returned (30-minute average power and energy), the date constraints, the default, and the activation prerequisite. It is nearly complete, though it could say more about partial date ranges or inactive-recording errors.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate, and it does: it specifies the date format (AAAA-MM-JJ), that dates are inclusive, the default when omitted (yesterday), and the maximum span (7 days). It does not clarify behavior when only one of debut or fin is supplied, but the essential semantics are covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it returns consumption every 30 minutes, including average power in watts and energy in kWh. This clearly distinguishes the tool's high-frequency interval data from siblings like conso_periode, but it does not explicitly name or contrast those siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage context: no dates defaults to yesterday, the maximum range is 7 days, and it only works if the client has activated load-curve recording at Enedis. It does not explicitly name alternatives such as conso_periode or state when not to use this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

puissance_maxA

Donne la puissance maximale atteinte chaque jour (en VA) et le jour le plus fort de la période. Utile pour savoir si l'abonnement est trop petit ou trop grand. Sans dates : hier. Dates au format AAAA-MM-JJ, incluses. Lecture seule.

NameRequiredDescriptionDefault
finNo
debutNo

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description still discloses key behavior: it is read-only, defaults to yesterday when no dates are supplied, and dates are inclusive. It does not cover permissions, error behavior, or edge cases such as supplying only one date.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: purpose first, then practical usage, default behavior, date format, and read-only status. Every sentence adds useful information with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only query with no output schema and no annotations, the description covers purpose, use case, default period, date format, inclusivity, and read-only safety. Remaining gaps are minor edge cases around partial date ranges and timezone assumptions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must carry parameter semantics. It usefully adds the date format AAAA-MM-JJ, the fact that dates are inclusive, and the default of yesterday, but it does not clarify individual behavior for debut versus fin or partial parameter combinations.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource: it returns daily maximum reached power in VA and the strongest day in the period. This is clear enough to identify the tool, though it does not explicitly distinguish itself from siblings such as conso_periode or courbe_charge.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides a clear use case: determining whether the subscription is too small or too large, and states the default behavior when dates are omitted. It does not explicitly say when to prefer this over sibling tools like conso_periode or courbe_charge.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.1.0
    • First observedconso_periode
    • First observedconso_veille
    • First observedcourbe_charge
    • First observedpuissance_max

TDQS

A4/5.0

Scored across 4 tools

Disambiguation4/5

The four tools target distinct query types: yesterday's total, a date-range daily series, a 30-minute load curve, and daily maximum power. The only overlap is that conso_veille is effectively a one-day convenience subset of conso_periode, but the descriptions clearly steer usage.

Naming Consistency4/5

All tool names use French noun phrases in snake_case (conso_veille, conso_periode, courbe_charge, puissance_max), which is consistent and readable. They do not follow a verb_noun pattern, but that is reasonable for a read-only server.

Tool Count5/5

Four tools is well-scoped for a Linky consumption reader: each provides a distinct view of consumption or power. No tool feels redundant or unnecessary.

Completeness4/5

The surface covers core consumption use cases: yesterday's total, date-range daily totals, load curve detail, and maximum power. Minor gaps exist, such as no explicit today/real-time query or cost/aggregate insights, but agents can work around them.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Provides read-only access to Shelly Cloud energy data, including live power, lifetime energy, and historical consumption with cost, enabling AI agents to monitor electricity usage without device control.
    1
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides tools to search, inspect, and query energy open data from ODRE (electricity & gas production, consumption, grid load, renewables) using natural language or direct tool calls.
    21 npm
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    Provides read-only access to a Ryde account so agents can pull completed rides and GPS routes, owned and available RidePasses, payments, city fare rules, and ride summaries or pass-cost comparisons. Requires an SMS-authenticated login and never modifies account data.
    20
    MIT