# Publisher and supply chain

Step 2 establishes who publishes the server and exactly what will run, before anything runs. The output is the publisher record in the review template.

## Evidence ladder for publisher identity

Strongest first. Record the strongest rung you reached, with the URL and the date you read it.

1. A vendor page that names this exact endpoint or package: developer docs, help centre, or a GitHub organisation linked from the vendor's own site.
2. An MCP Registry entry with a reverse-DNS namespace (`com.vendor/server-name`). The registry verified control of that domain.
3. An MCP Registry entry with an `io.github.<account>/...` namespace. That proves control of a GitHub account, nothing more.
4. A connector directory listing in an AI client. A listing review checks listing criteria; Anthropic's Claude Code security page says it reviews directory connectors against listing criteria but does not security-audit or manage MCP servers.
5. An open-source repository with an identifiable maintainer: an organisation, a history, a security policy.
6. A package page alone (npm, PyPI, Docker Hub). Anyone can publish a name.

Not evidence of who publishes a server: a blog post, a tutorial, a star count, a directory card written by the author, a freelancer's recommendation, an AI answer.

Rule: no rung 1 or 2 and no identifiable maintainer at rung 5 is blocker B1. A rung 3 (`io.github`) entry alone does not clear B1. A name that differs from the one rung 1 gives, by one character, is B1.

## MCP Registry facts

The registry is in preview; verify these on its about, authentication, package-types and moderation pages before quoting them.

- Namespaces: `io.github.*` names need a GitHub login; reverse-DNS names need a DNS or HTTP challenge that proves domain control.
- Package ownership checks differ by type: npm packages declare `mcpName` in package.json, PyPI projects put an `mcp-name` string in the README, OCI images carry the `io.modelcontextprotocol.server.name` annotation. A registry entry pointing at a package that fails these checks should not exist; if you find one, treat it as B1.
- Moderation: the registry tells consumers to assume minimal to no moderation and leaves security scanning to package registries and downstream aggregators. A registry entry is identity evidence, not a safety review.
- Record: the full server name, namespace type, the version entry and the package or remote URL it points to.

## Local package checks

Run these before installing anything. They read registry metadata only.

- Name: compare character by character with the vendor's page. Watch for an added or missing hyphen, `-mcp` against `mcp-`, a missing npm scope, a misspelled vendor name.
- npm: `npm view <package>@<version> --json` shows `repository`, `maintainers`, `time` (publish date per version) and `dist` (integrity, signatures, and attestations for versions published with provenance). Verify the field names live; `dist.attestations` appears only on versions built with provenance. The same data is at `https://registry.npmjs.org/<package>`.
- PyPI: `https://pypi.org/pypi/<project>/<version>/json` shows project URLs, release upload times and files. Check the project page for attestations; verify the current location live.
- Red flags in the release history: a maintainer added shortly before a release, a sudden major jump, a long gap and then a release, a version published hours ago, `preinstall` or `postinstall` scripts.
- Repository: archived (B1 when the server can write), last commit date, a security policy, the licence, and whether the release tag matches the package version.
- Wrapper check: if the server only wraps a SaaS API and the vendor offers a remote server, prefer the remote one. The MCP local server security guide gives the same advice; an unnecessary local install widens what can go wrong.
- Maintenance: no release or commit in 12 months, a single maintainer on a write-capable server, or a new maintainer since the last review is condition C12.

## What each launcher really pins

- `npx pkg@1.2.3`, `bunx`, `pnpm dlx`, `yarn dlx`, `uvx pkg==1.2.3`, `uv tool run`, `uv run --with`, `pipx run`: the top-level package only. Its dependencies resolve from version ranges at install time unless the package ships a lockfile its installer honours.
- A container image by digest (`image@sha256:...`): the whole tree is frozen.
- Git: only a full commit hash pins. A branch or tag can move.
- A bare name, `@latest` or a `^` or `~` range: the next start can fetch different code. Condition C1.
- Any other launcher (`python -m server`, `node server.js`): the scanner cannot tell, so it says pinning was not checked (INFO CF04). Ask what runs and how it is versioned.
- A package index flag or variable (`--registry`, `--index-url`, `--extra-index-url`, `--index`, `NPM_CONFIG_REGISTRY`, `UV_INDEX_URL`, `PIP_EXTRA_INDEX_URL` and similar) that points away from the default npm or PyPI index: whoever runs that index decides what installs, and with an extra index a same-named package on either one can win (dependency confusion). The scanner reports it as CF04; record the index owner and pin.

## Hosted third parties and proxies

Aggregators (Supermetrics, Windsor.ai, Funnel), automation platforms (Zapier, Make, n8n) and hosted community servers sit between the agent and your accounts. Record:

- Who operates the host and in which region it runs.
- Whether it stores upstream credentials (OAuth refresh tokens, API keys) or receives them per request, and whether keys travel in URLs it can log.
- Whether the platform is multi-tenant, and what it says about tenant isolation.
- Its security page, status page, incident history and whether a DPA exists (the privacy owner decides on the DPA).
- Which upstream scopes its sign-in requests. A proxy's scopes are set by the proxy, not by your client.

A host that holds upstream keys is inside your trust boundary. Review it at Tier 2 or higher.

## Isolation recipe for evaluating a local server

Run only after step 2 passes, or with the user's explicit approval to collect evidence for a NO-GO. A person runs it, or approves the exact command.

- Install with the network on, list tools with it off. A throwaway image works for any package:
  - npm: a two-line Dockerfile, `FROM node:<version>` and `RUN npm install -g <package>@<version>`.
  - PyPI: `FROM python:<version>` and `RUN pip install <project>==<version>`.
- Run it with `docker run -i --rm --network none --read-only --cap-drop ALL --security-opt no-new-privileges <image>`. The MCP Inspector security page gives `docker run -i --rm --network none <image>` as the stdio command for untrusted servers.
- No home-directory mounts and no Docker socket. Pass only the env vars the server needs, as named `-e NAME` flags that take the value from the person's shell, never the whole environment. `-e NAME=value` writes the value into the config (CF01).
- Capture tools through that command with the Inspector CLI (see client-controls.md). `tools/list` needs no network.
- If a test must reach the vendor API, allow egress to the vendor's hosts only, with a test identity.

## Advisory check

- Search the GitHub Advisory Database, NVD and OSV for the exact package and version, plus the vendor's security page. Record the source and date.
- The config scanner hard-codes these entries and says the list is not complete:
  - mcp-remote 0.0.5 to before 0.1.16: OS command injection when connecting to a malicious server's authorization endpoint (GHSA-6xpm-ggf7-wc3p, CVE-2025-6514, CVSS 9.6). Fixed in 0.1.16.
  - MCP Inspector below 0.14.1: remote code execution through its unauthenticated proxy (GHSA-7f8r-222p-6f5g, CVE-2025-49596, CVSS 9.4). Fixed in 0.14.1.
  - MCP Inspector below 0.16.6: command execution through cross-site scripting when it connects to an untrusted server (GHSA-g9hg-qhmf-q45m, CVE-2025-58444).
  - MCP Inspector 0.13.0 to 1.0.2: advisories published 2026-10-21 and fixed in 1.0.3, the deprecated 1.x line's security release. Two are critical remote code execution through a malicious OAuth callback (GHSA-4fc4-wcmh-4g86) or MCP App (GHSA-6x67-4w4w-9f3w); the others are GHSA-wmjf-2m46-2352, GHSA-2rq8-f55p-j2jp, GHSA-f6mc-jg3x-vq53, GHSA-m5pj-3536-3xcp and GHSA-8qgq-27v6-55w3. They are repository advisories without CVE IDs; read them on the Inspector's security advisories page.
  - MCP Inspector 2.0.0 to 2.11.0: code execution through an OAuth authorization URL on Windows and WSL (GHSA-q978-78c7-phqw), DNS rebinding that exposes the API token and stored OAuth tokens (GHSA-vcc9-3xgm-p9v4) and GHSA-m5pj-3536-3xcp. Fixed in 2.12.0.
  - MCP Inspector 2.0.0 to 2.13.0: medium advisories published 2026-11-04, fixed in 2.14.0: an MCP App that escapes its CSP (GHSA-h9j8-vj8g-v3gg), unvalidated `server.json` imports (GHSA-rwv7-mh2c-ff2p) and a stall on a malicious tool output schema (GHSA-2fw8-7jmw-r937). The last also affects 1.0.3 and below, fixed in 1.0.4.
- A matching advisory with a fixed version is blocker B7. Upgrade and pin first.
- mcp-remote's original GitHub URL now redirects; verify the current owner and maintainers before trusting a new release.

## Dated incidents and what each one teaches

- postmark-mcp on npm, September 2025: an unofficial package copied a vendor's open-source server, shipped clean versions, then version 1.0.16 added a hidden BCC of every email to an outside address. The vendor said it had no involvement. Unpinned launchers pulled the bad version on the next start. Lesson: check the name against the vendor's page and pin; a clean history says nothing about the next release.
- Smithery, June 2025: a build flaw on the hosting platform exposed an overprivileged token that controlled more than 3,000 hosted servers; it was fixed within two days. Lesson: a third-party host that holds your keys is inside your trust boundary.
- Asana's MCP server, June 2025: a bug could expose data across customer accounts and the server went offline for about two weeks. Lesson: vendor-hosted does not mean isolated. Know your kill switch before you need it.
- mcp-remote and MCP Inspector, mid 2025 and again in October and November 2026: the bridge and the dev tool were the vulnerable parts, not the servers. Lesson: scan every config, including the tools around the server, and pin the review tooling itself.
