{"slug": "show-hn-i-ran-70-mcp-servers-in-a-sandbox-and-logged-what-they-do", "title": "Show HN: I ran 70 MCP servers in a sandbox and logged what they do", "summary": "A developer ran 70 MCP servers in a sandboxed environment using Docker and strace to log runtime behavior, finding that 67 were clean at startup, 3 made outbound HTTPS connections consistent with their functions, and 1 read /etc/passwd (benign). The project highlights that static analysis misses runtime risks like unexpected network calls or credential theft.", "body_md": "A harness that runs MCP servers in an instrumented sandbox and records what they\n**actually do at runtime** — every network connection they open and every file\nthey touch — so you can see a server's real behavior instead of trusting its\ndescription.\n\nThe official MCP registry verifies *who published* a server (namespace\nauthentication tied to a GitHub account or domain). It does not verify *what a\nserver does once you run it*. The existing trust tooling in the ecosystem is\nalmost entirely **static analysis** — scanning source code and metadata. Static\nscans miss an entire class of risk that only shows up at runtime:\n\n- a server that connects somewhere unrelated to its stated purpose\n- a server that reads credentials or files outside its lane\n- a \"rug pull\": a server whose behavior changes in a later version,\n*after*you've already reviewed and trusted it\n\nThis project captures runtime behavior directly, which is the part almost nobody is watching yet.\n\nEach server is launched inside a disposable Linux container (Docker) under\n`strace`\n\n, with syscalls for file access (`openat`\n\n) and network connections\n(`connect`\n\n) logged. A fake credential is seeded into the environment as a canary,\nso any attempt to read or transmit it is visible in the trace. The container is\nthrowaway; nothing the server does touches the host.\n\n```\ndocker + strace  ──►  per-server syscall trace  ──►  behavior manifest\n```\n\nEverything runs locally. No server code executes on the host machine.\n\n**70 MCP servers** audited: the 20 most-downloaded on npm, plus 50 from the long\ntail. Full per-server summaries are in [ manifests/](/BhaveshThapar/mcp-audit/blob/main/manifests); the headline\nfindings are in\n\n[.](/BhaveshThapar/mcp-audit/blob/main/RESULTS.md)\n\n`RESULTS.md`\n\n**67 clean** at startup — no sensitive-file reads, no unexpected network.**3 made outbound HTTPS connections at startup**(`okx-trade-mcp`\n\n,`razorpay/blade-mcp`\n\n,`notebooklm-mcp-server`\n\n) to external hosts consistent with each server's function (an exchange integration, a GitHub fetch, cloud/CDN endpoints). None read sensitive files or touched the seeded canary.**1**(`bullmq-mcp`\n\n) reads`/etc/passwd`\n\nat startup via a standard glibc user lookup — flagged by the harness, then**verified benign**: the contents never leave the process.** No server transmitted sensitive data.**\n\nThe value shown here isn't \"everything was clean\" — it's that the harness surfaced the three servers that phone home and the one that touches a sensitive file, and each could be run down to a verdict from the trace. That is behavior static scanners don't see.\n\nScope note:this capturesstartup and idlebehavior only — the server booting and waiting. It does not yet exercise individual tool calls, which is where more behavior lives. See roadmap.\n\n**Tool-call tracing**— drive each server through its advertised tools and record behavior per call, not just at boot. (The 3 outbound servers above are the first place this matters.)**Version diffing**— re-run servers across releases and diff behavior to detect rug pulls — the highest-value, least-covered case.** Behavior manifests as a feed**— a stable, diffable per-server output that clients or registries could consume before install.** Wider coverage**— deeper into the long tail of single-maintainer servers.\n\n```\n# needs Docker\ngit clone <this repo> && cd mcp-audit\ndocker run --rm -v \"$PWD\":/audit -w /audit node:22 bash -c \\\n  'apt-get update -qq && apt-get install -y -qq strace && bash run.sh'   # batch 1\n# (run2.sh for the long-tail batch)\n\n# distil the traces into readable manifests\npython3 summarize.py\n```\n\nReading raw traces: a read inside the server's own dependencies is normal (it's\nloading its own code); a failed open (`-1 ENOENT`\n\n) is normal (a config file that\nisn't there); DNS (`port 53`\n\n) and package-download connections (Cloudflare\n`104.16.x.x`\n\n) are normal. You're looking for a server that reads a *sensitive*\nfile, or connects to a host with *nothing to do with its job*, once it's running.\n\nAnything that looks like a genuine issue is reported privately to the server's maintainer first, with time to fix, before it appears in any public writeup. Observations here are characterized conservatively — a flagged behavior is not an accusation until it's confirmed with the maintainer.\n\n*Built by Bhavesh Thapar*", "url": "https://wpnews.pro/news/show-hn-i-ran-70-mcp-servers-in-a-sandbox-and-logged-what-they-do", "canonical_source": "https://github.com/BhaveshThapar/mcp-audit", "published_at": "2026-07-08 22:44:41+00:00", "updated_at": "2026-07-08 23:12:36.416058+00:00", "lang": "en", "topics": ["ai-tools", "ai-safety", "developer-tools"], "entities": ["MCP", "Docker", "strace", "npm", "GitHub", "Cloudflare"], "alternates": {"html": "https://wpnews.pro/news/show-hn-i-ran-70-mcp-servers-in-a-sandbox-and-logged-what-they-do", "markdown": "https://wpnews.pro/news/show-hn-i-ran-70-mcp-servers-in-a-sandbox-and-logged-what-they-do.md", "text": "https://wpnews.pro/news/show-hn-i-ran-70-mcp-servers-in-a-sandbox-and-logged-what-they-do.txt", "jsonld": "https://wpnews.pro/news/show-hn-i-ran-70-mcp-servers-in-a-sandbox-and-logged-what-they-do.jsonld"}}