MCP - Model Context Protocol

Introduction

The Bitnovo Pay MCP server allows AI agents to interact with the Bitnovo Pay API to create and manage cryptocurrency payments autonomously. This integration facilitates crypto payment automation in applications using language models like ChatGPT, Claude, or Gemini.


What is MCP?

Model Context Protocol (MCP) is an open standard protocol that allows AI models to access external tools and services in a structured and secure manner.

Key MCP Concepts

Concept
Description

MCP Server

Process that exposes capabilities (tools, resources) through the MCP protocol via stdio

MCP Tools

Functions that the AI model can invoke (e.g., create_payment_link)

MCP Resources

Data that the server can provide to the model (e.g., cryptocurrency catalog)

stdio Transport

Communication between MCP client and server using standard input/output

The Bitnovo Pay MCP server implements this protocol to expose 8 MCP tools that enable complete crypto payment management from any compatible MCP client.

Official specification: modelcontextprotocol.io


Quick Start (5 minutes)

Step 1: Get your credentials

  1. Create an account at Bitnovo Pay

  2. Obtain your Device ID from the Bitnovo dashboard

  3. (Optional) Generate Device Secret for webhooks

Step 2: Configure your MCP client

Add this configuration to your MCP client (example for Claude Desktop):

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["-y", "@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "your_device_id_here",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com"
      }
    }
  }
}

Step 3: Restart your MCP client

Restart Claude Desktop, ChatGPT, or your MCP client to load the server.

Step 4: Test it!

Ask your AI assistant:

"Create a payment for 10 euros"

✅ You should receive a payment URL ready to share.


Main Features

  • 8 MCP tools for complete payment management:

    • create_payment_onchain - Generate cryptocurrency addresses for direct payments

    • create_payment_link - Create web payment URLs with redirect management

    • get_payment_status - Query payment status with detailed information

    • list_currencies_catalog - Get supported cryptocurrencies with filtering

    • generate_payment_qr - Generate custom QR codes from existing payments

    • get_webhook_events - Query webhook events received in real-time

    • get_webhook_url - Get the public webhook URL with configuration instructions

    • get_tunnel_status - Diagnose tunnel connection status

  • Automatic webhook system with 3 tunnel providers:

    • 🔗 ngrok: Free persistent URL (1 static domain per account)

    • 🌐 zrok: 100% free open-source with persistent URLs

    • 🏢 manual: For servers with public IP (N8N, Opal, VPS)

  • Compatible with multiple LLMs:

    • 🤖 OpenAI ChatGPT (GPT-5, GPT-4o, Responses API, Agents SDK)

    • 🧠 Google Gemini (Gemini 2.5 Flash/Pro Sept 2025, CLI, FastMCP)

    • 🔮 Claude (Claude Desktop, Claude Code)

  • High-quality QR codes (v1.1.0+):

    • 📱 Default resolution of 512px (improved from 300px) for modern displays

    • 🖨️ Support up to 2000px for professional printing

    • ✨ Sharp edges with optimized interpolation algorithms

    • 🎨 Custom Bitnovo Pay branding with smooth logo scaling

  • Security and privacy:

    • Mandatory HTTPS communication

    • HMAC signature validation for webhooks

    • Replay attack prevention with nonce cache

    • Sensitive data masked in logs

    • No local storage (stateless operation)

Prerequisites

Before starting, make sure you have:

Requirement
Description
Needed for

Node.js 18+

JavaScript runtime

Running MCP server

Bitnovo Pay Account

Registration at bitnovo.com/pay

Obtain credentials

Device ID

Device identifier from dashboard

All operations

⚠️ Device Secret

HMAC secret (optional)

Webhooks with validation

MCP Client

Claude, ChatGPT, or Gemini

Interact with server

⚠️ ngrok/zrok (optional)

Tunnel for webhooks

Local development with webhooks

Installation

There are two ways to install the Bitnovo Pay MCP server:

No prior installation required, just configure your MCP client (see "Platform Configuration" section below) using:

npx -y @bitnovopay/mcp-bitnovo-pay

✅ Advantages:

  • Always get the latest published version

  • No need to clone the repository

  • No need to compile the code

  • Automatic updates on each execution

  • Perfect for end users

Option 2: Clone the repository (For development)

If you need to modify the code or contribute to the project:

# Clone the repository
git clone https://github.com/bitnovo/mcp-bitnovo-pay.git
cd mcp-bitnovo-pay

# Install dependencies
npm install

# Build the project
npm run build

# Run in development mode
npm run dev

✅ Advantages:

  • Full control of source code

  • Allows modifications and local development

  • Ideal for contributing to the project

Credentials Configuration

Get your credentials from the Bitnovo Pay dashboard:

  • Device ID: Unique identifier for your merchant

  • Device Secret: (Required for webhooks) For HMAC signature validation

  • Base URL: Environment URL (development or production)

