reflect-mcp

reflect-mcp

Enables extraction of structured data from messy text with multi-model verification and human-in-the-loop review, surfacing only high-confidence results or flagging uncertain fields for confirmation.

Category
Visit Server

README

reflect-mcp

An MCP server for LLM tasks that verify themselves: Generator → second-model Critic → human-in-the-loop gate. Never a silent wrong answer.

tests coverage python license


1. TL;DR

reflect-mcp is a small multi-agent pipeline that checks its own work before surfacing it, exposed as an MCP server so it plugs into Claude Desktop, Cursor, or any MCP client. A Generator extracts a typed object from messy text; a second-model Critic scores every field and the whole extraction; a confidence-thresholded gate either returns the result or, if it isn't confident enough, returns a needs_review payload naming the exact fields a human should confirm.

                 ┌───────────┐      ┌──────────────┐      ┌────────┐      ┌──────────────┐
  messy text ──▶ │ Generator │ ───▶ │ heuristics + │ ───▶ │  Gate  │ ───▶ │  ok  /       │
                 │ (model A) │      │ Critic (B)   │      │ (HITL) │      │  needs_review│
                 └───────────┘      └──────────────┘      └────────┘      └──────────────┘
                   typed JSON        per-field + overall     threshold      flagged fields
                                       confidence            decision       + rationale

2. Why — the trust problem

LLMs are confidently wrong. The failure mode that actually hurts in production isn't a model that says "I don't know" — it's a model that returns a clean, plausible, wrong value with no signal that anything is off. reflect-mcp makes verification a first-class step: a different model critiques the output, deterministic structural checks run alongside it, and anything below a confidence threshold is escalated instead of emitted.

The pattern is well established: naive extraction pipelines look great in a demo and then quietly degrade on real inputs, and adding a multi-model verification + human-review gate is a proven way to claw accuracy back. The point reflect-mcp makes concrete: the verification discipline is the product, not the extraction call.

3. Demo

Messy input → typed JSON with per-field confidence → one low-confidence field flagged for review:

// input
"Hi team, bill from Globex Ltd. Ref GLX/2026/778, dated 2026-02-28.
 540.00 before tax, tax 43.20, amount due 683.20. Pay in GBP."

// output  (the model hallucinated the total; the critic + arithmetic check caught it)
{
  "status": "needs_review",
  "data": { "vendor_name": "Globex Ltd", "invoice_number": "GLX/2026/778",
            "invoice_date": "2026-02-28", "subtotal": 540.0, "tax": 43.2,
            "total": 683.2, "currency": "GBP" },
  "field_confidence": { "total": 0.12, "subtotal": 0.95, "tax": 0.95, "...": 0.95 },
  "overall_confidence": 0.70,
  "flagged_fields": [
    { "field": "total", "value": 683.2, "confidence": 0.12,
      "reason": "683.20 does not reconcile with 540.00 + 43.20; not found in source" }
  ],
  "notes": "1 field(s) below the 70% confidence threshold require human review before use."
}

(A ~20s GIF of this in Claude Desktop goes here in the published repo.)

4. Use it in Claude Desktop

Install and point Claude Desktop at the server. Add this to your claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "reflect-mcp": {
      "command": "/ABSOLUTE/PATH/reflect-mcp/.venv/bin/python",
      "args": ["-m", "reflect_mcp.server"],
      "env": {
        "REFLECT_PROVIDER": "anthropic",
        "ANTHROPIC_API_KEY": "sk-ant-...",
        "REFLECT_GENERATOR_MODEL": "claude-3-5-sonnet-latest",
        "REFLECT_CRITIC_MODEL": "claude-3-5-haiku-latest"
      }
    }
  }
}

Then restart Claude Desktop and ask it to "extract the invoice fields from this text …" — it will call the extract tool.

Tool exposed

Tool Args Returns
extract text: str, field_threshold=0.70, overall_threshold=0.75 { status, data, field_confidence, overall_confidence, flagged_fields, trace_id, notes }

When status == "needs_review", do not use data blindly — confirm the fields in flagged_fields first.

5. Architecture

Three roles, one shared trace id, clean layering (resolver-free by design — the pipeline has no infra dependency):

  • Generator (generator.py) — one LLM call → validated InvoiceData (Pydantic). Absent fields come back null; the model is instructed never to invent a value.
  • Critic (critic.py) — a different model (REFLECT_CRITIC_MODEL) is shown the source and the candidate extraction and must ground each field, returning per-field + overall confidence. A different model matters: self-critique with the same model tends to rubber-stamp its own errors.
  • Deterministic heuristics (heuristics.py) — a free, non-LLM signal: grounding (is a committed value actually present in the source?), arithmetic reconciliation (subtotal + tax == total), and ISO-date validation. These cap the Critic — a structurally impossible value can't be waved through on the Critic's word alone.
  • Gate (gate.py) — combines the two signals multiplicatively (combined = critic_confidence × heuristic_factor), flags any non-null field below field_threshold, and escalates the whole result if any field is flagged or overall confidence is below overall_threshold.

