Terminal Demo: Governance Decisions — Allowed vs Denied
Call the governance API directly with curl to simulate Claude Code's PreToolUse hook. An admin-scope agent is allowed; a user-scope agent is denied. See both decisions on the dashboard.
On this page
Overview
These demos call the governance endpoint directly with curl — simulating Claude Code's PreToolUse hook. No agent session required.
The developer_agent (admin scope) is allowed to call MCP tools. The associate_agent (user scope) is denied. Both decisions are logged and visible on the dashboard.
Prerequisites: Complete Setup & Authentication first. You need a valid token in demo/.token.
Governance: Allowed Path — Admin Scope
Governance Check
curl -s -X POST "http://localhost:8080/api/public/hooks/govern?plugin_id=enterprise-demo" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"hook_event_name": "PreToolUse",
"tool_name": "mcp__systemprompt__list_agents",
"agent_id": "developer_agent",
"session_id": "demo-happy-path",
"tool_input": {}
}' | python3 -m json.tool
The response contains "decision": "allow" — governance permits this tool call.
MCP Tool Result
After governance returns ALLOW, Claude Code proceeds to execute the tool. You can run it manually:
systemprompt plugins mcp call systemprompt list_agents
This returns a real list of running agents from the platform.
What Happens
- The curl request simulates a Claude Code PreToolUse hook firing
- The governance engine receives the request with JWT authentication
- It evaluates all rules for
developer_agent(admin scope) developer_agenthas admin scope and thesystempromptMCP server configured — the tool is allowed- The response returns
"decision": "allow"with the matched policy - In a real deployment, Claude Code would then execute the MCP tool
Why governance is synchronous: The PreToolUse hook blocks tool execution until the backend returns allow/deny. This is the enforcement point. If governance were async, tools could execute before the decision arrives.
Dashboard
Open /admin/governance. You should see the allow decision with:
- Agent:
developer_agent - Tool:
mcp__systemprompt__list_agents - Decision: allow
- Policy: the matched governance policy name
Governance: Refused Path — User Scope
Governance Check
curl -s -X POST "http://localhost:8080/api/public/hooks/govern?plugin_id=enterprise-demo" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"hook_event_name": "PreToolUse",
"tool_name": "mcp__systemprompt__list_agents",
"agent_id": "associate_agent",
"session_id": "demo-refused-path",
"tool_input": {}
}' | python3 -m json.tool
The response contains "decision": "deny" — governance blocks this tool call.
What Happens
- The curl request simulates a PreToolUse hook for
associate_agent - The governance engine evaluates the
scope_restrictionrule associate_agenthas user scope — admin-only tools are denied- The response returns
"decision": "deny"with the reason
Defense-in-depth — two independent layers:
Layer 1 — Mapping (preventive). In a real Claude Code deployment, user-scope agents never even see admin tools. The MCP server mapping excludes them entirely — the tool does not appear in the agent's tool list.
Layer 2 — Governance rules (enforcement). Even if mapping were misconfigured, the
scope_restrictionrule evaluates every PreToolUse hook call. A user-scope agent calling an admin tool is denied and logged. Neither layer depends on the other.
Dashboard
Open /admin/governance. You should see the deny decision with:
- Agent:
associate_agent - Tool:
mcp__systemprompt__list_agents - Decision: deny
- Policy:
scope_restriction
Side-by-Side Comparison
| developer_agent | associate_agent | |
|---|---|---|
| Scope | admin | user |
| Governance decision | allow | deny |
| Policy matched | admin tool access | scope_restriction |
| Tool executes | Yes (MCP call proceeds) | No (blocked by governance) |
| Defense layer | Passes both mapping and rules | Blocked at mapping level; denied at rules level |
| AI cost | Free (no AI call) | Free (no AI call) |
Both decisions are logged in governance_decisions and visible on the governance dashboard.
Audit
Verify both governance decisions exist in the database:
systemprompt infra db query \
"SELECT decision, tool_name, agent_id, agent_scope, policy FROM governance_decisions ORDER BY created_at DESC LIMIT 5"
Expected results:
| decision | tool_name | agent_id | agent_scope | policy |
|---|---|---|---|---|
| deny | mcp__systemprompt__list_agents | associate_agent | user | scope_restriction |
| allow | mcp__systemprompt__list_agents | developer_agent | admin | admin tool access |
Next
Run Audit Trails & Costs to inspect the governance trail and cost breakdown from these two demos.