
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
@vauban-pay/mcp
Advanced tools
Vauban Pay MCP server -- x402 payment tools for MCP clients.
Every payment leaves a receipt anyone can check without trusting us. Your agent makes its first real on-chain payment in about a minute (sponsored, Sepolia, no wallet), the receipt is a STARK anyone can verify offline against a fact registered on a public chain, and merchants are graded on their proven on-chain history, never on a declared counter.
Nothing here is private, and it is worth saying plainly: our proofs are transparent STARKs, not zero-knowledge ones. The amount, the currency, the timestamp and the payer are public inputs to the circuit. What the proof buys you is verifiability without trust, not confidentiality.
The plain x402 rail is served at https://pay.vauban.tech/x402-mainnet. Its
/supported endpoint answers the standard x402 v2 document, and /v1/quote
answers a 402 whose offer is exact on starknet:SN_MAIN for 0.5 STRK
(500000000000000000 atomic units of the STRK token
0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d), paid to
0x0386bc09b9ae44eb1475021db5c840d40d86bc49622946d2c006282fd81ad6bc, which is
also the feePayer: the facilitator pays the gas, you sign the transfer.
This server refuses that payment out of the box, on purpose, and three separate guards have to be opened by you before value can move. That is the point: a payment tool that pays anything a 402 names is a drain waiting to happen. Set these five variables to pay it:
| variable | why it exists |
|---|---|
PAY_MCP_STANDARD_X402_ACCOUNT_ADDRESS | your Starknet account. Yours, never ours. |
PAY_MCP_STANDARD_X402_PRIVATE_KEY | its key. Supply it through your own secrets handling; never commit it. |
STARKNET_RPC_URL | defaults to Sepolia. On mainnet you must set it, or the authorization is signed against the wrong chain id. |
PAY_MCP_STANDARD_X402_PAYEES | the payTo addresses this server may pay. Empty means none: it fails closed, so an unset install pays nobody. |
PAY_MCP_STANDARD_X402_MAX_WEI | cap for a single payment. Defaults to 0.1 STRK, which is below the 0.5 STRK offer above, so the default refuses it. Raise it deliberately. |
A refusal on any of these is the outcome unpayable, and it is a fact about
your configuration, never about the merchant.
The shielded rail and the batch proofs run on test networks only.
npx @vauban-pay/mcp
@vauban-org/pay-mcp@vauban-org/pay-mcp is the former name of this package. Every version
published under it stays on npmjs.org and stays installable, so nothing you
already installed breaks. @vauban-pay/mcp is the name to use from now on, and
the only one this README, the demo site and the MCP registry entry point at.
The move exists because npm resolves a registry per scope, never per
package. Four other @vauban-org packages live on GitHub Packages, so anyone
consuming both families could map the scope only one way and got, on the other,
a sincere 404 that reads exactly like this package does not exist.
@vauban-pay is our public scope: everything in it is on npmjs.org, and it
already carries @vauban-pay/substrate.
The MCP registry name does not change : it is still tech.vauban/pay-mcp,
a reverse-DNS namespace proven by a DNS record, unrelated to the npm name.
A 404 on @vauban-pay/mcp almost always means your client resolved the
@vauban-pay scope to a private registry (GitHub Packages, an internal proxy)
where this package is not hosted. It is published on npmjs.org. Ask the public
registry explicitly, for the scope and not just for the command:
npm view @vauban-pay/mcp version --@vauban-pay:registry=https://registry.npmjs.org
--registry alone is not enough : the scoped mapping wins over it. If that
command still fails, read the registry directly, which no client config can
distort: curl -s https://registry.npmjs.org/@vauban-pay%2fmcp.
Or as an MCP client entry (Claude Desktop, Claude Code, any MCP client):
{
"mcpServers": {
"vauban-pay": { "command": "npx", "args": ["-y", "@vauban-pay/mcp"] }
}
}
Listed in the official MCP Registry
as tech.vauban/pay-mcp ; a domain-verified name, not a platform account:
ownership is proven by an Ed25519 signature whose public key lives in a DNS
record at the apex of vauban.tech. Also on
Smithery.
Nothing of ours is required to run it: no account, no token, no Vauban endpoint.
jcs_hash and request_delegation_grant are offline. The two tools that read
Starknet use whichever node you point STARKNET_RPC_URL at, and the default is
a public one we do not operate.
There is a second entrypoint, dist/serve.js, which speaks HTTP and refuses
every call without a JWT issuer, audience and JWKS endpoint. That one is our own
Kubernetes deployment; it needs @vauban-org/auth, which is not on the public
registry, and it exits rather than serve unverified requests when that package
is missing. If you are reading this to try the tools, you want the stdio
entrypoint above.
After the client entry above, ask your agent to canonicalize and hash a JSON
object ; jcs_hash is fully offline, so this needs no keys, no RPC, no network:
Use the
jcs_hashtool on{"b": 2, "a": 1}and show me the digest.
You get back the RFC 8785 canonical form {"a":1,"b":2} and its SHA-256. The
point of the demo: two objects that differ only in key order hash to the same
digest ; that is the canonicalization every STARK receipt in this ecosystem is
built on. Change a value, and the digest changes; reorder keys, and it does not.
Then, still offline, request_delegation_grant builds and hashes a
bounded-spend authorization object without touching a chain. Only make_payment
in chain mode and verify_receipt in on-chain mode read Starknet, and only when
you give them an RPC ; see below.
Two entry points are declared, and only two:
import { ... } from '@vauban-pay/mcp'; // the server
import { classifyTransaction, fetchTransactionOutcome } from '@vauban-pay/mcp/starknet';
./starknet reads a Starknet transaction and classifies it. It verifies no
proof: a SUCCEEDED transaction carrying the expected payment event is a
settlement, not a STARK receipt. Checking a receipt is verify_receipt, which
is a different statement.
Anything else inside the package is an implementation detail and is now closed
by the exports field. Deep paths such as @vauban-pay/mcp/dist/... resolved
by accident before 0.6.1, only because no exports forbade them; they now fail
with ERR_PACKAGE_PATH_NOT_EXPORTED. If you need something that is not one of
the two entry points above, ask for it to be declared rather than reaching for
the file.
| Tool | Description |
|---|---|
jcs_hash | JCS (RFC 8785) canonicalize a JSON object and return its SHA-256 hex digest |
make_payment | Submit an x402 bounded-spend authorization; emits a real Starknet Sepolia tx when signer env vars are set |
verify_receipt | Verify a SettlementReceipt via JCS hash comparison (local mode) or by reading the PaymentReceiptEmitted on-chain event (chain mode) |
request_delegation_grant | Build and JCS-hash a DelegationGrant authorization object |
check_merchant | Check a merchant's proven service history (e.g. https://demo.pay.vauban.tech/x402/v1/history). Four verdicts, never conflated: PROVEN_SAMPLE, REFUTED (names the contradicting element), NO_RECORD (indeterminate, not negative), UNREACHABLE (a transport fact, not a merchant fact). An RPC outage degrades chain checks to structural level and says so |
pay_for_resource | The x402 client walk: GET a resource, and if it answers 402, download a STARK proof, gunzip it in memory, and POST it back to settle. Outcomes: no_payment_required (200 direct -- free, not a failure), paid (content + PAYMENT-RESPONSE receipt + manifest), refused (402 again, server's reason verbatim), unreachable (transport fact only). v0 limit: no public prover exists, so the proof submitted is always PRE-GENERATED and published by the merchant, never produced by this agent -- every paid result carries an honesty label saying exactly what the 200 does and does not prove. Standard x402 v2 (0.7.0): a foundation-format 402 with an exact/Starknet offer is paid by the configured account's own SNIP-9 signature instead (see below); fifth outcome unpayable |
| Variable | Required | Default | Description |
|---|---|---|---|
STARKNET_RPC_URL | No | https://starknet-sepolia-rpc.publicnode.com | Starknet Sepolia RPC. Point it at whichever node you trust; the default is a public one we do not operate, deliberately. |
PAY_MCP_SEPOLIA_ACCOUNT_ADDRESS | No | -- | Starknet Sepolia account address for chain mode. When absent, make_payment returns JCS-only (status: pending). |
PAY_MCP_SEPOLIA_PRIVATE_KEY | No | -- | Private key hex for that account. Never commit it; supply it through your own secrets handling. |
PAY_MCP_STANDARD_X402_PAYEES | No | empty (pays nobody) | Standard x402 path: comma-separated payTo addresses this server may pay, or * for any. Empty fails closed, on purpose: a server that paid any payee a 402 names would be a drain for whoever holds the key. |
PAY_MCP_STANDARD_X402_MAX_WEI | No | 100000000000000000 (0.1 STRK) | Standard x402 path: cap on one payment, in atomic units of the offer's asset. |
PAY_MCP_STANDARD_X402_MAX_PER_DAY | No | 100 | Standard x402 path: payments per UTC day (in-process counter; a restart resets it, which is why the two caps above exist too). |
PAY_MCP_STANDARD_X402_ACCOUNT_ADDRESS / PAY_MCP_STANDARD_X402_PRIVATE_KEY | No | falls back to PAY_MCP_SEPOLIA_* | Standard x402 path: a signer of its own, so a hosted server can pay standard 402s without flipping make_payment out of sponsored mode. |
The HTTP transport reads several more, all of them mandatory; it is not the entrypoint you want. See the Install section above.
pay_for_resource also pays the x402 foundation's own wire format. When a 402
carries the foundation's PaymentRequired document (standard base64 of JSON,
x402Version: 2) with an exact offer on starknet:SN_SEPOLIA or
starknet:SN_MAIN, the tool does not look for a proof: the configured account
signs a SNIP-9 outside execution for exactly that transfer (the client half of
@vauban-pay/x402-starknet),
the same GET is retried with PAYMENT-SIGNATURE, and the merchant's
facilitator settles it and pays the gas. Any merchant built with the
foundation's middleware and the merchant half of that package is payable this
way; so is https://demo.pay.vauban.tech/starknet/v1/quote.
What a paid outcome then carries: the content, the facilitator's receipt
(transaction, network, payer), standard{scheme, network, payTo, amount, asset, payer, feePayer}, and an honesty note: value moved from THIS agent's
account, no STARK proof was verified on this path, and the transaction is not
believed until it is read back from a Starknet node.
A fifth outcome, unpayable, is a fact about this server and never about the
merchant: no signer configured, payee not in PAY_MCP_STANDARD_X402_PAYEES
(empty fails closed), amount above PAY_MCP_STANDARD_X402_MAX_WEI, or no
Starknet offer among the accepts.
When PAY_MCP_SEPOLIA_ACCOUNT_ADDRESS and PAY_MCP_SEPOLIA_PRIVATE_KEY are both set:
make_payment builds the DelegationGrant object, JCS-hashes it, then calls emit_receipt on PaymentDemoEmitter at 0x044dd87a94a801cf775d4c5e4b6703102d4e97e1cd1d0a8879341219ae4f19ff.status: "submitted" with tx_hash, voyager_url, settlement_receipt, and receipt_jcs_hash.https://sepolia.voyager.online/tx/<hash>) can be opened to verify the PaymentReceiptEmitted event on-chain.The Sepolia account must hold testnet STRK to pay for transactions. Obtain from the StarkWare Sepolia faucet.
When signer env vars are absent (fallback / demo-grade mode):
make_payment returns status: "pending" with only the payment_hash and jcs_bytes_b64 fields.Local JCS mode -- provide receipt + expected_hash:
{
"receipt": { "type": "SettlementReceipt", "amount": 5000, "currency": "USDC" },
"expected_hash": "sha256:abcdef..."
}
Returns { valid, computed_hash, match }.
On-chain mode -- provide tx_hash:
{ "tx_hash": "0x<starknet_sepolia_tx_hash>" }
Reads the PaymentReceiptEmitted event from Starknet Sepolia, reconstructs the receipt object, and returns an audit-ready proof bundle including on_chain_event, reconstructed_receipt, computed_jcs_hash, and voyager_url.
Apache License 2.0. See LICENSE.
This package ships compiled output only. It implements the x402 payment wire format and STARK receipt verification described in the Vauban IETF Internet-Drafts; the specification is public and independently implementable.
FAQs
Vauban Pay MCP server -- x402 payment tools over StreamableHTTP
The npm package @vauban-pay/mcp receives a total of 142 weekly downloads. As such, @vauban-pay/mcp popularity was classified as not popular.
We found that @vauban-pay/mcp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.