mcp-tool-silent-empty-result
I diagnosed and independently verified the fix. The solution is written to:
~/SOLUTION-mcp-tool-silent-empty-result.md
Root cause — in hilo-mcp/src/tools/mod.rs, list_directory() gated its local-filesystem fallback on path == "/" only:
if entries.is_empty() && path == "/" { // <-- every other path fell through
if let Ok(cwd) = std::env::current_dir() { ... }
}
With no backends configured, any other path produced the virtual listing's empty vector and returned {"entries":[],"total":0} with rc=0. The nested if let Ok(...) also swallowed read_dir errors, so nonexistent and file paths were silently reported as empty directories.
Fix (commit 8476574) — fall back to the real filesystem for any path when the virtual listing is empty; return explicit McpError::Protocol for file paths (not a directory) and nonexistent paths (path does not exist); preserve the / → CWD special case.
I installed Rust 1.98.1, built the repo, and:
| Check | Result |
|---|---|
cargo test -p hilo_mcp |
42/42 pass (incl. the 3 new regression tests) |
cargo clippy -p hilo_mcp --all-targets -- -D warnings |
clean |
cargo fmt --all --check |
clean |
Live NDJSON probe vs rebuilt hilo serve --mcp |
fixed behavior confirmed |
Live probe, same binary tree, pre-fix vs post-fix:
PRE-FIX: "." -> 0 ; populated -> 0 ; / -> 4 ; missing -> 0 ; file -> 0 (all rc=0, no error)
POST-FIX: "." -> 4 ; populated -> 2 ; / -> 4 ; missing -> -32603 "does not exist" ; file -> -32603 "not a directory"
This both confirms the defect and demonstrates it is gone. I also documented the methodological point called out in the task: re-probe the live binary at HEAD before trusting a dogfood premise — the doc gives the exact rebuild/probe sequence.
# Evidence - Problem class: mcp-tool-silent-empty-result - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T09:23:39.509Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "MCP tool returns a success-shaped empty result instead of an error when it cannot enumerate: hilo serve --mcp vfs_list_directory returned {\"entries\":[],\"total\":0} with rc=0 on every populated directory because the local-filesystem fallback was gated on path==/ only. Anti-pattern: 0 results with no error is indistinguishable from an empty directory (hang class for agent consumers). Fix shape: extend fallback to ANY path, return explicit errors for nonexistent paths and file paths; re-probe the live binary before trusting a dogfood premise.", "environment": "linux", "language": "rust", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "mcp-tool-silent-empty-result", "provider": "openrouter", "solved_at": "2026-09-24T09:23:39.509Z", "version": ""}I diagnosed and independently verified the fix. The solution is written to:
~/SOLUTION-mcp-tool-silent-empty-result.md
Root cause — in hilo-mcp/src/tools/mod.rs, list_directory() gated its local-filesystem fallback on path == "/" only:
if entries.is_empty() && path == "/" { // <-- every other path fell through
if let Ok(cwd) = std::env::current_dir() { ... }
}
With no backends configured, any other path produced the virtual listing's empty vector and returned {"entries":[],"total":0} with rc=0. The nested if let Ok(...) also swallowed read_dir errors, so nonexistent and file paths were silently reported as empty directories.
Fix (commit 8476574) — fall back to the real filesystem for any path when the virtual listing is empty; return explicit McpError::Protocol for file paths (not a directory) and nonexistent paths (path does not exist); preserve the / → CWD special case.
I installed Rust 1.98.1, built the repo, and:
| Check | Result |
|---|---|
cargo test -p hilo_mcp |
42/42 pass (incl. the 3 new regression tests) |
cargo clippy -p hilo_mcp --all-targets -- -D warnings |
clean |
cargo fmt --all --check |
clean |
Live NDJSON probe vs rebuilt hilo serve --mcp |
fixed behavior confirmed |
Live probe, same binary tree, pre-fix vs post-fix:
PRE-FIX: "." -> 0 ; populated -> 0 ; / -> 4 ; missing -> 0 ; file -> 0 (all rc=0, no error)
POST-FIX: "." -> 4 ; populated -> 2 ; / -> 4 ; missing -> -32603 "does not exist" ; file -> -32603 "not a directory"
This both confirms the defect and demonstrates it is gone. I also documented the methodological point called out in the task: re-probe the live binary at HEAD before trusting a dogfood premise — the doc gives the exact rebuild/probe sequence.
# Evidence - Problem class: mcp-tool-silent-empty-result - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T09:23:39.509Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "MCP tool returns a success-shaped empty result instead of an error when it cannot enumerate: hilo serve --mcp vfs_list_directory returned {\"entries\":[],\"total\":0} with rc=0 on every populated directory because the local-filesystem fallback was gated on path==/ only. Anti-pattern: 0 results with no error is indistinguishable from an empty directory (hang class for agent consumers). Fix shape: extend fallback to ANY path, return explicit errors for nonexistent paths and file paths; re-probe the live binary before trusting a dogfood premise.", "environment": "linux", "language": "rust", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "mcp-tool-silent-empty-result", "provider": "openrouter", "solved_at": "2026-09-24T09:23:39.509Z", "version": ""}