Skip to content

Modernize MCP Server#22

Merged
peteretelej merged 13 commits intomainfrom
modernize-mcp
Mar 15, 2026
Merged

Modernize MCP Server#22
peteretelej merged 13 commits intomainfrom
modernize-mcp

Conversation

@peteretelej
Copy link
Owner

@peteretelej peteretelej commented Mar 14, 2026

The MCP SDK has evolved considerably. This bumps up the sdk version in use to benefit from improvements to the SDK. Also improves cross platform supprot

Summary by CodeRabbit

  • New Features

    • Multi-directory indexing, a gated delete-index tool, dynamic tool listing, and graceful storage shutdown
    • Configurable JSON logging with runtime log level control
  • Bug Fixes

    • Skip non-UTF-8 files with warning and normalize CRLF→LF
    • Path containment validation to prevent invalid/unauthorized file access
  • Chores

    • SDK dependency bumped; DB defaulted to WAL mode; .gitignore updated for SQLite WAL/SHM; simplified pre-push checks
  • Tests

    • New unit tests for delete-index, path validation, storage, and MCP handlers

…schema fix

Prevents path traversal attacks on get_content/get_chunk by validating file paths
against indexed directories. Bumps MCP SDK to 1.27.1 for security fixes, adds tool
annotations, changes index tool input from comma-separated string to proper array,
fixes directory prefix collision in SQL queries, and enables SQLite WAL mode.
…recovery hints

Adds SIGTERM/SIGINT handlers to close SQLite connections cleanly, per-directory
indexing mutex to prevent concurrent indexing of the same path, structured
JSON logging to stderr, MCP logging capability for client notifications, and
descriptive error messages with recovery guidance.
…NC path validation

Adds a delete_index tool (gated by DISABLE_DESTRUCTIVE env var) to remove
directory indexes, normalizes CRLF line endings before chunking for consistent
results, skips non-UTF-8 files during indexing with a warning, and validates
Windows UNC path format in path validation.
@coderabbitai
Copy link

coderabbitai bot commented Mar 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds structured JSON logging, per-directory indexing mutexes, multi-directory index API, an index-deletion tool, path-validation utilities, storage lifecycle and directory-management APIs, content normalization/UTF-8 guards, .gitignore updates, a dependency bump, and extensive tests.

Changes

Cohort / File(s) Summary
Logging Infrastructure
src/logger.ts
New JSON stderr logger with level filtering and env-based init; exports set/get/init/log and LogLevel type.
MCP Tools & Handlers
src/mcp.ts, src/mcp-handlers.ts
Adds delete_index tool, dynamic getMcpTools(), per-directory mutexing, multi-directory index input (directory_paths[]), indexed-dirs cache, setMcpServer/refreshIndexedDirsCache exports, handler signature updates to accept config, and MCP lifecycle/logging wiring.
Path Validation & Utilities
src/path-validation.ts
New exports resolveIndexedDirectories and validatePathWithinIndexedDirs (null-byte, UNC, symlink-aware, exact-or-prefix containment checks).
Storage Management
src/storage.ts
Registry of open SQLiteStorage instances, closeAllStorage(), WAL and busy_timeout PRAGMAs, directory APIs (getDirectories, deleteDirectory, deleteFilesByDirectory), and expanded path-matching/LIKE clause logic.
Indexing / Content Processing
src/indexing.ts
Adds logger usage, non-UTF-8 detection (U+FFFD) to skip files, CRLF→LF normalization, and skipped-file tracking to avoid embedding/indexing invalid files.
Tests
tests/delete-index.unit.test.ts, tests/path-validation.unit.test.ts, tests/storage.unit.test.ts, tests/mcp-handlers.test.ts
New and updated unit tests for delete_index, path validation/resolution, WAL mode and storage directory APIs, multi-directory indexing, mutex concurrency, and handler behavior.
Config & Scripts
.gitignore, package.json, scripts/pre-push
Add *.sqlite-wal and *.sqlite-shm to .gitignore; bump @modelcontextprotocol/sdk ^1.25.2 → ^1.27.1 in package.json; simplify pre-push script to run lint + tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client as MCP Client
    participant Server as MCP Server
    participant Handlers as MCP Handlers
    participant Storage as SQLiteStorage
    participant Qdrant as Qdrant Search

    Client->>Server: delete_index(directory_path)
    Server->>Handlers: handleDeleteIndexTool({directory_path}, config)
    Handlers->>Storage: getDirectory(directory_path)
    alt Directory exists
        Storage-->>Handlers: directory record
        Handlers->>Storage: deleteFilesByDirectory(directory_path)
        Storage-->>Handlers: deleted files count
        Handlers->>Storage: deleteDirectory(directory_path)
        Storage-->>Handlers: ✓
        Handlers->>Qdrant: delete points for directory
        Qdrant-->>Handlers: ✓
        Handlers->>Handlers: refreshIndexedDirsCache()
        Handlers->>Server: sendLoggingMessage(deletion summary)
        Server-->>Client: Tool result (summary)
    else Directory not indexed
        Storage-->>Handlers: null
        Handlers-->>Server: Error response
        Server-->>Client: Error: Not indexed
    end
