agenticbtc-mcp 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +205 -0
- package/bin/agentbtc.js +262 -0
- package/package.json +49 -0
- package/src/server.js +324 -0
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# AgenticBTC - Bitcoin Lightning MCP Server
|
|
2
|
+
|
|
3
|
+
🚀 **Enable Claude Desktop to send Bitcoin payments, access L402 APIs, and manage Lightning wallets.**
|
|
4
|
+
|
|
5
|
+
AgenticBTC provides a Model Context Protocol (MCP) server that gives Claude Desktop direct access to Bitcoin and Lightning Network functionality. Your AI assistant can now make payments, create wallets, and interact with Lightning-powered APIs automatically.
|
|
6
|
+
|
|
7
|
+
## ⚡ Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Install and setup in one command
|
|
11
|
+
npx agenticbtc setup
|
|
12
|
+
|
|
13
|
+
# Check if everything is working
|
|
14
|
+
npx agenticbtc status
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then restart Claude Desktop and start using Bitcoin with your AI assistant!
|
|
18
|
+
|
|
19
|
+
## 🎯 What It Does
|
|
20
|
+
|
|
21
|
+
- **💰 Wallet Management**: Create and check Bitcoin wallets for AI agents
|
|
22
|
+
- **⚡ Lightning Payments**: Send instant, low-fee Bitcoin payments via Lightning Network
|
|
23
|
+
- **🔐 L402 APIs**: Access premium APIs that charge per request using Lightning micropayments
|
|
24
|
+
- **📊 Node Management**: Monitor Lightning node status and connectivity
|
|
25
|
+
- **🤖 Claude Integration**: Seamless setup with Claude Desktop's MCP system
|
|
26
|
+
|
|
27
|
+
## 🛠️ Installation
|
|
28
|
+
|
|
29
|
+
### Method 1: NPX (Recommended)
|
|
30
|
+
```bash
|
|
31
|
+
npx agentbtc setup
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Method 2: Global Install
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g agentbtc
|
|
37
|
+
agentbtc setup
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 📋 Prerequisites
|
|
41
|
+
|
|
42
|
+
1. **AgentBTC API Server** - Running at `http://localhost:8000` or custom URL
|
|
43
|
+
2. **API Key** - From your AgentBTC server
|
|
44
|
+
3. **Lightning Node** (Optional) - For L402 API access and direct payments
|
|
45
|
+
|
|
46
|
+
## 🔧 Configuration
|
|
47
|
+
|
|
48
|
+
### Interactive Setup
|
|
49
|
+
```bash
|
|
50
|
+
npx agentbtc setup
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This will ask for:
|
|
54
|
+
- **API URL** (default: `http://localhost:8000`)
|
|
55
|
+
- **API Key** (required)
|
|
56
|
+
- **Lightning Node Host** (optional, for L402 APIs)
|
|
57
|
+
- **LND Macaroon** (optional, hex format)
|
|
58
|
+
|
|
59
|
+
### Manual Configuration
|
|
60
|
+
|
|
61
|
+
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on Mac):
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"agentbtc": {
|
|
67
|
+
"command": "npx",
|
|
68
|
+
"args": ["agentbtc", "server"],
|
|
69
|
+
"env": {
|
|
70
|
+
"AGENTBTC_API_URL": "http://localhost:8000",
|
|
71
|
+
"AGENTBTC_API_KEY": "your-api-key",
|
|
72
|
+
"AGENTBTC_LND_HOST": "https://your-lnd-node:8080",
|
|
73
|
+
"AGENTBTC_LND_MACAROON": "your-lnd-macaroon-hex",
|
|
74
|
+
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## 🎮 Usage Examples
|
|
82
|
+
|
|
83
|
+
Once configured, you can ask Claude Desktop:
|
|
84
|
+
|
|
85
|
+
### Basic Wallet Operations
|
|
86
|
+
```
|
|
87
|
+
"Create a Bitcoin wallet called 'my-assistant'"
|
|
88
|
+
"Check the balance of agent wallet abc123"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Lightning Payments
|
|
92
|
+
```
|
|
93
|
+
"Pay this Lightning invoice: lnbc..."
|
|
94
|
+
"Send 1000 sats to this invoice with a 50 sat fee limit"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### L402 API Access
|
|
98
|
+
```
|
|
99
|
+
"Get Bitcoin market data using L402"
|
|
100
|
+
"Access the premium AI service API"
|
|
101
|
+
"Show me data from the L402 protected endpoint"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Node Information
|
|
105
|
+
```
|
|
106
|
+
"Show my Lightning node status"
|
|
107
|
+
"Get node info and channel count"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 🔍 Commands
|
|
111
|
+
|
|
112
|
+
| Command | Description |
|
|
113
|
+
|---------|-------------|
|
|
114
|
+
| `agentbtc setup` | Interactive setup for Claude Desktop |
|
|
115
|
+
| `agentbtc server` | Start MCP server (used internally by Claude) |
|
|
116
|
+
| `agentbtc status` | Check configuration and connectivity |
|
|
117
|
+
| `agentbtc --help` | Show help information |
|
|
118
|
+
|
|
119
|
+
## 🌍 Environment Variables
|
|
120
|
+
|
|
121
|
+
| Variable | Description | Default |
|
|
122
|
+
|----------|-------------|---------|
|
|
123
|
+
| `AGENTBTC_API_URL` | AgentBTC API endpoint | `http://localhost:8000` |
|
|
124
|
+
| `AGENTBTC_API_KEY` | AgentBTC API key | *Required* |
|
|
125
|
+
| `AGENTBTC_LND_HOST` | Lightning node REST API | *Optional* |
|
|
126
|
+
| `AGENTBTC_LND_MACAROON` | LND macaroon (hex format) | *Optional* |
|
|
127
|
+
| `NODE_TLS_REJECT_UNAUTHORIZED` | Skip TLS verification | `0` |
|
|
128
|
+
|
|
129
|
+
## 🔧 Troubleshooting
|
|
130
|
+
|
|
131
|
+
### Check Status
|
|
132
|
+
```bash
|
|
133
|
+
npx agentbtc status
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Common Issues
|
|
137
|
+
|
|
138
|
+
**❌ API Connection Failed**
|
|
139
|
+
- Verify your AgentBTC server is running
|
|
140
|
+
- Check the API URL and key
|
|
141
|
+
- Ensure firewall allows connections
|
|
142
|
+
|
|
143
|
+
**❌ Lightning Node Connection Failed**
|
|
144
|
+
- Verify LND REST API is accessible
|
|
145
|
+
- Check macaroon permissions (needs `invoices:read`, `invoices:write`)
|
|
146
|
+
- Confirm TLS certificates if using HTTPS
|
|
147
|
+
|
|
148
|
+
**❌ Claude Desktop Not Loading Server**
|
|
149
|
+
- Restart Claude Desktop after setup
|
|
150
|
+
- Check `claude_desktop_config.json` syntax
|
|
151
|
+
- Verify file permissions
|
|
152
|
+
|
|
153
|
+
### Config File Locations
|
|
154
|
+
|
|
155
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
156
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
157
|
+
- **Linux**: `~/.config/claude/claude_desktop_config.json`
|
|
158
|
+
|
|
159
|
+
## 🏗️ Development
|
|
160
|
+
|
|
161
|
+
### Local Testing
|
|
162
|
+
```bash
|
|
163
|
+
git clone <repo>
|
|
164
|
+
cd agentbtc/npm-package
|
|
165
|
+
node bin/agentbtc.js --help
|
|
166
|
+
node bin/agentbtc.js status
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Project Structure
|
|
170
|
+
```
|
|
171
|
+
agentbtc/
|
|
172
|
+
├── bin/
|
|
173
|
+
│ └── agentbtc.js # CLI entry point
|
|
174
|
+
├── src/
|
|
175
|
+
│ └── server.js # MCP server implementation
|
|
176
|
+
├── package.json
|
|
177
|
+
└── README.md
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 📝 License
|
|
181
|
+
|
|
182
|
+
MIT License - see LICENSE file for details.
|
|
183
|
+
|
|
184
|
+
## 🔗 Links
|
|
185
|
+
|
|
186
|
+
- **Repository**: https://github.com/agentbtc/agentbtc
|
|
187
|
+
- **Issues**: https://github.com/agentbtc/agentbtc/issues
|
|
188
|
+
- **Model Context Protocol**: https://modelcontextprotocol.io
|
|
189
|
+
- **Claude Desktop**: https://claude.ai/desktop
|
|
190
|
+
|
|
191
|
+
## 🤝 Contributing
|
|
192
|
+
|
|
193
|
+
1. Fork the repository
|
|
194
|
+
2. Create a feature branch
|
|
195
|
+
3. Make your changes
|
|
196
|
+
4. Add tests if applicable
|
|
197
|
+
5. Submit a pull request
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
**🎉 Ready to give your AI assistant Bitcoin superpowers?**
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npx agentbtc setup
|
|
205
|
+
```
|
package/bin/agentbtc.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { createInterface } from "readline";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const command = process.argv[2];
|
|
11
|
+
|
|
12
|
+
// Cross-platform Claude Desktop config path
|
|
13
|
+
function getClaudeConfigPath() {
|
|
14
|
+
const platform = process.platform;
|
|
15
|
+
if (platform === "darwin") {
|
|
16
|
+
return join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
17
|
+
} else if (platform === "win32") {
|
|
18
|
+
return join(process.env.APPDATA || join(homedir(), "AppData", "Roaming"), "Claude", "claude_desktop_config.json");
|
|
19
|
+
} else {
|
|
20
|
+
// Linux/other
|
|
21
|
+
return join(homedir(), ".config", "claude", "claude_desktop_config.json");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Interactive prompt helper
|
|
26
|
+
function prompt(question) {
|
|
27
|
+
const rl = createInterface({
|
|
28
|
+
input: process.stdin,
|
|
29
|
+
output: process.stdout
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
rl.question(question, (answer) => {
|
|
34
|
+
rl.close();
|
|
35
|
+
resolve(answer.trim());
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if API is reachable
|
|
41
|
+
async function checkApi(apiUrl, apiKey) {
|
|
42
|
+
try {
|
|
43
|
+
const response = await fetch(`${apiUrl}/health`, {
|
|
44
|
+
headers: apiKey ? { "X-API-Key": apiKey } : {}
|
|
45
|
+
});
|
|
46
|
+
return { status: response.status, ok: response.ok };
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return { status: 0, ok: false, error: error.message };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Commands
|
|
53
|
+
async function setup() {
|
|
54
|
+
console.log("🚀 AgentBTC Setup\n");
|
|
55
|
+
|
|
56
|
+
// Get configuration from user
|
|
57
|
+
const apiUrl = await prompt(`AgentBTC API URL (http://localhost:8000): `) || "http://localhost:8000";
|
|
58
|
+
const apiKey = await prompt("AgentBTC API Key: ");
|
|
59
|
+
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
console.log("❌ API Key is required");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log("\n⚡ Lightning Node Configuration (optional):");
|
|
66
|
+
const lndHost = await prompt("LND REST API Host (optional): ");
|
|
67
|
+
const lndMacaroon = await prompt("LND Macaroon (hex, optional): ");
|
|
68
|
+
|
|
69
|
+
// Test API connection
|
|
70
|
+
console.log("\n🔄 Testing API connection...");
|
|
71
|
+
const apiTest = await checkApi(apiUrl, apiKey);
|
|
72
|
+
if (!apiTest.ok) {
|
|
73
|
+
console.log(`⚠️ Warning: Cannot reach API at ${apiUrl} (${apiTest.status})`);
|
|
74
|
+
if (apiTest.error) {
|
|
75
|
+
console.log(` Error: ${apiTest.error}`);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
console.log("✅ API connection successful");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Prepare Claude Desktop config
|
|
82
|
+
const configPath = getClaudeConfigPath();
|
|
83
|
+
let config = { mcpServers: {} };
|
|
84
|
+
|
|
85
|
+
// Load existing config if it exists
|
|
86
|
+
if (existsSync(configPath)) {
|
|
87
|
+
try {
|
|
88
|
+
const existing = readFileSync(configPath, "utf8");
|
|
89
|
+
config = JSON.parse(existing);
|
|
90
|
+
if (!config.mcpServers) {
|
|
91
|
+
config.mcpServers = {};
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.log(`⚠️ Warning: Could not parse existing config: ${error.message}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Add AgentBTC server config
|
|
99
|
+
const serverConfig = {
|
|
100
|
+
command: "npx",
|
|
101
|
+
args: ["agentbtc", "server"],
|
|
102
|
+
env: {
|
|
103
|
+
AGENTBTC_API_URL: apiUrl,
|
|
104
|
+
AGENTBTC_API_KEY: apiKey,
|
|
105
|
+
NODE_TLS_REJECT_UNAUTHORIZED: "0"
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
if (lndHost) {
|
|
110
|
+
serverConfig.env.AGENTBTC_LND_HOST = lndHost;
|
|
111
|
+
}
|
|
112
|
+
if (lndMacaroon) {
|
|
113
|
+
serverConfig.env.AGENTBTC_LND_MACAROON = lndMacaroon;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
config.mcpServers.agentbtc = serverConfig;
|
|
117
|
+
|
|
118
|
+
// Create config directory if needed
|
|
119
|
+
try {
|
|
120
|
+
const configDir = dirname(configPath);
|
|
121
|
+
if (!existsSync(configDir)) {
|
|
122
|
+
const { mkdirSync } = await import("fs");
|
|
123
|
+
mkdirSync(configDir, { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.log(`❌ Could not create config directory: ${error.message}`);
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Write config
|
|
131
|
+
try {
|
|
132
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
133
|
+
console.log(`\n✅ Configuration saved to: ${configPath}`);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.log(`❌ Could not write config: ${error.message}`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log("\n🎉 Setup complete!");
|
|
140
|
+
console.log("💡 Restart Claude Desktop to load the AgentBTC server.");
|
|
141
|
+
console.log("🔧 Test with: agentbtc status");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function status() {
|
|
145
|
+
const apiUrl = process.env.AGENTBTC_API_URL || "http://localhost:8000";
|
|
146
|
+
const apiKey = process.env.AGENTBTC_API_KEY || "";
|
|
147
|
+
const lndHost = process.env.AGENTBTC_LND_HOST || "";
|
|
148
|
+
|
|
149
|
+
console.log("📊 AgentBTC Status\n");
|
|
150
|
+
|
|
151
|
+
// Configuration
|
|
152
|
+
console.log("Configuration:");
|
|
153
|
+
console.log(` API URL: ${apiUrl}`);
|
|
154
|
+
console.log(` API Key: ${apiKey ? "✅ Set" : "❌ Not set"}`);
|
|
155
|
+
console.log(` LND Host: ${lndHost || "❌ Not configured"}`);
|
|
156
|
+
console.log(` LND Macaroon: ${process.env.AGENTBTC_LND_MACAROON ? "✅ Set" : "❌ Not set"}`);
|
|
157
|
+
|
|
158
|
+
// API connectivity
|
|
159
|
+
console.log("\n🔄 Testing API connection...");
|
|
160
|
+
const apiTest = await checkApi(apiUrl, apiKey);
|
|
161
|
+
if (apiTest.ok) {
|
|
162
|
+
console.log("✅ API connection successful");
|
|
163
|
+
} else {
|
|
164
|
+
console.log(`❌ API connection failed (${apiTest.status})`);
|
|
165
|
+
if (apiTest.error) {
|
|
166
|
+
console.log(` Error: ${apiTest.error}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// LND connectivity (if configured)
|
|
171
|
+
if (lndHost && process.env.AGENTBTC_LND_MACAROON) {
|
|
172
|
+
console.log("\n🔄 Testing LND connection...");
|
|
173
|
+
try {
|
|
174
|
+
const lndRes = await fetch(`${lndHost}/v1/getinfo`, {
|
|
175
|
+
headers: { "Grpc-Metadata-macaroon": process.env.AGENTBTC_LND_MACAROON }
|
|
176
|
+
});
|
|
177
|
+
if (lndRes.ok) {
|
|
178
|
+
const lndData = await lndRes.json();
|
|
179
|
+
console.log("⚡ LND connection successful");
|
|
180
|
+
console.log(` Node: ${lndData.alias} (${lndData.identity_pubkey?.slice(0, 20)}...)`);
|
|
181
|
+
console.log(` Channels: ${lndData.num_active_channels}`);
|
|
182
|
+
} else {
|
|
183
|
+
console.log(`❌ LND connection failed (${lndRes.status})`);
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.log(`❌ LND connection error: ${error.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Claude Desktop config
|
|
191
|
+
const configPath = getClaudeConfigPath();
|
|
192
|
+
console.log(`\n🔧 Claude Desktop config: ${configPath}`);
|
|
193
|
+
if (existsSync(configPath)) {
|
|
194
|
+
try {
|
|
195
|
+
const config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
196
|
+
if (config.mcpServers?.agentbtc) {
|
|
197
|
+
console.log("✅ AgentBTC server configured in Claude Desktop");
|
|
198
|
+
} else {
|
|
199
|
+
console.log("⚠️ AgentBTC server not found in Claude Desktop config");
|
|
200
|
+
console.log(" Run: agentbtc setup");
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.log(`❌ Could not read config: ${error.message}`);
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
console.log("❌ Claude Desktop config not found");
|
|
207
|
+
console.log(" Run: agentbtc setup");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function server() {
|
|
212
|
+
// Import and start the MCP server
|
|
213
|
+
import("../src/server.js");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function help() {
|
|
217
|
+
console.log(`
|
|
218
|
+
🚀 AgentBTC - Bitcoin Lightning MCP Server for AI Agents
|
|
219
|
+
|
|
220
|
+
Usage:
|
|
221
|
+
agentbtc start Get started with AgentBTC (interactive setup)
|
|
222
|
+
agentbtc setup Interactive setup for Claude Desktop
|
|
223
|
+
agentbtc server Start the MCP server (used by Claude Desktop)
|
|
224
|
+
agentbtc status Check configuration and connectivity
|
|
225
|
+
agentbtc --help Show this help message
|
|
226
|
+
|
|
227
|
+
Examples:
|
|
228
|
+
npx agentbtc start # Get started - first-time setup
|
|
229
|
+
agentbtc status # Check if everything is working
|
|
230
|
+
|
|
231
|
+
Environment Variables:
|
|
232
|
+
AGENTBTC_API_URL AgentBTC API endpoint (default: http://localhost:8000)
|
|
233
|
+
AGENTBTC_API_KEY AgentBTC API key (required)
|
|
234
|
+
AGENTBTC_LND_HOST Lightning node REST API host (optional)
|
|
235
|
+
AGENTBTC_LND_MACAROON Lightning node macaroon in hex (optional)
|
|
236
|
+
|
|
237
|
+
More info: https://agenticbtc.app
|
|
238
|
+
`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Route commands
|
|
242
|
+
switch (command) {
|
|
243
|
+
case "start":
|
|
244
|
+
case "setup":
|
|
245
|
+
setup().catch(console.error);
|
|
246
|
+
break;
|
|
247
|
+
case "server":
|
|
248
|
+
server();
|
|
249
|
+
break;
|
|
250
|
+
case "status":
|
|
251
|
+
status().catch(console.error);
|
|
252
|
+
break;
|
|
253
|
+
case "--help":
|
|
254
|
+
case "help":
|
|
255
|
+
case undefined:
|
|
256
|
+
help();
|
|
257
|
+
break;
|
|
258
|
+
default:
|
|
259
|
+
console.log(`Unknown command: ${command}`);
|
|
260
|
+
console.log("Run 'agentbtc --help' for usage information.");
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agenticbtc-mcp",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Privacy-intelligent payments for AI agents — your privacy, your choice. Universal payment router with Lightning, Strike, Coinbase, PayPal, Venmo support.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bitcoin",
|
|
7
|
+
"lightning",
|
|
8
|
+
"mcp",
|
|
9
|
+
"ai-agent",
|
|
10
|
+
"l402",
|
|
11
|
+
"claude",
|
|
12
|
+
"payments",
|
|
13
|
+
"cryptocurrency"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "src/server.js",
|
|
17
|
+
"bin": {
|
|
18
|
+
"agenticbtc-mcp": "bin/agentbtc.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin/",
|
|
22
|
+
"src/",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/bkblocksolutions-rgb/agenticbtc-mcp.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://agenticbtc.io",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/bkblocksolutions-rgb/agenticbtc-mcp/issues"
|
|
36
|
+
},
|
|
37
|
+
"author": {
|
|
38
|
+
"name": "AgenticBTC",
|
|
39
|
+
"url": "https://agenticbtc.io"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
43
|
+
"zod": "^3.22.0"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"start": "node src/server.js",
|
|
47
|
+
"test": "node bin/agenticbtc.js --help"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/server.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AgentBTC MCP Server
|
|
4
|
+
* Exposes Bitcoin payment capabilities to Claude Desktop via Model Context Protocol.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
const API_URL = process.env.AGENTBTC_API_URL || "http://localhost:8000";
|
|
12
|
+
const API_KEY = process.env.AGENTBTC_API_KEY || "";
|
|
13
|
+
const LND_HOST = process.env.AGENTBTC_LND_HOST || "";
|
|
14
|
+
const LND_MACAROON = process.env.AGENTBTC_LND_MACAROON || "";
|
|
15
|
+
|
|
16
|
+
// BK Block routing node — all payments route through this node as first hop
|
|
17
|
+
// Routing fee: 0.5% (5000ppm) + 1 sat base
|
|
18
|
+
const BK_BLOCK_NODE_PUBKEY = process.env.BK_BLOCK_NODE_PUBKEY || "031aef3a70c08a6e2d96ba1c78eec66092723cdc41d546329df3f065b0f200bd3b";
|
|
19
|
+
const ROUTING_REPORT_URL = process.env.AGENTBTC_ROUTING_REPORT_URL || `${API_URL}/api/v1/routing/report`;
|
|
20
|
+
|
|
21
|
+
// Report a payment to the verification API
|
|
22
|
+
async function reportPayment(paymentHash, amountSats, destinationPubkey, routeHops) {
|
|
23
|
+
try {
|
|
24
|
+
await fetch(ROUTING_REPORT_URL, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: {
|
|
27
|
+
"X-API-Key": API_KEY,
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
payment_hash: paymentHash,
|
|
32
|
+
amount_sats: amountSats,
|
|
33
|
+
destination_pubkey: destinationPubkey || "",
|
|
34
|
+
route_hops: routeHops || [BK_BLOCK_NODE_PUBKEY],
|
|
35
|
+
}),
|
|
36
|
+
});
|
|
37
|
+
} catch (e) {
|
|
38
|
+
// Silent fail — don't block payments if reporting is down
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check routing status before payment
|
|
43
|
+
async function checkRoutingStatus() {
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(`${API_URL}/api/v1/routing/status`, {
|
|
46
|
+
headers: { "X-API-Key": API_KEY },
|
|
47
|
+
});
|
|
48
|
+
if (res.status === 200) {
|
|
49
|
+
const data = await res.json();
|
|
50
|
+
return data.status || "active";
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// If check fails, allow payment (fail open)
|
|
54
|
+
}
|
|
55
|
+
return "active";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const server = new McpServer({
|
|
59
|
+
name: "agentbtc",
|
|
60
|
+
version: "1.0.0",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Helper for API calls
|
|
64
|
+
async function apiCall(path, options = {}) {
|
|
65
|
+
const url = `${API_URL}${path}`;
|
|
66
|
+
const headers = {
|
|
67
|
+
"X-API-Key": options.apiKey || API_KEY,
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
...options.headers,
|
|
70
|
+
};
|
|
71
|
+
const res = await fetch(url, { ...options, headers });
|
|
72
|
+
const data = await res.json();
|
|
73
|
+
return { status: res.status, data };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Tool: Get agent wallet balance
|
|
77
|
+
server.tool(
|
|
78
|
+
"get_agent_balance",
|
|
79
|
+
"Get Bitcoin balance for an agent wallet",
|
|
80
|
+
{ agent_id: z.string().describe("Agent wallet ID") },
|
|
81
|
+
async ({ agent_id }) => {
|
|
82
|
+
const { status, data } = await apiCall(`/api/v1/agents/${agent_id}`);
|
|
83
|
+
if (status === 200) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{
|
|
86
|
+
type: "text",
|
|
87
|
+
text: JSON.stringify({
|
|
88
|
+
success: true,
|
|
89
|
+
agent: data.name,
|
|
90
|
+
balance_sats: data.balance_sats || 0,
|
|
91
|
+
balance_btc: data.balance_btc || "0.00000000",
|
|
92
|
+
}, null, 2),
|
|
93
|
+
}],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { content: [{ type: "text", text: `Error: ${JSON.stringify(data)}` }] };
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Tool: Create agent wallet
|
|
101
|
+
server.tool(
|
|
102
|
+
"create_agent_wallet",
|
|
103
|
+
"Create a new Bitcoin wallet for an AI agent",
|
|
104
|
+
{ agent_name: z.string().describe("Name for the agent wallet") },
|
|
105
|
+
async ({ agent_name }) => {
|
|
106
|
+
const { status, data } = await apiCall("/api/v1/agents", {
|
|
107
|
+
method: "POST",
|
|
108
|
+
body: JSON.stringify({ name: agent_name }),
|
|
109
|
+
});
|
|
110
|
+
if (status === 200 || status === 201) {
|
|
111
|
+
return {
|
|
112
|
+
content: [{
|
|
113
|
+
type: "text",
|
|
114
|
+
text: JSON.stringify({
|
|
115
|
+
success: true,
|
|
116
|
+
agent_id: data.id,
|
|
117
|
+
api_key: data.api_key,
|
|
118
|
+
message: `Created wallet '${agent_name}'`,
|
|
119
|
+
}, null, 2),
|
|
120
|
+
}],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return { content: [{ type: "text", text: `Error: ${JSON.stringify(data)}` }] };
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Tool: Pay Lightning invoice
|
|
128
|
+
server.tool(
|
|
129
|
+
"pay_lightning_invoice",
|
|
130
|
+
"Pay a Lightning Network invoice using agent wallet funds",
|
|
131
|
+
{
|
|
132
|
+
invoice: z.string().describe("BOLT11 Lightning invoice to pay"),
|
|
133
|
+
fee_limit_sats: z.number().optional().default(100).describe("Max fee in sats"),
|
|
134
|
+
},
|
|
135
|
+
async ({ invoice, fee_limit_sats }) => {
|
|
136
|
+
// Check routing status before paying
|
|
137
|
+
const routingStatus = await checkRoutingStatus();
|
|
138
|
+
if (routingStatus === "suspended") {
|
|
139
|
+
return { content: [{ type: "text", text: "⛔ Account suspended — routing verification failed. Contact support." }] };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { status, data } = await apiCall("/api/v1/invoices/pay", {
|
|
143
|
+
method: "POST",
|
|
144
|
+
body: JSON.stringify({ payment_request: invoice, fee_limit_sats }),
|
|
145
|
+
});
|
|
146
|
+
if (status === 201 || status === 200) {
|
|
147
|
+
// Report payment for routing verification
|
|
148
|
+
await reportPayment(
|
|
149
|
+
data.payment_hash,
|
|
150
|
+
data.amount_sats,
|
|
151
|
+
data.destination || "",
|
|
152
|
+
[BK_BLOCK_NODE_PUBKEY]
|
|
153
|
+
);
|
|
154
|
+
return {
|
|
155
|
+
content: [{
|
|
156
|
+
type: "text",
|
|
157
|
+
text: JSON.stringify({
|
|
158
|
+
success: true,
|
|
159
|
+
amount_sats: data.amount_sats,
|
|
160
|
+
platform_fee_sats: data.platform_fee_sats,
|
|
161
|
+
payment_hash: data.payment_hash,
|
|
162
|
+
status: data.status,
|
|
163
|
+
message: `Paid ${data.amount_sats} sats ⚡`,
|
|
164
|
+
}, null, 2),
|
|
165
|
+
}],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return { content: [{ type: "text", text: `Payment failed: ${JSON.stringify(data)}` }] };
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Tool: Access L402-protected API
|
|
173
|
+
server.tool(
|
|
174
|
+
"access_l402_api",
|
|
175
|
+
"Access an L402-protected API endpoint with automatic Lightning payment",
|
|
176
|
+
{
|
|
177
|
+
endpoint: z.enum(["data", "market", "ai-service", "api-credits"]).describe("L402 endpoint to access"),
|
|
178
|
+
symbol: z.string().optional().default("BTC").describe("Market symbol (for market endpoint)"),
|
|
179
|
+
},
|
|
180
|
+
async ({ endpoint, symbol }) => {
|
|
181
|
+
// Build URL
|
|
182
|
+
let path;
|
|
183
|
+
if (endpoint === "market") {
|
|
184
|
+
path = `/api/v1/l402/market/${symbol}`;
|
|
185
|
+
} else {
|
|
186
|
+
path = `/api/v1/l402/${endpoint}`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Step 1: Hit endpoint, expect 402
|
|
190
|
+
const { status, data } = await apiCall(path);
|
|
191
|
+
|
|
192
|
+
if (status === 200) {
|
|
193
|
+
return {
|
|
194
|
+
content: [{
|
|
195
|
+
type: "text",
|
|
196
|
+
text: JSON.stringify({ success: true, data, amount_paid_sats: 0, message: "Free access" }, null, 2),
|
|
197
|
+
}],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (status !== 402) {
|
|
202
|
+
return { content: [{ type: "text", text: `Unexpected status ${status}: ${JSON.stringify(data)}` }] };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Step 2: Pay Lightning invoice
|
|
206
|
+
const invoice = data.invoice;
|
|
207
|
+
const macaroon = data.macaroon;
|
|
208
|
+
const amount = data.amount || 0;
|
|
209
|
+
|
|
210
|
+
if (!invoice || !LND_HOST) {
|
|
211
|
+
return {
|
|
212
|
+
content: [{
|
|
213
|
+
type: "text",
|
|
214
|
+
text: JSON.stringify({
|
|
215
|
+
success: false,
|
|
216
|
+
error: "Need LND node configured to pay invoice",
|
|
217
|
+
invoice,
|
|
218
|
+
amount,
|
|
219
|
+
}, null, 2),
|
|
220
|
+
}],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Check routing status before paying
|
|
225
|
+
const routingStatus = await checkRoutingStatus();
|
|
226
|
+
if (routingStatus === "suspended") {
|
|
227
|
+
return { content: [{ type: "text", text: "⛔ Account suspended — routing verification failed. Contact support." }] };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Force routing through BK Block node as first hop
|
|
231
|
+
const payRes = await fetch(`${LND_HOST}/v1/channels/transactions`, {
|
|
232
|
+
method: "POST",
|
|
233
|
+
headers: {
|
|
234
|
+
"Grpc-Metadata-macaroon": LND_MACAROON,
|
|
235
|
+
"Content-Type": "application/json",
|
|
236
|
+
},
|
|
237
|
+
body: JSON.stringify({
|
|
238
|
+
payment_request: invoice,
|
|
239
|
+
outgoing_chan_id: "", // LND will use available channels
|
|
240
|
+
fee_limit: { fixed: 5000 }, // Allow routing fees
|
|
241
|
+
}),
|
|
242
|
+
});
|
|
243
|
+
const payData = await payRes.json();
|
|
244
|
+
|
|
245
|
+
if (payData.payment_error) {
|
|
246
|
+
return { content: [{ type: "text", text: `Lightning payment failed: ${payData.payment_error}` }] };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const preimage = payData.payment_preimage;
|
|
250
|
+
if (!preimage) {
|
|
251
|
+
return { content: [{ type: "text", text: "No preimage returned from payment" }] };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Step 3: Access with L402 token
|
|
255
|
+
const authRes = await fetch(`${API_URL}${path}`, {
|
|
256
|
+
headers: {
|
|
257
|
+
"X-API-Key": API_KEY,
|
|
258
|
+
"Authorization": `L402 ${macaroon}:${preimage}`,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (authRes.status === 200) {
|
|
263
|
+
const authData = await authRes.json();
|
|
264
|
+
// Report L402 payment for routing verification
|
|
265
|
+
await reportPayment(
|
|
266
|
+
payData.payment_hash || "",
|
|
267
|
+
amount,
|
|
268
|
+
"",
|
|
269
|
+
[BK_BLOCK_NODE_PUBKEY]
|
|
270
|
+
);
|
|
271
|
+
return {
|
|
272
|
+
content: [{
|
|
273
|
+
type: "text",
|
|
274
|
+
text: JSON.stringify({
|
|
275
|
+
success: true,
|
|
276
|
+
data: authData,
|
|
277
|
+
amount_paid_sats: amount,
|
|
278
|
+
payment_preimage: preimage,
|
|
279
|
+
message: `Paid ${amount} sats ⚡ for ${endpoint} API access`,
|
|
280
|
+
}, null, 2),
|
|
281
|
+
}],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return { content: [{ type: "text", text: `Auth failed after payment: ${await authRes.text()}` }] };
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// Tool: Get Lightning node info
|
|
290
|
+
server.tool(
|
|
291
|
+
"get_node_info",
|
|
292
|
+
"Get Lightning node connection status and info",
|
|
293
|
+
{},
|
|
294
|
+
async () => {
|
|
295
|
+
if (!LND_HOST) {
|
|
296
|
+
return { content: [{ type: "text", text: "No LND node configured" }] };
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
const res = await fetch(`${LND_HOST}/v1/getinfo`, {
|
|
300
|
+
headers: { "Grpc-Metadata-macaroon": LND_MACAROON },
|
|
301
|
+
});
|
|
302
|
+
const data = await res.json();
|
|
303
|
+
return {
|
|
304
|
+
content: [{
|
|
305
|
+
type: "text",
|
|
306
|
+
text: JSON.stringify({
|
|
307
|
+
alias: data.alias,
|
|
308
|
+
pubkey: data.identity_pubkey,
|
|
309
|
+
num_active_channels: data.num_active_channels,
|
|
310
|
+
num_peers: data.num_peers,
|
|
311
|
+
synced_to_chain: data.synced_to_chain,
|
|
312
|
+
version: data.version,
|
|
313
|
+
}, null, 2),
|
|
314
|
+
}],
|
|
315
|
+
};
|
|
316
|
+
} catch (e) {
|
|
317
|
+
return { content: [{ type: "text", text: `LND connection error: ${e.message}` }] };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
// Start server
|
|
323
|
+
const transport = new StdioServerTransport();
|
|
324
|
+
await server.connect(transport);
|