Skip to content
On this pageSDKMCP server

Reference / Changelog

Changelog

What changed in each release of the two packages, newest first. Read every line marked Breaking before you upgrade: it can need a change in your code or change what your agent does. Each version is a link you can share.

SDK

@vulsight/guard on npm. Set it up with the SDK page.

Upgrade to the current release with one command. Then read the Breaking lines of every release you skipped.

Terminal
npm install @vulsight/guard@0.9.0

0.9.0

Added

  • Add owner-issued task selection with guard task options, startTask, and viaProxy task options.
  • Add repeat_merchant and task_purchase_cap review reasons with defaults of 300 seconds and 10 lifetime task purchases.
  • Clients may lower a task limit but cannot mint, renew, or raise it.

0.8.1

What changed

Changed

  • The package lists cerberus in its npm keywords.

Fixed

  • Combining guard().fetch directly with viaProxy from the same SDK copy now fails before any request or signature.
  • If an indirect wrapper is discovered after a signed request, the error warns to check the wallet and Decisions before retrying.
  • A missing or blank baseUrl gives setup guidance instead of a raw TypeError.

Security

  • viaProxy removes unsupported payment offers before signing, including Permit2 offers that could request an additional token approval.
  • Malformed and non-https seller URL errors no longer retain the URL or its proxy token.
  • Breaking A nonlocal baseUrl must use https. URL credentials, queries and fragments are refused during construction, before the API key is sent.

0.8.0

What changed

Added

  • viaProxy(fetch, proxyUrl) returns a fetch to pass to wrapFetchWithPayment in place of fetch. Every request then goes through the proxy URL, and the agent calls sellers by their own URLs.
  • viaProxy throws on a missing proxy URL, one that is not a proxy URL, and one that is not https (http is allowed on localhost). It refuses a seller URL that is not https or that carries a username or password, before anything is sent.
  • On a 402, viaProxy asks the seller for its own 402 directly and signs that copy. It throws ProxyTermsError, and signs nothing, when the proxy's payee or amount differs from the seller's. The agent must be able to reach the seller directly.
  • viaProxy throws when a paying fetch sits inside it, and names the right wrap order.

Changed

  • Breaking SDK errors now say Cerberus instead of VulSight Guard. Code that matches those sentences must update. The package homepage is https://cerberus.vulsight.com.
  • Breaking TypeScript change @vulsight/guard/types no longer exports Advisory or ContentDecision. The Recorded answer of reportContext drops two fields the server never sent.
  • decide under an idempotencyKey whose decision already has a settlement on file throws a GuardApiError with the 409 code decision_already_used. A new payment needs a new key.
  • A timeout says to check the guard's status at /api/v1/status if it keeps happening. Only enforce mode asks to read the page again and retry.
  • The package names VulSight as author and info@vulsight.com for bugs, and no longer links a repository.

Security

  • GuardClient reads the guard's rule sentences, agent names, and error messages as bounded single lines with no control characters. An error code it does not know reads as unknown.

0.7.0

What changed

Added

  • An agent set to observe on the guard gets one console.warn naming the decision enforce mode would have given, with its reasons.
  • DecisionRecord carries the agent's dashboard name as optional agentName on listed decisions.

