check_policy
Check whether a specific action (tool call, secret key read, or command execution) is permitted by the project's policy without actually executing it. Use as a dry-run to avoid blocked operations.
Instructions
[policy] Ask whether a single intended action would be allowed by the project's .q-ring.json policy without actually performing it. Use as a dry-run before calling a potentially-blocked tool, attempting to read a sensitive key, or invoking exec_with_secrets with a non-trivial command; prefer get_policy_summary for a one-shot overview of the entire policy. Read-only. Returns JSON { allowed, reason?, policySource } describing the decision. Returns an error 'Missing required parameter for the selected action type' if the matching argument for the chosen action is not supplied.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Which policy surface to query. 'tool' = MCP tool gate (needs `toolName`); 'key_read' = secret read gate (needs `key`); 'exec' = exec_with_secrets command gate (needs `command`). | |
| toolName | No | Tool id to evaluate, e.g. 'rotate_secret'. Required when `action` is 'tool'. | |
| key | No | Secret key name to evaluate. Required when `action` is 'key_read'. | |
| command | No | Command to evaluate against the exec allowlist/denylist. Required when `action` is 'exec'. | |
| projectPath | No | Absolute path to the project root for project-scoped secrets and policy resolution. Defaults to the MCP server's current working directory when omitted. |
Implementation Reference
- src/mcp/tools/policy.ts:47-64 (handler)The main handler for the 'check_policy' tool. Dispatches to checkToolPolicy, checkKeyReadPolicy, or checkExecPolicy based on the 'action' parameter.
async (params) => { if (params.action === "tool" && params.toolName) { const d = checkToolPolicy(params.toolName, params.projectPath); return text(JSON.stringify(d, null, 2)); } if (params.action === "key_read" && params.key) { const d = checkKeyReadPolicy(params.key, undefined, params.projectPath); return text(JSON.stringify(d, null, 2)); } if (params.action === "exec" && params.command) { const d = checkExecPolicy(params.command, params.projectPath); return text(JSON.stringify(d, null, 2)); } return text( "Missing required parameter for the selected action type", true, ); }, - src/mcp/tools/policy.ts:22-46 (schema)Zod input schema for the 'check_policy' tool: 'action' enum + optional toolName/key/command + projectPath.
action: z .enum(["tool", "key_read", "exec"]) .describe( "Which policy surface to query. 'tool' = MCP tool gate (needs `toolName`); 'key_read' = secret read gate (needs `key`); 'exec' = exec_with_secrets command gate (needs `command`).", ), toolName: z .string() .optional() .describe( "Tool id to evaluate, e.g. 'rotate_secret'. Required when `action` is 'tool'.", ), key: z .string() .optional() .describe( "Secret key name to evaluate. Required when `action` is 'key_read'.", ), command: z .string() .optional() .describe( "Command to evaluate against the exec allowlist/denylist. Required when `action` is 'exec'.", ), projectPath, }, - src/mcp/tools/policy.ts:14-65 (registration)Registration of the 'check_policy' MCP tool via server.tool() in registerPolicyTools.
server.tool( "check_policy", [ "[policy] Ask whether a single intended action would be allowed by the project's `.q-ring.json` policy without actually performing it.", "Use as a dry-run before calling a potentially-blocked tool, attempting to read a sensitive key, or invoking `exec_with_secrets` with a non-trivial command; prefer `get_policy_summary` for a one-shot overview of the entire policy.", "Read-only. Returns JSON `{ allowed, reason?, policySource }` describing the decision. Returns an error 'Missing required parameter for the selected action type' if the matching argument for the chosen `action` is not supplied.", ].join(" "), { action: z .enum(["tool", "key_read", "exec"]) .describe( "Which policy surface to query. 'tool' = MCP tool gate (needs `toolName`); 'key_read' = secret read gate (needs `key`); 'exec' = exec_with_secrets command gate (needs `command`).", ), toolName: z .string() .optional() .describe( "Tool id to evaluate, e.g. 'rotate_secret'. Required when `action` is 'tool'.", ), key: z .string() .optional() .describe( "Secret key name to evaluate. Required when `action` is 'key_read'.", ), command: z .string() .optional() .describe( "Command to evaluate against the exec allowlist/denylist. Required when `action` is 'exec'.", ), projectPath, }, async (params) => { if (params.action === "tool" && params.toolName) { const d = checkToolPolicy(params.toolName, params.projectPath); return text(JSON.stringify(d, null, 2)); } if (params.action === "key_read" && params.key) { const d = checkKeyReadPolicy(params.key, undefined, params.projectPath); return text(JSON.stringify(d, null, 2)); } if (params.action === "exec" && params.command) { const d = checkExecPolicy(params.command, params.projectPath); return text(JSON.stringify(d, null, 2)); } return text( "Missing required parameter for the selected action type", true, ); }, ); - src/core/policy.ts:56-77 (helper)Core helper that checks if a tool is allowed by project policy (allow/deny lists).
export function checkToolPolicy(toolName: string, projectPath?: string): PolicyDecision { const policy = loadPolicy(projectPath); if (!policy.mcp) return { allowed: true, policySource: "no-policy" }; if (policy.mcp.denyTools?.includes(toolName)) { return { allowed: false, reason: `Tool "${toolName}" is denied by project policy`, policySource: ".q-ring.json policy.mcp.denyTools", }; } if (policy.mcp.allowTools && !policy.mcp.allowTools.includes(toolName)) { return { allowed: false, reason: `Tool "${toolName}" is not in the allowlist`, policySource: ".q-ring.json policy.mcp.allowTools", }; } return { allowed: true, policySource: ".q-ring.json" }; } - src/core/policy.ts:160-187 (helper)Core helper that checks if a command is allowed by exec policy (allow/deny commands).
export function checkExecPolicy(command: string, projectPath?: string): PolicyDecision { const policy = loadPolicy(projectPath); if (!policy.exec) return { allowed: true, policySource: "no-policy" }; if (policy.exec.denyCommands) { const denied = policy.exec.denyCommands.find((d) => command.includes(d)); if (denied) { return { allowed: false, reason: `Command containing "${denied}" is denied by project policy`, policySource: ".q-ring.json policy.exec.denyCommands", }; } } if (policy.exec.allowCommands) { const allowed = policy.exec.allowCommands.some((a) => command.startsWith(a)); if (!allowed) { return { allowed: false, reason: `Command "${command}" is not in the exec allowlist`, policySource: ".q-ring.json policy.exec.allowCommands", }; } } return { allowed: true, policySource: ".q-ring.json" }; }