Hallucination Firewall
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Hallucination Firewallcheck my draft answer against the tool outputs and flag unsupported claims"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Hallucination Firewall
An MCP server that checks an AI agent's final answer against the tool outputs it actually received, and blocks or flags anything unsupported.
Agents call tools, get correct data back, and still misstate it in the answer: a wrong figure, an invented detail, a claim no source supports. The Hallucination Firewall records everything the tools returned, splits the draft answer into single-fact claims, checks each one against that evidence with a tiered verifier, and returns a verdict per claim with the tool output that proves or disproves it.
https://github.com/Parvathy-Raj/hallucination-firewall/raw/main/docs/demo.mp4
Demo: Claude Code answers a UAE VAT question using a document-search tool. The draft gets the registration threshold wrong; the firewall flags it ❌, cites the FTA guide passage with the correct figure, and the corrected answer goes out with a citation.
┌──────────┐ ┌────────────────────────────┐ ┌──────────────────┐
│ Agent │────▶│ Hallucination Firewall │────▶│ Downstream MCP │
│ (Claude) │◀────│ (MCP server + client) │◀────│ servers (DB, RAG,│
└──────────┘ │ │ │ web, files...) │
│ 1. Evidence ledger │ └──────────────────┘
│ 2. Claim extractor │
│ 3. Evidence retriever │
│ 4. Verifier (tiered) │
│ 5. Verdict + rewrite │
└────────────────────────────┘How it works
Evidence ledger. Every tool output is recorded, chunked and indexed with hybrid retrieval (BM25 +
bge-smallembeddings, merged with reciprocal rank fusion). Outputs get there three ways:Proxy mode: the firewall wraps your other MCP servers and re-exposes their tools (
uae_docs__search_guides), recording every result.Claude Code hook: a
PostToolUsehook records the output of any tool (Read, WebFetch, Bash, other MCP servers).By hand / SDK:
record_evidence(source, content), orfw.record()/@fw.watchin Python.
Claim extraction. An LLM splits the draft into atomic claims and labels each one factual, opinion or hedge. Only factual claims are checked. (A rule-based extractor is used when no LLM is configured.)
Tiered verification, cheapest first. Each tier either decides or passes the claim on:
Tier
Method
Cost
Catches
0
Exact checks: numbers, currency, percentages, dates, names, with rounding-aware matching
~0
$4.2Mvs$4.7M,AED 500,000vsAED 375,0001
NLI fact-checking model on CPU (
DeBERTa-v3-base-mnli-fever-anli)~0.15 s per evidence passage
Most clearly supported or contradicted claims
2
LLM judge, only when tier 1 is below the confidence threshold
$
Paraphrases, multi-hop, "the evidence doesn't say that"
Verdict + policy. Each claim is ✅
supported, ❌contradicted, ⚠️unsupportedor ➖not_checkable. The policy decides whether to block, annotate or rewrite. The rewrite is deterministic on purpose (an LLM rewriter could add hallucinations of its own): wrong figures are replaced with the evidence value, unsupported claims are hedged or removed, and citations to the tool outputs are added.
Related MCP server: footnote
The trust model (read this first)
An MCP server cannot force an agent to call verify_answer. This project closes that gap in three layers, from weakest to strongest:
Layer | How | Guarantee |
Server instructions | The server tells the agent to verify every answer built on tool results | Best effort: the model usually complies |
Claude Code |
| Enforced by the harness, inside Claude Code |
Python SDK |
| Enforced in your own code |
What it does not do: check claims against world knowledge. A claim that no tool output supports is unsupported, even if it happens to be true. That is the point: in an agent, an answer should be traceable to what the tools returned.
MCP interface
Tools
Tool | What it does |
| Verdict, confidence, deciding tier, evidence and correction for every claim, plus an overall grounding score and a block decision |
| Checks one claim |
| Full evidence passages and what each tier said |
| Corrected answer with citations |
|
|
| Add evidence that was not proxied or hooked |
| Clear the ledger for a new task |
Resources: ledger://session/current (everything the tools returned) and report://session/current (hallucination report for the session).
Quickstart
git clone https://github.com/Parvathy-Raj/hallucination-firewall
cd hallucination-firewall
uv sync --extra ml # tiers 1 and dense retrieval (CPU PyTorch); plain `uv sync` = tier 0 + BM25 only
uv run pytest -qCheck a draft from the command line:
uv run hfw verify --evidence report.txt --draft draft.txt --rewriteUse it in Claude Code
Copy
examples/claude_code/.mcp.jsoninto your project and fix the paths. It runs the firewall in proxy mode in front of the example UAE tax-docs server (downstream.json); put your own MCP servers there.Copy the hooks from
examples/claude_code/settings.jsoninto.claude/settings.json.capturerecords every tool output;stopmakes Claude verify before it answers.Set an LLM for claim extraction and the tier-2 judge (below). Without one, tiers 0 and 1 still run.
Claude Desktop: see examples/claude_desktop/claude_desktop_config.json.
Use it from Python (enforced in code)
from hfw import Firewall, HallucinationBlocked
fw = Firewall()
@fw.watch # every return value becomes evidence
def get_invoice(invoice_id: str) -> dict: ...
get_invoice(invoice_id="INV-1042")
try:
answer = fw.guard(draft) # raises if the policy blocks
except HallucinationBlocked as e:
answer, _ = fw.rewrite_grounded(draft)Full example: examples/sdk_example.py.
Configuration
Variable | Default | Meaning |
|
|
|
|
| Model for claim extraction and the judge |
| provider default | e.g. |
|
| Any HF NLI classifier; |
|
| Empty string = BM25 only |
|
| Shared by the server and the Claude Code hook |
| Downstream servers JSON (same as | |
| Extra |
Evaluation
Three benchmarks, all scored at the claim level (one claim per answer sentence, so human span labels map onto claims directly and every configuration sees identical claims):
RAGTruth (main benchmark): RAG answers (QA, summarization, data-to-text) with human-labelled hallucinated spans. Test split, sampled.
HaluEval QA: each question once with the right answer and once with a hallucinated one.
UAE agent traces (custom): 20 simulated agent turns built from the FTA guide chunks of my UAE regulatory RAG. The tool returns the gold passage plus two distractors; the draft has one correct sentence and three planted errors (changed number, negated obligation, claim from a guide that was never retrieved). Rebuild with
make traces UAE_CHUNKS=...; the corpus itself is not redistributed.
Metrics: precision / recall / F1 for catching hallucinated claims; false-flag rate (correct claims wrongly flagged); false-block rate (fully correct answers that the balanced policy would block, which is what users notice); latency p50/p95 per answer; LLM cost per answer.
make eval regenerates everything below. Every NLI score and LLM call is cached, so the configurations share identical judge calls and re-runs are free; cost and latency still charge each configuration for every call it makes.
Run make eval to fill this in.
Reading the results
The numbers here come from a small sample (rate limits on the free Groq tier I used for the judge), so treat differences of a few points as noise. The judge in these runs is gpt-oss-20b via Groq, not Claude; set HFW_EVAL_MODEL / HFW_EVAL_BASE_URL to compare others.
Repo layout
src/hfw/
├── server.py MCP server + tools + resources
├── proxy.py downstream MCP clients, ledger capture
├── ledger.py evidence store, chunking, hybrid index
├── claims.py claim extraction (LLM or rules)
├── verify/
│ ├── exact.py tier 0
│ ├── nli.py tier 1
│ ├── judge.py tier 2
│ └── pipeline.py confidence routing
├── policy.py strictness presets, block / annotate / rewrite
├── rewrite.py deterministic grounded rewrite with citations
├── firewall.py facade used by the server, CLI and SDK
├── hooks.py Claude Code PostToolUse + Stop hooks
└── cli.py `hfw serve`, `hfw verify`
evals/ datasets, runner, custom traces, results/
examples/ Claude Code / Desktop configs, demo server, SDK example
tests/ pytest (offline, no models needed)Likely questions
Objection | Answer |
"The agent can just skip verification." | The Stop hook or the SDK enforces it; the trust model above is explicit about what each layer guarantees. |
"The checker makes mistakes too." | The false-flag and false-block rates are measured, and the tier-1 threshold and |
"Too slow." | Tier 0 is microseconds and tier 1 runs on CPU; the LLM judge only sees the claims the cheap tiers could not settle. |
"What about facts not from tools?" | Out of scope by design: it checks claims against tool evidence, not world knowledge. |
Roadmap
MiniCheck / Vectara HHEM as alternative tier-1 models, and a FEVER run
Claim-to-span alignment so rewrites can edit inside long sentences
OpenTelemetry spans per tier
PyPI release
License
MIT
Available Tools
7 toolsexplain_verdictA
Show the full evidence passages and what each tier decided for a claim id (e.g. "r1.c2").
Inputs: claim_id (required). Returns: object
record_evidenceA
Add evidence by hand (e.g. text from a tool that is not proxied or hooked). Use the exact text the source returned, never your own summary.
Inputs: source (required), content (required). Returns: object
reset_sessionA
Clear the evidence ledger and verdict history (start of a new task).
Inputs: no named parameters. Returns: object
rewrite_groundedA
Return a corrected version of the draft: wrong figures replaced with the evidence values, unsupported claims hedged (or removed with unsupported="remove"), and citations to the tool outputs added.
Inputs: draft (required), unsupported. Returns: object
set_policyB
Configure the firewall.
Args: strictness: lenient | balanced | strict (presets for block_on and thresholds). tiers: which verifier tiers may run: 0 exact, 1 NLI model, 2 LLM judge. block_on: verdicts that block the answer, e.g. ["contradicted", "unsupported"]. action: what to do with a flagged answer: annotate | block | rewrite. threshold: tier-1 confidence (0.5-1.0) needed to decide without the LLM judge.
Inputs: tiers, action, block_on, threshold, +1 more. Returns: object
verify_answerA
Check every factual claim in a draft answer against recorded tool outputs.
Call this before giving the user any answer based on tool results. Returns a verdict per claim (supported / contradicted / unsupported / not_checkable), the evidence behind it, a correction for wrong figures, and whether the draft is blocked.
Args: draft: the full answer you intend to send. strictness: optional one-off override: lenient | balanced | strict. action: optional one-off override: annotate | block | rewrite.
Inputs: draft (required), action, strictness. Returns: object
verify_claimB
Check one standalone factual claim against recorded tool outputs.
Inputs: claim (required). Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
TDQS
Scored across 7 tools
Each tool targets a distinct step in the verify workflow: policy setup, evidence capture, single-claim check, whole-answer check, verdict inspection, and rewriting. The only mild overlap is verify_claim vs verify_answer, though the descriptions clearly distinguish one claim from every claim in a draft.
All seven tools follow a clean snake_case verb_noun pattern (verify_claim, set_policy, record_evidence, reset_session, verify_answer, explain_verdict, rewrite_grounded). No mixing of conventions.
Seven tools is well-scoped for a claim-verification firewall, with each tool covering a necessary stage of the workflow. Nothing feels padded or redundant.
The surface covers the full lifecycle: configure policy, record evidence, verify (single and bulk), explain verdicts, rewrite, and reset. Minor gaps exist in that there is no read-back of the current policy or a way to list/inspect recorded evidence, but core workflows are fully served.
Maintenance
Related MCP Connectors
Real-time fact-check, citation verification, and source-freshness for AI agents.
Fact-checks generated content against your sources of truth showing what to trust, change, & verify.
Checks the most important factual claims in AI output against live web sources by default.
Runtime gate for AI agent claims. Catches sycophantic drift, circular self-validation, causal errors
Related MCP Servers
AlicenseNot gradedqualityNot gradedmaintenanceEnables fact-checking of AI responses against reliable sources and validation of responses against document content to ensure accuracy and reliability.-- AlicenseNot gradedqualityDmaintenanceEnables agents to verify their own output mid-task by checking every claim against provided sources, returning supported, partial, unsupported, or contradicted verdicts with exact citations.MIT
- AlicenseAqualityCmaintenanceEnables production-grade answer verification for LLM agents by independently re-checking answers with a configurable verifier model, enforcing confidence policies, and producing Ed25519-signed, auditable verification results with optional web search and knowledge-base evidence.3MIT
- AlicenseNot gradedqualityCmaintenanceEnables an agent to verify every number in its answer against the tool outputs of the same session, storing recorded outputs and flagging fabricated values that no tool produced. It exposes check, record, and reset tools so agents can recompute, correct, or drop unvouched numbers before finalizing.MIT