VelvetSP/web-retrieval-mcp

GitHub ↗ · How this check works

Per-setup public labels: Linux / Haiku: Works

Static status badge · SVG · read-only JSON. Embed with ![Coldpass status](https://coldpass.dev/badge/VelvetSP--web-retrieval-mcp.svg).

mcp-server

Works

README claims checked out on 1 recorded setup.

Answers apply to each recorded setup, revision and operation. Earlier observations remain in Run details.

Tested on Linux
PlatformHaikuLuna
Linuxyes

Linux / Haiku 5.5: yes · Oct 25, 2026 · c0ee5646d2c2 · claude-code / 2.1.310 (Claude Code)
Recorded operation: web-retrieval-mcp · local stdio MCP start + initialize + tools/list

Does what it says
3 of 3 claims checked out
  • Only http and https initial URLs are accepted; an ftp URL is refused: Real input produced the expected result: refused non-http(s) URL scheme: ftp

    Source: Only `http` and `https` initial URLs are accepted.

    Input: {"tool": "web_fetch", "arguments": {"url": "ftp://example.com/file", "render": "never"}}

    Expected: refused non-http(s) URL scheme

    Command: web-retrieval-mcp tools/call {"tool": "web_fetch", "arguments": {"url": "ftp://example.com/file", "render": "never"}}

    Observed: refused non-http(s) URL scheme: ftp", "type": "text"}], "isError": false, "structuredContent": {"result": "RETRIEVAL_FAILED: ftp://example.com/file — refused non-http(s) URL scheme: ftp"}}

  • Initial hosts must resolve to globally routable addresses; localhost is refused: Real input produced the expected result: refused non-public address for 'localhost'

    Source: Initial hosts must resolve exclusively to globally routable addresses.

    Input: {"tool": "web_fetch", "arguments": {"url": "http://localhost:8/", "render": "never"}}

    Expected: refused non-public address

    Command: web-retrieval-mcp tools/call {"tool": "web_fetch", "arguments": {"url": "http://localhost:8/", "render": "never"}}

    Observed: refused non-public address for 'localhost': ::1", "type": "text"}], "isError": false, "structuredContent": {"result": "RETRIEVAL_FAILED: http://localhost:8/ — refused non-public address for 'localhost': ::1"}}

  • Tools resolve credentials lazily and report what a selected route is missing: Real input produced the expected result: no usable credential for EXA_API_KEY

    Source: Tools resolve credentials lazily

    Input: {"tool": "web_search", "arguments": {"query": "python package metadata"}}

    Expected: search reports the missing EXA_API_KEY credential

    Command: web-retrieval-mcp tools/call {"tool": "web_search", "arguments": {"query": "python package metadata"}}

    Observed: no usable credential for EXA_API_KEY; set EXA_API_KEY, add it to <guest-path>, or configure the optional keyring/OS secret store | firecrawl: no usable credential for FIRECRAWL_API_KEY; set FIRECRAWL_API_KEY, add it to <guest-path>, or configure the optional keyring/OS secret store", "type": "text"}], "isError": false, "structuredContent": {"result": "SEARCH_FAILED: python package metadata — exa: no usable credential for EXA_API_KEY; set EXA_API_KEY, add it to <guest-path>, or configure

MCP
Yes, observed
Local or hosted
local
Local: runs on your machine
Needs keys or browser
None seen
Maintained
Maintained
actively maintained: last commit 54 days ago; 4 commits and 2 merged PRs in 90 days; 0 open issues, 1 open PRs; 2 of 3 recent threads got a maintainer reply, median first reply 2042.2h; latest release 2026-09-01

Last commit Sep 1, 2026

Setup time
3 min 22 s
3 min 22 s to a working result
Issue
none found in recorded check
Examples
1 of 1 ran as expected
  • Offline SSRF guard tests pass against the installed source tree: command completed successfully

    Source: test_ssrf_guard.py

    Input: no arguments

    Expected: ALL TESTS PASS

    Command: <guest-path> test_ssrf_guard.py

    Observed: ['<resolve-error>'] want=['<resolve-error>'] OK flush: forbidden host -> blocked url: got=['http://127.0.0.1/x'] want=['http://127.0.0.1/x'] OK flush: public host not blocked: got=False want=False OK flush: 127.0.0.1 cached False: got=False want=False OK flush: 8.8.8.8 cached True: got=True want=True OK flush: drains pending: got=[] want=[] == network-layer residual disclosure == OK observer docstring still discloses the issued-before-teardown residual: got=True want=True OK web_fetch docstring still discloses the Camoufox SSRF residual: got=True want=True ALL TESTS PASS

  • Release gate ./run-tests.sh runs all suites and package build: Not run: the gate requires pip install -e '.[all,dev]' (the all extra runs camoufox fetch, which downloads a browser that is not permitted here) and provisions a Valkey fixture via tests/fixtures/valkey/install.sh. Only the offline test_ssrf_guard.py was run instead.

    Source: README.md Install from source / For development

    Expected: documented command completes successfully

    Observed: Not run: the gate requires pip install -e '.[all,dev]' (the all extra runs camoufox fetch, which downloads a browser that is not permitted here) and provisions a Valkey fixture via tests/fixtures/valkey/install.sh. Only the offline test_ssrf_guard.py was run instead.

  • Live SSRF redirect check with a browser: Needs a local browser (render extra / Camoufox), which was not installed; the shared CDP endpoint is not wired to this product's render path.

    Source: README.md Install from source

    Expected: documented command completes successfully

    Observed: Needs a local browser (render extra / Camoufox), which was not installed; the shared CDP endpoint is not wired to this product's render path.

Evidence
README behavior checked
Agent interfaces
MCP
MCP

Observed interfaces were exercised by the checker. Documentation alone does not confirm compatibility.

MCP context
6 tools · about 4,734 tokens
6 tools · about 4,734 tokens

Largest returned tools/list page in these observations. Compact UTF-8 definition bytes ÷ 4, rounded up; approximate, excluding client framing and tool results. This is context size, not proof that every tool works.

Measured on linux with Haiku 5.5 · Oct 25, 2026 on c0ee5646d2c2

Tested with
Claude Code / Haiku 5.5
  • Linux: Claude Code / Haiku 5.5 · agent 2.1.310 (Claude Code) · Checked operation passed · Oct 25, 2026 on c0ee5646d2c2

Each entry describes its recorded operation and source revision.

Safety
No unexpected network or file activity found in these checks

No problems traced to this repository code in these checks; a short run cannot prove safety. 2 static matches retained as context.

Reviewed as expected for a test run: attempted writes that failed, so nothing was written; resolver address lookups that send no data.

Full event, path and process lists for each observed setup:

Run details

Haiku 5.5

Native agent · Checked operation passed

Checked operation web-retrieval-mcp · local stdio MCP start + initialize + tools/list

Agent-reported scope: Run the web-retrieval-mcp stdio MCP server and call its tools (web_fetch / web_search)

MCP server returned: web-retrieval · version 0.2.0.

MCP tools returned: web_search, web_fetch, research_papers, research_paper, research_similar, research_github. Tool listing does not establish authenticated tool-call success.

Run details
run
VelvetSP--web-retrieval-mcp-claude-haiku-5-5-1792971571402393094
date
Oct 25, 2026
time to working
201.5s
product hosting / execution
Local stdio MCP server in the guest
recorded operation scope
product operation
model
claude-haiku-5-5
agent / version
claude-code / 2.1.310 (Claude Code)
platform
linux
reported cost
$0.020673
Recorded source
https://github.com/VelvetSP/web-retrieval-mcp
Backend
hyperv
Tool transport
mcp-assigned-vm
Requested source pin (not an observed revision)
{"commit": "c0ee5646d2c23ae27692d52f4c74387180c6cf24", "url": "https://github.com/VelvetSP/web-retrieval-mcp"}
Test version
readme-ca496ea10bb07418d1d8f1c256c2433a55547acc0fb62d8b6d86ae33c976fbfa
revision
c0ee5646d2c23ae27692d52f4c74387180c6cf24
Whose fault: prerequisite · diagnosed

No EXA_API_KEY or FIRECRAWL_API_KEY is configured in the guest, so web_search cannot return results and public-page fetches cannot be completed.

Safety: No problems traced to this repository code in these checks. 2 static matches retained as context.

No problems traced to this repository code in these checks. 2 static matches retained as context.

Reviewed as expected for a test run: attempted writes that failed, so nothing was written; resolver address lookups that send no data.

2 hardcoded secret matches retained as context; full list in the evidence.

OSV scanned supported lockfiles; unpinned dependencies and unsupported formats are outside this check.

A short run cannot establish that code is safe.

Connections show IP addresses; proxies can hide the final hostname.

Tracing covers dispatched commands and their children, not the whole OS; guest root can interfere with tracing.

Full safety evidence →

Answers recorded for this run
  • Only http and https initial URLs are accepted; an ftp URL is refused: Real input produced the expected result: refused non-http(s) URL scheme: ftp

    Source: Only `http` and `https` initial URLs are accepted.

    Input: {"tool": "web_fetch", "arguments": {"url": "ftp://example.com/file", "render": "never"}}

    Expected: refused non-http(s) URL scheme

    Command: web-retrieval-mcp tools/call {"tool": "web_fetch", "arguments": {"url": "ftp://example.com/file", "render": "never"}}

    Observed: refused non-http(s) URL scheme: ftp", "type": "text"}], "isError": false, "structuredContent": {"result": "RETRIEVAL_FAILED: ftp://example.com/file — refused non-http(s) URL scheme: ftp"}}

  • Initial hosts must resolve to globally routable addresses; localhost is refused: Real input produced the expected result: refused non-public address for 'localhost'

    Source: Initial hosts must resolve exclusively to globally routable addresses.

    Input: {"tool": "web_fetch", "arguments": {"url": "http://localhost:8/", "render": "never"}}

    Expected: refused non-public address

    Command: web-retrieval-mcp tools/call {"tool": "web_fetch", "arguments": {"url": "http://localhost:8/", "render": "never"}}

    Observed: refused non-public address for 'localhost': ::1", "type": "text"}], "isError": false, "structuredContent": {"result": "RETRIEVAL_FAILED: http://localhost:8/ — refused non-public address for 'localhost': ::1"}}

  • Tools resolve credentials lazily and report what a selected route is missing: Real input produced the expected result: no usable credential for EXA_API_KEY

    Source: Tools resolve credentials lazily

    Input: {"tool": "web_search", "arguments": {"query": "python package metadata"}}

    Expected: search reports the missing EXA_API_KEY credential

    Command: web-retrieval-mcp tools/call {"tool": "web_search", "arguments": {"query": "python package metadata"}}

    Observed: no usable credential for EXA_API_KEY; set EXA_API_KEY, add it to <guest-path>, or configure the optional keyring/OS secret store | firecrawl: no usable credential for FIRECRAWL_API_KEY; set FIRECRAWL_API_KEY, add it to <guest-path>, or configure the optional keyring/OS secret store", "type": "text"}], "isError": false, "structuredContent": {"result": "SEARCH_FAILED: python package metadata — exa: no usable credential for EXA_API_KEY; set EXA_API_KEY, add it to <guest-path>, or configure

Examples

  • Offline SSRF guard tests pass against the installed source tree: command completed successfully

    Source: test_ssrf_guard.py

    Input: no arguments

    Expected: ALL TESTS PASS

    Command: <guest-path> test_ssrf_guard.py

    Observed: ['<resolve-error>'] want=['<resolve-error>'] OK flush: forbidden host -> blocked url: got=['http://127.0.0.1/x'] want=['http://127.0.0.1/x'] OK flush: public host not blocked: got=False want=False OK flush: 127.0.0.1 cached False: got=False want=False OK flush: 8.8.8.8 cached True: got=True want=True OK flush: drains pending: got=[] want=[] == network-layer residual disclosure == OK observer docstring still discloses the issued-before-teardown residual: got=True want=True OK web_fetch docstring still discloses the Camoufox SSRF residual: got=True want=True ALL TESTS PASS

  • Release gate ./run-tests.sh runs all suites and package build: Not run: the gate requires pip install -e '.[all,dev]' (the all extra runs camoufox fetch, which downloads a browser that is not permitted here) and provisions a Valkey fixture via tests/fixtures/valkey/install.sh. Only the offline test_ssrf_guard.py was run instead.

    Source: README.md Install from source / For development

    Expected: documented command completes successfully

    Observed: Not run: the gate requires pip install -e '.[all,dev]' (the all extra runs camoufox fetch, which downloads a browser that is not permitted here) and provisions a Valkey fixture via tests/fixtures/valkey/install.sh. Only the offline test_ssrf_guard.py was run instead.

  • Live SSRF redirect check with a browser: Needs a local browser (render extra / Camoufox), which was not installed; the shared CDP endpoint is not wired to this product's render path.

    Source: README.md Install from source

    Expected: documented command completes successfully

    Observed: Needs a local browser (render extra / Camoufox), which was not installed; the shared CDP endpoint is not wired to this product's render path.

Local or hosted: local

What the agent did

Run the web-retrieval-mcp stdio MCP server and call its tools (web_fetch / web_search)

cd <guest-path> && pwd && ls -la && id -u && python3 --version && cat README.md 2>/dev/null | head -c 6000
echo "CDP:"; curl -s -m 10 -w "\nHTTP %{http_code}\n" "$CDP_URL/json/version"; echo; cat /etc/os-release | head -3
cd <guest-path> && sed -n '/## Quick start/,/## Six/p' README.md | head -c 7000; echo; echo ---; grep -n "^## \|^### " README.md | head -80
cd <guest-path> && sed -n '215,236p;326,344p;362,379p;402,425p' README.md
cd /tmp && (python3 -m venv --help >/dev/null 2>&1 && echo venv_ok || (sudo -n apt-get -o DPkg::Lock::Timeout=300 install -y python3-venv >/tmp/venv_install.log 2>&1; echo "apt rc=$?"; tail -3 /tmp/venv_install.log)); python3 -m venv <guest-path> && <guest-path> --version && <guest-path> -m pip --version
cd <guest-path> && <guest-path> -m pip install --quiet '<guest-path>,cache,keyring]' > <guest-path> 2>&1; echo rc=$?; tail -5 <guest-path>; ls <guest-path> | grep -i web
cd <guest-path> && sed -n '426,441p' README.md; echo ----; sed -n '286,300p' README.md; echo ----; env | grep -E "EXA|FIRECRAWL|TAVILY|WEB_" | sed 's/=.*/=<set>/' ; echo "env check done"
cd <guest-path> && ls src/web_retrieval_mcp | head -50; grep -rn -i "globally\|not globally\|loopback\|private address" src/web_retrieval_mcp/*.py | head -15; ls <guest-path> /inputs 2>&1 | head
cd <guest-path> && sed -n 285,330p src/web_retrieval_mcp/server.py; grep -n "def web_fetch\<email>\|def web_search" src/web_retrieval_mcp/server.py | head
mkdir -p <guest-path> && cat > <guest-path> <<'EOF'
import asyncio, sys, json
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
    params = StdioServerParameters(command="<guest-path>", args=[], cwd="<guest-path>")
    async with stdio_client(params) as (r, w):
        async with ClientSession(r, w) as s:
            await s.initialize()
            tools = await s.list_tools()
            print("TOOLS:", [t.name for t in tools.tools])
            res = await s.call_tool(sys.argv[1], json.loads(sys.argv[2]))
            for c in res.content:
                print("RESULT:", getattr(c, "text", c))
            print("isError:", res.isError)
asyncio.run(main())
EOF
cd <guest-path> && timeout 120 <guest-path> -I <guest-path> web_fetch '{"url":"http://127.0.0.1:9/","render":"never"}' 2>&1 | tail -30
cd <guest-path> && sed -i 's/res.isError/res.is_error/' <guest-path> && timeout 120 <guest-path> -I <guest-path> web_fetch '{"url":"http://127.0.0.1:9/","render":"never"}' 2>&1 | tail -30
cd <guest-path> && timeout 120 <guest-path> -I <guest-path> web_search '{"query":"python package metadata"}' 2>&1 | tail -8; timeout 120 <guest-path> -I <guest-path> web_fetch '{"url":"http://localhost:8/","render":"never"}' 2>&1 | tail -3
cd <guest-path> && timeout 120 <guest-path> -I <guest-path> web_fetch '{"url":"ftp://example.com/file","render":"never"}' 2>&1 | tail -2; cd <guest-path> && grep -n "Only \`http\` and \`https\` initial URLs are accepted.\|Initial hosts must resolve exclusively to globally routable addresses." README.md; sed -n 1,60p <guest-path> | head -80
cd <guest-path> && grep -rn -i "cron\|schedul\|--once\|--now\|while True" src/ | head; echo ---; cat run-tests.sh | head -60
cd <guest-path> && cat > probe.json <<'EOF'
{"kind":"mcp","argv":["web-retrieval-mcp"],"cwd":"<guest-path>","env":{"PATH":"<guest-path>"},"tool":"web_fetch","arguments":{"url":"http://127.0.0.1:9/","render":"never"},"expected_text":"refused non-public address for '127.0.0.1'"}
EOF
python3 -m json.tool probe.json >/dev/null && echo probe_ok
cd <guest-path> && cat > claims.json <<'EOF'
[
{"kind":"claim","claim":"Only http and https initial URLs are accepted; an ftp URL is refused","source":"README.md","source_file":"README.md","quote":"Only `http` and `https` initial URLs are accepted.","input":"ftp://example.com/file","expected":"refused non-http(s) URL scheme","probe_kind":"mcp","argv":["web-retrieval-mcp"],"env":{"PATH":"<guest-path>"},"tool":"web_fetch","arguments":{"url":"ftp://example.com/file","render":"never"},"expected_text":"refused non-http(s) URL scheme: ftp","cwd":"<guest-path>"},
{"kind":"claim","claim":"Initial hosts must resolve to globally routable addresses; localhost is refused","source":"README.md","source_file":"README.md","quote":"Initial hosts must resolve exclusively to globally routable addresses.","input":"http://localhost:8/","expected":"refused non-public address","probe_kind":"mcp","argv":["web-retrieval-mcp"],"env":{"PATH":"<guest-path>"},"tool":"web_fetch","arguments":{"url":"http://localhost:8/","render":"never"},"expected_text":"refused non-public address for 'localhost'","cwd":"<guest-path>"},
{"kind":"claim","claim":"Tools resolve credentials lazily and report what a selected route is missing","source":"README.md","source_file":"README.md","quote":"Tools resolve credentials lazily and report what a selected route is missing.","input":"python package metadata with no provider keys","expected":"search reports the missing EXA_API_KEY credential","probe_kind":"mcp","argv":["web-retrieval-mcp"],"env":{"PATH":"<guest-path>"},"tool":"web_search","arguments":{"query":"python package metadata"},"expected_text":"no usable credential for EXA_API_KEY","cwd":"<guest-path>"}
]
EOF
python3 -m json.tool claims.json >/dev/null && echo claims_ok
cd <guest-path> && cat > claims.json <<'EOF'
[
{"kind":"claim","claim":"Only http and https initial URLs are accepted; an ftp URL is refused","source":"Only `http` and `https` initial URLs are accepted.","input":"ftp://example.com/file","expected":"refused non-http(s) URL scheme","probe_kind":"mcp","argv":["web-retrieval-mcp"],"env":{"PATH":"<guest-path>"},"tool":"web_fetch","arguments":{"url":"ftp://example.com/file","render":"never"},"expected_text":"refused non-http(s) URL scheme: ftp","cwd":"<guest-path>"},
{"kind":"claim","claim":"Initial hosts must resolve to globally routable addresses; localhost is refused","source":"Initial hosts must resolve exclusively to globally routable addresses.","input":"http://localhost:8/","expected":"refused non-public address","probe_kind":"mcp","argv":["web-retrieval-mcp"],"env":{"PATH":"<guest-path>"},"tool":"web_fetch","arguments":{"url":"http://localhost:8/","render":"never"},"expected_text":"refused non-public address for 'localhost'","cwd":"<guest-path>"},
{"kind":"claim","claim":"Tools resolve credentials lazily and report what a selected route is missing","source":"Tools resolve credentials lazily","input":"python package metadata with no provider keys","expected":"search reports the missing EXA_API_KEY credential","probe_kind":"mcp","argv":["web-retrieval-mcp"],"env":{"PATH":"<guest-path>"},"tool":"web_search","arguments":{"query":"python package metadata"},"expected_text":"no usable credential for EXA_API_KEY","cwd":"<guest-path>"}
]
EOF
python3 -m json.tool claims.json >/dev/null && echo claims_ok; cd <guest-path> && grep -c "Tools resolve credentials lazily" README.md
cd <guest-path> && PYTHONPATH=<guest-path> timeout 280 <guest-path> test_ssrf_guard.py > <guest-path> 2>&1; echo rc=$?; tail -5 <guest-path>
cd <guest-path> && cat > examples.json <<'EOF'
[
{"kind":"example","claim":"Offline SSRF guard tests pass against the installed source tree","source":"test_ssrf_guard.py","input":"no arguments","expected":"ALL  TESTS PASS","argv":["<guest-path>","test_ssrf_guard.py"],"cwd":"<guest-path>","env":{"PYTHONPATH":"<guest-path>"},"expected_text":"ALL  TESTS PASS"},
{"kind":"example","claim":"Release gate ./run-tests.sh runs all suites and package build","source":"README.md Install from source / For development","reason":"Not run: the gate requires pip install -e '.[all,dev]' (the all extra runs camoufox fetch, which downloads a browser that is not permitted here) and provisions a Valkey fixture via tests/fixtures/valkey/install.sh. Only the offline test_ssrf_guard.py was run instead."},
{"kind":"example","claim":"Live SSRF redirect check with a browser","source":"README.md Install from source","reason":"Needs a local browser (render extra / Camoufox), which was not installed; the shared CDP endpoint is not wired to this product's render path.","deferred":true}
]
EOF
cat > cycles.json <<'EOF'
{"detected":false,"reason":"Entry points inspected (src/web_retrieval_mcp/server.py, __main__.py, install.py). The server runs as a request/response MCP stdio or Streamable HTTP service with no cron, scheduler, --once/--now flag, loop, or webhook; the only while True loop is a transport/serve loop. No product cycle to trigger.","checks":[]}
EOF
python3 -m json.tool examples.json >/dev/null && python3 -m json.tool cycles.json >/dev/null && echo ok
cd <guest-path> && cat > report.json <<'EOF'
{"outcome":"needs_credentials","fault_owner":"prerequisite","operation":"Run the web-retrieval-mcp stdio MCP server and call its tools (web_fetch / web_search)","operation_scope":"Python 3.12 venv with the base package plus tavily, cache and keyring extras; no provider API keys (EXA_API_KEY, FIRECRAWL_API_KEY, TAVILY_API_KEY) and no local Camoufox browser. Verified offline: server starts, lists six tools, web_fetch refuses non-http(s) schemes and loopback/localhost hosts (SSRF guard), web_search reports the missing EXA_API_KEY and FIRECRAWL_API_KEY credentials. Not verified: real web search results or fetching a public page, which need provider keys.","steps":["python3 -m venv <guest-path>","<guest-path> -m pip install '<guest-path>,cache,keyring]'","web-retrieval-mcp (stdio MCP server, driven with an MCP client)","web_fetch url=http://127.0.0.1:9/ -> RETRIEVAL_FAILED refused non-public address","web_search query=python package metadata -> SEARCH_FAILED no usable credential for EXA_API_KEY","PYTHONPATH=src python test_ssrf_guard.py -> ALL  TESTS PASS"],"deviations":["Did not run pip install '.[all]' or 'python -m camoufox fetch' because the all extra's camoufox fetch downloads a browser, which is not permitted; installed the non-render extras instead.","Did not run ./run-tests.sh (release gate): needs '.[all,dev]' and a Valkey fixture install; ran only test_ssrf_guard.py.","Workspace is not a git checkout, so the pinned commit c0ee5646d2c23ae27692d52f4c74387180c6cf24 could not be verified; the provided snapshot was used as is.","Did not set any provider key or obtain credentials."],"stuck_step":null,"main_blocker":"No EXA_API_KEY or FIRECRAWL_API_KEY is configured in the guest, so web_search cannot return results and public-page fetches cannot be completed.","plain_failure":null,"suggested_fix":"Provide EXA_API_KEY and FIRECRAWL_API_KEY (optional TAVILY_API_KEY) through the environment or keys.env, then re-run web_search and web_fetch on a public URL. Unrelated to the product code: the server itself starts and its guards work."}
EOF
python3 -m json.tool report.json >/dev/null && echo report_ok
cd <guest-path> && <env-var>=<guest-path> timeout 900 python3 -I <guest-path> --validate --replay > <guest-path> 2>&1; echo rc=$?; tail -c 4000 <guest-path>
cd <guest-path> && grep -o '"problems": \[[^]]*\]' validate.log | head -c 1500; echo; grep -o '"ok": [a-z]*\|"status": "[a-z_]*"\|"verdict": "[a-z_]*"' validate.log | sort | uniq -c; head -c 600 validate.log
pgrep -af "web-retrieval-mcp|client.py" || echo "no leftover processes"; ls <guest-path>