Changed

  • reportSettlement finds a payment's decision through x402's onPaymentResponse hook, which guard() now registers. It no longer reads the signed PAYMENT-SIGNATURE header.
  • Breaking Pass reportSettlement the response wrapFetchWithPayment returned, and call guard() before you register any onPaymentResponse hook of your own.
  • Breaking For a paid request sent without wrapFetchWithPayment, pass its response to processPaymentResult on new x402HTTPClient(client) before reportSettlement.
  • An x402 client with no onPaymentResponse gets a warning and cannot report a settlement.
  • Breaking guard() also registers a payment policy, so Hookable requires registerPolicy. An x402Client from @x402/core 2.24 or later has it. A structural wrapper must expose it.
  • Breaking A second guard() on the same x402 client throws. Give each session its own x402 client.
  • Breaking observe mode lets a payment through only when the guard did not answer in time. That means it was unreachable, sent a 408 or a 5xx other than 503 payment_screening_unavailable, or ran past its time budget.
  • A rate limit, and a page sent for checking that the guard refused, now abort the payment in both modes.
  • A page whose read failed or was still arriving after 5 s now aborts the payment in both modes.
  • A 402 or decision the SDK cannot read, and a held payment whose review poll failed, now abort in both modes.
  • Breaking guard() throws when session is not 1 to 200 characters with no NUL character.
  • Breaking guard() and new GuardClient() throw when apiKey is missing or blank. 0.6.0 built and failed on each call.
  • Breaking X402Proposal and X402Payment refuse an unknown field, so a caller of vulsight.api.decide must drop fields the API does not take.
  • Breaking X402Proposal and X402Payment refuse a scheme that is not lowercase letters, digits, and hyphens. Send exact as the 402 writes it.
  • X402Proposal and X402Payment accept x402Version (2 only), assetTransferMethod, paymentFlow, and the 402's extra.
  • Breaking TypeScript change RuleId includes payment_method_supported; handle it in exhaustive maps and switches.
  • When a seller offers several options, the x402 client pays with one the guard supports instead of aborting on an unsupported first one.
  • When a seller offers no supported option, the payment hook refuses before signing, in both modes. It lists up to five of the options offered.
  • The payment hook refuses before signing, in both modes, a 402 that is not x402 version 2.
  • On Solana, the payment hook refuses before signing a transfer method other than the default.
  • The payment carries the 402's declared assetTransferMethod and paymentFlow to the guard.
  • X402_SUPPORT and paymentSupport(payment) from @vulsight/guard/types are the supported payments and their check. The API, the advisory MCP tool, and the proxy use the same.
  • A refusal of an unsupported payment names the payment's own network.
  • reportSettlement reads the older X-PAYMENT-RESPONSE header too, and reports each receipt once. A repeat returns the recorded settlement.
  • reportSettlement keeps up to 1000 receipts. It refuses a receipt two payments name, a response from the unsigned vulsight.fetch, and a payment the guard did not decide.
  • On a second 402, or a receipt whose success is false, reportSettlement names the seller's reason only for known x402 refusal codes. Anything else is an unknown outcome.
  • A reportSettlement error for a short-funded wallet says to wait until the signed payment expires. On EVM it expires when the 402 offer's time limit passes. On Solana it expires when its recent blockhash is no longer valid.
  • reportSettlement reports a used or expired authorization, a failed simulation, and settlement_pending as possibly settled.
  • A possibly settled error says to check the wallet's transfers to the payee on chain before paying again. It says an allowed or approved amount still counts toward the daily cap for the UTC day the guard checked it.
  • In observe mode, a payment's amount counts toward the cap only if enforce mode would have allowed or held it. A held amount counts until the review timeout passes.
  • reportSettlement errors that name the paid URL drop its query.
  • A payment waits only for pages from its own site, and for text sent for checking under a label that names no site. Another site's unchecked page blocks only payments to that site.
  • The review wait tries again after a poll the guard did not answer or rate limited. A refused or unreadable poll aborts at once and names the held decision to check before a retry.
  • A held payment prints one console.warn when the wait starts. It names the decision, the Review page at <baseUrl>/review, and how long the SDK waits.
  • A payment still held when the wait ends says to deny the open review on the Review page first, then run the payment again. The open review counts toward today's cap until it expires.
  • An expired review says to run the payment again and approve it before it expires, or to raise the Review timeout and waitForReviewSeconds.
  • A refused or failed last review poll links the decision at <baseUrl>/decisions/<id>.
  • Breaking The held payment sentences changed. Code that matched the old "Ask your user to review it in the VulSight Guard dashboard." sentence must update.
  • file() given undefined or null sends nothing for checking and answers pass.
  • file() given any other value that is not a string sends nothing, answers unavailable, and logs a warning.
  • A text sent for checking that the guard refused or still rate limits aborts the next payment it concerns, in both modes.
  • Breaking Every guard call refuses a base URL that redirects and names the address to set.
  • Every guard call sends an x-vulsight-client header with the SDK version, and a 408 counts as an outage.
  • An error for an answer that is not the guard's names the host and status. An answer this SDK cannot read says to update it.
  • README and Policy docs: the daily cap is one USDC budget per key across every network and lane.
  • README: how long the proxy holds an EVM payment for review, and what its review_pending and resign_required answers mean.
  • README: where to answer a held payment's review. It also says to give the HTTP client a read timeout of at least 60 seconds.
  • README: the proxy's Solana receipt statuses, and the 16 MiB daily ceiling on text sent for checking.
  • README: the vs_test_ prefix as only a key format, and the MCP tool as advisory, not enforced.
  • README: the sample reads its keys from the environment, pays the demo seller, and says how to run it.

