{
  "markdown": "# LiveAuth MCP Server\n\n[![npm version](https://img.shields.io/npm/v/@liveauth-labs/mcp-server.svg)](https://www.npmjs.com/package/@liveauth-labs/mcp-server) [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![L402](https://img.shields.io/badge/auth-L402-F7931A.svg)](#l402-bundle-flow) [![MCP](https://img.shields.io/badge/protocol-MCP-7C3AED.svg)](https://modelcontextprotocol.io)\n\n> **Authentication, pay-per-call metering, and signed receipts for AI agents and MCP tools: Bitcoin-native, Lightning-backed, and L402 compatible.**\n\nThis 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.\n\n**Use it when you want to:**\n- Gate an API or MCP tool behind real cost-of-compute or real sats (anti-spam by design, not by CAPTCHA).\n- Charge AI agents per call without signing them up for an account.\n- Issue a tamper-evident audit trail (signed `mcp-call-receipt-v1`) for every paid tool invocation.\n- Offer Lightning-backed L402 bundle access for prepaid MCP sessions.\n\n**Try it in 5 seconds — no account, no API key:**\n\n```bash\nnpx @liveauth-labs/mcp-server\n```\n\nWithout 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.\n\n---\n\n## Available Tools (Glama / MCP auto-discovered)\n\n| Tool | Purpose |\n|---|---|\n| `liveauth_mcp_start` | Begin a session. Returns a PoW challenge, a Lightning invoice, or an L402 bundle hint. |\n| `liveauth_mcp_confirm` | Submit a solved PoW challenge, a paid Lightning invoice, or an L402 macaroon → receive a JWT. |\n| `liveauth_mcp_charge` | Meter usage after a call. With `toolName`, resolves registered tool pricing and records a paid revenue event. |\n| `liveauth_mcp_refresh` | Exchange a refresh token for a new JWT — no re-auth required. |\n| `liveauth_mcp_status` | Poll session/payment status (Lightning confirmation, expiry). |\n| `liveauth_mcp_lnurl` | Fetch the BOLT11 invoice for a session (lnget-compatible). |\n| `liveauth_mcp_usage` | Query remaining budget, calls used, and rate-limit windows. |\n\nFull parameter and response schemas are in the [Tool Reference](#tool-reference) below.\n\n---\n\n## 5-Minute Quick Start\n\n### Option 1 — Credential-free PoW (no account, no key, no wallet)\n\n```bash\nnpx @liveauth-labs/mcp-server\n```\n\nIn 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.\n\n### Option 2 — Production Mode\n\n1. Grab an API key at [liveauth.app](https://liveauth.app).\n2. Add to Claude Desktop's `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"liveauth\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@liveauth-labs/mcp-server\"],\n      \"env\": {\n        \"LIVEAUTH_API_BASE\": \"https://api.liveauth.app\",\n        \"LIVEAUTH_API_KEY\": \"la_pk_your_public_key\"\n      }\n    }\n  }\n}\n```\n\n3. Restart Claude. Done.\n\n### Option 3 — Programmatic (CLI / SDK)\n\n```bash\nexport LIVEAUTH_API_KEY=la_pk_xxx\nnpx @liveauth-labs/mcp-server\n```\n\nThe package is also a TypeScript SDK — see [SDK Usage](#sdk-usage) below. The CLI bin is `liveauth-mcp`.\n\n## Why LiveAuth?\n\n**For API providers / tool developers:**\n- Stop bots at the protocol layer. PoW and Lightning sats are non-replayable, non-phishable, and don't require user accounts.\n- Charge per call in sats. We sign a receipt you can show auditors, your customers, or your accountant.\n- Wrap any MCP tool with one line (`createMcpGate`) and you get per-tool revenue, per-tool min/max pricing, and idempotent retries.\n\n**For AI agents / agent builders:**\n- Permissionless access to paid APIs — solve a PoW or pay sats, get a JWT. No signup, no email, no OAuth dance.\n- Use PoW, Lightning invoices, or L402 bundle macaroons for agent access.\n- Projects can settle through a custom Lightning node when configured; otherwise payments use the LiveAuthCore-configured node.\n\n**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.\n\n## Installation\n\n```bash\nnpm install -g @liveauth-labs/mcp-server\n```\n\nOr use directly with npx:\n\n```bash\nnpx @liveauth-labs/mcp-server\n```\n\n## Goose\n\nLiveAuth for Goose uses the same standards-based stdio MCP server as every other client—there is no Goose wrapper, daemon, or duplicate authentication runtime.\n\n[Install in Goose](goose://extension?cmd=npx&arg=-y&arg=%40liveauth-labs%2Fmcp-server&timeout=300&id=liveauth&name=LiveAuth&description=Give+Goose+agents+authenticated+access+to+metered+and+paid+capabilities+through+LiveAuth.)\n\nOr print the official deep link and current fallbacks:\n\n```bash\nnpx @liveauth-labs/mcp-server setup goose\n```\n\nFor a one-off Goose CLI session:\n\n```bash\ngoose session --with-extension \"liveauth:npx -y @liveauth-labs/mcp-server\"\n```\n\nManual Goose stdio configuration, when the deep link is unavailable:\n\n```yaml\nextensions:\n  liveauth:\n    type: stdio\n    name: LiveAuth\n    enabled: true\n    cmd: npx\n    args: [\"-y\", \"@liveauth-labs/mcp-server\"]\n    env_keys: []\n    envs: {}\n    timeout: 300\n```\n\nDo 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.\n\n### Goose quick test\n\nAsk Goose:\n\n> Use LiveAuth to start the default authentication flow. Confirm the returned quote, then show my LiveAuth usage.\n\nThe initial flow uses the anonymous demo project's PoW challenge and does not require a wallet. A project public key is optional:\n\n| Variable | When to set it |\n|---|---|\n| `LIVEAUTH_API_KEY` | Project-specific policy, pricing, and attribution. |\n| `LIVEAUTH_API_BASE` | A self-hosted LiveAuth API instead of `https://api.liveauth.app`. |\n| `LIVEAUTH_DEMO=true` | Explicitly opt into the older locally simulated Lightning demo. |\n\nWhen a paid flow is requested, tool results retain the existing invoice fields and also include portable structured data:\n\n```json\n{\n  \"lightning\": {\n    \"invoice\": \"lnbc...\",\n    \"lightningUri\": \"lightning:lnbc...\",\n    \"amountSats\": 21,\n    \"expiresAt\": \"2030-03-17T17:46:40.000Z\",\n    \"status\": \"pending\"\n  }\n}\n```\n\nClients 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.\n\n### Goose troubleshooting\n\n- If the link does not open, run `npx @liveauth-labs/mcp-server setup goose` and use its one-session or manual fallback.\n- If `npx` is unavailable, install a current Node.js release (Node 18 or newer).\n- If a supplied project key is rejected, remove it to verify the anonymous PoW flow; invalid and revoked keys intentionally do not fall back to demo.\n- If a Lightning invoice expires, call `liveauth_mcp_start` again to obtain a fresh quote.\n- Keep refresh tokens and any non-public credentials out of logs and plaintext configuration.\n\nLiveAuth lets agents acquire authorization at runtime instead of requiring every tool to be provisioned with permanent credentials in advance.\n\n## SDK Usage\n\nThe 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.\n\n### Client Auth Helper\n\n```ts\nimport { createMcpClient } from '@liveauth-labs/mcp-server';\n\nconst liveauth = createMcpClient({\n  publicKey: 'la_pk_xxx',\n  baseUrl: 'https://api.liveauth.app',\n  onInvoice(invoice) {\n    // Render invoice.bolt11 as a QR code for a paid Lightning test.\n    console.log(invoice.bolt11);\n  },\n});\n\nconst session = await liveauth.start();\nconst token = await liveauth.confirm(session);\n\nconsole.log(token.jwt);\n```\n\nThe 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.\n\nFor 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.\n\nUse 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.\n\nTo require a real paid invoice:\n\n```ts\nconst session = await liveauth.start({ forceLightning: true });\nconsole.log(session.invoice?.bolt11);\n\n// Poll this after the invoice is paid.\nconst token = await liveauth.confirmLightning(session);\n```\n\n### Server Gate Helper\n\n```ts\nimport { createMcpGate } from '@liveauth-labs/mcp-server';\n\nconst gate = createMcpGate({\n  publicKey: 'la_pk_xxx',\n  baseUrl: 'https://api.liveauth.app',\n});\n\nconst result = await gate.invoke(\n  jwtFromYourTransport,\n  { message: 'hello' },\n  async (input, context) => ({\n    content: [{ type: 'text', text: input.message }],\n    charge: context.liveAuth.charge,\n  }),\n  {}\n);\n```\n\n`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.\n\n### Paid Tool Attribution\n\nIf your MCP server has a registered LiveAuth tool ID, pass `toolId` when creating the gate. Charges then go to:\n\n```text\nPOST /api/mcp/tools/{toolId}/charge\n```\n\ninstead of the legacy generic endpoint:\n\n```text\nPOST /api/mcp/charge\n```\n\nYou 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:\n\n```text\nPOST /api/mcp/charge\n```\n\nTool 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.\n\nRegistered 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.\n\n```ts\nimport { createMcpGate } from '@liveauth-labs/mcp-server';\n\nconst gate = createMcpGate({\n  publicKey: process.env.LIVEAUTH_PUBLIC_KEY!,\n  baseUrl: process.env.LIVEAUTH_API_URL ?? 'https://api.liveauth.app',\n  toolName: 'paid-research-tool',\n});\n\nconst result = await gate.invoke(\n  jwtFromYourTransport,\n  { url: 'https://example.com' },\n  async (input, context) => {\n    const page = await fetch(input.url).then(r => r.text());\n\n    return {\n      text: page,\n      revenueEventId: context.liveAuth.charge.revenueEventId,\n      receipt: context.liveAuth.charge.receipt,\n      netSats: context.liveAuth.charge.netSats,\n    };\n  },\n  { requestId: 'req_123' },\n  {\n    toolMethodName: 'web_fetch',\n    idempotencyKey: 'req_123',\n    agentId: 'agent_abc',\n    metadata: {\n      urlHost: new URL('https://example.com').hostname,\n    },\n  }\n);\n```\n\nWhen `toolId` or `toolName` is set, `GateToolOptions` supports:\n\n| Option | Purpose |\n|--------|---------|\n| `costSats` | Optional sats to charge for this call. Omit to use registered tool pricing or the project global price. |\n| `toolName` | Optional per-call tool slug/name override when using the generic endpoint. |\n| `toolMethodName` | Method within the tool, such as `web_fetch` or `search`. |\n| `idempotencyKey` | Retry-safe key. Reusing it for the same tool returns the original revenue event and signed receipt instead of double charging. |\n| `agentId` | Optional caller/agent identifier for reporting. |\n| `metadata` | Small JSON object for audit context. Do not store private tool output here. |\n\nTool charge responses include the normal budget counters plus revenue accounting:\n\n```json\n{\n  \"status\": \"ok\",\n  \"callsUsed\": 3,\n  \"satsUsed\": 15,\n  \"grossSats\": 5,\n  \"platformFeeSats\": 1,\n  \"netSats\": 4,\n  \"feeBasisPoints\": 500,\n  \"revenueEventId\": \"event-guid\",\n  \"toolId\": \"tool-guid\",\n  \"toolName\": \"Paid Research Tool\",\n  \"toolSlug\": \"paid-research-tool\",\n  \"receipt\": {\n    \"version\": \"mcp-call-receipt-v1\",\n    \"payload\": \"base64url-canonical-json\",\n    \"signature\": \"base64url-hmac-sha256\",\n    \"signatureAlgorithm\": \"HMAC-SHA256\",\n    \"keyId\": \"liveauth-mcp-receipt-v1\",\n    \"body\": {\n      \"receiptId\": \"mcp_receipt_eventguid\",\n      \"revenueEventId\": \"event-guid\",\n      \"mcpToolId\": \"tool-guid\",\n      \"toolName\": \"Paid Research Tool\",\n      \"toolSlug\": \"paid-research-tool\",\n      \"toolMethodName\": \"web_fetch\",\n      \"grossSats\": 5,\n      \"platformFeeSats\": 1,\n      \"netSats\": 4,\n      \"idempotencyKey\": \"req_123\"\n    }\n  }\n}\n```\n\nThe 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.\n\nIf no `toolId` or `toolName` is configured, the SDK keeps using `/api/mcp/charge` for backward-compatible usage metering.\n\n## Configuration\n\n### Claude Desktop\n\nAdd to your `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"liveauth\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@liveauth-labs/mcp-server\"],\n      \"env\": {\n        \"LIVEAUTH_API_BASE\": \"https://api.liveauth.app\",\n        \"LIVEAUTH_API_KEY\": \"la_pk_your_public_key\"\n      }\n    }\n  }\n}\n```\n\n**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.\n\n**Other env vars:**\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `LIVEAUTH_API_KEY` | _(unset)_ | Your LiveAuth project public key (`la_pk_…`). |\n| `LIVEAUTH_API_BASE` | `https://api.liveauth.app` | Override for self-hosted LiveAuth. |\n| `LIVEAUTH_DEMO` | `false` | Explicitly use the legacy locally simulated Lightning demo. |\n\n### Other MCP Clients\n\nThe server speaks stdio (JSON-RPC 2.0). Start it with:\n\n```bash\nliveauth-mcp\n```\n\nIt also works with any MCP-compatible client: Cursor, VS Code, ChatGPT, Windsurf, Continue, Cline.\n\n## Tool Reference\n\nFull 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`.\n\n### `liveauth_mcp_start`\n\nStart a new LiveAuth MCP session. Returns a PoW challenge by default, or a Lightning invoice if `forceLightning=true`.\n\n**Parameters:**\n- `forceLightning` (boolean, optional): If true, request Lightning invoice instead of PoW challenge\n- `forceL402` (boolean, optional): If true, start a session that should be confirmed with an L402 bundle macaroon\n\n**Returns (PoW):**\n```json\n{\n  \"quoteId\": \"uuid-of-session\",\n  \"powChallenge\": {\n    \"projectId\": \"guid\",\n    \"projectPublicKey\": \"la_pk_...\",\n    \"challengeHex\": \"a1b2c3...\",\n    \"targetHex\": \"0000ffff...\",\n    \"difficultyBits\": 18,\n    \"expiresAtUnix\": 1234567890,\n    \"signature\": \"sig...\"\n  },\n  \"invoice\": null\n}\n```\n\n**Returns (Lightning):**\n```json\n{\n  \"quoteId\": \"uuid-of-session\",\n  \"powChallenge\": null,\n  \"invoice\": {\n    \"bolt11\": \"lnbc...\",\n    \"amountSats\": 50,\n    \"expiresAtUnix\": 1234567890,\n    \"paymentHash\": \"abc123...\"\n  },\n  \"lightning\": {\n    \"invoice\": \"lnbc...\",\n    \"lightningUri\": \"lightning:lnbc...\",\n    \"amountSats\": 50,\n    \"expiresAt\": \"2009-02-13T23:31:30.000Z\",\n    \"expiresAtUnix\": 1234567890,\n    \"status\": \"pending\"\n  }\n}\n```\n\n**Returns (L402 bundle):**\n```json\n{\n  \"quoteId\": \"uuid-of-session\",\n  \"powChallenge\": null,\n  \"invoice\": null,\n  \"authHint\": \"l402_bundle\"\n}\n```\n\n### `liveauth_mcp_confirm`\n\nSubmit 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.\n\n**Parameters:**\n- `quoteId` (string): The quoteId from the start response\n- `challengeHex` (string, optional, PoW only): The challenge hex from the start response\n- `nonce` (number, optional, PoW only): The nonce that solves the PoW challenge\n- `hashHex` (string, optional, PoW only): The resulting hash (sha256 of `projectPublicKey:challengeHex:nonce`)\n- `expiresAtUnix` (number, optional, PoW only): Expiration timestamp from the challenge\n- `difficultyBits` (number, optional, PoW only): Difficulty bits from the challenge\n- `signature` (string, optional, PoW only): Signature from the challenge\n- `macaroon` (string, L402 only): Bundle macaroon returned from the L402 bundle claim flow\n\nWhen 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.\n\n**Returns:**\n```json\n{\n  \"jwt\": \"eyJhbGc...\",\n  \"expiresIn\": 600,\n  \"remainingBudgetSats\": 10000,\n  \"refreshToken\": \"abc123def456...\"\n}\n```\n\n**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.\n\n### `liveauth_mcp_charge`\n\nMeter 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.\n\n**Parameters:**\n- `callCostSats` (number, optional): Cost of the API call in sats. Omit to use backend pricing.\n- `toolName` (string, optional): Registered MCP tool slug/name for per-tool pricing and attribution.\n\n**Returns:**\n```json\n{\n  \"status\": \"ok\",\n  \"callsUsed\": 5,\n  \"satsUsed\": 15\n}\n```\n\nIf budget is exceeded:\n```json\n{\n  \"status\": \"deny\",\n  \"callsUsed\": 100,\n  \"satsUsed\": 1000,\n  \"reason\": \"budget_exceeded\"\n}\n```\n\n### `liveauth_mcp_status`\n\nCheck the status of an MCP session. Use to poll for Lightning payment confirmation.\n\n**Parameters:**\n- `quoteId` (string): The quoteId from the start response\n\n**Returns:**\n```json\n{\n  \"quoteId\": \"uuid-of-session\",\n  \"status\": \"pending\",\n  \"paymentStatus\": \"pending\",\n  \"expiresAt\": \"2026-02-17T12:00:00Z\"\n}\n```\n\nWhen `paymentStatus` is \"paid\", the session is confirmed. Call `liveauth_mcp_confirm` again to get the JWT.\n\n### `liveauth_mcp_lnurl`\n\nGet the Lightning invoice for a session (lnget-compatible). Use this to retrieve the BOLT11 invoice for payment with any Lightning wallet.\n\n**Parameters:**\n- `quoteId` (string): The quoteId from the start response\n\n**Returns:**\n```json\n{\n  \"pr\": \"lnbc2100n1...\",\n  \"routes\": []\n}\n```\n\n**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\".\n\n### `liveauth_mcp_usage`\n\nQuery current usage and remaining budget without making a charge. Use this to check status before making API calls.\n\n**Parameters:** (none required)\n\n**Returns:**\n```json\n{\n  \"status\": \"active\",\n  \"callsUsed\": 5,\n  \"satsUsed\": 15,\n  \"maxSatsPerDay\": 10000,\n  \"remainingBudgetSats\": 9985,\n  \"maxCallsPerMinute\": 60,\n  \"expiresAt\": \"2026-02-17T12:00:00Z\",\n  \"dayWindowStart\": \"2026-02-17T00:00:00Z\"\n}\n```\n\n### `liveauth_mcp_refresh`\n\nRefresh the JWT token without re-authenticating. Use the refreshToken returned from confirm to get a new JWT when the current one expires.\n\n**Parameters:**\n- `refreshToken` (string): The refreshToken from the confirm response\n\n**Returns:**\n```json\n{\n  \"jwt\": \"eyJhbGc...\",\n  \"expiresIn\": 600,\n  \"remainingBudgetSats\": 9985\n}\n```\n\n**Note:** Save the refreshToken securely. You'll need it to extend the session without solving a new PoW or making another Lightning payment.\n\n## Usage Example\n\n### PoW Authentication\n\n1. Call `liveauth_mcp_start` to get a PoW challenge and quoteId\n2. Call `liveauth_mcp_confirm` with the quoteId; the MCP server solves its cached challenge with the existing package solver\n3. Advanced clients may still submit an explicit solution (`hash = sha256(projectPublicKey:challengeHex:nonce)` where `hash < targetHex`)\n4. Use the JWT in `Authorization: Bearer <token>` header for API requests\n5. After each generic API call, call `liveauth_mcp_charge` with a call cost, or omit it to use the project global MCP price\n6. For monetized MCP tools, wrap handlers with `createMcpGate({ toolId })` or `createMcpGate({ toolName })` so each call creates a revenue event and signed receipt\n\n### Lightning Authentication\n\n1. Call `liveauth_mcp_start` with `forceLightning: true` to get a Lightning invoice\n2. Use `liveauth_mcp_lnurl` (or poll `liveauth_mcp_status`) to get the BOLT11 invoice\n3. Pay the invoice using your Lightning node/wallet\n4. Poll `liveauth_mcp_status` with the quoteId until paymentStatus is \"paid\"\n5. Call `liveauth_mcp_confirm` with just the quoteId to receive the JWT\n6. Use the JWT with either generic `liveauth_mcp_charge` metering or SDK paid-tool attribution\n\n## Authentication Flow\n\n```\n┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐\n│  AI Agent       │────▶│  MCP Server     │────▶│  LiveAuth API   │\n│                 │     │                 │     │                 │\n│ 1. Start       │     │ /api/mcp/start  │     │ Returns PoW    │\n│ 2. Solve PoW   │     │                 │     │ challenge       │\n│ 3. Confirm     │     │ /api/mcp/confirm│     │ Returns JWT    │\n│ 4. API calls   │     │                 │     │                 │\n│ 5. Charge      │     │ /api/mcp/charge │     │ Meter usage    │\n└─────────────────┘     └─────────────────┘     └─────────────────┘\n```\n\nPaid tool servers use the same JWT but charge through an attributed endpoint:\n\n```text\nAgent calls MCP tool\n→ Tool server calls POST /api/mcp/tools/{toolId}/charge\n  or POST /api/mcp/charge with toolName\n→ LiveAuth validates JWT and budget\n→ LiveAuth records gross / platform fee / net revenue and returns a signed receipt\n→ Tool handler runs and returns the result\n```\n\n## L402 Bundle Flow\n\nLiveAuthCore 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.\n\n```bash\n# 1. Create a bundle invoice.\ncurl -X POST https://api.liveauth.app/api/public/l402/bundle/invoice \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"publicKey\":\"la_pk_xxx\",\"tier\":\"starter\",\"agentId\":\"agent_abc\"}'\n\n# 2. After the invoice is paid, claim a macaroon.\ncurl -X POST https://api.liveauth.app/api/public/l402/bundle/claim \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"publicKey\":\"la_pk_xxx\",\"paymentHash\":\"payment_hash_from_step_1\"}'\n\n# 3. Start and confirm an MCP session with the macaroon.\ncurl -X POST https://api.liveauth.app/api/mcp/start \\\n  -H \"X-LW-Public: la_pk_xxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"forceL402\":true}'\n\ncurl -X POST https://api.liveauth.app/api/mcp/confirm \\\n  -H \"X-LW-Public: la_pk_xxx\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"quoteId\":\"quote_id_from_step_3\",\"macaroon\":\"macaroon_from_step_2\"}'\n```\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Run locally\nnode dist/cli.js\n```\n\n## Resources\n\n- [LiveAuth Demo & Docs](https://liveauth.app)\n- [MCP Protocol Spec](https://modelcontextprotocol.io)\n- [GitHub Repository](https://github.com/dulzuradev/liveauth-mcp)\n\n## License\n\nMIT\n\n---\n\n**Categories:** `authentication` · `payments` · `lightning` · `l402` · `bitcoin` · `pay-per-call` · `metering` · `agent-tools` · `anti-abuse` · `mcp-server` · `typescript`\n\n## Paid execution and diagnostics contract (SDK 1.2.0)\n\nThe gate validates the session, records the charge, then invokes the handler.\n**Authorization plus an accepted execution attempt is billable**, including a handler\nexception, timeout, or cancellation after charging. There is no automatic refund.\nInput rejection before the gate and charge denials do not consume usage. A revenue\nevent with status `Charged` proves billing, not successful tool execution.\n\nRegister the tool and move its lifecycle from `Draft` to `Active` before serving paid\ncalls. Draft means unpublished (`tool_unpublished`); Paused or other non-active states\nreturn `tool_inactive`. Public discovery also requires `Visibility=Public`, but\nvisibility is separate from lifecycle: active private/internal tools can be charged.\nThere is no separate publication flag or new visibility restriction in this change.\nUnknown or removed tools return HTTP 404 with JSON `status=deny`,\n`reason=tool_not_found`, and the supplied tool identity. Registered-tool lifecycle\nand budget denials retain HTTP 200 with `status=deny`.\n\n| Reason | Meaning |\n| --- | --- |\n| `tool_unpublished` | Tool is Draft. |\n| `tool_inactive` | Tool is Paused or otherwise non-active. |\n| `tool_not_found` | No matching non-removed tool. |\n| `budget_exceeded` | Existing budget policy rejected the charge. |\n| `rate_limited` | SDK-supported structured rate denial; the current MCP charge controller does not emit this reason or enforce its per-minute setting. |\n| `denied` | SDK fallback when the denial has no reason. Unknown future reason codes remain available on the SDK error. |\n\n`gate.charge()` returns structured denials with `ok=false`, including JSON HTTP\nerror responses with `status=deny`. `gate.invoke()` and `gate.gateTool()` throw\n`ChargeDeniedError` with `reason`, `code`, `toolName`, and `toolId`. For compatibility\nit extends `BudgetExceededError` (and `LiveAuthMcpError`); new handlers must inspect\n`reason` rather than assume every instance means budget exhaustion. Unrelated HTTP\nauthentication, transport, and validation failures keep their existing error path.\nOlder backends may still return plain-text unknown-tool errors until upgraded.\n\nOn handler failure the gate throws `ToolExecutionError` with `charge`,\n`idempotencyKey`, and a non-enumerable `cause`. Its public message is generic.\nExpose an allowlist of charge fields: `grossSats`, `revenueEventId`, signed `receipt`,\nand the idempotency key. Keep `isError=true` in the MCP response. Do not serialize\nor log the error cause, JWT-bearing handler context, or arbitrary metadata.\nReceipt payload/signature are existing public response artifacts and can be returned.\nA successful charge can have no receipt; preserve this distinction rather than\ninventing one. Billing metadata does not imply successful execution.\n\n```ts\nimport { ChargeDeniedError, ToolExecutionError } from '@liveauth-labs/mcp-server';\n\ntry {\n  return await gate.invoke(jwt, input, handler, {}, { idempotencyKey });\n} catch (error) {\n  if (error instanceof ToolExecutionError) {\n    return {\n      isError: true,\n      content: [{ type: 'text', text: 'Tool execution failed after authorization' }],\n      _meta: { liveauth: {\n        billed: true,\n        grossSats: error.charge.grossSats,\n        revenueEventId: error.charge.revenueEventId,\n        receipt: error.charge.receipt,\n        idempotencyKey: error.idempotencyKey,\n      } },\n    };\n  }\n  if (error instanceof ChargeDeniedError) {\n    // Map known reasons to a public response. Do not serialize error.details wholesale.\n    throw error;\n  }\n  throw error;\n}\n```\n\n### Three distinct identifiers\n\n- Receipt `body.requestId`: LiveAuth's server HTTP request/correlation identifier\n  for the original recorded charge. A retry returns that original receipt.\n- Receipt `body.idempotencyKey`: caller-controlled stable retry key. Deduplication\n  is scoped to the paying project and registered tool, not the server request ID.\n- InvokeWorks `_meta.requestId`: MCP/client correlation ID, taken from `X-Request-Id`\n  or generated by InvokeWorks. InvokeWorks intentionally also uses it as the\n  LiveAuth idempotency key.\n\nFor example, `_meta.requestId=\"client-123\"`, receipt\n`body.idempotencyKey=\"client-123\"`, and receipt `body.requestId=\"server-456\"`\nare valid together. The SDK accepts `idempotencyKey`; it does not send a separate\nclient request-ID option. Caller context `{ requestId }` is local handler context.\nUse a new key for a new logical call and reuse a key only for the same intended\noperation. Deduplicated charging does not cache handler results: retries can execute\nthe handler again. Tool-state and price checks still precede deduplication.\n",
  "bytes": 29451,
  "sha": "48863aa2fac4188f65aa074bbace8894503aad73c5acde28b3a68b8fd23cfae3",
  "repo_slug": "dulzuradev/liveauth-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_dulzuradev_liveauth_mcp_d9aece6e/readme"
}