@getalby/cli 0.0.1 → 0.2.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 +43 -31
- package/build/index.js +2 -2
- package/build/test/connection-secret.test.js +71 -0
- package/build/test/helpers.js +2 -1
- package/build/utils.js +41 -2
- package/package.json +2 -2
- package/build/auth.js +0 -20
- package/build/mcp_server.js +0 -36
- package/build/sse.js +0 -50
- package/build/streamable_http.js +0 -49
- package/build/tools/lightning/schemas/invoice.js +0 -20
- package/build/tools/nwc/schemas/transaction.js +0 -26
package/README.md
CHANGED
|
@@ -2,13 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
CLI for Nostr Wallet Connect (NIP-47) with lightning tools.
|
|
4
4
|
|
|
5
|
+
Built for agents - use with the [Alby Bitcoin Payments CLI Skill](https://github.com/getAlby/alby-cli-skill)
|
|
6
|
+
|
|
5
7
|
## Usage
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
|
|
10
|
+
# Pass a file path to a connection secret (preferred)
|
|
11
|
+
npx @getalby/cli -c /path/to/secret.txt <command> [options]
|
|
12
|
+
|
|
13
|
+
# Or pass connection string directly
|
|
14
|
+
npx @getalby/cli -c "nostr+walletconnect://..." <command> [options]
|
|
9
15
|
```
|
|
10
16
|
|
|
11
|
-
The
|
|
17
|
+
The `-c` option auto-detects whether you're passing a connection string or a file path. You can get a connection string from your NWC-compatible wallet (e.g., [Alby](https://getalby.com)).
|
|
18
|
+
|
|
19
|
+
You can also pass a connection string via the `NWC_URL` environment variable instead of using the `-c` option:
|
|
20
|
+
|
|
21
|
+
```txt
|
|
22
|
+
NWC_URL="nostr+walletconnect://..."
|
|
23
|
+
```
|
|
12
24
|
|
|
13
25
|
## Testing Wallet
|
|
14
26
|
|
|
@@ -110,44 +122,44 @@ npx @getalby/cli request-invoice-from-lightning-address --address "hello@getalby
|
|
|
110
122
|
|
|
111
123
|
### Wallet Commands
|
|
112
124
|
|
|
113
|
-
These require `--connection-secret`:
|
|
114
|
-
|
|
115
|
-
| Command
|
|
116
|
-
|
|
117
|
-
| `get-balance`
|
|
118
|
-
| `get-info`
|
|
119
|
-
| `get-wallet-service-info` | Get wallet capabilities
|
|
120
|
-
| `get-budget`
|
|
121
|
-
| `make-invoice`
|
|
122
|
-
| `pay-invoice`
|
|
123
|
-
| `pay-keysend`
|
|
124
|
-
| `lookup-invoice`
|
|
125
|
-
| `list-transactions`
|
|
126
|
-
| `sign-message`
|
|
127
|
-
| `wait-for-payment`
|
|
128
|
-
| `fetch-l402`
|
|
125
|
+
These require `-c` or `--connection-secret`:
|
|
126
|
+
|
|
127
|
+
| Command | Description | Required Options |
|
|
128
|
+
| ------------------------- | ------------------------------ | ------------------------------- |
|
|
129
|
+
| `get-balance` | Get wallet balance | - |
|
|
130
|
+
| `get-info` | Get wallet info | - |
|
|
131
|
+
| `get-wallet-service-info` | Get wallet capabilities | - |
|
|
132
|
+
| `get-budget` | Get wallet budget | - |
|
|
133
|
+
| `make-invoice` | Create a lightning invoice | `--amount` |
|
|
134
|
+
| `pay-invoice` | Pay a lightning invoice | `--invoice` |
|
|
135
|
+
| `pay-keysend` | Send a keysend payment | `--pubkey`, `--amount` |
|
|
136
|
+
| `lookup-invoice` | Look up an invoice | `--payment-hash` or `--invoice` |
|
|
137
|
+
| `list-transactions` | List transactions | - |
|
|
138
|
+
| `sign-message` | Sign a message with wallet key | `--message` |
|
|
139
|
+
| `wait-for-payment` | Wait for payment notification | `--payment-hash` |
|
|
140
|
+
| `fetch-l402` | Fetch L402-protected resource | `--url` |
|
|
129
141
|
|
|
130
142
|
### HOLD Invoice Commands
|
|
131
143
|
|
|
132
|
-
These require `--connection-secret`:
|
|
144
|
+
These require `-c` or `--connection-secret`:
|
|
133
145
|
|
|
134
|
-
| Command
|
|
135
|
-
|
|
136
|
-
| `make-hold-invoice`
|
|
137
|
-
| `settle-hold-invoice` | Settle a HOLD invoice | `--preimage`
|
|
138
|
-
| `cancel-hold-invoice` | Cancel a HOLD invoice | `--payment-hash`
|
|
146
|
+
| Command | Description | Required Options |
|
|
147
|
+
| --------------------- | --------------------- | ---------------------------- |
|
|
148
|
+
| `make-hold-invoice` | Create a HOLD invoice | `--amount`, `--payment-hash` |
|
|
149
|
+
| `settle-hold-invoice` | Settle a HOLD invoice | `--preimage` |
|
|
150
|
+
| `cancel-hold-invoice` | Cancel a HOLD invoice | `--payment-hash` |
|
|
139
151
|
|
|
140
152
|
### Lightning Tools
|
|
141
153
|
|
|
142
154
|
These don't require a wallet connection:
|
|
143
155
|
|
|
144
|
-
| Command
|
|
145
|
-
|
|
146
|
-
| `fiat-to-sats`
|
|
147
|
-
| `sats-to-fiat`
|
|
148
|
-
| `parse-invoice`
|
|
149
|
-
| `verify-preimage`
|
|
150
|
-
| `request-invoice-from-lightning-address` | Request invoice from lightning address | `--address`, `--amount`
|
|
156
|
+
| Command | Description | Required Options |
|
|
157
|
+
| ---------------------------------------- | -------------------------------------- | ------------------------- |
|
|
158
|
+
| `fiat-to-sats` | Convert fiat to sats | `--currency`, `--amount` |
|
|
159
|
+
| `sats-to-fiat` | Convert sats to fiat | `--amount`, `--currency` |
|
|
160
|
+
| `parse-invoice` | Parse a BOLT-11 invoice | `--invoice` |
|
|
161
|
+
| `verify-preimage` | Verify preimage against invoice | `--invoice`, `--preimage` |
|
|
162
|
+
| `request-invoice-from-lightning-address` | Request invoice from lightning address | `--address`, `--amount` |
|
|
151
163
|
|
|
152
164
|
## Output
|
|
153
165
|
|
package/build/index.js
CHANGED
|
@@ -24,8 +24,8 @@ const program = new Command();
|
|
|
24
24
|
program
|
|
25
25
|
.name("alby-cli")
|
|
26
26
|
.description("CLI for Nostr Wallet Connect (NIP-47) with lightning tools")
|
|
27
|
-
.version("0.
|
|
28
|
-
.option("-c, --connection-secret <string>", "NWC connection secret (nostr+walletconnect://...)");
|
|
27
|
+
.version("0.2.1")
|
|
28
|
+
.option("-c, --connection-secret <string>", "NWC connection secret (nostr+walletconnect://...) or path to file containing it (preferred)");
|
|
29
29
|
// Register all commands
|
|
30
30
|
registerGetBalanceCommand(program);
|
|
31
31
|
registerGetBudgetCommand(program);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, test, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { createTestWallet, runCli } from "./helpers.js";
|
|
3
|
+
import { writeFileSync, unlinkSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
describe("Connection Secret Handling", () => {
|
|
7
|
+
let wallet;
|
|
8
|
+
let secretFilePath;
|
|
9
|
+
let badSecretFilePath;
|
|
10
|
+
beforeAll(async () => {
|
|
11
|
+
wallet = await createTestWallet();
|
|
12
|
+
// Create temp file with valid connection secret
|
|
13
|
+
secretFilePath = join(tmpdir(), `nwc-test-secret-${Date.now()}.txt`);
|
|
14
|
+
writeFileSync(secretFilePath, wallet.nwcUrl);
|
|
15
|
+
// Create temp file with invalid/truncated content
|
|
16
|
+
badSecretFilePath = join(tmpdir(), `nwc-test-bad-secret-${Date.now()}.txt`);
|
|
17
|
+
writeFileSync(badSecretFilePath, "nostr+wallet");
|
|
18
|
+
}, 60000);
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
try {
|
|
21
|
+
unlinkSync(secretFilePath);
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
try {
|
|
25
|
+
unlinkSync(badSecretFilePath);
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
});
|
|
29
|
+
test("accepts connection string directly", () => {
|
|
30
|
+
const result = runCli(`-c "${wallet.nwcUrl}" get-balance`);
|
|
31
|
+
expect(result.success).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
test("reads connection secret from file", () => {
|
|
34
|
+
const result = runCli(`-c "${secretFilePath}" get-balance`);
|
|
35
|
+
expect(result.success).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
test("errors when file does not exist", () => {
|
|
38
|
+
const result = runCli(`-c "/tmp/nonexistent-file-${Date.now()}" get-balance`);
|
|
39
|
+
expect(result.success).toBe(false);
|
|
40
|
+
expect(result.output.error).toContain("Failed to read connection secret file");
|
|
41
|
+
});
|
|
42
|
+
test("errors when file contains invalid connection string", () => {
|
|
43
|
+
const result = runCli(`-c "${badSecretFilePath}" get-balance`);
|
|
44
|
+
expect(result.success).toBe(false);
|
|
45
|
+
expect(result.output.error).toContain("Invalid connection secret");
|
|
46
|
+
});
|
|
47
|
+
test("errors when no connection secret provided", () => {
|
|
48
|
+
const result = runCli("get-balance");
|
|
49
|
+
expect(result.success).toBe(false);
|
|
50
|
+
expect(result.output.error).toContain("--connection-secret is required");
|
|
51
|
+
});
|
|
52
|
+
test("errors when connection string is malformed", () => {
|
|
53
|
+
const result = runCli(`-c "nostr+walletconnect://asdf" get-balance`);
|
|
54
|
+
expect(result.success).toBe(false);
|
|
55
|
+
expect(result.output.error).toContain("Invalid connection secret");
|
|
56
|
+
});
|
|
57
|
+
test("reads connection secret from NWC_URL environment variable", () => {
|
|
58
|
+
const result = runCli("get-balance", { NWC_URL: wallet.nwcUrl });
|
|
59
|
+
expect(result.success).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
test("reads file path from NWC_URL environment variable", () => {
|
|
62
|
+
const result = runCli("get-balance", { NWC_URL: secretFilePath });
|
|
63
|
+
expect(result.success).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
test("command line argument takes precedence over environment variables", () => {
|
|
66
|
+
const result = runCli(`-c "${wallet.nwcUrl}" get-balance`, {
|
|
67
|
+
NWC_URL: "nostr+walletconnect://invalid"
|
|
68
|
+
});
|
|
69
|
+
expect(result.success).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
package/build/test/helpers.js
CHANGED
|
@@ -40,11 +40,12 @@ export async function createTestWallet(retries = 5) {
|
|
|
40
40
|
}
|
|
41
41
|
throw new Error("Failed to create test wallet");
|
|
42
42
|
}
|
|
43
|
-
export function runCli(args) {
|
|
43
|
+
export function runCli(args, env) {
|
|
44
44
|
try {
|
|
45
45
|
const result = execSync(`node build/index.js ${args}`, {
|
|
46
46
|
encoding: "utf-8",
|
|
47
47
|
cwd: process.cwd(),
|
|
48
|
+
env: env ? { ...process.env, ...env } : process.env,
|
|
48
49
|
});
|
|
49
50
|
return { success: true, output: JSON.parse(result) };
|
|
50
51
|
}
|
package/build/utils.js
CHANGED
|
@@ -1,12 +1,51 @@
|
|
|
1
1
|
import { NWCClient } from "@getalby/sdk";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
2
3
|
export function getClient(program) {
|
|
3
4
|
const opts = program.opts();
|
|
4
|
-
|
|
5
|
+
let connectionSecret = opts.connectionSecret;
|
|
6
|
+
// Check environment variables if --connection-secret not provided
|
|
7
|
+
if (!connectionSecret) {
|
|
8
|
+
connectionSecret = process.env.NWC_URL;
|
|
9
|
+
}
|
|
5
10
|
if (!connectionSecret) {
|
|
6
11
|
console.error("Error: --connection-secret is required for this command");
|
|
7
12
|
process.exit(1);
|
|
8
13
|
}
|
|
9
|
-
|
|
14
|
+
// Auto-detect: if it doesn't start with the protocol, treat as file path
|
|
15
|
+
if (!connectionSecret.startsWith("nostr+walletconnect://")) {
|
|
16
|
+
try {
|
|
17
|
+
connectionSecret = readFileSync(connectionSecret, "utf-8").trim();
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.error(`Error: Failed to read connection secret file "${opts.connectionSecret}": ${error instanceof Error ? error.message : String(error)}`);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// Validate the connection string format
|
|
25
|
+
if (!connectionSecret.startsWith("nostr+walletconnect://")) {
|
|
26
|
+
console.error(`Error: Invalid connection secret. Expected format: nostr+walletconnect://...\n` +
|
|
27
|
+
`Got: "${connectionSecret.substring(0, 50)}${connectionSecret.length > 50 ? "..." : ""}"\n` +
|
|
28
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const client = new NWCClient({ nostrWalletConnectUrl: connectionSecret });
|
|
32
|
+
// Validate client properties
|
|
33
|
+
if (!client.secret || !/^[0-9a-f]{64}$/i.test(client.secret)) {
|
|
34
|
+
console.error(`Error: Invalid connection secret. Missing or invalid secret key.\n` +
|
|
35
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
if (!client.walletPubkey || !/^[0-9a-f]{64}$/i.test(client.walletPubkey)) {
|
|
39
|
+
console.error(`Error: Invalid connection secret. Missing or invalid wallet pubkey.\n` +
|
|
40
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
if (!client.relayUrls) {
|
|
44
|
+
console.error(`Error: Invalid connection secret. Missing relay URL.\n` +
|
|
45
|
+
`Hint: Make sure the connection string is complete and not truncated.`);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
return client;
|
|
10
49
|
}
|
|
11
50
|
export function output(data) {
|
|
12
51
|
console.log(JSON.stringify(data, null, 2));
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@getalby/cli",
|
|
3
3
|
"description": "CLI for Nostr Wallet Connect (NIP-47) with a few additional useful lightning tools",
|
|
4
4
|
"repository": "https://github.com/getAlby/cli.git",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.2.1",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "build/index.js",
|
|
8
8
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"build": "tsc && chmod 755 build/index.js",
|
|
18
18
|
"start": "node build/index.js",
|
|
19
19
|
"dev": "yarn build && node build/index.js",
|
|
20
|
-
"test": "vitest run",
|
|
20
|
+
"test": "npm run build && vitest run",
|
|
21
21
|
"test:watch": "vitest"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|
package/build/auth.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
function getConnectionSecretFromBearerAuth(authorizationHeader) {
|
|
2
|
-
const authParts = authorizationHeader?.split(" ");
|
|
3
|
-
if (authParts?.length !== 2 ||
|
|
4
|
-
authParts[0] !== "Bearer" ||
|
|
5
|
-
!authParts[1].startsWith("nostr+walletconnect://")) {
|
|
6
|
-
return undefined;
|
|
7
|
-
}
|
|
8
|
-
return authParts[1];
|
|
9
|
-
}
|
|
10
|
-
function getConnectionSecretFromQueryParam(nwcParam) {
|
|
11
|
-
if (!nwcParam || !nwcParam.startsWith("nostr+walletconnect://")) {
|
|
12
|
-
return undefined;
|
|
13
|
-
}
|
|
14
|
-
return nwcParam;
|
|
15
|
-
}
|
|
16
|
-
export function getConnectionSecret(authorizationHeader, nwcQueryParam) {
|
|
17
|
-
// Try query parameter first, then fall back to bearer auth
|
|
18
|
-
return (getConnectionSecretFromQueryParam(nwcQueryParam) ||
|
|
19
|
-
getConnectionSecretFromBearerAuth(authorizationHeader));
|
|
20
|
-
}
|
package/build/mcp_server.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { webln } from "@getalby/sdk";
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { registerGetInfoTool } from "./tools/nwc/get_info.js";
|
|
4
|
-
import { registerGetWalletServiceInfoTool } from "./tools/nwc/get_wallet_service_info.js";
|
|
5
|
-
import { registerLookupInvoiceTool } from "./tools/nwc/lookup_invoice.js";
|
|
6
|
-
import { registerMakeInvoiceTool } from "./tools/nwc/make_invoice.js";
|
|
7
|
-
import { registerPayInvoiceTool } from "./tools/nwc/pay_invoice.js";
|
|
8
|
-
import { registerGetBalanceTool } from "./tools/nwc/get_balance.js";
|
|
9
|
-
import { registerListTransactionsTool } from "./tools/nwc/list_transactions.js";
|
|
10
|
-
import { registerFetchL402Tool } from "./tools/lightning/fetch_l402.js";
|
|
11
|
-
import { registerFiatToSatsTool } from "./tools/lightning/fiat_to_sats.js";
|
|
12
|
-
import { registerParseInvoiceTool } from "./tools/lightning/parse_invoice.js";
|
|
13
|
-
import { registerRequestInvoiceFromLightningAddressTool } from "./tools/lightning/request_invoice.js";
|
|
14
|
-
export function createMCPServer(client) {
|
|
15
|
-
const server = new McpServer({
|
|
16
|
-
name: "@getalby/mcp",
|
|
17
|
-
version: "1.1.0",
|
|
18
|
-
title: "Alby MCP Server",
|
|
19
|
-
});
|
|
20
|
-
// NWC
|
|
21
|
-
registerGetWalletServiceInfoTool(server, client);
|
|
22
|
-
registerGetInfoTool(server, client);
|
|
23
|
-
registerMakeInvoiceTool(server, client);
|
|
24
|
-
registerPayInvoiceTool(server, client);
|
|
25
|
-
registerGetBalanceTool(server, client);
|
|
26
|
-
registerLookupInvoiceTool(server, client);
|
|
27
|
-
registerListTransactionsTool(server, client);
|
|
28
|
-
// Lightning tools
|
|
29
|
-
registerFetchL402Tool(server, new webln.NostrWebLNProvider({
|
|
30
|
-
client,
|
|
31
|
-
}));
|
|
32
|
-
registerFiatToSatsTool(server);
|
|
33
|
-
registerParseInvoiceTool(server);
|
|
34
|
-
registerRequestInvoiceFromLightningAddressTool(server);
|
|
35
|
-
return server;
|
|
36
|
-
}
|
package/build/sse.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { nwc } from "@getalby/sdk";
|
|
2
|
-
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
3
|
-
import { createMCPServer } from "./mcp_server.js";
|
|
4
|
-
import { getConnectionSecret } from "./auth.js";
|
|
5
|
-
export function addSSEEndpoints(app) {
|
|
6
|
-
// Store transports for each session type
|
|
7
|
-
// SSE is deprecated so we do not put much effort in here (e.g. cleaning up unused sessions)
|
|
8
|
-
const sessions = {};
|
|
9
|
-
app.get("/sse", async (req, res) => {
|
|
10
|
-
const nostrWalletConnectUrl = getConnectionSecret(req.header("Authorization"), req.query.nwc);
|
|
11
|
-
if (!nostrWalletConnectUrl) {
|
|
12
|
-
res
|
|
13
|
-
.status(400)
|
|
14
|
-
.send("Bearer auth with NWC connection secret or nwc query parameter not provided");
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
const client = new nwc.NWCClient({
|
|
18
|
-
nostrWalletConnectUrl,
|
|
19
|
-
});
|
|
20
|
-
const transport = new SSEServerTransport("/messages", res);
|
|
21
|
-
const server = createMCPServer(client);
|
|
22
|
-
sessions[transport.sessionId] = {
|
|
23
|
-
server,
|
|
24
|
-
transport,
|
|
25
|
-
};
|
|
26
|
-
console.info("Created new SSE session", transport.sessionId);
|
|
27
|
-
if (req.query.sessionId) {
|
|
28
|
-
console.info("Request provided its own session ID: " + req.query.sessionId);
|
|
29
|
-
sessions[req.query.sessionId] = {
|
|
30
|
-
server,
|
|
31
|
-
transport,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
server.connect(transport);
|
|
35
|
-
});
|
|
36
|
-
app.post("/messages", (req, res) => {
|
|
37
|
-
const sessionId = req.query.sessionId;
|
|
38
|
-
console.info("SSE messages request", sessionId);
|
|
39
|
-
const session = sessions[sessionId];
|
|
40
|
-
if (session) {
|
|
41
|
-
console.info("Found session for messages request", sessionId);
|
|
42
|
-
session.transport.handlePostMessage(req, res);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
res
|
|
46
|
-
.status(400)
|
|
47
|
-
.send("No transport found for sessionId: " + req.query.sessionId);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
package/build/streamable_http.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { createMCPServer } from "./mcp_server.js";
|
|
2
|
-
import { nwc } from "@getalby/sdk";
|
|
3
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
-
import { getConnectionSecret } from "./auth.js";
|
|
5
|
-
import { json } from "express";
|
|
6
|
-
export function addStreamableHttpEndpoints(app) {
|
|
7
|
-
app.post("/mcp", json(), async (req, res) => {
|
|
8
|
-
// In stateless mode, create a new instance of transport and server for each request
|
|
9
|
-
// to ensure complete isolation. A single instance would cause request ID collisions
|
|
10
|
-
// when multiple clients connect concurrently.
|
|
11
|
-
try {
|
|
12
|
-
const nostrWalletConnectUrl = getConnectionSecret(req.header("Authorization"), req.query.nwc);
|
|
13
|
-
if (!nostrWalletConnectUrl) {
|
|
14
|
-
res
|
|
15
|
-
.status(400)
|
|
16
|
-
.send("Bearer auth with NWC connection secret or nwc query parameter not provided");
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
const client = new nwc.NWCClient({
|
|
20
|
-
nostrWalletConnectUrl,
|
|
21
|
-
});
|
|
22
|
-
const server = createMCPServer(client);
|
|
23
|
-
const transport = new StreamableHTTPServerTransport({
|
|
24
|
-
sessionIdGenerator: undefined,
|
|
25
|
-
});
|
|
26
|
-
res.on("close", () => {
|
|
27
|
-
console.log("Request closed");
|
|
28
|
-
transport.close();
|
|
29
|
-
server.close();
|
|
30
|
-
client.close();
|
|
31
|
-
});
|
|
32
|
-
await server.connect(transport);
|
|
33
|
-
await transport.handleRequest(req, res, req.body);
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
console.error("Error handling MCP request:", error);
|
|
37
|
-
if (!res.headersSent) {
|
|
38
|
-
res.status(500).json({
|
|
39
|
-
jsonrpc: "2.0",
|
|
40
|
-
error: {
|
|
41
|
-
code: -32603,
|
|
42
|
-
message: "Internal server error",
|
|
43
|
-
},
|
|
44
|
-
id: null,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export const invoiceSchema = {
|
|
3
|
-
paymentRequest: z.string().describe("The BOLT-11 payment request"),
|
|
4
|
-
paymentHash: z.string().describe("Payment hash"),
|
|
5
|
-
preimage: z.string().nullable().describe("Payment preimage if available"),
|
|
6
|
-
verify: z
|
|
7
|
-
.string()
|
|
8
|
-
.nullable()
|
|
9
|
-
.describe("URL to verify if the email was paid (LNURL-verify)"),
|
|
10
|
-
amount_in_sats: z.number().describe("Amount in sats"),
|
|
11
|
-
expiry: z.number().nullish().describe("Expiry time in seconds"),
|
|
12
|
-
timestamp: z.number().describe("Creation unix timestamp"),
|
|
13
|
-
createdDate: z.string().describe("Creation date string"),
|
|
14
|
-
expiryDate: z.string().nullish().describe("Expiry date string"),
|
|
15
|
-
description: z.string().nullable().describe("Invoice description"),
|
|
16
|
-
successAction: z
|
|
17
|
-
.unknown()
|
|
18
|
-
.nullable()
|
|
19
|
-
.describe("Success action to initiate after the invoice has been paid"),
|
|
20
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export const transactionSchema = {
|
|
3
|
-
type: z.enum(["incoming", "outgoing"]).describe("Transaction type"),
|
|
4
|
-
state: z
|
|
5
|
-
.enum(["settled", "pending", "failed"])
|
|
6
|
-
.nullish()
|
|
7
|
-
.describe("Transaction state"),
|
|
8
|
-
invoice: z.string().describe("BOLT-11 invoice"),
|
|
9
|
-
description: z.string().nullish().describe("Invoice description"),
|
|
10
|
-
description_hash: z.string().nullish().describe("Description hash"),
|
|
11
|
-
preimage: z.string().nullish().describe("Preimage of settled payment"),
|
|
12
|
-
payment_hash: z.string().describe("Payment hash"),
|
|
13
|
-
amount_in_sats: z.number().describe("Amount in sats"),
|
|
14
|
-
fees_paid_in_sats: z.number().nullish().describe("Fees paid in sats"),
|
|
15
|
-
settled_at: z.number().nullish().describe("Timestamp, of settled payment"),
|
|
16
|
-
created_at: z.number().describe("Creation unix timestamp"),
|
|
17
|
-
expires_at: z.number().nullish().describe("Expiry unix timestamp"), // TODO: remove nullish once Primal supports it
|
|
18
|
-
settle_deadline: z
|
|
19
|
-
.number()
|
|
20
|
-
.nullish()
|
|
21
|
-
.describe("HOLD invoice settle deadline"),
|
|
22
|
-
metadata: z
|
|
23
|
-
.unknown()
|
|
24
|
-
.nullish()
|
|
25
|
-
.describe("Additional metadata about the transaction"),
|
|
26
|
-
};
|