Fixed

  • When a seller answers the signed retry with a redirect, the wrapped fetch returns it unfollowed, where it used to throw. It warns that the payment may have been made. A browser hides redirects, so there it throws that warning.
  • A page the guard passed is sent for checking again when it is read a minute or more later. That replaces a pass the guard no longer honors.
  • A cited excerpt is cut between characters, so a page with an emoji at its 2000th unit no longer fails the decision.
  • A payment still held when the wait ends mentions Approve and always allow this payee. It does so only when a first payment is the only reason for the hold.

Security

  • Breaking TxHash refuses the all-zero hash and SolanaSignature the all-zero signature, the unsigned placeholders.
  • The payment hook refuses, before signing, terms that are not one of the 402's offers.
  • The hook that runs after signing blocks a payment, in every mode, when its x402 version, offer, or resource URL changed after the check. It also blocks one whose 402 was replaced so no decision matches it.
  • reportSettlement refuses, as the proxy does, a receipt whose transaction is the payment's own EIP-3009 nonce, the buyer's own Solana signature, or an unsigned placeholder. The error never repeats the value.
  • On Base, reportSettlement records the signing wallet as the payer of a receipt that names none. It refuses one naming another payer, as the proxy does. A paid 402 can be reported too.
  • vulsight.fetch throws on a URL under the guard's own /p/ proxy path, and on a read redirected onto one. That way a proxied payment is not decided twice, and its proxy token stays out of decision records.

0.6.0

What changed

Added

  • The wrapped fetch sends every 402 for checking, the unpaid one and one answering a signed retry. It sends the text the proxy sends for it: the description in its terms, then its body.
  • The next payment waits for a 402's check, and cites the 402 when it was the last read from that origin.
  • file(text, origin) sends the text under the origin of an http(s) URL given as origin. Text read from a seller's page is then linked to that seller.

Changed

  • A text is posted once per origin rather than once per hash, so identical text two sellers serve is linked to each of them.
  • README: what is checked in a body, and which text sent for checking holds payments.

Fixed

  • A binary body up to 4 MiB sends the readable text inside it for checking, where it sent 64 KiB of replacement characters. A body is binary when its first 8 KiB hold a NUL byte.
  • A binary body over 4 MiB is still sent at the cap and stays unchecked, since the rest was never read.
  • A payment no longer cites a page with no text, such as an empty body or an image with no readable text. The guard read such a page as one it never received and held the payment for review.

0.5.0

What changed

Added

  • Breaking TypeScript change Policy includes required blocklistEnabled; add false to constructed values, or use Policy.parse(input) to apply the default.
  • Breaking TypeScript change RuleId includes wallet_blocklist; handle it in exhaustive maps and switches.

Changed

  • Allow more time for pending reads, sending context for checking, one retry, and the payment decision, matching the longer content check budget.
  • README: explain that completed content blocks deny payments and review results hold them when content enforcement is enabled.

Fixed

  • Keep each payment attempt's quote identity separate so reused quotes cannot mix decision identifiers.
  • Keep the content check's answer, a person's review clearance, and whether the check is enforced in context and decision responses.

Security

  • README: treat the proxy token as a secret, keep its URL out of history, logs, and screenshots, and rotate the key if it leaks.

0.4.1

What changed

Added

  • observed402PayTo and payer carry descriptions.

Changed

  • A page read that failed aborts the payment with the page's origin and what cut the read short.
  • reportSettlement names the header, the field, and the next step when a seller's receipt cannot be recorded, not a raw decoder or zod error.
  • reportSettlement refuses a receipt whose network is not the payment's before it posts.
  • A decision's sentences open with the rule that denied or held the payment and end with the notes.

Fixed

  • The review poll rounds the time left up to a whole second, so waitForReviewSeconds 1 polls once where 0.4.0 could abort without a poll.
  • A NUL byte in a page is sent as the replacement character, and a leading byte order mark is kept.
  • A page the guard refused blocks one payment rather than every later one.
  • The origin sent with a text is clipped to 200 characters.

0.4.0

What changed

