Files
portal/tools/enforce-mcp-classification.mjs
T

44 lines
1.6 KiB
JavaScript

/**
* PreToolUse(mcp__*) wrapper for tools/mcp-tool-classifier.mjs.
* Router-gate v4 spec §5.3 + v4.1 G1/G12.
*
* Classifier categorises MCP tool calls; default-deny on unknown.
* 'ask' decision is treated as block (controller must seek explicit approval).
* Fail-CLOSE on internal error.
*/
import { fileURLToPath } from 'url';
import {
readStdin,
parseEventJson,
exitDecision,
} from './enforce-hook-helpers.mjs';
import { classifyMcpTool } from './mcp-tool-classifier.mjs';
export function decide({ toolName, toolInput }) {
const name = String(toolName || '');
if (!name.startsWith('mcp__')) return { block: false, reason: null };
const verdict = classifyMcpTool(name, toolInput || {}, {});
if (!verdict) return { block: false, reason: null };
if (verdict.decision === 'block' || verdict.decision === 'ask') {
return { block: true, reason: verdict.reason || `${name} requires approval (decision=${verdict.decision})` };
}
return { block: false, reason: null };
}
async function main() {
try {
const raw = await readStdin();
const event = parseEventJson(raw);
const r = decide({ toolName: event.tool_name, toolInput: event.tool_input });
if (r.block) {
return exitDecision({ block: true, message: `[mcp-classification] ${r.reason}` });
}
return exitDecision({ block: false });
} catch {
return exitDecision({ block: true, message: '[mcp-classification] внутренняя ошибка — fail-CLOSE' });
}
}
const isCli = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
if (isCli) main();