validation-mcp
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., "@validation-mcpRun the validation tests on pd_output.csv and save the report"
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.
validation-mcp
An MCP server that lets Claude act as an independent credit-model validator, and will not save a report that contains a number the code did not compute.
Give Claude a file of model output (each borrower's predicted probability of default and whether they defaulted) and it runs the tests a bank's second line would: discrimination, calibration, population stability and variable stability. Python computes every figure. Claude does what a validator adds on top: deciding which results matter, how severe they are and what the model owner should do. Then its report is checked against the numbers before it can be written to disk.
Why this is interesting beyond credit risk
An LLM writing a validation report is useful right up to the moment it misquotes a statistic. A prompt that says "only use numbers from the tool output" asks for care; this server enforces it.
Control | How it is implemented |
Model never computes | Every statistic comes from a tool; tools write each value into a ledger |
Ground every number |
|
No override | An unmatched number blocks the save. There is no flag to force it |
Traceable | Each match names the ledger key it matched, and the saved report carries the ledger as an appendix |
Honest uncertainty | Tools report what their results can't show: chance failures in grade tests, a weak error bound, a random split that makes stability look fine |
The same idea, applied to words instead of numbers, runs advice-guardrails: there an LLM judge's verdict counts only if the sentence it quotes is really in the reply.
What the check caught, for real
The example report was written by Claude (Opus 5.5, in Claude Code) from the tools' output. Its first draft said the model's PDs were "an over-prediction of 2.1 times" the observed default rate. The save was refused:
{"saved": false, "reason": "unsupported numbers",
"unsupported": [{"number": "2.1",
"context": "rved default rate of 20.0%, an over-prediction of 2.1 times. The overall binomial t",
"matched_ledger_keys": []}]}The arithmetic was right (41.2% / 20.0% = 2.07). That is the point: the figure came from the model, not the code, and a reviewer cannot tell correct model arithmetic from a plausible invention. The fix was to make the calibration tool report the ratio itself (2.07), after which the same draft passed with all 25 numbers matched.
Related MCP server: dq-mcp
The tests
Tool | Question | Method |
| Is the data fit to test? | Outcome is 0/1, score is a probability, default rate when each variable is missing vs present |
| Does the score rank defaulters first? | Gini with bootstrap 95% interval, KS, development vs hold-out gap |
| Are the PDs right? | Mean PD vs observed (exact binomial), Hosmer-Lemeshow, Brier, per-grade binomial test marked conservative or not conservative, failures expected by chance |
| Has the population moved? | PSI on the score and every variable, with missing values as their own bin |
| Would the same variables be chosen again? | Stability selection, below |
| Is every number in the report real? | The grounding check above |
Stability selection: my 2020 thesis, as a tool
My BSc thesis at TU Dublin ("Stability Selection in Logistic Regression", 2020) studied a question validators ask of every scorecard: would the same variables have been chosen from slightly different data?
The method (Meinshausen and Bühlmann, 2009): draw 100 random half-samples. On each, walk an L1-penalised logistic regression from a strong penalty to a weak one and keep the first q columns to enter. A variable's selection probability is the share of half-samples that kept it; at 0.7 or above (the threshold my thesis used) it is stable. Capping each half-sample at q is what gives an error bound: at most q² / ((2 × 0.7 − 1) × p) noise columns are expected among the stable ones.
When you name the variables the model under review uses, q defaults to their number, so the test asks: if the data picked this many variables on its own, would it reliably pick these? On the example model:
Variable | Selected in | In the model? |
DEBTINC, DELINQ, DEROG | 100 of 100 | yes |
VALUE | 100 of 100 | no |
CLAGE | 99 of 100 | yes |
NINQ | 41 of 100 | yes, unstable |
YOJ, LOAN | 0 of 100 | yes, unstable |
Run it
git clone https://github.com/Jayaramgokul/validation-mcp.git && cd validation-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m pytest tests/ -q # 34 offline tests
python examples/make_example.py # writes examples/hmeq_scored.csvConnect it to Claude
Claude Code:
claude mcp add validation -- /full/path/to/.venv/bin/python -m valmcp.serverClaude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"validation": {
"command": "/full/path/to/.venv/bin/python",
"args": ["-m", "valmcp.server"],
"env": {"VALMCP_OUT": "/full/path/to/reports"}
}
}
}Then ask, for example: "Load examples/hmeq_scored.csv (outcome BAD, score pd, split column sample with baseline dev, all twelve HMEQ variables as features). The model uses DELINQ, DEROG, CLAGE, NINQ, DEBTINC, YOJ and LOAN. Validate it and save a findings report."
Your data stays local: the server reads files from your disk over stdio and nothing is uploaded except what the tools return to Claude.
Layout
valmcp/
metrics.py Gini, KS, bootstrap, binomial, Hosmer-Lemeshow, Brier, PSI
selection.py stability selection, design matrix, error bound
ledger.py the ledger and the report check
server.py MCP server exposing the 8 tools
tests/ 34 known-answer and end-to-end tests, offline
examples/ a deliberately flawed HMEQ model, and the report Claude wrote on it
data/ HMEQ (public teaching dataset, see data/README.md)Limitations, stated plainly
The check proves a number exists, not that it is attached to the right claim. "Gini 0.20" would pass if 0.20 is some other ledger value. Each match names its ledger key so a reviewer can see the mapping; low-precision numbers ("4", "0.1") are only weakly checked.
Words are not checked. A report can still call a High finding Low. Severity is judgement, and stays the reviewer's job.
The example split is random. HMEQ has no dates, so stability passes by construction; the tool says so. Use an out-of-time sample in practice.
The error bound assumes exchangeable noise variables, which real credit data rarely satisfies. It is reported as a guide, and the tool warns when it is too loose to mean anything.
PD models only. The tests assume a binary default outcome and a probability score.
Licence
MIT. Data: Baesens, Roesch & Scheule, Credit Risk Analytics (Wiley, 2016).
This server cannot be deployed
Maintenance
Related MCP Connectors
Static linter for CLAUDE.md-style agent constitution files: 10 operational-guardrail checks.
Static linter for CLAUDE.md-style agent constitution files: 10 operational-guardrail checks.
Your audit methodology inside Claude: findings, risks, controls and workpapers in your team format.
Deterministic validation for AI-generated artifacts: JSON Schema, OpenAPI response, SQL syntax.
Related MCP Servers
- FlicenseNot gradedqualityAmaintenanceLocal-first MCP server for credit risk analysis. It enables explainable rule discovery, statistical validation, PSI drift monitoring, and root-cause diagnosis on user-approved Parquet data while preserving privacy.-
- AlicenseNot gradedqualityCmaintenanceEnables language models to run data-quality checks and profiling on local files, using dbt-style assertions like not_null, unique, relationships, and accepted_values.MIT
- AlicenseNot gradedqualityCmaintenanceEnables Claude Code to score its tool-calling transcripts for hallucinated action claims, unsafe edit ordering, redundant tool-call loops, and pass@k across repeated attempts at a task.MIT
- FlicenseAqualityBmaintenanceEnables LLMs to audit data drift and model degradation in tabular ML pipelines through deterministic statistical tests such as Kolmogorov-Smirnov and Population Stability Index, plus reusable prompts and standards resources.4-