The MCP server: stalwart mcp

stalwart mcp is the guardrail's MCP server. It runs on your machine, over stdio, and answers your AI assistant's questions about the guardrail, builds one with you, and measures it on your own calls. It reads and writes files in the folder it was started in (STALWART_MCP_HOME moves it). It calls the service only where a step already does (compiling a rule sheet, a hosted hunt, a red team), and it never calls a model: where a step needs one, your own assistant does it, through a prompt, and the server checks what it made.

Adding it to your client

$ stalwart mcp --config claude      # prints: claude mcp add stalwart -- <python> -m stalwart_agents.guardrails.server
$ stalwart mcp --config cursor      # the mcpServers entry for Cursor
$ stalwart mcp --config vscode      # the servers entry for VS Code

Asking what it can do

Tool Answers
capabilities(language) every check, from the guardrail's own list: what it catches and cannot, its inputs and on-fail options; where it can run; versions; what is guaranteed and what is not
explain_check(name, language) one check in full, with an allowed and a stopped example and its manual section
search_manual(question, language) the manual's sections that best match, by their words, in English or Portuguese; no model
how_to(goal, language) fixed recipes: "guard my MCP server", "add it to my Guardrails AI guard", "write a rule for refunds over 100", "evaluate on my traces", ...
explain_decision(decision_id) why a recorded call was stopped: the check, its rule and what it read

The manual's pages are resources: stalwart://manual/{page} (add /pt for Portuguese), one of building, checks, tool-kinds, rule-sheet, wording-search, evaluating, connecting, mcp.

Setting it up

Tool Does
catalogue_from_server(server, out) reads an MCP server's tools (its address, or a saved tools/list reply) into tools.json
writes_file(tools, out) drafts tool_kinds.yaml: each tool's kind, reason and source, unsure tools first
set_tool_kinds(changes, reasons) sets kinds as your assistant proposes them, recorded as source: assistant
compile_rules(sheet, out_dir) compiles rules.yaml with the service into compiled/
lint_rules(sheet, tools) a rule for a tool you do not have; a write no rule names
check_call(call, context) one dry-run answer; nothing runs
search_wordings(tools, language, checks, seconds) the wording search of the three checks that read the turn
snippet(where, upstream) the code for python, guardrails-ai or nemo, or gateway's catalogue.json and gateway.yaml

Measuring it

Tool Does
replay(trace, format) every call's answer; counts by rule; the calls stopped; confirmations per 100 calls; p50/p95 time; determinism
evaluate(labelled, format) false alarms and misses against your labels, per rule
counterfactual(trace, outcome_checker) your own outcome check, as run and with the stopped calls removed
hunt(sheet) the bug hunter on your rules (the service, with your key)
redteam(server) an MCP server's tools behind the gateway, proved for any call (the service, with your key)
compare_with_gai(trace, guard) your Guardrails AI guard alone, and with our validator
benchmark(name) fetches the tau2-bench airline or retail data from its pinned upstream commits

Prompts

A worked session

You: What can this guardrail check?

(the assistant calls capabilities) Twenty-one checks, in four stages: tool kinds; the admission chain (a withdrawn tool, an address not allowed, a step outside an approved plan); the ten rules of the tool check (allowed tools, the session record, arguments, destinations, order, undo, your rules, outside content, confirmation, any other write); and the checks of what was said (grounding, S1, S2, the confirm read). It judges tools, arguments, labels, history and rules, not what a reply means.

You: How do I add it to my Guardrails AI guard?

(the assistant calls how_to) Install stalwart-agents[guardrails], add gai.ToolCall(...) to your guard, and validate each tool call with guard.validate(gai.as_json(call), metadata={"stalwart": {...}}) before running it.

What a report holds

Tool names, rule names, counts, decision ids and digests. Never an argument value, and never a word a person said; every text passes the package's masking of secrets and personal data.