Download scripts/mcp-tmp-guard.js from Cnass/Claude: direct link, hf CLI and curl.
- Browser
- Download file 3.88 kB
-
https://huggingface.co/spaces/Cnass/Claude/resolve/main/scripts/mcp-tmp-guard.js
- Command line
-
hf download hf://spaces/Cnass/Claude/scripts/mcp-tmp-guard.js
-
curl -L -o mcp-tmp-guard.js https://huggingface.co/spaces/Cnass/Claude/resolve/main/scripts/mcp-tmp-guard.js
3.88 kB
| // Claude Code PreToolUse hook for the Bash tool (see scripts/mcp-tmp-guard): | |
| // refuses `claude mcp add ...` when the MCP server would run from /tmp, which | |
| // is wiped every time the Space restarts, and tells Claude where to put it. | |
| ; | |
| const path = require('path'); | |
| const { tempPathIn } = require(path.join(__dirname, 'temp-paths.js')); | |
| const MCP_ADD = /\bmcp\s+add(?:-json)?\b/; | |
| const CD = /^\s*(?:builtin\s+)?(?:cd|pushd)\s+["']?([^\s"']+)/; | |
| const TEMP_DIR = /^(?:\/var)?\/tmp(?:\/|$)|^\/dev\/shm(?:\/|$)/; | |
| const PWD = /\$\(pwd\)|`pwd`|\$\{?PWD\b/; | |
| // Only the `mcp add` part of a command line counts (`mv /tmp/x ~/mcp-servers/x | |
| // && claude mcp add ...` is the fix, not the problem), plus a `cd` into a temp | |
| // folder that the add part then refers to through $(pwd) / $PWD. | |
| function tempPathAdded(command) { | |
| let cwd = ''; | |
| for (const part of command.split(/&&|\|\||[;|\n]/)) { | |
| const cd = CD.exec(part); | |
| if (cd) cwd = TEMP_DIR.test(cd[1]) ? cd[1] : ''; | |
| if (!MCP_ADD.test(part)) continue; | |
| const found = tempPathIn(part) || (cwd && PWD.test(part) ? cwd : ''); | |
| if (found) return found; | |
| } | |
| return ''; | |
| } | |
| // `claude mcp add ... -- node ~/mcp-servers/x/...` runs straight from the | |
| // Storage Bucket, whose node_modules / venv links are gone after a restart. | |
| const BUCKET_SERVER = /(?:~|\$HOME|\$\{HOME\}|\/data\/home|\/home\/node)\/mcp-servers\/([A-Za-z0-9._-]+)/; | |
| function bucketServerAdded(command) { | |
| for (const part of command.split(/&&|\|\||[;|\n]/)) { | |
| if (!MCP_ADD.test(part) || /agentweb-mcp-run/.test(part)) continue; | |
| const m = BUCKET_SERVER.exec(part); | |
| if (m) return m[1]; | |
| } | |
| return ''; | |
| } | |
| function decide(event, home) { | |
| if (!event || event.tool_name !== 'Bash') return null; | |
| const command = event.tool_input && typeof event.tool_input.command === 'string' ? event.tool_input.command : ''; | |
| if (!MCP_ADD.test(command)) return null; | |
| const bucket = bucketServerAdded(command); | |
| if (bucket) { | |
| return { | |
| hookSpecificOutput: { | |
| hookEventName: 'PreToolUse', | |
| permissionDecision: 'deny', | |
| permissionDecisionReason: | |
| `Agent Web stopped this command: it registers an MCP server that runs straight from ~/mcp-servers/${bucket} on the Storage Bucket, ` + | |
| 'which loses symlinks and execute bits (node_modules/.bin, venv/bin/python) at every restart, so the server would break. ' + | |
| `Use: agentweb-install mcp <name> --dir ~/mcp-servers/${bucket} [--run "<command, e.g. node {build}/dist/index.js>"] [--env K=V] ` + | |
| '(it builds the runnable copy on local disk at every start and registers `agentweb-mcp-run <name>`). See `agentweb-install help`.', | |
| }, | |
| }; | |
| } | |
| const found = tempPathAdded(command); | |
| if (!found) return null; | |
| const target = `${home ? String(home).replace(/\/+$/, '') : '~'}/mcp-servers/<name>`; | |
| return { | |
| hookSpecificOutput: { | |
| hookEventName: 'PreToolUse', | |
| permissionDecision: 'deny', | |
| permissionDecisionReason: | |
| `Agent Web stopped this command: it registers an MCP server that uses ${found}. ` + | |
| '/tmp is wiped every time this Space restarts, so the server would stop working. ' + | |
| `Install it with \`agentweb-install mcp <name> --git <url>\` (or --dir <folder>) instead: the source goes to ${target} ` + | |
| 'and the runnable copy is rebuilt on local disk at every start. See `agentweb-install help`.', | |
| }, | |
| }; | |
| } | |
| if (require.main === module) { | |
| let raw = ''; | |
| process.stdin.setEncoding('utf8'); | |
| process.stdin.on('data', (chunk) => { | |
| raw += chunk; | |
| }); | |
| process.stdin.on('end', () => { | |
| let event; | |
| try { | |
| event = JSON.parse(raw); | |
| } catch { | |
| return; | |
| } | |
| const answer = decide(event, process.env.HOME); | |
| if (answer) process.stdout.write(JSON.stringify(answer)); | |
| }); | |
| } | |
| module.exports = { decide, tempPathAdded }; | |