mcp-trustlayer
<div align="center">
# mcp-trustlayer
**An MCP server that lets an agent ask whether it is allowed to act on its own output.**





</div>
---
An agent that has just extracted values from a document is the worst possible judge of whether those values are correct. It has no distance from its own output. This server moves that judgment outside the model: the agent proposes, a policy you control disposes.
```
agent ── "I extracted these 12 fields, may I write them?" ──▶ mcp-trustlayer
│
◀── "9 post. 3 to a human. Here is why for each." ────────────┘
```
## Why this exists
Most extraction pipelines gate on one signal, a confidence score, and inherit a failure they never see. Confidence catches the model being **unsure**. It cannot catch the model being **sure and wrong**, because nothing in a confidently wrong answer looks hesitant.
The worked example below carries a fee field at `0.94` confidence whose value is the plan-review line item rather than the total. No threshold you could reasonably set catches it. An independent verification pass does.
## Tools
### `evaluate_fields`
Returns a disposition per field, one of **`post`**, **`review`**, or **`escaped`**.
```json
{
"fields": [
{ "name": "permit_number", "value": "BLD-2024-041877", "confidence": 0.99, "verification": "verified" },
{ "name": "permit_fee", "value": "$1,240.00", "confidence": 0.94, "verification": "mismatch" },
{ "name": "contractor", "value": "Meridian", "confidence": 0.38, "verification": "not_found" }
],
"high_stakes": ["permit_number"],
"verified": true
}
```
```
1 posted, 2 held for review, 0 escaped
[POST] permit_number = 'BLD-2024-041877' (0.99/0.95) verified
[HOLD] permit_fee = '$1,240.00' (0.94/0.85) verification mismatch
[HOLD] contractor = 'Meridian' (0.38/0.85) confidence 0.38 below bar 0.85
```
Pass `"verified": false` to model what happens with no verification pass. The fee field flips to `escaped`, meaning it was written to the record while unsupported by its source. That number is what the second pass is worth.
### `calibrate_threshold`
Sweeps the bar and reports review load against escaped errors, with and without verification, so the threshold gets chosen deliberately rather than inherited.
```
bar verify-on: hold/escaped verify-off: hold/escaped
0.85 3 / 0 2 / 1
0.90 5 / 0 4 / 1
0.95 7 / 0 7 / 0
```
## Install
No dependencies. Python 3.10+.
```bash
git clone https://github.com/neelbarm/mcp-trustlayer
cd mcp-trustlayer
python -m mcp_trustlayer.server
```
### Claude Desktop
```json
{
"mcpServers": {
"trustlayer": {
"command": "python",
"args": ["-m", "mcp_trustlayer.server"],
"cwd": "/path/to/mcp-trustlayer"
}
}
}
```
## Speaking to it directly
MCP is JSON-RPC 2.0 over stdio, so you can drive it with a pipe.
```bash
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| python -m mcp_trustlayer.server
```
## Design notes
**The protocol is implemented directly, not via an SDK.** `protocol.py` is about 90 lines and handles `initialize`, `tools/list`, and `tools/call` over newline-delimited JSON-RPC. Keeping it dependency-free makes the wire format legible, which matters more in a reference implementation than convenience does.
**Notifications get no response.** `notifications/initialized` carries no `id` and must not be replied to. This is the most common place a hand-rolled MCP server breaks, so there is a test for it.
**Tool errors return content, not crashes.** A raised exception comes back as `isError: true` with the message in the content block, so the model can see what went wrong and adjust rather than losing the connection.
**Descriptions are written for the model, not for a human reading docs.** Each tool description says when to call it, because that text is the only instruction the model gets.
## Tests
```bash
python -m pytest tests -q # 10 passed
```
The suite includes an end-to-end case that spawns the server as a subprocess and speaks real JSON-RPC over stdio, rather than only calling the handler in-process.
## Related
- **[trustlayer](https://github.com/neelbarm/trustlayer)** — the same decision logic as a Python library
- **[plancheck](https://github.com/neelbarm/plancheck)** — the idea applied end to end to permit intake, with a [live demo](https://plancheck-neel.netlify.app/)
MIT. Built by [Neel Barmecha](https://neelbarmecha.netlify.app/).
TDQS
Scored across 2 tools
The two tools have clearly distinct purposes: evaluate_fields determines field dispositions, while calibrate_threshold analyzes trade-offs across threshold values. There is no overlap in their responsibilities.
Both tools follow the same verb_noun pattern ('evaluate_fields', 'calibrate_threshold'), using consistent lowercase with underscores. The pattern is predictable and clear.
With only 2 tools, the set is slightly thin, but this matches the narrow, focused scope of the trust-layer functionality. Each tool earns its place.
The workflow is missing a way to actually apply the calibrated threshold to the evaluate_fields tool; calibration is informational but there is no explicit 'set_threshold' or parameter. This creates a notable gap for end-to-end usage.