agenticbtc-mcp 1.0.10 → 1.0.12

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/server.js +44 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenticbtc-mcp",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Privacy-intelligent payments for AI agents — your privacy, your choice. Universal payment router with Lightning, Strike, Coinbase, PayPal, Venmo support.",
5
5
  "keywords": [
6
6
  "bitcoin",
package/src/server.js CHANGED
@@ -11,8 +11,8 @@ import crypto from "crypto";
11
11
 
12
12
  const API_URL = process.env.AGENTICBTC_API_URL || process.env.AGENTBTC_API_URL || "http://localhost:8000";
13
13
  const API_KEY = process.env.AGENTICBTC_API_KEY || process.env.AGENTBTC_API_KEY || "";
14
- const LND_HOST = process.env.AGENTBTC_LND_HOST || "";
15
- const LND_MACAROON = process.env.AGENTBTC_LND_MACAROON || "";
14
+ const LND_HOST = process.env.AGENTICBTC_LND_HOST || process.env.AGENTBTC_LND_HOST || "";
15
+ const LND_MACAROON = process.env.AGENTICBTC_LND_MACAROON || process.env.AGENTBTC_LND_MACAROON || "";
16
16
 
17
17
  // X (Twitter) credentials — optional, enables social posting tools
18
18
  const X_CONSUMER_KEY = process.env.TWITTER_CONSUMER_KEY || "";
@@ -849,20 +849,58 @@ server.tool(
849
849
  } else if (isStrikeHandle) {
850
850
  const handle = recipient.slice(1); // strip "$"
851
851
  if (!amount_usd && !amount_sats) return { content: [{ type: "text", text: "Error: amount_usd or amount_sats required for Strike payments" }] };
852
- if (resolvedAgent && amount_sats) {
853
- const policy = await checkSpendingPolicy(resolvedAgent.id, amount_sats);
852
+ const satsToSend = amount_sats || 0;
853
+ if (resolvedAgent && satsToSend) {
854
+ const policy = await checkSpendingPolicy(resolvedAgent.id, satsToSend);
854
855
  if (!policy.allowed) return { content: [{ type: "text", text: `Payment blocked: ${policy.reason}` }] };
855
856
  }
857
+
858
+ // Try Lightning-first: resolve handle@strike.me via LNURL
859
+ if (satsToSend > 0) {
860
+ try {
861
+ const lightningAddr = `${handle}@strike.me`;
862
+ const lnurlRes = await fetch(`https://strike.me/.well-known/lnurlp/${handle}`, { signal: AbortSignal.timeout(5000) });
863
+ if (lnurlRes.ok) {
864
+ const lnurlData = await lnurlRes.json();
865
+ if (lnurlData.status !== "ERROR") {
866
+ const minSats = Math.ceil((lnurlData.minSendable || 1000) / 1000);
867
+ const maxSats = Math.floor((lnurlData.maxSendable || 100000000000) / 1000);
868
+ if (satsToSend >= minSats && satsToSend <= maxSats) {
869
+ let callbackUrl = `${lnurlData.callback}${lnurlData.callback.includes("?") ? "&" : "?"}amount=${satsToSend * 1000}`;
870
+ if (memo && lnurlData.commentAllowed) callbackUrl += `&comment=${encodeURIComponent(memo)}`;
871
+ const invoiceRes = await fetch(callbackUrl, { signal: AbortSignal.timeout(5000) });
872
+ if (invoiceRes.ok) {
873
+ const invoiceData = await invoiceRes.json();
874
+ if (invoiceData.pr) {
875
+ const { status: payStatus, data: payData } = await apiCall("/api/v1/payments", {
876
+ method: "POST",
877
+ body: JSON.stringify({ invoice: invoiceData.pr, fee_limit_sats: Math.max(100, Math.floor(satsToSend * 0.01)) }),
878
+ });
879
+ if (payStatus === 200 && payData.success) {
880
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, type: "strike_handle_via_lightning", recipient, lightning_address: lightningAddr, amount_sats: satsToSend, fee_sats: payData.fee_sats || 0, payment_hash: payData.payment_hash, message: `Sent ${satsToSend} sats to ${recipient} via Lightning (${lightningAddr}) ⚡ — cheaper than Strike API` }) }] };
881
+ }
882
+ // Lightning failed — fall through to Strike API
883
+ }
884
+ }
885
+ }
886
+ }
887
+ }
888
+ } catch (_) {
889
+ // LNURL resolution failed — fall through to Strike API
890
+ }
891
+ }
892
+
893
+ // Fall back to Strike API
856
894
  const body = { handle, description: memo || `Payment to $${handle}` };
857
895
  if (amount_usd) body.amount_usd = amount_usd;
858
- if (amount_sats) body.amount_sats = amount_sats;
896
+ if (satsToSend) body.amount_sats = satsToSend;
859
897
  if (resolvedAgent) body.agent_id = resolvedAgent.id;
860
898
  const { status, data } = await apiCall("/api/v1/strike/pay", {
861
899
  method: "POST",
862
900
  body: JSON.stringify(body),
863
901
  });
864
902
  if (status === 200 && data.success) {
865
- return { content: [{ type: "text", text: JSON.stringify({ success: true, type: "strike_handle", recipient, amount_usd: data.amount_usd, fee_usd: data.fee_usd || 0, payment_id: data.payment_id, message: `Sent to ${recipient} via Strike 💸` }) }] };
903
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, type: "strike_handle_via_api", recipient, amount_usd: data.amount_usd, fee_usd: data.fee_usd || 0, payment_id: data.payment_id, message: `Sent to ${recipient} via Strike API 💸 (Lightning not available for this recipient)` }) }] };
866
904
  }
867
905
  return { content: [{ type: "text", text: `Payment failed: ${data?.detail || data?.error || JSON.stringify(data)}` }] };
868
906