bsv-mcp 0.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 ADDED
@@ -0,0 +1,258 @@
1
+ # Bitcoin SV MCP Server
2
+
3
+ A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework. This library provides wallet, ordinals, and utility functions for BSV blockchain interaction.
4
+
5
+ ## Installation
6
+
7
+ To install dependencies:
8
+
9
+ ```bash
10
+ bun install
11
+ ```
12
+
13
+ For global installation (recommended for MCP client integration):
14
+
15
+ ```bash
16
+ # Install globally with bun
17
+ bun install -g bsv-mcp
18
+
19
+ # Or with npm
20
+ npm install -g bsv-mcp
21
+ ```
22
+
23
+ ## External Dependencies
24
+
25
+ For full functionality of all tools, the following additional dependencies are installed:
26
+
27
+ ```bash
28
+ # For ordinal listing purchase functionality
29
+ js-1sat-ord
30
+ ```
31
+
32
+ ## Running the Server
33
+
34
+ Start the MCP server:
35
+
36
+ ```bash
37
+ # If installed locally
38
+ bun run index.ts
39
+
40
+ # If installed globally
41
+ bunx bsv-mcp
42
+ ```
43
+
44
+ ## Connecting to MCP Clients
45
+
46
+ This server implements the [Model Context Protocol](https://modelcontextprotocol.io/) (MCP), allowing AI assistants to utilize Bitcoin SV functionalities. You can connect this server to various MCP-compatible clients.
47
+
48
+ ### Cursor
49
+
50
+ To use the BSV MCP server with [Cursor](https://cursor.sh/):
51
+
52
+ 1. Install Cursor if you haven't already
53
+ 2. Install this package globally: `bun install -g bsv-mcp`
54
+ 3. Open Cursor and navigate to Settings → Extensions → Model Context Protocol
55
+ 4. Click "Add a new global MCP server"
56
+ 5. Enter the following configuration in JSON format:
57
+
58
+ ```json
59
+ {
60
+ "Bitcoin SV": {
61
+ "command": "env",
62
+ "args": [
63
+ "PRIVATE_KEY_WIF=<your_private_key_wif>",
64
+ "bunx",
65
+ "bsv-mcp"
66
+ ]
67
+ }
68
+ }
69
+ ```
70
+
71
+ 6. Replace `<your_private_key_wif>` with your actual private key WIF (keep this secure!)
72
+ 7. Click "Save"
73
+
74
+ The BSV tools will now be available to Cursor's AI assistant under the "Bitcoin SV" namespace.
75
+
76
+ ### Claude for Desktop
77
+
78
+ To connect this server to Claude for Desktop:
79
+
80
+ 1. Ensure you have [Claude for Desktop](https://claude.ai/desktop) installed and updated to the latest version
81
+ 2. Install this package globally: `bun install -g bsv-mcp`
82
+ 3. Open your Claude for Desktop configuration file:
83
+ ```bash
84
+ # macOS/Linux
85
+ code ~/Library/Application\ Support/Claude/claude_desktop_config.json
86
+
87
+ # Windows
88
+ code %APPDATA%\Claude\claude_desktop_config.json
89
+ ```
90
+ 4. Add the BSV MCP server to your configuration (create the file if it doesn't exist):
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "Bitcoin SV": {
95
+ "command": "env",
96
+ "args": [
97
+ "PRIVATE_KEY_WIF=<your_private_key_wif>",
98
+ "bunx",
99
+ "bsv-mcp"
100
+ ]
101
+ }
102
+ }
103
+ }
104
+ ```
105
+ 5. Replace `<your_private_key_wif>` with your actual private key WIF
106
+ 6. Save the file and restart Claude for Desktop
107
+ 7. The BSV tools will appear when you click the tools icon (hammer) in Claude for Desktop
108
+
109
+ ### Generic MCP Client Integration
110
+
111
+ For other MCP clients that support JSON configuration:
112
+
113
+ ```json
114
+ {
115
+ "Bitcoin SV": {
116
+ "command": "env",
117
+ "args": [
118
+ "PRIVATE_KEY_WIF=<your_private_key_wif>",
119
+ "bunx",
120
+ "bsv-mcp"
121
+ ]
122
+ }
123
+ }
124
+ ```
125
+
126
+ If running the server directly:
127
+
128
+ ```bash
129
+ # Set environment variable first
130
+ export PRIVATE_KEY_WIF=<your_private_key_wif>
131
+
132
+ # Then run the server
133
+ bunx bsv-mcp
134
+ ```
135
+
136
+ ## Available Tools
137
+
138
+ The toolkit is organized into several categories:
139
+
140
+ ### Wallet Tools
141
+
142
+ Wallet tools provide core BSV wallet functionality:
143
+
144
+ | Tool Name | Description |
145
+ |-----------|-------------|
146
+ | `wallet_getPublicKey` | Retrieves a public key for a specified protocol and key ID |
147
+ | `wallet_createSignature` | Creates a cryptographic signature for the provided data |
148
+ | `wallet_verifySignature` | Verifies a cryptographic signature against the provided data |
149
+ | `wallet_encrypt` | Encrypts data using a specified protocol and key |
150
+ | `wallet_decrypt` | Decrypts data using a specified protocol and key |
151
+ | `wallet_getAddress` | Returns a BSV address for the current wallet or a derived path |
152
+ | `wallet_sendToAddress` | Sends BSV to a specified address (supports BSV or USD amounts) |
153
+ | `wallet_purchaseListing` | Purchases an NFT from a marketplace listing |
154
+
155
+ ### BSV Tools
156
+
157
+ Tools for interacting with the BSV blockchain and network:
158
+
159
+ | Tool Name | Description |
160
+ |-----------|-------------|
161
+ | `bsv_getPrice` | Gets the current BSV price from an exchange API |
162
+ | `bsv_decodeTransaction` | Decodes a BSV transaction and returns detailed information |
163
+
164
+ ### Ordinals Tools
165
+
166
+ Tools for working with ordinals (NFTs) on BSV:
167
+
168
+ | Tool Name | Description |
169
+ |-----------|-------------|
170
+ | `ordinals_getInscription` | Retrieves detailed information about a specific inscription |
171
+ | `ordinals_searchInscriptions` | Searches for inscriptions based on various criteria |
172
+ | `ordinals_marketListings` | Retrieves current marketplace listings for inscriptions |
173
+ | `ordinals_bsv20MarketSales` | Gets information about BSV20 token market sales |
174
+ | `ordinals_getBsv20ById` | Retrieves details about a specific BSV20 token by ID |
175
+
176
+ ### Utility Tools
177
+
178
+ General-purpose utility functions:
179
+
180
+ | Tool Name | Description |
181
+ |-----------|-------------|
182
+ | `utils_convertData` | Converts data between different encodings (utf8, hex, base64, binary) |
183
+
184
+ ## Using the Tools with MCP
185
+
186
+ Once connected, you can use natural language to interact with Bitcoin SV through your AI assistant. Here are some example prompts:
187
+
188
+ ### Wallet Operations
189
+ - "Get my Bitcoin SV address"
190
+ - "Send 0.01 BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
191
+ - "Send $5 USD worth of BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
192
+
193
+ ### Ordinals (NFTs)
194
+ - "Show me information about the NFT with outpoint 6a89047af2cfac96da17d51ae8eb62c5f1d982be2bc4ba0d0cd2084b7ffed325_0"
195
+ - "Search for Pixel Zoide NFTs"
196
+ - "Show me the current marketplace listings for BSV NFTs"
197
+
198
+ ### Blockchain Operations
199
+ - "What is the current BSV price?"
200
+ - "Decode this BSV transaction: (transaction hex or ID)"
201
+
202
+ ### Data Conversion
203
+ - "Convert 'Hello World' from UTF-8 to hex format"
204
+
205
+ ## How MCP Works
206
+
207
+ When you interact with an MCP-enabled AI assistant:
208
+
209
+ 1. The AI analyzes your request and decides which tools to use
210
+ 2. With your approval, it calls the appropriate BSV MCP tool
211
+ 3. The server executes the requested operation on the Bitcoin SV blockchain
212
+ 4. The results are returned to the AI assistant
213
+ 5. The assistant presents the information in a natural, conversational way
214
+
215
+ ## Troubleshooting
216
+
217
+ If you're having issues connecting to the server:
218
+
219
+ 1. Ensure the package is properly installed: `bun install -g bsv-mcp`
220
+ 2. Verify your WIF private key is correctly set in the environment
221
+ 3. Check that your client supports MCP and is properly configured
222
+ 4. Look for error messages in the client's console output
223
+
224
+ For Claude for Desktop, check the logs at:
225
+ ```bash
226
+ tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
227
+ ```
228
+
229
+ For Cursor, check the Cursor MCP logs in Settings → Extensions → Model Context Protocol.
230
+
231
+ ## Development
232
+
233
+ This project was created using `bun init` in bun v1.2.9. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
234
+
235
+ ### Package Configuration
236
+
237
+ To ensure the package can be run with `bunx bsv-mcp`, make sure your `package.json` includes:
238
+
239
+ ```json
240
+ {
241
+ "name": "bsv-mcp",
242
+ "bin": {
243
+ "bsv-mcp": "./index.ts"
244
+ },
245
+ "type": "module"
246
+ // ...other configuration
247
+ }
248
+ ```
249
+
250
+ ### Running Tests
251
+
252
+ ```bash
253
+ bun test
254
+ ```
255
+
256
+ ## License
257
+
258
+ [Include your license information here]
package/biome.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3
+ "vcs": {
4
+ "enabled": false,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": false
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": false,
10
+ "ignore": []
11
+ },
12
+ "formatter": {
13
+ "enabled": true,
14
+ "indentStyle": "tab"
15
+ },
16
+ "organizeImports": {
17
+ "enabled": true
18
+ },
19
+ "linter": {
20
+ "enabled": true,
21
+ "rules": {
22
+ "recommended": true
23
+ }
24
+ },
25
+ "javascript": {
26
+ "formatter": {
27
+ "quoteStyle": "double"
28
+ }
29
+ }
30
+ }
package/index.ts ADDED
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bun
2
+ import {
3
+ PrivateKey,
4
+ } from "@bsv/sdk";
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { registerAllTools } from "./tools";
8
+ import { registerWalletTools } from "./tools/wallet/tools";
9
+ import { Wallet } from "./tools/wallet/wallet";
10
+
11
+ const server = new McpServer({
12
+ name: "Bitcoin SV MCP",
13
+ version: "1.0.0",
14
+ });
15
+
16
+ // Singleton wallet instance (for demo, could be replaced with real key management)
17
+ // If PRIVATE_KEY_WIF is set in the environment, use it to instantiate the Wallet
18
+ const privateKeyWif = process.env.PRIVATE_KEY_WIF;
19
+ const privKey = privateKeyWif ? PrivateKey.fromWif(privateKeyWif) : undefined;
20
+ const wallet = privKey ? new Wallet(privKey) : new Wallet();
21
+
22
+ // Register wallet tools separately (needs wallet instance)
23
+ registerWalletTools(server, wallet);
24
+
25
+ // Register all other tools (BSV, Ordinals, Utils, etc.)
26
+ registerAllTools(server);
27
+
28
+ // Debug: Log all registered tools
29
+ console.log("Registered tools:", Object.keys(server));
30
+ console.log("MCP Server:", server);
31
+
32
+ // Connect to the transport
33
+ const transport = new StdioServerTransport();
34
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "bsv-mcp",
3
+ "module": "index.ts",
4
+ "type": "module",
5
+ "version": "0.0.1",
6
+ "bin": {
7
+ "bsv-mcp": "./index.ts"
8
+ },
9
+ "private": false,
10
+ "devDependencies": {
11
+ "@biomejs/biome": "^1.9.4",
12
+ "@types/bun": "latest"
13
+ },
14
+ "peerDependencies": {
15
+ "typescript": "^5.8.3"
16
+ },
17
+ "dependencies": {
18
+ "@bsv/sdk": "^1.4.19",
19
+ "@modelcontextprotocol/sdk": "^1.9.0",
20
+ "@types/node": "^22.14.1",
21
+ "js-1sat-ord": "^0.1.80",
22
+ "satoshi-token": "^0.0.4",
23
+ "tsx": "^4.19.3",
24
+ "zod": "^3.24.2"
25
+ },
26
+ "scripts": {
27
+ "lint": "biome check .",
28
+ "lint:fix": "biome check . --write",
29
+ "prepare": "chmod +x ./index.ts"
30
+ }
31
+ }
@@ -0,0 +1,216 @@
1
+ import { expect, test } from "bun:test";
2
+ import { PrivateKey } from "@bsv/sdk";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { z } from "zod";
5
+ import { registerWalletTools } from "../../tools/wallet/tools";
6
+ import { Wallet } from "../../tools/wallet/wallet";
7
+
8
+ // List of expected wallet tool names
9
+ const EXPECTED_WALLET_TOOLS = [
10
+ "wallet_getPublicKey",
11
+ "wallet_createSignature",
12
+ "wallet_verifySignature",
13
+ "wallet_encrypt",
14
+ "wallet_decrypt",
15
+ "wallet_createAction",
16
+ "wallet_signAction",
17
+ "wallet_listActions",
18
+ "wallet_listOutputs",
19
+ "wallet_getNetwork",
20
+ "wallet_getVersion",
21
+ "wallet_revealCounterpartyKeyLinkage",
22
+ "wallet_revealSpecificKeyLinkage",
23
+ "wallet_createHmac",
24
+ "wallet_verifyHmac",
25
+ "wallet_abortAction",
26
+ "wallet_internalizeAction",
27
+ "wallet_relinquishOutput",
28
+ "wallet_acquireCertificate",
29
+ "wallet_listCertificates",
30
+ "wallet_proveCertificate",
31
+ "wallet_relinquishCertificate",
32
+ "wallet_discoverByIdentityKey",
33
+ "wallet_discoverByAttributes",
34
+ "wallet_isAuthenticated",
35
+ "wallet_waitForAuthentication",
36
+ "wallet_getHeaderForHeight",
37
+ ];
38
+
39
+ // Mock wallet with test responses
40
+ class TestWallet extends Wallet {
41
+ constructor() {
42
+ super(PrivateKey.fromRandom());
43
+ }
44
+
45
+ // Override with test implementations that actually return something
46
+ async getPublicKey() {
47
+ return { publicKey: "mockPublicKey123" };
48
+ }
49
+
50
+ // Note: other methods will still return 'not implemented' errors
51
+ }
52
+
53
+ test("MCP server registers all expected wallet tools", async () => {
54
+ // Create an MCP server
55
+ const server = new McpServer({
56
+ name: "Wallet Server Test",
57
+ version: "0.0.1",
58
+ });
59
+
60
+ // Create test wallet and register tools
61
+ const wallet = new TestWallet();
62
+ const handlers = registerWalletTools(server, wallet);
63
+
64
+ // Check that we have all expected tool handlers
65
+ for (const toolName of EXPECTED_WALLET_TOOLS) {
66
+ expect(handlers[toolName as keyof typeof handlers]).toBeDefined();
67
+ expect(typeof handlers[toolName as keyof typeof handlers]).toBe("function");
68
+ }
69
+
70
+ // Check that we don't have any unexpected handlers
71
+ const actualToolNames = Object.keys(handlers);
72
+ expect(actualToolNames.length).toBe(EXPECTED_WALLET_TOOLS.length);
73
+
74
+ for (const toolName of actualToolNames) {
75
+ expect(EXPECTED_WALLET_TOOLS).toContain(toolName);
76
+ }
77
+ });
78
+
79
+ test("MCP server with wallet tools", async () => {
80
+ // Create an MCP server
81
+ const server = new McpServer({
82
+ name: "Wallet Server Test",
83
+ version: "0.0.1",
84
+ });
85
+
86
+ // Create test wallet and register tools
87
+ const wallet = new TestWallet();
88
+ const handlers = registerWalletTools(server, wallet);
89
+
90
+ // Mock the request handler extra
91
+ const mockExtra = {
92
+ signal: new AbortController().signal,
93
+ };
94
+
95
+ // Test the overridden getPublicKey handler which should return data
96
+ const getPublicKeyHandler = handlers.wallet_getPublicKey;
97
+ const getPublicKeyResult = await getPublicKeyHandler({ args: {} }, mockExtra);
98
+
99
+ // Check the success result based on the actual structure
100
+ expect(getPublicKeyResult.content).toBeDefined();
101
+ expect(getPublicKeyResult.content?.[0]?.type).toBe("text");
102
+
103
+ const publicKeyContent = getPublicKeyResult.content?.[0]?.text;
104
+ if (typeof publicKeyContent === "string") {
105
+ expect(JSON.parse(publicKeyContent)).toEqual({
106
+ publicKey: "mockPublicKey123",
107
+ });
108
+ }
109
+ });
110
+
111
+ // For a more complete integration test, we could create a real HTTP server
112
+ // and use fetch to make requests to it
113
+ test.skip("MCP server with HTTP requests", async () => {
114
+ // Create MCP server
115
+ const server = new McpServer({
116
+ name: "Wallet Server Test",
117
+ version: "0.0.1",
118
+ });
119
+
120
+ // Register wallet tools
121
+ const wallet = new TestWallet();
122
+ const handlers = registerWalletTools(server, wallet);
123
+
124
+ // Define expected request type
125
+ type ToolCallRequest = {
126
+ type: string;
127
+ name: string;
128
+ args?: Record<string, unknown>;
129
+ };
130
+
131
+ // Create a simple HTTP server
132
+ const httpServer = Bun.serve({
133
+ port: 0, // Use random available port
134
+ async fetch(req) {
135
+ if (req.method === "POST") {
136
+ const bodyText = await req.text();
137
+ let body: unknown;
138
+
139
+ try {
140
+ body = JSON.parse(bodyText);
141
+ } catch (err) {
142
+ return new Response("Invalid JSON", { status: 400 });
143
+ }
144
+
145
+ // Type guard for the request body
146
+ const isToolCallRequest = (obj: unknown): obj is ToolCallRequest => {
147
+ return (
148
+ typeof obj === "object" &&
149
+ obj !== null &&
150
+ "type" in obj &&
151
+ "name" in obj &&
152
+ typeof (obj as ToolCallRequest).type === "string" &&
153
+ typeof (obj as ToolCallRequest).name === "string"
154
+ );
155
+ };
156
+
157
+ // Handle MCP requests by calling the appropriate tool
158
+ if (
159
+ isToolCallRequest(body) &&
160
+ body.type === "tool_call" &&
161
+ body.name.startsWith("wallet_")
162
+ ) {
163
+ // Get handler from our handlers object
164
+ const toolName = body.name as keyof typeof handlers;
165
+ const handler = handlers[toolName];
166
+ if (handler) {
167
+ const result = await handler(
168
+ { args: body.args || {} },
169
+ {
170
+ signal: new AbortController().signal,
171
+ },
172
+ );
173
+
174
+ return new Response(JSON.stringify(result), {
175
+ headers: { "Content-Type": "application/json" },
176
+ });
177
+ }
178
+ }
179
+ }
180
+
181
+ return new Response("Not found", { status: 404 });
182
+ },
183
+ });
184
+
185
+ try {
186
+ const port = httpServer.port;
187
+ const baseUrl = `http://localhost:${port}`;
188
+
189
+ // Test calling the getPublicKey tool
190
+ const response = await fetch(`${baseUrl}`, {
191
+ method: "POST",
192
+ headers: { "Content-Type": "application/json" },
193
+ body: JSON.stringify({
194
+ type: "tool_call",
195
+ name: "wallet_getPublicKey",
196
+ args: {},
197
+ }),
198
+ });
199
+
200
+ const resultData = (await response.json()) as {
201
+ isError?: boolean;
202
+ content?: Array<{ type: string; text: string }>;
203
+ };
204
+
205
+ expect(resultData.content).toBeDefined();
206
+ expect(resultData.content?.[0]?.type).toBe("text");
207
+
208
+ if (resultData.content?.[0]?.text) {
209
+ const parsedContent = JSON.parse(resultData.content[0].text);
210
+ expect(parsedContent).toEqual({ publicKey: "mockPublicKey123" });
211
+ }
212
+ } finally {
213
+ // Shutdown the server
214
+ httpServer.stop();
215
+ }
216
+ });