Skip to main content
Glama

Gridbert

check_invoice_reading

Read-onlyIdempotent

Letzter Schritt des Lesens mit DEINEM Modell: prüft deine Antwort auf den Leseauftrag aus get_invoice_reading_task. Gridbert ruft hier KEIN Modell auf. Geprüft wird deterministisch: Schema, wörtliches Zitat gegen die Textebene, Kreuzproben (Summen, Verbrauch, Arbeitspreis), Zuschuss und Rabatt; danach wird mit dem Regelleser zusammengeführt (sicher gelesene Regel-Felder haben Vorrang, deine Felder stehen höchstens auf 'mittel'). Das Ergebnis hat EXAKT die Form von read_invoice_facts — je Feld Wert, Zitat und Konfidenz (hoch|mittel|niedrig|fehlt) — und ist dem User vorzulegen und bestätigen zu lassen, bevor submit_invoice_facts es rechnet ('quellen_anker' unverändert weitergeben). rechnungsbetrag_brutto_eur ist der Betrag des Strombezugs (betrag_umfang='strombezug'); ist er gerechnet (begleitangaben.herkunft='gerechnet'), ist eine Summenprobe gegen die Blocksummen zirkulär und kein Beweis. Rechnet nichts, speichert nichts. Eingabe: rechnung_ref, 'regelwerk_version' UNVERÄNDERT aus get_invoice_reading_task, und 'antwort' (dein JSON-Objekt in Kompaktform, oder derselbe Text; ein Codeblock drumherum wird toleriert; null, wenn lesen_noetig=false war). Fehler: error_class 'antwort_ungueltig' mit einer lesbaren Liste unter 'fehler' — korrigiere genau diese Stellen und rufe noch einmal auf (ein Nachbesserungsversuch genügt in der Regel); 'regelwerk_veraltet': das Regelwerk hat sich geändert, hol die Aufgabe mit get_invoice_reading_task neu. Gabst du bei get_invoice_reading_task seitentexte mit, gib dieselben hier wieder mit.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "rechnung_ref": "<rechnung_ref>",
      "regelwerk_version": "<regelwerk_version>"
    },
    "name": "check_invoice_reading"
  }
}

Input Schema

NameRequiredDescriptionDefault
antwortNoDeine Antwort auf den Leseauftrag: das JSON-Objekt in Kompaktform (oder ein Text mit genau diesem JSON). null, wenn get_invoice_reading_task lesen_noetig=false meldete.
seitentexteNoOptional, nur wenn schon bei get_invoice_reading_task angegeben: derselbe Text je Seite (Index 0 = Seite 1).
rechnung_refYesDieselbe Kennung wie bei get_invoice_reading_task.
regelwerk_versionYesDie regelwerk_version aus get_invoice_reading_task, unverändert.

Output Schema

