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

@vulsight/guard-mcp

v0.3.0

Published

MCP server that asks VulSight Guard before an agent pays.

Downloads

2,179

Readme

@vulsight/guard-mcp

An MCP server that gives an agent in Claude Code, Codex, or any other MCP client a check_payment tool: ask VulSight Guard before paying, stop on deny, and quote the reason to the user. This tool is cooperative. The agent has to call it, and an agent that skips the call can still pay. For a check the agent cannot skip, route its x402 traffic through the guard proxy: the seller URL becomes https://vulsight-guard.vercel.app/p/<proxy token>/merchant.example/dataset (the seller's own scheme is left out, https is implied) and every payment is checked before it is forwarded. The payment hook in the @vulsight/guard SDK is enforced the same way for agents that use it.

Install

Claude Code:

claude mcp add vulsight-guard -e VULSIGHT_API_KEY=vs_test_... -- npx -y @vulsight/guard-mcp

Codex, in ~/.codex/config.toml:

[mcp_servers.vulsight-guard]
command = "npx"
args = ["-y", "@vulsight/guard-mcp"]
env = { "VULSIGHT_API_KEY" = "vs_test_..." }
tool_timeout_sec = 450

The last line leaves room for context filing, the decision, and the default two-minute review wait. Guard API calls have a 105-second deadline each; review polls add the requested poll duration. Increase the client limit further if you raise wait_seconds above its default. Claude Code needs no such line.

Any other MCP client (Cursor, Windsurf, Cline, VS Code, Zed, or your own agent on an MCP SDK) takes the same server through its own config, most of them in this JSON shape (VS Code keys it servers, Zed context_servers):

{
  "mcpServers": {
    "vulsight-guard": {
      "command": "npx",
      "args": ["-y", "@vulsight/guard-mcp"],
      "env": { "VULSIGHT_API_KEY": "vs_test_..." }
    }
  }
}

If the client caps how long a tool call may run, raise that cap to at least 450 seconds or pass a shorter wait_seconds on check_payment. Paste the skill text (npx -y @vulsight/guard-mcp --skill) into the client's rules or instructions file so the agent knows to call the tool before it pays. Agents built on a framework rather than an MCP client (the Vercel AI SDK, LangChain, the OpenAI Agents SDK) use the @vulsight/guard SDK instead, so the check runs in code rather than waiting for the agent to call a tool.

Then give the agent the skill that tells it when to call the tool. The package prints it. Run the lines for your tool once:

# Claude Code
mkdir -p ~/.claude/skills/vulsight-guard
npx -y @vulsight/guard-mcp --skill > ~/.claude/skills/vulsight-guard/SKILL.md

# Codex, once, in the project the agent works in
npx -y @vulsight/guard-mcp --skill codex >> AGENTS.md

npx -y @vulsight/guard-mcp downloads and runs the server. The package bundles the @vulsight/guard SDK, so there is nothing else to install. --version prints the version.

Claude Code can also file every tool result on its own, so the agent does not have to remember. The package prints the hook settings:

npx -y @vulsight/guard-mcp --hook

