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 +258 -0
- package/biome.json +30 -0
- package/index.ts +34 -0
- package/package.json +31 -0
- package/tests/integration/wallet-server.test.ts +216 -0
- package/tools/bsv/decodeTransaction.ts +299 -0
- package/tools/bsv/getPrice.ts +44 -0
- package/tools/bsv/index.ts +13 -0
- package/tools/bsv/token.ts +169 -0
- package/tools/index.ts +21 -0
- package/tools/ordinals/bsv20MarketSales.ts +118 -0
- package/tools/ordinals/getBsv20ById.ts +94 -0
- package/tools/ordinals/getInscription.ts +103 -0
- package/tools/ordinals/index.ts +19 -0
- package/tools/ordinals/marketListings.ts +137 -0
- package/tools/ordinals/searchInscriptions.ts +118 -0
- package/tools/utils/conversion.ts +58 -0
- package/tools/utils/index.ts +50 -0
- package/tools/wallet/getAddress.ts +38 -0
- package/tools/wallet/purchaseListing.ts +175 -0
- package/tools/wallet/schemas.ts +284 -0
- package/tools/wallet/sendToAddress.ts +112 -0
- package/tools/wallet/tools.test.ts +76 -0
- package/tools/wallet/tools.ts +206 -0
- package/tools/wallet/wallet.ts +290 -0
- package/tsconfig.json +28 -0
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
|
+
});
|