trace-mcp 1.4.1 → 1.5.4

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.
@@ -1,9 +1,12 @@
1
1
  @echo off
2
- REM trace-mcp-guard v0.2.0
2
+ REM trace-mcp-guard v0.4.0
3
3
  REM trace-mcp PreToolUse guard (Windows)
4
4
  REM Blocks Read/Grep/Glob/Bash on source code files - redirects to trace-mcp tools.
5
5
  REM Allows: non-code files, Read before Edit, safe Bash commands (git, npm, build, test).
6
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
7
10
  REM Install: add to ~\.claude\settings.json or .claude\settings.local.json
8
11
  REM See README.md for setup instructions.
9
12
 
@@ -47,6 +50,14 @@ REM Block code file reads - redirect to trace-mcp
47
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
48
51
  if not %errorlevel%==0 goto :allow
49
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
+
50
61
  REM Allow on second attempt (agent needs full content for Edit)
51
62
  set "SESSION_ID=default"
52
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"
@@ -54,7 +65,7 @@ set "DENY_DIR=%TEMP%\trace-mcp-guard-%SESSION_ID%"
54
65
  if not exist "%DENY_DIR%" mkdir "%DENY_DIR%" 2>nul
55
66
 
56
67
  REM Create a hash of the file path for the marker
57
- for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.MD5]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('%FILE_PATH%'))).Replace('-','')"`) do set "MARKER_HASH=%%i"
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"
58
69
  set "DENY_MARKER=%DENY_DIR%\%MARKER_HASH%"
59
70
 
60
71
  if exist "%DENY_MARKER%" (
@@ -137,6 +148,13 @@ REM Allow safe commands
137
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
138
149
  if %errorlevel%==0 goto :allow
139
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
+
140
158
  REM Block code exploration via bash
141
159
  set "HAS_EXPLORE=0"
142
160
  echo "%COMMAND%" | findstr /i /r "grep rg find cat head tail less more awk sed" >nul 2>&1
@@ -1,9 +1,13 @@
1
1
  #!/usr/bin/env bash
2
- # trace-mcp-guard v0.2.0
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
 
@@ -24,6 +28,11 @@ ENV_FILE_RE='\.env(\.[a-zA-Z0-9._-]+)?$'
24
28
  # Safe bash command prefixes — never block
25
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 )'
26
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
+
27
36
  deny() {
28
37
  local reason="$1"
29
38
  local context="$2"
@@ -45,6 +54,19 @@ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "default"')
45
54
  DENY_DIR="/tmp/trace-mcp-guard-${SESSION_ID}"
46
55
  mkdir -p "$DENY_DIR" 2>/dev/null
47
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
+
48
70
  # --- Read ---
49
71
  if [[ "$TOOL_NAME" == "Read" ]]; then
50
72
  FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
@@ -70,8 +92,18 @@ if [[ "$TOOL_NAME" == "Read" ]]; then
70
92
 
71
93
  # Block code file reads → redirect to trace-mcp
72
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
+
73
105
  # Allow on second attempt — agent needs full content for Edit
74
- DENY_MARKER="$DENY_DIR/$(echo "$FILE_PATH" | md5sum | cut -d' ' -f1)"
106
+ DENY_MARKER="$DENY_DIR/$(file_sha256 "$FILE_PATH")"
75
107
  if [[ -f "$DENY_MARKER" ]]; then
76
108
  rm -f "$DENY_MARKER"
77
109
  exit 0
@@ -153,6 +185,13 @@ if [[ "$TOOL_NAME" == "Bash" ]]; then
153
185
  exit 0
154
186
  fi
155
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
+
156
195
  # Block code exploration via bash (grep, find, cat, head, tail on code files)
157
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
158
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,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.4.1",
3
+ "version": "1.5.4",
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": ">=20.0.0"
21
+ "node": ">=18.0.0"
22
22
  },
23
23
  "license": "ELv2",
24
24
  "author": "Nikolai Vysotskyi",