groundtruth-mcp
groundtruth-mcp
코딩 에이전트가 저장소의 모든 파일을 읽을 수 있어도 여전히 추측에 불과합니다. 이 프로젝트는 여러분의 프로젝트 자체의 검사, 재실행, 시뮬레이션, 쿼리를 MCP 도구로 바꿔서, 에이전트가 예측하는 대신 자신의 편집 결과를 관찰하도록 합니다.
중국어 문서 · 채택 가이드 · 아키텍처 · 고정 시드가 필요한 이유
문제
구조화된 구성(config) — 워크플로 그래프, 규칙 파일, 상태 머신, 파이프라인 정의 — 을 편집하는 에이전트는 잘못된 종류의 맥락으로 작업하고 있습니다. 스키마는 읽을 수 있습니다. 그러나 실제로 실행될 때 어떤 일이 일어나는지는 읽을 수 없습니다.
그래서 추론합니다. 재시도 한도를 바꾸고 안전하다고 말합니다. "안전하다"는 그럴듯해 보이는 diff가 주어졌을 때 가장 그럴듯한 다음 토큰이었기 때문입니다. 아무도 실행하지 않았습니다. 위반한 제약 조건은 세 파일 떨어진 불변식(invariant)에 있거나, 정책이 마지막으로 조정된 이후 아무도 샘플링하지 않은 분포에 있습니다.
해결책은 더 나은 프롬프트가 아닙니다. 에이전트에게 관찰할 무언가를 주는 것입니다.
하는 일
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| S여러분이 직접 작성하는 네 개의 작은 함수로 만들어진 다섯 가지 도구:
도구 | 답변 | 유용하게 만드는 속성 |
| 이 구성은 자체적으로 일관성이 있는가? | 모든 문제는 편집할 정확한 경로를 포함한다 |
| 내가 이것을 실행하면 어떻게 되나? |
|
| 내 변경이 전반적으로 더 나은가, 더 나쁜가? | 시드 배치, 분포, 임계값, 통과/실패 |
| 데이터에 실제로 무엇이 있는가? | 정규식이 아닌 데이터베이스가 강제하는 읽기 전용 |
| 어떤 테이블이 존재하는가? | 아무도 스키마를 추측할 필요가 없도록 |
동일한 기능이 CLI로도 실행됩니다. 따라서 groundtruth simulate --gate는 에이전트가 최적화 대상으로 삼는 동일한 임계값을 읽는 병합 게이트입니다. 복사본이 하나뿐이므로 어긋날 수 없습니다.
60초
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_checkout번들로 제공되는 예제는 구성 기반 체크아웃입니다. 네 개의 페이지, 불안정한 결제 게이트웨이, 재시도 정책, 이탈하는 고객들이 있습니다. broken_checkout.json에는 에이전트가 실행할 수 없는 구성을 편집할 때 실제로 저지르는 실수들이 들어 있습니다.
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 in그중 여섯 개는 규칙 파일에서 비롯됩니다. RETRY_BUDGET_TOO_THIN은 여덟 줄의 Python에서 나옵니다. "이 재시도 예산이 제품의 실패 목표를 충족하는가"는 스키마가 아니라 산술이기 때문입니다.
이제 실행 하나를 지켜보세요:
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: success시드 3은 항상 그 일곱 단계를 만들어냅니다. 여러분의 머신에서든, CI에서든, 내년에도요. 그것이 이 결과를 읽을 가치가 있게 만드는 이유입니다.
그리고 그런 실행이 이천 개:
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 identically제 역할을 하는 부분
숫자 하나를 올려 보겠습니다. shipping.abandon_chance를 0.05에서 0.28로, 제품 조정처럼 보여서 리뷰를 통과하는 종류의 편집입니다:
$ 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 1구조적으로 완벽합니다. 전환율 21포인트가 사라졌습니다. 어떤 스키마, 타입 시스템, 코드 리뷰도 그걸 잡아내지 못합니다. 선언된 밴드가 있는 시드 배치는 사람이 diff를 읽기 전에 풀 리퀘스트에서 4초 만에 잡아냅니다.
반대 방향으로도 작동합니다. express_checkout은 표준 흐름보다 더 높은 성공률(91.0%)을 보여주지만 더 나쁜 구성입니다. 결제 실패율이 0.3%가 아니라 3.9%인데, 괜찮아 보이는 핵심 지표 안에 숨어 있습니다. 집계는 이를 놓치지만, 손으로 작성한 검증기는 명확히 지적합니다:
[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%)어느 한 계층도 다른 계층을 포함하지 않습니다. 그래서 두 개인 것입니다.
도입하기
모듈 하나, 구성 파일 하나. examples/checkout-flow/groundtruth_app.py가 전체 템플릿입니다. 주석 포함 약 백 줄입니다.
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)@kit.runner만으로 replay와 simulate를 모두 얻을 수 있습니다. 라이브러리가 시드마다 한 번씩 실행하고 결과를 보관합니다. 그 외의 모든 것(시드 배칭, 집계, 백분위수, 임계값 게이팅, 출력 예산, 오류 표현, MCP 표면)은 패키지에서 제공됩니다.
# 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"그런 다음 groundtruth doctor는 무엇이 연결되어 있는지 알려주고, groundtruth serve는 도구를 에이전트에 전달하며, groundtruth simulate --gate는 병합을 차단합니다. 도메인별 예제가 포함된 전체 안내: docs/ADOPTION.md.
기본 제공 규칙
구조 검사는 작성하는 것이 아니라 선언합니다. 구조화된 구성이 실제로 망가지는 방식을 각각 다루는 열두 가지 유형이 있습니다:
유형 | 탐지 내용 | 주요 필드 |
| 반쯤 작성된 항목 |
|
| 엔진이 조용히 가리는 중복 ID |
|
| 엔진이 처리하지 못하는 값 |
|
| 숫자가 들어가야 할 자리의 문자열 |
|
| 확률 필드에 있는 |
|
| 명명 규약을 위반하는 ID |
|
| 항목이 하나 필요한 곳의 빈 목록 |
|
| 이름이 바뀐 대상을 참조하는 참조 |
|
| 시작점에서 도달할 수 있는 경로가 없는 노드 |
|
| 나갈 방법이 없는 비단말(non-terminal) 노드 |
|
| 자기 자신으로 전이하는 노드 |
|
| 출구가 없는 순환 (의도적인 순환을 위한 |
|
선택자(selector)는 의도적으로 작게 만든 경로 언어입니다 — states[].transitions[].to — 그리고 모든 일치 항목은 발견된 구체적인 경로를 보고하므로, "전환이 유효하지 않습니다" 대신 states[3].transitions[1].to를 말할 수 있습니다.
각 규칙은 선택적으로 code, severity, hint를 받습니다. 힌트는 에이전트가 행동하는 문장이므로 명령형으로 작성하세요.
읽기 전용은 읽기 전용이다
query는 하나의 SELECT를 실행합니다. 두 계층이 이를 강제하며, 둘은 동등하지 않습니다.
키워드 스캔은 사용자 경험을 위한 것입니다. DELETE FROM …을 데이터베이스 오류 대신 "그렇게 할 수 없다"는 문장과 함께 거부합니다. 모델이 해독해야 하는 데이터베이스 오류 대신입니다. 이는 경계가 아닙니다. 텍스트에 대한 블록리스트는 항상 하나의 사례만으로도 틀릴 수 있으며, 전형적인 예는 SELECT * INTO audit_copy FROM users입니다. 이는 SELECT로 시작하고 거부된 동사가 없으며 테이블을 생성합니다.
경계는 데이터 저장소입니다. SQLite에서는 mode=ro와 PRAGMA query_only, PostgreSQL에서는 READ ONLY 트랜잭션, 둘 다에는 문장 타임아웃이 적용됩니다. 테스트는 가드를 완전히 우회하여 연결이 여전히 거부하는지 확인합니다.
열(column) 편집은 실제로 강제하는 유일한 텍스트 수준 제어입니다. deny_columns의 값은 가져온 후 결과 문자열이 생기기 전에 제거되므로 SELECT *로 유출될 수 없습니다. 반환되는 모든 것은 <untrusted> 태그로 감싸입니다. 명령처럼 생긴 내용이 담긴 notes 열은 데이터이므로 데이터로 표시되어야 하기 때문입니다.
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 stdio종료 코드: 0 정상, 1 발견 사항 있음(린트 오류, 밴드를 벗어난 임계값, 비결정성), 2 실행 불가(잘못된 구성, 누락된 기능, 거부된 쿼리). 기계가 읽을 수 있는 출력을 위해 lint, replay, simulate에 --json을 추가하세요.
설치
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+. 핵심에는 제3자 의존성이 없습니다 — 의도적인 설계로, CI 게이트가 에이전트 스택에 의존하지 않도록 합니다. SDK를 설치하지 않고도 기본 러너로 임계값을 강제할 수 있습니다.
연결 확인
python scripts/mcp_smoke.py [path/to/groundtruth.toml]실제 하위 프로세스로 서버를 생성하고, stdio로 초기화하고, 도구 목록을 가져오고, 그중 두 개를 호출하고, 반환된 결과를 출력합니다 — 클라이언트가 수행하는 것과 동일한 순서입니다. 에이전트가 도구를 보지 못한다고 탓하기 전에 실행하세요.
CI가 모든 풀 리퀘스트에서 강제하는 것
"테스트가 실행됨"을 의미하는 배지가 아니라 — 여섯 가지이며, 각각은 실제로 무언가를 차단한 적이 있습니다:
검사 | 게이트이지 제안이 아닌 이유 |
|
|
| 패키지가 |
| 테스트 69개, 커버리지 하한 75% (현재 분기 커버리지 포함 78%) |
| 실제 하위 프로세스, 실제 stdio, 실제 |
| 프로젝트 자신의 주장을 스스로에게 적용 |
| 실패할 수 없는 린트는 장식일 뿐이다 |
한계, 명확히 밝히면
SQL 테이블 허용 목록은 텍스트 기반입니다.
FROM과JOIN뒤의 식별자를 검사합니다. 실제 테이블별 강제는 데이터베이스 권한이며, 이것은 좋은 오류 메시지를 제공하는 가드레일일 뿐입니다. 실제로 지탱하는 것은 읽기 전용 트랜잭션입니다.키워드 블록리스트는 문자열 리터럴 내부에서도 일치합니다.
grant를 포함한 값으로 필터링하는 쿼리는 거부됩니다. 이를 고치려면 실제 SQL 파서가 필요한데, 파서가 경계가 아닌 상황에서 파서를 만드는 것은 가치가 없습니다.자동
LIMIT은 휴리스틱입니다. 하위 쿼리 내부의LIMIT은 최상위 추가를 억제합니다.max_rows는 여전히 렌더링되는 결과의 상한을 정합니다.선택자는 필터링하지 않습니다.
states[].transitions[]는 모든 것을 탐색합니다.states[kind=terminal]같은 것은 없습니다. 술어 언어는 아무도 요구하지 않은 세 번째 기능이 될 것입니다. 대신@kit.validator를 작성하세요.임계값은 프로젝트 전체에 적용되며 대상별이 아닙니다. 프로젝트의 모든 대상은 동일한 밴드로 판정됩니다. 구성에 실제로 다른 밴드가 필요한 프로젝트는 별도의
groundtruth.toml파일을 사용해야 합니다.PostgreSQL 소스는 구현되어 있지만 가볍게만 테스트되었습니다. 테스트 스위트는 서비스 컨테이너 없이 어디서나 실행할 수 있는 SQLite에서 경계를 검증합니다.
이 프로젝트는 어디서 왔는가
이 패턴이 자리 잡은 프라이빗 코드베이스에서 추출했습니다: 기여자들이 스키마 검증을 통과했지만 런타임에서 깨지는 구성을 계속 배포하던 작성 파이프라인입니다. 도메인별 부분은 뒤에 남았습니다. 일반화된 것은 형태 — 체크, 리플레이, 시뮬레이션, 쿼리 — 그리고 기능 목록보다 더 중요했던 일련의 결정들이었습니다:
에이전트와 CI가 함께 읽는 임계값 목록은 하나뿐입니다. 사본이 두 개가 되면 어긋나서 도구가 CI가 거부했을 수치에 대해 PASS를 보고하는 일이 발생했기 때문입니다.
유효한 대안을 인라인으로 명시하는 오류. 무엇을 전달할 수 있는지 알기 위해 두 번째 호출을 해야 하는 에이전트는 추측할 것이기 때문입니다.
실제 구성에서 생성된 도구 설명. 오래된 설명은 에이전트가 확신을 가지고 잘못 사용하는 도구가 되기 때문입니다.
모든 경로에서 출력 제한. 한 번의 과도한 쿼리가 대화의 나머지를 밀어낼 수 있기 때문입니다.
docs/ARCHITECTURE.md에는 모듈 맵과 전체 근거가 나와 있습니다.
라이선스
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