{"id":51224776,"url":"https://github.com/saagpatel/shadow-mcp","last_synced_at":"2026-06-28T10:03:09.954Z","repository":{"id":366282324,"uuid":"1275721028","full_name":"saagpatel/shadow-mcp","owner":"saagpatel","description":"Discover and risk-grade the MCP servers present on this machine (local-first OWASP MCP09 shadow-server inventory)","archived":false,"fork":false,"pushed_at":"2026-06-21T04:26:11.000Z","size":221,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-21T06:12:31.234Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/saagpatel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-21T03:56:11.000Z","updated_at":"2026-06-21T04:26:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/saagpatel/shadow-mcp","commit_stats":null,"previous_names":["saagpatel/shadow-mcp"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/saagpatel/shadow-mcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fshadow-mcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fshadow-mcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fshadow-mcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fshadow-mcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saagpatel","download_url":"https://codeload.github.com/saagpatel/shadow-mcp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saagpatel%2Fshadow-mcp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34884278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-28T10:03:08.992Z","updated_at":"2026-06-28T10:03:09.948Z","avatar_url":"https://github.com/saagpatel.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# shadow-mcp\n\nDiscover and risk-grade the MCP servers actually present on **this** machine.\n\nMost MCP security tooling assumes you already have a list of servers to audit.\nOn a real developer machine you don't: servers are scattered across Claude Code,\nCodex, Claude Desktop, project-local `.mcp.json` files, DXT extensions, and live\nprocesses that bind no port. shadow-mcp finds them first, then grades them.\n\nThis is the local-first answer to **OWASP MCP09:2025 — Shadow MCP Servers**.\n\n## What it does\n\n```\ndiscover  -\u003e  inventory  -\u003e  risk-grade  -\u003e  report\n```\n\n1. **Discover** (read-only) every place an MCP server is declared or running:\n   Claude Code (`~/.claude.json`, user + project scope), `claude mcp list`\n   (catches remote + plugin servers no file contains), Codex\n   (`~/.codex/config.toml` + profiles), project `.mcp.json`, Claude Desktop\n   config + DXT extension manifests, and the live process table.\n2. **Inventory**: merge sightings into one entry per logical server, even when a\n   server appears under different names across hosts (`personal-ops` vs\n   `personal_ops`), tracking every provenance.\n3. **Risk-grade** by **delegating** to the existing engines rather than\n   reimplementing them:\n   - [MCPAudit](../MCPAudit) for a 0-10 capability composite + injection findings\n   - [mcp-trust](../mcp-trust) for an authoritative A-F danger grade (when known)\n   - a thin local layer for the config-shaped OWASP dimensions the engines under-cover\n     (secrets/MCP01, supply-chain provenance/MCP04, transport exposure/MCP07).\n4. **Report**: a ranked terminal table, a machine-readable JSON inventory, or\n   markdown — plus a **Shadow \u0026 attention** section for the deltas that matter\n   (running-but-unconfigured, broad blast radius, capable-but-ungraded).\n\nThe risk model and its OWASP mapping live in [docs/risk-model.md](docs/risk-model.md).\n\n## Install\n\n```bash\nuv sync                 # installs deps incl. MCPAudit as a local editable engine\n```\n\nshadow-mcp grades against your local checkouts of MCPAudit (`../MCPAudit`) and\nmcp-trust (`../mcp-trust/registry.db`). Override with `SHADOW_MCP_MCPTRUST_DB`\nor `--registry-db`.\n\n## Use\n\n```bash\nuv run shadow-mcp scan                      # full pipeline, terminal report\nuv run shadow-mcp scan --json out.json      # machine-readable inventory\nuv run shadow-mcp scan --format markdown    # markdown report\nuv run shadow-mcp discover                  # inventory only, no grading\nuv run shadow-mcp sources                   # per-collector counts\nuv run shadow-mcp grade-missing             # A-F for servers the registry hasn't scanned\nuv run shadow-mcp deep-scan cost-tracker    # connect to a server, grade its real tools\n```\n\nUseful flags: `--no-processes` (skip the live process scan), `--no-cli` (skip\n`claude mcp list`), `--no-mcpaudit` (inventory + mcp-trust only), `--home PATH`\n(point discovery at a fixture tree).\n\n### Static vs connected grading\n\nBy default grading is **static** (config-only): no server is spawned, so grades\nreflect what's visible in the config. That's safe but coarse — a server's real\ncapability only shows once you connect and list its tools.\n\n`shadow-mcp scan --connect` (or `deep-scan [names...]`) **spawns** each stdio\nserver and enumerates its real tools, delegating to MCPAudit's connected engine\nfor a capability grade that actually differentiates (a filesystem server jumps\nfrom a static `A` to a connected `D`). This is **opt-in** because connecting\nexecutes the server; remote endpoints are never spawned (that's the network-scan\ntier), and a server that needs real secrets to start falls back to its static\ngrade.\n\n## Development\n\n```bash\nuv sync                       # dev tools + grading engines (the default groups)\nuv run pytest                 # full suite (61 + engine-backed tests)\nuv run ruff check .           # lint\n```\n\nThe grading engines are an optional `engines` dependency-group, resolved to your\nlocal checkouts of `../MCPAudit` and `../mcp-trust` via `[tool.uv.sources]`. The\ntool degrades to discovery-only without them (engine-backed tests skip cleanly),\nso CI installs without them:\n\n```bash\nuv sync --no-group engines    # discovery + local OWASP layer only (what CI runs)\n```\n\n## Safety\n\n- **Read-only discovery.** Collectors parse configs and list processes; nothing\n  they find is ever mutated. (`--connect`/`deep-scan` is the one path that\n  *executes* servers, and only when you explicitly ask.)\n- **Secrets stay out.** We record env variable *names* (to flag secret-bearing\n  servers per MCP01) but never their values. A captured inventory still contains\n  real local paths and hostnames, so treat `*.inventory.json` as private (it is\n  git-ignored by default).\n\n## Scope\n\nThis is the **local-first** tool: it inventories one machine from its configs\nand processes. A later network-scan expansion (probing hosts/ports for remote\nMCP endpoints, org-wide fleet inventory, typosquat-distance provenance checks)\nis deliberately out of scope here — see the bottom of `docs/risk-model.md` and\nthe project notes for what that would add.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaagpatel%2Fshadow-mcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaagpatel%2Fshadow-mcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaagpatel%2Fshadow-mcp/lists"}