Polygraph MCP Server
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., "@Polygraph MCP ServerCheck if legacy_claims_archive is still used by the fraud model."
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.
Polygraph

A lie detector for data catalogs.
Your catalog says the fraud model reads raw_claims and legacy_claims_archive.
Polygraph runs the pipeline, watches what it actually touches, and reports that
legacy_claims_archive has not been read since the refactor — while
fee_schedule, which nobody declared, is merged into the training set on every
run. Then it writes those findings back into DataHub as tags, so the next person
to open the catalog sees them.
Catalog says X. Runtime proves Y. Polygraph reconciles them inside DataHub's own UI.
And it is built for agents. Agents read catalogs over MCP now, and an agent
asking DataHub inherits the catalog's mistakes at machine speed. Polygraph ships
its own MCP server to run beside DataHub's official mcp-server-datahub:
one supplies what the catalog claims — testimony — the other what the runtime
proved — evidence — and the agent is required to keep the two apart.
{
"mcpServers": {
"polygraph": {
"command": "python",
"args": ["-m", "polygraph.mcp_server"],
"env": { "PYTHONPATH": "<repo>/src", "POLYGRAPH_GMS": "http://localhost:8080" }
},
"datahub": {
"command": "python",
"args": ["-m", "mcp_server_datahub", "--transport", "stdio"],
"env": { "DATAHUB_GMS_URL": "http://localhost:8080" }
}
}
}Use full interpreter paths in a real config — the tested Windows version is
docs/claude_desktop_config.example.json.
A recorded run of the agent working across both servers is in
examples/agent_transcript.md.
Built for Build with DataHub: The Agent Hackathon.
The problem
Lineage in a data catalog is testimony. Someone wrote it down — by hand, or via an ingestion connector that parsed SQL, or from a DAG definition. Then the code changed and the testimony did not.
Nobody notices, because a catalog has no way to be wrong out loud. A stale edge looks exactly like a correct one. A missing edge looks like nothing at all. Data scientists make decisions on lineage that has quietly drifted from reality, and the first sign of trouble is a model that stopped working for reasons no one can trace.
Polygraph closes that loop by making the runtime testify.
Related MCP server: 事件知識圖譜 MCP Server
Verdict semantics
Verdict | Means | Tag written |
| Declared, and runtime capture proves data flowed along it |
|
| Declared, but nothing flowed along it in the captured run |
|
| Runtime proves the edge exists; the catalog never mentioned it |
|
Read those carefully, because the asymmetry is real and Polygraph does not paper over it:
VERIFIEDis evidence from the run that was captured. It is not a proof about every run.PHANTOMmeans nothing flowed in this run. A genuinely conditional edge — a branch not taken — will look phantom. The report always names the run it is based on so a human can make that call.An observed node with no entry in
urn_map.yamlis reported as unmapped, never guessed at. Polygraph does no fuzzy matching.
Verify this without installing anything
The quickstart needs Docker and an ~8 GB image pull. None of that is required
to check the claims — every artifact in this README ships in
examples/, and checking takes about a minute:
The three verdicts —
reconciliation_report.mdis the reconciliation table below, exactly as the run produced it.The incident report and its digest — hash the shipped file; it matches the digest stored on the DataHub document:
sha256sum examples/incident_report.md # acbedff47da6255e6b69877f722e52c2421f711e560d8517919e04bfe12ee5d3The agent, actually running —
agent_transcript.mdis a recordedask --llmrun over both MCP servers. Gate 11 asserts it called real tools on both and cited the numbers those tools returned.What it looks like in DataHub —
ui_undeclared_source.png,ui_undeclared_no_lineage.png,ui_lineage.png,ui_integrity_score.png,ui_incident_document.png— the tags, the empty lineage tab, the structured property, and the incident document, on screen.
Everything in examples/ except the agent transcript reproduces byte-for-byte
from a clean clone. The transcript says so itself: model output varies between
runs.
Quickstart
Prerequisites: Docker Desktop with ~8 GB allocated, Python 3.11+, ~13 GB free inside the Docker VM.
git clone https://github.com/kishanraj41/datahub-polygraph
cd datahub-polygraph
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# 1. Stand up DataHub (first run pulls ~8 GB, 10-25 min)
datahub docker quickstart
datahub init --username datahub --password datahub
# 2. Seed the catalog with deliberately imperfect lineage
python demo/seed_catalog.py
# 3. Run the pipeline under AutoLineage capture
python demo/pipeline.py --mode healthy
# 4. Reduce the capture to a dataset-level graph
python -m polygraph.cli observe \
--trace runs/healthy/trace.json \
--out runs/healthy/observed_graph.json --root .
# 5. Reconcile declared against observed
python -m polygraph.cli reconcile --observed runs/healthy/observed_graph.json
# 5b. Read catalog context (owners, descriptions, search) through DataHub's
# own MCP Server. Optional; needs DataHub running.
python -m polygraph.cli catalog --search "/q fee+schedule"
# 6. Write the verdicts back into DataHub
python -m polygraph.cli writeback \
--report examples/reconciliation_report.json \
--document examples/reconciliation_report.mdOpen http://localhost:9002 (login datahub / datahub) and look at
polygraph.demo.fee_schedule. It is tagged polygraph:undeclared-source.
On Windows, scripts/run_gate1.ps1 then scripts/run_gate2.ps1 do all of the
above with preflight checks.
The incident path
python demo/pipeline.py --mode buggy # one changed line collapses F1
python -m polygraph.cli observe --trace runs/buggy/trace.json \
--out runs/buggy/observed_graph.json --root . --mode buggy
python -m polygraph.cli incidentReal output
Everything below is copied from actual runs, reproduced on two machines
(Linux/Python 3.11 and Windows 11/Python 3.12) with the pinned dependencies in
requirements.txt. Full artifacts are in examples/.
Reconciliation — one of each verdict against the seeded catalog:
Verdict | Upstream | Operations observed |
|
| filter → concat → merge → drop → select → LogisticRegression.fit |
|
| — |
|
| filter → merge → drop → select → LogisticRegression.fit |
Incident — one changed line (quantile(0.999) → quantile(0.05)):
baseline | degraded | |
F1 | 0.8282 | 0.0000 |
rows after filter | 5994 | 300 |
AutoLineage — the author's own capture library (see
Prior work) — localises the collapse to the filter
operation with an impact score of 1.0, against a next-ranked deviation three
orders of magnitude lower. The incident document names the owning team
(urn:li:corpGroup:ml-platform-team) resolved live from DataHub ownership.
The document's sha-256 is stored on the DataHub document as polygraph_sha256.
The report is byte-reproducible: rerunning the incident path on the same code
produces the identical file and therefore the identical digest. You can check
both — hash the shipped file, then regenerate it and hash again:
sha256sum examples/incident_report.md
# acbedff47da6255e6b69877f722e52c2421f711e560d8517919e04bfe12ee5d3Ask an agent instead
mcp-server-datahub lets an agent read what the catalog claims. Polygraph
ships its own MCP server so the same agent can read what the runtime proved,
and the gap between them.
python -m polygraph.mcp_server # stdioRegister it alongside DataHub's own server — the config block at the top of
this README registers both, and the tested Windows version is
docs/claude_desktop_config.example.json.
Tool | Answers |
| Did this asset's declared lineage survive a real run? |
| Score, precision, recall, and which way the catalog is wrong |
| What does the pipeline read that nobody declared? |
| Which declared edges carried no data? |
| The hash-verified incident, root operation and owner |
| What the verdicts do not establish |
Two design choices worth naming, because they are what stop an agent from overstating the findings:
Absent evidence is never silence. Every tool returns evidence_available.
Asked about an asset Polygraph has not examined, can_i_trust says so
explicitly — "there is no evidence either way. Do not treat this as a clean
bill of health." An empty result that reads as "nothing wrong" is the failure
mode this project exists to complain about, so the server refuses to produce
one.
Every tool returns evidence, not just a verdict. An agent that relays the
verdict is correct; one that reads the operation paths can disagree with it.
explain_verdict_semantics exists so an agent can look up what a verdict does
not establish before repeating it to a person.
Or ask from the command line
polygraph ask "what undeclared sources does the pipeline read?"
polygraph ask "can I trust fee_schedule?"
polygraph ask "why did f1 drop?"Two backends over the same six tool functions — there is one implementation of "can I trust this asset", not two:
Deterministic (default). A keyword router. It needs no API key and produces identical output for identical input, which is why every claim in this README reproduces from a bare clone. It is not an agent and does not describe itself as one. Asked something it cannot classify, it declines and lists what it can answer rather than running the nearest-matching tool — silently answering a different question than the one asked is worse than declining. Exit code 3 when it does not understand, so it is scriptable.
LLM (--llm). A real tool-use loop over eight tools — Polygraph's six,
plus datahub_get_entities and datahub_search, which proxy to DataHub's own
MCP Server. The model picks tools and writes the answer from what they return.
Needs ANTHROPIC_API_KEY and pip install anthropic. Gated behind a flag on
purpose: a judge should never need credentials to verify a documented result.
Without a key it says so plainly rather than degrading into something else.
The system prompt requires the model to say "Polygraph observed" for
anything from a Polygraph tool and "the catalog says" for anything from a
datahub_* tool. The gap between evidence and testimony is the whole subject;
an agent that blurs it in its answer would defeat the point of asking.
A real run is recorded in
examples/agent_transcript.md — produced by
scripts/run_gate11.ps1, which asserts the agent actually called tools, reached
both servers, and cited the numbers those tools returned. It is the one file in
examples/ that is not reproducible, and it says so at the top: language
model output varies between runs. Everything else there reproduces byte for
byte.
export POLYGRAPH_ASK_MODEL=claude-opus-5 # or --model; the default rots
polygraph ask --llm "Polygraph says the pipeline reads an undeclared source. \
Which asset is it, is it registered in the catalog, and who owns it?"The model ID is deliberately overridable. This shipped with a hardcoded ID that
had been retired for two months, so --llm failed every time — and nothing
noticed, because nothing ran it. That is what Gate 11 is for.
Talking to DataHub through DataHub's own MCP Server
Polygraph reaches DataHub two ways: the acryl-datahub SDK, and
mcp-server-datahub — DataHub's own MCP Server, launched as a stdio
subprocess exactly the way an agent client launches it.
That is deliberate rather than decorative. Polygraph's argument is about what a catalog tells the people and agents who ask it, so it should be checking the answer DataHub actually gives an agent.
Three MCP tools are used:
Tool | What Polygraph asks it | Where |
| who owns this asset, how is it described |
|
| is this undeclared source registered at all |
|
| what does the catalog say feeds this job |
|
get_lineage is not the default path. The reason is a story worth keeping:
on the stack this was built against it returned a 500 from GMS, and the first
diagnosis written here — a search-dialect misconfiguration — was wrong. The
OpenSearch container had simply died, and GMS could not resolve the hostname
search. A DataHub stack in that state answers /config, reports healthy,
serves every entity read, and fails only the queries that need search. See
docs/DATAHUB_MCP.md for the full account, including what
would have caught it sooner.
The default stays on the SDK for the reason that outage demonstrated: it reads
the dataJobInputOutput aspect from MySQL and kept working the whole time, while
every search-backed path was down. A default should be the one that still answers
when something is broken.
When the MCP lineage path does work, scripts/run_gate10.ps1 reconciles twice,
once per path, and fails if the per-edge verdicts differ. Matching totals with
differing edges fails too — a summary-only check would let that through.
Every MCP response is parsed by walking the payload rather than indexing a
fixed path. A hard-coded path would break silently on a server upgrade: an empty
upstream set would make Polygraph report a catalog full of phantom edges, and a
missing ownership node would make it report an unowned asset. Confidently
wrong, in the exact way this project exists to catch. An empty lineage result
therefore raises instead of producing verdicts.
Not-found is reported in-band, and reading it is not optional. get_entities
runs an existence check and, for a URN it cannot find, returns
{"error": "Entity <urn> not found", "urn": ...} — an entry that still carries a
urn. Index the response by URN and treat presence as existence, and a
fabricated asset comes back as a real one. Polygraph honours the server's verdict
and reports the URN as found: false with the reason attached, because "no owner
recorded" and "no such asset" are different answers. Gate 10a checks it against
the live catalog with a deliberately fabricated URN.
A second finding, separate from the point-in-time bug: the MCP Server cannot
report a data job's declared inputs at all. inputOutput / inputDatasets /
outputDatasets appear in none of its GraphQL documents, though GMS exposes
DataJob.inputOutput — verified by scripts/probe_gms.py. That is a one-field
upstream fix, not a DataHub limitation.
Architecture
flowchart LR
subgraph Catalog["DataHub"]
DJ["dataJob<br/>train_fraud_model"]
DS["datasets"]
KB["knowledge base"]
end
subgraph Runtime["Your pipeline, unmodified"]
PIPE["pandas + scikit-learn"]
AL["AutoLineage<br/>239 import-time hooks"]
end
PIPE -->|"captured by"| AL
AL -->|"trace.json"| OBS["observed.py<br/>operation graph → dataset graph"]
DJ -->|"dataJobInputOutput"| DEC["declared.py"]
OBS --> REC["reconcile.py<br/>VERIFIED / PHANTOM / UNDECLARED"]
DEC --> REC
REC --> WB["writeback.py"]
WB -->|"tags"| DS
WB -->|"documents"| KB
REC --> INC["incident.py<br/>metric delta + root-cause ranking"]
INC --> KBThe interesting problem is the middle box. AutoLineage records lineage at
operation granularity — every pandas transform, every sklearn call, linking
dataframe versions. DataHub declares lineage at dataset granularity. Bridging
them required three non-obvious decisions, documented in
src/polygraph/observed.py:
Anchors. A node is catalog-visible if it is a file read, a file write, or a fitted model. Two anchors form an edge when a path connects them through no other anchor.
Union of paths, not shortest path. AutoLineage links
LogisticRegression.fitdirectly back to an early hub node, so the shortest route from source file to model skips the filter, the merge and the split. Those operations really ran between the two assets. Shortest-path would have left the incident report with nothing to name.Self-loops carry the payload. When a transform does not change dataframe identity, AutoLineage emits
parent_id == child_id. The planted bug is one of those. Discarding them is correct for topology and catastrophic for diagnosis.
Limitations
Stated plainly, because a tool that accuses catalogs of lying should be direct about what it cannot do.
Single-run evidence. Every verdict describes one captured run. Conditional branches not taken during capture are indistinguishable from dead edges.
Inputs only. Polygraph reconciles edges into a job. It does not reconcile outputs. AutoLineage cannot link a numpy
predict()output into a newly constructed DataFrame, so a declaredjob → predictionsedge would come backPHANTOM— a tool limitation dressed up as a stale catalog edge. Scoping to inputs is also what the tags claim:undeclared-source.Shape-preserving bugs are invisible. The analyzer ranks row-count and column-count deviations. A unit error that scales a column by 1000 while preserving every shape would not appear at all.
Localisation is to an operation, not a line number.
Info-level anomalies are excluded from the incident report. AutoLineage emits timing-sensitive counters at
infoseverity that vary between identical runs. Including them made the report non-reproducible — same code, same seed, different sha-256. They are filtered so the published digest means something; they do not affect localisation.Synthetic demo data.
demo/pipeline.pygenerates its own 6,000-row dataset with a fixed seed so the demo reproduces from a clean clone with no downloads. The AutoLineage paper's headline case uses the real Kaggle credit-card fraud dataset; that data is 150 MB and not redistributable, so it is not part of this repo.pathlib.Pathbreaks capture. AutoLineage's IO hooks testisinstance(path, str), sopd.read_csv(Path(...))records nothing — no file lineage at all, silently.demo/pipeline.pypassesstr(...)explicitly. This is an upstream bug, not a design choice.Everything search-backed depends on a healthy OpenSearch. That includes
reconcile --declared-via mcp,polygraph catalog --search, thedatahub_searchagent tool, and DataHub's own UI search and Lineage tab. When OpenSearch is down the rest of DataHub keeps answering, so this is easy to mistake for a Polygraph bug.scripts/stack_status.ps1settles it in seconds.DataHub's MCP Server cannot report declared job inputs. Its GraphQL documents never request
DataJob.inputOutput. So even with the point-in-time bug fixed, the only MCP route to declared lineage isget_lineage, which reports the rendered graph rather than the asserted aspect.Catalog context is unverified.
polygraph catalogand thedatahub_*agent tools report owners and descriptions as the catalog states them. Polygraph verifies lineage and nothing else — an owner field can be as stale as an edge, and Polygraph will not tell you.Existence comes from DataHub, not from inference. Polygraph reports an asset as missing only when the MCP Server's own existence check says so. It does not guess from how sparse a response looks — an entity can be registered and entirely undocumented, and calling that nonexistent would be as wrong as inventing one.
Python 3.12 is untested by DataHub. The CLI warns. It worked throughout this build, but 3.11 is the supported version.
One pipeline, one catalog shape. The URN mapping is explicit YAML. Using Polygraph on your own pipeline means writing your own
urn_map.yaml.
Prior work
Polygraph is built on the author's own earlier work, disclosed here rather than left for a reader to discover:
AutoLineage: Zero-Code Data Lineage for Python ML Pipelines — the runtime capture library Polygraph depends on, and the source of the planted-bug evaluation this demo's fraud pipeline is drawn from. SSRN 6683825
RudriQ — deviation-weighted root-cause analysis and deterministic audit reporting, which the incident path here follows. SSRN 7222278
autolineage is MIT-licensed and installed from PyPI as a pinned dependency.
No code from either project was copied into this repository.
What is new here, and what is not: the runtime capture and the anomaly ranking are prior work. The reconciliation of declared against observed lineage, the three verdicts and their semantics, the integrity score, the write-back into DataHub, and both MCP integrations were written for this project.
License
Apache License 2.0. See LICENSE.
Available Tools
6 toolscan_i_trustCan I TrustB
Answer whether an asset's declared lineage held up under runtime capture.
This is the question Polygraph exists for. Returns the verdict Polygraph reached, the evidence behind it, and an explicit statement of what the verdict does and does not establish.
| Name | Required | Description | Default |
|---|---|---|---|
| asset_urn | Yes | A DataHub dataset or dataJob URN. |
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 carries the full burden. It does disclose useful behavior: it returns a verdict, the supporting evidence, and an explicit statement of what the verdict does and does not establish — a meaningful disclosure about interpretive limits. But it says nothing about permissions, failure modes, or when no runtime capture exists, leaving gaps.
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 core purpose is front-loaded and reasonably tight, but the middle sentence ('This is the question Polygraph exists for.') is promotional filler that earns no place in a tool definition and dilutes the useful content.
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?
For a single-parameter query tool with an output schema present, the description need not explain return structure, and it correctly signals what the response establishes. The one real omission is sibling differentiation, which the description leaves entirely to inference.
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?
Only one parameter (asset_urn) and schema description coverage is 100%, so the schema already documents it. The description adds no syntax, format, or accepted-URN nuance beyond what the schema provides, so the baseline 3 applies.
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?
States a specific verb ('Answer whether...') and resource (an asset's declared lineage vs. runtime capture), which is clearly distinguishable from the name alone. However, it never differentiates itself from close siblings like get_integrity_score or explain_verdict_semantics, so the agent must infer the boundary.
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?
No when-to-use vs. when-not guidance is given. The line 'This is the question Polygraph exists for' is positioning, not routing, and does not tell an agent when to pick this over get_integrity_score or explain_verdict_semantics.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
explain_verdict_semanticsExplain Verdict SemanticsA
Explain exactly what Polygraph's three verdicts mean and do not mean.
Call this before presenting verdicts to a person. The verdicts are easy to overstate, and overstating them is the failure mode Polygraph exists to prevent in the first place.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden, and it does disclose the key behavioral risk: verdicts are easy to overstate, which is the failure mode this tool guards against. For a zero-param, read-only explainer there is little else to disclose (no auth, side effects, or rate limits implied), so this is adequate but adds only moderate behavioral context.
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?
Three short sentences, front-loaded with the core purpose, then the trigger, then the rationale. The final clause is somewhat rhetorical but reinforces why the tool matters, so it earns its place without much waste.
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?
With no parameters and an output schema present, the return values need not be described, and the definition says enough for an agent to know what it does and when to call it. The only minor gap is the absence of any note on what form the explanation takes, which the output schema presumably covers.
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?
The tool takes zero parameters, so there are no parameter semantics to document; the baseline for a no-arg tool applies and nothing in the description misrepresents the (empty) input contract.
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?
States a specific verb (explain) and a specific resource (what Polygraph's three verdicts mean and do not mean). This is clearly distinguishable from the data-fetching siblings (can_i_trust, get_integrity_score, list_undeclared_sources), all of which retrieve results rather than explain terminology.
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?
"Call this before presenting verdicts to a person" gives an explicit, actionable trigger condition tied to a recognizable workflow moment. It doesn't name alternatives or a when-not case, but for a standalone explainer tool the conditional is clear and sufficient.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_incident_reportGet Incident ReportB
Retrieve a Polygraph incident report from DataHub's knowledge base.
Incident reports name the operation responsible for a model-quality collapse, the owning team, and the real before/after metrics.
| Name | Required | Description | Default |
|---|---|---|---|
| document_urn | No | Full document URN. Omit to fetch the demo incident. |
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 carries the full burden, but it does disclose the content an incident report carries (responsible operation, owning team, before/after metrics), which is real signal. It's silent on auth requirements, whether the knowledge base is writable, or failure behavior, so the safety profile is only implied by 'Retrieve'.
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?
Three short sentences, front-loaded with the action, followed by what the report contains. No filler and nothing repeated from the name or schema.
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?
An output schema exists, so return structure needn't be spelled out, and the description compensates by summarizing the report's content. For a read-only knowledge-base fetch with one optional param, this is close to complete; only the sibling-routing and auth context are missing.
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 description coverage is 100% and the single parameter's schema entry already explains the URN and the demo fallback. The description adds no format, syntax, or URN example, so baseline 3 applies.
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?
States a specific verb+resource ('Retrieve a Polygraph incident report from DataHub's knowledge base') and clarifies the payload (operation responsible, owning team, before/after metrics). That content sketch distinguishes it from siblings like get_integrity_score or can_i_trust, though it never names those alternatives explicitly.
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?
No when-to-use guidance, no prerequisites, and no routing against the five sibling tools (can_i_trust, get_integrity_score, explain_verdict_semantics, etc.). The only conditional language, 'Omit to fetch the demo incident,' lives in the schema, not the description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_integrity_scoreGet Integrity ScoreB
Get the Lineage Integrity Score for a job, with its components.
The score is the Jaccard index of declared vs observed edges. Precision and recall are returned separately because a low score from stale claims and a low score from missing sources need different fixes.
| Name | Required | Description | Default |
|---|---|---|---|
| job_urn | Yes | A DataHub dataJob URN. |
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 carries the full behavioral burden. It usefully discloses what the score means (Jaccard index of declared vs observed edges) and that precision and recall are returned separately, but says nothing about side effects, permissions, or what happens for a job with no observed edges.
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?
Two short sentences, purpose front-loaded, and the second sentence establishes the diagnostic framing rather than restating the first. No filler.
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?
An output schema exists, so return values need not be spelled out, and for a one-parameter read tool the description covers the essentials plus useful metric semantics. It stops just short of full completeness by omitting any relationship to the sibling tools the agent must choose between.
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 description coverage is 100% for the single job_urn parameter, so the schema already documents it fully. The description adds nothing about the parameter (e.g., URN format or where to obtain it), so baseline 3 applies.
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?
States a specific verb and resource: 'Get the Lineage Integrity Score for a job, with its components.' It is clear what the tool returns, but it never distinguishes itself from the topically similar sibling can_i_trust or explain_verdict_semantics, so an agent must infer the boundary itself.
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?
There is no when-to-use/when-not guidance and no alternatives are named, despite five siblings that plausibly overlap (can_i_trust, explain_verdict_semantics). The second sentence explains how to interpret precision vs recall, which is output interpretation, not tool selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_phantom_edgesList Phantom EdgesA
List declared lineage edges that carried no data in the captured run.
Usually stale declarations left behind by a refactor. A genuinely conditional edge whose branch was not taken looks identical, so these need human judgement rather than automatic deletion.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
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 carries the burden, and it does well: it discloses that output mixes two indistinguishable cases (stale declarations vs. untaken conditional branches), which is critical behavioral context for interpreting results. It is neither framed as read-only nor as destructive, but the 'declared, carried no data' framing plus the warning against deletion implies a read/diagnostic operation. It stops short of 5 because read-only safety and 'no filtering parameters' scoping are inferred, not stated.
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?
Three tight sentences: what it is, why it appears (refactor residue), and the critical caveat about interpretation. Front-loaded with the core definition; no filler. Slightly longer than the bare minimum but every sentence adds value.
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 zero params and an existing output schema, the description's job is to explain the concept and interpretation, which it does. The one residual gap is that it does not explicitly state the operation is read-only or whether results are scoped to a single run, but the 'captured run' phrasing and the anti-deletion warning cover the agent's decision needs.
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?
Zero parameters, so the baseline of 4 applies. The description correctly implies a whole-run, unfiltered list (contrast with a hypothetical by-edge lookup), which is the relevant semantic note for a no-arg tool.
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?
States a specific verb (List) and a precisely defined resource: 'declared lineage edges that carried no data in the captured run.' The concept of a 'phantom edge' is defined operationally rather than assumed, so the agent knows exactly what entities this returns.
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 frames when this is relevant and warns against a misuse: these 'need human judgement rather than automatic deletion,' implying it is a diagnostic/triage list, not a cleanup trigger. It does not name a specific sibling alternative or explicit exclusions, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_undeclared_sourcesList Undeclared SourcesA
List assets the runtime reads that the catalog never declared.
These are usually the most consequential findings: nobody reviewing the catalog could have known these assets are inputs.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
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 carries the full behavioural burden. 'List' implies a read-only operation and the text conveys the semantic weight of the results (hidden runtime inputs), but it says nothing about scope of the scan, whether results are scoped to a run/snapshot, determinism, or ordering.
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?
Two short sentences, definition front-loaded before the significance note. Every sentence earns its place with no filler.
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?
An output schema exists, so return-shape explanation is not required, and the description supplies the conceptual meaning of the entries. For a no-argument listing tool this is close to complete, with only scope/ordering context missing.
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?
The tool takes zero parameters, so there is nothing to disambiguate; baseline 4 applies. The description correctly avoids inventing parameter detail that does not exist.
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 gives a precise verb+resource: 'List assets the runtime reads that the catalog never declared.' That is a specific, self-contained definition of an undeclared input. It does not, however, contrast itself with the adjacent concept in the sibling list_phantom_edges, which an agent could easily confuse with it.
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?
Usage is implied rather than stated: calling out that these are 'usually the most consequential findings' signals priority/triage intent. There is no explicit when-to-use, no prerequisites, and no routing to the sibling list_phantom_edges that covers the adjacent finding category.
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.
6 tool updates
v0.1.0- First observed
can_i_trust - First observed
explain_verdict_semantics - First observed
get_incident_report - First observed
get_integrity_score - First observed
list_phantom_edges - First observed
list_undeclared_sources
TDQS
Scored across 6 tools
Most tools are clearly distinct: list_undeclared_sources and list_phantom_edges target different finding types, and get_incident_report and explain_verdict_semantics are self-contained. However, can_i_trust and get_integrity_score both address lineage trust at different granularities (asset verdict vs job score), which could cause an agent to misselect without careful reading.
Five of six tools follow a consistent snake_case verb_noun pattern (get_integrity_score, list_*, get_incident_report, explain_verdict_semantics). can_i_trust is also snake_case but is a question phrase rather than verb_noun, a minor deviation.
Six tools are well-scoped for a specialized lineage integrity server: they cover the core questions without redundancy. No unnecessary bloat or missing essential operation.
The surface covers the main analysis workflow: trust verdict, integrity score with components, undeclared sources, phantom edges, incident reports, and verdict semantics. Minor gaps remain (e.g., retrieving raw declared/observed edges for manual comparison), but agents can work around them via existing tools.
Maintenance
Related MCP Connectors
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
Guarded MCP server for agent-readable business truth, provenance, readiness, and discovery.
Tamper-evident proof creation and verification for AI agents via MCP, A2A, and REST.
Immutable agent diagrams: create, edit, validate and render versioned diagrams over MCP.
Related MCP Servers
- AlicenseAqualityCmaintenanceExposes a materialized service dependency graph as an MCP toolset for persistent, queryable root-cause analysis via Cypher queries.51Apache 2.0
- AlicenseNot gradedqualityDmaintenanceProvides an incident-centric knowledge graph via MCP stdio, enabling storage and query of incidents, systems, signatures, and their causal relationships, with tools for creating, linking, and tracing incidents.MIT
- AlicenseNot gradedqualityBmaintenanceExposes code graphs across multi-program repositories via MCP, enabling humans and agents to query the fleet with evidence.MIT
- AlicenseNot gradedqualityBmaintenanceProvides full lineage graph analysis for Power BI .pbix files, enabling source-to-visual-field traversal. Exposes REST and MCP endpoints for building, searching, and exporting lineage graphs.50 PyPIMIT