Added

  • The payment hook sends an Idempotency-Key per payment attempt, so a retried call is decided once.
  • Solana Devnet and Solana Mainnet USDC beside Base Sepolia and Base, with base58 address checks.
  • holdOverAtomic on Policy and the amount_hold_over reason.
  • decisions.list pages with a before cursor.

Changed

  • A policy list holds at most 200 payees.

Security

  • Every request field carries its own sentence, and a resource URL with credentials is refused.

0.3.0

What changed

Added

  • The seller URL cap is the exported RESOURCE_URL_MAX, 2000 characters.

Changed

  • Abort reasons name the cause. A 200 that is not the guard's JSON names the host, and a 402 with no resource aborts instead of throwing.
  • A review still open at the wait budget keeps its rule sentences.

Security

  • A NUL byte in any field is refused at the boundary.

0.2.2

What changed

Changed

  • Inputs are bounded and every 422 is worded.
  • README: a text at the 64 KiB cap counts as unavailable, never a pass.

Fixed

  • Every page sent for checking is kept until the guard has taken it.

0.2.1

What changed

Changed

  • Longer payment verification deadlines.

0.2.0

What changed

Added

  • allowedResources on Policy: a payment for a resource outside the declared task is held.
  • Every tool result can be sent for checking, with a Claude Code hook.
  • BASE_MAINNET beside BASE_SEPOLIA; a payment on any other network is blocked before the guard is asked.

Changed

  • The default review wait is 120 seconds, the default policy's review timeout.
  • The excerpt sent for checking is the API's full 2000 characters, and a flagged page read again renews the hold.

Fixed

  • A rejected API key aborts with the key sentence instead of blaming the page.

0.1.2

What changed

Changed

  • "Seller" for the party the agent pays, in the sentences and the README.

0.1.1

What changed

Changed

  • @x402/core unpinned.
  • Error sentences name the host, the code, and the next step.

Fixed

  • The page sent for checking is clipped to 64 KiB of UTF-8, and the payment hook stays inside its 5 s budget.
  • Settlement reports are verified, deduplicated, and awaited.

0.1.0

What changed

Added

  • First publish: guard() with the x402 payment hook, the API client, and the shared types.

MCP server

@vulsight/guard-mcp on npm. Set it up with its setup page.

advisory

The MCP tool is advisory. The agent chooses whether to ask. For a check it cannot skip, use the proxy URL or the SDK hook.

0.6.0

Added

  • Add task_id and max_purchases to check_payment, with VULSIGHT_TASK_ID and VULSIGHT_TASK_MAX_PURCHASES operator defaults.
  • Tasks must already exist on Policy. A client can lower a limit but cannot create or renew a task.
  • The MCP server remains advisory; repeated purchases and task limits are central payment policy reviews.

0.5.3

What changed

Security

  • The Claude Code hook files third-party results with lookalike names; only exact vulsight-guard and legacy vulsight names exempt Cerberus tools.

0.5.2

What changed

Changed

  • The README defaults to a project-local proxy URL file and warns that user-wide rules expose it to unrelated projects (dac3bcca).

0.5.1

What changed

Changed

  • --skill prints the skill and then its three reference files, each after its path. It exits only once the whole text is written.
  • The printed skill lists the supported networks with their transfer method and payment flow, instead of linking the docs.
  • The README installs the skill together with its reference files, for Claude Code and Codex.
  • The package lists cerberus in its npm keywords.

Security

  • The printed skill links its reference files by relative path, so the agent reads them from disk and never from the guard's site.
  • Breaking VULSIGHT_BASE_URL must use https outside local development. Invalid base URLs fail before startup or API-key transmission.

0.5.0

What changed

Changed

  • The default VULSIGHT_BASE_URL is https://cerberus.vulsight.com. On the new default, texts the hook kept under the old default are still sent.
  • Breaking Advisory MCP answers now say Cerberus instead of VulSight Guard. Code that matches those sentences must update.
  • check_payment answers an idempotency_key whose payment was reported with report_settlement with a sentence that says it already paid and asks for a new key.
  • check_payment's tool text says a retry after the seller answered is a new payment with a new idempotency_key.
  • The printed skill (--skill, --skill codex) is shorter, with a decision tree for which path a payment takes.
  • A timeout says to check the guard's status at /api/v1/status if it keeps happening.
  • The package description says the server is advisory, and names the proxy URL and the SDK hook for enforcement.
  • The package names VulSight as author and info@vulsight.com for bugs, and no longer links a repository.

