groundlens
OfficialThis server exposes a single MCP tool, find_unsupported_words, that proofreads a RAG answer against its retrieved sources and returns the weakest-supported words with evidence for a human reviewer.
Checks grounding: Given an answer and sources (id + text), finds which words the sources least support, returning the weakest anchors rather than a verdict.
Two checks in one: Numbers are checked arithmetically (formatting-normalized, so 10,000 = 10000 = $10,000, with locale support), while words are checked by embedding similarity.
Configurable depth (k): Returns the k weakest anchors (default 4).
Locale-aware number parsing:
localeparameter (e.g., 'es' vs 'en' vs 'und') controls how decimals/thousands separators are interpreted.Evidence with receipts: Each finding includes the word, support score, the closest matching text in sources, the source id, and notes — plus a top-level floor, encoder id, and sha256 of the result for reproducibility.
No threshold, no hallucination verdict: It deliberately avoids classifying answers as right/wrong; it reports marks and lets the reader decide.
Private by default: Runs as a local stdio MCP server (for Claude Desktop/Code, Cursor, VS Code), so no text leaves the machine.

Your verification and evidence layer for AI.
What it is · How it works · Engine · Verifiers · Policies · Evidence records · Quick start · Determinism · Examples · FAQ · Roadmap
Related MCP server: Arkheia Hallucination Detection MCP
What GroundLens is
GroundLens checks what an AI system said, decides under rules you wrote, and produces a signed record that anyone can verify offline. When source documents exist, it checks the answer against them, exactly for numbers and by anchoring for words; when they do not, other verifiers speak (symbolic rules, geometric grounding indices, an LLM judge if your policy allows one). It runs locally, needs no knowledge of how your system is built, and treats every check as evidence a third party can inspect rather than a score you have to trust.
Best for teams | Standout |
Shipping AI answers into regulated or high-stakes workflows who need proof, not a score, that each answer was checked. | Several verification methods under one contract, with or without source documents (exact numeric and rule checks, lexical grounding, geometric indices, an optional LLM judge); policies in YAML that decide instead of hard-coded thresholds; signed, hash-chained records verifiable offline; no network, no runtime dependencies. |
How it works
flowchart LR
A[answer + sources] --> B[claims] --> C[verifiers] --> D[evidence] --> E[policy] --> F[decision]
E --> G[signed evidence record]Verifier | Policy |
A verifier produces evidence, not truth. Exact numeric checks, lexical grounding, semantic similarity, NLI, the geometric SGI and DGI indices, symbolic rules, your own verifiers and, if you allow it, an LLM judge: each one reports what it measured and how sure it is. None of them decides. | A policy interprets the evidence. A short YAML file you control says which verifiers are required, recommended, optional or forbidden, what thresholds apply, and how evidence becomes a decision. It can map each outcome to the governance or regulatory control it concerns, such as an article of the EU AI Act. |
The whole chain becomes a record. Input hashes, the verifiers and model hashes that ran, the evidence, the policy and its hash, the decision, the regulatory mapping, and the hash of the previous record, sealed with an Ed25519 signature. A log of records is an audit trail you can hand over as a file.
GroundLens is AI system agnostic. It works on outputs and evidence, locally, with no network access, so independent verification is possible even in sensitive environments.
Engine
Groundlens engine is a Rust library wrapped for Python, with no runtime dependencies and no network access of any kind. It contains the claim extractor, the exact numeric verifier (numbers, currencies, percentages, physical units, in several locales), the symbolic rules verifier, the policy engine with two bundled policies, and the signed evidence records.
The engine is a Rust workspace under crates/: contracts and hashing (gl-core), text normalisation (gl-text), numerals and units
(gl-numeric), the verifiers, the policy engine, records, bundles, the model host (gl-onnx, on tract, no
native library) and the one pipeline everything calls (gl-engine). The Python package is a thin binding over it; glv is the same engine as a
binary. No engine crate depends on an HTTP or TLS library, and a CI job fails the build if one ever does.
cargo build --release # engine and glv
cd python && maturin build --release # Python wheelVerifiers
verifier | what it does | guarantee | in |
| numbers, currencies, percentages and physical units, compared exactly in base units: | exact, bit-identical everywhere | yes |
| your own symbolic rules (an APR must be a percentage, a date must fall inside the contract term) | exact | yes |
| whether each word of the answer is anchored in the sources, by contextual token similarity on a frozen multilingual encoder, reported as the weakest anchor rather than an average | reproducible: pinned model hash, scores within 1e-6 across machines | with the base bundle |
NLI, semantic, SGI, DGI, LLM judge | entailment, meaning, geometric grounding and model-based judgement | optional verifiers, see the roadmap | later releases |
Locales matter for numbers: 1.234 is one thousand in Spanish and one and a
bit in English. GroundLens reads en, es, ca, de, fr, it, pt,
nl and Swiss formats, knows short and long scale words, and keeps every
legitimate reading of an ambiguous numeral instead of guessing. The base
bundle's encoder covers about a hundred languages.
Policies
A policy is a short YAML file. Two policies over the same evidence can reach different decisions, and both are correct: that is where your risk appetite lives, not in the engine.
record = verify(answer, sources, policy="eu_ai_act_high_risk_v1")
record.decision # 'FAIL'
record.regulatory_mapping # [{'article': 'Art. 15(1)', ...}, {'article': 'Art. 12(1)', ...}]The bundled eu_ai_act_high_risk_v1 policy maps outcomes to Art. 15(1)
(accuracy and robustness) and Art. 12(1) (record keeping) of Regulation
(EU) 2024/1689. Write your own with Policy.from_yaml(); every policy has a
version and a hash, and the hash goes into every record it decides.
id: acme_rag_v1
version: 1.0.0
verifiers:
required: [groundlens.numeric, groundlens.lexical]
forbidden: [llm_judge.*]
thresholds:
groundlens.lexical: { support_min: 0.60, guard_band: 0.02 }
decision:
any_contradiction_from: [groundlens.numeric, groundlens.rules.*]
unresolved_claims: REVIEWScores from statistical verifiers drift slightly between machines, so every
threshold carries a guard band: a score inside the band is REVIEW
everywhere, never PASS on one laptop and FAIL on another.
groundlens policy lint refuses a band narrower than the verifier's
declared tolerance.
Every check leaves a record
record.content_hash # same input, policy and bundle → same hash, on any machine
record.verify() # recompute every hash and the Ed25519 signature, offline
Record.verify_chain(Record.read_log("records.jsonl"))Change one byte anywhere in a record and verification fails. Append records
to a JSON Lines log and each one carries the hash of the previous one.
groundlens report turns a log into a human-readable report with a
one-page guide for auditors.
Quick start
pip installinstalls the groundlens engine.
pip install groundlens # installs the GroundLens enginefrom groundlens import verify
question = "What is the invoice total?"
source = "...the total amount due is 10,000 dollars, payable within 30 days..."
answer = "The invoice total is 1,000 dollars, due in 30 days."
record = verify(answer, [("invoice.pdf#p1", source)], question=question)
print(record.report())FAIL policy=groundlens_default_v1 record=rec_350455f44e60_4dbfea8eb79c
c2 groundlens.numeric contradicted 0.00 nearest in invoice.pdf#p1: '10,000 dollars'groundlens bundle pull baseis a separate, explicit step.
groundlens bundle pull base # optional: enables the lexical verifier (≈470 MB, once)It downloads the base bundle (about 470 MB: the multilingual-e5-small encoder in f32,
its tokenizer and a manifest of hashes) from this repository's releases
into a per-user directory, checks it against a hash pinned in the engine,
and refuses anything else. It is the only command in the package that
opens a network connection. With the bundle installed, the lexical
verifier runs and every record names the bundle by hash. In an isolated
environment, copy the bundle directory by hand and point
GROUNDLENS_BUNDLE_DIR at it.
groundlenscommand line. Everything in this README except the lexical verifier works with that install alone. Nothing leaves your machine.
groundlens verify --answer answer.txt --question question.txt \
--source "invoice.pdf#p1=invoice.txt" --policy eu_ai_act_high_risk_v1 --log records.jsonl
groundlens record verify records.jsonl # every hash, every link, every signature
groundlens report records.jsonl --out report # report.md, report.json, README-auditor.md
groundlens policy lint policies/eu_ai_act_high_risk_v1.yaml
groundlens bundle status # is the base bundle installed, where, which hashExit codes: 0 PASS, 1 FAIL, 2 error, 3 REVIEW. The Rust binary
glv exposes the same commands.
Determinism
Same input, same answer, on any machine
Each verifier declares what it guarantees. exact verifiers use no
floating point at all. reproducible verifiers run a pinned model, in
f32, on a pure-Rust inference engine, and their scores stay within a
declared tolerance. Anything non_deterministic, such as an LLM judge, is
recorded with its model, prompt hash and settings, and only decides if the
policy says so.
This is tested rather than promised: the CI runs the invoice example, with and without the lexical channel, on Linux, macOS and Windows under a Turkish locale and a Pacific timezone, and compares the record hash with a committed value.
Examples
Two notebooks under examples/notebooks run in
Google Colab:
Verify an AI answer against its sources: one example in English, German, French, Spanish and Italian, from
pip installto a signed record, with a wrong number, a paraphrase and a policy change.Evidence records for auditors: a log of verifications, chain verification, tamper detection, the EU AI Act mapping and the report an auditor receives.
Contributions are welcome; see CONTRIBUTING.md and SECURITY.md.
groundlens.dev · Javier Marín, 2026 (javier@groundlens.dev)
Available Tools
1 toolfind_unsupported_wordsA
Given an answer and the sources it was supposedly drawn from, return the words the sources least support, each paired with the closest thing in the sources.
Numbers are checked by arithmetic, not by meaning: a value is present or it is not, and formatting is normalised first, so 10,000 and 10000 and $10,000 are one number. Words are checked by embedding similarity.
Returns evidence for a human to judge. It does NOT return a verdict on whether the answer is hallucinated, and there is no threshold to compare the floor to. Report the weakest anchors and let the reader decide.
| Name | Required | Description | Default |
|---|---|---|---|
| k | No | ||
| answer | Yes | ||
| locale | No | und | |
| sources | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description takes full responsibility for behavioral disclosure. It explains how numbers and words are checked (arithmetic vs embedding similarity), normalisation, and that it returns evidence for human judgment. It also clarifies there is no threshold, adding depth.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is succinct and logically structured: purpose, method, and clarification. Every sentence contributes value, with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema exists, return format is not needed. For a tool with 4 params and 2 required, the description provides enough behavioral context for an agent to invoke it appropriately. Minor gap: no mention of side effects or rate limits, but for an analysis tool this may not be critical.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It explains 'answer' and 'sources' but does not explain 'k' or 'locale'. The mention of 'floor' hints at k but is ambiguous. Only partial coverage for half the parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states exactly what the tool does: identifies the words an answer's sources least support, paired with closest matches. It clearly distinguishes from a verdict tool, making its purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states what it does not do (i.e., does not return a verdict) and directs the user to interpret evidence themselves. This provides clear context, though it doesn't name alternatives (none exist).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.0- First observed
find_unsupported_words
TDQS
Scored across 1 tool
With only a single tool, there is no possibility of confusion or overlap between tools. The tool's purpose is clearly defined and distinct by default.
The tool name 'find_unsupported_words' follows a predictable verb_object pattern with clear separation using underscores. As the only tool, naming conventions are uniform and unambiguous.
A single tool for 'groundlens' feels very thin. The server's name implies a broader scope around grounding or hallucination detection, but it only exposes one specific function, leaving many likely related operations unaddressed.
The tool covers one specific aspect of grounding analysis (finding unsupported words) but provides no overall verdict, no threshold, and no supporting functions like source retrieval or metric computation. The domain appears incomplete for comprehensive hallucination assessment.
Maintenance
Related MCP Connectors
Fact-checks generated content against your sources of truth showing what to trust, change, & verify.
Real-time fact-check, citation verification, and source-freshness for AI agents.
Prose linter + AI-slop detector: weasel words, passive voice, hedging, and research-cited AI tells
Turn grounded AI answers into trusted comparisons, plans, timelines, and decision views.
Related MCP Servers
- Apache 2.0

Arkheia Hallucinationofficial
AlicenseNot gradedqualityBmaintenanceDetect fabrication and hallucination in any LLM output. Score responses from GPT-4o, Claude, Gemini, Llama and 30+ models. Free tier included.1MIT- AlicenseBqualityCmaintenancea typescript mcp to a langfuse MCP that enables you to see and connect agents to lanfuse data2787 npm1MIT
- AlicenseAqualityBmaintenanceMCP server for verifying AI agent claims vs reality — single-transcript inline grounding-check that flags when an agent's response states facts not in the input context, when its code silently swallows exceptions and substitutes mock data, or when its multi-turn transcript contains contradictions or unverified completion claims. Sub-second, local, free, no API calls.41MIT