approve_gate
Approve or reject pending mandatory gate decisions to enforce human-in-the-loop governance. Lists pending gates when no gate ID is given.
Instructions
Approve or reject a pending MANDATORY gate decision. Lists pending gates if no gate_id provided. This is the human-in-the-loop mechanism for MANDATORY classifications.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action to perform | |
| gate_id | No | Gate ID to approve/reject (required for approve/reject) | |
| approved_by | No | Identity of the approver | HUMAN |
| rationale | No | Reason for approval/rejection | |
| webauthn_proof | No | Optional WebAuthn passkey proof for cryptographic identity verification |
Implementation Reference
- src/mcp/tools/approve-gate.ts:42-253 (handler)Handler registration and execution logic for the approve_gate MCP tool. Supports 4 actions: list (show pending gates), approve (approve with optional WebAuthn), reject (reject with optional WebAuthn), break_glass (emergency override). Delegates to engine.gate.approve(), engine.gate.reject(), engine.gate.breakGlassApprove() for core logic.
export function registerApproveGateTool(server: McpServer, engine: GovernanceEngine): void { server.tool( 'approve_gate', 'Approve or reject a pending MANDATORY gate decision. Lists pending gates if no gate_id provided. This is the human-in-the-loop mechanism for MANDATORY classifications.', { action: z.enum(['list', 'approve', 'reject', 'break_glass']).describe('Action to perform'), gate_id: z.string().optional().describe('Gate ID to approve/reject (required for approve/reject)'), approved_by: z.string().default('HUMAN').describe('Identity of the approver'), rationale: z.string().optional().describe('Reason for approval/rejection'), webauthn_proof: z.object({ credentialId: z.string(), userId: z.string(), verifiedAt: z.string(), signatureVerified: z.boolean(), }).optional().describe('Optional WebAuthn passkey proof for cryptographic identity verification'), }, { title: 'Approve or Reject Mandatory Gate', readOnlyHint: false, idempotentHint: false, destructiveHint: false, openWorldHint: false, _meta: { ui: { resourceUri: 'ui://gate-approval' } } } as any, async (input) => { if (input.action === 'list') { const pending = engine.gate.getPendingApprovals(); // Tool accountability tracking engine.telemetryService.emitToolCall('approve_gate', `gate-list-${Date.now().toString(36)}`, 'INFORMATIONAL', true); return { content: [{ type: 'text' as const, text: JSON.stringify({ pendingCount: pending.length, pending: pending.map(p => ({ gateId: p.gateId, operation: p.operation, classification: p.classification, requestedAt: p.requestedAt, ownerRole: p.ownerRole, sla: p.sla, })), }, null, 2) }], }; } if (!input.gate_id) { return { content: [{ type: 'text' as const, text: JSON.stringify({ error: 'MISSING_GATE_ID', message: 'gate_id is required for approve/reject actions.', }) }], isError: true, }; } if (input.action === 'break_glass') { const entry = engine.ledger.begin( 'gate-break_glass', MaiClassification.MANDATORY, GiaLayer.MCP, input.approved_by ); entry.addMetadata('gateId', input.gate_id!); entry.addMetadata('action', input.action); if (input.rationale) entry.addMetadata('rationale', input.rationale); try { const success = engine.gate.breakGlassApprove( input.gate_id!, input.approved_by, 'mcp-break-glass-' + Date.now().toString(36), input.rationale ?? 'Break-glass via MCP tool' ); const score = engine.scorer.scoreDefault('gate-break_glass'); const completedEntry = entry.complete(score, { classification: MaiClassification.MANDATORY, confidence: 1.0, rationale: `Gate break_glass: ${input.rationale || 'No rationale provided'}`, requiresGate: false, }); engine.ledger.record(completedEntry); // Auto-emit governance telemetry engine.telemetryService.emitGateAction(entry.id, 'break_glass', input.gate_id!, input.approved_by); engine.telemetryService.emitToolCall('approve_gate', entry.id, 'MANDATORY', true); return { content: [{ type: 'text' as const, text: JSON.stringify({ action: 'BREAK_GLASS_OVERRIDE', gateId: input.gate_id, approvedBy: input.approved_by, success, message: success ? `Gate ${input.gate_id} approved via break-glass emergency override. Mandatory post-review required.` : await enhanceNotFoundMessage(input.gate_id!), }, null, 2) }], isError: !success, }; } catch (error) { engine.telemetryService.emitToolCall('approve_gate', entry.id, 'MANDATORY', false); const failedEntry = entry.fail(error instanceof Error ? error : new Error('Gate break_glass failed'), MaiClassification.MANDATORY); engine.ledger.record(failedEntry); throw error; } } if (input.action === 'approve') { // Pre-check: is this gate actually pending? Needed to distinguish // "not found" from "passkey required" when approve() returns false. const pendingBefore = engine.gate.getPendingApprovals().some(p => p.gateId === input.gate_id); const entry = engine.ledger.begin( 'gate-approve', MaiClassification.MANDATORY, GiaLayer.MCP, input.approved_by ); entry.addMetadata('gateId', input.gate_id); entry.addMetadata('action', input.action); if (input.rationale) entry.addMetadata('rationale', input.rationale); try { const success = engine.gate.approve(input.gate_id, input.approved_by, input.rationale, input.webauthn_proof); const score = engine.scorer.scoreDefault('gate-approve'); const completedEntry = entry.complete(score, { classification: MaiClassification.MANDATORY, confidence: 1.0, rationale: `Gate approve: ${input.rationale || 'No rationale provided'}`, requiresGate: false, }); engine.ledger.record(completedEntry); // Auto-emit governance telemetry engine.telemetryService.emitGateAction(entry.id, 'approve', input.gate_id, input.approved_by); engine.telemetryService.emitToolCall('approve_gate', entry.id, 'MANDATORY', true); // Distinguish failure reasons: gate not found vs passkey required let message: string; if (success) { message = `Gate ${input.gate_id} approved by ${input.approved_by}${input.webauthn_proof ? ' with WebAuthn passkey verification' : ''}.`; } else if (pendingBefore) { // Gate exists but approve() returned false → passkey enforcement rejected it message = `Gate ${input.gate_id} requires WebAuthn passkey proof for approval. Retry with webauthn_proof parameter.`; } else { message = await enhanceNotFoundMessage(input.gate_id!); } return { content: [{ type: 'text' as const, text: JSON.stringify({ action: success ? 'APPROVED' : (pendingBefore ? 'PASSKEY_REQUIRED' : 'NOT_FOUND'), gateId: input.gate_id, approvedBy: input.webauthn_proof ? input.webauthn_proof.userId : input.approved_by, passkeyVerified: !!input.webauthn_proof, success, message, }, null, 2) }], isError: !success, }; } catch (error) { engine.telemetryService.emitToolCall('approve_gate', entry.id, 'MANDATORY', false); const failedEntry = entry.fail(error instanceof Error ? error : new Error('Gate approve failed'), MaiClassification.MANDATORY); engine.ledger.record(failedEntry); throw error; } } // reject — also pass webauthnProof for identity verification on rejections const rejectedBy = input.webauthn_proof ? input.webauthn_proof.userId : input.approved_by; const entry = engine.ledger.begin( 'gate-reject', MaiClassification.MANDATORY, GiaLayer.MCP, rejectedBy ); entry.addMetadata('gateId', input.gate_id); entry.addMetadata('action', input.action); if (input.rationale) entry.addMetadata('rationale', input.rationale); try { const success = engine.gate.reject(input.gate_id, rejectedBy, input.rationale, input.webauthn_proof); const score = engine.scorer.scoreDefault('gate-reject'); const completedEntry = entry.complete(score, { classification: MaiClassification.MANDATORY, confidence: 1.0, rationale: `Gate reject: ${input.rationale || 'No rationale provided'}`, requiresGate: false, }); engine.ledger.record(completedEntry); // Auto-emit governance telemetry engine.telemetryService.emitGateAction(entry.id, 'reject', input.gate_id, rejectedBy); engine.telemetryService.emitToolCall('approve_gate', entry.id, 'MANDATORY', true); return { content: [{ type: 'text' as const, text: JSON.stringify({ action: 'REJECTED', gateId: input.gate_id, rejectedBy, passkeyVerified: !!input.webauthn_proof, success, message: success ? `Gate ${input.gate_id} rejected by ${rejectedBy}${input.webauthn_proof ? ' with WebAuthn passkey verification' : ''}.` : await enhanceNotFoundMessage(input.gate_id!), }, null, 2) }], isError: !success, }; } catch (error) { engine.telemetryService.emitToolCall('approve_gate', entry.id, 'MANDATORY', false); const failedEntry = entry.fail(error instanceof Error ? error : new Error('Gate reject failed'), MaiClassification.MANDATORY); engine.ledger.record(failedEntry); throw error; } } ); } - src/mcp/tools/approve-gate.ts:46-58 (schema)Zod schema defining the input parameters: action (enum: list/approve/reject/break_glass), gate_id (optional string), approved_by (defaults to HUMAN), rationale (optional), webauthn_proof (optional object with credentialId, userId, verifiedAt, signatureVerified).
{ action: z.enum(['list', 'approve', 'reject', 'break_glass']).describe('Action to perform'), gate_id: z.string().optional().describe('Gate ID to approve/reject (required for approve/reject)'), approved_by: z.string().default('HUMAN').describe('Identity of the approver'), rationale: z.string().optional().describe('Reason for approval/rejection'), webauthn_proof: z.object({ credentialId: z.string(), userId: z.string(), verifiedAt: z.string(), signatureVerified: z.boolean(), }).optional().describe('Optional WebAuthn passkey proof for cryptographic identity verification'), }, { title: 'Approve or Reject Mandatory Gate', readOnlyHint: false, idempotentHint: false, destructiveHint: false, openWorldHint: false, _meta: { ui: { resourceUri: 'ui://gate-approval' } } } as any, - src/mcp/server.ts:35-112 (registration)Tool is imported and registered in the MCP server's TOOL_REGISTRY with 'operator' tier visibility — meaning it's only exposed to local stdio clients (Claude Code / Claude Desktop), never to external HTTP clients.
import { registerApproveGateTool } from './tools/approve-gate.js'; import { registerAgentRightsTool } from './tools/agent-rights.js'; import { registerPrecedentTools } from './tools/precedent.js'; import { registerCitizenshipTools } from './tools/citizenship.js'; import { registerBranchAuthorityTools } from './tools/branchAuthority.js'; import { registerColonyTools } from './tools/colony.js'; import { registerMemoryPackTools } from './tools/memory-packs.js'; import { registerValueMetricsTools } from './tools/value-metrics.js'; import { registerSRTTools } from './tools/srt.js'; import { registerVerifyLedgerTool } from './tools/verify-ledger.js'; import { registerExportLedgerTool } from './tools/export-ledger.js'; import { registerRemediationPackTools } from './tools/remediation-packs.js'; import { registerPhoenixRecoveryTools } from './tools/phoenix-recovery.js'; import { registerGovernedRetrievalTools } from './tools/governed-retrieval.js'; import { registerContextAuthorityTool } from './tools/context-authority.js'; import { registerInstitutionTools } from './tools/institution.js'; import { registerContextReviveTool } from './tools/context-revive.js'; import { registerGovernedSamplingTool } from './tools/governed-sampling.js'; import { registerChainOfReasoningTools } from './tools/chain-of-reasoning.js'; import { GovernedSampling } from '../core/sampling/index.js'; // Runtime accountability instrumentation — wraps server.tool() registrations // so every invocation is bookended with startSession()/endSession(). import { wrapServerWithRuntimeAccountability } from './runtime-accountability-wrapper.js'; // Import resource handlers import { registerResources } from './resources/index.js'; // Import prompt handlers import { registerPrompts } from './prompts/index.js'; // ============================================================================ // Tool Visibility Tiers — Tenant Isolation for External MCP Clients // ============================================================================ // // Three tiers control which tools are registered per session: // PUBLIC — stateless scoring/classification tools, safe for any external client // TENANT — tools that access data, filtered by tenant ID (professional+ tier) // OPERATOR — internal infrastructure tools (local stdio only, never exposed externally) // // The Smithery gateway and all HTTP clients get PUBLIC by default. // Paying customers (professional/enterprise DB keys) get PUBLIC + TENANT. // Local stdio (Claude Code / Claude Desktop) gets all tiers (OPERATOR). // ============================================================================ export type ToolVisibility = 'public' | 'tenant' | 'operator'; /** * Maps each tool registration function to its visibility tier. * Grouped by the tier each set of tools belongs to. */ const TOOL_REGISTRY: Array<{ tier: ToolVisibility; register: (server: McpServer, engine: GovernanceEngine) => void; description: string; }> = [ // --- PUBLIC: Stateless governance scoring — no data exposure --- { tier: 'public', register: registerClassifyDecisionTool, description: 'classify_decision' }, { tier: 'public', register: registerEvaluateThresholdTool, description: 'evaluate_threshold' }, { tier: 'public', register: registerScoreGovernanceTool, description: 'score_governance' }, { tier: 'public', register: registerAssessRiskTierTool, description: 'assess_risk_tier' }, { tier: 'public', register: registerMapComplianceTool, description: 'map_compliance' }, { tier: 'public', register: registerVerifyLedgerTool, description: 'verify_ledger' }, // --- TENANT: Data-bearing tools, scoped to authenticated tenant --- { tier: 'tenant', register: registerAuditPipelineTool, description: 'audit_pipeline' }, { tier: 'tenant', register: registerMonitorAgentsTool, description: 'monitor_agents' }, { tier: 'tenant', register: registerSystemStatusTool, description: 'system_status' }, { tier: 'tenant', register: registerGenerateReportTool, description: 'generate_report' }, { tier: 'tenant', register: registerExportLedgerTool, description: 'export_ledger' }, { tier: 'tenant', register: registerValueMetricsTools, description: 'value_metrics (record_value_metric, record_governance_event, generate_impact_report)' }, { tier: 'tenant', register: registerMemoryPackTools, description: 'memory_packs (seal, load, transfer, compose, distill, promote)' }, { tier: 'tenant', register: registerPhoenixRecoveryTools, description: 'phoenix (snapshot, verify_integrity, recovery_health)' }, { tier: 'public', register: registerContextAuthorityTool, description: 'request_context (governed context authority)' }, { tier: 'tenant', register: (server, _engine) => registerInstitutionTools(server), description: 'board (list_institutions, list_charters, convene_session, get_session, install_kit)' }, // --- OPERATOR: Internal infrastructure — never exposed to external clients --- { tier: 'operator', register: registerApproveGateTool, description: 'approve_gate' }, - src/core/audit/telemetry.ts:160-201 (registration)Tool accountability profile registered in GOVERNED_TOOL_REGISTRY with toolClass 'gate', riskTier 'critical', maiDefault 'MANDATORY', requiresHumanApproval true, category 'governance'.
export const GOVERNED_TOOL_REGISTRY: IToolAccountabilityProfile[] = [ // ── Core Governance (10 tools) ── { toolName: 'classify_decision', toolClass: 'read', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'governance' }, { toolName: 'score_governance', toolClass: 'read', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'governance' }, { toolName: 'evaluate_threshold', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'governance' }, { toolName: 'assess_risk_tier', toolClass: 'read', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'governance' }, { toolName: 'map_compliance', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'governance' }, { toolName: 'audit_pipeline', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'forensics' }, { toolName: 'approve_gate', toolClass: 'gate', riskTier: 'critical', maiDefault: 'MANDATORY', requiresHumanApproval: true, category: 'governance' }, { toolName: 'monitor_agents', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'operations' }, { toolName: 'generate_report', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'reporting' }, { toolName: 'system_status', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'operations' }, // ── Knowledge Packs (6 tools) ── { toolName: 'seal_memory_pack', toolClass: 'write', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'knowledge' }, { toolName: 'load_memory_pack', toolClass: 'read', riskTier: 'low', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'knowledge' }, { toolName: 'transfer_memory_pack', toolClass: 'write', riskTier: 'high', maiDefault: 'MANDATORY', requiresHumanApproval: true, category: 'knowledge' }, { toolName: 'compose_memory_packs', toolClass: 'write', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'knowledge' }, { toolName: 'distill_memory_pack', toolClass: 'write', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'knowledge' }, { toolName: 'promote_memory_pack', toolClass: 'admin', riskTier: 'high', maiDefault: 'MANDATORY', requiresHumanApproval: true, category: 'knowledge' }, // ── SRT (4 tools) ── { toolName: 'srt_run_watchdog', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'srt' }, { toolName: 'srt_diagnose', toolClass: 'read', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'srt' }, { toolName: 'srt_approve_repair', toolClass: 'gate', riskTier: 'critical', maiDefault: 'MANDATORY', requiresHumanApproval: true, category: 'srt' }, { toolName: 'srt_generate_postmortem', toolClass: 'read', riskTier: 'low', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'srt' }, // ── Value Metrics (3 tools) ── { toolName: 'record_value_metric', toolClass: 'write', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'metrics' }, { toolName: 'record_governance_event', toolClass: 'write', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'metrics' }, { toolName: 'generate_impact_report', toolClass: 'read', riskTier: 'low', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'metrics' }, // ── Remediation & Operations (5 tools) ── { toolName: 'gia_scan_environment', toolClass: 'external', riskTier: 'moderate', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'operations' }, { toolName: 'gia_list_packs', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'operations' }, { toolName: 'gia_dry_run_pack', toolClass: 'read', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'operations' }, { toolName: 'gia_apply_pack', toolClass: 'external', riskTier: 'critical', maiDefault: 'MANDATORY', requiresHumanApproval: true, category: 'operations' }, { toolName: 'gia_run_patrol', toolClass: 'external', riskTier: 'moderate', maiDefault: 'ADVISORY', requiresHumanApproval: false, category: 'operations' }, // ── Forensics (1 tool) ── { toolName: 'verify_ledger', toolClass: 'read', riskTier: 'low', maiDefault: 'INFORMATIONAL', requiresHumanApproval: false, category: 'forensics' }, ]; - src/mcp/tools/approve-gate.ts:21-40 (helper)Helper function that enhances 'not found' error messages by looking up the gate in the PostgreSQL database. Provides detailed messages for TIMED_OUT, APPROVED, or REJECTED states — useful when MCP server restart cleared in-memory gates.
async function enhanceNotFoundMessage(gateId: string): Promise<string> { try { const dbRecord = await getGateDbRecord(gateId); if (!dbRecord) { return `Gate ${gateId} not found in pending approvals or DB. Check the gate_id is correct.`; } if (dbRecord.status === 'TIMED_OUT') { const at = dbRecord.resolvedAt?.toISOString() || 'unknown'; return `Gate ${gateId} timed out at ${at} (DB status: TIMED_OUT). The in-memory gate was cleared, likely by MCP server restart. Re-issue the original request to create a fresh gate.`; } if (dbRecord.status === 'APPROVED' || dbRecord.status === 'REJECTED') { const at = dbRecord.resolvedAt?.toISOString() || 'unknown'; const by = dbRecord.approvedBy || 'unknown'; return `Gate ${gateId} already ${dbRecord.status.toLowerCase()} by ${by} at ${at} (DB status: ${dbRecord.status}). Cannot ${dbRecord.status === 'APPROVED' ? 'approve' : 'reject'} again.`; } return `Gate ${gateId} not in pending approvals; DB status: ${dbRecord.status}.`; } catch { return `Gate ${gateId} not found in pending approvals.`; } }