npx -y @vulsight/guard-mcp --hook prints one JSON object; add its hooks key to ~/.claude/settings.json (or the project's .claude/settings.json). It runs npx -y @vulsight/guard-mcp file-tool-result after every tool call, async so the tool call does not wait on it. The command reads Claude Code's hook payload on stdin, posts the tool's result under the page's origin when the tool was given an http(s) url (WebFetch, for example) and under the tool's name otherwise, skips the guard's own tools (the server must be installed under a name containing vulsight, as the install line does, for the skip to apply), AskUserQuestion, and empty results, and exits 0 on any runtime failure, so a guard that is down never stops the agent (a failure is one line on stderr, in Claude Code's debug log; only a missing key exits 1, a non-blocking notice, so an unconfigured hook is not silent forever). It reads VULSIGHT_API_KEY and VULSIGHT_BASE_URL from the environment Claude Code was started in, so export them in that shell. The hook and the server derive one session from the Claude Code project directory (both are given it), so a page the hook flags is considered by the next check_payment: only payments to that page's seller when the hook filed it under the page's origin, and every payment when it filed it under the tool's name. Because it runs in the background, a filing can still be in flight when the agent calls check_payment a moment later and land after the decision; the post has 105 seconds to finish within the hook's 120-second timeout. The server can still finish a filing after its client disconnects. Treat the hook as background defense: before paying, the agent files the content it is about to act on with file_context, as the skill says, and the declared payment and the rules carry the enforcement. Codex has no equivalent hook; there the agent calls file_context.

The hook files every tool result, so a secret a tool prints (a .env read, printenv) is sent to the guard. The complete submitted text and check results are kept privately until account deletion. An enabled shadow comparison sends the same text to its background service and keeps its result and highlighted passages with that history. The hook and the server must use the same API key: the content result is matched by key and session, so a filing under another key does not affect a check_payment. The hook therefore shares that key's per-minute rate limit, and a very chatty tool loop can hit the ceiling, after which the hook drops filings, one line on stderr each, until the minute passes. Your own messages are never filed: you are the principal, and the check is for untrusted content.

Environment

| Variable | Required | What it is | | --- | --- | --- | | VULSIGHT_API_KEY | yes | An API key from your VulSight Guard dashboard. The server never prints it. | | VULSIGHT_BASE_URL | no | Where VulSight Guard runs. Defaults to https://vulsight-guard.vercel.app. | | VULSIGHT_SESSION | no | Pins the guard session that every filing and check_payment carries. Unset, the session is one per Claude Code project directory, else one per server process. |

Tools

Every result is plain sentences the agent can quote to its user. A rejected input or a failure to reach a verdict is a short plain-text answer, flagged as an error, never a list and never a stack trace.

  • check_payment takes the payment as the seller's 402 states it (kind: "x402_payment" with payTo, amountAtomic, asset, network, scheme, resourceUrl, and observed402PayTo, the payee the seller's own 402 named, so a redirected payee is denied) or the EVM contract call about to be signed (kind: "evm_call" with to, value, data, network), an optional context (the text of the page or tool result that led to the payment, clipped to 64 KiB, and the guard files a clipped text as unchecked since it never read the rest), an optional seller_402 (the seller's 402 answer as the agent received it, its body and the resource description from its payment terms, filed the same way; the decision cites context when both are given), wait_seconds (default 120, at most 300) to wait for a human reviewer, and an optional idempotency_key, your key for this payment attempt (1 to 200 characters): send the same key again when you retry a call that timed out, so the guard decides the payment once, unless seller_402 changed; without one, a repeated call is decided again. It answers with the verdict, the reasons, and the decision id. Amounts are decimal strings in the asset's smallest unit and networks are CAIP-2 ids: eip155:84532 is Base Sepolia and eip155:8453 is Base. It also knows Solana Devnet (solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1) and Solana Mainnet (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp). Solana wallet and mint addresses are case-sensitive, and settlement txHash values are base58 transaction signatures.
  • report_settlement records a payment the guard allowed once it settles, so the ledger shows it: decisionId from check_payment, txHash (the header's transaction), payer, and network from the PAYMENT-RESPONSE header of the paid response, payee, amountAtomic, and asset from the 402 requirement the agent accepted, and direction sent. An optional response, the body of the answer to the paid request, is filed under the seller that decision paid, beside the settlement, so the next check_payment waits for it, and the answer adds what the check found. A server with no record of the decision (one restarted since the check) files it without its seller and says so.
  • get_policy reads the policy the guard applies to this agent: networks, assets, payee lists, limits, and the review timeout.
  • list_recent_decisions lists the account's newest decisions, limit from 1 to 50.
  • file_context files text the agent read that did not go through check_payment's context (a page, a file, a search result, another tool's output): text, and an origin: for a web page its origin as a URL (https://seller.example), so a flagged page holds only payments to that seller, else a label such as the tool's name (default tool); any other label, a bare host or a tool's name, holds every payment in the session. The answer to a paid request goes here too, with the seller's origin, when there is no receipt to report, for example when the seller answered it with an error or another 402. It answers in a sentence or two: flagged, clear (no evidence found, not proof the text is safe), or unchecked when the content check did not finish or the text was over 64 KiB, in which case only its start was filed and the answer says so. A NUL byte is filed as the replacement character and counts three bytes toward the 64 KiB cap, and a leading byte order mark is kept, so a text opening with one has a new hash and is scanned once more.

Text filed through context, seller_402, response, or file_context belongs to one session: the value of VULSIGHT_SESSION when it is set, else one per Claude Code project directory (the session the hook uses too), else one per server process. A flagged text affects later payments in that session for an hour: only payments to its seller when it was filed under a site (a page's origin as a URL), and every payment when it was filed under any other label, a bare host or a tool's name. With content enforcement enabled, a completed content block denies the payment even when its amount and seller are allowed; a check requiring review holds it for a person. A session that files faster than its key's per-minute limit (or the account's, across every key) gets a 429, which file_context reports as an error and the hook drops with one line on stderr. A file_context filing or a hook filing never blocks the agent. A context or seller_402 filing that fails inside check_payment fails the check closed, and the answer says do not pay. A response filing that fails does not stop the settlement: the answer says the response was not checked, and the next check_payment files it again before it decides. While the guard is out (a 429, a 5xx, or no answer) that check fails closed, saying do not pay; a response the guard refuses fails one check with the refusal and is then dropped.

Contract support

MCP accepts only exact x402 proposals. Its verdict covers the supplied payment terms, not omitted execution extensions or supplementary permit and approval signatures. Those need their own supported checks; a payment approval is not general signing permission.

The evm_call proposal supports canonical USDC transfers and bounded allowance grants on the native USDC contracts on Base and Base Sepolia. Unknown functions, arbitrary token contracts, extra calldata, unsupported execution-envelope fields and dangerous approval forms are denied, including in observe mode. Old approvals are checked against the same support limits before reuse. An allowlisted address does not grant permission to call any function on it.

Zero grants still undergo asset and spender checks. Finite grants can be spent later by the approved spender; Guard does not track total on-chain allowance or control that later execution. NFT operations, swaps, batches and arbitrary Solana programs are not covered. MCP checks a proposal and returns a verdict. Your wallet integration must enforce that verdict on the exact action it signs; another signing tool can bypass MCP.

Transports

stdio by default. --http <port> serves streamable HTTP at http://127.0.0.1:<port>/mcp for clients that connect over HTTP. Each request is stateless and shares the process's one session, and a request whose Host header is not 127.0.0.1:<port> or localhost:<port> is refused with 403:

VULSIGHT_API_KEY=vs_test_... npx -y @vulsight/guard-mcp --http 3333

Check it

bun test packages/mcp