Check S14 · agent surface family · severity CRITICAL · automatic · one of the 15 skill and plugin checks
The question this check asks: does this Claude Code plugin's .mcp.json launch an MCP server with a command that fetches something and runs it? An MCP server a plugin declares starts with the session, runs with the user's privileges, and answers every tool call the agent routes to it. Its launch line is the first thing that executes.
A plugin may carry an .mcp.json at its root, or declare mcpServers inline in .claude-plugin/plugin.json; either way Claude Code starts those servers for the session, and the documentation's own line applies: “Plugins and marketplaces are highly trusted components that can execute arbitrary code on your machine with your user privileges.” The shape that turns a launch line into a payload is the one C20 has read out of repositories' MCP configurations since the first roster: a command that both fetches — curl, wget, Invoke-WebRequest — and executes what it fetched, before any trust prompt. Reversec's “Skill Issues” of 5 May 2026 and Datadog's Clawsights write-up of 11 May both turn on the same fact about skills — that a command can run “before the rendered skill reaches the model” — and an MCP launch line is the plugin-level surface with the same property. No published malicious plugin exists in either Anthropic marketplace to name; the malicious skills page says so, and that is the argument for reading the document before the session starts rather than after.
S14 runs on a plugin key. It fetches .mcp.json when it exists, reads the mcpServers object inline in plugin.json when one is declared, parses the JSON, and reads every server's command plus its arguments, joined — the way a launcher reads it. A line fires when it both fetches and executes what it fetched: a pipe into a shell, an inline bash -c "curl … && ./x", or PowerShell's iex. That finding is mcp-exec:.mcp.json, CRITICAL, 30 points, and a BLOCK under the default profile — C20's rule and C20's code, through the function the 22 use, quoting the offending line. Unparseable JSON yields nothing, deliberately: a launcher would not read it either.
Two further rows are notes, at 0 points, action allow, and never move a verdict. skill-mcp-launch:<name> names a server whose command is not one of the documented launchers — npx, uvx, uv, docker, node, python, python3, deno, bunx, bun, pnpx, pnpm, yarn — so a shell, an absolute path or a binary the plugin ships is listed with the first sixty characters of its line. skill-mcp-remote:<host> names a server declared by url: every tool call leaves the machine, and the note says to where. Three of each at most.
Measured on 20 September 2026 through the same code as /v1/skill/score, over all 310 official-marketplace plugins and the first 100 community plugins: the shell-fetch shape fired on none; the launcher and remote-server notes fired where plugins declare them and, being notes, moved no verdict. The C20 page records the one cost that rule is known to carry on repositories — a single legitimate npx -y line on the GitHub top-100 — and that is why the fetch and the execute must be distinct here as there, so a bare npx never fires.
A server that legitimately bootstraps itself — fetches its own installer from its vendor and runs it in the launch line — would fire, and should; the line is shown, and it is the same line a compromised plugin would carry. No plugin on either marketplace does this today. A server launched by a wrapper script the plugin ships is a note here and its script is S10's to read. A remote server on the vendor's own host is a note that reads like a finding to someone who has not seen one before; it is the declared surface, and the host name on the decision is the point.
What the server does once it is running. S14 reads the launch line; a server started by npx some-package is that package's code, and neither this tier nor the deep scan follows a package name into the registry — a package named on a command line is S1's question only where it declares a repository. MCP servers outside the plugin — in a user's or a project's configuration — are not a listing and are not assessed; the agent check-up on the roadmap is where an installation's own servers will be read. A bare skill has no MCP document, and a plugin without one starts nothing with the session, so an absent document is a pass, not a skip. ClawHub (@owner/slug) is parsed and routed but not assessed on this tier — and a ClawHub skill is never a plugin, so this check has nothing to run there in any case.
Scope, the same on every page of this roster: RepoGates does not see git clone, package managers or curl — outside Claude Code with the RepoGates plugin, whose hook refuses a clone or install that names a blocked repository on the command line, before it runs. The hook sees Bash tool calls in that one client and nothing else: npx skills add owner/repo and claude plugin install name@marketplace are shell lines it judges; /plugin install is a slash command the hook never sees; a skill copied by hand into ~/.claude/skills is never seen at all. This tier reads the listing record, the tree once, and at most twelve files of 64 KB each — SKILL.md files first, then a plugin's agents and commands, then the scripts beside each skill; more than fits is a partial scan, stated, and never a pass on what was not read. The deep scan reads the whole package. A PASS means nothing matched on what was read, never that the skill was run.
Open the plugin's MCP configuration and read every launch line. Claude Code starts each server with your user privileges when the session opens:
jq -r '.mcpServers | to_entries[] | "\(.key): \(.value.command // .value.url) \((.value.args // []) | join(" "))"' .mcp.json
jq -r '.mcpServers // empty | to_entries[] | "\(.key): \(.value.command // .value.url)"' .claude-plugin/plugin.json
A command that contains curl or wget and a pipe into sh or bash is the shape this check blocks on. A launcher that is not npx, uvx, docker, node or python is one to look at; a url tells you where your tool calls will go.
Why is an MCP server launched by npx not a finding? Because it is the documented shape. The MCP servers a plugin declares are launched, almost without exception, by npx, uvx, docker, node or python — the launchers the check knows — and a server started that way is the plugin describing what it runs, not hiding it. The false-positive principle that graded the earlier rosters holds: a declared surface is a note. What is a finding on its own is the C20 shape — a launch command that fetches something and executes what it fetched — and that is critical and blocks. A launcher outside the list, or a server reached over a URL, is a note so the reader sees it beside whatever else fired.
What does a remote MCP server mean for the machine? That every tool call the agent makes through that server leaves the machine: the arguments go to the host in the URL and the results come back from it. That is an ordinary integration — a hosted API behind an MCP endpoint — and the check lists the host as a note rather than grading it, because a plugin that declares a remote server is telling you where its tools run. What the note gives you is the host name, on the decision, before the session starts.
Did any marketplace plugin fire this check? Not above a note. On 20 September 2026 the shell-fetch shape fired on none of the 310 official-marketplace plugins and none of the first 100 community plugins; the launcher and remote-server notes fired where plugins declare them, and a note never moves a verdict. The rule is the one C20 has applied to repositories' MCP configurations since the first roster, where the one known cost is a single legitimate npx -y line on the GitHub top-100 — which is why that class was kept short of an instant red there, and why here the fetch and the execute must be distinct.
C20 MCP shell launch — the rule and the code this check reuses, on a repository's launcher configs · S13 Hooks and monitors — the same rule on the plugin's hooks, the other document that runs without the model deciding · S15 Credential redirect — a base URL pointed at a stranger's host, the other thing a plugin's configuration can do to every API call.
Add RepoGates to Chrome Pricing
Numbers on this page: the 20 September 2026 measurement of all 310 official-marketplace plugins and the first 100 community-marketplace plugins, run through the same code as /v1/skill/score; the Claude Code plugins reference; Reversec Labs, 5 May 2026; Datadog Security Labs, 11 May 2026; the C20 page's top-100 figure where GitHub is cited; the incident record behind the malicious skills page; thresholds and weights from the product's policy and scoring tables.