The extract tool (server.py) is the only module that imports the mcp SDK, and providers.py is the only module that imports a provider SDK — so the core pipeline and the entire test suite have zero hard dependency on either.

6. Evaluation

A tiny golden-file eval treats LLM quality as measurable, not vibes. eval/golden/dataset.jsonl holds labeled messy-text → expected-fields examples; eval/run_eval.py runs the full pipeline over them and reports field accuracy, exact-match accuracy, needs-review rate, and exactly where it fails.

make eval          # OFFLINE: deterministic, uses recorded model outputs (no API keys)
make eval-live     # LIVE: runs against REFLECT_PROVIDER (needs credentials)

Offline run (recorded fixtures, one example carries an injected hallucination to prove the harness catches misses):

============================================================
reflect-mcp eval  |  mode: OFFLINE (replay fixtures)
============================================================
examples          : 5
field accuracy    : 97.1%  (34/35)
exact-match acc   : 80.0%  (4/5)
needs-review rate : 20.0%  (1/5)
------------------------------------------------------------
field-level failures (where it breaks):
  [inv-004] total: expected 583.2 but got 683.2
============================================================
PASS: field accuracy 97.1% >= required 90.0%

make eval exits non-zero below --min-accuracy (default 0.90), so it works as an offline eval-CI gate with no secrets.

7. Testing

Deterministic tests around a stochastic core. The single LLM boundary (LLMClient) is mocked with a ScriptedLLMClient that returns canned JSON and raises if it's ever asked for more than expected — so pytest runs fully offline, with no API keys, and can't leak a real call.

make install       # .venv + package + dev/server deps
make test          # deterministic offline suite
make cov           # with coverage

The flagship test is tests/test_critic_catches_error.py: the Generator is fed a deliberately wrong value (vendor_name = "Invoice" — a word that does appear in the source, so the deterministic grounding check can't catch it), and the test asserts the second-model Critic catches it and the gate routes to needs_review. It also flips only the Critic's verdict and shows the decision flip to ok — proving the verification step is doing real work, not the plumbing.

8. Design decisions

  • Second-model Critic (multi-model reflection), not self-critique. The Critic defaults to a different, cheaper model than the Generator (sonnet generates, haiku critiques). Cross-model disagreement is a real signal; same-model self-review is mostly agreement theater.
  • Provider switching via env var. REFLECT_PROVIDER selects anthropic | openai | bedrock, so a reviewer runs it against whatever key they have. The Bedrock (Claude) path is there for AWS users; the public-API paths (Anthropic/OpenAI) let anyone try it without AWS. Provider SDKs are optional extras (pip install 'reflect-mcp[anthropic]') and imported lazily.
  • Confidence is multiplicative. critic_confidence × heuristic_factor — evidence can only lower confidence. A structurally impossible value (bad arithmetic, ungrounded amount) is capped regardless of how sure the Critic sounded.
  • Nulls are honest. A field absent from the source returns null and is never flagged — abstention is correct behavior, not a low-confidence guess.
  • Prompt versioning + injection awareness. Prompts live in versioned files (src/reflect_mcp/prompts/). Both instruct the model to treat document content as data, not commands — the source text is untrusted input, a basic prompt-injection guard.
  • Observability. Every stage emits a structured JSON log line carrying a shared trace_id, so one extraction is greppable end to end.

9. What I'd productionize next

  • Batching + caching of Generator/Critic calls; per-request cost/latency budgets.
  • A real human-review queue (persist needs_review payloads, route to a reviewer UI, feed corrections back as new golden examples).
  • Offline eval in CI on every PR (the make eval gate is already CI-shaped) plus periodic live eval to catch model drift.
  • Confidence calibration — replace hand-set thresholds with thresholds tuned on the labeled set to hit a target precision.
  • Streaming + partial results, and a richer schema library (invoices, receipts, resumes, log lines) behind the same verify-then-gate loop.

Repository layout

reflect-mcp/
├── src/reflect_mcp/
│   ├── schema.py          # Pydantic extraction + verdict/decision models
│   ├── providers.py       # provider switching (Anthropic/OpenAI/Bedrock), lazy SDKs
│   ├── scripted.py        # ScriptedLLMClient — the mock at the LLM boundary
│   ├── generator.py       # Generator stage
│   ├── critic.py          # second-model Critic stage
│   ├── heuristics.py      # deterministic structural checks
│   ├── gate.py            # confidence gate / HITL decision
│   ├── pipeline.py        # orchestration (generate → heuristics → critique → decide)
│   ├── logging_config.py  # structured JSON-line logging
│   ├── parsing.py         # robust JSON extraction + prompt loader
│   ├── prompts/           # versioned generator.txt / critic.txt
│   └── server.py          # FastMCP server exposing the `extract` tool
├── eval/
│   ├── golden/dataset.jsonl   # labeled golden examples
│   ├── fixtures/replay.json   # recorded model outputs for offline eval
│   └── run_eval.py            # eval harness
├── tests/                 # deterministic, fully offline
├── Makefile               # install / test / cov / eval targets
├── pyproject.toml
└── LICENSE

License

MIT — see LICENSE.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured