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.
Current version: v1.1.0 | Last update: September 30, 2025
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
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.
Quick Start (5 minutes)
Step 1: Get your credentials
Create an account at Bitnovo Pay
Obtain your Device ID from the Bitnovo dashboard
(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"
}
}
}
}Configuration file location:
Claude Desktop (macOS):
~/Library/Application Support/Claude/claude_desktop_config.jsonClaude Desktop (Windows):
%APPDATA%\Claude\claude_desktop_config.jsonOpenAI ChatGPT:
~/.config/openai/mcp-config.jsonGoogle Gemini:
~/.config/gemini/mcp-config.json
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 paymentscreate_payment_link- Create web payment URLs with redirect managementget_payment_status- Query payment status with detailed informationlist_currencies_catalog- Get supported cryptocurrencies with filteringgenerate_payment_qr- Generate custom QR codes from existing paymentsget_webhook_events- Query webhook events received in real-timeget_webhook_url- Get the public webhook URL with configuration instructionsget_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
✅ Node.js 18+
JavaScript runtime
Running MCP server
✅ 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
Quick start: You only need Node.js 18+ and Bitnovo credentials to begin. Webhooks are optional.
Installation
There are two ways to install the Bitnovo Pay MCP server:
Option 1: Using npx (Recommended)
Recommended for most users - Simplest way and always up-to-date
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)
For developers only - Requires TypeScript and Node.js knowledge
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"
}
}
}
}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 assignedPE(Pending): Waiting for customer paymentAC(Awaiting Completion): Crypto detected in mempoolCO(Completed): Payment confirmed on blockchainEX(Expired): Payment time limit exceededCA(Cancelled): Payment cancelledFA(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 startedconnecting: Tunnel initializingconnected: Tunnel active and healthyreconnecting: Connection lost, attempting to reconnecterror: 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
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 domainSetup:
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/bitnovoSetup:
Create account at myzrok.io
Install zrok CLI:
brew install openziti/zrok/zrokEnable account:
zrok enable YOUR_TOKENReserve 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:
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 URLWebhook Security
HMAC-SHA256 Validation
All webhooks are validated using:
signature = hex(hmac_sha256(device_secret, nonce + raw_body))Validation process:
Extract nonce and signature from headers
X-NONCEandX-SIGNATURECalculate expected signature using device_secret
Compare using timing-safe comparison
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:
HMAC validation ensures only Bitnovo (with your device_secret) can create valid signatures
Without the device_secret, attackers can't generate valid signatures
All requests without valid signatures are rejected (401 Unauthorized)
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
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:
Customer confirms order → AI generates payment link
Customer chooses preferred cryptocurrency in gateway
Makes payment with wallet
Gateway redirects to
url_okafter confirmationSystem 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:
Waiter requests tip QR → AI generates BTC address
Customer scans QR with Bitcoin wallet
Payment confirms in minutes
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:
International client receives USDC address
Transfers from exchange or wallet
Blockchain confirmation
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:
Player selects skin → AI generates QR + link
Pays with their preferred crypto (BTC, ETH, etc.)
System detects payment via webhook
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:
Customer books → AI generates payment link
Customer pays deposit in preferred cryptocurrency
Automatic booking confirmation
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 receiptExample 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:
Tool Request → Validation → Service Layer → API Client → Bitnovo API
Response → Error Handling → Data Transformation → JSON Response
Logging: All operations logged with sensitive data masking
Webhooks:
Bitnovo sends webhook → Tunnel (ngrok/zrok) → Webhook Server (HTTP :3000)
HMAC Validation → Nonce verification → Event Store storage
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 # RequiredWebhook 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 hourTunnel 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.
IMPORTANT: Never expose your credentials in public repositories, logs, or shared configurations.
🛡️ 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:
Bitnovo sends webhook with headers
X-NONCEandX-SIGNATUREServer calculates expected signature using
BITNOVO_DEVICE_SECRETTiming-safe comparison between signatures
Immediate rejection if they don't match (401 Unauthorized)
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
Security best practices:
✅ Use environment variables, never hardcode secrets
✅ Rotate
BITNOVO_DEVICE_SECRETregularly✅ Use production URLs (
https://pos.bitnovo.com) in production environments✅ Limit access to MCP configuration file (permissions 600)
❌ NEVER share
BITNOVO_DEVICE_SECRETin logs, repos, or messages
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:
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
❌ Error: "MCP server not found" or "Server failed to start"
Cause: MCP server cannot initialize.
Solutions:
Verify Node.js 18+ is installed:
node --versionTry running manually:
npx -y @bitnovopay/mcp-bitnovo-payCheck environment variables are correctly configured
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:
Copy Device ID from Bitnovo Pay dashboard
Verify it's a valid UUID (format:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)Ensure no spaces before/after the ID
Confirm you're using the correct device for the environment (dev/prod)
❌ Error: CURRENCY_NOT_SUPPORTED
Cause: Requested cryptocurrency not available.
Solutions:
Use
list_currencies_catalogto see available optionsVerify exact symbol (e.g.,
BTC, notBitcoinorbtc)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:
Check limits with
list_currencies_catalogAdjust amount within allowed range
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:
Create new payment with
create_payment_onchainorcreate_payment_linkWarn your customer about time limit before it expires
Use
get_payment_statusto monitorexpires_atfield
Important: Onchain payments with specific cryptocurrency start their timer immediately upon creation. Communicate this to the user.
❌ Error: WEBHOOK_NOT_ENABLED
Cause: Attempting to use webhook tools without enabling them.
Solutions:
Add
"WEBHOOK_ENABLED": "true"to configuration(Optional) Configure tunnel with
TUNNEL_ENABLEDand providerRestart 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:
Verify your authtoken:
ngrok config checkConfirm your static domain is claimed in ngrok dashboard
Test manually:
ngrok http --domain=your-domain.ngrok-free.app 3000
zrok Solutions:
Verify zrok is enabled:
zrok statusConfirm reserved share exists:
zrok share reservedTest connection:
zrok share reserved your-share-name
❌ Error: INVALID_SIGNATURE (webhooks)
Cause: Webhook HMAC signature doesn't match.
Solutions:
Verify
BITNOVO_DEVICE_SECRETis correct (64 hex characters)Confirm it's the same secret configured in Bitnovo dashboard
Don't modify webhook body before validation
Ensure using correct device secret (dev/prod)
Complete Error Codes
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 serverPerformance
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
GitHub Repository: github.com/bitnovo/mcp-bitnovo-pay
Bitnovo Support: bitnovo.com
MCP Protocol: modelcontextprotocol.io
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