From 0bc9f631031509ead7695f8da9a67fd311684eb1 Mon Sep 17 00:00:00 2001 From: Ivan Robles Date: Sat, 21 Mar 2026 20:08:47 -0600 Subject: [PATCH] add sendAll and descriptionHash params to payment tools. --- .gitignore | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- readme.md | 18 +++++++++++------- src/tools/create_invoice.ts | 16 ++++++++++++---- src/tools/pay_invoice.ts | 7 +++++-- src/tools/pay_lightning_address.ts | 7 +++++-- src/tools/pay_offer.ts | 7 +++++-- 8 files changed, 43 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 6b08be5..80a3c58 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,5 @@ Thumbs.db # AI Agents or Tools CLAUDE.md +.claude +*PLAN.md diff --git a/package-lock.json b/package-lock.json index 1b7d206..6e205ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "phoenixd-mcp-server", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "phoenixd-mcp-server", - "version": "1.1.0", + "version": "1.2.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.25.3", diff --git a/package.json b/package.json index 8fe91c6..c38f8af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "phoenixd-mcp-server", - "version": "1.1.0", + "version": "1.2.0", "description": "Connect a phoenixd bitcoin lightning wallet to your LLM.", "keywords": [ "phoenixd", diff --git a/readme.md b/readme.md index 177fc04..9b02d6b 100644 --- a/readme.md +++ b/readme.md @@ -73,7 +73,8 @@ If phoenixd runs on a remote server behind a reverse proxy with TLS: - `create-invoice` - Create a bolt11 invoice with a specified amount and description. - Inputs: - - `description` (string): The description of the invoice (max. 128 characters). + - `description` (string, optional): The description of the invoice (max. 128 characters). Either `description` or `descriptionHash` must be provided. + - `descriptionHash` (string, optional): SHA256 hash of the description. Use instead of `description`. - `amountSat` (number, optional): The amount requested by the invoice, in satoshi. If not set, the invoice can be paid by any amount. - `expirySeconds` (number, optional): The invoice expiry in seconds, default 3600 (1 hour). - `externalId` (string, optional): A custom identifier to link the invoice to an external system. @@ -90,22 +91,26 @@ If phoenixd runs on a remote server behind a reverse proxy with TLS: - Pay a bolt11 invoice. - Inputs: - `invoice` (string): The bolt11 invoice to pay. - - `amountMsat` (number, optional): Amount to pay in millisatoshis (only for zero-amount invoices). + - `amountSat` (number, optional): Amount to pay in satoshis (only for zero-amount invoices). Mutually exclusive with `sendAll`. + - `sendAll` (boolean, optional): If true, empties the wallet. Mutually exclusive with `amountSat`. - `externalId` (string, optional): A custom identifier to link the payment to an external system. - `pay-offer` - Pay a bolt12 offer. - Inputs: - `offer` (string): The bolt12 offer to pay. - - `amountMsat` (number): Amount to pay in millisatoshis. Required because BOLT12 offers can be reusable and variable-amount. + - `amountSat` (number, optional): Amount to pay in satoshis. Mutually exclusive with `sendAll`. + - `sendAll` (boolean, optional): If true, empties the wallet. Mutually exclusive with `amountSat`. + - `message` (string, optional): A message for the recipient. - `externalId` (string, optional): A custom identifier to link the payment to an external system. - `pay-lightning-address` - Pay a Lightning Address. - Inputs: - `address` (string): The Lightning Address to pay (e.g., swamppawpaw18@phoenixwallet.me). - - `amountSat` (number): Amount to pay in satoshis. - - `comment` (string, optional): Optional comment to include with the payment. + - `amountSat` (number, optional): Amount to pay in satoshis. Mutually exclusive with `sendAll`. + - `sendAll` (boolean, optional): If true, empties the wallet. Mutually exclusive with `amountSat`. + - `message` (string, optional): Optional message to include with the payment. - `pay-on-chain` - Send an on-chain Bitcoin payment. @@ -115,9 +120,8 @@ If phoenixd runs on a remote server behind a reverse proxy with TLS: - `feerateSatByte` (number, optional): Fee rate in satoshis per byte. - `bump-fee` - - Bump the fee of an unconfirmed on-chain transaction. + - Bumps the fee rate of all unconfirmed on-chain transactions using CPFP. Returns the ID of the child transaction. - Inputs: - - `txId` (string): The transaction ID to bump. - `feerateSatByte` (number): New fee rate in satoshis per byte. - `list-incoming-payments` diff --git a/src/tools/create_invoice.ts b/src/tools/create_invoice.ts index 218e34a..09c6665 100644 --- a/src/tools/create_invoice.ts +++ b/src/tools/create_invoice.ts @@ -12,17 +12,25 @@ export function registerCreateInvoiceTool( 'create-invoice', 'Create a bolt11 invoice with a specified amount and description', { - description: z.string().describe('The description of the invoice (max. 128 characters).'), + description: z.string().optional() + .describe('The description of the invoice (max. 128 characters). Either description or descriptionHash must be provided.'), + descriptionHash: z.string().optional().describe('SHA256 hash of the description. Use instead of description.'), amountSat: z.number().optional() .describe('The amount requested by the invoice, in satoshi. If not set, the invoice can be paid by any amount.'), expirySeconds: z.number().optional().describe('The invoice expiry in seconds, by default 3600 (1 hour).'), externalId: z.string().optional().describe('A custom identifier. Use that to link the invoice to an external system.'), - webhookUrl: z.string().optional().describe('A webhook url that will be notified when this specific payment has been received. '), + webhookUrl: z.string().optional().describe('A webhook url that will be notified when this specific payment has been received.'), }, - async ({ description, amountSat, expirySeconds, externalId, webhookUrl }) => { + async ({ description, descriptionHash, amountSat, expirySeconds, externalId, webhookUrl }) => { validateEnv(config); - const paramsObj: Record = { description }; + if (!description && !descriptionHash) { + return formatToolError('Either description or descriptionHash must be provided.'); + } + + const paramsObj: Record = {}; + if (description !== undefined) paramsObj.description = description; + if (descriptionHash !== undefined) paramsObj.descriptionHash = descriptionHash; if (amountSat !== undefined) paramsObj.amountSat = amountSat.toString(); if (expirySeconds !== undefined) paramsObj.expirySeconds = expirySeconds.toString(); if (externalId !== undefined) paramsObj.externalId = externalId; diff --git a/src/tools/pay_invoice.ts b/src/tools/pay_invoice.ts index 8392438..ce175d4 100644 --- a/src/tools/pay_invoice.ts +++ b/src/tools/pay_invoice.ts @@ -12,14 +12,17 @@ export function registerPayInvoiceTool( 'pay-invoice', 'Pay a bolt11 invoice with a specified amount and description', { - amountSat: z.number().optional().describe('The amount to pay, in satoshi. If not set, will pay the amount requested in the invoice.'), invoice: z.string().describe('The bolt11 invoice to pay.'), + amountSat: z.number().optional() + .describe('The amount to pay, in satoshi. If not set, will pay the amount requested in the invoice. Mutually exclusive with sendAll.'), + sendAll: z.boolean().optional().describe('If true, empties the wallet. Mutually exclusive with amountSat.'), }, - async ({ amountSat, invoice }) => { + async ({ amountSat, invoice, sendAll }) => { validateEnv(config); const paramsObj: Record = { invoice }; if (amountSat !== undefined) paramsObj.amountSat = amountSat.toString(); + if (sendAll !== undefined) paramsObj.sendAll = sendAll.toString(); const result = await fetchPhoenixd(config, '/payinvoice', { method: 'POST', diff --git a/src/tools/pay_lightning_address.ts b/src/tools/pay_lightning_address.ts index 5ffddd3..ce06c30 100644 --- a/src/tools/pay_lightning_address.ts +++ b/src/tools/pay_lightning_address.ts @@ -12,15 +12,18 @@ export function registerPayLightningAddressTool( 'pay-lightning-address', 'Pays an email-like Lightning address, either based on BIP-353 or LNURL', { - amountSat: z.number().optional().describe('The amount in satoshi. If unset, will pay the amount requested in the offer.'), address: z.string().describe('The Lightning address to pay.'), + amountSat: z.number().optional() + .describe('The amount in satoshi. If unset, will pay the amount requested. Mutually exclusive with sendAll.'), + sendAll: z.boolean().optional().describe('If true, empties the wallet. Mutually exclusive with amountSat.'), message: z.string().optional().describe('A message for the recipient.'), }, - async ({ amountSat, address, message }) => { + async ({ amountSat, address, message, sendAll }) => { validateEnv(config); const paramsObj: Record = { address }; if (amountSat !== undefined) paramsObj.amountSat = amountSat.toString(); + if (sendAll !== undefined) paramsObj.sendAll = sendAll.toString(); if (message !== undefined) paramsObj.message = message; const result = await fetchPhoenixd(config, '/paylnaddress', { diff --git a/src/tools/pay_offer.ts b/src/tools/pay_offer.ts index 5485084..5449895 100644 --- a/src/tools/pay_offer.ts +++ b/src/tools/pay_offer.ts @@ -12,15 +12,18 @@ export function registerPayOfferTool( 'pay-offer', 'Pay a bolt12 offer', { - amountSat: z.number().optional().describe('The amount in satoshi. If unset, will pay the amount requested in the offer.'), offer: z.string().describe('The bolt12 offer to pay.'), + amountSat: z.number().optional() + .describe('The amount in satoshi. If unset, will pay the amount requested in the offer. Mutually exclusive with sendAll.'), + sendAll: z.boolean().optional().describe('If true, empties the wallet. Mutually exclusive with amountSat.'), message: z.string().optional().describe('A message for the recipient.'), }, - async ({ amountSat, offer, message }) => { + async ({ amountSat, offer, message, sendAll }) => { validateEnv(config); const paramsObj: Record = { offer }; if (amountSat !== undefined) paramsObj.amountSat = amountSat.toString(); + if (sendAll !== undefined) paramsObj.sendAll = sendAll.toString(); if (message !== undefined) paramsObj.message = message; const result = await fetchPhoenixd(config, '/payoffer', {