Loading
sequenceDiagram
    participant Client1 as Client A
    participant Client2 as Client B
    participant Server as MCP Server
    participant Mutex as Indexing Mutex
    participant Handlers as MCP Handlers
    participant Indexer as Indexing Engine

    Client1->>Server: index({directory_paths: ['/path']})
    Client2->>Server: index({directory_paths: ['/path']})
    Server->>Mutex: Acquire lock for /path
    Mutex-->>Server: Locked (Client A)
    Server->>Handlers: handleIndexTool (Client A)
    Handlers->>Indexer: perform indexing (normalize, chunk, embed)
    Note over Handlers: Client B waits for lock
    Indexer-->>Handlers: Indexing complete
    Handlers->>Mutex: Release lock
    Mutex-->>Server: Available
    Server->>Client1: Tool result
    Server->>Mutex: Acquire lock for /path
    Mutex-->>Server: Locked (Client B)
    Server->>Handlers: handleIndexTool (Client B)
    Handlers->>Indexer: perform indexing
    Indexer-->>Handlers: Indexing complete
    Handlers->>Mutex: Release lock
    Server->>Client2: Tool result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I hopped through logs and paths so wide,
I locked each burrow, kept conflicts aside,
Cleared old nests with a tidy sweep,
PRAGMAs set, tests awake from sleep,
Multi-path indexing now bounds with pride. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title "Modernize MCP Server" is vague and generic, using the non-descriptive term "Modernize" that does not clearly convey the specific changes in the changeset. Use a more specific title that describes concrete changes, such as "Bump MCP SDK to v1.27.1 and add delete index tool with path validation" or "Add delete index support and cross-platform path handling to MCP server".
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch modernize-mcp
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Update transitive deps (hono, rollup, minimatch, ajv, flatted) to resolve 6 security advisories.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (1)
src/indexing.ts (1)

255-263: Replace fs.readFile with TextDecoder to properly detect invalid UTF-8.

The current approach at line 258 checks for \uFFFD after decoding with Node's 'utf-8' option, which silently replaces invalid byte sequences with that character. This causes false positives: any legitimate file containing U+FFFD gets incorrectly skipped. Use TextDecoder with { fatal: true } to throw on actual invalid byte sequences instead.

