oct.mcp.safety module#
Purpose#
Safety Gate (Layer 2.5) for the oct-mcp six-layer pipeline.
Enforces the Human-in-the-Loop (HITL) consent protocol for all
write/destructive tool calls, and applies dry-run defaults.
Responsibilities#
Define
SafetyDecision— the structured result of a safety check (proceed, requires_confirmation, dry_run, reason).Define
SafetyGatewith a singleSafetyGate.check()entry point consumed byoct.mcp.tools._run_tool_write().Define
_format_confirmation_request()which produces the structured[oct-mcp requires_confirmation] …response string returned to the AI host when confirmation is needed.
HITL Protocol (D-5B-1 — stateless)#
No server-side pending state.
First call (
confirm=False, default): safety gate returnsproceed=False. The caller returns a[oct-mcp requires_confirmation] …string to the AI host; no execution occurs.Second call (
confirm=True): safety gate returnsproceed=True; full execution proceeds.
The AI host re-submits the identical call with confirm=True after
the user approves in the host UI. There is no replay window to exploit
(T-08) because each call is evaluated independently.
Dry-run rules (D-5B-2, D-5B-5)#
Non-destructive tools → always
proceed=True,dry_run=False.Destructive,
confirm=False→proceed=False,requires_confirmation=True.oct_format,confirm=True,apply=False→proceed=True,dry_run=True.oct_format,confirm=True,apply=True→proceed=True,dry_run=False.Other destructive,
confirm=True→proceed=True,dry_run=False. (Tools with their owndry_runfield honour that field directly — the executor reads it from the validated args dict.)
Diagnostics#
Domain: MCP Levels:
L2 — lifecycle: safety decision L4 — deep trace: field values
Contracts#
This module does not import
click, the MCP SDK, or anyoct.mcp.executor/oct.mcp.toolsmodule (no cycles).SafetyGate.check()never raises for valid input.The confirmation request string always starts with the sentinel
[oct-mcp requires_confirmation]so callers can detect it via simple prefix matching.
- class oct.mcp.safety.SafetyDecision(proceed: bool, requires_confirmation: bool, dry_run: bool, reason: str)[source]#
Bases:
objectResult of a
SafetyGate.check()call.- proceed#
True→ execution may continue;False→ return the confirmation request to the AI host.- Type:
bool
- requires_confirmation#
Truewhen the AI host must re-submit the call withconfirm=Truebefore execution proceeds.- Type:
bool
- dry_run#
True→ executor should pass--dry-run(or equivalent) to the CLI subprocess. Applies only tooct_format(whereapply=Falsetriggers dry-run even withconfirm=True). Other tools that have their owndry_runfield manage it independently.- Type:
bool
- reason#
Human-readable explanation forwarded to audit logs.
- Type:
str
- dry_run: bool#
- proceed: bool#
- reason: str#
- requires_confirmation: bool#
- class oct.mcp.safety.SafetyGate(config: McpConfig)[source]#
Bases:
objectHITL + dry-run enforcement gate for write tools.
One instance is created per MCP server session (in
server.py) and stored inServerState.The gate is stateless — each
check()call is independent. There is no in-memory pending-confirmation queue. This eliminates the T-08 replay-window attack surface.- Parameters:
config – Active
McpConfig. The gate readsconfig.dry_run_default_for_writesto decide the fallback dry-run behaviour.
- check(tool_name: str, spec: ToolSpec, args: dict) SafetyDecision[source]#
Evaluate the HITL gate and dry-run rules for tool_name.
- Parameters:
tool_name – MCP tool name (e.g.
"oct_format").spec –
ToolSpecfor tool_name.args – Validated args dict (
model.model_dump()).
- Return type:
SafetyDecision— never raises.