The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Liveauth MCP listing page.
Authentication, pay-per-call metering, and signed receipts for AI agents and MCP tools: Bitcoin-native, Lightning-backed, and L402 compatible.
This MCP server lets any AI agent authenticate against your API using proof-of-work (free, no account) or Lightning Network micropayments (sats), then meter and monetize subsequent tool calls with per-call pricing, idempotent revenue events, and HMAC-signed receipts that auditors can verify offline.
Use it when you want to:
mcp-call-receipt-v1) for every paid tool invocation.Try it in 5 seconds — no account, no API key:
Without configuration, the server uses LiveAuth's anonymous demo project and the real PoW flow. Add LIVEAUTH_API_KEY only when you need a specific project's policy, pricing, or attribution.
| Tool | Purpose |
|---|---|
liveauth_mcp_start | Begin a session. Returns a PoW challenge, a Lightning invoice, or an L402 bundle hint. |
liveauth_mcp_confirm | Submit a solved PoW challenge, a paid Lightning invoice, or an L402 macaroon → receive a JWT. |
liveauth_mcp_charge | Meter usage after a call. With toolName, resolves registered tool pricing and records a paid revenue event. |
liveauth_mcp_refresh | Exchange a refresh token for a new JWT — no re-auth required. |
liveauth_mcp_status | Poll session/payment status (Lightning confirmation, expiry). |
liveauth_mcp_lnurl | Fetch the BOLT11 invoice for a session (lnget-compatible). |
liveauth_mcp_usage | Query remaining budget, calls used, and rate-limit windows. |
Full parameter and response schemas are in the Tool Reference below.
In an MCP client, call liveauth_mcp_start, then call liveauth_mcp_confirm with only the returned quoteId. The package reuses its existing PoW solver locally and the LiveAuth API verifies the signed challenge before issuing a short-lived session JWT.
claude_desktop_config.json:The package is also a TypeScript SDK — see SDK Usage below. The CLI bin is liveauth-mcp.
For API providers / tool developers:
createMcpGate) and you get per-tool revenue, per-tool min/max pricing, and idempotent retries.For AI agents / agent builders:
The math that matters: if your tool is being scraped by a bot, charging 1 sat per call is enough to make the scraper unprofitable. We call this cost-of-attack economics, and it's the whole reason we exist.
Or use directly with npx:
LiveAuth for Goose uses the same standards-based stdio MCP server as every other client—there is no Goose wrapper, daemon, or duplicate authentication runtime.
Or print the official deep link and current fallbacks:
For a one-off Goose CLI session:
Manual Goose stdio configuration, when the deep link is unavailable:
Do not edit an existing Goose config destructively. Prefer the deep link or goose configure; if you add project configuration later, enter it through Goose's extension secret settings rather than shared plaintext YAML.
Ask Goose:
Use LiveAuth to start the default authentication flow. Confirm the returned quote, then show my LiveAuth usage.
The initial flow uses the anonymous demo project's PoW challenge and does not require a wallet. A project public key is optional:
| Variable | When to set it |
|---|---|
LIVEAUTH_API_KEY | Project-specific policy, pricing, and attribution. |
LIVEAUTH_API_BASE | A self-hosted LiveAuth API instead of https://api.liveauth.app. |
LIVEAUTH_DEMO=true | Explicitly opt into the older locally simulated Lightning demo. |
When a paid flow is requested, tool results retain the existing invoice fields and also include portable structured data:
Clients with MCP Apps support can render the included QR, Open Wallet action, expiration, and live paid/pending/expired state. Other clients receive the JSON and QR image content as ordinary MCP results.
npx @liveauth-labs/mcp-server setup goose and use its one-session or manual fallback.npx is unavailable, install a current Node.js release (Node 18 or newer).liveauth_mcp_start again to obtain a fresh quote.LiveAuth lets agents acquire authorization at runtime instead of requiring every tool to be provisioned with permanent credentials in advance.
The package can also be imported as a TypeScript/JavaScript SDK. Importing the package does not start the stdio MCP server; the CLI lives at the liveauth-mcp bin.
The client stores confirmed JWTs, refreshes them before expiry when a refresh token is returned, and exposes the current token through liveauth.token. Call liveauth.destroy() when your app is shutting down to clear token state and refresh timers.
For PoW, config.publicKey is the credential sent in X-LW-Public. It may be either the project's primary public key or an active API public key belonging to that project. The API returns the canonical project key in session.powChallenge.projectPublicKey; the solver hashes that returned key, and confirmation still sends the configured credential. These two key strings can legitimately differ, so comparing them for equality is not a project-isolation check.
Use sessions from your trusted LiveAuth API endpoint. The server binds the quote and signed challenge to the resolved project and issues a JWT with projectId and authType. For diagnostics, compare the JWT's projectId with the expected project ID from your console, without logging the token. Decoding claims alone does not verify a JWT signature.
To require a real paid invoice:
gate.invoke(...) validates the JWT, charges the configured sats cost or the backend project default, and passes context.liveAuth into your handler. The older gate.gateTool(...) name is still supported.
If your MCP server has a registered LiveAuth tool ID, pass toolId when creating the gate. Charges then go to:
instead of the legacy generic endpoint:
You can also pass a registered tool slug/name as toolName. In that mode charges go to the generic endpoint with tool identity in the body:
Tool charges preserve the same session budget checks, but also record an immutable revenue event with gross sats, LiveAuth platform fee, developer net sats, tool method name, paying project/session/token, metadata, and idempotency key. When costSats is omitted, LiveAuthCore uses the registered tool's default price; without toolId or toolName, it falls back to the project's global MCP price.
Registered tools can also have a paid-call webhook URL. On every successful new paid call, LiveAuthCore queues a liveauth.mcp.tool.paid_call webhook with the tool identity, gross/platform/net sats, revenue event ID, metadata, and the signed receipt. If the tool webhook URL is blank, LiveAuthCore falls back to the project's webhook URL; idempotent retries do not enqueue duplicates.
When toolId or toolName is set, GateToolOptions supports:
| Option | Purpose |
|---|---|
costSats | Optional sats to charge for this call. Omit to use registered tool pricing or the project global price. |
toolName | Optional per-call tool slug/name override when using the generic endpoint. |
toolMethodName | Method within the tool, such as web_fetch or search. |
idempotencyKey | Retry-safe key. Reusing it for the same tool returns the original revenue event and signed receipt instead of double charging. |
agentId | Optional caller/agent identifier for reporting. |
metadata | Small JSON object for audit context. Do not store private tool output here. |
Tool charge responses include the normal budget counters plus revenue accounting:
The receipt is a signed per-call audit artifact returned by LiveAuthCore for paid tool charges. Store it with your tool result when you need proof of charge or later reconciliation.
If no toolId or toolName is configured, the SDK keeps using /api/mcp/charge for backward-compatible usage metering.
Add to your claude_desktop_config.json:
Credential-free mode: If you omit LIVEAUTH_API_KEY, the server calls the normal MCP endpoints without a project header. LiveAuth binds its configured anonymous demo project, returns a signed PoW challenge, and preserves normal verification, JWT, rate-limit, and metering boundaries. LIVEAUTH_DEMO=true remains an explicit opt-in to the older locally simulated Lightning preview.
Other env vars:
| Variable | Default | Purpose |
|---|---|---|
LIVEAUTH_API_KEY | (unset) | Your LiveAuth project public key (la_pk_…). |
LIVEAUTH_API_BASE | https://api.liveauth.app | Override for self-hosted LiveAuth. |
LIVEAUTH_DEMO | false | Explicitly use the legacy locally simulated Lightning demo. |
The server speaks stdio (JSON-RPC 2.0). Start it with:
It also works with any MCP-compatible client: Cursor, VS Code, ChatGPT, Windsurf, Continue, Cline.
Full schemas for each MCP tool. Each tool is JSON-RPC 2.0 compatible and tested under src/index.test.ts and src/cli.test.ts.
liveauth_mcp_startStart a new LiveAuth MCP session. Returns a PoW challenge by default, or a Lightning invoice if forceLightning=true.
Parameters:
forceLightning (boolean, optional): If true, request Lightning invoice instead of PoW challengeforceL402 (boolean, optional): If true, start a session that should be confirmed with an L402 bundle macaroonReturns (PoW):
Returns (Lightning):
Returns (L402 bundle):
liveauth_mcp_confirmSubmit a solved proof-of-work challenge, let the package solve its cached challenge, poll a Lightning payment, or present an L402 macaroon to receive a JWT authentication token.
Parameters:
quoteId (string): The quoteId from the start responsechallengeHex (string, optional, PoW only): The challenge hex from the start responsenonce (number, optional, PoW only): The nonce that solves the PoW challengehashHex (string, optional, PoW only): The resulting hash (sha256 of projectPublicKey:challengeHex:nonce)expiresAtUnix (number, optional, PoW only): Expiration timestamp from the challengedifficultyBits (number, optional, PoW only): Difficulty bits from the challengesignature (string, optional, PoW only): Signature from the challengemacaroon (string, L402 only): Bundle macaroon returned from the L402 bundle claim flowWhen the challenge came from this MCP server, calling confirm with quoteId alone reuses the package's existing PoW solver. Explicit solution fields remain supported for compatibility.
Returns:
Note: Store the refreshToken securely. It is returned in MCP tool data but never written to stderr or application logs. Use liveauth_mcp_refresh to get a new JWT without re-authenticating.
liveauth_mcp_chargeMeter API usage after making an authenticated call. The bundled MCP server calls the generic /api/mcp/charge endpoint. Supplying toolName lets LiveAuth resolve a registered tool, apply its configured price, and create a paid-tool revenue event; omitting toolName keeps backward-compatible generic metering.
Parameters:
callCostSats (number, optional): Cost of the API call in sats. Omit to use backend pricing.toolName (string, optional): Registered MCP tool slug/name for per-tool pricing and attribution.Returns:
If budget is exceeded:
liveauth_mcp_statusCheck the status of an MCP session. Use to poll for Lightning payment confirmation.
Parameters:
quoteId (string): The quoteId from the start responseReturns:
When paymentStatus is "paid", the session is confirmed. Call liveauth_mcp_confirm again to get the JWT.
liveauth_mcp_lnurlGet the Lightning invoice for a session (lnget-compatible). Use this to retrieve the BOLT11 invoice for payment with any Lightning wallet.
Parameters:
quoteId (string): The quoteId from the start responseReturns:
Note: This is compatible with lnget and other Lightning payment tools. Use this to poll for the invoice when liveauth_mcp_confirm returns "payment pending".
liveauth_mcp_usageQuery current usage and remaining budget without making a charge. Use this to check status before making API calls.
Parameters: (none required)
Returns:
liveauth_mcp_refreshRefresh the JWT token without re-authenticating. Use the refreshToken returned from confirm to get a new JWT when the current one expires.
Parameters:
refreshToken (string): The refreshToken from the confirm responseReturns:
Note: Save the refreshToken securely. You'll need it to extend the session without solving a new PoW or making another Lightning payment.
liveauth_mcp_start to get a PoW challenge and quoteIdliveauth_mcp_confirm with the quoteId; the MCP server solves its cached challenge with the existing package solverhash = sha256(projectPublicKey:challengeHex:nonce) where hash < targetHex)Authorization: Bearer <token> header for API requestsliveauth_mcp_charge with a call cost, or omit it to use the project global MCP pricecreateMcpGate({ toolId }) or createMcpGate({ toolName }) so each call creates a revenue event and signed receiptliveauth_mcp_start with forceLightning: true to get a Lightning invoiceliveauth_mcp_lnurl (or poll liveauth_mcp_status) to get the BOLT11 invoiceliveauth_mcp_status with the quoteId until paymentStatus is "paid"liveauth_mcp_confirm with just the quoteId to receive the JWTliveauth_mcp_charge metering or SDK paid-tool attributionPaid tool servers use the same JWT but charge through an attributed endpoint:
LiveAuthCore supports Lightning-backed L402 bundles for prepaid MCP access. Buy a bundle, claim the macaroon after payment, then start an MCP session in L402 mode and confirm it with that macaroon.
MIT
Categories: authentication · payments · lightning · l402 · bitcoin · pay-per-call · metering · agent-tools · anti-abuse · mcp-server · typescript
The gate validates the session, records the charge, then invokes the handler.
Authorization plus an accepted execution attempt is billable, including a handler
exception, timeout, or cancellation after charging. There is no automatic refund.
Input rejection before the gate and charge denials do not consume usage. A revenue
event with status Charged proves billing, not successful tool execution.
Register the tool and move its lifecycle from Draft to Active before serving paid
calls. Draft means unpublished (tool_unpublished); Paused or other non-active states
return tool_inactive. Public discovery also requires Visibility=Public, but
visibility is separate from lifecycle: active private/internal tools can be charged.
There is no separate publication flag or new visibility restriction in this change.
Unknown or removed tools return HTTP 404 with JSON status=deny,
reason=tool_not_found, and the supplied tool identity. Registered-tool lifecycle
and budget denials retain HTTP 200 with status=deny.
| Reason | Meaning |
|---|---|
tool_unpublished | Tool is Draft. |
tool_inactive | Tool is Paused or otherwise non-active. |
tool_not_found | No matching non-removed tool. |
budget_exceeded | Existing budget policy rejected the charge. |
rate_limited | SDK-supported structured rate denial; the current MCP charge controller does not emit this reason or enforce its per-minute setting. |
denied | SDK fallback when the denial has no reason. Unknown future reason codes remain available on the SDK error. |
gate.charge() returns structured denials with ok=false, including JSON HTTP
error responses with status=deny. gate.invoke() and gate.gateTool() throw
ChargeDeniedError with reason, code, toolName, and toolId. For compatibility
it extends BudgetExceededError (and LiveAuthMcpError); new handlers must inspect
reason rather than assume every instance means budget exhaustion. Unrelated HTTP
authentication, transport, and validation failures keep their existing error path.
Older backends may still return plain-text unknown-tool errors until upgraded.
On handler failure the gate throws ToolExecutionError with charge,
idempotencyKey, and a non-enumerable cause. Its public message is generic.
Expose an allowlist of charge fields: grossSats, revenueEventId, signed receipt,
and the idempotency key. Keep isError=true in the MCP response. Do not serialize
or log the error cause, JWT-bearing handler context, or arbitrary metadata.
Receipt payload/signature are existing public response artifacts and can be returned.
A successful charge can have no receipt; preserve this distinction rather than
inventing one. Billing metadata does not imply successful execution.
body.requestId: LiveAuth's server HTTP request/correlation identifier
for the original recorded charge. A retry returns that original receipt.body.idempotencyKey: caller-controlled stable retry key. Deduplication
is scoped to the paying project and registered tool, not the server request ID._meta.requestId: MCP/client correlation ID, taken from X-Request-Id
or generated by InvokeWorks. InvokeWorks intentionally also uses it as the
LiveAuth idempotency key.For example, _meta.requestId="client-123", receipt
body.idempotencyKey="client-123", and receipt body.requestId="server-456"
are valid together. The SDK accepts idempotencyKey; it does not send a separate
client request-ID option. Caller context { requestId } is local handler context.
Use a new key for a new logical call and reuse a key only for the same intended
operation. Deduplicated charging does not cache handler results: retries can execute
the handler again. Tool-state and price checks still precede deduplication.