44 lines
1.6 KiB
JavaScript
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();
|