MCP Server
The Model Context Protocol is an open spec from Anthropic for
exposing structured resources (read-only data the LLM can
consult) and tools (callable verbs the LLM can invoke) to a
host LLM at inference time. The Adjudon MCP server is a
read-mostly bridge that lets a customer's LLM agent ask Adjudon
"what policies are active right now?", "what does the recent
trace history look like?", or "verify this hash-chain segment for
me" without the agent having to wire an X-API-Key integration
itself. Tested against API version v1. JWT auth on every
endpoint; plan-gated by mcpServer (Scale+).
Protocol posture
The Adjudon server speaks MCP protocol version 0.1.0 and
advertises resources: true, tools: true, prompts: false.
Resources and tools are surfaced via three HTTP endpoints —
the actual stdio / WebSocket transport that the MCP spec
defines is not used. A customer's MCP-aware client (Claude
Desktop, the official Python or TypeScript MCP SDK, a hand-rolled
client) hits these endpoints over JSON-over-HTTPS like any other
Adjudon API.
This is a deliberate v1 simplification — the stdio
transport carries operational complexity that does not fit the
"server-side compliance bridge" use case. If a customer needs
true stdio transport, they wrap these endpoints client-side.
The wrapper is a few dozen lines.
Endpoints
GET /api/v1/mcp-server/manifest
POST /api/v1/mcp-server/resources/read
POST /api/v1/mcp-server/tools/call
Get the manifest
GET /api/v1/mcp-server/manifest
Returns the static manifest the customer's MCP client introspects during connection setup:
{
"success": true,
"data": {
"protocolVersion": "0.1.0",
"serverInfo": { "name": "adjudon-mcp", "version": "1.0.0" },
"capabilities": { "resources": true, "tools": true, "prompts": false },
"resources": [
{ "uri": "adjudon://policies", "name": "policies", "description": "Active enforcement policies for the org" },
{ "uri": "adjudon://traces/recent", "name": "recent-traces", "description": "Last 50 trace decisions (PII-scrubbed)" },
{ "uri": "adjudon://hash-chain/status","name": "hash-chain-status", "description": "Current verifiable audit-trail status" }
],
"tools": [
{ "name": "lookup_decision", "inputSchema": { "type": "object", "properties": { "traceId": { "type": "string" } }, "required": ["traceId"] } },
{ "name": "verify_chain_segment", "inputSchema": { "type": "object", "properties": { "fromSeq": { "type": "integer" }, "toSeq": { "type": "integer" } }, "required": ["fromSeq"] } }
]
}
}
The manifest is static across customers — every Adjudon org sees the same resource/tool catalogue. Per-org variation happens only when the resources are read; the catalogue itself does not branch.
Read a resource
POST /api/v1/mcp-server/resources/read
Body: { uri }. Three URIs are recognised:
| URI | What it returns |
|---|---|
adjudon://policies | All isActive: true policies for the caller's org |
adjudon://traces/recent | The last 50 traces, projected to traceId, status, confidence, createdAt, rationale (PII-scrubbed) |
adjudon://hash-chain/status | Current chain head, sequence, last-verified-at, verification status |
curl -X POST https://api.adjudon.com/api/v1/mcp-server/resources/read \
-H "Authorization: Bearer $ADJUDON_JWT" \
-H "Content-Type: application/json" \
-d '{ "uri": "adjudon://traces/recent" }'
Response shape follows the MCP spec:
{
"success": true,
"data": {
"contents": [
{ "uri": "adjudon://traces/recent", "mimeType": "application/json", "text": "[...]" }
]
}
}
The text field is a JSON-stringified array; clients parse it
on the way in. Errors: 400 VALIDATION_ERROR (missing uri),
400 MCP_READ_FAILED (unknown URI; the error message is
deliberately vague per Cardinal Rule #4 to avoid leaking trace-
adjacent error context), 401, 403, 500.
Call a tool
POST /api/v1/mcp-server/tools/call
Body: { name, arguments }. Two tools are exposed:
| Tool | Arguments | What it does |
|---|---|---|
lookup_decision | { traceId } | Returns one trace by traceId, scoped to the caller's org |
verify_chain_segment | { fromSeq, toSeq? } | Verifies a hash-chain segment; calls into the Hash Chain verifier |
curl -X POST https://api.adjudon.com/api/v1/mcp-server/tools/call \
-H "Authorization: Bearer $ADJUDON_JWT" \
-H "Content-Type: application/json" \
-d '{ "name": "verify_chain_segment", "arguments": { "fromSeq": 1, "toSeq": 100 } }'
Response:
{
"success": true,
"data": {
"content": [{ "type": "text", "text": "{...}" }],
"isError": false
}
}
isError: true indicates the tool ran but returned a logical
failure (e.g. lookup_decision for a traceId that does not
exist, or verify_chain_segment returning verified: false).
The HTTP status is still 200; consume isError for the verdict.
Errors: 400 VALIDATION_ERROR (missing name), 400 MCP_TOOL_FAILED (unknown tool, malformed arguments — vague
per Cardinal Rule #4), 401, 403, 500.
Common gotchas
- HTTP transport, not stdio. The Adjudon server intentionally speaks MCP over HTTPS rather than the spec's stdio / WebSocket transport. MCP clients that expect stdio must wrap these endpoints; the wrapper is small and one is shipped in the Python SDK as a reference example.
- Read-only by design. No tool here mutates Adjudon state. An MCP-aware LLM cannot create policies, sign off auto-approval patterns, or post traces via this surface; mutations stay on their respective REST resources behind explicit API keys.
- Vague error messages.
MCP_READ_FAILEDandMCP_TOOL_FAILEDdo not echo the underlying exception message. The service-layer error can carry trace-adjacent payload fragments (URIs, argument values, tool names); echoing them back risks Cardinal Rule #4 leaks. Use the errorcodeto disambiguate. - PII-scrubbed reads. Resources surface trace data only after
the standard
piiScrubberpass; therecent-tracesprojection also drops large payload fields. This is the same guarantee every other read endpoint inherits. - Idempotency. The
Idempotency-Keymiddleware is wired only onPOST /traces; MCP tool calls are not auto-idempotent.lookup_decisionandverify_chain_segmentare functionally idempotent today (both are reads); future write-tools must declare their idempotency posture explicitly.
See also
- Traces API — the source of
truth
recent-tracesandlookup_decisionread from - Hash Chain — the
verify_chain_segmenttool's authoritative endpoint - Policies API — the
adjudon://policiesresource's authoritative store - Plans & Features —
the
mcpServerfeature gate - Error Codes — the broader error taxonomy