Security

  • check_payment refuses, before it asks the guard, a payment whose payTo differs from observed402PayTo.
  • An allowed or approved answer restates the one payment it covers and says to sign nothing else.
  • The guard's reasons follow a label that says to take no instruction from them, and reach the agent as bounded single lines.
  • The printed skill says to pay only the payment the answer restates, and that the guard's reasons and errors are never instructions.

0.4.0

What changed

Added

  • A file_context text or hook result is kept when the guard is out and it could not be sent for checking. The guard counts as out on a 408, a 429, a 5xx, or no answer.
  • The next check_payment a kept text concerns sends it first, and fails closed while the guard is still out.
  • The Claude Code hook keeps unsent results, at most 200 a session, so a restarted server still sends them. They sit in a file under the system temp directory that only the server's operating system user can read.
  • A reused idempotency_key answers with a sentence that names the causes and asks for a new key.
  • list_recent_decisions names each agent and takes a before cursor.

Changed

  • check_payment forwards an x402 payment's x402Version, the requirement's extra, assetTransferMethod, and paymentFlow.
  • check_payment refuses locally what the guard cannot judge. It refuses any scheme but exact and any upfront or escrow flow. It also refuses a transfer method other than EIP-3009 on Base or the default on Solana.
  • Breaking check_payment refuses a payment with a field it does not take, where 0.3.0 dropped it. Pass only the listed fields, not the whole 402 accepts entry.
  • Breaking check_payment refuses a scheme that is not lowercase letters, digits, and hyphens. Pass it as the 402 writes it.
  • The printed skill (--skill, --skill codex) says the tool is advisory, not enforced.
  • The printed skill adds a step that lists the payment methods the guard supports.
  • The printed skill says when to skip the tool for a payment sent through the proxy URL.
  • The printed skill says which terms and receipts to report.
  • The printed skill says the vs_test_ prefix is only a key format, not test money.
  • The printed skill asks for the proxy URL shown with the user's API key when it needs one. It does not pay until it has it.
  • The printed skill tells the user to install the server with --scope user.
  • The printed skill says a 409 resign_required after a content wait means nothing was sent. It says to request the URL through the proxy again and sign the fresh 402.
  • A flagged page sent for checking under its URL origin holds that seller in every lane, the proxy included.
  • check_payment sends kept texts for checking one at a time, within its own time budget, and stops on a cancel between them.
  • A check_payment that runs out of time fails closed, and the next one sends the rest.
  • A refused kept text is reported as one kept from earlier, not one sent with this payment.
  • A page's kept text tells the agent to send it again with file_context before a payment through the proxy URL.
  • check_payment's held and expired answers name the Review page at <baseUrl>/review.
  • A held answer says to repeat check_payment with the same payment, context, seller_402, and idempotency_key when nothing new was read. Otherwise, or with no key, have the review denied, send any new content with file_context, and check again.
  • An expired answer says to check again with a new idempotency_key and approve before it expires, or to raise the Review timeout and wait_seconds.
  • A hold whose answer could not be read links <baseUrl>/decisions/<id>.
  • report_settlement and the printed skill say a receipt whose success is false may still have settled. file_context says so too.
  • report_settlement and the printed skill say the seller can use the signed payment until it expires. On EVM it expires when the 402 offer's time limit passes. On Solana it expires when its recent blockhash is no longer valid.
  • report_settlement and the printed skill say to check the wallet's transfers to the payee on chain before paying again.
  • report_settlement and the printed skill say an allowed or approved amount still counts toward the daily cap. It counts for the UTC day the guard checked it.
  • In observe mode, a payment's amount counts toward the cap only if enforce mode would have allowed or held it. A held amount counts until the review timeout passes.
  • get_policy prints limits and thresholds in USDC instead of the asset's smallest unit.
  • get_policy says an allowed payee skips the wallet blocklist and the guard's issuer freeze check. The token contract still refuses a transfer to a frozen account.
  • A failed, rate-limited, or unreadable review poll is tried again until wait_seconds runs out. A wait whose every poll failed says the answer could not be read.
  • Breaking Guard calls refuse a missing key and a base URL that redirects; set the key and the final base URL.
  • Guard calls send x-vulsight-client and treat a 408 as an outage.
  • The daily cap is one budget per key across networks, and get_policy says so.
  • README: install with --scope user, and how to get a lost proxy URL. The guard's tools do not pay, since the agent brings its own x402 payment tool and wallet.
  • Breaking Node 20.3 or later.

