Deterministic runtime enforcement of step order for AI agents: ALLOW/DENY before a step runs.
One address for any MCP client. No sign-in needed on this server.
1 read1 other
Grouped by tool name. At most: creates or changes data: records, files, settings.
Odel charges nothing per call. The publisher may charge for its own service.
A Model Context Protocol server that exposes the live AgenticRail enforcement gate to any MCP client as two tools.
AgenticRail is a deterministic enforcement layer for AI agents: it holds an agent to its declared step order, refuses replays and skipped steps, and seals each completed sequence with a signed receipt. This server is the MCP adapter in front of it.
In the words the problem usually gets asked in: it is a policy enforcement point for AI agent tool calls. It catches skipped steps and procedural hallucination (an agent reporting success on a step that never ran) by refusing the out-of-order step before it runs, and every decision, ALLOW or DENY, becomes a tamper-evident audit trail entry.
Endpoint: https://mcp.agenticrail.nz/ (Streamable HTTP, stateless)
Protocol: 2026-07-28 — the revision that retired the initialize exchange and Mcp-Session-Id. This server was built stateless with neither, so it needed no migration. initialize is still answered for older clients.
Registry: nz.agenticrail/gate on the official MCP registry
| Tool | What it does | Calls |
|---|---|---|
evaluate_step | ALLOW/DENY a single agent step before it runs; seals a signed receipt | POST https://api.agenticrail.nz/v1/evaluate |
verify_receipt | Fetch a sequence's verification report; confirm the receipt chain is intact | POST https://report.agenticrail.nz/report |
Call evaluate_step before running each step of a sequence, and do not run a step the gate DENYs.
Every refusal carries its own remedy in the response envelope — unsigned, DENY-only, because it describes the sequence's state now rather than the decision that was made:
| refusal | what comes back |
|---|---|
ACTION_NOT_ALLOWED | allowed_action_types — exactly what this step would have accepted |
SEQUENCE_VIOLATION | next_expected_step — the step the sequence is waiting for |
STEP_ORDER_MISMATCH | locked_step_order — the order this sequence was locked to on its first call |
UNKNOWN_STEP | expected_step_order + step_order_source (caller or msmd_spine) |
action_type is an enum of exactly eight values, and each step accepts only a subset. The enum in the tool schema is a hint, not a control — it binds only a client that validates its own arguments, and an invented verb is by far the most common first refusal: READ, QUERY, TOOL_CALL, LOOKUP, EXECUTE and the like are not action types, however well they describe your step. The gate is the control. When it refuses, allowed_action_types comes back carrying the ones this step would have taken.
A refused call locks nothing. A denial is answered before the sequence store is written to, so the run is not spoiled: the call was wrong, not the sequence. Correct the field the denial names and send the step again. The lock is set only by actually sending a step_order on a call that is allowed; from then on that list is enforced and a different one is refused with STEP_ORDER_MISMATCH. Omitting step_order sets no lock at all — it means only that this call is judged against the built-in MSMD spine, whose step names are almost certainly not yours.
So if your process has its own step names, send step_order with those names and the gate will enforce your order instead. One step is a valid sequence.
In Claude Code:
# zero config — uses the public demo key
claude mcp add --transport http agenticrail https://mcp.agenticrail.nz/
# with your own key
claude mcp add --transport http agenticrail https://mcp.agenticrail.nz/ \
--header "Authorization: Bearer <your-agenticrail-key>"
Any Streamable-HTTP MCP client works — point it at the URL.
BASE=https://mcp.agenticrail.nz/
curl -s -X POST "$BASE" -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq .
# ⚠️ Use a sequence_id nobody else will pick. On the shared demo key the id is
# GLOBAL and sealing is PERMANENT — a fixed one in an example works once for one
# person on earth and returns SEALED_SEQUENCE for everyone after.
SEQ="mcp-smoke-$(date +%s)-$RANDOM"
curl -s -X POST "$BASE" -H 'content-type: application/json' \
-d "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{
\"name\":\"evaluate_step\",
\"arguments\":{\"sequence_id\":\"$SEQ\",\"step\":\"intake\",
\"action_type\":\"CHECK_STATE\"}}}" | jq .
With no Authorization header the public demo key is used and your sequence_id comes back rewritten to demo-mcp-<your id> — demo- marks the public lane, mcp- marks it as anonymous MCP traffic. Use the id returned in the response from then on; the one you sent will not resolve. This is intended, not a leak.
A demo- sequence's report needs no key to read, so treat anything you send on it as public.
mcp.agenticrail.nz is on the same zone as api. and report., so a plain fetch() would be a same-zone loopback (Cloudflare error 1002). The bindings hit the identical public handlers — they are not an internal bypass.Mcp-Session-Id is issued or required; every POST is self-contained. The transport shell is handleRpc + the fetch handler — the only part a spec revision touches. The value-bearing calls (callEvaluate / callVerify) are plain HTTPS and don't change.GET / serves the discovery card; every other GET path 404s. POST is left permissive on purpose so a client that appends a path to the endpoint URL still works./.well-known/oauth-* is correct — it is how an MCP server says no auth required. agent.json, agent-card.json, x402 and ai-plugin.json are protocols this server does not implement; answering them would be a claim.npx wrangler deploy
Operated by TUARA KURI LIMITED (NZBN 9429053582867), Hokianga, Aotearoa New Zealand.