pmcp@3.0.3
CAUTION· not startedPublisher claims: "PMCP - Progressive MCP: Minimal context bloat with on-demand tool discovery" Observed in sandbox: no network, touches nothing outside the project, writes no memory.
Coverage
Not started: dependency install failed; the server itself was not observed.- Tested
- the dependency install step · every file read, file written and host contacted by what ran
- Not tested
- server startup · the tool list · tool behavior
What this means for the label: Caution here covers what ran. The server's tools were not exercised with a real credential, so nothing about their behavior is asserted; the credential summary below says what to do with that.
Before you give it your token
- It will ask for
API_TOKENBOOTSTRAP_CLONE_TOKENBOOTSTRAP_HMAC_SECRETGH_TOKENGITHUB_TOKENPMCP_AUTH_MODEPMCP_AUTH_TOKENPMCP_OAUTH_AUDIENCEPMCP_OAUTH_ISSUERPMCP_OAUTH_JWKS_URLVERCEL_AUTOMATION_BYPASS_SECRETWORKER_API_KEYor--src-authfile/--dest-authfile/--oauth-audience/--auth-token/--auth-token-file/--oauth-issuer/--oauth-jwks-url/--init-author-email/--init-author-name/--init-author-urlConfiguration, not credentials: BOOTSTRAP_ADOPTION_MODE, BOOTSTRAP_HMAC_KEY_ID, BOOTSTRAP_INGEST_ENDPOINT, BOOTSTRAP_INSTALL_MODE, BOOTSTRAP_JOB_ID, BOOTSTRAP_PAYLOAD, BOOTSTRAP_PIPELINE_BRANCH, BOOTSTRAP_PIPELINE_ID, BOOTSTRAP_POLICY_MODE, BOOTSTRAP_REPO, BOOTSTRAP_REQUESTED_MODE, BOOTSTRAP_RUN_ID, BOOTSTRAP_SPEC_SOURCE_KIND, BOOTSTRAP_SPEC_SOURCE_VALUE, BOOTSTRAP_TARGET_BRANCH, BOOTSTRAP_TRIGGERED_BY, GITHUB_ACTIONS, GITHUB_OUTPUT, GITHUB_REF_NAME, GITHUB_REF_TYPE, GITHUB_RUN_ATTEMPT, GITHUB_RUN_ID, GITHUB_SHA, GITHUB_WORKSPACE, GOVERNED_PIPELINE_NODE_MODULES, MUT_END, MUT_FILE, MUT_MARKER- Where it talks to
- no vendor host named in the codeAlso example.invalid, ghcr.io, metadata-check.invalid, private.invalid, crates.io, docker.com — not the publisher's domain; check what for before connecting.
- What the honeypot saw
- no credential files read · no network
- What we could not test
- server startup · the tool list · tool behavior
Connect it with a scoped credential, under the guard, and read the third-party hosts first. The code names example.invalid, ghcr.io, metadata-check.invalid, private.invalid, crates.io, docker.com as well as its own vendor. The guard injects API_TOKEN, BOOTSTRAP_CLONE_TOKEN, BOOTSTRAP_HMAC_SECRET, GH_TOKEN, GITHUB_TOKEN, PMCP_AUTH_MODE, PMCP_AUTH_TOKEN, PMCP_OAUTH_AUDIENCE, PMCP_OAUTH_ISSUER, PMCP_OAUTH_JWKS_URL, VERCEL_AUTOMATION_BYPASS_SECRET, WORKER_API_KEY only into requests to the host you bind (and its subdomains) and refuses the rest.
Never paste a credential into a chat, a ticket or a report, including ours. How the guard holds it.
Is [email protected] safe to install?
KillerSkills ran it in the KillerSkills honeypot sandbox (2 scenarios; not started: dependency install failed; the server itself was not observed) on 2026-11-14 and scored it 70/100 (Caution). It is not known to be malicious, but it has behaviour or code a security policy should decide on before install. Notable findings: Access to sensitive file paths; Access to sensitive file paths; Access to sensitive file paths. Summary: no network, touches nothing outside the project, writes no memory.
Behavior trace
Honeypot run · 0 s · 2 scenarios · 0 deviationsStatic findings
all.** ``<project>/.env.pmcp`` ships with a checkout, so a clone could link itFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."""The root a checkout file named by its path (``<cwd>/.env.pmcp``) is confined to.Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.description="Set, sync, and check secrets in user/project PMCP .env files.",Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.api_key_available: bool = False # True if key found in .envFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.env_instructions: "Uses active kubeconfig (~/.kube/config or KUBECONFIG env var)"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.return resolve_project_root(project) / ".env.pmcp"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# every npm/npx server silently loses auto-update and version reporting.Fix: Pin versions and sources; vendor anything fetched at install time.ErrorCode.E502_API_KEY_MISSING: "Set the required environment variable in .env",Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# LAST RESORT, above the kill on purpose. faulthandler's C watchdog dumpsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.behalf -- ``cli.load_startup_env`` reads ``.env`` and ``.env.pmcp`` before argFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# reads ``.env.pmcp``; ``_check_api_key_available`` reads ``.env``), and thenFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.operator's behalf (``load_startup_env`` reads ``.env`` and ``.env.pmcp``),Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.(``pmcp.env``, ``.env.pmcp``), the trust store, package approvals, the registryFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# removal is fail-closed, so it costs auto-update for an unusual config andFix: Pin versions and sources; vendor anything fetched at install time.``.env.pmcp`` linked out of the checkout contributes nothing, so a file theFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# the default is the entire point. An omission now costs auto-update for oneFix: Pin versions and sources; vendor anything fetched at install time.# the kill it only fires when pytest-timeout's handler never got to run --Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# `npx --name foo probe`). `HOME` relocates `~/.npmrc`, so `HOME=/x` withFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.const cloneUrl = `https://x-access-token:${cloneToken}@github.com/${repository_full_name}.git`;Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.' ' - run: curl -sd @"$RUNNER_TEMP/published-wheel/report.json" "$UPLOAD_URL"Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.# 60 s -> async stacks | 700 s -> thread stacks + kill itemFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell._PROJECT_STORE_PATH = ".env.pmcp"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.checkout's plain ``.env``; ``pmcp_introduced_keys`` covers a plant whose storeFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# auto-update but never mints a wrong identity.Fix: Pin versions and sources; vendor anything fetched at install time.remote_config = config.configFix: Pin versions and sources; vendor anything fetched at install time.# `.env.pmcp` that is a symlink leaving the project (pmcp.atomic_write),Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.``docker run --env-file .env <image>`` resolved to ``.env`` and any twoFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.#: ``project`` (``<project>/.env.pmcp``, or a checkout file named by ``path=``)Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.before, and the gateway still reads ``.env`` for its own configuration.Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.// A refusal costs auto-update coverage for that server; a wrong answer is aFix: Pin versions and sources; vendor anything fetched at install time.return root / ".env.pmcp", (root, "project")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# .env, then read back when the subprocess is provisioned. Only theFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.' ' - run: curl -sfT "$RUNNER_TEMP/published-wheel/report.json" "$UPLOAD_URL"Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.remote_config = config.configFix: Pin versions and sources; vendor anything fetched at install time.env_instructions: "Uses active kubeconfig (~/.kube/config or KUBECONFIG env var)"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."""Load the startup env files, recording what the plain ``.env`` introduced.Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.name = "pmcp.env" if scope != "project" else ".env.pmcp"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."""A name read from a file pmcp does not own (``.env``, ``.env.pmcp``,Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.# `npx --name foo probe`). `HOME` relocates `~/.npmrc`, so `HOME=/x` withFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.// Walk up from a realpath'd npm/npx entry point to the npm package root, i.e.Fix: Pin versions and sources; vendor anything fetched at install time.' ' - run: wget --post-file="$RUNNER_TEMP/published-wheel/report.json" "$UPLOAD_URL"Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why."""Monitor subprocess output as heartbeat, kill on timeout.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# The backstop is disarmed only now, after the kill it guardsFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.subprocess.check_output(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out."""The kill for a child pmcp started with :func:`detached`: it andFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.// gateway.update_server probe `npx -y exec@latest --help` for every npx serverFix: Pin versions and sources; vendor anything fetched at install time.child = subprocess.Popen(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.config=RemoteMcpServerConfig(type="streamable-http", url=probe_url),Fix: Namespace tool names and keep tool descriptions literal; no instructions to the agent inside them.by what an argument looks like. In ``npx -y [email protected] --token abcdef0123456789``Fix: Pin versions and sources; vendor anything fetched at install time.// Two different npm installations on PATH would mean `npx <pkg>` andFix: Pin versions and sources; vendor anything fetched at install time.subprocess.run(command, cwd=work, env=env, check=True, timeout=300)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.destination. A write that fails partway -- ``ENOSPC``, a quota, a killFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# The one kill rule (round 7, claude N-B): the group is signalledFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.``.mcp.json``, an overlay, a manifest), fit for any output: itself whenFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.is tracked by nothing and its kill falls back to the child alone."""Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.subprocess.run(["git", "diff", "--quiet", "HEAD", "--"], cwd=source, check=True)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.kill site shares (Consiliency/pmcp#388 round 7): the leader only whileFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.output = subprocess.check_output(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.// Name an npm/npx server's package using **npm's own parser**, or refuse.Fix: Pin versions and sources; vendor anything fetched at install time.' ' - run: curl -sf [email protected] "$UPLOAD_URL"Fix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.subprocess.run(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.os.killpg(pgid, signal.SIGKILL if kill else signal.SIGTERM)Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.output = subprocess.run(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.subprocess.run(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.refs = subprocess.check_output(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.:func:`pmcp.child_output.kill_process_tree_now`, the one kill rule everyFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# --- the one kill rule (Consiliency/pmcp#388 rounds 6-7) ----------------------Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.config=RemoteMcpServerConfig(type="streamable-http", url=gateway_url),Fix: Namespace tool names and keep tool descriptions literal; no instructions to the agent inside them.process = subprocess.Popen(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.sha = subprocess.check_output(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.# user-scope secrets) to 0700. Never chmod a pre-existing directory such asFix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.// "has a name" check: `npx -y myalias@npm:left-pad` RUNS `left-pad`, but npaFix: Pin versions and sources; vendor anything fetched at install time.subprocess.run(args, cwd=cwd, env=env, check=True, timeout=300)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.process = subprocess.Popen(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.proc = subprocess.run(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.Reads from both stdout and stderr - npx writes progress to stderr,Fix: Pin versions and sources; vendor anything fetched at install time.return subprocess.run(["git", *args], capture_output=True, text=True, check=False)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.raise PermissionError(f"refusing to chmod a symlink: {target}") from excFix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.config=RemoteMcpServerConfig(type="streamable-http", url=gateway_url),Fix: Namespace tool names and keep tool descriptions literal; no instructions to the agent inside them.Windows' group kill -- and closing pmcp's handle kills what is left.Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.tree = subprocess.check_output(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.# wait is the caller's to have; the kill is not (round 11,Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# Heartbeat timeout - kill if no output for this longFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.proc = subprocess.run(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out."""``subprocess.run(command, capture_output=True, text=True)`` for aFix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.return subprocess.run(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.every kill site, Consiliency/pmcp#388 round 7)."""Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.# (:func:`track`). Every kill site goes through :func:`kill_process_tree_now`Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.output = subprocess.check_output(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out."""*args* with the npx package slot pinned to *version*, and the name.Fix: Pin versions and sources; vendor anything fetched at install time."""Is *command* npx spelled BARE -- the launcher the PATH resolves?Fix: Pin versions and sources; vendor anything fetched at install time.# `npx --name foo probe`). `HOME` relocates `~/.npmrc`, so `HOME=/x` withFix: Pin versions and sources; vendor anything fetched at install time.# the kill (Consiliency/pmcp#324; round 8, codex F003). After theFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.``npx --userconfig /tmp/rc probe`` and ``npx --registry http://x probe``Fix: Pin versions and sources; vendor anything fetched at install time.# never signals the process, so a probe like `npx <pkg> --help` against aFix: Pin versions and sources; vendor anything fetched at install time.# materialised by `load_manifest` into the npx package slot of `args` andFix: Pin versions and sources; vendor anything fetched at install time.``npx -y <pkg>`` shape and resolve.Fix: Pin versions and sources; vendor anything fetched at install time.servers whose install command IS the server (e.g. ``npx @brightdata/mcp``):Fix: Pin versions and sources; vendor anything fetched at install time.# or version runs. `cache` is NOT here: npx runs a cached package on itsFix: Pin versions and sources; vendor anything fetched at install time."""Return the raw npm/npx package token from *args*, or ``None``.Fix: Pin versions and sources; vendor anything fetched at install time.# after registration) kill the process synchronously; this killFix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.{ "mcpServers": { "pmcp": { "type": "http", "url": "http://127.0.0.1:3344/mcp" } } }Fix: Namespace tool names and keep tool descriptions literal; no instructions to the agent inside them.# every npm/npx server silently loses auto-update and version reporting.Fix: Pin versions and sources; vendor anything fetched at install time.# CPython kill a leader alone (Consiliency/pmcp#388 round 14,Fix: Build commands from fixed arguments; do not interpolate untrusted strings into a shell.of ``[email protected]`` attached to an argv of ``npx -y pkg`` approves nothing: npxFix: Pin versions and sources; vendor anything fetched at install time."""Name an npm/npx server's package via npm's own parser, or refuse.Fix: Pin versions and sources; vendor anything fetched at install time.npx -y probe`` fetches ``http://127.0.0.1:9/evil-pkg``.Fix: Pin versions and sources; vendor anything fetched at install time.# changes WHAT an approved `npx -y name@version` fetches -- the registry it asksFix: Pin versions and sources; vendor anything fetched at install time.`npx -y totally-arbitrary-evil-package`. This gate decides on the package'sFix: Pin versions and sources; vendor anything fetched at install time.# connected and running, so a version-only update like `npx pkg@latest`Fix: Pin versions and sources; vendor anything fetched at install time.both re-resolve at every ``npx -y`` spawn, which is the drift a pin existsFix: Pin versions and sources; vendor anything fetched at install time."""Is *executable* npx under any platform spelling (``NPX.CMD``, ``npx.bat``)?Fix: Pin versions and sources; vendor anything fetched at install time.proc = subprocess.Popen(Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.# ``. _ -``. pypi identifiers are a subset of this. A leading ``-`` (which npxFix: Pin versions and sources; vendor anything fetched at install time.*package* with `npx -y`, so allowlisting `internal-approved-tool` executedFix: Pin versions and sources; vendor anything fetched at install time.- "npx --yes envinfo --version"Fix: Pin versions and sources; vendor anything fetched at install time.# `npx -y {name}@latest --help` -- and `npx -y` INSTALLS WITHOUT PROMPTINGFix: Pin versions and sources; vendor anything fetched at install time.# escaped `tar\.gz` (npm-package-arg 13.x). pmcp runs whatever npx is on PATH.Fix: Pin versions and sources; vendor anything fetched at install time.ignores `mode` for intermediates, and create-then-chmod leaves a window inFix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.# resolves here before its probe spawns `npx <pkg>`, so without thisFix: Pin versions and sources; vendor anything fetched at install time.package code, e.g. ``npx <pkg> --help``, so it must not inherit otherFix: Pin versions and sources; vendor anything fetched at install time.# NPM_CONFIG__AUTH, which the pinned `npx -y` spawn would then readFix: Pin versions and sources; vendor anything fetched at install time.# option -- verified: `env npm_config_package=evil-pkg npx -y probe` fetchesFix: Pin versions and sources; vendor anything fetched at install time.# shorthand is not. Collapsing them made `npx --silent true <arg>` reportFix: Pin versions and sources; vendor anything fetched at install time.Cost to the agent
Tokens estimated at four characters each. The tool list is re-read by the agent on every turn, so it is the number to compare between servers that do the same job: ranked across the index.
Known vulnerabilities
No advisories in OSV for pmcp@3.0.3 as of 52 min ago. This is the package itself, not its dependencies.
What would make this Verified
10 points to 80 · weights: behavior 45%, static 25%, prompt 15%, publisher 15%- Reads credential paths from the home directory≈ +9 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.
- Unclaimed publisher≈ +8 on the totalClaim consiliency: a verified publisher gets +50 on publisher trust. It is free and usually instant with GitHub sign-in.
- Access to sensitive file paths≈ +4 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.
- References a credential file in the home directory≈ +4 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.
- Auto-update or dynamic fetch configuration≈ +4 on the totalPin versions and sources; vendor anything fetched at install time.
- URL with embedded credentials≈ +4 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.