{
  "$schema": "https://readbeforerun.ai/schema/entry.schema.json",
  "entry": {
    "id": "cisco-mcp-scanner",
    "name": "Cisco MCP Scanner Plugin",
    "summary": "Claude Code plugin that scans MCP servers and tools for prompt injection, tool poisoning and other risks via the Cisco MCP scanner.",
    "draft": "review",
    "type": "plugin",
    "mission": [
      "agent-security"
    ],
    "task": [
      "agent-supply-chain-scan"
    ],
    "domain": [
      "ai-security"
    ],
    "runtime": [
      "claude-code"
    ],
    "tier": "tried",
    "last_checked": "2026-11-07",
    "next_check": "2027-05-06",
    "freshness": "fresh",
    "superseded": false,
    "in_my_kit": null,
    "advice": null,
    "code_shows": "reads credential stores · talks to any host · runs shell commands or eval · changes config or installs globally · Approval: unknown",
    "source": {
      "repo": "https://github.com/cisco-ai-defense/mcp-scanner",
      "subpath": "claude-code-plugin",
      "commit": "5d147dc7e3635606e41104112fa297b45444b05c",
      "version": "v4.8.6",
      "license": "Apache-2.0"
    },
    "install": {
      "skills_cli": null,
      "claude_code": null,
      "upstream": "pip install cisco-ai-mcp-scanner\nclaude --plugin-dir /path/to/claude-code-plugin",
      "bundled": false
    },
    "scan": {
      "status": "static scan pending (not run)"
    },
    "reach": {
      "method": "reach-v0.1",
      "level": 3,
      "level_source": "static",
      "level_label": "privileged",
      "unknown_axes": 1,
      "verdict": "beyond-declared",
      "badge": "reach 3 · beyond declared (1)",
      "long": "reach 3 from code (privileged); lab: beyond declared on 1 of 6 rights: network",
      "declared": {
        "fs": 1,
        "net": 1,
        "exec": 2,
        "cred": 3
      },
      "static": {
        "level": 3,
        "fs": 1,
        "net": 2,
        "exec": 2,
        "cred": 3,
        "persist": 2,
        "hitl": null,
        "unknown": [
          "hitl"
        ]
      },
      "observed": {
        "level": 2,
        "fs": null,
        "net": 2,
        "exec": 0,
        "cred": 0,
        "persist": 0,
        "hitl": null,
        "unknown": [
          "fs",
          "hitl"
        ],
        "run": "2026-11-07"
      },
      "surplus": {
        "axes": {
          "fs": null,
          "net": 1,
          "exec": 0,
          "cred": 0,
          "persist": 0,
          "hitl": null
        },
        "count": 1,
        "beyond_static": [],
        "headline": "Beyond declared on 1 of 6 rights: network (observed 2, declared 1), with blocked attempts to reach GitHub at start-up even in YARA-only mode."
      },
      "fit": "partial",
      "exports": {
        "aivss": {
          "fs": 0.5,
          "net": 0.5,
          "exec": 0.5,
          "cred": 1,
          "persist": 0.5,
          "hitl": null
        },
        "usf_risk_tier": "L3"
      }
    },
    "profile": {
      "read_at": "5d147dc7e3635606e41104112fa297b45444b05c",
      "read_on": "2026-10-25",
      "overview": "A Claude Code plugin whose slash commands and skill have Claude run Cisco's mcp-scanner CLI to check MCP servers, tools, prompts and resources for prompt injection, tool poisoning and other threats with YARA rules, an LLM and the Cisco AI Defense API.",
      "purpose": [
        "Scan MCP servers and tools for security threats and risks from inside Claude Code.",
        "Detect prompt injection, tool poisoning and other threats in MCP tools, prompts and resources.",
        "Detect malicious MCP tools by combining the Cisco AI Defense inspect API, YARA rules and LLM-based analysis."
      ],
      "use_cases": [
        {
          "text": "Scan new MCP servers before adding them to a client configuration",
          "task": "agent-supply-chain-scan"
        },
        {
          "text": "Rescan existing MCP client configurations periodically for new threats",
          "task": "agent-supply-chain-scan"
        },
        {
          "text": "Analyze MCP server source code for mismatches between documentation and implementation",
          "task": "code-review-sast"
        },
        {
          "text": "Scan pre-generated tools, prompts and resources JSON files in CI/CD without a live server",
          "task": "agent-supply-chain-scan"
        }
      ],
      "maturity": null,
      "capabilities": [
        {
          "name": "quick-scan, scan-remote",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default"
        },
        {
          "name": "deep-scan",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default"
        },
        {
          "name": "scan-known-configs",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default"
        },
        {
          "name": "scan-config",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default"
        },
        {
          "name": "scan-prompts, scan-resources",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default"
        },
        {
          "name": "scan-behavioral",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default"
        },
        {
          "name": "mcp-security-scan",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default"
        },
        {
          "name": "pypi-scan, npm-scan",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default"
        },
        {
          "name": "vulnerable-package",
          "effects": [
            "reads",
            "changes"
          ],
          "enabled": "default"
        }
      ],
      "effects": [
        "reads",
        "changes",
        "runs-commands",
        "sends-data"
      ],
      "runs": "in-client",
      "listens": [
        {
          "mode": "optional",
          "bind": "configurable",
          "sign_in": "none"
        }
      ],
      "sends_to": [
        {
          "to": "Remote MCP server (named URL)",
          "kind": "target",
          "when": "default"
        },
        {
          "to": "Remote servers listed in client configs",
          "kind": "target",
          "when": "optional"
        },
        {
          "to": "LLM provider via LiteLLM",
          "kind": "model-provider",
          "when": "optional"
        },
        {
          "to": "Cisco AI Defense inspect API",
          "kind": "vendor-api",
          "when": "optional"
        },
        {
          "to": "Package registries of launched servers",
          "kind": "download",
          "when": "optional"
        },
        {
          "to": "mcp.deepwiki.com",
          "kind": "target",
          "when": "optional"
        },
        {
          "to": "VirusTotal",
          "kind": "vendor-api",
          "when": "optional"
        }
      ],
      "model": "client",
      "needs": [
        {
          "kind": "runtime",
          "text": "Claude Code, which loads the plugin from its folder with --plugin-dir."
        },
        {
          "kind": "tools",
          "text": "The cisco-ai-mcp-scanner Python package, installed separately from the plugin."
        },
        {
          "kind": "runtime",
          "text": "Python 3.11.4 or newer."
        },
        {
          "kind": "tools",
          "text": "uv, because the skill tells Claude to start the scanner with uv run mcp-scanner."
        },
        {
          "kind": "model-key",
          "text": "MCP_SCANNER_LLM_API_KEY for the llm and behavioral analyzers used by deep-scan, scan-prompts, scan-resources and scan-behavioral."
        },
        {
          "kind": "credentials",
          "text": "MCP_SCANNER_API_KEY for the Cisco AI Defense api analyzer."
        },
        {
          "kind": "account",
          "text": "A Cisco AI Defense subscription for the api analyzer."
        }
      ]
    },
    "urls": {
      "page": "/catalog/cisco-mcp-scanner/",
      "json": "/entries/cisco-mcp-scanner.json",
      "md": "/catalog/cisco-mcp-scanner.md",
      "badge": "/badge/cisco-mcp-scanner.svg"
    }
  },
  "data": {
    "schema_version": "0.5",
    "slug": "cisco-mcp-scanner",
    "name": "Cisco MCP Scanner Plugin",
    "summary": "Claude Code plugin that scans MCP servers and tools for prompt injection, tool poisoning and other risks via the Cisco MCP scanner.",
    "draft": "review",
    "in_my_kit": null,
    "type": "plugin",
    "mission": [
      "agent-security"
    ],
    "task": [
      "agent-supply-chain-scan"
    ],
    "domain": [
      "ai-security"
    ],
    "phase": [
      "ais:release"
    ],
    "role": [
      "ai-platform"
    ],
    "runtime": [
      "claude-code"
    ],
    "auth": null,
    "autonomy": null,
    "addresses": [],
    "frameworks": {
      "owasp_ast": [],
      "owasp_asi": [],
      "attack_tactic": [],
      "atlas": []
    },
    "source": {
      "repo": "https://github.com/cisco-ai-defense/mcp-scanner",
      "subpath": "claude-code-plugin",
      "commit": "5d147dc7e3635606e41104112fa297b45444b05c",
      "version": "v4.8.6",
      "license": "Apache-2.0",
      "manifest": "plugin.json",
      "also_listed": {}
    },
    "upstream": {
      "checked_at": "2026-10-19",
      "updated_at": "2026-10-16",
      "current_release": "v4.8.6",
      "archived": false,
      "stars": 1094,
      "commit_history": {},
      "releases_since_pin": 0
    },
    "install": {
      "upstream": "pip install cisco-ai-mcp-scanner\nclaude --plugin-dir /path/to/claude-code-plugin",
      "upstream_evidence": "claude-code-plugin/README.md#L18-32",
      "bundled": false
    },
    "scan": [
      {
        "tool": "cisco-skill-scanner",
        "version": "not-run",
        "llm_judge": false,
        "date": "2026-10-19",
        "result": "pending"
      }
    ],
    "privileges": {
      "method": "reach-v0.1",
      "declared": {
        "axes": {
          "fs": 1,
          "net": 1,
          "exec": 2,
          "cred": 3
        },
        "evidence": {
          "fs": "claude-code-plugin/skills/security-scan/SKILL.md#L62-66 author lists the home-directory MCP client config files the known-config scan reads",
          "net": "claude-code-plugin/skills/security-scan/SKILL.md#L197-199 author names the Cisco AI Defense API endpoint for the api analyzer",
          "exec": "claude-code-plugin/skills/security-scan/SKILL.md#L74-89 author documents that the scanner launches a user-chosen stdio server command",
          "cred": "claude-code-plugin/skills/security-scan/SKILL.md#L213-216 author documents authenticating the LLM analyzer with a named AWS profile, which reads the local AWS credential store"
        }
      },
      "static": {
        "level": 3,
        "axes": {
          "fs": 1,
          "net": 2,
          "exec": 2,
          "cred": 3,
          "persist": 2,
          "hitl": null
        },
        "evidence": {
          "fs": "claude-code-plugin/skills/security-scan/SKILL.md#L53-66 the known-config scan reads MCP client configs in the home directory, outside the working directory",
          "net": "claude-code-plugin/skills/security-scan/SKILL.md#L58-59 the known-configs scan connects to every remote MCP server listed in local client configs, hosts the user does not name",
          "exec": "claude-code-plugin/skills/security-scan/SKILL.md#L87-89 the skill tells the agent to launch any program as a stdio MCP server",
          "cred": "claude-code-plugin/commands/scan-known-configs.md#L11-18 the command reads the MCP client configuration files of Windsurf, Cursor and Claude Desktop, which can hold server API keys and tokens",
          "persist": "claude-code-plugin/README.md#L16-18 the documented prerequisite installs the scanner package into the active Python environment",
          "hitl": "no evidence: the skill and command frontmatter set no allowed-tools, permission mode or auto-approve, so the approval posture cannot be read statically"
        },
        "unknown": [
          "hitl"
        ]
      },
      "observed": {
        "level": 2,
        "axes": {
          "fs": null,
          "net": 2,
          "exec": 0,
          "cred": 0,
          "persist": 0,
          "hitl": null
        },
        "unknown": [
          "fs",
          "hitl"
        ],
        "run": "runs/2026-11-07",
        "valid_for": {
          "commit": "5d147dc7e3635606e41104112fa297b45444b05c",
          "version": "v4.8.6"
        }
      },
      "surplus": {
        "axes": {
          "fs": null,
          "net": 1,
          "exec": 0,
          "cred": 0,
          "persist": 0,
          "hitl": null
        },
        "count": 1,
        "beyond_static": [],
        "headline": "Beyond declared on 1 of 6 rights: network (observed 2, declared 1), with blocked attempts to reach GitHub at start-up even in YARA-only mode."
      },
      "fit": "partial",
      "min_config": {
        "status": "derived",
        "claude_code": "run the yara analyzer only (no API keys, no cloud upload); avoid --scan-known-configs and the stdio launch mode unless you trust every server in your local configs; remove scanner and LLM API keys from the environment before a known-configs scan, because every started server inherits it",
        "note": "derived from the code; of it, the lab ran the YARA-only CLI without keys (runs/2026-11-07), not the plugin path"
      },
      "override": null,
      "reviews": []
    },
    "status": {
      "tier": "tried",
      "last_checked": "2026-11-07",
      "next_check": "2027-05-06",
      "freshness": "fresh",
      "superseded": false,
      "superseded_by": null,
      "added": "2026-10-19",
      "added_by": "scout"
    },
    "evidence": {
      "label": "observation",
      "runs": [
        "runs/2026-11-07"
      ],
      "badge": "/badge/cisco-mcp-scanner.svg"
    },
    "changes": [
      {
        "date": "2026-11-07",
        "value": "tier",
        "from": "listed",
        "to": "tried",
        "reason": "First lab run published (runs/2026-11-07): the YARA-only CLI from the pinned commit, observed reach 2, beyond declared on network."
      }
    ]
  },
  "profile": {
    "schema_version": "0.1",
    "entry": "cisco-mcp-scanner",
    "draft": "review",
    "commit": "5d147dc7e3635606e41104112fa297b45444b05c",
    "read_on": "2026-10-25",
    "check": {
      "date": "2026-10-25",
      "facts": 60,
      "confirmed": 60,
      "verified": 51
    },
    "overview": {
      "text": "A Claude Code plugin whose slash commands and skill have Claude run Cisco's mcp-scanner CLI to check MCP servers, tools, prompts and resources for prompt injection, tool poisoning and other threats with YARA rules, an LLM and the Cisco AI Defense API.",
      "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L3 skill frontmatter description"
    },
    "purpose": [
      {
        "text": "Scan MCP servers and tools for security threats and risks from inside Claude Code.",
        "ev": "claude-code-plugin/README.md#L3 plugin description"
      },
      {
        "text": "Detect prompt injection, tool poisoning and other threats in MCP tools, prompts and resources.",
        "ev": "claude-code-plugin/.claude-plugin/plugin.json#L3 manifest description"
      },
      {
        "text": "Detect malicious MCP tools by combining the Cisco AI Defense inspect API, YARA rules and LLM-based analysis.",
        "ev": "README.md#L10 main project description"
      }
    ],
    "use_cases": [
      {
        "text": "Scan new MCP servers before adding them to a client configuration",
        "task": "agent-supply-chain-scan",
        "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L239 best practice in skill"
      },
      {
        "text": "Rescan existing MCP client configurations periodically for new threats",
        "task": "agent-supply-chain-scan",
        "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L240 best practice in skill"
      },
      {
        "text": "Analyze MCP server source code for mismatches between documentation and implementation",
        "task": "code-review-sast",
        "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L130 behavioral code analysis mode"
      },
      {
        "text": "Scan pre-generated tools, prompts and resources JSON files in CI/CD without a live server",
        "task": "agent-supply-chain-scan",
        "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L152 static/offline mode"
      }
    ],
    "not_for": [],
    "capabilities": {
      "coverage": "partial",
      "missing": "CLI features the plugin does not wrap: virustotal, pypi-scan, npm-scan (Docker), vulnerable-package --fix, readiness, prompt_defense, meta, REST API server",
      "groups": [
        {
          "name": "quick-scan, scan-remote",
          "does": "Run mcp-scanner with the YARA analyzer against a remote MCP server URL and report a summary (quick-scan) or details (scan-remote).",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default",
          "ev": "claude-code-plugin/commands/quick-scan.md#L9-13 YARA-only remote scan command"
        },
        {
          "name": "deep-scan",
          "does": "Run the YARA, LLM and Cisco AI Defense API analyzers against a remote MCP server and report findings by severity.",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default",
          "ev": "claude-code-plugin/commands/deep-scan.md#L11 command with all three analyzers"
        },
        {
          "name": "scan-known-configs",
          "does": "Scan the servers defined in the well-known MCP config files of Windsurf, Cursor, Claude Desktop and VS Code with the YARA analyzer.",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default",
          "ev": "claude-code-plugin/commands/scan-known-configs.md#L11-18 command and config list"
        },
        {
          "name": "scan-config",
          "does": "Scan every MCP server defined in one config file given by path with the YARA analyzer.",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default",
          "ev": "claude-code-plugin/commands/scan-config.md#L11 config file scan command"
        },
        {
          "name": "scan-prompts, scan-resources",
          "does": "Fetch the prompts or resources of a remote MCP server and check them with the LLM analyzer.",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default",
          "ev": "claude-code-plugin/commands/scan-prompts.md#L11 prompts scan with llm analyzer"
        },
        {
          "name": "scan-behavioral",
          "does": "Analyze MCP server source code in a file or directory for docstring-versus-implementation mismatches with the LLM-backed behavioral analyzer.",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default",
          "ev": "claude-code-plugin/commands/scan-behavioral.md#L14-19 behavioral command and LLM requirement"
        },
        {
          "name": "mcp-security-scan",
          "does": "Skill Claude uses on MCP security requests; adds launching and scanning stdio MCP servers, server-instruction scans and combined analyzers to the commands.",
          "effects": [
            "reads",
            "runs-commands",
            "sends-data"
          ],
          "enabled": "default",
          "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L74-75 stdio scanning mode in skill"
        },
        {
          "name": "pypi-scan, npm-scan",
          "does": "Download a PyPI or npm package and scan it with behavioral analysis, inside a Docker sandbox by default or in-process with --no-docker.",
          "effects": [
            "reads",
            "runs-commands"
          ],
          "enabled": "default",
          "ev": "README.md#L289-290 package scanning subcommands"
        },
        {
          "name": "vulnerable-package",
          "does": "Check a requirements file or project for known vulnerable Python dependencies using PyPI or OSV data; --fix changes the vulnerable dependencies automatically.",
          "effects": [
            "reads",
            "changes"
          ],
          "enabled": "default",
          "ev": "README.md#L531-540 vulnerability service option and fix flag"
        }
      ]
    },
    "architecture": {
      "summary": {
        "text": "Markdown slash commands and a skill in Claude Code have the model run the local mcp-scanner Python CLI; the CLI reaches remote MCP servers over SSE or streamable HTTP, starts stdio servers as child processes, and can send content to an LLM provider via LiteLLM or to the Cisco AI Defense API.",
        "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L35 remote transport types"
      },
      "runs": {
        "where": "in-client",
        "text": "Runs as Claude Code commands and a skill; the model executes the separately installed mcp-scanner CLI on the local machine.",
        "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L8 skill tells the model to use the CLI"
      },
      "modes": [
        {
          "where": "local-program",
          "mode": "stand-alone CLI",
          "switch": "mcp-scanner",
          "text": "The mcp-scanner CLI also runs on its own from a terminal, outside Claude Code.",
          "ev": "README.md#L23 multiple modes feature"
        },
        {
          "where": "local-service",
          "mode": "REST API server",
          "switch": "mcp-scanner-api",
          "text": "mcp-scanner-api starts a FastAPI REST server that exposes the scan functions as HTTP endpoints.",
          "ev": "README.md#L713 API server start command"
        }
      ],
      "parts": [
        {
          "name": "Plugin commands and skill",
          "zone": "client",
          "does": "Slash commands under the mcp-scanner: namespace and the mcp-security-scan skill that tell Claude which mcp-scanner command to run.",
          "ev": "claude-code-plugin/README.md#L39 command namespace"
        },
        {
          "name": "mcp-scanner CLI",
          "zone": "machine",
          "does": "Python package cisco-ai-mcp-scanner; the mcp-scanner entry point runs the analyzers and the MCP client connections.",
          "ev": "pyproject.toml#L120 console script entry point"
        },
        {
          "name": "stdio MCP servers",
          "zone": "machine",
          "does": "Local MCP server programs the scanner starts as child processes over stdio to list their tools.",
          "ev": "mcpscanner/core/scanner.py#L2366-2374 stdio client started with server parameters"
        },
        {
          "name": "Remote MCP servers",
          "zone": "outside",
          "does": "SSE or streamable HTTP MCP servers named by URL or found in client configs; their tools, prompts, resources and instructions are read.",
          "ev": "README.md#L281 remote subcommand"
        },
        {
          "name": "LLM provider",
          "zone": "outside",
          "does": "Model reached through LiteLLM for the llm and behavioral analyzers, for example OpenAI, Azure OpenAI, AWS Bedrock or a local endpoint.",
          "ev": "README.md#L129 model setting accepts LiteLLM models"
        },
        {
          "name": "Cisco AI Defense API",
          "zone": "outside",
          "does": "Cisco inspect API used by the api analyzer; the default endpoint is in the US region and MCP_SCANNER_ENDPOINT changes it.",
          "ev": "README.md#L106 default endpoint"
        }
      ],
      "listens": [
        {
          "what": "mcp-scanner-api serves the REST scan endpoints on 127.0.0.1:8000 by default; --host and --port change the address.",
          "mode": "optional",
          "bind": "configurable",
          "sign_in": "none",
          "ev": "mcpscanner/config/constants.py#L63-64 default host and port"
        }
      ],
      "connections": [
        {
          "to": "Remote MCP server (named URL)",
          "kind": "target",
          "when": "default",
          "what": "Connects to the server URL passed to a command to list and read its tools, prompts, resources or instructions.",
          "ev": "claude-code-plugin/commands/scan-remote.md#L11 remote scan command"
        },
        {
          "to": "Remote servers listed in client configs",
          "kind": "target",
          "when": "optional",
          "what": "Config scans connect to each remote server listed in the scanned MCP client config files.",
          "ev": "mcpscanner/core/scanner.py#L2678-2683 remote servers from configs are scanned"
        },
        {
          "to": "LLM provider via LiteLLM",
          "kind": "model-provider",
          "when": "optional",
          "what": "Sends tool descriptions, prompt or resource text, or source code as chat messages when the llm or behavioral analyzer runs.",
          "ev": "mcpscanner/core/analyzers/llm_analyzer.py#L649 LiteLLM completion call"
        },
        {
          "to": "Cisco AI Defense inspect API",
          "kind": "vendor-api",
          "when": "optional",
          "what": "Sends content to the Cisco AI Defense inspect API with the key in the X-Cisco-AI-Defense-API-Key header when the api analyzer runs.",
          "ev": "mcpscanner/core/analyzers/api_analyzer.py#L86 API key header"
        },
        {
          "to": "Package registries of launched servers",
          "kind": "download",
          "when": "optional",
          "what": "Stdio servers launched through commands such as uvx can download their dependencies on first run.",
          "ev": "README.md#L167 stdio timeout note"
        },
        {
          "to": "mcp.deepwiki.com",
          "kind": "target",
          "when": "optional",
          "what": "When the CLI runs without a target, --server-url defaults to https://mcp.deepwiki.com/mcp.",
          "ev": "mcpscanner/cli.py#L1580-1584 default server URL"
        },
        {
          "to": "VirusTotal",
          "kind": "vendor-api",
          "when": "optional",
          "what": "The virustotal subcommand and analyzer look up file SHA256 hashes at VirusTotal; they are enabled whenever VIRUSTOTAL_API_KEY is configured unless MCP_SCANNER_VIRUSTOTAL_ENABLED is false.",
          "ev": "README.md#L145-154 VirusTotal analyzer, auto-enable and opt-out variable"
        }
      ],
      "model": {
        "who": "client",
        "text": "Claude's model reads the plugin commands and skill and runs the scanner; the llm and behavioral analyzers additionally call a model provider with the user's key.",
        "ev": "claude-code-plugin/README.md#L55 skill used by Claude"
      }
    },
    "needs": [
      {
        "kind": "runtime",
        "text": "Claude Code, which loads the plugin from its folder with --plugin-dir.",
        "ev": "claude-code-plugin/README.md#L32 plugin load command"
      },
      {
        "kind": "tools",
        "text": "The cisco-ai-mcp-scanner Python package, installed separately from the plugin.",
        "ev": "claude-code-plugin/README.md#L16-18 prerequisite install"
      },
      {
        "kind": "runtime",
        "text": "Python 3.11.4 or newer.",
        "ev": "pyproject.toml#L16 Python requirement"
      },
      {
        "kind": "tools",
        "text": "uv, because the skill tells Claude to start the scanner with uv run mcp-scanner.",
        "ev": "claude-code-plugin/skills/security-scan/SKILL.md#L10 uv run instruction"
      },
      {
        "kind": "model-key",
        "text": "MCP_SCANNER_LLM_API_KEY for the llm and behavioral analyzers used by deep-scan, scan-prompts, scan-resources and scan-behavioral.",
        "ev": "claude-code-plugin/README.md#L67-68 analyzer requirements"
      },
      {
        "kind": "credentials",
        "text": "MCP_SCANNER_API_KEY for the Cisco AI Defense api analyzer.",
        "ev": "claude-code-plugin/README.md#L69 api analyzer requirement"
      },
      {
        "kind": "account",
        "text": "A Cisco AI Defense subscription for the api analyzer.",
        "ev": "README.md#L814 subscription contact"
      }
    ],
    "installer": [
      {
        "text": "The plugin loads from a local folder with claude --plugin-dir; the README names a marketplace install for when the plugin is published.",
        "ev": "claude-code-plugin/README.md#L29-35 plugin install section"
      },
      {
        "text": "pip install cisco-ai-mcp-scanner installs the latest package release without a version pin.",
        "ev": "claude-code-plugin/README.md#L18 prerequisite install line"
      },
      {
        "text": "The main README installs the CLI as a uv tool with Python 3.13, unpinned, or from the GitHub repository with --from git+.",
        "ev": "README.md#L53-61 CLI install commands"
      },
      {
        "text": "The folder passed to claude --plugin-dir is the claude-code-plugin directory of the mcp-scanner repository, which the README fetches with git clone.",
        "ev": "README.md#L67 repository clone command that provides the claude-code-plugin folder"
      },
      {
        "text": "The cisco-ai-mcp-scanner package build includes only the mcpscanner modules, so pip install does not provide the plugin folder.",
        "ev": "pyproject.toml#L141-142 setuptools package discovery limited to mcpscanner*"
      }
    ],
    "limits": [
      {
        "text": "The known-configs scan reads MCP client config files in the home directory, on Windows including Claude Desktop's claude_desktop_config.json and VS Code's settings.json.",
        "ev": "mcpscanner/config/constants.py#L392-400 Windows config paths"
      },
      {
        "text": "Config scans start each stdio server listed in the scanned file to list its tools.",
        "ev": "mcpscanner/core/scanner.py#L2658-2662 stdio servers from configs are launched"
      },
      {
        "text": "Stdio servers start with the scanner's full process environment merged with the env entries from their config.",
        "ev": "mcpscanner/core/scanner.py#L2364-2370 environment passed to stdio server"
      },
      {
        "text": "A --bearer-token given to known-configs or config is sent to every remote server discovered in the configs.",
        "ev": "README.md#L348-351 bearer token applied to config servers"
      },
      {
        "text": "Without --analyzers the CLI runs api, yara and llm, so content goes to Cisco and the LLM provider when their keys are set.",
        "ev": "mcpscanner/cli.py#L1533-1537 default analyzer list"
      },
      {
        "text": "The README starts the REST API with --host 0.0.0.0, which binds it to all interfaces.",
        "ev": "README.md#L716 API server example with all-interfaces host"
      },
      {
        "text": "The REST /scan-tool handler's parameters are the request body, the incoming HTTP request and a scanner factory; its signature has no authentication dependency.",
        "ev": "mcpscanner/api/router.py#L392-401 endpoint signature without auth dependency"
      },
      {
        "text": "The author states that behavioral analysis cannot detect runtime-only behaviors and may miss heavily obfuscated code.",
        "ev": "docs/behavioral-scanning.md#L364-365 listed limitations"
      },
      {
        "text": "MCP_SCANNER_VIRUSTOTAL_UPLOAD_FILES uploads unknown files to VirusTotal for scanning; the documented default is false.",
        "ev": "README.md#L156-157 file upload option"
      },
      {
        "text": "The REST /scan-tool endpoint connects to the server_url named in the request body, with API-key or custom-header auth also taken from that body.",
        "ev": "mcpscanner/api/router.py#L420-429 auth and target taken from the request"
      },
      {
        "text": "Config scans connect to each remote server with its URL and the auth given on the command line only; headers stored for that server in the scanned config file are not sent.",
        "ev": "mcpscanner/core/scanner.py#L2681-2682 remote scan call passes only server_config.url and the CLI auth, not the config's headers field"
      },
      {
        "text": "The CLI sets the Cisco AI Defense key only when the api analyzer is selected and the LLM key only for the llm, behavioral or meta analyzers, so scans run with --analyzers yara call neither service.",
        "ev": "mcpscanner/cli.py#L205-214 API and LLM keys are set only for the selected analyzers"
      }
    ],
    "not_found": [
      {
        "field": "maturity",
        "searched": "claude-code-plugin/.claude-plugin/plugin.json, marketplace.json, claude-code-plugin/README.md, README.md, pyproject.toml classifiers for preview/beta/alpha/stable/deprecated"
      },
      {
        "field": "not_for",
        "searched": "README.md, claude-code-plugin/README.md, SKILL.md, all nine command files, SECURITY.md for out-of-scope or authorised-use statements"
      },
      {
        "field": "capability_count",
        "searched": "claude-code-plugin/README.md command table and README.md; numbers given (20 heuristic rules, 12 attack vectors, three engines) do not count tools, commands or skills"
      },
      {
        "field": "autonomy",
        "searched": "not applicable: entry type is plugin, not agent"
      },
      {
        "field": "declared permissions",
        "searched": "plugin.json, marketplace.json, SKILL.md frontmatter and command frontmatter for allowed-tools or permission statements"
      }
    ]
  },
  "verdict": null,
  "runs": [
    {
      "schema_version": "0.5",
      "run_id": "20261107T125926Z-cisco-mcp-scan-yara",
      "entry": "cisco-mcp-scanner",
      "draft": "review",
      "label": "observation",
      "method_version": "0.1",
      "reach_method": "reach-v0.1",
      "published": "2026-11-07",
      "run_by": "Ilja Schlak",
      "disclaimer": "One run is an observation, not a benchmark. Observed reach holds for exactly this configuration: the stand-alone CLI in YARA-only mode, without a model or API keys.",
      "tool": {
        "name": "Cisco MCP Scanner CLI (cisco-ai-mcp-scanner, YARA only)",
        "commit": "5d147dc7e3635606e41104112fa297b45444b05c",
        "version": "v4.8.6",
        "install_source": "upstream",
        "declared_file": "declared.json"
      },
      "runtime": {
        "client": "standalone",
        "client_version": "n/a (no agent client)",
        "permission_mode": "n/a (no approval layer)",
        "allow_list": [],
        "sandbox": {
          "lab_vm": true,
          "external_allowlist_empty": true,
          "dns_via_lab_resolver": true,
          "target_only": true
        },
        "user": "tool (uid 1001), no sudo"
      },
      "model": null,
      "target": {
        "name": "Lab fixture vuln-mcp-server (MCP over streamable HTTP)",
        "version": "0.1.0",
        "ground_truth": "fixed before the run: 5 embedded probes (tool poisoning, exfiltration, schema, prompt and server-instruction injection) and 2 clean controls; probe ids, places and classes are in findings.jsonl",
        "contamination": "synthetic, inert bait strings written for the lab; no capability claim; the scanner is measured, not the server"
      },
      "access_mode": "white-box",
      "task_given": "Run without a model: install the Cisco mcp-scanner CLI, start it in YARA-only mode against the prepared MCP server and observe the scanner's reach and any egress. Expected: a connection to the target only, no egress to the internet.",
      "timebox_minutes": 10,
      "overnight": false,
      "phases": {
        "install": {
          "start": "2026-11-07T12:59:26Z",
          "end": "2026-11-07T13:02:31Z",
          "egress_hosts": [
            "pypi.org",
            "files.pythonhosted.org"
          ]
        },
        "run": {
          "start": "2026-11-07T13:02:31Z",
          "end": "2026-11-07T13:05:09Z",
          "egress_hosts": []
        }
      },
      "duration_seconds": 158,
      "result": {
        "score": "At tool level all 3 baited tools were flagged HIGH and both clean controls stayed SAFE. The prompt and server-instruction probes were not scanned: the remote scan lists tools only.",
        "findings_reported": 3,
        "sample_validated": 3,
        "true_positives": 3,
        "false_positives": 0,
        "scope_violations": [],
        "file": "findings.jsonl",
        "notes": "A small synthetic sample from a lab fixture, so no general detection rate follows: coverage 3 of 5 probes, recall 1.0 on the scanned ones (score.json). Even in YARA-only mode without keys the scanner tries at start-up to download litellm's model cost map from GitHub (tool-stderr.txt line 2). The lab resolver answered that name with its sinkhole (dns.jsonl line 1), the connection attempts were dropped (blocked.jsonl), and the scan began about six seconds after the tool started: tool start 13:02:35 (host-events.jsonl line 4), first request 13:02:41 (target-access.txt line 2). During the install phase the same host was requested once through the lab proxy, one second after the last package download, and refused (install-egress.jsonl line 202). The install took the 99 locked packages with their hashes from the project's uv.lock."
      },
      "reach_observed": {
        "level": 2,
        "axes": {
          "fs": {
            "value": null,
            "evidence": "no evidence: no writes besides result paths (fs-diff.json); reads are not audited, so 0 (reads only the working directory) and 1 (reads outside) cannot be told apart; /tmp in the runner is a tmpfs and invisible to fs-diff"
          },
          "net": {
            "value": 2,
            "evidence": "blocked.jsonl#L1-5 the runner (10.73.2.10) tried five times, a second apart, to connect to 198.18.0.1:443, the lab's DNS sinkhole, and was dropped (line 1 lies half a second before the tool start the host recorded, because the lab machines' clocks differ by about a second); dns.jsonl#L1 the runner asked for raw.githubusercontent.com and was given the sinkhole address; tool-stderr.txt#L2 the scanner logs that its litellm dependency could not fetch the model cost map from raw.githubusercontent.com and fell back to a local copy; gateway-sets.txt#L2-5 no external target was allowed during the run (run_allow has no elements); target-access.txt#L2-4 the scan itself reached only the declared target (run->tgt)"
          },
          "exec": {
            "value": 0,
            "evidence": "none found: no execve besides the tool itself. In host-events.jsonl lines 1 to 3 are the lab harness (timeout, the start wrapper and a uname the wrapper runs before handing over), line 4 is the tool starting (python3 running mcp-scanner), and nothing was started after it"
          },
          "cred": {
            "value": 0,
            "evidence": "none found: no reads of the watched credential stores (auditd: SSH, cloud CLIs, Kubernetes, GnuPG, browser profiles, Docker, netrc, npmrc, pypirc, agent tokens, /etc/shadow, sudoers), which include the three canary files planted for this run (AWS credentials, netrc, npmrc); no canary in egress.jsonl or dns.jsonl. Environment variables are not observable"
          },
          "persist": {
            "value": 0,
            "evidence": "none found: no persistence paths in fs-diff.json (outside the lab's churn filter) and no writes to the watched start-up files (shell rc, autostart, systemd --user)"
          },
          "hitl": {
            "value": null,
            "evidence": "no evidence: no approval events, the CLI ran without an agent client or an approval layer"
          }
        },
        "unknown": [
          "fs",
          "hitl"
        ]
      },
      "surplus": {
        "axes": {
          "fs": null,
          "net": 1,
          "exec": 0,
          "cred": 0,
          "persist": 0,
          "hitl": null
        },
        "count": 1,
        "beyond_static": [],
        "headline": "Beyond declared on 1 of 6 rights: network (observed 2, declared 1), with blocked attempts to reach GitHub at start-up even in YARA-only mode."
      },
      "fit": "partial",
      "verdict": {
        "use_again": "with-limits",
        "one_sentence": "Useful as a key-free first pass over an MCP server's tools: it flagged all three baited tools without a false alarm, but it reads tools only and reaches for GitHub at start-up.",
        "limits": "Measured only in YARA-only mode against one small synthetic fixture, so no detection rate follows. The default remote scan lists tools only: prompts, resources and server instructions stay unscanned. At start-up the scanner fetches a model price list from GitHub that it does not need for a YARA scan; run it without internet access or behind an allowlist, where the fetch fails and the scan goes on with the bundled copy, as observed. The API and LLM analyzers, the known-configs scan and the stdio launch mode were not run and keep the code reading."
      },
      "min_config_tested": {
        "cli": "mcp-scanner --log-level error --analyzers yara --format raw --server-url <target URL>, without API keys and without cloud upload (tool-cmd.sh)"
      },
      "incidents": {
        "realtime_alerts": 0,
        "blocked": "blocked.jsonl lines 1 to 5: SYN packets from the runner to the lab's DNS sinkhole on port 443, all dropped; install-egress.jsonl line 202: a CONNECT to raw.githubusercontent.com during the install phase, refused by the lab proxy (403)",
        "aborted": false
      },
      "artifacts": {
        "manifest": "manifest.json",
        "declared": "declared.json",
        "egress": "egress.jsonl",
        "blocked": "blocked.jsonl",
        "fs_diff": "fs-diff.json",
        "host_events": "host-events.jsonl",
        "hitl_events": "hitl-events.jsonl",
        "findings": "findings.jsonl",
        "score": "score.json",
        "extra": [
          "scanner-output.json",
          "tool-cmd.sh",
          "tool-stderr.txt",
          "target-access.txt",
          "gateway-sets.txt",
          "dns.jsonl",
          "install-egress.jsonl"
        ],
        "secret_scan": {
          "redact": "bait texts of the lab fixture in scanner-output.json replaced by their SHA-256, ANSI colour codes and CRLF removed from tool-stderr.txt, the host path removed from score.json, only the two allow sets of the gateway ruleset published (gateway-sets.txt); scan of all published files by analysis/export-package.py for this run's canary values, the original bait texts, AWS keys, private keys, API tokens and host identifiers: no hits",
          "gitleaks": "not run: not installed on the lab host; the scan above instead",
          "date": "2026-11-07"
        },
        "private": [
          "pcap/",
          "diff/",
          "raw/runner-audit.log",
          "raw/tool-stdout.txt",
          "raw/canaries.txt",
          "raw/seed/",
          "raw/fs-diff-raw.csv",
          "preflight.log",
          "preflight.json",
          "profile.yaml",
          "task.txt",
          "allowlist.txt",
          "approver-policy.json",
          "proxy-policy.json",
          "sensors/gw-nft.txt",
          "sensors/egress.flows",
          "sensors/dns.log",
          "sensors/install-dns.log",
          "sensors/install-fs-diff.json"
        ]
      },
      "interests": "Private project, privately funded. The author is employed by an IT consultancy; the company is neither client nor sponsor of this measurement and had no influence on selection, execution or result. No business relationship with the maintainers of the tested tool; no credits, licences or payments accepted from tested vendors. Deviations are stated in this field.",
      "right_of_reply": "dispute-process",
      "replications": []
    }
  ]
}