Suggested fix
-          const rawContent = await fs.readFile(file.path, 'utf-8');
-
-          // Skip non-UTF-8 files (Node.js inserts U+FFFD for invalid byte sequences)
-          if (rawContent.includes('\uFFFD')) {
+          const rawBytes = await fs.readFile(file.path);
+          let rawContent: string;
+          try {
+            rawContent = new TextDecoder('utf-8', { fatal: true }).decode(rawBytes);
+          } catch {
             log('warning', 'Skipping non-UTF-8 file', { path: file.path });
             await sqlite.upsertFile(file, [], ['Skipped: file appears to be non-UTF-8 encoded']);
             skipped++;
             continue;
-          }
+          }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/indexing.ts` around lines 255 - 263, The code currently reads files using
fs.readFile(..., 'utf-8') and checks rawContent for '\uFFFD', which
misclassifies legitimate files containing U+FFFD as invalid; replace the reading
step to read raw bytes (fs.readFile without encoding or Buffer) and decode with
new TextDecoder('utf-8', { fatal: true }) so invalid UTF-8 throws instead of
being replaced, catch that specific decode error to run log('warning', ...) and
sqlite.upsertFile(file, [], ['Skipped: file appears to be non-UTF-8 encoded'])
and only increment skipped/continue on decode failures, otherwise use the
decoded string as rawContent for downstream processing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/mcp-handlers.ts`:
- Around line 348-363: The current loop that calls
qdrant.deletePointsByFilePath(file.path) suppresses failures (logging warnings)
and then proceeds to call sqlite.deleteFilesByDirectory(dirPath) and
sqlite.deleteDirectory(dirPath), which can leave orphaned vectors; change this
so that in the code that iterates over files (the files const and the for..of
loop calling qdrant.deletePointsByFilePath) you collect any errors (or
short-circuit on first error), and if any qdrant deletion fails return or throw
an error/partial-failure result instead of proceeding to
sqlite.deleteFilesByDirectory and sqlite.deleteDirectory; only perform
sqlite.deleteFilesByDirectory(dirPath) and sqlite.deleteDirectory(dirPath) when
all qdrant.deletePointsByFilePath calls succeeded (or include clear
partial-failure reporting when some succeeded and some failed).
- Around line 121-140: The per-directory lock uses un-normalized keys
(args.directory_paths.map(p => p.trim())) and handleDeleteIndexTool doesn't use
the same lock, allowing races and bypass via trailing slashes, symlinks, or case
differences; fix by canonicalizing each directory path before using it as a
mutex key (e.g., call a shared normalize function that does
path.resolve/path.normalize, fs.realpathSync or async realpath to follow
symlinks, strip trailing slashes, and on Windows apply .toLowerCase()) and
replace usages of args.directory_paths/map trim with that normalizer when
creating indexingPromise and setting/getting indexingMutex (symbols:
indexingMutex, indexingPromise, resolveIndexing, args.directory_paths). Also
update handleDeleteIndexTool to normalize its input path and to acquire/await
the same per-directory mutex (or set a delete-specific mutex entry) before
performing deletions so deletes serialize with in-flight indexing; ensure both
index and delete code always set and clear the mutex (resolveIndexing) in
finally blocks to avoid stale locks.
- Around line 39-47: ensureIndexedDirsCache currently calls initializeStorage
which always initializes Qdrant; change it so the cache population uses
SQLite-only initialization and mark the cache as initialized even if empty: add
a new SQLite-only initializer (or add an option to initializeStorage, e.g.,
initializeStorage(config, { skipVectorStore: true })) and call that from
ensureIndexedDirsCache, then call refreshIndexedDirsCache(sqlite) and set a
boolean flag (e.g., indexedDirsCacheInitialized = true) after refresh so
indexedDirsCache.size === 0 no longer triggers repeated startup work; reference
ensureIndexedDirsCache, initializeStorage (or new initializeSqliteOnly),
refreshIndexedDirsCache, and indexedDirsCache in your change.

In `@src/mcp.ts`:
- Around line 341-344: The DISABLE_DESTRUCTIVE flag is only preventing
DELETE_INDEX_TOOL from being listed but not from being executed; update the
CallTool execution path to enforce the same flag check so destructive tools
cannot be invoked by name. In the CallTool handler (the function that processes
tool calls), add a guard that checks process.env.DISABLE_DESTRUCTIVE === 'true'
and rejects/throws when the requested tool matches DELETE_INDEX_TOOL (or any
tool with a destructive marker), or introduce an isDestructive(tool) helper and
use it to block execution; keep the existing ListTools logic but ensure both
listing and execution use the same guard so DELETE_INDEX_TOOL cannot be called
when destructive operations are disabled.

In `@src/storage.ts`:
- Around line 442-443: Queries that use a hardcoded '/' in the LIKE pattern must
be made platform-agnostic and must escape SQL LIKE wildcards; update any
occurrences in getFilesByDirectory(), deleteFilesByDirectory(), and the
status/count queries that prepare statements like this.db.prepare('SELECT * FROM
files WHERE path = ? OR path LIKE ?') to: normalize the input directory (e.g.,
path.normalize(directoryPath)), escape '%' and '_' (and the escape char) in that
normalized directory (e.g., dir.replace(/([%_\\])/g, '\\$1')), then build two
LIKE patterns using both path.sep and its alternate separator (e.g.,
`${escapedDir}${sep}%` and `${escapedDir}${altSep}%`) so both "C:\\a\\b\\%" and
"C:/a/b/%" match cross-platform, pass those as parameters to the prepared
statement, and add an ESCAPE '\\' clause or use parameter escaping consistent
with your DB driver so the escaped wildcards are treated literally.