NameRequiredDescriptionDefault
okYesimmer true. Fehler kommen als isError ohne structuredContent zurück (errors.error_result: {ok:false, error_class, fehler}).
felderYesEin Eintrag je gelesenem Feld.
hinweisNoOptionaler Hinweis ans Modell (F14) — Datenstand-Warnung, nächster Schritt, Granularitäts-Caveat. Darf null sein.
rabatteNoDie Rabatt- und Bonuszeilen des Lieferanten hinter einmalrabatte_brutto_eur. Nur zur Anzeige; NICHT an submit_invoice_facts weitergeben.
pruefbarYesfalse = keine prüfbare Jahres-, Zwischen- oder Monatsrechnung. Dann steht der Grund in 'ablehnungsgrund' und es gibt keine Prüfung.
workspaceNoNur bei Berater-Accounts: der Arbeitsbereich, in dem dieser Call gelandet ist (dispatch.py stempelt ihn zentral an JEDES Result). Haushalts-Results tragen das Feld nicht.
energieartNostrom | gas | kombi | unbekannt.
gelesen_mitNotextebene = aus der Textebene des PDFs gelesen (nur dann kann ein Feld 'hoch' sein). ocr = aus einem Scan oder Foto bzw. aus Seitentexten des Aufrufers gelesen (höchstens 'mittel').
rechnungstypYesjahresrechnung | zwischenrechnung | monatsrechnung | unbekannt.
betrag_umfangNo'strombezug' = rechnungsbetrag_brutto_eur ist der Betrag des STROMBEZUGS (Regelwerk v4: gedruckte Strom-Endsumme ohne andere Sparte und ohne Einspeisung, oder aus den drei Strom-Blocksummen gerechnet). FEHLT das Feld, stammt der Betrag aus einem Weg, der das nicht zusagt (Altpfad, Regelleser bei Einspeisung) oder es gibt keinen Betrag: Kombi- und PV-Rechnungen sind dann abzuweisen, nicht zu prüfen.
quellen_ankerYesFertig für die Übergabe an submit_invoice_facts: Objekte {feld, zitat}. NICHT neu bauen — dort wird geprüft, ob der übergebene Zahlenwert im Zitat wirklich vorkommt.
ablehnungsgrundNoteilbetrag | einspeisung | energiegemeinschaft | grosskunde | ausland | keine_energierechnung — oder null, wenn prüfbar.
energiepositionenNoDie EINZELNEN Zeilen des Energieblocks, wenn die Rechnung dort mehr als eine führt (z.B. Spotpreis, Stromqualität, Mehrkosten Ökoenergie/HKN). Jeder Eintrag ist fertig für submit_invoice_facts: {bezeichnung, wert_eur, ist_netto}. **Unverändert weiterreichen, nichts ergänzen** — ein viertes Feld lässt die ganze Einreichung ablehnen. LEERE LISTE heisst 'keine erkannt', NICHT 'der Energieblock ist leer': dann wird der Block wie bisher über Arbeitspreis × Menge nachgerechnet. Die Belege dazu stehen in quellen_anker unter dem Feldnamen 'energiepositionen'.
einmalrabatte_brutto_eurNoEinmaleffekte dieser Rechnung in €, brutto und positiv — oder null: befristete und einmalige Rabatte des Lieferanten (Neukunden-, Wechsel-, Aktionsbonus, Gratis-/Freimonate) × 1,2 plus ein Stromkostenzuschuss, der im Rechnungsbetrag schon abgezogen ist. Dauerhafte Nachlässe (Online, Bankeinzug, Bestandskunde) zählen nie, sie stecken im Arbeitspreis; ein Zuschuss ausserhalb des Betrags nie. Wiederkehrende Kosten der Periode = rechnungsbetrag_brutto_eur + einmalrabatte_brutto_eur. Gerechnet, nicht gelesen; NICHT an submit_invoice_facts weitergeben.
einspeisung_auf_rechnungNotrue = die Rechnung rechnet neben dem Strombezug auch Einspeisung, Überschuss oder den Verkauf erzeugter Energie ab (PV). Dann ist rechnungsbetrag_brutto_eur der Betrag des BEZUGS, nicht der gedruckte Saldo. false = keine Einspeisung auf der Rechnung. FEHLT das Feld, ist es unbekannt (Regelleser, Antwort ohne die Angabe) — nicht als false lesen.
rabatt_im_energieblock_eurNoSumme der gelesenen Rabatte des Lieferanten in €, netto und positiv — oder null. Unverändert an submit_invoice_facts weitergeben: die Nachrechnung zieht ihn nur ab, wenn der Energieblock danach aufgeht.
rabatt_ausserhalb_bloecke_eurNoRabatt-/Bonuszeile AUSSERHALB der drei Blöcke in €, netto und negativ (Rabatt = Minuszahl) — oder null. Nur gesetzt, wenn die Rechnung damit aufgeht. Unverändert an submit_invoice_facts weitergeben: (Summe Blöcke + Rabatt) × 1,2 − Zuschuss = Rechnungsbetrag.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changed
    • addedOutput schema / properties / betrag_umfang
      Added value: +{
      +  "description": "'strombezug' = rechnungsbetrag_brutto_eur ist der Betrag des STROMBEZUGS (Regelwerk v4: gedruckte Strom-Endsumme ohne andere Sparte und ohne Einspeisung, oder aus den drei Strom-Blocksummen gerechnet). FEHLT das Feld, stammt der Betrag aus einem Weg, der das nicht zusagt (Altpfad, Regelleser bei Einspeisung) oder es gibt keinen Betrag: Kombi- und PV-Rechnungen sind dann abzuweisen, nicht zu prüfen.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / einspeisung_auf_rechnung
      Added value: +{
      +  "description": "true = die Rechnung rechnet neben dem Strombezug auch Einspeisung, Überschuss oder den Verkauf erzeugter Energie ab (PV). Dann ist rechnungsbetrag_brutto_eur der Betrag des BEZUGS, nicht der gedruckte Saldo. false = keine Einspeisung auf der Rechnung. FEHLT das Feld, ist es unbekannt (Regelleser, Antwort ohne die Angabe) — nicht als false lesen.",
      +  "type": "boolean"
      +}
    • changedOutput schema / properties / felder / items / properties / begleitangaben / description
      Previous value: -"Angaben, ohne die der Wert nicht zu deuten ist — grundgebuehr_zeitraum (monat|jahr), die ist_netto-Markierungen sowie beim Arbeitspreis arbeitspreis_herkunft und arbeitspreis_rechenweg. Sie tragen keine eigene Konfidenz. **Nie annehmen, was hier steht:** eine Grundgebühr von 60,00 mit zeitraum='jahr' ist 5,00 im Monat, und wer 'monat' unterstellt, rechnet um den Faktor zwölf falsch. **arbeitspreis_herkunft** ist 'gelesen' oder 'gerechnet'. Bei 'gerechnet' gibt es kein Zitat, weil keine Zeile der Rechnung diesen Wert trägt — stattdessen steht in arbeitspreis_rechenweg der vollständige Rechenweg aus gelesenen Feldern derselben Rechnung. Wer den Wert anzeigt, zeigt diesen Satz dazu: er ist die Herkunft, nicht ein Beleg, und hebt die Konfidenz deshalb nie über 'niedrig'. **herkunft** ('gelesen' | 'gerechnet') gilt für jedes Feld und fehlt beim Regelleser: 'gerechnet' heisst, der Wert steht so nicht auf der Rechnung, sondern ist aus gelesenen Zeilen gerechnet (Jahr / 12, brutto / 1,2, Summe der Positionen); **rechenweg** nennt dann die Rechnung. Das Ergebnis rechnet der Gateway selbst nach, nie ein Modell. Beim Rechnungsbetrag stehen, wenn die Rechnung einen Stromkostenzuschuss ausweist, **stromkostenzuschuss_eur** (Betrag) und **stromkostenzuschuss_lage** ('im_betrag_abgezogen' | 'ausserhalb_des_betrags' | 'unklar'): der Rechnungsbetrag ist immer der gedruckte, also nach einem abgezogenen Zuschuss. Leeres Objekt heisst: für dieses Feld gibt es keine."New value: +"Angaben, ohne die der Wert nicht zu deuten ist — grundgebuehr_zeitraum (monat|jahr), die ist_netto-Markierungen sowie beim Arbeitspreis arbeitspreis_herkunft und arbeitspreis_rechenweg. Sie tragen keine eigene Konfidenz. **Nie annehmen, was hier steht:** eine Grundgebühr von 60,00 mit zeitraum='jahr' ist 5,00 im Monat, und wer 'monat' unterstellt, rechnet um den Faktor zwölf falsch. **arbeitspreis_herkunft** ist 'gelesen' oder 'gerechnet'. Bei 'gerechnet' gibt es kein Zitat, weil keine Zeile der Rechnung diesen Wert trägt — stattdessen steht in arbeitspreis_rechenweg der vollständige Rechenweg aus gelesenen Feldern derselben Rechnung. Wer den Wert anzeigt, zeigt diesen Satz dazu: er ist die Herkunft, nicht ein Beleg, und hebt die Konfidenz deshalb nie über 'niedrig'. **herkunft** ('gelesen' | 'gerechnet') gilt für jedes Feld und fehlt beim Regelleser: 'gerechnet' heisst, der Wert steht so nicht auf der Rechnung, sondern ist aus gelesenen Zeilen gerechnet (Jahr / 12, brutto / 1,2, Summe der Positionen); **rechenweg** nennt dann die Rechnung. Das Ergebnis rechnet der Gateway selbst nach, nie ein Modell. **Ist der Rechnungsbetrag gerechnet (herkunft='gerechnet'), ist eine Summenprobe gegen die Blocksummen zirkulär** — er entsteht aus denselben Blocksummen — **und kein Beweis.** **rechnungsbetrag_brutto_eur ist der Betrag des STROMBEZUGS.** Druckt die Rechnung keine eigene Strom-Endsumme ohne andere Sparte und ohne Einspeisung (Strom+Gas ohne Strombetrag, PV-Saldo), steht er mit herkunft='gerechnet', rechenweg ((Energie + Netz + Abgaben netto) x 1,2 minus ein im Betrag abgezogener Stromkostenzuschuss), zitat null und Konfidenz 'niedrig': ein abgeleiteter Betrag, nie sicher, dem Menschen vorzulegen. Fehlt der Betrag, war er nicht eindeutig. Die Felder **gas_verbrauch_kwh** (kWh wie gedruckt) und **gas_rechnungsbetrag_brutto_eur** stehen nur bei energieart kombi/gas: der Gasteil, nur zur Anzeige. Sie beeinflussen kein Stromfeld, es gibt keinen Gas-Vergleich, und sie gehen NICHT an submit_invoice_facts. Beim Rechnungsbetrag stehen, wenn die Rechnung einen Stromkostenzuschuss ausweist, **stromkostenzuschuss_eur** (Betrag) und **stromkostenzuschuss_lage** ('im_betrag_abgezogen' | 'ausserhalb_des_betrags' | 'unklar'): der Rechnungsbetrag ist immer der gedruckte, also nach einem abgezogenen Zuschuss. Leeres Objekt heisst: für dieses Feld gibt es keine."
  2. Changed2 schema fields changed
    • addedOutput schema / properties / einmalrabatte_brutto_eur
      Added value: +{
      +  "description": "Einmaleffekte dieser Rechnung in €, brutto und positiv — oder null: befristete und einmalige Rabatte des Lieferanten (Neukunden-, Wechsel-, Aktionsbonus, Gratis-/Freimonate) × 1,2 plus ein Stromkostenzuschuss, der im Rechnungsbetrag schon abgezogen ist. Dauerhafte Nachlässe (Online, Bankeinzug, Bestandskunde) zählen nie, sie stecken im Arbeitspreis; ein Zuschuss ausserhalb des Betrags nie. Wiederkehrende Kosten der Periode = rechnungsbetrag_brutto_eur + einmalrabatte_brutto_eur. Gerechnet, nicht gelesen; NICHT an submit_invoice_facts weitergeben.",
      +  "type": [
      +    "number",
      +    "null"
      +  ]
      +}
    • addedOutput schema / properties / rabatte
      Added value: +{
      +  "description": "Die Rabatt- und Bonuszeilen des Lieferanten hinter einmalrabatte_brutto_eur. Nur zur Anzeige; NICHT an submit_invoice_facts weitergeben.",
      +  "items": {
      +    "description": "Eine Rabattzeile.",
      +    "properties": {
      +      "art": {
      +        "description": "befristet | einmalig | dauerhaft | unklar — nach dem Wortlaut.",
      +        "type": "string"
      +      },
      +      "belegt": {
      +        "description": "true = die Zeile rechnet sich selbst nach (Menge × Satz = Betrag).",
      +        "type": "boolean"
      +      },
      +      "betrag_netto_eur": {
      +        "description": "Nachlass netto in €, positiv.",
      +        "type": "number"
      +      },
      +      "bezeichnung": {
      +        "description": "Zeilenbeschriftung der Rechnung.",
      +        "type": "string"
      +      },
      +      "zitat": {
      +        "description": "Die Zeile von der Rechnung.",
      +        "type": [
      +          "string",
      +          "null"
      +        ]
      +      }
      +    },
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  3. Added

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already declare read-only/idempotent, and the description adds substantial context beyond them: what is checked (schema, literal quote against the page text, cross-checks on sums/consumption/working price), that merging with the rule reader caps the model's own fields at 'mittel', the 'quellen_anker' pass-through requirement, and the caveat that a sum check is circular when herkunft='gerechnet'. Nothing contradicts the readOnly/idempotent hints.

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

Conciseness4/5

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

Front-loaded with the step's role and the key non-behavior ('ruft KEIN Modell auf'), and every sentence carries substantive instruction. It is, however, one very dense paragraph with many parenthetical clauses and mid-sentence German quotations, which makes it harder to scan than a short bulleted split into input/expected result/errors would be.

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

Completeness5/5

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

For a tool that feeds a multi-step pipeline, the description covers the prerequisite, the validation semantics, the precedence rules of the merge, the output shape, both error classes and the follow-up action. Since an output schema exists, describing return values is optional, and the shape note confirms consistency with read_invoice_facts rather than duplicating field docs.

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 100%, so the baseline is 3; the description goes slightly further by stating that 'regelwerk_version' must be copied unchanged, that 'antwort' may be a compact JSON object or a text with the same JSON (code fences tolerated) or null when lesen_noetig=false, and that 'seitentexte' must repeat what was sent earlier. This clarifies accepted formats rather than just restating parameter names.

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

Purpose5/5

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

The first sentence names the specific operation ("Letzter Schritt des Lesens ... prüft deine Antwort auf den Leseauftrag") and immediately clarifies what it is not ("Gridbert ruft hier KEIN Modell auf"). It explicitly situates itself between get_invoice_reading_task, read_invoice_facts and submit_invoice_facts, so an agent can place it in the pipeline without opening the schema.

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

Usage Guidelines5/5

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

Gives the exact position in the workflow (after get_invoice_reading_task, before submit_invoice_facts), the mandatory user-confirmation gate, and the retry policy for 'antwort_ungueltig' (one improvement attempt usually suffices). It also names the alternative path on 'regelwerk_veraltet': re-fetch via get_invoice_reading_task. When-to-use and when-to-restart are both explicit.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources