Policy Engine
The policy engine evaluates every tool call, memory write, SQL query, and delegation before execution. It is the single enforcement point — no operation bypasses policy.
Because Moneypenny exposes its full API through MCP, you can manage policies conversationally from any MCP-compatible client (Claude Desktop, Cursor, etc.) or through the CLI.
How It Works
Every policy-relevant action is evaluated as a triple:
(actor, action, resource) → allow | deny | auditPolicies are ordered by priority. The first matching rule wins. If no rule
matches, the default mode applies (AllowByDefault or DenyByDefault).
Adding Policies
mp policy add \ --name "no-destructive-sql" \ --effect deny \ --action "execute" \ --resource "sql:*DROP*" \ --message "Destructive SQL is blocked by policy"mp policy add \ --name "no-shell" \ --effect deny \ --action "call" \ --resource "shell_exec"Ask your MCP-connected agent:
Add a policy that blocks all DROP SQL statements
Block shell access for all agents
mp policy add \ --name "audit-memory-searches" \ --effect audit \ --action "search" \ --resource "memory"Ask your MCP-connected agent:
Audit all memory searches
Pattern Matching
Actor, action, and resource fields use glob patterns:
| Pattern | Matches |
|---|---|
* | Everything |
main | Exact match |
sql:*DROP* | Any SQL containing “DROP” |
tool:shell_* | Any shell-prefixed tool |
Three Effects
| Effect | Behavior |
|---|---|
allow | Permit the action (default for most setups) |
deny | Block the action; return a denial reason to the agent |
audit | Allow the action but log it explicitly in the audit trail |
Behavioral Rules
Beyond simple allow/deny, policies support behavioral constraints:
- Rate limiting — cap tool calls per time window
- Retry loop detection — break agents stuck calling the same tool
- Token budgets — per-session token spending limits
- Time windows — restrict operations to specific hours (cron patterns)
Testing Policies
Dry-run a policy evaluation without executing anything:
mp policy test "DROP TABLE users"mp policy test "SELECT * FROM facts"Ask your MCP-connected agent:
Would a DROP TABLE query be allowed?
Test whether SELECT * FROM facts passes policy
Listing and Inspecting
mp policy listAsk your MCP-connected agent:
Show me all active policies
Violations
mp policy violationsmp policy violations --last 24hAsk your MCP-connected agent:
Show me recent policy violations
What policy violations happened in the last 24 hours?
Policy Explanation
Ask why a decision was made:
echo '{"op":"policy.explain","args":{ "actor":"main", "action":"call", "resource":"shell_exec"}}' | mp sidecarAsk your MCP-connected agent:
Why was my shell command denied?
Explain the policy decision for shell_exec
The agent itself can ask “why was I denied?” — explanations are returned as structured data with the matching policy ID and reason.
Denials as Context
When a tool call is denied, the denial is not an error — it’s returned to the agent as context. The agent can adapt:
- Explain to the user why it can’t do something
- Suggest an alternative approach
- Ask for explicit confirmation
Stuck detection breaks retry loops: if the agent calls the same denied tool three times in a row, the loop is halted and the agent is forced to respond in natural language.
Loading from Files
Bulk-load policies from a JSON file:
mp policy load policies.jsonAsk your MCP-connected agent:
Load policies from the file policies.json
Audit Trail
Every policy decision is recorded in the policy_audit table with:
- Actor, action, resource
- Effect (allow/deny/audit)
- Matching policy ID
- Reason text
- Timestamp
- Session context
Query the audit trail:
mp auditmp audit search "deny"mp audit export --format jsonAsk your MCP-connected agent:
Show me the audit trail
Search audit for denied actions
Export the audit log as JSON