MCP tool pin: detect changed tool definitions (rug pulls) and hidden Unicode
Send an MCP server's tools/list result (up to 200 tools) and, after the first call, the hashes you approved. Returns a reproducible manifest hash, a per-tool hash, which tools were added, removed or changed since approval (silent description and schema edits are how MCP rug pulls and tool poisoning land), and Unicode smuggling findings in every name, description and schema string. Call it at session start before loading tools. Pure code, no model; the hash recipe (RFC 8785 JCS + SHA-256) is in the output so you can recompute it.
What you get
Approve a server's tools once, then catch any later change to what the model is told, for $0.005 per session.
Call it
Send the JSON body below to https://agents.writerslogic.com/api/v1/mcp-tool-pin. An unpaid request answers 402 with the price and how to pay; a request that fails with a 5xx status is not charged.
curl -i -X POST https://agents.writerslogic.com/api/v1/mcp-tool-pin \
-H 'content-type: application/json' \
-d '{"tools":[{"name":"get_weather","description":"Get the forecast for a city.","inputSchema":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}},{"name":"send_email","description":"Send an email.","inputSchema":{"type":"object","properties":{"to":{"type":"string"},"body":{"type":"string"}}}}],"approvedTools":{"get_weather":"0000000000000000000000000000000000000000000000000000000000000000"}}'
Input
| Field | Type | Required | Description |
|---|---|---|---|
tools | array<object>; max 200 items | yes | The tools array from an MCP tools/list response, as received. |
approvedManifestHash | string; length 64..64 | no | manifestHash from the call you approved. |
approvedTools | object | no | Map of tool name to the per-tool hash you approved; enables the added/removed/changed diff. |
Example request body
{
"tools": [
{
"name": "get_weather",
"description": "Get the forecast for a city.",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"type": "string"
}
},
"required": [
"city"
]
}
},
{
"name": "send_email",
"description": "Send an email.",
"inputSchema": {
"type": "object",
"properties": {
"to": {
"type": "string"
},
"body": {
"type": "string"
}
}
}
}
],
"approvedTools": {
"get_weather": "0000000000000000000000000000000000000000000000000000000000000000"
}
}
Result
The response is { "data": ..., "_provenance": ... }. data carries these fields; _provenance is a signed W3C Verifiable Credential with a C2PA-style manifest, checked free at POST /api/v1/verify-provenance.
| Field | Type | Required | Description |
|---|---|---|---|
verdict | string; one of "MATCHES_APPROVED", "CHANGED_SINCE_APPROVED", "NO_BASELINE" | no | |
unicodeVerdict | string; one of "BLOCK", "REVIEW", "NO_FINDING" | no | NO_FINDING means none of the 19 Unicode rules fired; it is not a review of what the tools do. |
nextStep | string | no | |
manifestHash | string | no | |
tools | array<object> | no | |
diff | object | no | Present when approvedTools was sent. |
unicodeFindings | array<object> | no | |
hashRecipe | string | no | |
engine | string | no | |
upgrade | object | no | The full agent this micro check is a slice of, its price and what it adds. |
Pay and connect
Available through: x402 (USDC on Base), MCP.
Machine-readable
- This agent as JSON (schemas, example, OpenAPI operation)
- Free signed sample response
- OpenAPI 3.1 · Catalog · Index of every agent