Platform Configuration

OpenAI ChatGPT

To integrate with ChatGPT (GPT-4o, GPT-5), create or edit your configuration file:

Location: ~/.config/openai/mcp-config.json

Option A: Using npx (Recommended)

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "your_device_id_here",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com",
        "BITNOVO_DEVICE_SECRET": "your_device_secret_hex"
      }
    }
  }
}

With webhooks enabled (local development with ngrok):

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["-y", "@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "your_device_id_here",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com",
        "BITNOVO_DEVICE_SECRET": "your_device_secret_hex",
        "WEBHOOK_ENABLED": "true",
        "TUNNEL_ENABLED": "true",
        "TUNNEL_PROVIDER": "ngrok",
        "NGROK_AUTHTOKEN": "your_ngrok_token",
        "NGROK_DOMAIN": "bitnovo-dev.ngrok-free.app"
      }
    }
  }
}

Supported since: March 2025 (GPT-5 since August 2025)

Google Gemini

To integrate with Gemini 2.5, configure the FastMCP file:

Location: ~/.config/gemini/mcp-config.json

Option A: Using npx (Recommended)

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "your_device_id_here",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com",
        "BITNOVO_DEVICE_SECRET": "your_device_secret_hex"
      }
    }
  }
}

Supported since: April 2025 (Sept 2025 Models)

Claude (Anthropic)

To integrate with Claude Desktop or Claude Code:

macOS Location: ~/Library/Application Support/Claude/claude_desktop_config.json Windows Location: %APPDATA%\Claude\claude_desktop_config.json

Basic configuration (without webhooks)

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "your_device_id_here",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com",
        "BITNOVO_DEVICE_SECRET": "your_device_secret_hex"
      }
    }
  }
}

Configuration with webhooks (local development with ngrok)

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "your_device_id_here",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com",
        "BITNOVO_DEVICE_SECRET": "your_device_secret_hex",
        "WEBHOOK_ENABLED": "true",
        "TUNNEL_ENABLED": "true",
        "TUNNEL_PROVIDER": "ngrok",
        "NGROK_AUTHTOKEN": "your_ngrok_token",
        "NGROK_DOMAIN": "bitnovo-dev.ngrok-free.app"
      }
    }
  }
}

Configuration for N8N/Opal/Server

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "your_device_id_here",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com",
        "BITNOVO_DEVICE_SECRET": "your_device_secret_hex",
        "WEBHOOK_ENABLED": "true",
        "TUNNEL_ENABLED": "false",
        "WEBHOOK_PUBLIC_URL": "https://n8n.company.com"
      }
    }
  }
}

Supported since: 2025

MCP Tools Reference

Payment Tools (5)

1. create_payment_onchain

Creates a payment with a specific cryptocurrency address for direct blockchain transactions.

Use when: User specifies a concrete cryptocurrency (Bitcoin, ETH, USDC, etc.)

Parameters:

{
  "amount_eur": 50.0,
  "input_currency": "BTC",
  "fiat": "EUR",
  "notes": "Coffee payment",
  "include_qr": true
}

Response:

{
  "identifier": "abc-123-def",
  "payment_uri": "bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.001",
  "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
  "qr_address": "data:image/png;base64,...",
  "qr_payment_uri": "data:image/png;base64,...",
  "expected_output_amount": "0.001",
  "currency_id": "BTC",
  "fiat_amount": "50.00",
  "status": "PE"
}

Usage examples:

  • "Create a Bitcoin payment for 50 euros"

  • "Generate ETH address for 100 euros"

  • "I need a USDC QR for 25 euros"


2. create_payment_link

Creates a web payment URL where the customer can choose their preferred cryptocurrency.

Use when: Generic payment request without specific cryptocurrency mentioned (DEFAULT OPTION)

Parameters:

{
  "amount_eur": 50.0,
  "fiat": "EUR",
  "url_ok": "https://mystore.com/success",
  "url_ko": "https://mystore.com/cancelled",
  "notes": "Order #1234",
  "include_qr": true
}

Response:

{
  "identifier": "abc-123-def",
  "web_url": "https://payments.bitnovo.com/pay/abc-123-def",
  "qr_web_url": "data:image/png;base64,...",
  "fiat_amount": "50.00",
  "fiat_currency": "EUR",
  "status": "PE"
}

Usage examples:

  • "Create a payment for 50 euros"

  • "Generate QR to collect 100 euros"

  • "Give me the payment link for 25 euros"


3. get_payment_status

Queries the current status of a payment with detailed information.

Parameters:

{
  "identifier": "abc-123-def"
}

Response:

