groundtruth-mcp
groundtruth-mcp
Dein Coding-Agent kann jede Datei in deinem Repository lesen und rät trotzdem. Dies verwandelt die eigenen Checks, Replays, Simulationen und Abfragen deines Projekts in MCP-Werkzeuge, sodass er die Konsequenzen seiner Änderung beobachtet, statt sie vorherzusagen.
中文文档 · Einführungsleitfaden · Architektur · Warum feste Seeds
Das Problem
Ein Agent, der eine strukturierte Konfiguration bearbeitet – einen Workflow-Graphen, eine Regeldatei, eine Zustandsmaschine, eine Pipeline-Definition – arbeitet mit der falschen Art von Kontext. Er kann das Schema lesen. Er kann nicht lesen, was passiert, wenn das Ding läuft.
Also inferiert er. Er ändert ein Wiederholungslimit und sagt dir, die Änderung sei sicher, weil „sicher" das plausibelste nächste Token angesichts eines Diffs war, der vernünftig aussah. Niemand hat etwas ausgeführt. Die verletzte Randbedingung lebt in einer Invariante drei Dateien weiter, oder in einer Verteilung, die niemand mehr abgetastet hat, seit die Richtlinie zuletzt angepasst wurde.
Die Lösung ist kein besserer Prompt. Es geht darum, dem Agenten etwas zu geben, das er beobachten kann.
Was es tut
flowchart LR
E[Agent edits a config] --> L[lint]
L -->|DANGLING_TRANSITION at states 1.transitions 0.to| E
E --> R[replay seed=7]
R -->|the 5 steps that actually ran| E
E --> S[simulate 2000 seeds]
S -->|88.3% success · p95 2566ms · PASS| E
S --> G["CI: groundtruth simulate --gate"]
G -->|same config, same thresholds| SFünf Werkzeuge, gebaut aus vier kleinen Funktionen, die du schreibst:
Tool | Beantwortet | Eigenschaft, die es nützlich macht |
| Ist diese Konfiguration in sich konsistent? | Jedes Problem trägt den genauen Pfad zum Bearbeiten |
| Was passiert, wenn ich diesen Lauf ausführe? | Reine Funktion von |
| Ist meine Änderung insgesamt besser oder schlechter? | Seeded Batch, Verteilung, Schwellenwerte, bestanden/nicht bestanden |
| Was steckt tatsächlich in den Daten? | Schreibgeschützt erzwungen durch die Datenbank, nicht durch eine Regex |
| Welche Tabellen existieren? | Damit niemand ein Schema raten muss |
Dieselben Fähigkeiten laufen als CLI, also ist groundtruth simulate --gate ein Merge-Gate, das die gleichen Schwellenwerte liest, gegen die der Agent optimiert. Sie können nicht auseinanderdriften, weil es nur eine Kopie gibt.
Sechzig Sekunden
pip install "groundtruth-mcp[mcp]"
git clone https://github.com/ZhenGtai123/groundtruth-mcp && cd groundtruth-mcp
groundtruth --config examples/checkout-flow/groundtruth.toml lint broken_checkoutDas gebündelte Beispiel ist ein konfigurationsgesteuerter Checkout: vier Seiten, ein unzuverlässiges Zahlungs-Gateway, eine Wiederholungsrichtlinie, Kunden, die gehen. broken_checkout.json enthält die Fehler, die ein Agent tatsächlich macht, wenn er Konfiguration bearbeitet, die er nicht ausführen kann.
broken_checkout: BLOCKED errors=6 warnings=1 infos=0
source: flows\broken_checkout.json
-- ERRORS — these block (6) --
[DANGLING_TRANSITION] states[1].transitions[0].to 'payment_methd' does not name any states.id
fix: point it at an existing state id, or delete the transition
[DEAD_END] states[6] 'review_hold' has no outgoing edge and is not marked terminal — a run that arrives here stops with no result
fix: give it a transition, or mark it kind = "terminal" with an outcome
[DUPLICATE_STATE] states[2] duplicate id='shipping' (first declared at states[1])
fix: rename one of them; the engine silently uses the first and ignores the rest
[RATE_OUT_OF_RANGE] policy.gateway_failure_rate 1.4 is above the maximum 1.0
fix: this is a probability, not a percentage — 0.18, not 18
[RETRY_BUDGET_TOO_THIN] policy.max_retries 140% gateway failure with 1 retries leaves 196.0% of checkouts failing on payment alone (budget: 2.0%)
fix: raise max_retries, or lower gateway_failure_rate if the gateway improved
[UNKNOWN_STATE_KIND] states[3].kind 'stage' is not one of ['step', 'gateway', 'retry', 'terminal']
fix: the engine only knows these four kinds; anything else is treated as a plain step
-- WARNINGS (1) --
[UNREACHABLE_STATE] states[4] 'gift_wrap' cannot be reached from 'cart_review'
fix: no path from start reaches this state — delete it, or wire it inSechs davon stammen aus einer Regeldatei. RETRY_BUDGET_TOO_THIN stammt aus acht Zeilen Python, denn „erfüllt dieses Wiederholungsbudget das Ausfallziel des Produkts" ist Arithmetik, kein Schema.
Nun sieh dir einen Lauf an:
groundtruth --config examples/checkout-flow/groundtruth.toml replay standard_checkout --seed 3standard_checkout seed=3 outcome=success steps=7 fingerprint=52b66a2024a61b5d
metrics: latency_ms=2506 payment_attempts=2 steps=7
-- TRACE --
0. cart_review --always-->
1. shipping --always-->
2. payment_method --always-->
3. authorize --failure--> # attempt 1 declined
4. retry_decision --retries_left--> # 0 retry(s) used of 2
5. authorize --success--> # attempt 2 authorized
6. confirmed # terminal: successSeed 3 erzeugt immer diese sieben Schritte – auf deinem Rechner, in CI, nächstes Jahr. Das macht es lesenswert.
Und zweitausend davon:
groundtruth --config examples/checkout-flow/groundtruth.toml \
simulate standard_checkout --runs 2000 --seed 0 --gate --check-determinismstandard_checkout: PASS runs=2000 base_seed=0 fingerprint=449e16b50c8184c0
-- OUTCOMES --
success: 1767 (88.3%)
abandoned: 227 (11.3%)
payment_failed: 6 (0.3%)
-- METRICS (mean / p50 / p95 / max) --
latency_ms: 1587.75 / 1553 / 2566 / 3626
payment_attempts: 1.06 / 1 / 2 / 3
steps: 5.12 / 5 / 7 / 10
-- THRESHOLDS --
PASS rate:success = 0.8835 expected >= 0.8 (below this, the flow is losing customers faster than the business case allows)
PASS rate:stuck = 0 expected <= 0 (a run with nowhere to go is always a config bug, never bad luck)
PASS p95:latency_ms = 2566 expected <= 4000 (95th-percentile checkout wall time, retries included)
PASS mean:payment_attempts = 1.0585 expected <= 1.6 (rising attempts mean the gateway is degrading or the retry policy is too eager)
note: determinism: 20 seeds re-ran identicallyDer Teil, der sich bezahlt macht
Erhöhe eine Zahl – shipping.abandon_chance von 0.05 auf 0.28, die Art von Änderung, die wie ein Produkt-Feinschliff aussieht und das Review besteht:
$ groundtruth lint standard_checkout
standard_checkout: OK errors=0 warnings=0 infos=0 # exit 0
$ groundtruth simulate standard_checkout --runs 2000 --seed 0 --gate
standard_checkout: FAIL runs=2000 base_seed=0 fingerprint=5a7c0d9feed5adca
-- OUTCOMES --
success: 1336 (66.8%)
abandoned: 660 (33.0%)
-- THRESHOLDS --
FAIL rate:success = 0.668 expected >= 0.8
PASS rate:stuck = 0 expected <= 0
PASS p95:latency_ms = 2549 expected <= 4000
PASS mean:payment_attempts = 0.795 expected <= 1.6
# exit 1Strukturell perfekt. Einundzwanzig Prozentpunkte Conversion weg. Kein Schema, Typsystem oder Code-Review fängt das ab; ein Seeded Batch mit deklariertem Band fängt es in vier Sekunden, im Pull Request, bevor ein Mensch den Diff liest.
Es funktioniert auch in die andere Richtung. express_checkout erzielt eine höhere Erfolgsquote als der Standardfluss – 91,0 % – und ist die schlechtere Konfiguration: ihre Zahlungsausfälle liegen bei 3,9 % gegenüber 0,3 %, versteckt in einer Kennzahl, die gut aussieht. Das Aggregat übersieht es; der handgeschriebene Validator sagt es deutlich:
[RETRY_BUDGET_TOO_THIN] policy.max_retries 18% gateway failure with 1 retries
leaves 3.2% of checkouts failing on payment alone (budget: 2.0%)Keine Ebene ersetzt die andere. Deshalb gibt es zwei.
Einführung
Ein Modul, eine Konfigurationsdatei. examples/checkout-flow/groundtruth_app.py ist die gesamte Vorlage – etwa hundert Zeilen inklusive Kommentaren.
from groundtruth_mcp import Context, Issue, Loaded, Toolkit, Trace
kit = Toolkit(name="my-project", subject_noun="pipeline")
@kit.loader
def load(name: str):
path = CONFIG_DIR / f"{name}.yaml"
if not path.is_file():
return None # → "no pipeline named X; available: ..."
return Loaded(subject=parse(path), source=str(path))
@kit.validator
def check(pipeline, ctx: Context) -> list[Issue]:
... # the checks a rule file can't express
@kit.runner
def run_once(pipeline, seed: int, ctx: Context) -> Trace:
... # one run, pure in (pipeline, seed)Allein @kit.runner gibt dir sowohl replay als auch simulate – die Bibliothek führt es einmal pro Seed aus und behält das Ergebnis. Alles andere (Seed-Batching, Aggregation, Perzentile, Schwellenwert-Gating, Ausgabe-Budgetierung, Fehlerformulierung, die MCP-Oberfläche) kommt aus dem Paket.
# groundtruth.toml
[project]
toolkit = "groundtruth_app:kit"
[lint]
rules = "rules.toml"
[[thresholds]]
metric = "rate:success"
min = 0.80
note = "why this number, for whoever has to change it"Dann sagt dir groundtruth doctor, was verdrahtet ist, groundtruth serve übergibt die Werkzeuge an einen Agenten, und groundtruth simulate --gate blockiert den Merge. Vollständige Anleitung mit domänenspezifischen Beispielen: docs/ADOPTION.md.
Regeln, die du kostenlos bekommst
Strukturprüfungen werden deklariert, nicht geschrieben. Zwölf Typen, die jeweils eine Art abdecken, wie strukturierte Konfigurationen tatsächlich verrotten:
Typ | Fängt | Schlüsselfelder |
| halb geschriebene Einträge |
|
| doppelte IDs, die die Engine still überschattet |
|
| einen Wert, den deine Engine nicht verarbeitet |
|
| einen String, wo eine Zahl hingehört |
|
|
|
|
| IDs, die einen Namensvertrag brechen |
|
| eine leere Liste, wo ein Eintrag erforderlich ist |
|
| einen Verweis auf etwas, das umbenannt wurde |
|
| einen Knoten, den kein Pfad vom Start erreicht |
|
| einen Nicht-Endknoten ohne Ausweg |
|
| einen Knoten, der zu sich selbst übergeht |
|
| einen Ring ohne Ausgang (mit einer |
|
Selektoren sind eine bewusst kleine Pfadsprache – states[].transitions[].to – und jede Übereinstimmung meldet den konkreten Pfad, an dem sie gefunden wurde, was states[3].transitions[1].to möglich macht statt „ein Übergang ist ungültig".
Jede Regel akzeptiert optional code, severity und hint. Der Hint ist der Satz, nach dem ein Agent handelt; schreibe ihn also im Imperativ.
Schreibgeschützt bedeutet schreibgeschützt
query führt genau ein SELECT aus. Zwei Ebenen erzwingen das, und sie sind nicht gleichwertig.
Der Keyword-Scan ist User Experience: Er lehnt DELETE FROM … mit einem erklärenden Satz ab, statt mit einem Datenbankfehler, den das Modell entschlüsseln muss. Er ist nicht die Grenze – eine Blockliste über Text ist immer einen Fall von falsch entfernt, und die kanonische Demonstration ist SELECT * INTO audit_copy FROM users, das mit SELECT beginnt, kein verbotenes Verb enthält und eine Tabelle erzeugt.
Die Grenze ist der Datenspeicher: mode=ro plus PRAGMA query_only bei SQLite, eine READ ONLY-Transaktion bei PostgreSQL, ein Statement-Timeout bei beiden. Die Tests umgehen die Absicherung vollständig und bestätigen, dass die Verbindung sich weiterhin weigert.
Spalten-Redaktion ist die einzige Textebenen-Kontrolle, die Durchsetzung ist: Werte in deny_columns werden nach dem Abruf und bevor der Ergebnisstring existiert verworfen, sodass SELECT * sie nicht leaken kann. Alles Zurückgegebene ist in <untrusted>-Tags gewrappt, weil eine notes-Spalte, die etwas enthält, das wie eine Anweisung geformt ist, Daten sind und als Daten ankommen müssen.
CLI
groundtruth [--config PATH] <command>
doctor what is wired up, what is missing
targets the configs this project exposes
lint TARGET exit 1 on errors
replay TARGET --seed N one deterministic run, full trace
simulate TARGET --runs N --seed N --gate --check-determinism
query "SELECT ..." one read-only statement
schema readable tables and columns
serve the MCP server, over stdioExit-Codes: 0 sauber, 1 Befunde (Lint-Fehler, ein Schwellenwert außerhalb seines Bandes, Nichtdeterminismus), 2 konnte nicht ausgeführt werden (schlechte Konfiguration, fehlende Fähigkeit, abgelehnte Abfrage). Füge --json zu lint, replay und simulate hinzu, für maschinenlesbare Ausgabe.
Installation
pip install groundtruth-mcp # core: rules, simulation, gating, CLI
pip install "groundtruth-mcp[mcp]" # + the MCP server
pip install "groundtruth-mcp[postgres]" # + the PostgreSQL data sourcePython 3.11+. Der Kern hat keine Drittanbieter-Abhängigkeiten – das ist beabsichtigt, damit das CI-Gate nicht vom Agent-Stack abhängt. Ein nackter Runner kann deine Schwellenwerte durchsetzen, ohne ein SDK zu installieren.
Verdrahtung verifizieren
python scripts/mcp_smoke.py [path/to/groundtruth.toml]Startet den Server als echten Subprozess, initialisiert über stdio, listet Werkzeuge, ruft zwei davon auf, gibt zurück, was zurückkam – dieselbe Sequenz, die ein Client ausführt. Führe es aus, bevor du dem Agenten die Schuld gibst, deine Werkzeuge nicht zu sehen.
Was CI bei jedem Pull Request durchsetzt
Kein Abzeichen, das bedeutet „die Tests liefen" – sechs Dinge, von denen jedes schon etwas blockiert hat:
Check | Warum es ein Gate ist und kein Vorschlag |
| Einschließlich |
| Das Paket liefert |
| 69 Tests, Abdeckungsuntergrenze 75 % (aktuell 78 % mit Branch-Abdeckung) |
| Ein echter Subprozess, echtes stdio, echtes |
| Das eigene Argument des Projekts, auf sich selbst angewendet |
| Ein Lint, das nicht fehlschlagen kann, ist dekorativ |
Einschränkungen, klar benannt
Die SQL-Tabellen-Allowlist ist textbasiert. Sie scannt nach Bezeichnern nach
FROMundJOIN. Echte Durchsetzung pro Tabelle ist ein Datenbank-Grant; dies ist eine Schutzleiste mit einer guten Fehlermeldung, und die Read-only-Transaktion ist das, was tatsächlich hält.Die Keyword-Blockliste greift innerhalb von String-Literalen. Eine Abfrage, die auf einen Wert mit
grantfiltert, wird abgelehnt. Das zu beheben erfordert einen echten SQL-Parser, den zu bauen sich nicht lohnt, wenn der Parser nicht die Grenze ist.Auto-
LIMITist eine Heuristik. EinLIMITin einer Unterabfrage unterdrückt das Hinzufügen auf oberster Ebene.max_rowsbegrenzt weiterhin, was gerendert wird.Selektoren filtern nicht.
states[].transitions[]geht durch alles; es gibt keinstates[kind=terminal]. Eine Prädikatsprache wäre das dritte Feature, das niemand verlangt hat. Schreibe stattdessen einen@kit.validator.Schwellenwerte sind projektweit, nicht pro Ziel. Jedes Ziel in einem Projekt wird gegen dieselben Bänder bewertet. Projekte, deren Konfigurationen wirklich unterschiedliche Bänder benötigen, sollten separate
groundtruth.toml-Dateien sein.Die PostgreSQL-Quelle ist implementiert, aber leicht getestet – die Testsuite beweist die Grenze gegen SQLite, wo sie überall ohne Service-Container laufen kann.
Woher das kommt
Extrahiert aus einer privaten Codebasis, in der sich das Muster seinen Platz verdient hat: eine Authoring-Pipeline, deren Mitwirkende immer wieder Konfigurationen auslieferten, die die Schema-Validierung bestanden und zur Laufzeit brachen. Die domänenspezifischen Teile blieben zurück. Was sich verallgemeinerte, war die Form — check, replay, simulate, query — plus eine Reihe von Entscheidungen, die sich als wichtiger herausstellten als die Feature-Liste:
Eine Schwellenwertliste, gelesen sowohl vom Agenten als auch von CI, weil zwei Kopien voneinander abwichen und das Tool eine Weile lang PASS für Zahlen meldete, die CI abgelehnt hätte.
Fehler, die die gültigen Alternativen inline benennen, denn ein Agent, der einen zweiten Aufruf tätigen muss, um zu erfahren, was er übergeben darf, wird stattdessen raten.
Tool-Beschreibungen, die aus der Live-Konfiguration zusammengestellt werden, denn eine veraltete Beschreibung ist ein Tool, das der Agent falsch und dennoch selbstbewusst verwendet.
Ausgabe auf jedem Pfad begrenzt, denn eine ausufernde Abfrage kann den Rest der Konversation verdrängen.
docs/ARCHITECTURE.md enthält die Modulübersicht und die vollständige Begründung.
Lizenz
MIT.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
A paid remote MCP for OpenAI Codex agent coordination MCP, built to return verdicts, receipts, usage
Free public MCP for AI agents — 193 tools, 44 workflows. No API key.
Agent Replay Debugger MCP — record every agent step + deterministic replay. Step-debugger for
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ZhenGtai123/groundtruth-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server