npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@froggychips/mcp-vault

v0.16.0

Published

Offline security check for MCP configs in CI. A GitHub Action, pre-commit hook and CLI that flag unpinned MCP servers, plaintext secrets, typosquats, tool poisoning and toxic tool flows — offline, zero dependencies, no telemetry.

Downloads

897

Readme

mcp-vault

npm License: MIT runtime deps: 0 tests: 1253 passing

Offline security check for MCP configs in CI.

A GitHub Action, pre-commit hook and CLI that flag unpinned MCP servers, plaintext secrets, typosquats, tool poisoning and toxic tool flows — offline, zero dependencies, no telemetry.

It reads the MCP configs committed to a repository (.mcp.json, .vscode/mcp.json, .cursor/mcp.json) and fails the build before anyone's editor launches something it should not.

Add to CI in one line

# .github/workflows/mcp-vault.yml
name: mcp-vault
on:
  pull_request:
    paths: ['**/.mcp.json', '**/.vscode/mcp.json', '**/.cursor/mcp.json', '.mcp-vault.policy.json']
permissions:
  contents: read
jobs:
  mcp-vault:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1   # v7.0.1
        with:
          persist-credentials: false
      - uses: froggychips/mcp-skills-vault@<sha>   # v0.16.0 — the full 40-character commit SHA
        with:
          paths: '**/.mcp.json **/.vscode/mcp.json **/.cursor/mcp.json'

