trace-mcp 1.2.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -10
- package/dist/cli.js +25559 -21664
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +388 -167
- package/dist/index.js +22442 -19818
- package/dist/index.js.map +1 -1
- package/hooks/trace-mcp-guard.cmd +195 -0
- package/hooks/trace-mcp-guard.sh +69 -5
- package/hooks/trace-mcp-precompact.cmd +33 -0
- package/hooks/trace-mcp-precompact.sh +57 -0
- package/hooks/trace-mcp-reindex.cmd +43 -0
- package/hooks/trace-mcp-worktree.cmd +33 -0
- package/hooks/trace-mcp-worktree.sh +57 -0
- package/package.json +2 -2
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM trace-mcp-guard v0.4.0
|
|
3
|
+
REM trace-mcp PreToolUse guard (Windows)
|
|
4
|
+
REM Blocks Read/Grep/Glob/Bash on source code files - redirects to trace-mcp tools.
|
|
5
|
+
REM Allows: non-code files, Read before Edit, safe Bash commands (git, npm, build, test).
|
|
6
|
+
REM
|
|
7
|
+
REM Consultation markers: trace-mcp server writes markers when tools access files.
|
|
8
|
+
REM If a marker exists, Read is allowed immediately.
|
|
9
|
+
REM
|
|
10
|
+
REM Install: add to ~\.claude\settings.json or .claude\settings.local.json
|
|
11
|
+
REM See README.md for setup instructions.
|
|
12
|
+
|
|
13
|
+
setlocal enabledelayedexpansion
|
|
14
|
+
|
|
15
|
+
REM Read JSON from stdin into a temp file
|
|
16
|
+
set "TMPINPUT=%TEMP%\trace-mcp-guard-input-%RANDOM%.json"
|
|
17
|
+
more > "%TMPINPUT%"
|
|
18
|
+
|
|
19
|
+
REM Get tool name from env or parse from JSON
|
|
20
|
+
if defined CLAUDE_TOOL_NAME (
|
|
21
|
+
set "TOOL_NAME=%CLAUDE_TOOL_NAME%"
|
|
22
|
+
) else (
|
|
23
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_name"`) do set "TOOL_NAME=%%i"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
if "%TOOL_NAME%"=="" goto :allow
|
|
27
|
+
|
|
28
|
+
REM --- Read ---
|
|
29
|
+
if /i not "%TOOL_NAME%"=="Read" goto :check_grep
|
|
30
|
+
|
|
31
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.file_path"`) do set "FILE_PATH=%%i"
|
|
32
|
+
|
|
33
|
+
REM Block .env files
|
|
34
|
+
echo "%FILE_PATH%" | findstr /i /r "\.env" >nul 2>&1
|
|
35
|
+
if %errorlevel%==0 (
|
|
36
|
+
set "REL_PATH=%FILE_PATH%"
|
|
37
|
+
call :deny "Use get_env_vars for .env files - it masks sensitive values (passwords, API keys, tokens)." "trace-mcp alternatives: get_env_vars to list keys + types without exposing secrets."
|
|
38
|
+
goto :cleanup
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
REM Allow non-code files
|
|
42
|
+
echo "%FILE_PATH%" | findstr /i /r "\.md$ \.json$ \.jsonc$ \.yaml$ \.yml$ \.toml$ \.ini$ \.cfg$ \.txt$ \.html$ \.xml$ \.csv$ \.svg$ \.lock$ \.log$ \.sh$ \.bash$ \.zsh$ \.fish$ \.ps1$ \.bat$ \.cmd$" >nul 2>&1
|
|
43
|
+
if %errorlevel%==0 goto :allow
|
|
44
|
+
|
|
45
|
+
REM Allow files in non-source dirs
|
|
46
|
+
echo "%FILE_PATH%" | findstr /i /r "node_modules\\ vendor\\ dist\\ build\\ \.git\\" >nul 2>&1
|
|
47
|
+
if %errorlevel%==0 goto :allow
|
|
48
|
+
|
|
49
|
+
REM Block code file reads - redirect to trace-mcp
|
|
50
|
+
echo "%FILE_PATH%" | findstr /i /r "\.ts$ \.tsx$ \.js$ \.jsx$ \.mjs$ \.cjs$ \.py$ \.pyi$ \.go$ \.rs$ \.java$ \.kt$ \.kts$ \.rb$ \.php$ \.cs$ \.cpp$ \.c$ \.h$ \.hpp$ \.swift$ \.scala$ \.vue$ \.svelte$ \.astro$" >nul 2>&1
|
|
51
|
+
if not %errorlevel%==0 goto :allow
|
|
52
|
+
|
|
53
|
+
REM Check consultation markers (trace-mcp server writes these when get_outline/get_symbol called)
|
|
54
|
+
for /f "usebackq delims=" %%h in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('%CD%'))).Replace('-','').Substring(0,12).ToLower()"`) do set "PROJ_HASH=%%h"
|
|
55
|
+
set "REL_FOR_HASH=%FILE_PATH%"
|
|
56
|
+
set "REL_FOR_HASH=!REL_FOR_HASH:%CD%\=!"
|
|
57
|
+
set "REL_FOR_HASH=!REL_FOR_HASH:\=/!"
|
|
58
|
+
for /f "usebackq delims=" %%h in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('!REL_FOR_HASH!'))).Replace('-','').ToLower()"`) do set "FILE_HASH=%%h"
|
|
59
|
+
if exist "%TEMP%\trace-mcp-consulted-!PROJ_HASH!\!FILE_HASH!" goto :allow
|
|
60
|
+
|
|
61
|
+
REM Allow on second attempt (agent needs full content for Edit)
|
|
62
|
+
set "SESSION_ID=default"
|
|
63
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "try { (Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).session_id } catch { 'default' }"`) do set "SESSION_ID=%%i"
|
|
64
|
+
set "DENY_DIR=%TEMP%\trace-mcp-guard-%SESSION_ID%"
|
|
65
|
+
if not exist "%DENY_DIR%" mkdir "%DENY_DIR%" 2>nul
|
|
66
|
+
|
|
67
|
+
REM Create a hash of the file path for the marker
|
|
68
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('%FILE_PATH%'))).Replace('-','').ToLower()"`) do set "MARKER_HASH=%%i"
|
|
69
|
+
set "DENY_MARKER=%DENY_DIR%\%MARKER_HASH%"
|
|
70
|
+
|
|
71
|
+
if exist "%DENY_MARKER%" (
|
|
72
|
+
del "%DENY_MARKER%" 2>nul
|
|
73
|
+
goto :allow
|
|
74
|
+
)
|
|
75
|
+
echo.> "%DENY_MARKER%"
|
|
76
|
+
|
|
77
|
+
call :deny "Use trace-mcp for code reading - it returns only what you need, saving tokens." "trace-mcp alternatives: get_outline, get_symbol, search, get_feature_context. If you need full file content before editing, retry Read - it will be allowed."
|
|
78
|
+
goto :cleanup
|
|
79
|
+
|
|
80
|
+
:check_grep
|
|
81
|
+
REM --- Grep ---
|
|
82
|
+
if /i not "%TOOL_NAME%"=="Grep" goto :check_glob
|
|
83
|
+
|
|
84
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.path"`) do set "GREP_PATH=%%i"
|
|
85
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.glob"`) do set "GREP_GLOB=%%i"
|
|
86
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.type"`) do set "GREP_TYPE=%%i"
|
|
87
|
+
|
|
88
|
+
REM Block grep on .env files
|
|
89
|
+
echo "%GREP_GLOB%" | findstr /i /r "\.env" >nul 2>&1
|
|
90
|
+
if %errorlevel%==0 (
|
|
91
|
+
call :deny "Use get_env_vars for .env files - it masks sensitive values." "trace-mcp alternatives: get_env_vars with pattern filter."
|
|
92
|
+
goto :cleanup
|
|
93
|
+
)
|
|
94
|
+
echo "%GREP_PATH%" | findstr /i /r "\.env" >nul 2>&1
|
|
95
|
+
if %errorlevel%==0 (
|
|
96
|
+
call :deny "Use get_env_vars for .env files - it masks sensitive values." "trace-mcp alternatives: get_env_vars with pattern filter."
|
|
97
|
+
goto :cleanup
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
REM Allow grep on non-code file types
|
|
101
|
+
echo "%GREP_GLOB%" | findstr /i /r "\.md \.json \.ya*ml \.toml \.txt \.html \.xml \.csv \.cfg \.ini \.lock \.log" >nul 2>&1
|
|
102
|
+
if %errorlevel%==0 goto :allow
|
|
103
|
+
|
|
104
|
+
REM Allow grep on non-code type filter
|
|
105
|
+
if /i "%GREP_TYPE%"=="md" goto :allow
|
|
106
|
+
if /i "%GREP_TYPE%"=="json" goto :allow
|
|
107
|
+
if /i "%GREP_TYPE%"=="yaml" goto :allow
|
|
108
|
+
if /i "%GREP_TYPE%"=="toml" goto :allow
|
|
109
|
+
if /i "%GREP_TYPE%"=="xml" goto :allow
|
|
110
|
+
if /i "%GREP_TYPE%"=="html" goto :allow
|
|
111
|
+
if /i "%GREP_TYPE%"=="csv" goto :allow
|
|
112
|
+
|
|
113
|
+
REM Allow grep on config dirs
|
|
114
|
+
echo "%GREP_PATH%" | findstr /i /r "node_modules vendor dist build \.git" >nul 2>&1
|
|
115
|
+
if %errorlevel%==0 goto :allow
|
|
116
|
+
|
|
117
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.pattern"`) do set "PATTERN=%%i"
|
|
118
|
+
call :deny "Use trace-mcp for code search - it understands symbols and relationships." "trace-mcp alternatives: search, find_usages, get_call_graph. Use Grep only for non-code files (.md, .json, .yaml, config)."
|
|
119
|
+
goto :cleanup
|
|
120
|
+
|
|
121
|
+
:check_glob
|
|
122
|
+
REM --- Glob ---
|
|
123
|
+
if /i not "%TOOL_NAME%"=="Glob" goto :check_bash
|
|
124
|
+
|
|
125
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.pattern"`) do set "GLOB_PATTERN=%%i"
|
|
126
|
+
|
|
127
|
+
REM Block glob on .env patterns
|
|
128
|
+
echo "%GLOB_PATTERN%" | findstr /i /r "\.env" >nul 2>&1
|
|
129
|
+
if %errorlevel%==0 (
|
|
130
|
+
call :deny "Use get_env_vars for .env files - it masks sensitive values." "trace-mcp alternatives: get_env_vars to list all env vars across all .env files."
|
|
131
|
+
goto :cleanup
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
REM Allow glob for non-code patterns
|
|
135
|
+
echo "%GLOB_PATTERN%" | findstr /i /r "\.md \.json \.ya*ml \.toml \.txt \.html \.xml \.csv \.cfg \.ini \.lock \.log" >nul 2>&1
|
|
136
|
+
if %errorlevel%==0 goto :allow
|
|
137
|
+
|
|
138
|
+
call :deny "Use trace-mcp for code file discovery - it knows your project structure." "trace-mcp alternatives: get_project_map, search with file_pattern, get_outline. Use Glob only for non-code file patterns."
|
|
139
|
+
goto :cleanup
|
|
140
|
+
|
|
141
|
+
:check_bash
|
|
142
|
+
REM --- Bash ---
|
|
143
|
+
if /i not "%TOOL_NAME%"=="Bash" goto :allow
|
|
144
|
+
|
|
145
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.command"`) do set "COMMAND=%%i"
|
|
146
|
+
|
|
147
|
+
REM Allow safe commands
|
|
148
|
+
echo "%COMMAND%" | findstr /i /r /c:"^git " /c:"^npm " /c:"^npx " /c:"^pnpm " /c:"^yarn " /c:"^bun " /c:"^node " /c:"^deno " /c:"^cargo " /c:"^go " /c:"^make " /c:"^mvn " /c:"^gradle " /c:"^docker " /c:"^kubectl " /c:"^helm " /c:"^terraform " /c:"^pip " /c:"^poetry " /c:"^uv " /c:"^pytest " /c:"^vitest " /c:"^jest " /c:"^phpunit " /c:"^composer " /c:"^artisan " /c:"^rails " /c:"^bundle " /c:"^mix " /c:"^dotnet " /c:"^cmake " >nul 2>&1
|
|
149
|
+
if %errorlevel%==0 goto :allow
|
|
150
|
+
|
|
151
|
+
REM Block bash commands targeting .env files - prevent secret leakage
|
|
152
|
+
echo "%COMMAND%" | findstr /i /r "\.env" >nul 2>&1
|
|
153
|
+
if %errorlevel%==0 (
|
|
154
|
+
call :deny "Use get_env_vars for .env files - it masks sensitive values (passwords, API keys, tokens)." "trace-mcp alternatives: get_env_vars to list keys + types without exposing secrets. Never access .env files via shell."
|
|
155
|
+
goto :cleanup
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
REM Block code exploration via bash
|
|
159
|
+
set "HAS_EXPLORE=0"
|
|
160
|
+
echo "%COMMAND%" | findstr /i /r "grep rg find cat head tail less more awk sed" >nul 2>&1
|
|
161
|
+
if %errorlevel%==0 set "HAS_EXPLORE=1"
|
|
162
|
+
|
|
163
|
+
set "HAS_CODE=0"
|
|
164
|
+
echo "%COMMAND%" | findstr /i /r "\.ts \.tsx \.js \.jsx \.py \.go \.rs \.java \.rb \.php \.cs \.cpp \.c \.h \.swift \.scala \.vue \.svelte" >nul 2>&1
|
|
165
|
+
if %errorlevel%==0 set "HAS_CODE=1"
|
|
166
|
+
|
|
167
|
+
if "%HAS_EXPLORE%"=="1" if "%HAS_CODE%"=="1" (
|
|
168
|
+
call :deny "Use trace-mcp instead of shell commands for code exploration." "trace-mcp has structured tools: search, get_symbol, get_outline, find_usages. Use Bash only for builds, tests, git, and system commands."
|
|
169
|
+
goto :cleanup
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
goto :allow
|
|
173
|
+
|
|
174
|
+
REM --- Helpers ---
|
|
175
|
+
|
|
176
|
+
:deny
|
|
177
|
+
set "REASON=%~1"
|
|
178
|
+
set "CONTEXT=%~2"
|
|
179
|
+
echo {
|
|
180
|
+
echo "hookSpecificOutput": {
|
|
181
|
+
echo "hookEventName": "PreToolUse",
|
|
182
|
+
echo "permissionDecision": "deny",
|
|
183
|
+
echo "permissionDecisionReason": "%REASON%",
|
|
184
|
+
echo "additionalContext": "%CONTEXT%"
|
|
185
|
+
echo }
|
|
186
|
+
echo }
|
|
187
|
+
goto :eof
|
|
188
|
+
|
|
189
|
+
:allow
|
|
190
|
+
del "%TMPINPUT%" 2>nul
|
|
191
|
+
exit /b 0
|
|
192
|
+
|
|
193
|
+
:cleanup
|
|
194
|
+
del "%TMPINPUT%" 2>nul
|
|
195
|
+
exit /b 0
|
package/hooks/trace-mcp-guard.sh
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# trace-mcp-guard v0.
|
|
2
|
+
# trace-mcp-guard v0.4.0
|
|
3
3
|
# trace-mcp PreToolUse guard
|
|
4
4
|
# Blocks Read/Grep/Glob/Bash on source code files → redirects to trace-mcp tools.
|
|
5
5
|
# Allows: non-code files, Read before Edit, safe Bash commands (git, npm, build, test).
|
|
6
6
|
#
|
|
7
|
+
# Consultation markers: trace-mcp server writes markers when tools access files
|
|
8
|
+
# (get_outline, get_symbol, etc.). If a marker exists for a file, Read is allowed
|
|
9
|
+
# immediately — the agent already consulted trace-mcp for this file.
|
|
10
|
+
#
|
|
7
11
|
# Install: add to ~/.claude/settings.json or .claude/settings.local.json
|
|
8
12
|
# See README.md for setup instructions.
|
|
9
13
|
|
|
@@ -16,11 +20,19 @@ TOOL_NAME="${CLAUDE_TOOL_NAME:-$(echo "$INPUT" | jq -r '.tool_name // empty')}"
|
|
|
16
20
|
CODE_EXT_RE='\.(ts|tsx|js|jsx|mjs|cjs|py|pyi|go|rs|java|kt|kts|rb|php|cs|cpp|c|h|hpp|swift|scala|vue|svelte|astro|blade\.php)$'
|
|
17
21
|
|
|
18
22
|
# Non-code extensions — always allow
|
|
19
|
-
NONCODE_EXT_RE='\.(md|json|jsonc|yaml|yml|toml|ini|cfg|
|
|
23
|
+
NONCODE_EXT_RE='\.(md|json|jsonc|yaml|yml|toml|ini|cfg|txt|html|xml|csv|svg|lock|log|sh|bash|zsh|fish|ps1|bat|cmd|dockerfile|dockerignore|gitignore|gitattributes|editorconfig|prettierrc|eslintrc|stylelintrc)$'
|
|
24
|
+
|
|
25
|
+
# .env files — always route through trace-mcp to prevent secret leakage
|
|
26
|
+
ENV_FILE_RE='\.env(\.[a-zA-Z0-9._-]+)?$'
|
|
20
27
|
|
|
21
28
|
# Safe bash command prefixes — never block
|
|
22
29
|
SAFE_BASH_RE='^(git |npm |npx |pnpm |yarn |bun |node |deno |cargo |go |make |mvn |gradle |docker |kubectl |helm |terraform |pip |poetry |uv |pytest |vitest |jest |phpunit |composer |artisan |rails |bundle |mix |dotnet |cmake |ninja |meson )'
|
|
23
30
|
|
|
31
|
+
# Cross-platform sha256 hash (Linux: sha256sum, macOS: shasum)
|
|
32
|
+
file_sha256() {
|
|
33
|
+
echo -n "$1" | sha256sum 2>/dev/null | cut -d' ' -f1 || echo -n "$1" | shasum -a 256 2>/dev/null | cut -d' ' -f1
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
deny() {
|
|
25
37
|
local reason="$1"
|
|
26
38
|
local context="$2"
|
|
@@ -42,10 +54,31 @@ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "default"')
|
|
|
42
54
|
DENY_DIR="/tmp/trace-mcp-guard-${SESSION_ID}"
|
|
43
55
|
mkdir -p "$DENY_DIR" 2>/dev/null
|
|
44
56
|
|
|
57
|
+
# Consultation markers: trace-mcp server writes these when get_outline/get_symbol/etc. are called.
|
|
58
|
+
# If a file was already consulted via trace-mcp, allow Read immediately (agent needs full content for Edit).
|
|
59
|
+
# Dir format: $TMPDIR/trace-mcp-consulted-{sha256(projectRoot)[:12]}/{sha256(relPath)}
|
|
60
|
+
PROJECT_ROOT="$(pwd)"
|
|
61
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
62
|
+
PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | sha256sum | cut -c1-12)
|
|
63
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
64
|
+
PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | shasum -a 256 | cut -c1-12)
|
|
65
|
+
else
|
|
66
|
+
PROJECT_HASH=""
|
|
67
|
+
fi
|
|
68
|
+
CONSULTED_DIR="${TMPDIR:-/tmp}/trace-mcp-consulted-${PROJECT_HASH}"
|
|
69
|
+
|
|
45
70
|
# --- Read ---
|
|
46
71
|
if [[ "$TOOL_NAME" == "Read" ]]; then
|
|
47
72
|
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
48
73
|
|
|
74
|
+
# Block .env files — prevent secret leakage to AI model context
|
|
75
|
+
if echo "$FILE_PATH" | grep -qiE "$ENV_FILE_RE"; then
|
|
76
|
+
REL_PATH=$(echo "$FILE_PATH" | sed "s|^$(pwd)/||")
|
|
77
|
+
deny \
|
|
78
|
+
"Use get_env_vars for .env files — it masks sensitive values (passwords, API keys, tokens)." \
|
|
79
|
+
"trace-mcp alternatives for ${REL_PATH}:\\n- get_env_vars { \\\"file\\\": \\\"${REL_PATH}\\\" } — list keys + types without exposing secrets\\n- get_env_vars { \\\"pattern\\\": \\\"DB_\\\" } — filter by key prefix\\nNever read .env files directly — secrets will leak into AI model context."
|
|
80
|
+
fi
|
|
81
|
+
|
|
49
82
|
# Allow non-code files
|
|
50
83
|
if echo "$FILE_PATH" | grep -qiE "$NONCODE_EXT_RE"; then
|
|
51
84
|
exit 0
|
|
@@ -59,8 +92,18 @@ if [[ "$TOOL_NAME" == "Read" ]]; then
|
|
|
59
92
|
|
|
60
93
|
# Block code file reads → redirect to trace-mcp
|
|
61
94
|
if echo "$FILE_PATH" | grep -qiE "$CODE_EXT_RE"; then
|
|
95
|
+
# Compute relative path for consultation marker check (server writes markers keyed by relative path)
|
|
96
|
+
REL_PATH_FOR_HASH=$(echo "$FILE_PATH" | sed "s|^${PROJECT_ROOT}/||")
|
|
97
|
+
CONSULTED_HASH=$(file_sha256 "$REL_PATH_FOR_HASH")
|
|
98
|
+
|
|
99
|
+
# Check if file was already consulted via trace-mcp (get_outline, get_symbol, etc.)
|
|
100
|
+
if [[ -n "$PROJECT_HASH" && -f "$CONSULTED_DIR/$CONSULTED_HASH" ]]; then
|
|
101
|
+
# File was consulted via trace-mcp → allow Read (agent needs full content for Edit)
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
|
|
62
105
|
# Allow on second attempt — agent needs full content for Edit
|
|
63
|
-
DENY_MARKER="$DENY_DIR/$(
|
|
106
|
+
DENY_MARKER="$DENY_DIR/$(file_sha256 "$FILE_PATH")"
|
|
64
107
|
if [[ -f "$DENY_MARKER" ]]; then
|
|
65
108
|
rm -f "$DENY_MARKER"
|
|
66
109
|
exit 0
|
|
@@ -82,8 +125,15 @@ if [[ "$TOOL_NAME" == "Grep" ]]; then
|
|
|
82
125
|
GREP_GLOB=$(echo "$INPUT" | jq -r '.tool_input.glob // empty')
|
|
83
126
|
GREP_TYPE=$(echo "$INPUT" | jq -r '.tool_input.type // empty')
|
|
84
127
|
|
|
128
|
+
# Block grep on .env files — prevent secret leakage
|
|
129
|
+
if echo "$GREP_GLOB" | grep -qiE '\.env' || echo "$GREP_PATH" | grep -qiE "$ENV_FILE_RE"; then
|
|
130
|
+
deny \
|
|
131
|
+
"Use get_env_vars for .env files — it masks sensitive values." \
|
|
132
|
+
"trace-mcp alternatives:\\n- get_env_vars { \\\"pattern\\\": \\\"search_term\\\" } — find env vars by key pattern without exposing values"
|
|
133
|
+
fi
|
|
134
|
+
|
|
85
135
|
# Allow grep on non-code file types
|
|
86
|
-
if echo "$GREP_GLOB" | grep -qiE '\.(md|json|ya?ml|toml|
|
|
136
|
+
if echo "$GREP_GLOB" | grep -qiE '\.(md|json|ya?ml|toml|txt|html|xml|csv|cfg|ini|lock|log)'; then
|
|
87
137
|
exit 0
|
|
88
138
|
fi
|
|
89
139
|
|
|
@@ -108,8 +158,15 @@ fi
|
|
|
108
158
|
if [[ "$TOOL_NAME" == "Glob" ]]; then
|
|
109
159
|
GLOB_PATTERN=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty')
|
|
110
160
|
|
|
161
|
+
# Block glob on .env patterns
|
|
162
|
+
if echo "$GLOB_PATTERN" | grep -qiE '\.env'; then
|
|
163
|
+
deny \
|
|
164
|
+
"Use get_env_vars for .env files — it masks sensitive values." \
|
|
165
|
+
"trace-mcp alternatives:\\n- get_env_vars {} — list all env vars across all .env files"
|
|
166
|
+
fi
|
|
167
|
+
|
|
111
168
|
# Allow glob for non-code patterns
|
|
112
|
-
if echo "$GLOB_PATTERN" | grep -qiE '\.(md|json|ya?ml|toml|
|
|
169
|
+
if echo "$GLOB_PATTERN" | grep -qiE '\.(md|json|ya?ml|toml|txt|html|xml|csv|cfg|ini|lock|log)'; then
|
|
113
170
|
exit 0
|
|
114
171
|
fi
|
|
115
172
|
|
|
@@ -128,6 +185,13 @@ if [[ "$TOOL_NAME" == "Bash" ]]; then
|
|
|
128
185
|
exit 0
|
|
129
186
|
fi
|
|
130
187
|
|
|
188
|
+
# Block bash commands targeting .env files — prevent secret leakage
|
|
189
|
+
if echo "$COMMAND" | grep -qiE "$ENV_FILE_RE"; then
|
|
190
|
+
deny \
|
|
191
|
+
"Use get_env_vars for .env files — it masks sensitive values (passwords, API keys, tokens)." \
|
|
192
|
+
"trace-mcp alternatives:\\n- get_env_vars {} — list all env vars across all .env files\\n- get_env_vars { \\\"pattern\\\": \\\"DB_\\\" } — filter by key prefix\\nNever access .env files via shell — secrets will leak into AI model context."
|
|
193
|
+
fi
|
|
194
|
+
|
|
131
195
|
# Block code exploration via bash (grep, find, cat, head, tail on code files)
|
|
132
196
|
if echo "$COMMAND" | grep -qE '(^|\|)\s*(grep|rg|find|cat|head|tail|less|more|awk|sed)\s' && echo "$COMMAND" | grep -qiE "$CODE_EXT_RE"; then
|
|
133
197
|
deny \
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM trace-mcp-precompact v0.1.0
|
|
3
|
+
REM trace-mcp PreCompact hook (Windows)
|
|
4
|
+
REM Injects session snapshot into compacted context to prevent "compaction amnesia".
|
|
5
|
+
REM Reads the live snapshot file written by the running trace-mcp MCP server and
|
|
6
|
+
REM returns it via the systemMessage field in Claude Code's hook output schema.
|
|
7
|
+
REM
|
|
8
|
+
REM Install: add to ~\.claude\settings.json or .claude\settings.local.json under PreCompact
|
|
9
|
+
REM See README.md for setup instructions.
|
|
10
|
+
|
|
11
|
+
setlocal enabledelayedexpansion
|
|
12
|
+
|
|
13
|
+
REM Determine project root from working directory
|
|
14
|
+
set "PROJECT_ROOT=%CD%"
|
|
15
|
+
|
|
16
|
+
REM Compute project hash using PowerShell (sha256, first 12 hex chars)
|
|
17
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('%PROJECT_ROOT%'))).Replace('-','').Substring(0,12).ToLower()"`) do set "PROJECT_HASH=%%i"
|
|
18
|
+
|
|
19
|
+
if "%PROJECT_HASH%"=="" goto :done
|
|
20
|
+
|
|
21
|
+
set "SNAPSHOT_FILE=%USERPROFILE%\.trace-mcp\sessions\%PROJECT_HASH%-snapshot.json"
|
|
22
|
+
|
|
23
|
+
if not exist "%SNAPSHOT_FILE%" goto :done
|
|
24
|
+
|
|
25
|
+
REM Read markdown from snapshot file and output as systemMessage
|
|
26
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "$j = Get-Content '%SNAPSHOT_FILE%' -Raw | ConvertFrom-Json; if ($j.markdown) { $j.markdown }"`) do set "MARKDOWN=%%i"
|
|
27
|
+
|
|
28
|
+
if "%MARKDOWN%"=="" goto :done
|
|
29
|
+
|
|
30
|
+
powershell -NoProfile -Command "$m = (Get-Content '%SNAPSHOT_FILE%' -Raw | ConvertFrom-Json).markdown; @{systemMessage=$m} | ConvertTo-Json -Compress"
|
|
31
|
+
|
|
32
|
+
:done
|
|
33
|
+
exit /b 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# trace-mcp-precompact v0.1.0
|
|
3
|
+
# trace-mcp PreCompact hook
|
|
4
|
+
# Injects session snapshot into compacted context to prevent "compaction amnesia".
|
|
5
|
+
# Reads the live snapshot file written by the running trace-mcp MCP server and
|
|
6
|
+
# returns it via the systemMessage field in Claude Code's hook output schema.
|
|
7
|
+
#
|
|
8
|
+
# Install: add to ~/.claude/settings.json or .claude/settings.local.json under PreCompact
|
|
9
|
+
# See README.md for setup instructions.
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
|
|
13
|
+
# Determine project root from working directory
|
|
14
|
+
PROJECT_ROOT="$(pwd)"
|
|
15
|
+
|
|
16
|
+
# Compute project hash (same algorithm as trace-mcp: sha256, first 12 hex chars)
|
|
17
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
18
|
+
PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | sha256sum | cut -c1-12)
|
|
19
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
20
|
+
PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | shasum -a 256 | cut -c1-12)
|
|
21
|
+
else
|
|
22
|
+
# Fallback: no hash available, exit silently
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
SNAPSHOT_FILE="$HOME/.trace-mcp/sessions/${PROJECT_HASH}-snapshot.json"
|
|
27
|
+
|
|
28
|
+
# If no snapshot file exists, exit silently (no session data yet)
|
|
29
|
+
if [[ ! -f "$SNAPSHOT_FILE" ]]; then
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Check file freshness — skip if older than 10 minutes (stale session)
|
|
34
|
+
if command -v stat >/dev/null 2>&1; then
|
|
35
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
36
|
+
FILE_MTIME=$(stat -f %m "$SNAPSHOT_FILE" 2>/dev/null || echo 0)
|
|
37
|
+
else
|
|
38
|
+
FILE_MTIME=$(stat -c %Y "$SNAPSHOT_FILE" 2>/dev/null || echo 0)
|
|
39
|
+
fi
|
|
40
|
+
NOW=$(date +%s)
|
|
41
|
+
AGE=$(( NOW - FILE_MTIME ))
|
|
42
|
+
if [[ $AGE -gt 600 ]]; then
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Read the markdown snapshot from the JSON file
|
|
48
|
+
MARKDOWN=$(jq -r '.markdown // empty' "$SNAPSHOT_FILE" 2>/dev/null)
|
|
49
|
+
|
|
50
|
+
if [[ -z "$MARKDOWN" ]]; then
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Output systemMessage for Claude Code to inject into compacted context
|
|
55
|
+
jq -n --arg msg "$MARKDOWN" '{ "systemMessage": $msg }'
|
|
56
|
+
|
|
57
|
+
exit 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM trace-mcp-reindex v0.1.0
|
|
3
|
+
REM trace-mcp PostToolUse auto-reindex hook (Windows)
|
|
4
|
+
REM Triggers incremental reindex after Edit/Write/MultiEdit on code files.
|
|
5
|
+
REM Runs trace-mcp index-file in the background - non-blocking.
|
|
6
|
+
REM
|
|
7
|
+
REM Install: add to ~\.claude\settings.json or .claude\settings.local.json under PostToolUse
|
|
8
|
+
REM See README.md for setup instructions.
|
|
9
|
+
|
|
10
|
+
setlocal enabledelayedexpansion
|
|
11
|
+
|
|
12
|
+
REM Read JSON from stdin into a temp file
|
|
13
|
+
set "TMPINPUT=%TEMP%\trace-mcp-reindex-input-%RANDOM%.json"
|
|
14
|
+
more > "%TMPINPUT%"
|
|
15
|
+
|
|
16
|
+
REM Get tool name from env or parse from JSON
|
|
17
|
+
if defined CLAUDE_TOOL_NAME (
|
|
18
|
+
set "TOOL_NAME=%CLAUDE_TOOL_NAME%"
|
|
19
|
+
) else (
|
|
20
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_name"`) do set "TOOL_NAME=%%i"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
REM Only handle edit-like tools
|
|
24
|
+
if /i not "%TOOL_NAME%"=="Edit" if /i not "%TOOL_NAME%"=="Write" if /i not "%TOOL_NAME%"=="MultiEdit" goto :done
|
|
25
|
+
|
|
26
|
+
for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.file_path"`) do set "FILE_PATH=%%i"
|
|
27
|
+
|
|
28
|
+
if "%FILE_PATH%"=="" goto :done
|
|
29
|
+
|
|
30
|
+
REM Skip non-code files
|
|
31
|
+
echo "%FILE_PATH%" | findstr /i /r "\.md$ \.json$ \.jsonc$ \.yaml$ \.yml$ \.toml$ \.ini$ \.cfg$ \.env \.txt$ \.html$ \.xml$ \.csv$ \.svg$ \.lock$ \.log$ \.sh$ \.bash$ \.zsh$ \.fish$ \.ps1$ \.bat$ \.cmd$" >nul 2>&1
|
|
32
|
+
if %errorlevel%==0 goto :done
|
|
33
|
+
|
|
34
|
+
REM Skip if not a recognised code file
|
|
35
|
+
echo "%FILE_PATH%" | findstr /i /r "\.ts$ \.tsx$ \.js$ \.jsx$ \.mjs$ \.cjs$ \.py$ \.pyi$ \.go$ \.rs$ \.java$ \.kt$ \.kts$ \.rb$ \.php$ \.cs$ \.cpp$ \.c$ \.h$ \.hpp$ \.swift$ \.scala$ \.vue$ \.svelte$ \.astro$" >nul 2>&1
|
|
36
|
+
if not %errorlevel%==0 goto :done
|
|
37
|
+
|
|
38
|
+
REM Reindex in background - non-blocking, silent
|
|
39
|
+
start /b "" cmd /c "trace-mcp index-file "%FILE_PATH%" >nul 2>&1"
|
|
40
|
+
|
|
41
|
+
:done
|
|
42
|
+
del "%TMPINPUT%" 2>nul
|
|
43
|
+
exit /b 0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@echo off
|
|
2
|
+
REM trace-mcp-worktree v0.1.0
|
|
3
|
+
REM trace-mcp WorktreeCreate / WorktreeRemove hook
|
|
4
|
+
REM On WorktreeCreate: registers and indexes the new worktree.
|
|
5
|
+
REM On WorktreeRemove: deregisters the worktree project.
|
|
6
|
+
|
|
7
|
+
setlocal enabledelayedexpansion
|
|
8
|
+
|
|
9
|
+
set "INPUT="
|
|
10
|
+
for /f "delims=" %%i in ('more') do set "INPUT=!INPUT!%%i"
|
|
11
|
+
|
|
12
|
+
REM Extract event type
|
|
13
|
+
for /f "delims=" %%e in ('echo !INPUT! ^| jq -r ".hook_event_name // .event // empty" 2^>nul') do set "EVENT=%%e"
|
|
14
|
+
if not defined CLAUDE_HOOK_EVENT set "CLAUDE_HOOK_EVENT=%EVENT%"
|
|
15
|
+
if defined CLAUDE_HOOK_EVENT set "EVENT=%CLAUDE_HOOK_EVENT%"
|
|
16
|
+
|
|
17
|
+
REM Extract worktree path
|
|
18
|
+
for /f "delims=" %%p in ('echo !INPUT! ^| jq -r ".tool_input.path // .worktree_path // .path // .cwd // empty" 2^>nul') do set "WPATH=%%p"
|
|
19
|
+
|
|
20
|
+
if "%WPATH%"=="" exit /b 0
|
|
21
|
+
|
|
22
|
+
if "%EVENT%"=="WorktreeCreate" (
|
|
23
|
+
start /b trace-mcp add "%WPATH%" --force --json >nul 2>&1
|
|
24
|
+
) else if "%EVENT%"=="WorktreeRemove" (
|
|
25
|
+
REM Clean up worktree DB file if it exists
|
|
26
|
+
for /f "delims=" %%h in ('echo %WPATH%^| certutil -hashfile - SHA256 2^>nul ^| findstr /v "hash"') do set "PHASH=%%h"
|
|
27
|
+
if defined PHASH (
|
|
28
|
+
set "PHASH=!PHASH:~0,12!"
|
|
29
|
+
del /q "%USERPROFILE%\.trace-mcp\db\!PHASH!.db" "%USERPROFILE%\.trace-mcp\db\!PHASH!.db-shm" "%USERPROFILE%\.trace-mcp\db\!PHASH!.db-wal" 2>nul
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
exit /b 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# trace-mcp-worktree v0.1.0
|
|
3
|
+
# trace-mcp WorktreeCreate / WorktreeRemove hook
|
|
4
|
+
# On WorktreeCreate: registers and indexes the new worktree so trace-mcp tools work immediately.
|
|
5
|
+
# On WorktreeRemove: deregisters the worktree project from the registry.
|
|
6
|
+
#
|
|
7
|
+
# Install: add to ~/.claude/settings.json or .claude/settings.local.json
|
|
8
|
+
# See README.md for setup instructions.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
INPUT=$(cat)
|
|
13
|
+
|
|
14
|
+
# Claude Code passes the event type via CLAUDE_HOOK_EVENT or hook_event_name
|
|
15
|
+
EVENT="${CLAUDE_HOOK_EVENT:-}"
|
|
16
|
+
if [[ -z "$EVENT" ]]; then
|
|
17
|
+
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // .event // empty' 2>/dev/null)
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
# Extract worktree path from hook input
|
|
21
|
+
WORKTREE_PATH=$(echo "$INPUT" | jq -r '.tool_input.path // .worktree_path // .path // empty' 2>/dev/null)
|
|
22
|
+
|
|
23
|
+
if [[ -z "$WORKTREE_PATH" ]]; then
|
|
24
|
+
# Fallback: use working directory from input
|
|
25
|
+
WORKTREE_PATH=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
if [[ -z "$WORKTREE_PATH" ]]; then
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Resolve to absolute path
|
|
33
|
+
WORKTREE_PATH=$(cd "$WORKTREE_PATH" 2>/dev/null && pwd || echo "$WORKTREE_PATH")
|
|
34
|
+
|
|
35
|
+
case "$EVENT" in
|
|
36
|
+
WorktreeCreate|worktree_create|create)
|
|
37
|
+
# Register and index the new worktree in the background
|
|
38
|
+
# --force: re-register even if parent repo is already known (worktree is a different root)
|
|
39
|
+
nohup trace-mcp add "$WORKTREE_PATH" --force --json >/dev/null 2>&1 &
|
|
40
|
+
;;
|
|
41
|
+
WorktreeRemove|worktree_remove|remove)
|
|
42
|
+
# Clean up the worktree DB file if it exists.
|
|
43
|
+
# The global registry entry becomes stale but is harmless — next `trace-mcp add` overwrites it.
|
|
44
|
+
PROJECT_HASH=""
|
|
45
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
46
|
+
PROJECT_HASH=$(echo -n "$WORKTREE_PATH" | sha256sum | cut -c1-12)
|
|
47
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
48
|
+
PROJECT_HASH=$(echo -n "$WORKTREE_PATH" | shasum -a 256 | cut -c1-12)
|
|
49
|
+
fi
|
|
50
|
+
if [[ -n "$PROJECT_HASH" ]]; then
|
|
51
|
+
DB_FILE="$HOME/.trace-mcp/db/${PROJECT_HASH}.db"
|
|
52
|
+
rm -f "$DB_FILE" "$DB_FILE-shm" "$DB_FILE-wal" 2>/dev/null
|
|
53
|
+
fi
|
|
54
|
+
;;
|
|
55
|
+
esac
|
|
56
|
+
|
|
57
|
+
exit 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trace-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Framework-aware code intelligence MCP server — 48+ frameworks, 68 languages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"prepublishOnly": "npm run build"
|
|
19
19
|
},
|
|
20
20
|
"engines": {
|
|
21
|
-
"node": ">=
|
|
21
|
+
"node": ">=18.0.0"
|
|
22
22
|
},
|
|
23
23
|
"license": "ELv2",
|
|
24
24
|
"author": "Nikolai Vysotskyi",
|