bsv-mcp 0.0.23 → 0.0.24
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 +225 -38
- package/index.ts +27 -4
- package/package.json +3 -3
- package/tools/a2b/call.ts +32 -0
- package/tools/a2b/discover.ts +101 -0
- package/tools/index.ts +8 -0
- package/tools/wallet/a2bPublishAgent.ts +284 -0
- package/tools/wallet/a2bPublishMcp.ts +161 -0
- package/tools/wallet/getPublicKey.ts +35 -0
- package/tools/wallet/tools.ts +15 -20
package/README.md
CHANGED
|
@@ -8,6 +8,30 @@
|
|
|
8
8
|
|
|
9
9
|
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.
|
|
10
10
|
|
|
11
|
+
## Installation and Setup
|
|
12
|
+
|
|
13
|
+
### Prerequisites
|
|
14
|
+
|
|
15
|
+
This project is built using [Bun](https://bun.sh/), a fast JavaScript runtime and package manager. While Bun is recommended for best performance, the server can also run with Node.js and npm.
|
|
16
|
+
|
|
17
|
+
#### Installing Bun (Recommended)
|
|
18
|
+
|
|
19
|
+
**macOS (using Homebrew):**
|
|
20
|
+
```bash
|
|
21
|
+
brew tap oven-sh/bun
|
|
22
|
+
brew install bun
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**macOS/Linux/WSL (using installer script):**
|
|
26
|
+
```bash
|
|
27
|
+
curl -fsSL https://bun.sh/install | bash
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Windows:**
|
|
31
|
+
Windows users should use WSL (Windows Subsystem for Linux) or Docker to run Bun.
|
|
32
|
+
|
|
33
|
+
Node.js and npm will also work but may not offer the same performance benefits.
|
|
34
|
+
|
|
11
35
|
## Connecting to MCP Clients
|
|
12
36
|
|
|
13
37
|
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.
|
|
@@ -19,39 +43,59 @@ This server implements the [Model Context Protocol](https://modelcontextprotocol
|
|
|
19
43
|
To use the BSV MCP server with [Cursor](https://cursor.sh/):
|
|
20
44
|
|
|
21
45
|
1. Install Cursor if you haven't already
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
24
|
-
4.
|
|
25
|
-
5. Enter the following configuration in JSON format:
|
|
46
|
+
2. Open Cursor and navigate to Settings → Extensions → Model Context Protocol
|
|
47
|
+
3. Click "Add a new global MCP server"
|
|
48
|
+
4. Enter the following configuration in JSON format:
|
|
26
49
|
|
|
27
50
|
```json
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"Bitcoin SV": {
|
|
54
|
+
"command": "bunx",
|
|
55
|
+
"args": [
|
|
56
|
+
"bsv-mcp@latest"
|
|
57
|
+
],
|
|
58
|
+
"env": {
|
|
59
|
+
"PRIVATE_KEY_WIF": "<your_private_key_wif>"
|
|
38
60
|
}
|
|
39
61
|
}
|
|
40
62
|
}
|
|
63
|
+
}
|
|
41
64
|
```
|
|
42
65
|
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
5. Replace `<your_private_key_wif>` with your actual private key WIF (keep this secure!)
|
|
67
|
+
6. Click "Save"
|
|
45
68
|
|
|
46
69
|
The BSV tools will now be available to Cursor's AI assistant under the "Bitcoin SV" namespace.
|
|
47
70
|
|
|
71
|
+
#### Alternative for npm users
|
|
72
|
+
|
|
73
|
+
If you prefer to use npm instead of Bun:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"Bitcoin SV": {
|
|
79
|
+
"command": "npx",
|
|
80
|
+
"args": [
|
|
81
|
+
"bsv-mcp@latest"
|
|
82
|
+
],
|
|
83
|
+
"env": {
|
|
84
|
+
"PRIVATE_KEY_WIF": "<your_private_key_wif>"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
48
91
|
### Claude for Desktop
|
|
49
92
|
|
|
50
93
|
To connect this server to Claude for Desktop:
|
|
51
94
|
|
|
52
|
-
1.
|
|
53
|
-
2.
|
|
54
|
-
|
|
95
|
+
1. Open [Claude for Desktop](https://claude.ai/desktop) and go to Claude > Settings > Developer
|
|
96
|
+
2. Click "Edit Config".
|
|
97
|
+
|
|
98
|
+
Open the Claude configuration json file in your favorite text editor. If you prefer to do it from the cli:
|
|
55
99
|
|
|
56
100
|
```bash
|
|
57
101
|
# macOS/Linux
|
|
@@ -61,25 +105,29 @@ To connect this server to Claude for Desktop:
|
|
|
61
105
|
code %APPDATA%\Claude\claude_desktop_config.json
|
|
62
106
|
```
|
|
63
107
|
|
|
64
|
-
|
|
108
|
+
3. Add the BSV MCP server to your configuration:
|
|
65
109
|
```json
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
110
|
+
{
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"Bitcoin SV": {
|
|
113
|
+
"command": "bunx",
|
|
114
|
+
"args": [
|
|
115
|
+
"bsv-mcp@latest"
|
|
116
|
+
],
|
|
117
|
+
"env": {
|
|
118
|
+
"PRIVATE_KEY_WIF": "<your_private_key_wif>"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
79
123
|
```
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
124
|
+
4. Replace `<your_private_key_wif>` with your actual private key WIF
|
|
125
|
+
5. Save the file and restart Claude for Desktop
|
|
126
|
+
6. The BSV tools will appear when you click the tools icon (hammer) in Claude for Desktop
|
|
127
|
+
|
|
128
|
+
#### Alternative for npm users (Claude)
|
|
129
|
+
|
|
130
|
+
If you prefer to use npm instead of Bun, replace the "command" field with "npx".
|
|
83
131
|
|
|
84
132
|
## Available Tools
|
|
85
133
|
|
|
@@ -179,6 +227,102 @@ Once connected, you can use natural language to interact with Bitcoin SV through
|
|
|
179
227
|
|
|
180
228
|
- "Convert 'Hello World' from UTF-8 to hex format"
|
|
181
229
|
|
|
230
|
+
## MCP Prompts and Resources
|
|
231
|
+
|
|
232
|
+
The BSV MCP server exposes specialized prompts and resources that provide detailed information and context about Bitcoin SV technologies. These can be accessed by AI models to enhance their understanding and capabilities.
|
|
233
|
+
|
|
234
|
+
### Available Prompts
|
|
235
|
+
|
|
236
|
+
The server provides the following educational prompts that can be accessed directly via the MCP protocol:
|
|
237
|
+
|
|
238
|
+
#### Ordinals Prompt
|
|
239
|
+
- **Identifier**: `bitcoin_sv_ordinals`
|
|
240
|
+
- **Description**: Comprehensive information about Bitcoin SV ordinals, including what they are, how they work, and how to use them.
|
|
241
|
+
- **Usage**: Ask the assistant about "Bitcoin SV ordinals" or "1Sat Ordinals" to access this information.
|
|
242
|
+
|
|
243
|
+
#### BSV SDK Prompts
|
|
244
|
+
A collection of prompts providing detailed information about the Bitcoin SV SDK:
|
|
245
|
+
|
|
246
|
+
- **Overview**
|
|
247
|
+
- **Identifier**: `bitcoin_sv_sdk_overview`
|
|
248
|
+
- **Description**: General overview of the Bitcoin SV SDK, including its purpose and main components.
|
|
249
|
+
- **Usage**: "Tell me about the BSV SDK" or "What is the Bitcoin SV SDK?"
|
|
250
|
+
|
|
251
|
+
- **Wallet Operations**
|
|
252
|
+
- **Identifier**: `bitcoin_sv_sdk_wallet`
|
|
253
|
+
- **Description**: Information about wallet operations in the BSV SDK.
|
|
254
|
+
- **Usage**: "How do wallet operations work in the BSV SDK?"
|
|
255
|
+
|
|
256
|
+
- **Transaction Building**
|
|
257
|
+
- **Identifier**: `bitcoin_sv_sdk_transaction`
|
|
258
|
+
- **Description**: Details about transaction creation and manipulation.
|
|
259
|
+
- **Usage**: "Explain BSV SDK transaction building" or "How do I create transactions with BSV SDK?"
|
|
260
|
+
|
|
261
|
+
- **Authentication**
|
|
262
|
+
- **Identifier**: `bitcoin_sv_sdk_auth`
|
|
263
|
+
- **Description**: Authentication and identity protocols in BSV SDK.
|
|
264
|
+
- **Usage**: "How does authentication work with BSV SDK?"
|
|
265
|
+
|
|
266
|
+
- **Cryptography**
|
|
267
|
+
- **Identifier**: `bitcoin_sv_sdk_cryptography`
|
|
268
|
+
- **Description**: Signing, encryption, and verification functionality.
|
|
269
|
+
- **Usage**: "Explain BSV SDK cryptography features"
|
|
270
|
+
|
|
271
|
+
- **Scripting**
|
|
272
|
+
- **Identifier**: `bitcoin_sv_sdk_script`
|
|
273
|
+
- **Description**: Bitcoin scripting and contract capabilities.
|
|
274
|
+
- **Usage**: "How do I work with Bitcoin scripts using the BSV SDK?"
|
|
275
|
+
|
|
276
|
+
- **Primitives**
|
|
277
|
+
- **Identifier**: `bitcoin_sv_sdk_primitives`
|
|
278
|
+
- **Description**: Core data types and structures in the BSV SDK.
|
|
279
|
+
- **Usage**: "What primitives are available in the BSV SDK?"
|
|
280
|
+
|
|
281
|
+
### Available Resources
|
|
282
|
+
|
|
283
|
+
The server also provides access to Bitcoin Request for Comments (BRC) specifications and documentation:
|
|
284
|
+
|
|
285
|
+
#### BRC Resources
|
|
286
|
+
- **BRCs Overview**
|
|
287
|
+
- **Identifier**: `brcs_readme`
|
|
288
|
+
- **Description**: Overview of all Bitcoin SV protocol specifications in the BRCs repository.
|
|
289
|
+
- **Usage**: "Show me the Bitcoin SV BRCs overview"
|
|
290
|
+
|
|
291
|
+
- **BRCs Summary**
|
|
292
|
+
- **Identifier**: `brcs_summary`
|
|
293
|
+
- **Description**: Table of contents for all Bitcoin SV BRCs.
|
|
294
|
+
- **Usage**: "Give me a summary of Bitcoin SV BRCs"
|
|
295
|
+
|
|
296
|
+
- **Specific BRC Specifications**
|
|
297
|
+
- **Identifier**: `brc_spec`
|
|
298
|
+
- **Description**: Access specific BRC specifications by category and number.
|
|
299
|
+
- **Usage**: "Show me BRC 8 on Transaction Envelopes" or "What does BRC 1 specify?"
|
|
300
|
+
|
|
301
|
+
#### BRC Categories
|
|
302
|
+
The BRC specifications are organized into the following categories:
|
|
303
|
+
- Wallet
|
|
304
|
+
- Transactions
|
|
305
|
+
- Scripts
|
|
306
|
+
- Tokens
|
|
307
|
+
- Overlays
|
|
308
|
+
- Payments
|
|
309
|
+
- Peer-to-Peer
|
|
310
|
+
- Key Derivation
|
|
311
|
+
- Outpoints
|
|
312
|
+
- Opinions
|
|
313
|
+
- State Machines
|
|
314
|
+
- Apps
|
|
315
|
+
|
|
316
|
+
### Using Prompts and Resources
|
|
317
|
+
|
|
318
|
+
AI models can use these prompts and resources to provide more accurate and detailed responses about Bitcoin SV technologies. As a user, you can:
|
|
319
|
+
|
|
320
|
+
1. **Ask about a specific topic**: "Tell me about Bitcoin SV ordinals" or "Explain BSV SDK transaction building"
|
|
321
|
+
2. **Request specific BRC details**: "What does BRC 8 specify?" or "Show me the BRC on Transaction Creation"
|
|
322
|
+
3. **Get general overviews**: "What is the BSV SDK?" or "Show me a summary of all BRCs"
|
|
323
|
+
|
|
324
|
+
These prompts and resources enhance the AI's knowledge base, enabling more technical and accurate responses even for complex Bitcoin SV topics.
|
|
325
|
+
|
|
182
326
|
## How MCP Works
|
|
183
327
|
|
|
184
328
|
When you interact with an MCP-enabled AI assistant:
|
|
@@ -191,17 +335,39 @@ When you interact with an MCP-enabled AI assistant:
|
|
|
191
335
|
|
|
192
336
|
## Troubleshooting
|
|
193
337
|
|
|
194
|
-
If you're having issues
|
|
338
|
+
If you're having issues with the BSV MCP server:
|
|
339
|
+
|
|
340
|
+
### Connection Issues
|
|
195
341
|
|
|
196
|
-
1.
|
|
342
|
+
1. Make sure Bun or Node.js is installed on your system
|
|
197
343
|
2. Verify your WIF private key is correctly set in the environment
|
|
198
344
|
3. Check that your client supports MCP and is properly configured
|
|
199
345
|
4. Look for error messages in the client's console output
|
|
200
346
|
|
|
347
|
+
### Keeping Bun Up to Date
|
|
348
|
+
|
|
349
|
+
It's important to keep Bun updated to the latest version to ensure compatibility:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Update Bun to the latest version
|
|
353
|
+
bun upgrade
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
To verify your current Bun version:
|
|
357
|
+
```bash
|
|
358
|
+
bun --version
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Logging and Debugging
|
|
362
|
+
|
|
201
363
|
For Claude for Desktop, check the logs at:
|
|
202
364
|
|
|
203
365
|
```bash
|
|
366
|
+
# macOS/Linux
|
|
204
367
|
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
|
|
368
|
+
|
|
369
|
+
# Windows
|
|
370
|
+
type %APPDATA%\Claude\Logs\mcp*.log
|
|
205
371
|
```
|
|
206
372
|
|
|
207
373
|
For Cursor, check the Cursor MCP logs in Settings → Extensions → Model Context Protocol.
|
|
@@ -314,16 +480,37 @@ The tool supports both mainnet and testnet:
|
|
|
314
480
|
|
|
315
481
|
# Development
|
|
316
482
|
|
|
483
|
+
## Project Setup
|
|
484
|
+
|
|
485
|
+
If you want to contribute to the project or run it locally:
|
|
486
|
+
|
|
487
|
+
1. Clone the repository:
|
|
488
|
+
```bash
|
|
489
|
+
git clone https://github.com/b-open-io/bsv-mcp.git
|
|
490
|
+
cd bsv-mcp
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
2. Install dependencies:
|
|
494
|
+
```bash
|
|
495
|
+
bun install
|
|
496
|
+
# or with npm
|
|
497
|
+
npm install
|
|
498
|
+
```
|
|
499
|
+
|
|
317
500
|
## Running the Server
|
|
318
501
|
|
|
319
502
|
```bash
|
|
320
503
|
bun run index.ts
|
|
504
|
+
# or with npm
|
|
505
|
+
npm run start
|
|
321
506
|
```
|
|
322
507
|
|
|
323
508
|
### Running Tests
|
|
324
509
|
|
|
325
510
|
```bash
|
|
326
511
|
bun test
|
|
512
|
+
# or with npm
|
|
513
|
+
npm test
|
|
327
514
|
```
|
|
328
515
|
|
|
329
516
|
## License
|
package/index.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { PrivateKey } from "@bsv/sdk";
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { registerAllPrompts } from "./prompts";
|
|
6
|
+
import { registerResources } from "./resources/resources";
|
|
5
7
|
import { registerAllTools } from "./tools";
|
|
6
8
|
import { registerWalletTools } from "./tools/wallet/tools";
|
|
7
9
|
import { Wallet } from "./tools/wallet/wallet";
|
|
@@ -43,10 +45,25 @@ function validatePrivateKey(): PrivateKey {
|
|
|
43
45
|
// Validate private key early before starting the server
|
|
44
46
|
const privKey = validatePrivateKey();
|
|
45
47
|
|
|
46
|
-
const server = new McpServer(
|
|
47
|
-
name: "Bitcoin SV
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const server = new McpServer(
|
|
49
|
+
{ name: "Bitcoin SV", version: "0.0.24" },
|
|
50
|
+
// {
|
|
51
|
+
// // Advertise only what you actually implement
|
|
52
|
+
// capabilities: {
|
|
53
|
+
// completions: {},
|
|
54
|
+
// experimental: {},
|
|
55
|
+
// logging: {},
|
|
56
|
+
// prompts: {},
|
|
57
|
+
// resources: {},
|
|
58
|
+
// tools: {},
|
|
59
|
+
// },
|
|
60
|
+
// // Optional instructions banner for clients
|
|
61
|
+
// instructions: `
|
|
62
|
+
// This server exposes Bitcoin SV helpers.
|
|
63
|
+
// Tools are idempotent unless marked destructive.
|
|
64
|
+
// `,
|
|
65
|
+
// },
|
|
66
|
+
);
|
|
50
67
|
|
|
51
68
|
// Initialize wallet with the validated private key
|
|
52
69
|
const wallet = new Wallet(privKey);
|
|
@@ -57,6 +74,12 @@ registerWalletTools(server, wallet);
|
|
|
57
74
|
// Register all other tools (BSV, Ordinals, Utils, etc.)
|
|
58
75
|
registerAllTools(server);
|
|
59
76
|
|
|
77
|
+
// Register resources
|
|
78
|
+
registerResources(server);
|
|
79
|
+
|
|
80
|
+
// Register prompts
|
|
81
|
+
registerAllPrompts(server);
|
|
82
|
+
|
|
60
83
|
// Connect to the transport
|
|
61
84
|
const transport = new StdioServerTransport();
|
|
62
85
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "bsv-mcp",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.24",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "satchmo",
|
|
8
8
|
"description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"typescript": "^5.8.3"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@bsv/sdk": "^1.4.
|
|
45
|
-
"@modelcontextprotocol/sdk": "^1.10.
|
|
44
|
+
"@bsv/sdk": "^1.4.20",
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.10.2",
|
|
46
46
|
"@types/node": "^22.14.1",
|
|
47
47
|
"js-1sat-ord": "^0.1.81",
|
|
48
48
|
"mnee": "^2.0.0",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
// Schema for invoking another agent via A2A protocol
|
|
6
|
+
export const a2aCallArgsSchema = z.object({
|
|
7
|
+
url: z.string().url().describe("Full agent-to-agent endpoint URL"),
|
|
8
|
+
method: z.string().describe("A2A method name to invoke"),
|
|
9
|
+
params: z
|
|
10
|
+
.record(z.any())
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Payload parameters for the A2A call"),
|
|
13
|
+
});
|
|
14
|
+
export type A2aCallArgs = z.infer<typeof a2aCallArgsSchema>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Registers the a2a_call tool for agent-to-agent HTTP/SSE calls
|
|
18
|
+
*/
|
|
19
|
+
export function registerA2aCallTool(server: McpServer) {
|
|
20
|
+
server.tool(
|
|
21
|
+
"a2a_call",
|
|
22
|
+
"Invoke a remote agent's A2A endpoint via HTTP/SSE",
|
|
23
|
+
{ args: a2aCallArgsSchema },
|
|
24
|
+
async ({ args }: { args: A2aCallArgs }, extra: RequestHandlerExtra) => {
|
|
25
|
+
// TODO: implement HTTP request logic (e.g., fetch, SSE)
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: "text", text: "Not implemented" }],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
+
import type {
|
|
4
|
+
ServerNotification,
|
|
5
|
+
ServerRequest,
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
type OverlayRequest = {
|
|
10
|
+
type: "agent" | "tool";
|
|
11
|
+
query: string;
|
|
12
|
+
limit: number;
|
|
13
|
+
offset: number;
|
|
14
|
+
fromBlock?: number;
|
|
15
|
+
toBlock?: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type OverlayResponse = {
|
|
19
|
+
agents: {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
capabilities: string[];
|
|
23
|
+
}[];
|
|
24
|
+
tools: {
|
|
25
|
+
name: string;
|
|
26
|
+
description: string;
|
|
27
|
+
}[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Schema for agent discovery parameters
|
|
31
|
+
export const a2bDiscoverArgsSchema = z.object({
|
|
32
|
+
queryType: z.enum(["agent", "tool"]).describe("Type of discovery to perform"),
|
|
33
|
+
query: z.string().describe("Search agent or tool names, descriptions"),
|
|
34
|
+
limit: z.number().optional().describe("Limit the number of results"),
|
|
35
|
+
offset: z.number().optional().describe("Offset the results"),
|
|
36
|
+
fromBlock: z.number().optional().describe("From block"),
|
|
37
|
+
toBlock: z.number().optional().describe("To block"),
|
|
38
|
+
});
|
|
39
|
+
export type A2bDiscoverArgs = z.infer<typeof a2bDiscoverArgsSchema>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Registers the a2b_discover tool for on-chain agent discovery
|
|
43
|
+
*/
|
|
44
|
+
export function registerA2bDiscoverTool(server: McpServer) {
|
|
45
|
+
server.tool(
|
|
46
|
+
"a2b_discover",
|
|
47
|
+
"Search on-chain agent and MCP tool records. Use 'agent' to search for agents, 'tool' to search for MCP tools.",
|
|
48
|
+
{ args: a2bDiscoverArgsSchema },
|
|
49
|
+
async (
|
|
50
|
+
{ args }: { args: A2bDiscoverArgs },
|
|
51
|
+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
52
|
+
) => {
|
|
53
|
+
if (args.queryType === "agent") {
|
|
54
|
+
return {
|
|
55
|
+
content: [
|
|
56
|
+
{ type: "text", text: "Agent discovery is not supported yet" },
|
|
57
|
+
],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (args.queryType !== "tool") {
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: "text",
|
|
67
|
+
text: "Only tool discovery is supported currently",
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
isError: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const params = new URLSearchParams();
|
|
76
|
+
params.set("type", args.queryType);
|
|
77
|
+
params.set("query", args.query);
|
|
78
|
+
params.set("limit", args.limit?.toString() ?? "5");
|
|
79
|
+
params.set("offset", args.offset?.toString() ?? "0");
|
|
80
|
+
if (args.fromBlock) {
|
|
81
|
+
params.set("fromBlock", args.fromBlock.toString());
|
|
82
|
+
}
|
|
83
|
+
if (args.toBlock) {
|
|
84
|
+
params.set("toBlock", args.toBlock.toString());
|
|
85
|
+
}
|
|
86
|
+
const OVERLAY_URL = `https://overlay.a2b.network/v1/search?${params.toString()}`;
|
|
87
|
+
const response = await fetch(OVERLAY_URL);
|
|
88
|
+
const data = (await response.json()) as OverlayResponse;
|
|
89
|
+
return {
|
|
90
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
91
|
+
isError: false,
|
|
92
|
+
};
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: "text", text: `Error querying overlay: ${error}` }],
|
|
96
|
+
isError: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
}
|
package/tools/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { registerA2aCallTool } from "./a2b/call";
|
|
3
|
+
import { registerA2bDiscoverTool } from "./a2b/discover";
|
|
2
4
|
import { registerBsvTools } from "./bsv";
|
|
3
5
|
import { registerMneeTools } from "./mnee";
|
|
4
6
|
import { registerOrdinalsTools } from "./ordinals";
|
|
@@ -18,6 +20,12 @@ export function registerAllTools(server: McpServer): void {
|
|
|
18
20
|
// Register utility tools
|
|
19
21
|
registerUtilsTools(server);
|
|
20
22
|
|
|
23
|
+
// Register agent-to-blockchain discovery tool
|
|
24
|
+
registerA2bDiscoverTool(server);
|
|
25
|
+
|
|
26
|
+
// Register agent-to-agent call tool
|
|
27
|
+
// registerA2aCallTool(server);
|
|
28
|
+
|
|
21
29
|
// Register MNEE tools
|
|
22
30
|
registerMneeTools(server);
|
|
23
31
|
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { Utils } from "@bsv/sdk";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
|
+
import type {
|
|
5
|
+
ServerNotification,
|
|
6
|
+
ServerRequest,
|
|
7
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import { createOrdinals } from "js-1sat-ord";
|
|
9
|
+
import type {
|
|
10
|
+
ChangeResult,
|
|
11
|
+
Destination,
|
|
12
|
+
Inscription,
|
|
13
|
+
PreMAP,
|
|
14
|
+
} from "js-1sat-ord";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import type { Wallet } from "./wallet";
|
|
17
|
+
const { toArray, toBase64 } = Utils;
|
|
18
|
+
|
|
19
|
+
// https://raw.githubusercontent.com/google/A2A/refs/heads/main/specification/json/a2a.json
|
|
20
|
+
|
|
21
|
+
// A2A AgentCard schema (per A2A spec)
|
|
22
|
+
const AgentCapabilitiesSchema = z.object({
|
|
23
|
+
streaming: z.boolean().default(false),
|
|
24
|
+
pushNotifications: z.boolean().default(false),
|
|
25
|
+
stateTransitionHistory: z.boolean().default(false),
|
|
26
|
+
});
|
|
27
|
+
const AgentSkillSchema = z.object({
|
|
28
|
+
id: z.string(),
|
|
29
|
+
name: z.string(),
|
|
30
|
+
description: z.string().nullable(),
|
|
31
|
+
tags: z.array(z.string()).nullable(),
|
|
32
|
+
examples: z.array(z.string()).nullable(),
|
|
33
|
+
inputModes: z.array(z.string()).nullable(),
|
|
34
|
+
outputModes: z.array(z.string()).nullable(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Provider per A2A spec
|
|
38
|
+
const AgentProviderSchema = z
|
|
39
|
+
.object({
|
|
40
|
+
organization: z.string(),
|
|
41
|
+
url: z.string().url().nullable().default(null),
|
|
42
|
+
})
|
|
43
|
+
.nullable()
|
|
44
|
+
.default(null);
|
|
45
|
+
|
|
46
|
+
// Authentication per A2A spec
|
|
47
|
+
const AgentAuthenticationSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
schemes: z.array(z.string()),
|
|
50
|
+
credentials: z.string().nullable().default(null),
|
|
51
|
+
})
|
|
52
|
+
.nullable()
|
|
53
|
+
.default(null);
|
|
54
|
+
|
|
55
|
+
// Pricing plan schema
|
|
56
|
+
export const PricingSchema = z.object({
|
|
57
|
+
id: z.string(),
|
|
58
|
+
description: z.string().nullable().optional(),
|
|
59
|
+
currency: z.string(),
|
|
60
|
+
amount: z
|
|
61
|
+
.number()
|
|
62
|
+
.describe(
|
|
63
|
+
"Cost in standard units of currency (e.g., BSV, USD, not satoshis)",
|
|
64
|
+
),
|
|
65
|
+
address: z.string(),
|
|
66
|
+
acceptedCurrencies: z.array(z.string()).optional(),
|
|
67
|
+
skills: z.array(z.string()).optional(),
|
|
68
|
+
interval: z.enum(["day", "week", "month", "year"]).nullable().optional(),
|
|
69
|
+
includedCalls: z.record(z.number()).optional(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export type PricingConfig = z.infer<typeof PricingSchema>;
|
|
73
|
+
|
|
74
|
+
export const AgentCardSchema = z.object({
|
|
75
|
+
name: z.string(),
|
|
76
|
+
description: z.string().nullable().default(null),
|
|
77
|
+
url: z.string().url(),
|
|
78
|
+
provider: AgentProviderSchema,
|
|
79
|
+
version: z.string(),
|
|
80
|
+
documentationUrl: z.string().url().nullable().default(null),
|
|
81
|
+
capabilities: AgentCapabilitiesSchema,
|
|
82
|
+
authentication: AgentAuthenticationSchema,
|
|
83
|
+
defaultInputModes: z.array(z.string()).default(["text"]),
|
|
84
|
+
defaultOutputModes: z.array(z.string()).default(["text"]),
|
|
85
|
+
skills: z.array(AgentSkillSchema),
|
|
86
|
+
"x-payment": z.array(PricingSchema).optional().default([]),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Schema for on-chain agent publish parameters
|
|
90
|
+
export const a2bPublishArgsSchema = z.object({
|
|
91
|
+
agentUrl: z
|
|
92
|
+
.string()
|
|
93
|
+
.url()
|
|
94
|
+
.describe("Agent base URL (e.g. https://example.com)"),
|
|
95
|
+
agentName: z.string().describe("Human-friendly agent name"),
|
|
96
|
+
description: z
|
|
97
|
+
.string()
|
|
98
|
+
.nullable()
|
|
99
|
+
.optional()
|
|
100
|
+
.describe("Optional agent description"),
|
|
101
|
+
providerOrganization: z
|
|
102
|
+
.string()
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("Optional provider organization name"),
|
|
105
|
+
providerUrl: z.string().url().optional().describe("Optional provider URL"),
|
|
106
|
+
version: z.string().optional().describe("Optional agent version"),
|
|
107
|
+
documentationUrl: z
|
|
108
|
+
.string()
|
|
109
|
+
.url()
|
|
110
|
+
.nullable()
|
|
111
|
+
.optional()
|
|
112
|
+
.describe("Optional documentation URL"),
|
|
113
|
+
streaming: z
|
|
114
|
+
.boolean()
|
|
115
|
+
.default(false)
|
|
116
|
+
.describe("Supports SSE (tasks/sendSubscribe)"),
|
|
117
|
+
pushNotifications: z
|
|
118
|
+
.boolean()
|
|
119
|
+
.default(false)
|
|
120
|
+
.describe("Supports push notifications"),
|
|
121
|
+
stateTransitionHistory: z
|
|
122
|
+
.boolean()
|
|
123
|
+
.default(false)
|
|
124
|
+
.describe("Supports state transition history"),
|
|
125
|
+
defaultInputModes: z
|
|
126
|
+
.array(z.string())
|
|
127
|
+
.default(["text"])
|
|
128
|
+
.describe("Default input modes"),
|
|
129
|
+
defaultOutputModes: z
|
|
130
|
+
.array(z.string())
|
|
131
|
+
.default(["text"])
|
|
132
|
+
.describe("Default output modes"),
|
|
133
|
+
skills: z
|
|
134
|
+
.array(AgentSkillSchema)
|
|
135
|
+
.optional()
|
|
136
|
+
.default([])
|
|
137
|
+
.describe("List of agent skills"),
|
|
138
|
+
destinationAddress: z
|
|
139
|
+
.string()
|
|
140
|
+
.optional()
|
|
141
|
+
.describe("Optional target address for inscription"),
|
|
142
|
+
});
|
|
143
|
+
export type A2bPublishArgs = z.infer<typeof a2bPublishArgsSchema>;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Registers the wallet_a2bPublish tool for publishing an agent record on-chain
|
|
147
|
+
*/
|
|
148
|
+
export function registerA2bPublishAgentTool(server: McpServer, wallet: Wallet) {
|
|
149
|
+
server.tool(
|
|
150
|
+
"wallet_a2bPublish",
|
|
151
|
+
"Publish an agent.json record on-chain via Ordinal inscription",
|
|
152
|
+
{ args: a2bPublishArgsSchema },
|
|
153
|
+
async (
|
|
154
|
+
{ args }: { args: A2bPublishArgs },
|
|
155
|
+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
156
|
+
) => {
|
|
157
|
+
try {
|
|
158
|
+
const paymentPk = wallet.getPrivateKey();
|
|
159
|
+
if (!paymentPk) throw new Error("No private key available");
|
|
160
|
+
const { paymentUtxos } = await wallet.getUtxos();
|
|
161
|
+
if (!paymentUtxos?.length)
|
|
162
|
+
throw new Error("No payment UTXOs available to fund inscription");
|
|
163
|
+
|
|
164
|
+
// TODO: Get the skills from actually running the MCP instead of trusting the agent args for args.skills
|
|
165
|
+
const walletAddress = paymentPk.toAddress().toString();
|
|
166
|
+
|
|
167
|
+
// Create pricing plans using the new schema
|
|
168
|
+
const pricingConfig: PricingConfig[] = [
|
|
169
|
+
// {
|
|
170
|
+
// id: "subscription-premium",
|
|
171
|
+
// description: "Premium subscription with all features",
|
|
172
|
+
// currency: "USD",
|
|
173
|
+
// amount: 10,
|
|
174
|
+
// address: walletAddress,
|
|
175
|
+
// interval: "month",
|
|
176
|
+
// skills: ["wallet_a2bPublish", "wallet_a2bCall"],
|
|
177
|
+
// },
|
|
178
|
+
// {
|
|
179
|
+
// id: "subscription-free",
|
|
180
|
+
// description: "Free tier with limited features",
|
|
181
|
+
// currency: "USD",
|
|
182
|
+
// amount: 0,
|
|
183
|
+
// address: walletAddress,
|
|
184
|
+
// interval: "month",
|
|
185
|
+
// skills: ["wallet_a2bCall"],
|
|
186
|
+
// },
|
|
187
|
+
{
|
|
188
|
+
id: "pay-per-call-default",
|
|
189
|
+
description: "Pay-per-call for publish operations",
|
|
190
|
+
currency: "USD",
|
|
191
|
+
amount: 1,
|
|
192
|
+
address: walletAddress,
|
|
193
|
+
skills: ["wallet_a2bPublish"],
|
|
194
|
+
interval: null,
|
|
195
|
+
},
|
|
196
|
+
// {
|
|
197
|
+
// id: "pay-per-call-royalty",
|
|
198
|
+
// description: "Royalty payment for agent",
|
|
199
|
+
// currency: "USD",
|
|
200
|
+
// amount: 1,
|
|
201
|
+
// address: "1JOExxxxxxxxxxxx",
|
|
202
|
+
// skills: ["wallet_a2bPublish"],
|
|
203
|
+
// interval: null,
|
|
204
|
+
// }
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
// Assemble AgentCard with defaults and user overrides
|
|
208
|
+
const agentCard = {
|
|
209
|
+
name: args.agentName,
|
|
210
|
+
description: args.description ?? null,
|
|
211
|
+
url: args.agentUrl,
|
|
212
|
+
provider:
|
|
213
|
+
args.providerOrganization && args.providerUrl
|
|
214
|
+
? {
|
|
215
|
+
organization: args.providerOrganization,
|
|
216
|
+
url: args.providerUrl,
|
|
217
|
+
}
|
|
218
|
+
: null,
|
|
219
|
+
version: args.version ?? "1.0.0",
|
|
220
|
+
documentationUrl: args.documentationUrl ?? null,
|
|
221
|
+
capabilities: {
|
|
222
|
+
streaming: args.streaming,
|
|
223
|
+
pushNotifications: args.pushNotifications,
|
|
224
|
+
stateTransitionHistory: args.stateTransitionHistory,
|
|
225
|
+
},
|
|
226
|
+
authentication: null,
|
|
227
|
+
defaultInputModes: args.defaultInputModes,
|
|
228
|
+
defaultOutputModes: args.defaultOutputModes,
|
|
229
|
+
skills: args.skills,
|
|
230
|
+
"x-payment-config": pricingConfig,
|
|
231
|
+
};
|
|
232
|
+
// Validate compliance
|
|
233
|
+
AgentCardSchema.parse(agentCard);
|
|
234
|
+
const fileContent = JSON.stringify(agentCard, null, 2);
|
|
235
|
+
// Base64 payload for inscription
|
|
236
|
+
const dataB64 = toBase64(toArray(fileContent));
|
|
237
|
+
const inscription: Inscription = {
|
|
238
|
+
dataB64,
|
|
239
|
+
contentType: "application/json",
|
|
240
|
+
};
|
|
241
|
+
// Destination for the ordinal
|
|
242
|
+
const targetAddress = args.destinationAddress ?? walletAddress;
|
|
243
|
+
const destinations: Destination[] = [
|
|
244
|
+
{ address: targetAddress, inscription },
|
|
245
|
+
];
|
|
246
|
+
// Default MAP metadata: file path, content type, encoding
|
|
247
|
+
const metaData: PreMAP = { app: "bsv-mcp", type: "a2b" };
|
|
248
|
+
|
|
249
|
+
// Inscribe the ordinal on-chain via js-1sat-ord
|
|
250
|
+
const result = await createOrdinals({
|
|
251
|
+
utxos: paymentUtxos,
|
|
252
|
+
destinations,
|
|
253
|
+
paymentPk,
|
|
254
|
+
changeAddress: walletAddress,
|
|
255
|
+
metaData,
|
|
256
|
+
});
|
|
257
|
+
const changeResult = result as ChangeResult;
|
|
258
|
+
await changeResult.tx.broadcast();
|
|
259
|
+
// Refresh UTXOs
|
|
260
|
+
try {
|
|
261
|
+
await wallet.refreshUtxos();
|
|
262
|
+
} catch {}
|
|
263
|
+
// Return transaction details
|
|
264
|
+
return {
|
|
265
|
+
content: [
|
|
266
|
+
{
|
|
267
|
+
type: "text",
|
|
268
|
+
text: JSON.stringify({
|
|
269
|
+
txid: changeResult.tx.id("hex"),
|
|
270
|
+
spentOutpoints: changeResult.spentOutpoints,
|
|
271
|
+
payChange: changeResult.payChange,
|
|
272
|
+
inscriptionAddress: targetAddress,
|
|
273
|
+
agentCard,
|
|
274
|
+
}),
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
} catch (err: unknown) {
|
|
279
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
280
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
);
|
|
284
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { Utils } from "@bsv/sdk";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
4
|
+
import type {
|
|
5
|
+
ServerNotification,
|
|
6
|
+
ServerRequest,
|
|
7
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import { createOrdinals } from "js-1sat-ord";
|
|
9
|
+
import type {
|
|
10
|
+
ChangeResult,
|
|
11
|
+
Destination,
|
|
12
|
+
Inscription,
|
|
13
|
+
PreMAP,
|
|
14
|
+
} from "js-1sat-ord";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import type { Wallet } from "./wallet";
|
|
17
|
+
const { toArray, toBase64 } = Utils;
|
|
18
|
+
|
|
19
|
+
// Schema for the MCP tool configuration
|
|
20
|
+
export const McpConfigSchema = z.object({
|
|
21
|
+
command: z.string().describe("The command to execute the tool"),
|
|
22
|
+
args: z.array(z.string()).describe("Arguments to pass to the command"),
|
|
23
|
+
env: z.record(z.string()).optional().describe("Environment variables"),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export type McpConfig = z.infer<typeof McpConfigSchema>;
|
|
27
|
+
|
|
28
|
+
// Schema for on-chain tool publish parameters
|
|
29
|
+
export const a2bPublishMcpArgsSchema = z.object({
|
|
30
|
+
toolName: z.string().describe("Human-friendly tool name"),
|
|
31
|
+
command: z.string().describe("The command to execute the tool"),
|
|
32
|
+
args: z.array(z.string()).describe("Arguments to pass to the command"),
|
|
33
|
+
env: z
|
|
34
|
+
.record(z.string())
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Optional environment variables"),
|
|
37
|
+
description: z.string().optional().describe("Optional tool description"),
|
|
38
|
+
destinationAddress: z
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Optional target address for inscription"),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export type A2bPublishMcpArgs = z.infer<typeof a2bPublishMcpArgsSchema>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Registers the wallet_a2bPublishMcp for publishing an MCP tool configuration on-chain
|
|
48
|
+
*/
|
|
49
|
+
export function registerA2bPublishMcpTool(server: McpServer, wallet: Wallet) {
|
|
50
|
+
server.tool(
|
|
51
|
+
"wallet_a2bPublishMcp",
|
|
52
|
+
"Publish an MCP tool configuration record on-chain via Ordinal inscription",
|
|
53
|
+
{ args: a2bPublishMcpArgsSchema },
|
|
54
|
+
async (
|
|
55
|
+
{ args }: { args: A2bPublishMcpArgs },
|
|
56
|
+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
57
|
+
) => {
|
|
58
|
+
try {
|
|
59
|
+
const paymentPk = wallet.getPrivateKey();
|
|
60
|
+
if (!paymentPk) throw new Error("No private key available");
|
|
61
|
+
|
|
62
|
+
const { paymentUtxos } = await wallet.getUtxos();
|
|
63
|
+
if (!paymentUtxos?.length)
|
|
64
|
+
throw new Error("No payment UTXOs available to fund inscription");
|
|
65
|
+
|
|
66
|
+
const walletAddress = paymentPk.toAddress().toString();
|
|
67
|
+
|
|
68
|
+
// Assemble tool configuration
|
|
69
|
+
const toolConfig: McpConfig = {
|
|
70
|
+
command: args.command,
|
|
71
|
+
args: args.args,
|
|
72
|
+
env: args.env,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Validate compliance
|
|
76
|
+
McpConfigSchema.parse(toolConfig);
|
|
77
|
+
|
|
78
|
+
// Prepare the full configuration with metadata
|
|
79
|
+
const fullConfig = {
|
|
80
|
+
name: args.toolName,
|
|
81
|
+
description: args.description || `MCP Tool: ${args.toolName}`,
|
|
82
|
+
config: toolConfig,
|
|
83
|
+
type: "mcp-tool",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const fileContent = JSON.stringify(fullConfig, null, 2);
|
|
87
|
+
|
|
88
|
+
// Base64 payload for inscription
|
|
89
|
+
const dataB64 = toBase64(toArray(fileContent));
|
|
90
|
+
const inscription: Inscription = {
|
|
91
|
+
dataB64,
|
|
92
|
+
contentType: "application/json",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Destination for the ordinal
|
|
96
|
+
const targetAddress = args.destinationAddress ?? walletAddress;
|
|
97
|
+
const destinations: Destination[] = [
|
|
98
|
+
{ address: targetAddress, inscription },
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
// Default MAP metadata: file path, content type, encoding
|
|
102
|
+
const metaData: PreMAP = { app: "bsv-mcp", type: "a2b-mcp" };
|
|
103
|
+
|
|
104
|
+
// Inscribe the ordinal on-chain via js-1sat-ord
|
|
105
|
+
const result = await createOrdinals({
|
|
106
|
+
utxos: paymentUtxos,
|
|
107
|
+
destinations,
|
|
108
|
+
paymentPk,
|
|
109
|
+
changeAddress: walletAddress,
|
|
110
|
+
metaData,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const changeResult = result as ChangeResult;
|
|
114
|
+
|
|
115
|
+
// Broadcast the transaction
|
|
116
|
+
await changeResult.tx.broadcast();
|
|
117
|
+
|
|
118
|
+
// Refresh UTXOs after spending
|
|
119
|
+
try {
|
|
120
|
+
await wallet.refreshUtxos();
|
|
121
|
+
} catch (refreshError) {
|
|
122
|
+
console.warn(
|
|
123
|
+
"Failed to refresh UTXOs after transaction:",
|
|
124
|
+
refreshError,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Build a nicely formatted result
|
|
129
|
+
const outpointIndex = 0; // First output with the inscription
|
|
130
|
+
const outpoint = `${changeResult.tx.id("hex")}_${outpointIndex}`;
|
|
131
|
+
|
|
132
|
+
// Tool URL for discovery is the outpoint
|
|
133
|
+
const onchainUrl = `ord://${outpoint}`;
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
content: [
|
|
137
|
+
{
|
|
138
|
+
type: "text",
|
|
139
|
+
text: JSON.stringify(
|
|
140
|
+
{
|
|
141
|
+
status: "success",
|
|
142
|
+
txid: changeResult.tx.id("hex"),
|
|
143
|
+
outpoint,
|
|
144
|
+
onchainUrl,
|
|
145
|
+
toolName: args.toolName,
|
|
146
|
+
description: args.description || `MCP Tool: ${args.toolName}`,
|
|
147
|
+
address: targetAddress,
|
|
148
|
+
},
|
|
149
|
+
null,
|
|
150
|
+
2,
|
|
151
|
+
),
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
} catch (err: unknown) {
|
|
156
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
157
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
+
import type {
|
|
4
|
+
ServerNotification,
|
|
5
|
+
ServerRequest,
|
|
6
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
import type { z } from "zod";
|
|
8
|
+
import { getPublicKeyArgsSchema } from "./schemas";
|
|
9
|
+
import type { Wallet } from "./wallet";
|
|
10
|
+
|
|
11
|
+
// Use the schema imported from schemas.ts
|
|
12
|
+
export type GetPublicKeyArgs = z.infer<typeof getPublicKeyArgsSchema>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Register the getPublicKey tool
|
|
16
|
+
*/
|
|
17
|
+
export function registerGetPublicKeyTool(server: McpServer, wallet: Wallet) {
|
|
18
|
+
server.tool(
|
|
19
|
+
"wallet_getPublicKey",
|
|
20
|
+
"Retrieves the current wallet's public key. This public key can be used for cryptographic operations like signature verification or encryption.",
|
|
21
|
+
{ args: getPublicKeyArgsSchema },
|
|
22
|
+
async (
|
|
23
|
+
{ args }: { args: GetPublicKeyArgs },
|
|
24
|
+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
25
|
+
) => {
|
|
26
|
+
try {
|
|
27
|
+
const result = await wallet.getPublicKey(args);
|
|
28
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
29
|
+
} catch (err: unknown) {
|
|
30
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
31
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
}
|
package/tools/wallet/tools.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
createSignatureArgsSchema,
|
|
32
32
|
type emptyArgsSchema,
|
|
33
33
|
type getAddressArgsSchema,
|
|
34
|
-
getPublicKeyArgsSchema,
|
|
34
|
+
type getPublicKeyArgsSchema,
|
|
35
35
|
type purchaseListingArgsSchema,
|
|
36
36
|
type sendToAddressArgsSchema,
|
|
37
37
|
verifySignatureArgsSchema,
|
|
@@ -42,9 +42,13 @@ import { Utils, type WalletProtocol } from "@bsv/sdk";
|
|
|
42
42
|
import { registerCreateOrdinalsTool } from "./createOrdinals";
|
|
43
43
|
import type { createOrdinalsArgsSchema } from "./createOrdinals";
|
|
44
44
|
import { registerGetAddressTool } from "./getAddress";
|
|
45
|
+
import { registerGetPublicKeyTool } from "./getPublicKey";
|
|
45
46
|
import { registerPurchaseListingTool } from "./purchaseListing";
|
|
46
47
|
import { registerSendToAddressTool } from "./sendToAddress";
|
|
47
48
|
import { registerTransferOrdTokenTool } from "./transferOrdToken";
|
|
49
|
+
|
|
50
|
+
import type { a2bPublishArgsSchema } from "./a2bPublishAgent";
|
|
51
|
+
import { registerA2bPublishMcpTool } from "./a2bPublishMcp";
|
|
48
52
|
import type { transferOrdTokenArgsSchema } from "./transferOrdToken";
|
|
49
53
|
|
|
50
54
|
// Define mapping from tool names to argument schemas
|
|
@@ -77,6 +81,7 @@ type ToolArgSchemas = {
|
|
|
77
81
|
wallet_sendToAddress: typeof sendToAddressArgsSchema;
|
|
78
82
|
wallet_purchaseListing: typeof purchaseListingArgsSchema;
|
|
79
83
|
wallet_transferOrdToken: typeof transferOrdTokenArgsSchema;
|
|
84
|
+
wallet_a2bPublish: typeof a2bPublishArgsSchema;
|
|
80
85
|
wallet_createOrdinals: typeof createOrdinalsArgsSchema;
|
|
81
86
|
};
|
|
82
87
|
|
|
@@ -115,34 +120,24 @@ export function registerWalletTools(
|
|
|
115
120
|
// Register the wallet_getAddress tool
|
|
116
121
|
registerGetAddressTool(server);
|
|
117
122
|
|
|
123
|
+
// Register the wallet_getPublicKey tool
|
|
124
|
+
registerGetPublicKeyTool(server, wallet);
|
|
125
|
+
|
|
118
126
|
// Register the wallet_purchaseListing tool
|
|
119
127
|
registerPurchaseListingTool(server, wallet);
|
|
120
128
|
|
|
121
129
|
// Register the wallet_transferOrdToken tool
|
|
122
130
|
registerTransferOrdTokenTool(server, wallet);
|
|
123
131
|
|
|
132
|
+
// Register the wallet_a2bPublishAgent tool
|
|
133
|
+
// registerA2bPublishAgentTool(server, wallet);
|
|
134
|
+
|
|
135
|
+
// Register the wallet_a2bPublishMcp tool
|
|
136
|
+
registerA2bPublishMcpTool(server, wallet);
|
|
137
|
+
|
|
124
138
|
// Register only the minimal public-facing tools
|
|
125
139
|
// wallet_createAction, wallet_signAction and wallet_getHeight have been removed
|
|
126
140
|
|
|
127
|
-
// Register wallet_getPublicKey
|
|
128
|
-
registerTool(
|
|
129
|
-
"wallet_getPublicKey",
|
|
130
|
-
"Retrieves the current wallet's public key. This public key can be used for cryptographic operations like signature verification or encryption.",
|
|
131
|
-
{ args: getPublicKeyArgsSchema },
|
|
132
|
-
async (
|
|
133
|
-
{ args }: { args: z.infer<typeof getPublicKeyArgsSchema> },
|
|
134
|
-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
|
|
135
|
-
) => {
|
|
136
|
-
try {
|
|
137
|
-
const result = await wallet.getPublicKey(args);
|
|
138
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
139
|
-
} catch (err: unknown) {
|
|
140
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
141
|
-
return { content: [{ type: "text", text: msg }], isError: true };
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
);
|
|
145
|
-
|
|
146
141
|
// Register wallet_createSignature
|
|
147
142
|
registerTool(
|
|
148
143
|
"wallet_createSignature",
|