{
  "identifier": "abc-123-def",
  "status": "CO",
  "status_description": "Completed",
  "fiat_amount": "50.00",
  "crypto_amount": "0.001",
  "currency_id": "BTC",
  "created_at": "2025-01-15T10:30:00Z",
  "updated_at": "2025-01-15T10:35:00Z"
}

Possible statuses:

  • NR (Not Ready): Pre-payment created, no crypto assigned

  • PE (Pending): Waiting for customer payment

  • AC (Awaiting Completion): Crypto detected in mempool

  • CO (Completed): Payment confirmed on blockchain

  • EX (Expired): Payment time limit exceeded

  • CA (Cancelled): Payment cancelled

  • FA (Failed): Transaction failed to confirm

Usage examples:

  • "What's the status of payment abc-123-def?"

  • "Was the payment completed?"

  • "Check payment status"


4. list_currencies_catalog

Gets the catalog of available cryptocurrencies with optional amount filtering.

Parameters:

{
  "filter_by_amount": 25.0
}

Response:

{
  "currencies": [
    {
      "symbol": "BTC",
      "name": "Bitcoin",
      "blockchain": "Bitcoin",
      "min_amount": "0.0001",
      "max_amount": "10.0",
      "decimals": 8
    },
    {
      "symbol": "ETH",
      "name": "Ethereum",
      "blockchain": "Ethereum",
      "min_amount": "0.001",
      "max_amount": "100.0",
      "decimals": 18
    }
  ]
}

Usage examples:

  • "What cryptocurrencies are available?"

  • "Which coins accept payments of 50 euros?"

  • "Show crypto catalog"


5. generate_payment_qr

Generates custom QR codes from existing payments.

Parameters:

{
  "identifier": "abc-123-def",
  "qr_type": "both",
  "size": 512,
  "style": "branded",
  "branding": true
}

qr_type options:

  • address: Only crypto address (customer enters amount manually)

  • payment_uri: Address + amount included (recommended)

  • both: Generates both types (recommended)

  • gateway_url: QR of payment gateway URL

Response:

{
  "qr_address": "data:image/png;base64,...",
  "qr_payment_uri": "data:image/png;base64,...",
  "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
  "payment_uri": "bitcoin:1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?amount=0.001"
}

Usage examples:

  • "Generate larger QR for the payment"

  • "Create QR without branding"

  • "I need a 500px QR"


Webhook Tools (3)

6. get_webhook_events

Queries webhook events received in real-time from Bitnovo Pay API.

Available when: WEBHOOK_ENABLED=true

Parameters:

{
  "identifier": "abc-123-def",
  "limit": 50,
  "validated_only": true
}

Response:

{
  "events": [
    {
      "event_id": "abc-123:1234567890",
      "identifier": "abc-123-def",
      "status": "CO",
      "received_at": "2025-09-30T10:30:00.000Z",
      "validated": true,
      "payload": {
        "identifier": "abc-123-def",
        "status": "CO",
        "confirmed_amount": 0.0012,
        "crypto_amount": 0.0012
      }
    }
  ],
  "total_count": 1
}

Usage examples:

  • "Has any webhook arrived for payment abc-123?"

  • "Show all webhook events"

  • "Was payment confirmation received?"


7. get_webhook_url

Gets the public webhook URL with configuration instructions for the Bitnovo dashboard.

Available when: WEBHOOK_ENABLED=true

Parameters:

{
  "validate": true
}

Response:

{
  "webhook_url": "https://bitnovo-dev.ngrok-free.app/webhook/bitnovo",
  "provider": "ngrok",
  "validated": true,
  "instructions": "✅ ngrok tunnel active with persistent URL.\n\nConfiguration steps:\n1. Copy this URL: https://bitnovo-dev.ngrok-free.app/webhook/bitnovo\n2. Go to https://pay.bitnovo.com\n3. Navigate to: Settings → Merchant → Devices\n4. Select device\n5. Configure 'notification_url' with the URL\n\nNote: This URL is persistent and won't change between restarts."
}

Usage examples:

  • "What's my webhook URL?"

  • "Give me the URL to configure in Bitnovo"

  • "How do I set up webhooks?"


8. get_tunnel_status

Diagnoses the tunnel connection status (ngrok, zrok, or manual).

Available when: WEBHOOK_ENABLED=true

Parameters: None

Response:

{
  "enabled": true,
  "provider": "ngrok",
  "status": "connected",
  "public_url": "https://bitnovo-dev.ngrok-free.app",
  "connected_at": "2025-09-30T10:30:00.000Z",
  "last_error": null,
  "reconnect_attempts": 0,
  "health_check_enabled": true,
  "context_detected": {
    "execution_context": "local",
    "confidence": 0.7,
    "suggested_provider": "ngrok",
    "indicators": ["Local development environment detected"]
  }
}

Connection statuses:

  • disconnected: Tunnel not started

  • connecting: Tunnel initializing

  • connected: Tunnel active and healthy

  • reconnecting: Connection lost, attempting to reconnect

  • error: Failed after maximum retries

