P
PostToolUse (mcp__(.*[Cc]arta.*|2827383e-1775-4df3-b6ff-04d5392f6d18|640916d9-5e0c-4d0b-830a-03500fa93e5e|1af3c326-9402-463c-ba5c-d62de59cf57b|8eb65db0-0cbd-4f50-833b-3057c150d41a|33b9b857-8443-4b2d-b191-2d9b6c50eb86)__.*)
HookFires on
PostToolUsematchingmcp__(.*[Cc]arta.*|2827383e-1775-4df3-b6ff-04d5392f6d18|640916d9-5e0c-4d0b-830a-03500fa93e5e|1af3c326-9402-463c-ba5c-d62de59cf57b|8eb65db0-0cbd-4f50-833b-3057c150d41a|33b9b857-8443-4b2d-b191-2d9b6c50eb86)__.*Immediately after a tool returns, with its result available.
Where this sits in a session
- Session start→
- Prompt submitted→
- Prompt expansion→
- Before tool use→
- After tool use→
- Notification→
- Before compaction→
- Subagent finished→
- Turn finished→
- Session end
Can block — This hook is able to deny the action or send Claude back to work.
What it runs
Executed automatically when the event fires — no prompt, no confirmation.
command· timeout 5s
node "${CLAUDE_PLUGIN_ROOT}/scripts/hooks/post-tool-tracker.js"Install it yourself
Paste into .claude/settings.json to run this hook without installing the whole plugin. Adjust the paths to point at your own copy of the scripts.
settings.json
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/post-tool-tracker.js\"",
"timeout": 5
}
],
"matcher": "mcp__(.*[Cc]arta.*|2827383e-1775-4df3-b6ff-04d5392f6d18|640916d9-5e0c-4d0b-830a-03500fa93e5e|1af3c326-9402-463c-ba5c-d62de59cf57b|8eb65db0-0cbd-4f50-833b-3057c150d41a|33b9b857-8443-4b2d-b191-2d9b6c50eb86)__.*"
}
]
}
}Source
Read this before installing — it runs on your machine with your permissions.
#!/usr/bin/env node
/**
* PostToolUse hook: cache discover() output and track user preferences.
*
* After a discover() call, caches the command registry to
* ${CLAUDE_PLUGIN_DATA}/cache/commands.json for faster subsequent lookups.
*
* After a fetch() call, records the corporation_id to
* ${CLAUDE_PLUGIN_DATA}/prefs.json for smart defaults.
*
* Part of the official Carta AI Agent Plugin.
*/
const fs = require('fs');
const path = require('path');
const MAX_RECENT_CORPORATIONS = 10;
let pluginVersion = 'unknown';
try {
const pluginJsonPath = path.resolve(__dirname, '../../.claude-plugin/plugin.json');
const pluginJson = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
pluginVersion = pluginJson.version || 'unknown';
} catch {}
let inputData = '';
process.stdin.on('data', chunk => (inputData += chunk));
process.stdin.on('end', () => {
try {
const dataDir = process.env.CLAUDE_PLUGIN_DATA;
if (!dataDir) {
done();
return;
}
const input = JSON.parse(inputData);
const { tool_name, tool_input, tool_result } = input;
const parts = (tool_name || '').split('__');
const shortName = parts.length >= 3 ? parts[parts.length - 1] : tool_name;
const resultText = extractResultText(tool_result);
if (shortName === 'discover' && resultText) {
cacheDiscoverResult(dataDir, resultText);
}
if (shortName === 'fetch' && tool_input) {
trackCorporation(dataDir, tool_input);
}
} catch (err) {
process.stderr.write(`post-tool-tracker error: ${err.message}\n`);
}
done();
});
function extractResultText(toolResult) {
if (!toolResult || !toolResult.content) return null;
for (const block of toolResult.content) {
if (block.type === 'text' && block.text) return block.text;
}
return null;
}
// commands.json is read by the benchmark framework (carta-mcp agent_runner.py)
// and will be read by skills via ${CLAUDE_PLUGIN_DATA} substitution in SKILL.md
function cacheDiscoverResult(dataDir, resultText) {
try {
const commands = JSON.parse(resultText);
if (!Array.isArray(commands)) return;
const cacheDir = path.join(dataDir, 'cache');
fs.mkdirSync(cacheDir, { recursive: true });
const cacheData = {
cached_at: new Date().toISOString(),
plugin_version: pluginVersion,
commands,
};
fs.writeFileSync(path.join(cacheDir, 'commands.json'), JSON.stringify(cacheData));
} catch {}
}
function trackCorporation(dataDir, toolInput) {
try {
let params = toolInput.params || {};
if (typeof params === 'string') {
try { params = JSON.parse(params); } catch { return; }
}
const corpId = params.corporation_id;
if (corpId == null) return;
const corpIdStr = String(corpId);
const prefsPath = path.join(dataDir, 'prefs.json');
let prefs = {};
try { prefs = JSON.parse(fs.readFileSync(prefsPath, 'utf8')); } catch {}
prefs.last_corporation_id = corpIdStr;
const recent = prefs.recent_corporations || [];
const idx = recent.indexOf(corpIdStr);
if (idx !== -1) recent.splice(idx, 1);
recent.push(corpIdStr);
prefs.recent_corporations = recent.slice(-MAX_RECENT_CORPORATIONS);
prefs.updated_at = new Date().toISOString();
fs.writeFileSync(prefsPath, JSON.stringify(prefs));
} catch {}
}
function done() {
process.stdout.write(JSON.stringify({
hookSpecificOutput: {
hookEventName: 'PostToolUse',
},
}));
process.exit(0);
}
Shipped by 1 plugin
Installing any of these installs this hook.
Reviews
Log in to leave a review.
No reviews yet — be the first.
Explore related
Other things in this space — across every part of the ecosystem, not just hooks.