bsv-mcp 0.0.20 → 0.0.22
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 +100 -98
- package/index.ts +28 -20
- package/package.json +5 -4
- package/tools/index.ts +10 -14
- package/tools/mnee/getBalance.ts +56 -0
- package/tools/mnee/index.ts +21 -0
- package/tools/mnee/parseTx.ts +45 -0
- package/tools/mnee/sendMnee.ts +97 -0
- package/tools/wallet/tools.ts +0 -6
- package/tools/wallet/{transferOrdToken/index.ts → transferOrdToken.ts} +1 -1
- package/tools/a2b/call.ts +0 -32
- package/tools/a2b/discover.ts +0 -30
- package/tools/wallet/a2bPublish.ts +0 -150
package/README.md
CHANGED
|
@@ -8,31 +8,6 @@
|
|
|
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
|
|
12
|
-
|
|
13
|
-
To install dependencies:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
bun install
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## External Dependencies
|
|
20
|
-
|
|
21
|
-
For full functionality of all tools, the following additional dependencies are installed:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
# For ordinal listing purchase functionality
|
|
25
|
-
js-1sat-ord
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Running the Server
|
|
29
|
-
|
|
30
|
-
Start the MCP server:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
bun run index.ts
|
|
34
|
-
```
|
|
35
|
-
|
|
36
11
|
## Connecting to MCP Clients
|
|
37
12
|
|
|
38
13
|
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.
|
|
@@ -75,28 +50,30 @@ To connect this server to Claude for Desktop:
|
|
|
75
50
|
1. Ensure you have [Claude for Desktop](https://claude.ai/desktop) installed and updated to the latest version
|
|
76
51
|
2. Clone this repository and run `bun install` in the project directory
|
|
77
52
|
3. Open your Claude for Desktop configuration file:
|
|
53
|
+
|
|
78
54
|
```bash
|
|
79
55
|
# macOS/Linux
|
|
80
|
-
code ~/Library/Application Support/Claude/claude_desktop_config.json
|
|
81
|
-
|
|
56
|
+
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
|
57
|
+
|
|
82
58
|
# Windows
|
|
83
59
|
code %APPDATA%\Claude\claude_desktop_config.json
|
|
84
60
|
```
|
|
61
|
+
|
|
85
62
|
4. Add the BSV MCP server to your configuration (create the file if it doesn't exist):
|
|
86
63
|
```json
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
64
|
+
{
|
|
65
|
+
"mcpServers": {
|
|
66
|
+
"Bitcoin SV": {
|
|
67
|
+
"command": "bunx",
|
|
68
|
+
"args": [
|
|
69
|
+
"bsv-mcp@latest"
|
|
70
|
+
],
|
|
71
|
+
"env": {
|
|
72
|
+
"PRIVATE_KEY_WIF": "<your_private_key_wif>"
|
|
73
|
+
}
|
|
96
74
|
}
|
|
97
75
|
}
|
|
98
76
|
}
|
|
99
|
-
}
|
|
100
77
|
```
|
|
101
78
|
5. Replace `<your_private_key_wif>` with your actual private key WIF
|
|
102
79
|
6. Save the file and restart Claude for Desktop
|
|
@@ -110,61 +87,76 @@ The toolkit is organized into several categories:
|
|
|
110
87
|
|
|
111
88
|
Wallet tools provide core BSV wallet functionality:
|
|
112
89
|
|
|
113
|
-
| Tool Name
|
|
114
|
-
|
|
115
|
-
| `wallet_getPublicKey`
|
|
116
|
-
| `wallet_createSignature` | Creates a cryptographic signature for the provided data
|
|
117
|
-
| `wallet_verifySignature` | Verifies a cryptographic signature against the provided data
|
|
118
|
-
| `wallet_encryption`
|
|
119
|
-
| `wallet_getAddress`
|
|
120
|
-
| `wallet_sendToAddress`
|
|
121
|
-
| `wallet_purchaseListing` | Purchases NFTs or BSV-20/BSV-21 tokens from marketplace listings
|
|
122
|
-
| `wallet_createOrdinals`
|
|
90
|
+
| Tool Name | Description | Example Output |
|
|
91
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `wallet_getPublicKey` | Retrieves a public key for a specified protocol and key ID | `{"publicKey":"032d0c73eb9270e9e009fd1f9dd77e19cf764fbad5f799560c4e8fd414e40d6fc2"}` |
|
|
93
|
+
| `wallet_createSignature` | Creates a cryptographic signature for the provided data | `{"signature":[144,124,85,193,226,45,140,249,9,177,11,167,33,215,209,38,...]}` |
|
|
94
|
+
| `wallet_verifySignature` | Verifies a cryptographic signature against the provided data | `{"isValid":true}` |
|
|
95
|
+
| `wallet_encryption` | Combined tool for encrypting and decrypting data using the wallet's cryptographic keys.<br><br>**Examples:**<br>1. Encrypt text: `"Encrypt this message: Hello World"`<br>2. Decrypt data: `"Decrypt this data that was previously encrypted for me"` | Encrypt: `{"ciphertext":[89,32,155,38,125,22,49,226,26,...]}` <br> Decrypt: `{"plaintext":"hello world"}` |
|
|
96
|
+
| `wallet_getAddress` | Returns a BSV address for the current wallet or a derived path | `{"address":"1ExampleBsvAddressXXXXXXXXXXXXXXXXX","status":"ok"}` |
|
|
97
|
+
| `wallet_sendToAddress` | Sends BSV to a specified address (supports BSV or USD amounts) | `{"status":"success","txid":"a1b2c3d4e5f6...","satoshis":1000000}` |
|
|
98
|
+
| `wallet_purchaseListing` | Purchases NFTs or BSV-20/BSV-21 tokens from marketplace listings | `{"status":"success","txid":"a1b2c3d4e5f6...","type":"nft","origin":"abcdef123456..."}` |
|
|
99
|
+
| `wallet_createOrdinals` | Creates and inscribes ordinals on the BSV blockchain | `{"txid":"a1b2c3d4e5f6...","inscriptionAddress":"1ExampleAddress...","contentType":"image/png"}` |
|
|
123
100
|
|
|
124
101
|
### BSV Tools
|
|
125
102
|
|
|
126
103
|
Tools for interacting with the BSV blockchain and network:
|
|
127
104
|
|
|
128
|
-
| Tool Name
|
|
129
|
-
|
|
130
|
-
| `bsv_getPrice`
|
|
131
|
-
| `bsv_decodeTransaction` | Decodes a BSV transaction and returns detailed information
|
|
132
|
-
| `bsv_explore`
|
|
105
|
+
| Tool Name | Description | Example Output |
|
|
106
|
+
| ----------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
107
|
+
| `bsv_getPrice` | Gets the current BSV price from an exchange API | `Current BSV price: $38.75 USD` |
|
|
108
|
+
| `bsv_decodeTransaction` | Decodes a BSV transaction and returns detailed information | `{"txid":"a1b2c3d4e5f6...","version":1,"locktime":0,"size":225,"inputs":[...],"outputs":[...]}` |
|
|
109
|
+
| `bsv_explore` | Comprehensive blockchain explorer tool accessing WhatsOnChain API endpoints | `{"chain_info":{"chain":"main","blocks":826458,"headers":826458,"bestblockhash":"0000000000..."}}` |
|
|
133
110
|
|
|
134
111
|
### Ordinals Tools
|
|
135
112
|
|
|
136
113
|
Tools for working with ordinals (NFTs) on BSV:
|
|
137
114
|
|
|
138
|
-
| Tool Name
|
|
139
|
-
|
|
140
|
-
| `ordinals_getInscription`
|
|
141
|
-
| `ordinals_searchInscriptions`
|
|
142
|
-
| `ordinals_marketListings`
|
|
143
|
-
| `ordinals_marketSales`
|
|
144
|
-
| `ordinals_getTokenByIdOrTicker` | Retrieves details about a specific BSV20 token by ID
|
|
115
|
+
| Tool Name | Description | Example Output |
|
|
116
|
+
| ------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| `ordinals_getInscription` | Retrieves detailed information about a specific inscription | `{"id":"a1b2c3d4e5f6...","origin":"a1b2c3d4e5f6...","contentType":"image/png","content":"iVBORw0KGgoAAA..."}` |
|
|
118
|
+
| `ordinals_searchInscriptions` | Searches for inscriptions based on various criteria | `{"results":[{"id":"a1b2c3...","contentType":"image/png","owner":"1Example..."},...]}` |
|
|
119
|
+
| `ordinals_marketListings` | Retrieves market listings for NFTs, BSV-20, and BSV-21 tokens with unified interface | `{"results":[{"txid":"a1b2c3...","price":9990000,"tick":"PEPE","listing":true},...]}` |
|
|
120
|
+
| `ordinals_marketSales` | Gets information about BSV-20 and BSV-21 token market sales | `{"results":[{"txid":"a1b2c3...","price":34710050,"tick":"$BTC","sale":true},...]}` |
|
|
121
|
+
| `ordinals_getTokenByIdOrTicker` | Retrieves details about a specific BSV20 token by ID | `{"tick":"PEPE","max":"21000000","lim":"1000","dec":"2"}` |
|
|
145
122
|
|
|
146
123
|
### Utility Tools
|
|
147
124
|
|
|
148
125
|
General-purpose utility functions:
|
|
149
126
|
|
|
150
|
-
| Tool Name
|
|
151
|
-
|
|
127
|
+
| Tool Name | Description | Example Output |
|
|
128
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
|
|
152
129
|
| `utils_convertData` | Converts data between different encoding formats (utf8, hex, base64, binary).<br><br>**Parameters:**<br>- `data` (required): The string to convert<br>- `from` (required): Source encoding format (utf8, hex, base64, or binary)<br>- `to` (required): Target encoding format (utf8, hex, base64, or binary)<br><br>**Examples:**<br>- UTF-8 to hex: `{"data": "hello world", "from": "utf8", "to": "hex"}` → `68656c6c6f20776f726c64`<br>- UTF-8 to base64: `{"data": "Hello World", "from": "utf8", "to": "base64"}` → `SGVsbG8gV29ybGQ=`<br>- base64 to UTF-8: `{"data": "SGVsbG8gV29ybGQ=", "from": "base64", "to": "utf8"}` → `Hello World`<br>- hex to base64: `{"data": "68656c6c6f20776f726c64", "from": "hex", "to": "base64"}` → `aGVsbG8gd29ybGQ=`<br><br>**Notes:**<br>- All parameters are required<br>- The tool returns the converted data as a string<br>- For binary conversion, data is represented as an array of byte values | `"SGVsbG8gV29ybGQ="` (UTF-8 "Hello World" converted to base64) |
|
|
153
130
|
|
|
131
|
+
### MNEE Tools
|
|
132
|
+
|
|
133
|
+
Tools for working with MNEE tokens:
|
|
134
|
+
|
|
135
|
+
| Tool Name | Description | Example Output |
|
|
136
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
137
|
+
| `mnee_getBalance` | Retrieves the current MNEE token balance for the wallet | `{"balance": {"amount": 2900, "decimalAmount": 0.029}}` |
|
|
138
|
+
| `mnee_sendMnee` | Send MNEE tokens to a specified address. Supports both MNEE and USD amounts | `{"success": true, "txid": "d1ce853934964e6c1fe9f44c918a824f175c6ab466b966f49ebc0682a8318895", "rawtx": "0100000002a0be40d8942015f1...", "mneeAmount": 0.01, "usdAmount": "$0.01", "recipient": "15mNxEkyKJXPD8amic6oLUjS45zBKQQoLu"}` |
|
|
139
|
+
| `mnee_parseTx` | Parse an MNEE transaction to get detailed information about its operations and amounts. All amounts are in atomic units with 5 decimal precision (e.g. 1000 atomic units = 0.01 MNEE) | `{"txid": "d1ce853934964e6c1fe9f44c918a824f175c6ab466b966f49ebc0682a8318895", "environment": "production", "type": "transfer", "inputs": [{"address": "18izL7Wtm2fx3ALoRY3MkY2VFSMjArP62D", "amount": 2900}], "outputs": [{"address": "15mNxEkyKJXPD8amic6oLUjS45zBKQQoLu", "amount": 1000}, {"address": "19Vq2TV8aVhFNLQkhDMdnEQ7zT96x6F3PK", "amount": 100}, {"address": "18izL7Wtm2fx3ALoRY3MkY2VFSMjArP62D", "amount": 1800}]}` |
|
|
140
|
+
|
|
154
141
|
## Using the Tools with MCP
|
|
155
142
|
|
|
156
143
|
Once connected, you can use natural language to interact with Bitcoin SV through your AI assistant. Here are some example prompts:
|
|
157
144
|
|
|
158
145
|
### Wallet Operations
|
|
146
|
+
|
|
159
147
|
- "Get my Bitcoin SV address"
|
|
160
148
|
- "Send 0.01 BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
|
|
161
149
|
- "Send $5 USD worth of BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
|
|
150
|
+
- "Send 0.01 MNEE to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
|
|
151
|
+
- "Check my MNEE balance"
|
|
152
|
+
- "Parse this MNEE transaction: txid"
|
|
162
153
|
- "Encrypt this message using my wallet's keys"
|
|
163
154
|
- "Decrypt this data that was previously encrypted for me"
|
|
164
155
|
- "Purchase this NFT listing: txid_vout"
|
|
165
156
|
- "Purchase this BSV-20 token listing: txid_vout"
|
|
166
157
|
|
|
167
158
|
### Ordinals (NFTs)
|
|
159
|
+
|
|
168
160
|
- "Show me information about the NFT with outpoint 6a89047af2cfac96da17d51ae8eb62c5f1d982be2bc4ba0d0cd2084b7ffed325_0"
|
|
169
161
|
- "Search for Pixel Zoide NFTs"
|
|
170
162
|
- "Show me the current marketplace listings for BSV NFTs"
|
|
@@ -172,6 +164,7 @@ Once connected, you can use natural language to interact with Bitcoin SV through
|
|
|
172
164
|
- "Get recent BSV-20 token sales"
|
|
173
165
|
|
|
174
166
|
### Blockchain Operations
|
|
167
|
+
|
|
175
168
|
- "What is the current BSV price?"
|
|
176
169
|
- "Decode this BSV transaction: (transaction hex or ID)"
|
|
177
170
|
- "Get the latest Bitcoin SV chain information"
|
|
@@ -181,6 +174,7 @@ Once connected, you can use natural language to interact with Bitcoin SV through
|
|
|
181
174
|
- "Get details for transaction with hash a1b2c3d4e5f6..."
|
|
182
175
|
|
|
183
176
|
### Data Conversion
|
|
177
|
+
|
|
184
178
|
- "Convert 'Hello World' from UTF-8 to hex format"
|
|
185
179
|
|
|
186
180
|
## How MCP Works
|
|
@@ -203,6 +197,7 @@ If you're having issues connecting to the server:
|
|
|
203
197
|
4. Look for error messages in the client's console output
|
|
204
198
|
|
|
205
199
|
For Claude for Desktop, check the logs at:
|
|
200
|
+
|
|
206
201
|
```bash
|
|
207
202
|
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
|
|
208
203
|
```
|
|
@@ -226,47 +221,53 @@ The `bsv_explore` tool provides comprehensive access to the Bitcoin SV blockchai
|
|
|
226
221
|
The tool supports the following endpoint categories and specific endpoints:
|
|
227
222
|
|
|
228
223
|
#### Chain Data
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
224
|
+
|
|
225
|
+
| Endpoint | Description | Required Parameters | Example Response |
|
|
226
|
+
| -------------------- | ----------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------- |
|
|
227
|
+
| `chain_info` | Network statistics, difficulty, and chain work | None | `{"chain":"main","blocks":826458,"headers":826458,"bestblockhash":"000000000000..."}` |
|
|
228
|
+
| `chain_tips` | Current chain tips including heights and states | None | `[{"height":826458,"hash":"000000000000...","branchlen":0,"status":"active"}]` |
|
|
229
|
+
| `circulating_supply` | Current BSV circulating supply | None | `{"bsv":21000000}` |
|
|
230
|
+
| `peer_info` | Connected peer statistics | None | `[{"addr":"1.2.3.4:8333","services":"000000000000...","lastsend":1621234567}]` |
|
|
235
231
|
|
|
236
232
|
#### Block Data
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
|
240
|
-
| `
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
233
|
+
|
|
234
|
+
| Endpoint | Description | Required Parameters | Example Response |
|
|
235
|
+
| --------------------- | --------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------- |
|
|
236
|
+
| `block_by_hash` | Complete block data via hash | `blockHash` | `{"hash":"000000000000...","confirmations":1000,"size":1000000,...}` |
|
|
237
|
+
| `block_by_height` | Complete block data via height | `blockHeight` | `{"hash":"000000000000...","confirmations":1000,"size":1000000,...}` |
|
|
238
|
+
| `tag_count_by_height` | Stats on tag count for a specific block | `blockHeight` | `{"tags":{"amp":3,"bitkey":5,"metanet":12,"planaria":7,"b":120}}` |
|
|
239
|
+
| `block_headers` | Retrieves the last 10 block headers | None | `[{"hash":"000000000000...","height":826458,"version":536870912,...},...]` |
|
|
240
|
+
| `block_pages` | Retrieves pages of transaction IDs for large blocks | `blockHash`, optional: `pageNumber` | `["tx1hash","tx2hash","tx3hash",...]` |
|
|
244
241
|
|
|
245
242
|
#### Stats Data
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
|
249
|
-
| `
|
|
250
|
-
| `
|
|
243
|
+
|
|
244
|
+
| Endpoint | Description | Required Parameters | Example Response |
|
|
245
|
+
| ----------------------- | ----------------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------- |
|
|
246
|
+
| `block_stats_by_height` | Block statistics for a specific height | `blockHeight` | `{"size":123456,"txCount":512,"outputTotal":54.12345678,"outputTotalUsd":2345.67,...}` |
|
|
247
|
+
| `block_miner_stats` | Block mining statistics for a time period | optional: `days` (default 7) | `{"blocks":{"miner1":412,"miner2":208,...},"total":1008}` |
|
|
248
|
+
| `miner_summary_stats` | Summary of mining statistics | optional: `days` (default 7) | `{"totalBlocks":1008,"totalFees":1.23456789,"totalFeesUsd":53.67,...}` |
|
|
251
249
|
|
|
252
250
|
#### Transaction Data
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
251
|
+
|
|
252
|
+
| Endpoint | Description | Required Parameters | Example Response |
|
|
253
|
+
| ----------------- | --------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
|
254
|
+
| `tx_by_hash` | Detailed transaction data | `txHash` | `{"txid":"a1b2c3d4e5f6...","version":1,"locktime":0,"size":225,...}` |
|
|
255
|
+
| `tx_raw` | Raw transaction hex data | `txHash` | `"01000000012345abcdef..."` |
|
|
256
|
+
| `tx_receipt` | Transaction receipt | `txHash` | `{"blockHash":"000000000000...","blockHeight":800000,"confirmations":26458}` |
|
|
257
|
+
| `bulk_tx_details` | Retrieve multiple transactions in one request | `txids` (array) | `[{"txid":"a1b2c3d4e5f6...","version":1,...}, {"txid":"b2c3d4e5f6a7...","version":1,...}]` |
|
|
259
258
|
|
|
260
259
|
#### Address Data
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
|
264
|
-
| `
|
|
260
|
+
|
|
261
|
+
| Endpoint | Description | Required Parameters | Example Response |
|
|
262
|
+
| ----------------- | ------------------------------- | ---------------------------- | --------------------------------------------------------------- |
|
|
263
|
+
| `address_history` | Transaction history for address | `address`, optional: `limit` | `[{"tx_hash":"a1b2c3d4e5f6...","height":800000},...]` |
|
|
264
|
+
| `address_utxos` | Unspent outputs for address | `address` | `[{"tx_hash":"a1b2c3d4e5f6...","tx_pos":0,"value":100000},...]` |
|
|
265
265
|
|
|
266
266
|
#### Network
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
|
267
|
+
|
|
268
|
+
| Endpoint | Description | Required Parameters | Example Response |
|
|
269
|
+
| -------- | ---------------- | ------------------- | --------------------- |
|
|
270
|
+
| `health` | API health check | None | `{"status":"synced"}` |
|
|
270
271
|
|
|
271
272
|
### Usage Examples
|
|
272
273
|
|
|
@@ -305,16 +306,17 @@ Under the hood, the tool accepts parameters to specify which data to retrieve:
|
|
|
305
306
|
### Network Options
|
|
306
307
|
|
|
307
308
|
The tool supports both mainnet and testnet:
|
|
309
|
+
|
|
308
310
|
- `main`: Bitcoin SV mainnet (default)
|
|
309
311
|
- `test`: Bitcoin SV testnet
|
|
310
312
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
This project was created using `bun init` in bun v1.2.9. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
|
313
|
+
# Development
|
|
314
314
|
|
|
315
|
-
|
|
315
|
+
## Running the Server
|
|
316
316
|
|
|
317
|
-
|
|
317
|
+
```bash
|
|
318
|
+
bun run index.ts
|
|
319
|
+
```
|
|
318
320
|
|
|
319
321
|
### Running Tests
|
|
320
322
|
|
|
@@ -324,4 +326,4 @@ bun test
|
|
|
324
326
|
|
|
325
327
|
## License
|
|
326
328
|
|
|
327
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
329
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
package/index.ts
CHANGED
|
@@ -11,25 +11,33 @@ import { Wallet } from "./tools/wallet/wallet";
|
|
|
11
11
|
* Exits the process with an error message if validation fails
|
|
12
12
|
*/
|
|
13
13
|
function validatePrivateKey(): PrivateKey {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
14
|
+
const privateKeyWif = process.env.PRIVATE_KEY_WIF;
|
|
15
|
+
|
|
16
|
+
// Check if private key is set
|
|
17
|
+
if (!privateKeyWif) {
|
|
18
|
+
console.error(
|
|
19
|
+
"\x1b[31mError: PRIVATE_KEY_WIF environment variable is not set\x1b[0m"
|
|
20
|
+
);
|
|
21
|
+
console.error(
|
|
22
|
+
"Please set this variable with a valid Bitcoin SV private key in WIF format"
|
|
23
|
+
);
|
|
24
|
+
console.error(
|
|
25
|
+
"Example: PRIVATE_KEY_WIF=your_private_key_wif bun run index.ts"
|
|
26
|
+
);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Validate the private key format
|
|
31
|
+
try {
|
|
32
|
+
return PrivateKey.fromWif(privateKeyWif);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error("\x1b[31mError: Invalid private key format\x1b[0m");
|
|
35
|
+
console.error(
|
|
36
|
+
"The PRIVATE_KEY_WIF provided is not a valid Bitcoin SV private key in WIF format"
|
|
37
|
+
);
|
|
38
|
+
console.error("Please check your key and try again");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
// Validate private key early before starting the server
|
|
@@ -37,7 +45,7 @@ const privKey = validatePrivateKey();
|
|
|
37
45
|
|
|
38
46
|
const server = new McpServer({
|
|
39
47
|
name: "Bitcoin SV MCP Server",
|
|
40
|
-
version: "0.0.
|
|
48
|
+
version: "0.0.22",
|
|
41
49
|
});
|
|
42
50
|
|
|
43
51
|
// Initialize wallet with the validated private key
|
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.22",
|
|
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",
|
|
@@ -42,11 +42,12 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@bsv/sdk": "^1.4.19",
|
|
45
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.10.1",
|
|
46
46
|
"@types/node": "^22.14.1",
|
|
47
|
-
"js-1sat-ord": "^0.1.
|
|
47
|
+
"js-1sat-ord": "^0.1.81",
|
|
48
|
+
"mnee": "^2.0.0",
|
|
48
49
|
"satoshi-token": "^0.0.4",
|
|
49
|
-
"zod": "^3.24.
|
|
50
|
+
"zod": "^3.24.3"
|
|
50
51
|
},
|
|
51
52
|
"scripts": {
|
|
52
53
|
"lint": "biome check .",
|
package/tools/index.ts
CHANGED
|
@@ -2,28 +2,24 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { registerBsvTools } from "./bsv";
|
|
3
3
|
import { registerOrdinalsTools } from "./ordinals";
|
|
4
4
|
import { registerUtilsTools } from "./utils";
|
|
5
|
-
import {
|
|
6
|
-
import { registerA2aCallTool } from "./a2b/call";
|
|
5
|
+
import { registerMneeTools } from "./mnee";
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Register all tools with the MCP server
|
|
10
9
|
* @param server The MCP server instance
|
|
11
10
|
*/
|
|
12
11
|
export function registerAllTools(server: McpServer): void {
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
// Register BSV-related tools
|
|
13
|
+
registerBsvTools(server);
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
// Register Ordinals-related tools
|
|
16
|
+
registerOrdinalsTools(server);
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// Register utility tools
|
|
19
|
+
registerUtilsTools(server);
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
// Register MNEE tools
|
|
22
|
+
registerMneeTools(server);
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
registerA2aCallTool(server);
|
|
27
|
-
|
|
28
|
-
// Add more tool categories as needed
|
|
24
|
+
// Add more tool categories as needed
|
|
29
25
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
5
|
+
import type { MneeInterface } from "mnee";
|
|
6
|
+
|
|
7
|
+
export const getBalanceArgsSchema = z.object({});
|
|
8
|
+
|
|
9
|
+
export type GetBalanceArgs = z.infer<typeof getBalanceArgsSchema>;
|
|
10
|
+
|
|
11
|
+
export function registerGetBalanceTool(
|
|
12
|
+
server: McpServer,
|
|
13
|
+
mnee: MneeInterface
|
|
14
|
+
): void {
|
|
15
|
+
server.tool(
|
|
16
|
+
"mnee_getBalance",
|
|
17
|
+
"Retrieves the current MNEE token balance for the wallet. Returns the balance in MNEE tokens.",
|
|
18
|
+
{
|
|
19
|
+
args: getBalanceArgsSchema,
|
|
20
|
+
},
|
|
21
|
+
async (
|
|
22
|
+
{ args }: { args: GetBalanceArgs },
|
|
23
|
+
extra: RequestHandlerExtra<any, any>
|
|
24
|
+
) => {
|
|
25
|
+
try {
|
|
26
|
+
// Get private key from wallet
|
|
27
|
+
const privateKey = PrivateKey.fromWif(process.env.PRIVATE_KEY_WIF!);
|
|
28
|
+
if (!privateKey) {
|
|
29
|
+
throw new Error("No private key available");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const address = privateKey.toAddress().toString();
|
|
33
|
+
const balance = await mnee.balance(address);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: JSON.stringify({ balance }, null, 2),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: error instanceof Error ? error.message : String(error),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
isError: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { registerGetBalanceTool } from "./getBalance";
|
|
3
|
+
import Mnee from "mnee";
|
|
4
|
+
import { registerSendMneeTool } from "./sendMnee";
|
|
5
|
+
import { registerParseTxTool } from "./parseTx";
|
|
6
|
+
|
|
7
|
+
const mnee = new Mnee({
|
|
8
|
+
environment: "production",
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Register all MNEE tools with the MCP server
|
|
12
|
+
* @param server The MCP server instance
|
|
13
|
+
*/
|
|
14
|
+
export function registerMneeTools(server: McpServer): void {
|
|
15
|
+
// Register MNEE-related tools
|
|
16
|
+
registerGetBalanceTool(server, mnee);
|
|
17
|
+
|
|
18
|
+
registerSendMneeTool(server, mnee);
|
|
19
|
+
|
|
20
|
+
registerParseTxTool(server, mnee);
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import type { MneeInterface, ParseTxResponse } from "mnee";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Schema for the parseTx tool arguments.
|
|
9
|
+
*/
|
|
10
|
+
export const parseTxArgsSchema = z.object({
|
|
11
|
+
txid: z.string().describe("Transaction ID to parse"),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export type ParseTxArgs = z.infer<typeof parseTxArgsSchema>;
|
|
15
|
+
|
|
16
|
+
export function registerParseTxTool(
|
|
17
|
+
server: McpServer,
|
|
18
|
+
mnee: MneeInterface
|
|
19
|
+
): void {
|
|
20
|
+
server.tool(
|
|
21
|
+
"mnee_parseTx",
|
|
22
|
+
"Parse an MNEE transaction to get detailed information about its operations and amounts. All amounts are in atomic units with 5 decimal precision (e.g. 1000 atomic units = 0.01 MNEE).",
|
|
23
|
+
{ args: parseTxArgsSchema },
|
|
24
|
+
async (
|
|
25
|
+
{ args }: { args: ParseTxArgs },
|
|
26
|
+
extra: RequestHandlerExtra<any, any>
|
|
27
|
+
): Promise<CallToolResult> => {
|
|
28
|
+
try {
|
|
29
|
+
const result: ParseTxResponse = await mnee.parseTx(args.txid);
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
content: [
|
|
33
|
+
{
|
|
34
|
+
type: "text",
|
|
35
|
+
text: JSON.stringify(result, null, 2),
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
41
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
3
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import type Mnee from "mnee";
|
|
5
|
+
import type { SendMNEE, TransferResponse } from "mnee";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Schema for the sendMnee tool arguments.
|
|
10
|
+
*/
|
|
11
|
+
export const sendMneeArgsSchema = z.object({
|
|
12
|
+
address: z.string().describe("The recipient's address"),
|
|
13
|
+
amount: z.number().describe("Amount to send"),
|
|
14
|
+
currency: z
|
|
15
|
+
.enum(["MNEE", "USD"])
|
|
16
|
+
.default("MNEE")
|
|
17
|
+
.describe("Currency of the amount (MNEE or USD)"),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export type SendMneeArgs = z.infer<typeof sendMneeArgsSchema>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Format a number as USD
|
|
24
|
+
*/
|
|
25
|
+
function formatUSD(amount: number): string {
|
|
26
|
+
return new Intl.NumberFormat("en-US", {
|
|
27
|
+
style: "currency",
|
|
28
|
+
currency: "USD",
|
|
29
|
+
minimumFractionDigits: 2,
|
|
30
|
+
maximumFractionDigits: 2,
|
|
31
|
+
}).format(amount);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Registers the mnee_sendMnee tool for sending MNEE tokens
|
|
36
|
+
*/
|
|
37
|
+
export function registerSendMneeTool(server: McpServer, mnee: Mnee): void {
|
|
38
|
+
server.tool(
|
|
39
|
+
"mnee_sendMnee",
|
|
40
|
+
"Send MNEE tokens to a specified address",
|
|
41
|
+
{ args: sendMneeArgsSchema },
|
|
42
|
+
async (
|
|
43
|
+
{ args }: { args: SendMneeArgs },
|
|
44
|
+
extra: RequestHandlerExtra<any, any>
|
|
45
|
+
): Promise<CallToolResult> => {
|
|
46
|
+
try {
|
|
47
|
+
// Since 1 MNEE = $1, the amount is the same in both currencies
|
|
48
|
+
const mneeAmount = args.amount;
|
|
49
|
+
|
|
50
|
+
const transferRequest: SendMNEE[] = [
|
|
51
|
+
{
|
|
52
|
+
address: args.address,
|
|
53
|
+
amount: mneeAmount,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// Get WIF from environment
|
|
58
|
+
const wif = process.env.PRIVATE_KEY_WIF;
|
|
59
|
+
if (!wif) {
|
|
60
|
+
throw new Error("PRIVATE_KEY_WIF environment variable is not set");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const result: TransferResponse = await mnee.transfer(
|
|
64
|
+
transferRequest,
|
|
65
|
+
wif
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (result.error) {
|
|
69
|
+
throw new Error(result.error);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: JSON.stringify(
|
|
77
|
+
{
|
|
78
|
+
success: true,
|
|
79
|
+
txid: result.txid,
|
|
80
|
+
rawtx: result.rawtx,
|
|
81
|
+
mneeAmount: mneeAmount,
|
|
82
|
+
usdAmount: formatUSD(mneeAmount),
|
|
83
|
+
recipient: args.address,
|
|
84
|
+
},
|
|
85
|
+
null,
|
|
86
|
+
2
|
|
87
|
+
),
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
93
|
+
return { content: [{ type: "text", text: msg }], isError: true };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
package/tools/wallet/tools.ts
CHANGED
|
@@ -40,9 +40,7 @@ import { registerGetAddressTool } from "./getAddress";
|
|
|
40
40
|
import { registerPurchaseListingTool } from "./purchaseListing";
|
|
41
41
|
import { registerSendToAddressTool } from "./sendToAddress";
|
|
42
42
|
import { registerTransferOrdTokenTool } from "./transferOrdToken";
|
|
43
|
-
import { registerA2bPublishTool } from "./a2bPublish";
|
|
44
43
|
import type { transferOrdTokenArgsSchema } from "./transferOrdToken";
|
|
45
|
-
import type { a2bPublishArgsSchema } from "./a2bPublish";
|
|
46
44
|
import { Utils, type WalletProtocol } from "@bsv/sdk";
|
|
47
45
|
|
|
48
46
|
// Define mapping from tool names to argument schemas
|
|
@@ -75,7 +73,6 @@ type ToolArgSchemas = {
|
|
|
75
73
|
wallet_sendToAddress: typeof sendToAddressArgsSchema;
|
|
76
74
|
wallet_purchaseListing: typeof purchaseListingArgsSchema;
|
|
77
75
|
wallet_transferOrdToken: typeof transferOrdTokenArgsSchema;
|
|
78
|
-
wallet_a2bPublish: typeof a2bPublishArgsSchema;
|
|
79
76
|
wallet_createOrdinals: typeof createOrdinalsArgsSchema;
|
|
80
77
|
};
|
|
81
78
|
|
|
@@ -120,9 +117,6 @@ export function registerWalletTools(
|
|
|
120
117
|
// Register the wallet_transferOrdToken tool
|
|
121
118
|
registerTransferOrdTokenTool(server, wallet);
|
|
122
119
|
|
|
123
|
-
// Register the wallet_a2bPublish tool
|
|
124
|
-
registerA2bPublishTool(server, wallet);
|
|
125
|
-
|
|
126
120
|
// Register only the minimal public-facing tools
|
|
127
121
|
// wallet_createAction, wallet_signAction and wallet_getHeight have been removed
|
|
128
122
|
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type Distribution,
|
|
15
15
|
type TokenChangeResult,
|
|
16
16
|
} from "js-1sat-ord";
|
|
17
|
-
import type { Wallet } from "
|
|
17
|
+
import type { Wallet } from "./wallet";
|
|
18
18
|
|
|
19
19
|
// Schema for BSV-20/BSV-21 token transfer arguments
|
|
20
20
|
export const transferOrdTokenArgsSchema = z.object({
|
package/tools/a2b/call.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
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.record(z.any()).optional().describe("Payload parameters for the A2A call"),
|
|
10
|
-
});
|
|
11
|
-
export type A2aCallArgs = z.infer<typeof a2aCallArgsSchema>;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Registers the a2a_call tool for agent-to-agent HTTP/SSE calls
|
|
15
|
-
*/
|
|
16
|
-
export function registerA2aCallTool(server: McpServer) {
|
|
17
|
-
server.tool(
|
|
18
|
-
"a2a_call",
|
|
19
|
-
"Invoke a remote agent's A2A endpoint via HTTP/SSE",
|
|
20
|
-
{ args: a2aCallArgsSchema },
|
|
21
|
-
async (
|
|
22
|
-
{ args }: { args: A2aCallArgs },
|
|
23
|
-
extra: RequestHandlerExtra
|
|
24
|
-
) => {
|
|
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
|
-
}
|
package/tools/a2b/discover.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
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 agent discovery parameters
|
|
6
|
-
export const a2bDiscoverArgsSchema = z.object({
|
|
7
|
-
query: z.string().describe("Agent name or capability to search for"),
|
|
8
|
-
});
|
|
9
|
-
export type A2bDiscoverArgs = z.infer<typeof a2bDiscoverArgsSchema>;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Registers the a2b_discover tool for on-chain agent discovery
|
|
13
|
-
*/
|
|
14
|
-
export function registerA2bDiscoverTool(server: McpServer) {
|
|
15
|
-
server.tool(
|
|
16
|
-
"a2b_discover",
|
|
17
|
-
"Search on-chain agent records by name or capability",
|
|
18
|
-
{ args: a2bDiscoverArgsSchema },
|
|
19
|
-
async (
|
|
20
|
-
{ args }: { args: A2bDiscoverArgs },
|
|
21
|
-
extra: RequestHandlerExtra
|
|
22
|
-
) => {
|
|
23
|
-
// TODO: implement on-chain lookup logic
|
|
24
|
-
return {
|
|
25
|
-
content: [{ type: "text", text: "Not implemented" }],
|
|
26
|
-
isError: true,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
);
|
|
30
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
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
|
-
import type { Wallet } from "./wallet";
|
|
5
|
-
import { createOrdinals } from "js-1sat-ord";
|
|
6
|
-
import type { Inscription, Destination, ChangeResult, PreMAP } from "js-1sat-ord";
|
|
7
|
-
import { Utils } from "@bsv/sdk";
|
|
8
|
-
const { toArray, toBase64 } = Utils;
|
|
9
|
-
|
|
10
|
-
// https://raw.githubusercontent.com/google/A2A/refs/heads/main/specification/json/a2a.json
|
|
11
|
-
|
|
12
|
-
// A2A AgentCard schema (per A2A spec)
|
|
13
|
-
const AgentCapabilitiesSchema = z.object({
|
|
14
|
-
streaming: z.boolean().default(false),
|
|
15
|
-
pushNotifications: z.boolean().default(false),
|
|
16
|
-
stateTransitionHistory: z.boolean().default(false),
|
|
17
|
-
});
|
|
18
|
-
const AgentSkillSchema = z.object({
|
|
19
|
-
id: z.string(),
|
|
20
|
-
name: z.string(),
|
|
21
|
-
description: z.string().nullable(),
|
|
22
|
-
tags: z.array(z.string()).nullable(),
|
|
23
|
-
examples: z.array(z.string()).nullable(),
|
|
24
|
-
inputModes: z.array(z.string()).nullable(),
|
|
25
|
-
outputModes: z.array(z.string()).nullable(),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Provider per A2A spec
|
|
29
|
-
const AgentProviderSchema = z.object({
|
|
30
|
-
organization: z.string(),
|
|
31
|
-
url: z.string().url().nullable().default(null),
|
|
32
|
-
}).nullable().default(null);
|
|
33
|
-
|
|
34
|
-
// Authentication per A2A spec
|
|
35
|
-
const AgentAuthenticationSchema = z.object({
|
|
36
|
-
schemes: z.array(z.string()),
|
|
37
|
-
credentials: z.string().nullable().default(null),
|
|
38
|
-
}).nullable().default(null);
|
|
39
|
-
|
|
40
|
-
export const AgentCardSchema = z.object({
|
|
41
|
-
name: z.string(),
|
|
42
|
-
description: z.string().nullable().default(null),
|
|
43
|
-
url: z.string().url(),
|
|
44
|
-
provider: AgentProviderSchema,
|
|
45
|
-
version: z.string(),
|
|
46
|
-
documentationUrl: z.string().url().nullable().default(null),
|
|
47
|
-
capabilities: AgentCapabilitiesSchema,
|
|
48
|
-
authentication: AgentAuthenticationSchema,
|
|
49
|
-
defaultInputModes: z.array(z.string()).default(["text"]),
|
|
50
|
-
defaultOutputModes: z.array(z.string()).default(["text"]),
|
|
51
|
-
skills: z.array(AgentSkillSchema),
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Schema for on-chain agent publish parameters
|
|
55
|
-
export const a2bPublishArgsSchema = z.object({
|
|
56
|
-
agentUrl: z.string().url().describe("Agent base URL (e.g. https://example.com)"),
|
|
57
|
-
agentName: z.string().describe("Human-friendly agent name"),
|
|
58
|
-
description: z.string().nullable().optional().describe("Optional agent description"),
|
|
59
|
-
providerOrganization: z.string().optional().describe("Optional provider organization name"),
|
|
60
|
-
providerUrl: z.string().url().optional().describe("Optional provider URL"),
|
|
61
|
-
version: z.string().optional().describe("Optional agent version"),
|
|
62
|
-
documentationUrl: z.string().url().nullable().optional().describe("Optional documentation URL"),
|
|
63
|
-
streaming: z.boolean().default(false).describe("Supports SSE (tasks/sendSubscribe)"),
|
|
64
|
-
pushNotifications: z.boolean().default(false).describe("Supports push notifications"),
|
|
65
|
-
stateTransitionHistory: z.boolean().default(false).describe("Supports state transition history"),
|
|
66
|
-
defaultInputModes: z.array(z.string()).default(["text"]).describe("Default input modes"),
|
|
67
|
-
defaultOutputModes: z.array(z.string()).default(["text"]).describe("Default output modes"),
|
|
68
|
-
skills: z.array(AgentSkillSchema).optional().default([]).describe("List of agent skills"),
|
|
69
|
-
destinationAddress: z.string().optional().describe("Optional target address for inscription"),
|
|
70
|
-
});
|
|
71
|
-
export type A2bPublishArgs = z.infer<typeof a2bPublishArgsSchema>;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Registers the wallet_a2bPublish tool for publishing an agent record on-chain
|
|
75
|
-
*/
|
|
76
|
-
export function registerA2bPublishTool(server: McpServer, wallet: Wallet) {
|
|
77
|
-
server.tool(
|
|
78
|
-
"wallet_a2bPublish",
|
|
79
|
-
"Publish an agent.json record on-chain via Ordinal inscription",
|
|
80
|
-
{ args: a2bPublishArgsSchema },
|
|
81
|
-
async (
|
|
82
|
-
{ args }: { args: A2bPublishArgs },
|
|
83
|
-
extra: RequestHandlerExtra
|
|
84
|
-
) => {
|
|
85
|
-
try {
|
|
86
|
-
const paymentPk = wallet.getPrivateKey();
|
|
87
|
-
if (!paymentPk) throw new Error("No private key available");
|
|
88
|
-
const { paymentUtxos } = await wallet.getUtxos();
|
|
89
|
-
if (!paymentUtxos?.length) throw new Error("No payment UTXOs available to fund inscription");
|
|
90
|
-
|
|
91
|
-
// Assemble AgentCard with defaults and user overrides
|
|
92
|
-
const agentCard = {
|
|
93
|
-
name: args.agentName,
|
|
94
|
-
description: args.description ?? null,
|
|
95
|
-
url: args.agentUrl,
|
|
96
|
-
provider:
|
|
97
|
-
args.providerOrganization && args.providerUrl
|
|
98
|
-
? { organization: args.providerOrganization, url: args.providerUrl }
|
|
99
|
-
: null,
|
|
100
|
-
version: args.version ?? "1.0.0",
|
|
101
|
-
documentationUrl: args.documentationUrl ?? null,
|
|
102
|
-
capabilities: {
|
|
103
|
-
streaming: args.streaming,
|
|
104
|
-
pushNotifications: args.pushNotifications,
|
|
105
|
-
stateTransitionHistory: args.stateTransitionHistory,
|
|
106
|
-
},
|
|
107
|
-
authentication: null,
|
|
108
|
-
defaultInputModes: args.defaultInputModes,
|
|
109
|
-
defaultOutputModes: args.defaultOutputModes,
|
|
110
|
-
skills: args.skills,
|
|
111
|
-
};
|
|
112
|
-
// Validate compliance
|
|
113
|
-
AgentCardSchema.parse(agentCard);
|
|
114
|
-
const fileContent = JSON.stringify(agentCard, null, 2);
|
|
115
|
-
// Base64 payload for inscription
|
|
116
|
-
const dataB64 = toBase64(toArray(fileContent));
|
|
117
|
-
const inscription: Inscription = { dataB64, contentType: "application/json" };
|
|
118
|
-
// Destination for the ordinal
|
|
119
|
-
const walletAddress = paymentPk.toAddress().toString();
|
|
120
|
-
const targetAddress = args.destinationAddress ?? walletAddress;
|
|
121
|
-
const destinations: Destination[] = [{ address: targetAddress, inscription }];
|
|
122
|
-
// Default MAP metadata: file path, content type, encoding
|
|
123
|
-
const metaData: PreMAP = { app: 'bsv-mcp', type: 'agent' };
|
|
124
|
-
|
|
125
|
-
// Inscribe the ordinal on-chain via js-1sat-ord
|
|
126
|
-
const result = await createOrdinals({ utxos: paymentUtxos, destinations, paymentPk, changeAddress: walletAddress, metaData });
|
|
127
|
-
const changeResult = result as ChangeResult;
|
|
128
|
-
await changeResult.tx.broadcast();
|
|
129
|
-
// Refresh UTXOs
|
|
130
|
-
try { await wallet.refreshUtxos(); } catch {}
|
|
131
|
-
// Return transaction details
|
|
132
|
-
return {
|
|
133
|
-
content: [{
|
|
134
|
-
type: "text",
|
|
135
|
-
text: JSON.stringify({
|
|
136
|
-
txid: changeResult.tx.id("hex"),
|
|
137
|
-
spentOutpoints: changeResult.spentOutpoints,
|
|
138
|
-
payChange: changeResult.payChange,
|
|
139
|
-
inscriptionAddress: targetAddress,
|
|
140
|
-
agentCard,
|
|
141
|
-
}),
|
|
142
|
-
}],
|
|
143
|
-
};
|
|
144
|
-
} catch (err: unknown) {
|
|
145
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
146
|
-
return { content: [{ type: "text", text: msg }], isError: true };
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
);
|
|
150
|
-
}
|