Usage examples:

  • "Is the webhook tunnel working?"

  • "What's the connection status?"

  • "Diagnose tunnel issues"


Webhooks and Tunnels System

Dual-Server Architecture

The MCP server can run two servers simultaneously:

┌─────────────────────────────────────────────────────────┐
│             MCP Bitnovo Pay Server                      │
│                                                         │
│  ┌──────────────┐  ┌──────────────────┐ ┌────────────┐│
│  │ MCP Server   │  │ Webhook Server   │ │  Tunnel    ││
│  │ (stdio)      │  │ (HTTP :3000)     │ │  Manager   ││
│  └──────┬───────┘  └────────┬─────────┘ └──────┬─────┘│
│         │                   │                   │      │
│         │    Event Store    │     Public URL    │      │
│         │   (in-memory)     │   (ngrok/zrok)    │      │
│         └──────────┬────────┴──────────┬────────┘      │
└────────────────────┼───────────────────┼───────────────┘
                     │                   │
            ┌────────┴────────┐  ┌───────┴────────┐
            │                 │  │                │
       Claude Desktop   Bitnovo API    Tunnel Provider
       (MCP Tools)      (Webhooks)    (ngrok/zrok/manual)

Tunnel Providers

Provider Comparison

Provider
Cost
Persistent URL
Best For
Stability

ngrok

Free (1 static domain)

✅ Yes

Local development

~99% uptime

zrok

100% Free

✅ Yes (reserved shares)

Open-source preference

Medium-high

manual

Depends on hosting

✅ Yes

Servers with public IP

Server-dependent

1. ngrok (Recommended for Local Development)

