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 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. Clone this repository and run `bun install` in the project directory
23
- 3. Open Cursor and navigate to Settings → Extensions → Model Context Protocol
24
- 4. Click "Add a new global MCP server"
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
- "mcpServers": {
30
- "Bitcoin SV": {
31
- "command": "bunx",
32
- "args": [
33
- "bsv-mcp@latest"
34
- ],
35
- "env": {
36
- "PRIVATE_KEY_WIF": "<your_private_key_wif>"
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
- 6. Replace `<your_private_key_wif>` with your actual private key WIF (keep this secure!)
44
- 7. Click "Save"
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. Ensure you have [Claude for Desktop](https://claude.ai/desktop) installed and updated to the latest version
53
- 2. Clone this repository and run `bun install` in the project directory
54
- 3. Open your Claude for Desktop configuration file:
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
- 4. Add the BSV MCP server to your configuration (create the file if it doesn't exist):
108
+ 3. Add the BSV MCP server to your configuration:
65
109
  ```json
66
- {
67
- "mcpServers": {
68
- "Bitcoin SV": {
69
- "command": "bunx",
70
- "args": [
71
- "bsv-mcp@latest"
72
- ],
73
- "env": {
74
- "PRIVATE_KEY_WIF": "<your_private_key_wif>"
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
- 5. Replace `<your_private_key_wif>` with your actual private key WIF
81
- 6. Save the file and restart Claude for Desktop
82
- 7. The BSV tools will appear when you click the tools icon (hammer) in Claude for Desktop
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 connecting to the server:
338
+ If you're having issues with the BSV MCP server:
339
+
340
+ ### Connection Issues
195
341
 
196
- 1. Ensure the package dependencies are properly installed: `bun install`
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 MCP Server",
48
- version: "0.0.23",
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.23",
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.19",
45
- "@modelcontextprotocol/sdk": "^1.10.1",
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
+ }
@@ -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",