Fixed

  • A cancelled check_payment stops before it decides. A response the guard refuses is told to the report_settlement call that sent it, not sent again in another client's check.
  • The Claude Code hook reports a missing key or a failed send for checking to the agent. A bare unknown command prints usage and exits 1.
  • An excerpt cut inside a character is sent whole, and text with nothing to read counts as a pass.

Security

  • report_settlement refuses an all-zero txHash or Solana signature, the unsigned placeholders, before it asks the guard, as the proxy does.

0.3.0

What changed

Added

  • check_payment takes seller_402, the seller's 402 answer, and sends it for checking before deciding, like context; the decision cites context when both are given.
  • report_settlement takes response, the body of the paid answer. It sends the body for checking under the paid seller's origin, and the next check_payment waits for it.
  • If report_settlement cannot send response for checking, the settlement stays recorded. The next check_payment sends it first and fails closed while the guard is out.
  • A paid response the guard refuses fails one check and is then dropped, as in the SDK.

Changed

  • file_context and the skill ask for a web page's origin as a URL in origin, so a flagged page holds only payments to that seller. Any other label, such as a bare host or a tool's name, still holds every payment in the session.
  • file_context and the skill say to send for checking a paid answer that has no receipt to report.
  • The hook sends a tool result fetched from an http(s) url for checking under that url's origin, and anything else under the tool's name.

Fixed

  • An empty response records the settlement and sends nothing for checking.

Security

  • A retry that reuses idempotency_key with a changed seller_402 is decided again instead of replaying the earlier answer.

0.2.8

What changed

Fixed

  • Return 404 for malformed request paths without stopping the server.

Security

  • Reject browser requests from foreign origins on the local HTTP endpoint.

0.2.7

What changed

Added

  • check_payment asks for observed402PayTo, the payee the seller's own 402 named, so a redirected payee is denied, and its schema carries the field descriptions.

Changed

  • The dashboard step follows every pending review, with or without a wait.
  • The denying sentence comes before the notes in every answer.
  • README: a NUL is sent as the replacement character, a leading byte order mark is kept, and the stored excerpt carries the flagged passages.

Fixed

  • The hook command sends a tool result as reportContext clips it, and a review poll with under a second left still polls for one.
  • A NUL in a check_payment context posts as the replacement character.

0.2.6

What changed

Added

  • check_payment takes an idempotency_key, so a retried call is decided once.
  • Solana Devnet and Solana Mainnet USDC beside Base Sepolia and Base.
  • The policy read names the hold threshold.

Fixed

  • The review poll's remaining wait is clamped at zero, ending the negative timeout warning.

0.2.5

What changed

Added

  • --help on the CLI, and abort reasons that name the cause.

Fixed

  • A session's texts are sent for checking in one chain, and a payment waits for it to drain. So a page sent with the payment reaches the guard before it decides.

0.2.4

What changed

Fixed

  • A text at the 64 KiB cap counts as unavailable, never a pass.

0.2.3

What changed

Changed

  • README claims corrected before the release.

0.2.2

What changed

Changed

  • Longer payment verification deadlines.
  • README: any MCP client can take the server, which is advisory.

0.2.1

What changed

Changed

  • The default review wait is 120 seconds like the SDK, and the documented Codex config carries the tool timeout.

0.2.0

What changed

Added

  • A payment for a resource outside the declared task is held.
  • file_context sends every tool result for checking, and a Claude Code hook does it for that client.
  • Base beside Base Sepolia as a policy network.

Changed

  • The excerpt sent for checking is the API's full 2000 characters.

0.1.3

What changed

Changed

  • "Seller" for the party the agent pays.

0.1.2

What changed

Changed

  • Version bump beside sdk 0.1.1, which unpinned @x402/core.

0.1.1

What changed

Added

  • --skill prints the bundled Claude Code skill, --skill codex the Codex snippet.

0.1.0

What changed

Added

  • First publish: the advisory MCP server with check_payment and get_policy, and the bundled skill.