Features:

  • Free static domain (1 per account since 2023)

  • Persistent URL (doesn't change on restarts)

  • High reliability (~99% uptime)

  • 24-hour timeout (auto-reconnection handles this)

Configuration:

TUNNEL_ENABLED=true
TUNNEL_PROVIDER=ngrok
NGROK_AUTHTOKEN=your_ngrok_token
NGROK_DOMAIN=bitnovo-dev.ngrok-free.app  # Your free static domain

Setup:

  1. Create account at ngrok.com

  2. Get authtoken from dashboard

  3. Claim free static domain at domains

2. zrok (Open-Source Alternative)

Features:

  • 100% Free (no limits on reserved shares)

  • Persistent URL with unique-name

  • Open-source (built on OpenZiti)

  • Medium-high stability (improving with each release)

Configuration:

TUNNEL_ENABLED=true
TUNNEL_PROVIDER=zrok
ZROK_TOKEN=your_zrok_token
ZROK_UNIQUE_NAME=bitnovo-webhooks  # Your reserved share name

# Results in persistent URL:
# https://bitnovo-webhooks.share.zrok.io/webhook/bitnovo

Setup:

  1. Create account at myzrok.io

  2. Install zrok CLI: brew install openziti/zrok/zrok

  3. Enable account: zrok enable YOUR_TOKEN

  4. Reserve share: zrok reserve public --unique-name bitnovo-webhooks 3000

3. manual (Servers with Public IP)

Best For:

  • N8N instances

  • Opal deployments

  • VPS/cloud servers

  • Docker with ingress

  • Kubernetes with LoadBalancer

Environment Auto-detection:

The system automatically detects these environments:

Environment
Detection Method

N8N

Variables N8N_HOST or N8N_PROTOCOL

Opal

Variables OPAL_WEBHOOK_URL or OPAL_HOST

Kubernetes

Variable KUBERNETES_SERVICE_HOST

Docker

File /.dockerenv or DOCKER_HOST

VPS/Server

Multiple indicators (systemd, PM2, etc.)

Configuration:

WEBHOOK_ENABLED=true
TUNNEL_ENABLED=false  # or TUNNEL_PROVIDER=manual
WEBHOOK_PUBLIC_URL=https://n8n.company.com  # Server's public URL

Webhook Security

HMAC-SHA256 Validation

All webhooks are validated using:

signature = hex(hmac_sha256(device_secret, nonce + raw_body))

Validation process:

  1. Extract nonce and signature from headers X-NONCE and X-SIGNATURE

  2. Calculate expected signature using device_secret

  3. Compare using timing-safe comparison

  4. Reject if signatures don't match (401 Unauthorized)

Replay Attack Prevention

  • Nonce cache: Stores used nonces for 5 minutes

  • Duplicate detection: Rejects webhooks with already-used nonces

  • Event deduplication: Same event received multiple times stored once

Why Public URLs Are Secure?

Question: "Won't malicious actors send fake webhooks to my public URL?"

Answer: No, because:

  1. HMAC validation ensures only Bitnovo (with your device_secret) can create valid signatures

  2. Without the device_secret, attackers can't generate valid signatures

  3. All requests without valid signatures are rejected (401 Unauthorized)

  4. Replay prevention with nonces stops reuse of captured valid requests

Security Model:

Attacker sends fake webhook → Missing/invalid signature → 401 Rejected ✅
Attacker replays captured webhook → Nonce already used → 401 Rejected ✅
Bitnovo sends webhook → Valid signature + fresh nonce → 200 Accepted ✅

Decision Tree for LLM Payment Tool Selection

CRITICAL RULE: Does user explicitly mention a cryptocurrency?

User mentions specific crypto (Bitcoin, BTC, Ethereum, ETH, USDC, etc.)?

Use create_payment_onchain with that specific input_currency

User does NOT mention specific crypto

Use create_payment_link (DEFAULT OPTION)

Payment Methods Comparison Table

Feature
create_payment_onchain
create_payment_link

Returns

Crypto address + QR

Web URL

Customer chooses crypto?

No (fixed)

Yes (in gateway)

Best for

Crypto-specific payments

Generic payments (DEFAULT)

Sharing method

Show QR/address

Send link

Redirects

N/A

Yes (url_ok/url_ko)

Use when

User specifies crypto

NO crypto mentioned

Examples

"Bitcoin payment", "ETH address"

"Payment for 24 euros", "Generate QR"

Usage Examples

Example 1: Generic payment (without specific crypto)

User request:

"I need to create a payment for 50 euros"

Tool to use: create_payment_link

Command:

{
  "amount_eur": 50.0,
  "notes": "Generic payment"
}

Result: Web URL where customer can choose any available cryptocurrency.


Example 2: Bitcoin specific payment

User request:

"Create a Bitcoin payment for 100 euros"

Tool to use: create_payment_onchain

Command:

{
  "amount_eur": 100.0,
  "input_currency": "BTC",
  "notes": "Bitcoin payment"
}

Result: Specific Bitcoin address + QR with amount included.


Example 3: Verify payment with webhooks

User request:

"Was payment abc-123-def completed?"

Tool 1: get_webhook_events

Command:

{
  "identifier": "abc-123-def",
  "validated_only": true
}

Result: List of webhook events received showing real-time updated status.

Alternative - Tool 2: get_payment_status

Command:

{
  "identifier": "abc-123-def"
}

Result: Current payment status queried from API.


Example 4: Industry use cases

🛒 E-commerce: Checkout payment

Scenario: Customer completes €150 purchase, needs cryptocurrency flexibility

Tool: create_payment_link

{
  "amount_eur": 150.0,
  "url_ok": "https://store.com/order/12345/confirmed",
  "url_ko": "https://store.com/order/12345/cancelled",
  "notes": "Order #12345 - Bluetooth Headphones"
}

Flow:

  1. Customer confirms order → AI generates payment link

  2. Customer chooses preferred cryptocurrency in gateway

  3. Makes payment with wallet

  4. Gateway redirects to url_ok after confirmation

  5. System processes order automatically


☕ Cafe/Restaurant: Bitcoin tips

Scenario: Customer wants to leave €5 tip in Bitcoin

Tool: create_payment_onchain

{
  "amount_eur": 5.0,
  "input_currency": "BTC",
  "notes": "Tip - Table 7"
}

Flow:

  1. Waiter requests tip QR → AI generates BTC address

  2. Customer scans QR with Bitcoin wallet

  3. Payment confirms in minutes

  4. System notifies waiter via webhook


💼 Freelance: International invoice in stablecoins

Scenario: Freelancer charges $500 USD for project, prefers USDC

Tool: create_payment_onchain

{
  "amount_eur": 500.0,
  "input_currency": "USDC_ERC20",
  "fiat": "USD",
  "notes": "Invoice #2025-001 - Web development"
}

Flow:

  1. International client receives USDC address

  2. Transfers from exchange or wallet

  3. Blockchain confirmation

  4. Freelancer receives funds without intermediaries


🎮 Gaming: In-game purchases

Scenario: Player buys €25 skin, chooses cryptocurrency

Tool: create_payment_link

{
  "amount_eur": 25.0,
  "notes": "In-game purchase - Dragon Skin Legendary",
  "include_qr": true
}

Flow:

  1. Player selects skin → AI generates QR + link

  2. Pays with their preferred crypto (BTC, ETH, etc.)

  3. System detects payment via webhook

  4. Automatic item unlock in account


🏨 Hotel: Reservation with deposit

Scenario: Room reservation with €200 deposit

Tool: create_payment_link

{
  "amount_eur": 200.0,
  "url_ok": "https://hotel.com/bookings/confirm/789",
  "url_ko": "https://hotel.com/bookings/cancel/789",
  "notes": "Reservation deposit - Presidential Suite Oct 15-20"
}

Flow:

  1. Customer books → AI generates payment link

  2. Customer pays deposit in preferred cryptocurrency

  3. Automatic booking confirmation

  4. Frictionless check-in


Example 5: Real-time payment monitoring

Scenario: Point of sale system monitoring payments

Complete flow:

// 1. Create payment
create_payment_link({
  amount_eur: 50.0,
  notes: "POS Sale #4567"
})
// → Identifier: "abc-123-def"

// 2. Monitor with webhooks (recommended)
get_webhook_events({
  identifier: "abc-123-def",
  validated_only: true
})
// → Receives events in real-time

// 3. Manually verify status (alternative)
get_payment_status({
  identifier: "abc-123-def"
})
// → Status: "CO" (Completed)

// 4. Confirm payment completed
// → System updates inventory, prints receipt

Example 6: Configure webhooks

User request:

"How do I configure webhooks in Bitnovo?"

Tool: get_webhook_url

Command:

{
  "validate": true
}

Result: Webhook URL + step-by-step instructions to configure in Bitnovo dashboard.

Technical Architecture

System Layers

┌─────────────────┐
│   MCP Tools     │ ← 8 tools: 5 payment + 3 webhook
│ (src/tools/)    │
├─────────────────┤
│   Services      │ ← Business logic: PaymentService, CurrencyService
│ (src/services/) │
├─────────────────┤
│   API Client    │ ← Bitnovo API integration with retry logic
│ (src/api/)      │
├─────────────────┤
│ Webhook Server  │ ← Express HTTP server + Event Store + Tunnel Manager
│ (src/webhook-*) │
├─────────────────┤
│   Utilities     │ ← Logging, validation, error handling, crypto
│ (src/utils/)    │
└─────────────────┘

Data Flow

Payments:

  1. Tool Request → Validation → Service Layer → API Client → Bitnovo API

  2. Response → Error Handling → Data Transformation → JSON Response

  3. Logging: All operations logged with sensitive data masking

Webhooks:

  1. Bitnovo sends webhook → Tunnel (ngrok/zrok) → Webhook Server (HTTP :3000)

  2. HMAC Validation → Nonce verification → Event Store storage

  3. MCP Query → get_webhook_events → Event Store data

Environment Variables

Required Variables

BITNOVO_DEVICE_ID=your_device_id_here        # Required
BITNOVO_BASE_URL=https://pos.bitnovo.com     # Required

Webhook Variables (Optional)

# Enable webhooks
WEBHOOK_ENABLED=true
WEBHOOK_PORT=3000
WEBHOOK_HOST=0.0.0.0
WEBHOOK_PATH=/webhook/bitnovo

# Security
BITNOVO_DEVICE_SECRET=your_device_secret_hex   # Required for webhooks

# Event store
WEBHOOK_MAX_EVENTS=1000
WEBHOOK_EVENT_TTL_MS=3600000  # 1 hour

Tunnel Variables (Optional)

# Tunnel configuration
TUNNEL_ENABLED=true
TUNNEL_PROVIDER=ngrok  # Options: ngrok, zrok, manual

# ngrok specific
NGROK_AUTHTOKEN=your_ngrok_token
NGROK_DOMAIN=bitnovo-dev.ngrok-free.app  # Optional: free static domain

# zrok specific
ZROK_TOKEN=your_zrok_token
ZROK_UNIQUE_NAME=bitnovo-webhooks  # Your reserved share name

# manual provider
WEBHOOK_PUBLIC_URL=https://n8n.company.com  # For manual provider

# Health monitoring and reconnection
TUNNEL_HEALTH_CHECK_INTERVAL=60000       # 60 seconds (default)
TUNNEL_RECONNECT_MAX_RETRIES=10          # Maximum retry attempts
TUNNEL_RECONNECT_BACKOFF_MS=5000         # Initial backoff delay

🔒 Security

Security is a fundamental priority of the Bitnovo Pay MCP server. We implement multiple layers of protection to ensure secure transactions.

🛡️ Implemented Security Principles

1. Secure Communication

  • Mandatory HTTPS: All API calls use HTTPS exclusively

  • No HTTP: HTTP requests are automatically rejected

  • TLS 1.2+: Encrypted communication with modern protocols

2. HMAC-SHA256 Webhook Validation

Webhooks are protected with HMAC cryptographic signatures:

signature = hex(hmac_sha256(device_secret, nonce + raw_body))

Validation process:

  1. Bitnovo sends webhook with headers X-NONCE and X-SIGNATURE

  2. Server calculates expected signature using BITNOVO_DEVICE_SECRET

  3. Timing-safe comparison between signatures

  4. Immediate rejection if they don't match (401 Unauthorized)

Do I need webhooks? Only if you require real-time notifications. For manual status queries, use get_payment_status.

3. Replay Attack Prevention

  • Nonce cache: Stores used nonces for 5 minutes

  • Duplicate detection: Rejects webhooks with already-used nonces

  • Event deduplication: Same event received multiple times stored once

Example of rejected attack:

Attacker replays valid webhook → Nonce already used → 401 Rejected ✅

4. Data Privacy

  • Masked logs: Device IDs, secrets, and crypto addresses partially hidden

  • No exchange rates: Rates not exposed to prevent inaccuracies

  • Stateless design: No local persistence, real-time API queries

  • Minimal data: Only necessary data requested for operation

Example of masked log:

[INFO] Payment created for device: 12345678-****-****-****-********90ab
[INFO] Webhook validated with signature: a3f2c1...******

5. Resilience and Availability

  • Timeouts: 5 seconds maximum per API operation

  • Smart retries: Maximum 2 retries with exponential backoff

  • Tunnel auto-reconnection: Exponential backoff up to 10 retries

  • Health monitoring: Tunnel connection verification every 60 seconds

🔐 Secure Credentials Configuration

Recommended permissions for configuration file:

# Claude Desktop config (macOS)
chmod 600 ~/Library/Application\ Support/Claude/claude_desktop_config.json

# OpenAI ChatGPT config
chmod 600 ~/.config/openai/mcp-config.json

🌐 Public Tunnel Security

Common question: "Isn't it insecure to expose a public URL for webhooks?"

Answer: No, thanks to HMAC validation:

Scenario
Result

Attacker sends fake webhook

❌ Invalid signature → 401 Rejected

Attacker replays captured webhook

❌ Nonce already used → 401 Rejected

Bitnovo sends legitimate webhook

✅ Valid signature + fresh nonce → 200 Accepted

Conclusion: Only Bitnovo (with your BITNOVO_DEVICE_SECRET) can generate valid webhooks.

📋 Security Checklist

Before using in production, verify:


🔧 Troubleshooting

Common Problems and Solutions

Tip: Check MCP server logs for detailed diagnostics. Logs include masked security information.

❌ Error: "MCP server not found" or "Server failed to start"

Cause: MCP server cannot initialize.

Solutions:

  1. Verify Node.js 18+ is installed: node --version

  2. Try running manually: npx -y @bitnovopay/mcp-bitnovo-pay

  3. Check environment variables are correctly configured

  4. Look for errors in MCP client logs (Claude Desktop: ~/Library/Logs/Claude/)

Example of correct configuration:

{
  "mcpServers": {
    "bitnovo-pay": {
      "command": "npx",
      "args": ["-y", "@bitnovopay/mcp-bitnovo-pay"],
      "env": {
        "BITNOVO_DEVICE_ID": "12345678-abcd-...",
        "BITNOVO_BASE_URL": "https://pos.bitnovo.com"
      }
    }
  }
}

❌ Error: INVALID_DEVICE_ID

Cause: Incorrect or invalid Device ID.

Solutions:

  1. Copy Device ID from Bitnovo Pay dashboard

  2. Verify it's a valid UUID (format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)

  3. Ensure no spaces before/after the ID

  4. Confirm you're using the correct device for the environment (dev/prod)


❌ Error: CURRENCY_NOT_SUPPORTED

Cause: Requested cryptocurrency not available.

Solutions:

  1. Use list_currencies_catalog to see available options

  2. Verify exact symbol (e.g., BTC, not Bitcoin or btc)

  3. Confirm currency is active in your Bitnovo account

Example:

// ❌ Incorrect
{ "input_currency": "Bitcoin" }

// ✅ Correct
{ "input_currency": "BTC" }

❌ Error: AMOUNT_TOO_LOW / AMOUNT_TOO_HIGH

Cause: Amount outside cryptocurrency limits.

Solutions:

  1. Check limits with list_currencies_catalog

  2. Adjust amount within allowed range

  3. For large payments, consider splitting into multiple transactions

Example of limits query:

// Request
{ "filter_by_amount": 50.0 }

// Response shows min_amount and max_amount
[
  {
    "symbol": "BTC",
    "min_amount": 0.01,
    "max_amount": null  // No upper limit
  }
]

❌ Error: PAYMENT_EXPIRED

Cause: Payment exceeded expiration time limit.

Solutions:

  1. Create new payment with create_payment_onchain or create_payment_link

  2. Warn your customer about time limit before it expires

  3. Use get_payment_status to monitor expires_at field


❌ Error: WEBHOOK_NOT_ENABLED

Cause: Attempting to use webhook tools without enabling them.

Solutions:

  1. Add "WEBHOOK_ENABLED": "true" to configuration

  2. (Optional) Configure tunnel with TUNNEL_ENABLED and provider

  3. Restart MCP server

Minimal webhook configuration:

{
  "env": {
    "BITNOVO_DEVICE_ID": "...",
    "BITNOVO_BASE_URL": "...",
    "BITNOVO_DEVICE_SECRET": "...",
    "WEBHOOK_ENABLED": "true"
  }
}

❌ Error: TUNNEL_CONNECTION_FAILED

Cause: Failed to connect with ngrok or zrok.

ngrok Solutions:

  1. Verify your authtoken: ngrok config check

  2. Confirm your static domain is claimed in ngrok dashboard

  3. Test manually: ngrok http --domain=your-domain.ngrok-free.app 3000

zrok Solutions:

  1. Verify zrok is enabled: zrok status

  2. Confirm reserved share exists: zrok share reserved

  3. Test connection: zrok share reserved your-share-name


❌ Error: INVALID_SIGNATURE (webhooks)

Cause: Webhook HMAC signature doesn't match.

Solutions:

  1. Verify BITNOVO_DEVICE_SECRET is correct (64 hex characters)

  2. Confirm it's the same secret configured in Bitnovo dashboard

  3. Don't modify webhook body before validation

  4. Ensure using correct device secret (dev/prod)


Complete Error Codes

Code
Description
Recommended Action

INVALID_DEVICE_ID

Invalid Device ID

Verify credentials in Bitnovo dashboard

INVALID_DEVICE_SECRET

Incorrect Device Secret

Verify 64-character hex format

CURRENCY_NOT_SUPPORTED

Unsupported cryptocurrency

Use list_currencies_catalog

AMOUNT_TOO_LOW

Amount below minimum

Increase payment amount

AMOUNT_TOO_HIGH

Amount above maximum

Reduce amount or split payment

PAYMENT_NOT_FOUND

Payment not found

Verify identifier

PAYMENT_EXPIRED

Payment expired

Create new payment

WEBHOOK_NOT_ENABLED

Webhooks not enabled

Configure WEBHOOK_ENABLED=true

TUNNEL_CONNECTION_FAILED

Tunnel connection failed

Verify ngrok/zrok credentials

INVALID_SIGNATURE

Invalid HMAC signature

Verify BITNOVO_DEVICE_SECRET

Error Response Format

All errors follow this standard format:

{
  "error": {
    "code": "CURRENCY_NOT_SUPPORTED",
    "message": "The specified currency is not supported",
    "details": {
      "input_currency": "INVALID_COIN",
      "supported_currencies": ["BTC", "ETH", "USDC", "..."]
    }
  }
}

Development

Available Commands

npm run build        # Compile TypeScript to JavaScript
npm run dev          # Development server with hot reload
npm start            # Start production server

Performance

Event Store

  • Storage: In-memory (fast, no persistence)

  • Capacity: 1000 events (configurable)

  • TTL: 1 hour (configurable)

  • Cleanup: Automatic every 5 minutes

  • Indexing: Fast search by payment identifier

Tunnels

  • ngrok: ~99% uptime, ~10-50ms added latency

  • zrok: Medium-high uptime, ~20-100ms added latency

  • manual: Server-dependent, no tunnel overhead

Memory Usage

Event Store:

  • Estimated memory per event: ~2KB

  • 1000 events ≈ 2MB

  • 10000 events ≈ 20MB

Tunnel Manager:

  • ngrok: ~5-10MB overhead

  • zrok: ~10-20MB overhead (includes OpenZiti)

  • manual: ~0MB (no tunnel process)

Support and Resources

Known Limitations

  • Single-tenant operation: One Device ID per server instance

  • No local persistence: All queries are real-time to API

  • No exchange rates: For privacy and accuracy, exchange rates not exposed

  • Timeouts: 5 seconds maximum per API operation

  • Retries: Maximum 2 retries with exponential backoff

  • Event Store: In-memory (lost on restart)

  • Free tunnels: Each provider's limitations apply

Changelog

v1.1.0 (2025-09-30) - Tunnel System

  • Automatic tunnel management with 3 providers (ngrok, zrok, manual)

  • Context auto-detection (N8N, Opal, Docker, Kubernetes, VPS, local)

  • Persistent URLs with free ngrok static domains and zrok reserved shares

  • Auto-reconnection with exponential backoff (up to 10 retries)

  • Health monitoring every 60 seconds with automatic recovery

  • New MCP tools: get_webhook_url, get_tunnel_status

  • Zero configuration for common deployment scenarios

v1.0.0 (2025-09-28) - Initial Webhook Implementation

  • ✅ Initial webhook implementation

  • ✅ Event store (in-memory)

  • ✅ HMAC signature validation

  • ✅ Replay attack prevention

  • ✅ Dual-server mode (stdio + HTTP)

  • ✅ New MCP tool: get_webhook_events

  • ✅ Health check and stats endpoints

License

This project is licensed under the MIT License - see the LICENSE file for details.


Last updated: September 30, 2025 Server version: v1.1.0

Última actualización