In `@tests/mcp-handlers.test.ts`:
- Around line 435-459: The test's mock of indexDirectories and current
assertions don't guarantee serialization; update the test that uses
handleIndexTool and indexDirectories to assert ordering by inspecting callOrder:
after running the two concurrent calls, verify that the first "start:/same"
occurs before the first "end:/same" and that the second "start:/same" occurs
after that first "end:/same" (e.g., check indices of "start:/same" and
"end:/same" entries or assert the full sequence is start,end,start,end) so the
test fails if the second index starts before the first finishes.

In `@tests/storage.unit.test.ts`:
- Around line 89-90: The test hard-codes a Unix /tmp path (tmpPath) before
assigning it to config.storage.sqlitePath, which fails on Windows; change the
test to create a platform-safe temporary file path using the OS temp directory
(e.g., Node's os.tmpdir() or a temp-file helper) and assign that generated path
to config.storage.sqlitePath so the test runs cross-platform (locate the tmpPath
assignment and update how tmpPath is constructed in the test).
- Around line 117-118: The test relies on insertion order but getDirectories()
(storage.getDirectories) does not guarantee ORDER BY, so make the assertion
order-independent: either sort the returned array (const dirs = (await
storage.getDirectories()).sort()) and compare to a sorted expected array, or use
expect(dirs).toEqual(expect.arrayContaining(['/dir1','/dir2'])) plus a length
check to ensure no extra entries; update the test accordingly where
storage.getDirectories() is called.

---

Nitpick comments:
In `@src/indexing.ts`:
- Around line 255-263: The code currently reads files using fs.readFile(...,
'utf-8') and checks rawContent for '\uFFFD', which misclassifies legitimate
files containing U+FFFD as invalid; replace the reading step to read raw bytes
(fs.readFile without encoding or Buffer) and decode with new
TextDecoder('utf-8', { fatal: true }) so invalid UTF-8 throws instead of being
replaced, catch that specific decode error to run log('warning', ...) and
sqlite.upsertFile(file, [], ['Skipped: file appears to be non-UTF-8 encoded'])
and only increment skipped/continue on decode failures, otherwise use the
decoded string as rawContent for downstream processing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e2b1754-f5ca-4c3a-b710-a6841a1a57d9

📥 Commits

Reviewing files that changed from the base of the PR and between faf032d and d0561f6.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (16)
  • .gitignore
  • _docs/mcp-modernization/1-security-and-correctness/pr.md
  • _docs/mcp-modernization/2-operational-hardening/pr.md
  • _docs/mcp-modernization/3-features-and-cross-platform/pr.md
  • _docs/mcp-modernization/plan.md
  • package.json
  • src/indexing.ts
  • src/logger.ts
  • src/mcp-handlers.ts
  • src/mcp.ts
  • src/path-validation.ts
  • src/storage.ts
  • tests/delete-index.unit.test.ts
  • tests/mcp-handlers.test.ts
  • tests/path-validation.unit.test.ts
  • tests/storage.unit.test.ts

@codecov
Copy link

codecov bot commented Mar 14, 2026

Codecov Report

❌ Patch coverage is 81.32388% with 79 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.06%. Comparing base (faf032d) to head (0c6e28b).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #22      +/-   ##
==========================================
- Coverage   81.26%   81.06%   -0.21%     
==========================================
  Files          13       15       +2     
  Lines        2306     2672     +366     
  Branches      332      519     +187     
==========================================
+ Hits         1874     2166     +292     
- Misses        432      506      +74     
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/path-validation.unit.test.ts (1)

165-170: Redundant assertion.

Line 167 asserts dirs.size equals 2, then line 170 asserts dirs.size is greater than or equal to 2. The second assertion is redundant and weaker.

Proposed fix
       expect(dirs.size).toBe(2);
       // The resolved paths should contain the original paths (resolved)
       // Since these directories don't exist, they fall back to path.resolve()
-      expect(dirs.size).toBeGreaterThanOrEqual(2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/path-validation.unit.test.ts` around lines 165 - 170, The test contains
a redundant assertion: after calling resolveIndexedDirectories(storage), the
test first asserts expect(dirs.size).toBe(2) and then redundantly asserts
expect(dirs.size).toBeGreaterThanOrEqual(2); remove the weaker second assertion
so only the precise check remains. Locate the test using
resolveIndexedDirectories and the variable dirs in
tests/path-validation.unit.test.ts and delete the
expect(dirs.size).toBeGreaterThanOrEqual(2) line.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/path-validation.unit.test.ts`:
- Around line 165-170: The test contains a redundant assertion: after calling
resolveIndexedDirectories(storage), the test first asserts
expect(dirs.size).toBe(2) and then redundantly asserts
expect(dirs.size).toBeGreaterThanOrEqual(2); remove the weaker second assertion
so only the precise check remains. Locate the test using
resolveIndexedDirectories and the variable dirs in
tests/path-validation.unit.test.ts and delete the
expect(dirs.size).toBeGreaterThanOrEqual(2) line.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae261f14-b24b-42ad-8fe4-3855f8f56266

📥 Commits

Reviewing files that changed from the base of the PR and between d0561f6 and a3944c7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • .gitignore
  • package.json
  • src/indexing.ts
  • src/logger.ts
  • src/mcp-handlers.ts
  • src/mcp.ts
  • src/path-validation.ts
  • src/storage.ts
  • tests/delete-index.unit.test.ts
  • tests/mcp-handlers.test.ts
  • tests/path-validation.unit.test.ts
  • tests/storage.unit.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/storage.unit.test.ts
  • src/path-validation.ts
  • src/logger.ts
  • .gitignore
  • tests/delete-index.unit.test.ts

- Guard delete_index at CallTool time when DISABLE_DESTRUCTIVE is set
- Use SQLite-only init for path validation cache (no Qdrant dependency)
- Add initialized flag so empty cache doesn't re-trigger init
- Normalize mutex keys (resolve, realpath, strip trailing sep)
- Acquire per-directory mutex in handleDeleteIndexTool
- Abort delete when vector cleanup fails to prevent orphaned vectors
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/pre-push`:
- Line 6: The pre-push script's echo ("Linting and type checking...") is
misleading because the script only runs npm run lint; either update the message
to reflect linting only or add a typecheck step. Edit the scripts/pre-push file
to either change the echo to e.g. "Running lint..." (or similar) to match the
existing npm run lint invocation, or append a type check command (e.g. run your
project’s typecheck script such as npm run typecheck or tsc --noEmit) after npm
run lint so the log accurately reflects both actions; update the echo
accordingly if you add the typecheck.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 441315b7-c6cd-4c97-9e6a-2f2da0daa380

📥 Commits

Reviewing files that changed from the base of the PR and between e1cd1e0 and 8fcfa6a.

📒 Files selected for processing (1)
  • scripts/pre-push

- LIKE queries now match both / and \ separators with proper escaping
- Replace hard-coded /tmp with os.tmpdir() in tests
- Sort getDirectories() assertion to avoid non-deterministic order
- Assert sequential execution order in mutex serialization test
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/storage.ts (1)

906-907: ⚠️ Potential issue | 🟡 Minor

Escape % and _ in directory status LIKE predicates.

On Line 906 and Line 907, the status subqueries only escape backslashes. Directory paths containing % or _ will be treated as wildcards, causing incorrect files_count/chunks_count.

Suggested fix
-        (SELECT COUNT(*) FROM files f WHERE f.path = d.path OR f.path LIKE REPLACE(d.path, '\\', '\\\\') || '/%' ESCAPE '\\' OR f.path LIKE REPLACE(d.path, '\\', '\\\\') || '\\%' ESCAPE '\\') as files_count,
-        (SELECT COALESCE(SUM(json_array_length(f.chunks_json)), 0) FROM files f WHERE (f.path = d.path OR f.path LIKE REPLACE(d.path, '\\', '\\\\') || '/%' ESCAPE '\\' OR f.path LIKE REPLACE(d.path, '\\', '\\\\') || '\\%' ESCAPE '\\') AND f.chunks_json IS NOT NULL) as chunks_count
+        (SELECT COUNT(*) FROM files f WHERE f.path = d.path OR f.path LIKE REPLACE(REPLACE(REPLACE(d.path, '\\', '\\\\'), '%', '\\%'), '_', '\\_') || '/%' ESCAPE '\\' OR f.path LIKE REPLACE(REPLACE(REPLACE(d.path, '\\', '\\\\'), '%', '\\%'), '_', '\\_') || '\\%' ESCAPE '\\') as files_count,
+        (SELECT COALESCE(SUM(json_array_length(f.chunks_json)), 0) FROM files f WHERE (f.path = d.path OR f.path LIKE REPLACE(REPLACE(REPLACE(d.path, '\\', '\\\\'), '%', '\\%'), '_', '\\_') || '/%' ESCAPE '\\' OR f.path LIKE REPLACE(REPLACE(REPLACE(d.path, '\\', '\\\\'), '%', '\\%'), '_', '\\_') || '\\%' ESCAPE '\\') AND f.chunks_json IS NOT NULL) as chunks_count
#!/bin/bash
set -euo pipefail

# Verify current status query only escapes backslashes in LIKE branch
nl -ba src/storage.ts | sed -n '901,909p'
rg -n "REPLACE\\(d\\.path, '\\\\\\\\', '\\\\\\\\\\\\\\\\'\\).*LIKE" src/storage.ts -C2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/storage.ts` around lines 906 - 907, The LIKE predicates in the
files_count and chunks_count subqueries treat % and _ in directory names as
wildcards because only backslashes are escaped; update the REPLACE chain for
d.path used in both subqueries (the expressions used to build the LIKE patterns
in files_count and chunks_count) to also escape '%' and '_' (e.g.,
REPLACE(REPLACE(REPLACE(d.path, '\\', '\\\\'), '%', '\\%'), '_', '\\_')) so the
pattern treats literal percent/underscore characters, and keep the existing
ESCAPE '\' clause; make the same change for both places where REPLACE(d.path,
'\\', '\\\\') is currently used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/mcp-handlers.test.ts`:
- Around line 297-305: Add an assertion that validation runs before file-read by
comparing mock invocation order: after calling handleGetContentTool, grab the
mocked functions validatePathWithinIndexedDirs and getFileContent (and for the
chunk test getChunkContent) and assert their invocation order using their
mock.invocationCallOrder (e.g.
expect(validatePathWithinIndexedDirs.mock.invocationCallOrder[0]).toBeLessThan(getFileContent.mock.invocationCallOrder[0]));
update both tests that currently only check toHaveBeenCalled to also perform
this ordering assertion referencing validatePathWithinIndexedDirs,
getFileContent, and getChunkContent as appropriate.

---

Duplicate comments:
In `@src/storage.ts`:
- Around line 906-907: The LIKE predicates in the files_count and chunks_count
subqueries treat % and _ in directory names as wildcards because only
backslashes are escaped; update the REPLACE chain for d.path used in both
subqueries (the expressions used to build the LIKE patterns in files_count and
chunks_count) to also escape '%' and '_' (e.g., REPLACE(REPLACE(REPLACE(d.path,
'\\', '\\\\'), '%', '\\%'), '_', '\\_')) so the pattern treats literal
percent/underscore characters, and keep the existing ESCAPE '\' clause; make the
same change for both places where REPLACE(d.path, '\\', '\\\\') is currently
used.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 73da9e2b-f775-401c-b48e-9849c36351d0

📥 Commits

Reviewing files that changed from the base of the PR and between 8fcfa6a and a0e04c0.

📒 Files selected for processing (3)
  • src/storage.ts
  • tests/mcp-handlers.test.ts
  • tests/storage.unit.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/storage.unit.test.ts

curl --max-time kills active downloads because it counts wall-clock
time, not idle time. The streaming API keeps the connection open the
entire download, so --max-time always fires before completion.

ollama pull CLI handles retries and stall detection internally.
Integration tests pass locally at 30s against ollama:latest, but
CI runners (2-core, shared resources) need more headroom for cold
model loading on first embedding call.
@peteretelej peteretelej merged commit ef859fb into main Mar 15, 2026
11 checks passed
@peteretelej peteretelej deleted the modernize-mcp branch March 15, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant