iflow-mcp_breez-breez-mcp 0.1.0__tar.gz
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.
- iflow_mcp_breez_breez_mcp-0.1.0/PKG-INFO +279 -0
- iflow_mcp_breez_breez_mcp-0.1.0/README.md +253 -0
- iflow_mcp_breez_breez_mcp-0.1.0/iflow_mcp_breez_breez_mcp.egg-info/PKG-INFO +279 -0
- iflow_mcp_breez_breez_mcp-0.1.0/iflow_mcp_breez_breez_mcp.egg-info/SOURCES.txt +16 -0
- iflow_mcp_breez_breez_mcp-0.1.0/iflow_mcp_breez_breez_mcp.egg-info/dependency_links.txt +1 -0
- iflow_mcp_breez_breez_mcp-0.1.0/iflow_mcp_breez_breez_mcp.egg-info/entry_points.txt +2 -0
- iflow_mcp_breez_breez_mcp-0.1.0/iflow_mcp_breez_breez_mcp.egg-info/requires.txt +5 -0
- iflow_mcp_breez_breez_mcp-0.1.0/iflow_mcp_breez_breez_mcp.egg-info/top_level.txt +1 -0
- iflow_mcp_breez_breez_mcp-0.1.0/pyproject.toml +48 -0
- iflow_mcp_breez_breez_mcp-0.1.0/setup.cfg +4 -0
- iflow_mcp_breez_breez_mcp-0.1.0/src/__init__.py +0 -0
- iflow_mcp_breez_breez_mcp-0.1.0/src/config.py +25 -0
- iflow_mcp_breez_breez_mcp-0.1.0/src/main.py +282 -0
- iflow_mcp_breez_breez_mcp-0.1.0/src/sdk_manager.py +125 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iflow-mcp_breez-breez-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Breez MCP Server - FastMCP implementation for Lightning wallet functionality
|
|
5
|
+
Author: Breez MCP Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/breez/breez-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/breez/breez-mcp
|
|
9
|
+
Project-URL: Documentation, https://github.com/breez/breez-mcp#readme
|
|
10
|
+
Keywords: mcp,lightning,breez,bitcoin,wallet
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: fastmcp>=0.4.0
|
|
22
|
+
Requires-Dist: breez-sdk-spark>=0.2.7
|
|
23
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0.0
|
|
25
|
+
Requires-Dist: uvicorn[standard]>=0.24.0
|
|
26
|
+
|
|
27
|
+
# Breez MCP Server — FastMCP Implementation
|
|
28
|
+
|
|
29
|
+
A unified MCP server that exposes Lightning functionality through the Breez SDK (Spark implementation) using FastMCP. Supports both stdio and HTTP transport modes.
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
- Python 3.11+ (for local development or `uvx`)
|
|
34
|
+
- [Docker](https://docs.docker.com/get-docker/) (optional, for container workflows)
|
|
35
|
+
- [uv](https://github.com/astral-sh/uv) (optional, for ephemeral environments)
|
|
36
|
+
- Breez API key which you can request [here](https://breez.technology/request-api-key/#contact-us-form-sdk)
|
|
37
|
+
|
|
38
|
+
## Configure Credentials
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cp .env.example .env
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Edit `.env` with your secrets. Required variables:
|
|
45
|
+
|
|
46
|
+
| Variable | Required | Default | Purpose |
|
|
47
|
+
|----------|----------|---------|---------|
|
|
48
|
+
| `BREEZ_API_KEY` | ✅ | – | Breez Spark API key |
|
|
49
|
+
| `BREEZ_MNEMONIC` | ✅ | – | 12-word mnemonic controlling the wallet |
|
|
50
|
+
| `BREEZ_NETWORK` | ❌ | `mainnet` | Set to `testnet` for sandbox usage |
|
|
51
|
+
| `BREEZ_DATA_DIR` | ❌ | `./data` | Wallet storage directory |
|
|
52
|
+
| `BREEZ_TRANSPORT_MODE` | ❌ | `stdio` | Transport mode: `stdio`, `http`, or `asgi` |
|
|
53
|
+
| `BREEZ_HTTP_HOST` | ❌ | `0.0.0.0` | HTTP server host (HTTP mode only) |
|
|
54
|
+
| `BREEZ_HTTP_PORT` | ❌ | `8000` | HTTP server port (HTTP mode only) |
|
|
55
|
+
| `BREEZ_HTTP_PATH` | ❌ | `/mcp` | HTTP endpoint path (HTTP mode only) |
|
|
56
|
+
|
|
57
|
+
## Run the Server
|
|
58
|
+
|
|
59
|
+
Choose the runtime that transport mode that fits your workflow.
|
|
60
|
+
|
|
61
|
+
### STDIO Mode (Default for MCP clients)
|
|
62
|
+
|
|
63
|
+
For use with Claude Desktop and other MCP clients:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Local virtualenv
|
|
67
|
+
python -m venv .venv
|
|
68
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
69
|
+
pip install -r requirements.txt
|
|
70
|
+
python -m src.main
|
|
71
|
+
|
|
72
|
+
# Or with uvx (no persistent venv)
|
|
73
|
+
uvx --from . breez-mcp
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### HTTP Mode (for web API access)
|
|
77
|
+
|
|
78
|
+
For accessing the MCP server via HTTP API:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Set environment variable
|
|
82
|
+
export BREEZ_TRANSPORT_MODE=http
|
|
83
|
+
|
|
84
|
+
# Or add to .env file
|
|
85
|
+
echo "BREEZ_TRANSPORT_MODE=http" >> .env
|
|
86
|
+
|
|
87
|
+
# Run the server
|
|
88
|
+
python -m src.main
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The server will be available at `http://localhost:8000/mcp`
|
|
92
|
+
|
|
93
|
+
### ASGI Mode (for external ASGI servers)
|
|
94
|
+
|
|
95
|
+
For deployment with external ASGI servers like Gunicorn:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Set environment variable
|
|
99
|
+
export BREEZ_TRANSPORT_MODE=asgi
|
|
100
|
+
|
|
101
|
+
# Run with uvicorn
|
|
102
|
+
uvicorn src.main:app --host 0.0.0.0 --port 8000
|
|
103
|
+
|
|
104
|
+
# Or with Gunicorn (production)
|
|
105
|
+
gunicorn src.main:app -w 4 -k uvicorn.workers.UvicornWorker
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Docker Compose
|
|
109
|
+
|
|
110
|
+
Run both modes simultaneously:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# STDIO mode
|
|
114
|
+
docker compose --profile stdio up -d
|
|
115
|
+
docker compose logs -f breez-mcp-stdio
|
|
116
|
+
|
|
117
|
+
# HTTP mode
|
|
118
|
+
docker compose --profile http up -d
|
|
119
|
+
docker compose logs -f breez-mcp-http
|
|
120
|
+
|
|
121
|
+
# Stop
|
|
122
|
+
docker compose --profile http down
|
|
123
|
+
docker compose --profile stdio down
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Docker (direct)
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Build image
|
|
130
|
+
docker build -t breez-mcp .
|
|
131
|
+
|
|
132
|
+
# STDIO mode (default)
|
|
133
|
+
docker run --rm \
|
|
134
|
+
-e BREEZ_API_KEY="$BREEZ_API_KEY" \
|
|
135
|
+
-e BREEZ_MNEMONIC="$BREEZ_MNEMONIC" \
|
|
136
|
+
-v $(pwd)/data:/app/data \
|
|
137
|
+
breez-mcp
|
|
138
|
+
|
|
139
|
+
# HTTP mode
|
|
140
|
+
docker run --rm -p 8000:8000 \
|
|
141
|
+
-e BREEZ_TRANSPORT_MODE=http \
|
|
142
|
+
-e BREEZ_API_KEY="$BREEZ_API_KEY" \
|
|
143
|
+
-e BREEZ_MNEMONIC="$BREEZ_MNEMONIC" \
|
|
144
|
+
-v $(pwd)/data:/app/data \
|
|
145
|
+
breez-mcp
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
To keep STDIN/STDOUT attached for Claude Desktop, add `-i` to the `docker run` command.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## Claude Desktop Integration
|
|
152
|
+
|
|
153
|
+
### Quick install
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
mcp install src.main --name "breez-mcp"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Use `-f .env` or `-v KEY=value` to supply credentials during installation if desired.
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
### Docker from Claude Desktop
|
|
163
|
+
|
|
164
|
+
Ensure the image exists (`docker build -t breez-mcp .`), then configure:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"mcpServers": {
|
|
169
|
+
"breez": {
|
|
170
|
+
"command": "docker",
|
|
171
|
+
"args": [
|
|
172
|
+
"run", "--rm", "-i",
|
|
173
|
+
"-e", "BREEZ_API_KEY",
|
|
174
|
+
"-e", "BREEZ_MNEMONIC",
|
|
175
|
+
"-e", "BREEZ_TRANSPORT_MODE=stdio",
|
|
176
|
+
"-v", "/absolute/path/to/breez-mcp/data:/app/data",
|
|
177
|
+
"breez-mcp"
|
|
178
|
+
],
|
|
179
|
+
"cwd": "/absolute/path/to/breez-mcp",
|
|
180
|
+
"env": {
|
|
181
|
+
"BREEZ_API_KEY": "${env:BREEZ_API_KEY}",
|
|
182
|
+
"BREEZ_MNEMONIC": "${env:BREEZ_MNEMONIC}",
|
|
183
|
+
"BREEZ_NETWORK": "mainnet"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Docker's `-e VAR` syntax reads the value of `VAR` from the environment supplied via the `env` block.
|
|
191
|
+
|
|
192
|
+
### uvx from Claude Desktop
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"mcpServers": {
|
|
197
|
+
"breez": {
|
|
198
|
+
"command": "uvx",
|
|
199
|
+
"args": ["--from", ".", "breez-mcp"],
|
|
200
|
+
"cwd": "/absolute/path/to/breez-mcp",
|
|
201
|
+
"env": {
|
|
202
|
+
"BREEZ_API_KEY": "${env:BREEZ_API_KEY}",
|
|
203
|
+
"BREEZ_MNEMONIC": "${env:BREEZ_MNEMONIC}",
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Verification
|
|
211
|
+
|
|
212
|
+
- Restart Claude Desktop after adding the configuration.
|
|
213
|
+
- Run `mcp list` to ensure the server registered.
|
|
214
|
+
- Ask Claude prompts like “Check my wallet balance” or “Create an invoice for 1000 sats” to validate tool routing.
|
|
215
|
+
|
|
216
|
+
## Available Tools
|
|
217
|
+
|
|
218
|
+
- `get_balance` — comprehensive wallet balance with limits and formatted amounts
|
|
219
|
+
- `get_node_info` — detailed node information including capabilities and sync status
|
|
220
|
+
- `send_payment` — send a Lightning payment with complete transaction details
|
|
221
|
+
- `create_invoice` — generate a BOLT11 invoice with all invoice data
|
|
222
|
+
- `list_payments` — comprehensive payment history with full details
|
|
223
|
+
|
|
224
|
+
## Example Prompts
|
|
225
|
+
|
|
226
|
+
- "Check my wallet balance"
|
|
227
|
+
- "Create an invoice for 1000 sats for coffee"
|
|
228
|
+
- "Send payment to lnbc1…"
|
|
229
|
+
- "Show me my recent payments"
|
|
230
|
+
|
|
231
|
+
## HTTP API Usage (HTTP Mode)
|
|
232
|
+
|
|
233
|
+
When running in HTTP mode, you can interact with the MCP server via HTTP requests:
|
|
234
|
+
|
|
235
|
+
### Health Check
|
|
236
|
+
```bash
|
|
237
|
+
curl http://localhost:8000/health
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### List Available Tools
|
|
241
|
+
```bash
|
|
242
|
+
curl http://localhost:8000/mcp/tools/list
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Call a Tool (MCP Protocol)
|
|
246
|
+
The HTTP mode follows the MCP protocol over HTTP. You'll need to send properly formatted MCP JSON-RPC requests to `http://localhost:8000/mcp`.
|
|
247
|
+
|
|
248
|
+
Example using MCP Inspector or other MCP clients:
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"jsonrpc": "2.0",
|
|
252
|
+
"method": "tools/call",
|
|
253
|
+
"params": {
|
|
254
|
+
"name": "get_balance",
|
|
255
|
+
"arguments": {}
|
|
256
|
+
},
|
|
257
|
+
"id": 1
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Send to:
|
|
262
|
+
```bash
|
|
263
|
+
curl -X POST http://localhost:8000/mcp \
|
|
264
|
+
-H "Content-Type: application/json" \
|
|
265
|
+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_balance","arguments":{}},"id":1}'
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Security Notes
|
|
269
|
+
|
|
270
|
+
- Never commit `.env`; keep secrets in your shell or a secrets manager.
|
|
271
|
+
- Treat the mnemonic as the wallet’s private key. Rotate immediately if leaked.
|
|
272
|
+
- Default network is `mainnet`. For experimentation, explicitly set `BREEZ_NETWORK=testnet`.
|
|
273
|
+
- When using containers, mount `./data` to preserve state between runs and prevent secret leakage in container layers.
|
|
274
|
+
|
|
275
|
+
## Troubleshooting
|
|
276
|
+
|
|
277
|
+
- **Missing environment variables** — ensure `.env` exists or export the required variables before starting.
|
|
278
|
+
- **SDK connection failures** — verify required env vars, try `python list_payments_cli.py --limit 1 --verbose` to confirm SDK connectivity, and check `http://localhost:8000/health` in HTTP mode.
|
|
279
|
+
- **Claude Desktop cannot find the server** — double-check absolute paths in `cwd` and restart the application after configuration changes.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Breez MCP Server — FastMCP Implementation
|
|
2
|
+
|
|
3
|
+
A unified MCP server that exposes Lightning functionality through the Breez SDK (Spark implementation) using FastMCP. Supports both stdio and HTTP transport modes.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- Python 3.11+ (for local development or `uvx`)
|
|
8
|
+
- [Docker](https://docs.docker.com/get-docker/) (optional, for container workflows)
|
|
9
|
+
- [uv](https://github.com/astral-sh/uv) (optional, for ephemeral environments)
|
|
10
|
+
- Breez API key which you can request [here](https://breez.technology/request-api-key/#contact-us-form-sdk)
|
|
11
|
+
|
|
12
|
+
## Configure Credentials
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cp .env.example .env
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Edit `.env` with your secrets. Required variables:
|
|
19
|
+
|
|
20
|
+
| Variable | Required | Default | Purpose |
|
|
21
|
+
|----------|----------|---------|---------|
|
|
22
|
+
| `BREEZ_API_KEY` | ✅ | – | Breez Spark API key |
|
|
23
|
+
| `BREEZ_MNEMONIC` | ✅ | – | 12-word mnemonic controlling the wallet |
|
|
24
|
+
| `BREEZ_NETWORK` | ❌ | `mainnet` | Set to `testnet` for sandbox usage |
|
|
25
|
+
| `BREEZ_DATA_DIR` | ❌ | `./data` | Wallet storage directory |
|
|
26
|
+
| `BREEZ_TRANSPORT_MODE` | ❌ | `stdio` | Transport mode: `stdio`, `http`, or `asgi` |
|
|
27
|
+
| `BREEZ_HTTP_HOST` | ❌ | `0.0.0.0` | HTTP server host (HTTP mode only) |
|
|
28
|
+
| `BREEZ_HTTP_PORT` | ❌ | `8000` | HTTP server port (HTTP mode only) |
|
|
29
|
+
| `BREEZ_HTTP_PATH` | ❌ | `/mcp` | HTTP endpoint path (HTTP mode only) |
|
|
30
|
+
|
|
31
|
+
## Run the Server
|
|
32
|
+
|
|
33
|
+
Choose the runtime that transport mode that fits your workflow.
|
|
34
|
+
|
|
35
|
+
### STDIO Mode (Default for MCP clients)
|
|
36
|
+
|
|
37
|
+
For use with Claude Desktop and other MCP clients:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Local virtualenv
|
|
41
|
+
python -m venv .venv
|
|
42
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
43
|
+
pip install -r requirements.txt
|
|
44
|
+
python -m src.main
|
|
45
|
+
|
|
46
|
+
# Or with uvx (no persistent venv)
|
|
47
|
+
uvx --from . breez-mcp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### HTTP Mode (for web API access)
|
|
51
|
+
|
|
52
|
+
For accessing the MCP server via HTTP API:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Set environment variable
|
|
56
|
+
export BREEZ_TRANSPORT_MODE=http
|
|
57
|
+
|
|
58
|
+
# Or add to .env file
|
|
59
|
+
echo "BREEZ_TRANSPORT_MODE=http" >> .env
|
|
60
|
+
|
|
61
|
+
# Run the server
|
|
62
|
+
python -m src.main
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The server will be available at `http://localhost:8000/mcp`
|
|
66
|
+
|
|
67
|
+
### ASGI Mode (for external ASGI servers)
|
|
68
|
+
|
|
69
|
+
For deployment with external ASGI servers like Gunicorn:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Set environment variable
|
|
73
|
+
export BREEZ_TRANSPORT_MODE=asgi
|
|
74
|
+
|
|
75
|
+
# Run with uvicorn
|
|
76
|
+
uvicorn src.main:app --host 0.0.0.0 --port 8000
|
|
77
|
+
|
|
78
|
+
# Or with Gunicorn (production)
|
|
79
|
+
gunicorn src.main:app -w 4 -k uvicorn.workers.UvicornWorker
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Docker Compose
|
|
83
|
+
|
|
84
|
+
Run both modes simultaneously:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# STDIO mode
|
|
88
|
+
docker compose --profile stdio up -d
|
|
89
|
+
docker compose logs -f breez-mcp-stdio
|
|
90
|
+
|
|
91
|
+
# HTTP mode
|
|
92
|
+
docker compose --profile http up -d
|
|
93
|
+
docker compose logs -f breez-mcp-http
|
|
94
|
+
|
|
95
|
+
# Stop
|
|
96
|
+
docker compose --profile http down
|
|
97
|
+
docker compose --profile stdio down
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Docker (direct)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Build image
|
|
104
|
+
docker build -t breez-mcp .
|
|
105
|
+
|
|
106
|
+
# STDIO mode (default)
|
|
107
|
+
docker run --rm \
|
|
108
|
+
-e BREEZ_API_KEY="$BREEZ_API_KEY" \
|
|
109
|
+
-e BREEZ_MNEMONIC="$BREEZ_MNEMONIC" \
|
|
110
|
+
-v $(pwd)/data:/app/data \
|
|
111
|
+
breez-mcp
|
|
112
|
+
|
|
113
|
+
# HTTP mode
|
|
114
|
+
docker run --rm -p 8000:8000 \
|
|
115
|
+
-e BREEZ_TRANSPORT_MODE=http \
|
|
116
|
+
-e BREEZ_API_KEY="$BREEZ_API_KEY" \
|
|
117
|
+
-e BREEZ_MNEMONIC="$BREEZ_MNEMONIC" \
|
|
118
|
+
-v $(pwd)/data:/app/data \
|
|
119
|
+
breez-mcp
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
To keep STDIN/STDOUT attached for Claude Desktop, add `-i` to the `docker run` command.
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
## Claude Desktop Integration
|
|
126
|
+
|
|
127
|
+
### Quick install
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
mcp install src.main --name "breez-mcp"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Use `-f .env` or `-v KEY=value` to supply credentials during installation if desired.
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
### Docker from Claude Desktop
|
|
137
|
+
|
|
138
|
+
Ensure the image exists (`docker build -t breez-mcp .`), then configure:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"mcpServers": {
|
|
143
|
+
"breez": {
|
|
144
|
+
"command": "docker",
|
|
145
|
+
"args": [
|
|
146
|
+
"run", "--rm", "-i",
|
|
147
|
+
"-e", "BREEZ_API_KEY",
|
|
148
|
+
"-e", "BREEZ_MNEMONIC",
|
|
149
|
+
"-e", "BREEZ_TRANSPORT_MODE=stdio",
|
|
150
|
+
"-v", "/absolute/path/to/breez-mcp/data:/app/data",
|
|
151
|
+
"breez-mcp"
|
|
152
|
+
],
|
|
153
|
+
"cwd": "/absolute/path/to/breez-mcp",
|
|
154
|
+
"env": {
|
|
155
|
+
"BREEZ_API_KEY": "${env:BREEZ_API_KEY}",
|
|
156
|
+
"BREEZ_MNEMONIC": "${env:BREEZ_MNEMONIC}",
|
|
157
|
+
"BREEZ_NETWORK": "mainnet"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Docker's `-e VAR` syntax reads the value of `VAR` from the environment supplied via the `env` block.
|
|
165
|
+
|
|
166
|
+
### uvx from Claude Desktop
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{
|
|
170
|
+
"mcpServers": {
|
|
171
|
+
"breez": {
|
|
172
|
+
"command": "uvx",
|
|
173
|
+
"args": ["--from", ".", "breez-mcp"],
|
|
174
|
+
"cwd": "/absolute/path/to/breez-mcp",
|
|
175
|
+
"env": {
|
|
176
|
+
"BREEZ_API_KEY": "${env:BREEZ_API_KEY}",
|
|
177
|
+
"BREEZ_MNEMONIC": "${env:BREEZ_MNEMONIC}",
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Verification
|
|
185
|
+
|
|
186
|
+
- Restart Claude Desktop after adding the configuration.
|
|
187
|
+
- Run `mcp list` to ensure the server registered.
|
|
188
|
+
- Ask Claude prompts like “Check my wallet balance” or “Create an invoice for 1000 sats” to validate tool routing.
|
|
189
|
+
|
|
190
|
+
## Available Tools
|
|
191
|
+
|
|
192
|
+
- `get_balance` — comprehensive wallet balance with limits and formatted amounts
|
|
193
|
+
- `get_node_info` — detailed node information including capabilities and sync status
|
|
194
|
+
- `send_payment` — send a Lightning payment with complete transaction details
|
|
195
|
+
- `create_invoice` — generate a BOLT11 invoice with all invoice data
|
|
196
|
+
- `list_payments` — comprehensive payment history with full details
|
|
197
|
+
|
|
198
|
+
## Example Prompts
|
|
199
|
+
|
|
200
|
+
- "Check my wallet balance"
|
|
201
|
+
- "Create an invoice for 1000 sats for coffee"
|
|
202
|
+
- "Send payment to lnbc1…"
|
|
203
|
+
- "Show me my recent payments"
|
|
204
|
+
|
|
205
|
+
## HTTP API Usage (HTTP Mode)
|
|
206
|
+
|
|
207
|
+
When running in HTTP mode, you can interact with the MCP server via HTTP requests:
|
|
208
|
+
|
|
209
|
+
### Health Check
|
|
210
|
+
```bash
|
|
211
|
+
curl http://localhost:8000/health
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### List Available Tools
|
|
215
|
+
```bash
|
|
216
|
+
curl http://localhost:8000/mcp/tools/list
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Call a Tool (MCP Protocol)
|
|
220
|
+
The HTTP mode follows the MCP protocol over HTTP. You'll need to send properly formatted MCP JSON-RPC requests to `http://localhost:8000/mcp`.
|
|
221
|
+
|
|
222
|
+
Example using MCP Inspector or other MCP clients:
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"jsonrpc": "2.0",
|
|
226
|
+
"method": "tools/call",
|
|
227
|
+
"params": {
|
|
228
|
+
"name": "get_balance",
|
|
229
|
+
"arguments": {}
|
|
230
|
+
},
|
|
231
|
+
"id": 1
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Send to:
|
|
236
|
+
```bash
|
|
237
|
+
curl -X POST http://localhost:8000/mcp \
|
|
238
|
+
-H "Content-Type: application/json" \
|
|
239
|
+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_balance","arguments":{}},"id":1}'
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Security Notes
|
|
243
|
+
|
|
244
|
+
- Never commit `.env`; keep secrets in your shell or a secrets manager.
|
|
245
|
+
- Treat the mnemonic as the wallet’s private key. Rotate immediately if leaked.
|
|
246
|
+
- Default network is `mainnet`. For experimentation, explicitly set `BREEZ_NETWORK=testnet`.
|
|
247
|
+
- When using containers, mount `./data` to preserve state between runs and prevent secret leakage in container layers.
|
|
248
|
+
|
|
249
|
+
## Troubleshooting
|
|
250
|
+
|
|
251
|
+
- **Missing environment variables** — ensure `.env` exists or export the required variables before starting.
|
|
252
|
+
- **SDK connection failures** — verify required env vars, try `python list_payments_cli.py --limit 1 --verbose` to confirm SDK connectivity, and check `http://localhost:8000/health` in HTTP mode.
|
|
253
|
+
- **Claude Desktop cannot find the server** — double-check absolute paths in `cwd` and restart the application after configuration changes.
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iflow-mcp_breez-breez-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Breez MCP Server - FastMCP implementation for Lightning wallet functionality
|
|
5
|
+
Author: Breez MCP Contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/breez/breez-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/breez/breez-mcp
|
|
9
|
+
Project-URL: Documentation, https://github.com/breez/breez-mcp#readme
|
|
10
|
+
Keywords: mcp,lightning,breez,bitcoin,wallet
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: fastmcp>=0.4.0
|
|
22
|
+
Requires-Dist: breez-sdk-spark>=0.2.7
|
|
23
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0.0
|
|
25
|
+
Requires-Dist: uvicorn[standard]>=0.24.0
|
|
26
|
+
|
|
27
|
+
# Breez MCP Server — FastMCP Implementation
|
|
28
|
+
|
|
29
|
+
A unified MCP server that exposes Lightning functionality through the Breez SDK (Spark implementation) using FastMCP. Supports both stdio and HTTP transport modes.
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
- Python 3.11+ (for local development or `uvx`)
|
|
34
|
+
- [Docker](https://docs.docker.com/get-docker/) (optional, for container workflows)
|
|
35
|
+
- [uv](https://github.com/astral-sh/uv) (optional, for ephemeral environments)
|
|
36
|
+
- Breez API key which you can request [here](https://breez.technology/request-api-key/#contact-us-form-sdk)
|
|
37
|
+
|
|
38
|
+
## Configure Credentials
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cp .env.example .env
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Edit `.env` with your secrets. Required variables:
|
|
45
|
+
|
|
46
|
+
| Variable | Required | Default | Purpose |
|
|
47
|
+
|----------|----------|---------|---------|
|
|
48
|
+
| `BREEZ_API_KEY` | ✅ | – | Breez Spark API key |
|
|
49
|
+
| `BREEZ_MNEMONIC` | ✅ | – | 12-word mnemonic controlling the wallet |
|
|
50
|
+
| `BREEZ_NETWORK` | ❌ | `mainnet` | Set to `testnet` for sandbox usage |
|
|
51
|
+
| `BREEZ_DATA_DIR` | ❌ | `./data` | Wallet storage directory |
|
|
52
|
+
| `BREEZ_TRANSPORT_MODE` | ❌ | `stdio` | Transport mode: `stdio`, `http`, or `asgi` |
|
|
53
|
+
| `BREEZ_HTTP_HOST` | ❌ | `0.0.0.0` | HTTP server host (HTTP mode only) |
|
|
54
|
+
| `BREEZ_HTTP_PORT` | ❌ | `8000` | HTTP server port (HTTP mode only) |
|
|
55
|
+
| `BREEZ_HTTP_PATH` | ❌ | `/mcp` | HTTP endpoint path (HTTP mode only) |
|
|
56
|
+
|
|
57
|
+
## Run the Server
|
|
58
|
+
|
|
59
|
+
Choose the runtime that transport mode that fits your workflow.
|
|
60
|
+
|
|
61
|
+
### STDIO Mode (Default for MCP clients)
|
|
62
|
+
|
|
63
|
+
For use with Claude Desktop and other MCP clients:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Local virtualenv
|
|
67
|
+
python -m venv .venv
|
|
68
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
69
|
+
pip install -r requirements.txt
|
|
70
|
+
python -m src.main
|
|
71
|
+
|
|
72
|
+
# Or with uvx (no persistent venv)
|
|
73
|
+
uvx --from . breez-mcp
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### HTTP Mode (for web API access)
|
|
77
|
+
|
|
78
|
+
For accessing the MCP server via HTTP API:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Set environment variable
|
|
82
|
+
export BREEZ_TRANSPORT_MODE=http
|
|
83
|
+
|
|
84
|
+
# Or add to .env file
|
|
85
|
+
echo "BREEZ_TRANSPORT_MODE=http" >> .env
|
|
86
|
+
|
|
87
|
+
# Run the server
|
|
88
|
+
python -m src.main
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The server will be available at `http://localhost:8000/mcp`
|
|
92
|
+
|
|
93
|
+
### ASGI Mode (for external ASGI servers)
|
|
94
|
+
|
|
95
|
+
For deployment with external ASGI servers like Gunicorn:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Set environment variable
|
|
99
|
+
export BREEZ_TRANSPORT_MODE=asgi
|
|
100
|
+
|
|
101
|
+
# Run with uvicorn
|
|
102
|
+
uvicorn src.main:app --host 0.0.0.0 --port 8000
|
|
103
|
+
|
|
104
|
+
# Or with Gunicorn (production)
|
|
105
|
+
gunicorn src.main:app -w 4 -k uvicorn.workers.UvicornWorker
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Docker Compose
|
|
109
|
+
|
|
110
|
+
Run both modes simultaneously:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# STDIO mode
|
|
114
|
+
docker compose --profile stdio up -d
|
|
115
|
+
docker compose logs -f breez-mcp-stdio
|
|
116
|
+
|
|
117
|
+
# HTTP mode
|
|
118
|
+
docker compose --profile http up -d
|
|
119
|
+
docker compose logs -f breez-mcp-http
|
|
120
|
+
|
|
121
|
+
# Stop
|
|
122
|
+
docker compose --profile http down
|
|
123
|
+
docker compose --profile stdio down
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Docker (direct)
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Build image
|
|
130
|
+
docker build -t breez-mcp .
|
|
131
|
+
|
|
132
|
+
# STDIO mode (default)
|
|
133
|
+
docker run --rm \
|
|
134
|
+
-e BREEZ_API_KEY="$BREEZ_API_KEY" \
|
|
135
|
+
-e BREEZ_MNEMONIC="$BREEZ_MNEMONIC" \
|
|
136
|
+
-v $(pwd)/data:/app/data \
|
|
137
|
+
breez-mcp
|
|
138
|
+
|
|
139
|
+
# HTTP mode
|
|
140
|
+
docker run --rm -p 8000:8000 \
|
|
141
|
+
-e BREEZ_TRANSPORT_MODE=http \
|
|
142
|
+
-e BREEZ_API_KEY="$BREEZ_API_KEY" \
|
|
143
|
+
-e BREEZ_MNEMONIC="$BREEZ_MNEMONIC" \
|
|
144
|
+
-v $(pwd)/data:/app/data \
|
|
145
|
+
breez-mcp
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
To keep STDIN/STDOUT attached for Claude Desktop, add `-i` to the `docker run` command.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
## Claude Desktop Integration
|
|
152
|
+
|
|
153
|
+
### Quick install
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
mcp install src.main --name "breez-mcp"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Use `-f .env` or `-v KEY=value` to supply credentials during installation if desired.
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
### Docker from Claude Desktop
|
|
163
|
+
|
|
164
|
+
Ensure the image exists (`docker build -t breez-mcp .`), then configure:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"mcpServers": {
|
|
169
|
+
"breez": {
|
|
170
|
+
"command": "docker",
|
|
171
|
+
"args": [
|
|
172
|
+
"run", "--rm", "-i",
|
|
173
|
+
"-e", "BREEZ_API_KEY",
|
|
174
|
+
"-e", "BREEZ_MNEMONIC",
|
|
175
|
+
"-e", "BREEZ_TRANSPORT_MODE=stdio",
|
|
176
|
+
"-v", "/absolute/path/to/breez-mcp/data:/app/data",
|
|
177
|
+
"breez-mcp"
|
|
178
|
+
],
|
|
179
|
+
"cwd": "/absolute/path/to/breez-mcp",
|
|
180
|
+
"env": {
|
|
181
|
+
"BREEZ_API_KEY": "${env:BREEZ_API_KEY}",
|
|
182
|
+
"BREEZ_MNEMONIC": "${env:BREEZ_MNEMONIC}",
|
|
183
|
+
"BREEZ_NETWORK": "mainnet"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Docker's `-e VAR` syntax reads the value of `VAR` from the environment supplied via the `env` block.
|
|
191
|
+
|
|
192
|
+
### uvx from Claude Desktop
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"mcpServers": {
|
|
197
|
+
"breez": {
|
|
198
|
+
"command": "uvx",
|
|
199
|
+
"args": ["--from", ".", "breez-mcp"],
|
|
200
|
+
"cwd": "/absolute/path/to/breez-mcp",
|
|
201
|
+
"env": {
|
|
202
|
+
"BREEZ_API_KEY": "${env:BREEZ_API_KEY}",
|
|
203
|
+
"BREEZ_MNEMONIC": "${env:BREEZ_MNEMONIC}",
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Verification
|
|
211
|
+
|
|
212
|
+
- Restart Claude Desktop after adding the configuration.
|
|
213
|
+
- Run `mcp list` to ensure the server registered.
|
|
214
|
+
- Ask Claude prompts like “Check my wallet balance” or “Create an invoice for 1000 sats” to validate tool routing.
|
|
215
|
+
|
|
216
|
+
## Available Tools
|
|
217
|
+
|
|
218
|
+
- `get_balance` — comprehensive wallet balance with limits and formatted amounts
|
|
219
|
+
- `get_node_info` — detailed node information including capabilities and sync status
|
|
220
|
+
- `send_payment` — send a Lightning payment with complete transaction details
|
|
221
|
+
- `create_invoice` — generate a BOLT11 invoice with all invoice data
|
|
222
|
+
- `list_payments` — comprehensive payment history with full details
|
|
223
|
+
|
|
224
|
+
## Example Prompts
|
|
225
|
+
|
|
226
|
+
- "Check my wallet balance"
|
|
227
|
+
- "Create an invoice for 1000 sats for coffee"
|
|
228
|
+
- "Send payment to lnbc1…"
|
|
229
|
+
- "Show me my recent payments"
|
|
230
|
+
|
|
231
|
+
## HTTP API Usage (HTTP Mode)
|
|
232
|
+
|
|
233
|
+
When running in HTTP mode, you can interact with the MCP server via HTTP requests:
|
|
234
|
+
|
|
235
|
+
### Health Check
|
|
236
|
+
```bash
|
|
237
|
+
curl http://localhost:8000/health
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### List Available Tools
|
|
241
|
+
```bash
|
|
242
|
+
curl http://localhost:8000/mcp/tools/list
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Call a Tool (MCP Protocol)
|
|
246
|
+
The HTTP mode follows the MCP protocol over HTTP. You'll need to send properly formatted MCP JSON-RPC requests to `http://localhost:8000/mcp`.
|
|
247
|
+
|
|
248
|
+
Example using MCP Inspector or other MCP clients:
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"jsonrpc": "2.0",
|
|
252
|
+
"method": "tools/call",
|
|
253
|
+
"params": {
|
|
254
|
+
"name": "get_balance",
|
|
255
|
+
"arguments": {}
|
|
256
|
+
},
|
|
257
|
+
"id": 1
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Send to:
|
|
262
|
+
```bash
|
|
263
|
+
curl -X POST http://localhost:8000/mcp \
|
|
264
|
+
-H "Content-Type: application/json" \
|
|
265
|
+
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_balance","arguments":{}},"id":1}'
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Security Notes
|
|
269
|
+
|
|
270
|
+
- Never commit `.env`; keep secrets in your shell or a secrets manager.
|
|
271
|
+
- Treat the mnemonic as the wallet’s private key. Rotate immediately if leaked.
|
|
272
|
+
- Default network is `mainnet`. For experimentation, explicitly set `BREEZ_NETWORK=testnet`.
|
|
273
|
+
- When using containers, mount `./data` to preserve state between runs and prevent secret leakage in container layers.
|
|
274
|
+
|
|
275
|
+
## Troubleshooting
|
|
276
|
+
|
|
277
|
+
- **Missing environment variables** — ensure `.env` exists or export the required variables before starting.
|
|
278
|
+
- **SDK connection failures** — verify required env vars, try `python list_payments_cli.py --limit 1 --verbose` to confirm SDK connectivity, and check `http://localhost:8000/health` in HTTP mode.
|
|
279
|
+
- **Claude Desktop cannot find the server** — double-check absolute paths in `cwd` and restart the application after configuration changes.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
./src/__init__.py
|
|
4
|
+
./src/config.py
|
|
5
|
+
./src/main.py
|
|
6
|
+
./src/sdk_manager.py
|
|
7
|
+
iflow_mcp_breez_breez_mcp.egg-info/PKG-INFO
|
|
8
|
+
iflow_mcp_breez_breez_mcp.egg-info/SOURCES.txt
|
|
9
|
+
iflow_mcp_breez_breez_mcp.egg-info/dependency_links.txt
|
|
10
|
+
iflow_mcp_breez_breez_mcp.egg-info/entry_points.txt
|
|
11
|
+
iflow_mcp_breez_breez_mcp.egg-info/requires.txt
|
|
12
|
+
iflow_mcp_breez_breez_mcp.egg-info/top_level.txt
|
|
13
|
+
src/__init__.py
|
|
14
|
+
src/config.py
|
|
15
|
+
src/main.py
|
|
16
|
+
src/sdk_manager.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
src
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "iflow-mcp_breez-breez-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Breez MCP Server - FastMCP implementation for Lightning wallet functionality"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Breez MCP Contributors"}
|
|
14
|
+
]
|
|
15
|
+
keywords = ["mcp", "lightning", "breez", "bitcoin", "wallet"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
"Topic :: Office/Business :: Financial",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
dependencies = [
|
|
28
|
+
"fastmcp>=0.4.0",
|
|
29
|
+
"breez-sdk-spark>=0.2.7",
|
|
30
|
+
"python-dotenv>=1.0.0",
|
|
31
|
+
"pydantic>=2.0.0",
|
|
32
|
+
"uvicorn[standard]>=0.24.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/breez/breez-mcp"
|
|
37
|
+
Repository = "https://github.com/breez/breez-mcp"
|
|
38
|
+
Documentation = "https://github.com/breez/breez-mcp#readme"
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
iflow-mcp_breez-breez-mcp = "src.main:run"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["."]
|
|
45
|
+
include = ["src*"]
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.package-dir]
|
|
48
|
+
"" = "."
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dotenv import load_dotenv
|
|
3
|
+
from breez_sdk_spark import Network
|
|
4
|
+
|
|
5
|
+
load_dotenv()
|
|
6
|
+
|
|
7
|
+
class Config:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
# Check if we're in test mode
|
|
10
|
+
self.test_mode = os.getenv("BREEZ_TEST_MODE", "false").lower() == "true"
|
|
11
|
+
|
|
12
|
+
if self.test_mode:
|
|
13
|
+
# In test mode, use dummy values
|
|
14
|
+
self.api_key = "test_api_key"
|
|
15
|
+
self.mnemonic = "test mnemonic word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12"
|
|
16
|
+
else:
|
|
17
|
+
# In production mode, require real values
|
|
18
|
+
self.api_key = os.getenv("BREEZ_API_KEY")
|
|
19
|
+
self.mnemonic = os.getenv("BREEZ_MNEMONIC")
|
|
20
|
+
|
|
21
|
+
if not all([self.api_key, self.mnemonic]):
|
|
22
|
+
raise ValueError("Missing required environment variables: BREEZ_API_KEY, BREEZ_MNEMONIC")
|
|
23
|
+
|
|
24
|
+
self.network = Network.TESTNET if os.getenv("BREEZ_NETWORK", "mainnet").lower() == "testnet" else Network.MAINNET
|
|
25
|
+
self.data_dir = os.getenv("BREEZ_DATA_DIR", "./data")
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import json
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
from typing import Annotated, Dict, Any, List
|
|
8
|
+
|
|
9
|
+
# Import standard MCP SDK
|
|
10
|
+
from mcp.server.stdio import stdio_server
|
|
11
|
+
from mcp.server import Server
|
|
12
|
+
from mcp.types import Tool, TextContent
|
|
13
|
+
from pydantic import Field
|
|
14
|
+
|
|
15
|
+
from .config import Config
|
|
16
|
+
from .sdk_manager import SDKManager
|
|
17
|
+
|
|
18
|
+
# Global SDK manager
|
|
19
|
+
sdk_manager = None
|
|
20
|
+
|
|
21
|
+
# Create MCP server
|
|
22
|
+
server = Server("breez-mcp")
|
|
23
|
+
|
|
24
|
+
async def initialize_sdk():
|
|
25
|
+
"""Initialize SDK"""
|
|
26
|
+
global sdk_manager
|
|
27
|
+
logging.info("Starting Breez MCP server...")
|
|
28
|
+
sdk_manager = SDKManager()
|
|
29
|
+
await sdk_manager.connect()
|
|
30
|
+
|
|
31
|
+
async def cleanup_sdk():
|
|
32
|
+
"""Cleanup SDK"""
|
|
33
|
+
global sdk_manager
|
|
34
|
+
if sdk_manager:
|
|
35
|
+
await sdk_manager.disconnect()
|
|
36
|
+
logging.info("Server shutdown complete")
|
|
37
|
+
|
|
38
|
+
@server.list_tools()
|
|
39
|
+
async def list_tools() -> List[Tool]:
|
|
40
|
+
"""List available tools"""
|
|
41
|
+
return [
|
|
42
|
+
Tool(
|
|
43
|
+
name="get_balance",
|
|
44
|
+
description="Get wallet balance with limits and formatted amounts",
|
|
45
|
+
inputSchema={"type": "object", "properties": {}}
|
|
46
|
+
),
|
|
47
|
+
Tool(
|
|
48
|
+
name="get_node_info",
|
|
49
|
+
description="Get detailed node information including capabilities and sync status",
|
|
50
|
+
inputSchema={"type": "object", "properties": {}}
|
|
51
|
+
),
|
|
52
|
+
Tool(
|
|
53
|
+
name="send_payment",
|
|
54
|
+
description="Send a Lightning payment with complete transaction details",
|
|
55
|
+
inputSchema={
|
|
56
|
+
"type": "object",
|
|
57
|
+
"properties": {
|
|
58
|
+
"invoice": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "BOLT11 invoice to pay"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"required": ["invoice"]
|
|
64
|
+
}
|
|
65
|
+
),
|
|
66
|
+
Tool(
|
|
67
|
+
name="create_invoice",
|
|
68
|
+
description="Generate a BOLT11 invoice with all invoice data",
|
|
69
|
+
inputSchema={
|
|
70
|
+
"type": "object",
|
|
71
|
+
"properties": {
|
|
72
|
+
"amount_sats": {
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"description": "Amount in satoshis",
|
|
75
|
+
"minimum": 1
|
|
76
|
+
},
|
|
77
|
+
"description": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "Payment description",
|
|
80
|
+
"default": "MCP Payment"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"required": ["amount_sats"]
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
Tool(
|
|
87
|
+
name="list_payments",
|
|
88
|
+
description="List recent payments with full details",
|
|
89
|
+
inputSchema={
|
|
90
|
+
"type": "object",
|
|
91
|
+
"properties": {
|
|
92
|
+
"limit": {
|
|
93
|
+
"type": "integer",
|
|
94
|
+
"description": "Number of payments to return",
|
|
95
|
+
"minimum": 1,
|
|
96
|
+
"maximum": 100,
|
|
97
|
+
"default": 10
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
@server.call_tool()
|
|
105
|
+
async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]:
|
|
106
|
+
"""Call a tool"""
|
|
107
|
+
if not sdk_manager:
|
|
108
|
+
return [TextContent(type="text", text="SDK not initialized")]
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
if name == "get_balance":
|
|
112
|
+
result = await get_balance()
|
|
113
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
114
|
+
|
|
115
|
+
elif name == "get_node_info":
|
|
116
|
+
result = await get_node_info()
|
|
117
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
118
|
+
|
|
119
|
+
elif name == "send_payment":
|
|
120
|
+
invoice = arguments.get("invoice")
|
|
121
|
+
result = await send_payment(invoice)
|
|
122
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
123
|
+
|
|
124
|
+
elif name == "create_invoice":
|
|
125
|
+
amount_sats = arguments.get("amount_sats")
|
|
126
|
+
description = arguments.get("description", "MCP Payment")
|
|
127
|
+
result = await create_invoice(amount_sats, description)
|
|
128
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
129
|
+
|
|
130
|
+
elif name == "list_payments":
|
|
131
|
+
limit = arguments.get("limit", 10)
|
|
132
|
+
result = await list_payments(limit)
|
|
133
|
+
return [TextContent(type="text", text=json.dumps(result, indent=2))]
|
|
134
|
+
|
|
135
|
+
else:
|
|
136
|
+
return [TextContent(type="text", text=f"Unknown tool: {name}")]
|
|
137
|
+
|
|
138
|
+
except Exception as e:
|
|
139
|
+
logging.error(f"Error calling tool {name}: {e}")
|
|
140
|
+
return [TextContent(type="text", text=json.dumps({"error": str(e)}))]
|
|
141
|
+
|
|
142
|
+
async def get_balance() -> Dict[str, Any]:
|
|
143
|
+
"""Get wallet balance"""
|
|
144
|
+
from breez_sdk_spark import GetInfoRequest
|
|
145
|
+
info = await sdk_manager.get_sdk().get_info(request=GetInfoRequest(ensure_synced=True))
|
|
146
|
+
|
|
147
|
+
balance = {
|
|
148
|
+
"balance_sat": getattr(info, 'balance_sats', 0) if hasattr(info, 'balance_sats') else 0,
|
|
149
|
+
"pending_incoming_sat": getattr(info, 'pending_incoming_sats', 0) if hasattr(info, 'pending_incoming_sats') else 0,
|
|
150
|
+
"pending_outgoing_sat": getattr(info, 'pending_outgoing_sats', 0) if hasattr(info, 'pending_outgoing_sats') else 0,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if hasattr(info, 'max_payable_sats'):
|
|
154
|
+
balance["max_payable_sat"] = info.max_payable_sats
|
|
155
|
+
if hasattr(info, 'max_receivable_sats'):
|
|
156
|
+
balance["max_receivable_sat"] = info.max_receivable_sats
|
|
157
|
+
|
|
158
|
+
balance["balance_formatted"] = f"{balance['balance_sat']:,} sats"
|
|
159
|
+
return balance
|
|
160
|
+
|
|
161
|
+
async def get_node_info() -> Dict[str, Any]:
|
|
162
|
+
"""Get node information"""
|
|
163
|
+
from breez_sdk_spark import GetInfoRequest
|
|
164
|
+
info = await sdk_manager.get_sdk().get_info(request=GetInfoRequest(ensure_synced=True))
|
|
165
|
+
|
|
166
|
+
node_info = {}
|
|
167
|
+
for attr in ['id', 'node_id', 'nodeId', 'pubkey', 'public_key', 'node_pubkey']:
|
|
168
|
+
if hasattr(info, attr):
|
|
169
|
+
node_info["node_id"] = getattr(info, attr)
|
|
170
|
+
break
|
|
171
|
+
|
|
172
|
+
node_info["network"] = str(getattr(info, 'network', 'unknown')) if hasattr(info, 'network') else 'unknown'
|
|
173
|
+
node_info["channels_count"] = len(info.channels) if hasattr(info, 'channels') and info.channels else 0
|
|
174
|
+
node_info["synced"] = getattr(info, 'synced', True) if hasattr(info, 'synced') else True
|
|
175
|
+
|
|
176
|
+
if hasattr(info, 'balance_sats'):
|
|
177
|
+
node_info["balance_sat"] = info.balance_sats
|
|
178
|
+
node_info["balance_formatted"] = f"{info.balance_sats:,} sats"
|
|
179
|
+
|
|
180
|
+
return node_info
|
|
181
|
+
|
|
182
|
+
async def send_payment(invoice: str) -> Dict[str, Any]:
|
|
183
|
+
"""Send a Lightning payment"""
|
|
184
|
+
from breez_sdk_spark import PrepareSendPaymentRequest, SendPaymentRequest
|
|
185
|
+
|
|
186
|
+
sdk = sdk_manager.get_sdk()
|
|
187
|
+
prepare_request = PrepareSendPaymentRequest(payment_request=invoice)
|
|
188
|
+
prepare_response = await sdk.prepare_send_payment(request=prepare_request)
|
|
189
|
+
|
|
190
|
+
send_request = SendPaymentRequest(prepare_response=prepare_response)
|
|
191
|
+
send_response = await sdk.send_payment(request=send_request)
|
|
192
|
+
|
|
193
|
+
result = {"status": "success", "message": "Payment sent successfully"}
|
|
194
|
+
|
|
195
|
+
if hasattr(send_response, 'payment') and send_response.payment:
|
|
196
|
+
payment = send_response.payment
|
|
197
|
+
result["id"] = getattr(payment, 'id', None)
|
|
198
|
+
result["amount_sat"] = getattr(payment, 'amount', None)
|
|
199
|
+
result["tx_id"] = getattr(payment, 'tx_id', None)
|
|
200
|
+
|
|
201
|
+
if hasattr(send_response, 'payment_hash'):
|
|
202
|
+
result["txid"] = send_response.payment_hash
|
|
203
|
+
|
|
204
|
+
return result
|
|
205
|
+
|
|
206
|
+
async def create_invoice(amount_sats: int, description: str = "MCP Payment") -> Dict[str, Any]:
|
|
207
|
+
"""Create a Lightning invoice"""
|
|
208
|
+
from breez_sdk_spark import ReceivePaymentRequest, ReceivePaymentMethod
|
|
209
|
+
|
|
210
|
+
sdk = sdk_manager.get_sdk()
|
|
211
|
+
payment_method = ReceivePaymentMethod.BOLT11_INVOICE(
|
|
212
|
+
description=description,
|
|
213
|
+
amount_sats=amount_sats
|
|
214
|
+
)
|
|
215
|
+
request = ReceivePaymentRequest(payment_method=payment_method)
|
|
216
|
+
response = await sdk.receive_payment(request=request)
|
|
217
|
+
|
|
218
|
+
result = {
|
|
219
|
+
"status": "success",
|
|
220
|
+
"message": "Invoice created successfully",
|
|
221
|
+
"amount_sat": amount_sats,
|
|
222
|
+
"description": description
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if hasattr(response, 'payment_request'):
|
|
226
|
+
result["invoice"] = response.payment_request
|
|
227
|
+
if hasattr(response, 'payment_hash'):
|
|
228
|
+
result["payment_hash"] = response.payment_hash
|
|
229
|
+
|
|
230
|
+
return result
|
|
231
|
+
|
|
232
|
+
async def list_payments(limit: int = 10) -> Dict[str, Any]:
|
|
233
|
+
"""List recent payments"""
|
|
234
|
+
from breez_sdk_spark import ListPaymentsRequest
|
|
235
|
+
|
|
236
|
+
sdk = sdk_manager.get_sdk()
|
|
237
|
+
request = ListPaymentsRequest(limit=limit, sort_ascending=False)
|
|
238
|
+
response = await sdk.list_payments(request=request)
|
|
239
|
+
|
|
240
|
+
result = {"payments": [], "total_count": 0}
|
|
241
|
+
|
|
242
|
+
if hasattr(response, 'payments') and response.payments:
|
|
243
|
+
result["total_count"] = len(response.payments)
|
|
244
|
+
for payment in response.payments:
|
|
245
|
+
payment_data = {
|
|
246
|
+
'id': getattr(payment, 'id', None),
|
|
247
|
+
'timestamp': getattr(payment, 'timestamp', None),
|
|
248
|
+
'amount_sat': getattr(payment, 'amount', None),
|
|
249
|
+
'status': str(getattr(payment, 'status', 'UNKNOWN')),
|
|
250
|
+
}
|
|
251
|
+
result["payments"].append(payment_data)
|
|
252
|
+
|
|
253
|
+
return result
|
|
254
|
+
|
|
255
|
+
def run():
|
|
256
|
+
"""Entry point for setuptools - runs the async main function"""
|
|
257
|
+
asyncio.run(main())
|
|
258
|
+
|
|
259
|
+
async def main():
|
|
260
|
+
"""Main entry point"""
|
|
261
|
+
# Setup logging
|
|
262
|
+
logging.basicConfig(
|
|
263
|
+
level=logging.INFO,
|
|
264
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
265
|
+
stream=sys.stdout
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Initialize SDK
|
|
269
|
+
await initialize_sdk()
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
273
|
+
await server.run(
|
|
274
|
+
read_stream,
|
|
275
|
+
write_stream,
|
|
276
|
+
server.create_initialization_options()
|
|
277
|
+
)
|
|
278
|
+
finally:
|
|
279
|
+
await cleanup_sdk()
|
|
280
|
+
|
|
281
|
+
if __name__ == "__main__":
|
|
282
|
+
run()
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from breez_sdk_spark import (
|
|
5
|
+
BreezSdk,
|
|
6
|
+
connect,
|
|
7
|
+
ConnectRequest,
|
|
8
|
+
default_config,
|
|
9
|
+
Seed
|
|
10
|
+
)
|
|
11
|
+
from .config import Config
|
|
12
|
+
|
|
13
|
+
# Mock SDK for testing
|
|
14
|
+
class MockBreezSdk:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.connected = True
|
|
17
|
+
|
|
18
|
+
async def disconnect(self):
|
|
19
|
+
self.connected = False
|
|
20
|
+
|
|
21
|
+
async def get_info(self, request=None):
|
|
22
|
+
return MockNodeInfo()
|
|
23
|
+
|
|
24
|
+
async def prepare_send_payment(self, request=None):
|
|
25
|
+
return MockPrepareResponse()
|
|
26
|
+
|
|
27
|
+
async def send_payment(self, request=None):
|
|
28
|
+
return MockSendPaymentResponse()
|
|
29
|
+
|
|
30
|
+
async def receive_payment(self, request=None):
|
|
31
|
+
return MockReceivePaymentResponse()
|
|
32
|
+
|
|
33
|
+
async def list_payments(self, request=None):
|
|
34
|
+
return MockListPaymentsResponse()
|
|
35
|
+
|
|
36
|
+
class MockNodeInfo:
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.id = "test_node_id_123456789"
|
|
39
|
+
self.balance_sats = 1000000
|
|
40
|
+
self.pending_incoming_sats = 0
|
|
41
|
+
self.pending_outgoing_sats = 50000
|
|
42
|
+
self.max_payable_sats = 950000
|
|
43
|
+
self.max_receivable_sats = 2000000
|
|
44
|
+
self.network = "mainnet"
|
|
45
|
+
self.channels = []
|
|
46
|
+
self.synced = True
|
|
47
|
+
self.block_height = 800000
|
|
48
|
+
|
|
49
|
+
class MockPrepareResponse:
|
|
50
|
+
def __init__(self):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
class MockSendPaymentResponse:
|
|
54
|
+
def __init__(self):
|
|
55
|
+
self.payment = MockPayment()
|
|
56
|
+
self.payment_hash = "test_payment_hash_123"
|
|
57
|
+
|
|
58
|
+
class MockReceivePaymentResponse:
|
|
59
|
+
def __init__(self):
|
|
60
|
+
self.payment_request = "lnbc1000n1p3knh2dpp5jfqh5..."
|
|
61
|
+
self.payment_hash = "test_invoice_hash_456"
|
|
62
|
+
self.fee_sats = 1
|
|
63
|
+
|
|
64
|
+
class MockPayment:
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self.id = "test_payment_id_123"
|
|
67
|
+
self.timestamp = 1234567890
|
|
68
|
+
self.amount = 1000
|
|
69
|
+
self.fees = 10
|
|
70
|
+
self.payment_type = "PaymentType.SEND"
|
|
71
|
+
self.status = "PaymentStatus.COMPLETED"
|
|
72
|
+
self.destination = "lnbc..."
|
|
73
|
+
self.tx_id = "test_tx_id_789"
|
|
74
|
+
self.details = MockPaymentDetails()
|
|
75
|
+
|
|
76
|
+
class MockPaymentDetails:
|
|
77
|
+
def __init__(self):
|
|
78
|
+
self.payment_hash = "test_payment_hash_123"
|
|
79
|
+
self.preimage = "test_preimage_abc"
|
|
80
|
+
self.description = "Test payment"
|
|
81
|
+
self.invoice = "lnbc1000n1p3knh2d..."
|
|
82
|
+
|
|
83
|
+
class MockListPaymentsResponse:
|
|
84
|
+
def __init__(self):
|
|
85
|
+
self.payments = [MockPayment()]
|
|
86
|
+
|
|
87
|
+
class SDKManager:
|
|
88
|
+
def __init__(self):
|
|
89
|
+
self.config = Config()
|
|
90
|
+
self.sdk: BreezSdk = None
|
|
91
|
+
self.test_mode = os.getenv("BREEZ_TEST_MODE", "false").lower() == "true"
|
|
92
|
+
|
|
93
|
+
async def connect(self):
|
|
94
|
+
try:
|
|
95
|
+
if self.test_mode:
|
|
96
|
+
# Use mock SDK for testing
|
|
97
|
+
self.sdk = MockBreezSdk()
|
|
98
|
+
logging.info("Connected to Mock Breez SDK (test mode)")
|
|
99
|
+
else:
|
|
100
|
+
# Use real SDK
|
|
101
|
+
seed = Seed.MNEMONIC(mnemonic=self.config.mnemonic, passphrase=None)
|
|
102
|
+
config = default_config(network=self.config.network)
|
|
103
|
+
config.api_key = self.config.api_key
|
|
104
|
+
|
|
105
|
+
self.sdk = await connect(
|
|
106
|
+
request=ConnectRequest(
|
|
107
|
+
config=config,
|
|
108
|
+
seed=seed,
|
|
109
|
+
storage_dir=self.config.data_dir
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
logging.info("Connected to Breez SDK")
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logging.error(f"Failed to connect: {e}")
|
|
115
|
+
raise
|
|
116
|
+
|
|
117
|
+
async def disconnect(self):
|
|
118
|
+
if self.sdk:
|
|
119
|
+
await self.sdk.disconnect()
|
|
120
|
+
logging.info("Disconnected from Breez SDK")
|
|
121
|
+
|
|
122
|
+
def get_sdk(self) -> BreezSdk:
|
|
123
|
+
if not self.sdk:
|
|
124
|
+
raise RuntimeError("SDK not connected")
|
|
125
|
+
return self.sdk
|