Replace <sha> with the full commit SHA of a release tag (git ls-remote https://github.com/froggychips/mcp-skills-vault refs/tags/v0.16.0). The action runs its own checkout — no npm install, no token, no network — so the SHA pins both the checker and the data it checks against; a tag or branch would let both change under you, and the action warns when it is not given a SHA. The job summary lists every decision; sarif: true puts findings on the config lines in code scanning. All inputs, outputs and the pre-commit hook: docs/GITHUB_ACTION.md.

What it prints

On examples/insecure/.mcp.json:

$ npx -y @froggychips/mcp-vault check examples/insecure/.mcp.json --as-of 2026-10-01T12:00:00Z
examples/insecure/.mcp.json
  (the config as a whole)
    ! untrusted content + private data + outward sink across aws, browser [flows/lethal-trifecta: warn]
      fix: keep browser and aws in separate profiles (different project configs or hosts), or drop the outward tools of browser
    ! browser alone reads untrusted content and can run destructive actions [flows/untrusted-destructive: warn]
      fix: require approval for browser's destructive tools (browser_drop, browser_evaluate, browser_run_code_unsafe), or scope them out (DB hint: --caps core)
  :3 browser
    ! this config launches @playwright/mcp@latest, a tag rather than a version: whatever is latest at each start runs [config/unpinned-launch: warn; so pin/missing: unknown]
      fix: pin it to the version the vault verified (playwright-mcp): "args": ["-y","@playwright/[email protected]"]
  :7 memory
    ! this config launches mcp-server-memmory without a version: whatever is latest at each start runs [config/unpinned-launch: warn; so pin/missing: unknown]
      fix: pin to an exact version (mcp-server-memmory@<x.y.z>)
    ! npm package mcp-server-memmory is not in the vault but looks like mcp-server-memory (npm @modelcontextprotocol/server-memory) — a doubled letter: mcp-server-memmory vs mcp-server-memory. Likely an impersonation of mcp-server-memory. [lookalike/doubled-letter: warn]
      fix: if you meant the vetted server: mcp-vault install mcp-server-memory; if this one is yours: --allow-lookalike mcp-server-memmory
  :11 postgres
    ! this config launches @modelcontextprotocol/server-postgres from a source other than the public registry: installs from another registry (--registry). [config/launch-source-override: unknown; so verify/unverified: unknown]
      fix: launch it from the public registry, or verify that source yourself
  :14 postgres
    ✗ postgres connection string with a password in plain text at mcpServers.postgres.env.DATABASE_URL (15 chars, …); the file is tracked by git, so the value is in history [secrets/connection-string: deny, fails]
      fix: replace it with mcpServers.postgres.env.DATABASE_URL: "postgres://app:${DATABASE_URL}@db.internal:5432/app" and set the value in the environment; it is in git history, so rotate it
  :16 aws
    ✗ availability: yanked (observed 2026-10-01) [finding/severity ← evidence/availability: deny, fails]
      fix: the vault recorded this against the exact release launched here: move to a release without it (mcp-vault explain mcp-server-aws)
    ✗ availability is yanked (as of 2026-10-01) [trust/availability: deny, fails]
      fix: the vault recorded this against the exact release launched here: move to a release without it (mcp-vault explain mcp-server-aws)

FAIL — 3 failing, 6 to look at · 4 servers in 1 config · fail on deny · offline

Exit 1. The password is never printed — only its type, location and length. aws is recognised as the vault's mcp-server-aws by what it launches (the key is only a label), and that exact release is recorded as yanked, so it fails. With --fail-on unknown, the Action's default, 6 fail; with --strict, all 9.

What it catches

| Check | Example finding | How to fix | |---|---|---| | Unpinned launch | npx -y @playwright/mcp@latest, a range like ^1.2, no version at all | pin the exact version; for a known server the fix line gives the verified one | | Plaintext secret | a token, API key, Bearer … or database password in env, args, headers or url | move it to the environment and reference it (${VAR}, ${env:VAR}, ${input:id} — the syntax your host documents) | | Typosquat / lookalike | mcp-server-memmory, a homoglyph, a swapped or dropped npm scope, an added -official | use the real package, or --allow-lookalike <name> if it is yours | | Package source override | --registry, --userconfig, a uv index, npm_config_registry in the server's env | launch from the public registry, or verify that source yourself | | Known server, bad release | a server the vault knows, launched on a release recorded as yanked or with an advisory against it (deny), or on a version the vault never verified (unknown); with --online, the registry's hash and live advisories too | move to the version the vault verified (mcp-vault upgrade computes the shortest one that clears the advisories) | | Tool poisoning | a tool description with hidden Unicode, ANSI escapes, "ignore previous instructions", credential paths — via mcp-vault tool-scan on a captured tools/list, or eval --installed --sandbox for your own servers. check applies stored scans; the eval snapshot shipped today carries none yet | drop or replace the server; report it upstream | | Toxic flows, shadowing | one config that reads untrusted content, reaches private data and can send outward; two servers exposing the same tool name | split servers into separate configs, or scope out the tools (the fix line names them) | | Organisation policy | a server outside your allowlist, a denied licence or capability, an unapproved tool change | add it to the allowlist, or mcp-vault approve <server> |

Every finding has a rule id (config/unpinned-launch, secrets/connection-string, …) and lands on the config line it is about, in text, JSON and SARIF.

Run it locally

npx -y @froggychips/mcp-vault check                      # .mcp.json, .vscode/mcp.json, .cursor/mcp.json here
npx -y @froggychips/mcp-vault check path/to/.mcp.json --strict

check reads exactly those files and nothing from your home directory, so the answer is the same on every machine. Offline by default; --online adds the live registries and advisory feeds. Servers are matched to the vault by what they launch; the config key is just a label. As a pre-commit hook:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/froggychips/mcp-skills-vault
    rev: <sha>   # v0.16.0 — `pre-commit autoupdate --freeze` writes it
    hooks:
      - id: mcp-vault

pre-commit installs the commit rev: names, which carries no release signature, so — as with the Action's SHA — the pin is the DB's integrity; the hook says DB integrity: pinned by pre-commit rev. Pin rev: to a full SHA.

Your own machine: status

npx -y @froggychips/mcp-vault status

One screen over every MCP host config on this machine — Claude Code (project and user), Claude Desktop, Cursor, VS Code, Codex: what is installed, which servers launch a version nobody verified, plaintext secrets, context cost, toxic flows. No network calls. mcp-vault secrets lists the plaintext credentials alone; mcp-vault audit is the long form. All commands.

Policy for organisations

A .mcp-vault.policy.json in the repository sets the bar once for CI and for everyone's shell ("unpinnedLaunch": "fail", "signatures": "require", licence deny lists, …). An organisation can keep one policy with an allowlist — default deny, by npm scope, GitHub owner, registry namespace, entry or pinned artifact — that projects inherit with "extends" and can only tighten, plus per-tool approval recorded in mcp.lock.json. An unknown key is an error, not a no-op. Policy and org policy.

How it decides

Every check produces findings with a state — observed, or not-run, no-data, stale — and one function, decide(), turns findings and the policy into a decision per subject: allow, warn, unknown or deny, with the rule that decided it. "Nobody looked" is unknown, never clean.

  • Exit codes: 0 nothing fails, 1 something fails, 2 could not answer (unreadable config or policy, bad arguments). 2 never means clean.
  • Threshold: check fails on deny by default; --fail-on unknown also fails what could not be checked; --strict (= --fail-on warn) fails warnings too.
  • Time is an input: stored evidence has a shelf life, so --as-of <date> replays a decision at a fixed instant. Same DB, policy, --as-of and version → same bytes: result = f(db, evidence, policy, asOf, rules_version).

How it decides · ADR 0001

The vault DB: extra signal for known servers

The checks above work on any config. For servers the vault knows — a curated DB of 112 known servers — it adds a reference point: the verified version and hash (npm sha512, PyPI sha256, Docker @sha256), recorded advisories, availability (yanked, unpublished), the npm registry signature, provenance bound to the artifact digest, and a behavioural smoke run. Each claim is dated and expires. check, status and explain apply it to what a config launches: a recorded problem (yanked, advisory) fails at any age; on a config line an aged-out "nothing found" is shown as context and does not change the exit code (why). A server the DB does not know is reported as not in the vault — unknown, not clean. The DB ships signed (Ed25519) in the npm package. What is in it.

What it is not

  • Not a sandbox and not a runtime monitor. It reads configs before anything runs; it does not watch what a server does. (mcp-trace is an experimental sister project for that.)
  • Weak on remote servers. An HTTP/SSE server ships no artifact, so there is nothing to pin or hash: it is checked for secrets in its URL and headers and for flows, and is otherwise reported as unknown.
  • Not proof a server is benign. A matching hash says you got the bytes that were reviewed, not that they are safe.
  • Not a catalogue of MCP servers. The DB is a supporting signal, not a directory to browse.

Status: 0.16, looking for first users

0.16.0 is the first release built around check, the Action and the pre-commit hook. The JSON schemas and the policy format may still change before 1.0 — COMPATIBILITY.md says what is stable and what is not.

An honest note on how it got here. The previous README said no new feature would ship until three people had used the tool. That did not hold: between 0.15.2 and 0.16.0, 31 feature commits landed — check, the Action, plaintext secrets, lookalikes, tool poisoning, toxic flows, org policy, the signed DB, one decide() — all before anyone outside the project had used it. The numbers do not show users yet: 698 npm downloads from 31 Aug to 29 Sep, about 80% of them on three release days and split almost evenly between 0.15.1 and 0.15.2 (mirrors and scanners, not people), and 5–10 a day in between; on GitHub over 14 days, 4 views, 0 stars, 0 issues from outside. The goal from here is first users, not features. If you run it on a real config, an issue saying what it found — or why you stopped — is the most useful thing you can send. docs/ADOPTION.md

Docs

License

MIT