This is page 1 of 2. Use http://codebase.md/b-open-io/bsv-mcp?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .gitignore
├── biome.json
├── bun.lock
├── CHANGELOG.md
├── Dockerfile
├── docs
│ ├── a2b.md
│ └── images
│ └── mcp-config-example.png
├── index.ts
├── LICENSE
├── package.json
├── prompts
│ ├── bsvSdk
│ │ ├── auth.ts
│ │ ├── cryptography.ts
│ │ ├── index.ts
│ │ ├── overview.ts
│ │ ├── primitives.ts
│ │ ├── script.ts
│ │ ├── transaction.ts
│ │ └── wallet.ts
│ ├── index.ts
│ └── ordinals.ts
├── README.md
├── resources
│ ├── brcs.ts
│ ├── changelog.ts
│ ├── junglebus.ts
│ └── resources.ts
├── smithery.yaml
├── tools
│ ├── a2b
│ │ ├── call.ts
│ │ └── discover.ts
│ ├── bsv
│ │ ├── decodeTransaction.ts
│ │ ├── explore.ts
│ │ ├── getPrice.ts
│ │ ├── index.ts
│ │ └── token.ts
│ ├── constants.ts
│ ├── index.ts
│ ├── mnee
│ │ ├── getBalance.ts
│ │ ├── index.ts
│ │ ├── parseTx.ts
│ │ └── sendMnee.ts
│ ├── ordinals
│ │ ├── getInscription.ts
│ │ ├── getTokenByIdOrTicker.ts
│ │ ├── index.ts
│ │ ├── marketListings.ts
│ │ ├── marketSales.ts
│ │ └── searchInscriptions.ts
│ ├── utils
│ │ ├── conversion.ts
│ │ └── index.ts
│ └── wallet
│ ├── a2bPublishAgent.ts
│ ├── a2bPublishMcp.ts
│ ├── createOrdinals.ts
│ ├── fetchPaymentUtxos.ts
│ ├── getAddress.ts
│ ├── getPublicKey.ts
│ ├── purchaseListing.ts
│ ├── refreshUtxos.ts
│ ├── schemas.ts
│ ├── sendOrdinals.ts
│ ├── sendToAddress.ts
│ ├── tools.ts
│ ├── transferOrdToken.ts
│ └── wallet.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | # dependencies (bun install)
2 | node_modules
3 |
4 | # output
5 | out
6 | dist
7 | *.tgz
8 |
9 | # code coverage
10 | coverage
11 | *.lcov
12 |
13 | # logs
14 | logs
15 | _.log
16 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
17 |
18 | # dotenv environment variable files
19 | .env
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .env.local
24 |
25 | # caches
26 | .eslintcache
27 | .cache
28 | *.tsbuildinfo
29 |
30 | # IntelliJ based IDEs
31 | .idea
32 |
33 | # Finder (MacOS) folder config
34 | .DS_Store
35 | todo.md
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Bitcoin SV MCP Server
2 |
3 | [](https://smithery.ai/server/@b-open-io/bsv-mcp)
4 |
5 | > **⚠️ NOTICE: Experimental Work in Progress**
6 | > This project is in an early experimental stage. Features may change, and the API is not yet stable.
7 | > Contributions, feedback, and bug reports are welcome! Feel free to open issues or submit pull requests.
8 |
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 |
11 | ## Installation and Setup
12 |
13 | ### Use Bun (Optional but recommended)
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 as Bun is designed to be backward compatible with node.
16 |
17 | #### Installing Bun
18 |
19 | **macOS (using Homebrew):**
20 | ```bash
21 | brew install oven-sh/bun/bun
22 | ```
23 |
24 | **macOS/Linux/WSL (using installer script):**
25 | ```bash
26 | curl -fsSL https://bun.sh/install | bash
27 | ```
28 |
29 | **Windows:**
30 | Windows users should use WSL (Windows Subsystem for Linux) or Docker to run Bun.
31 |
32 | Node.js and npm will also work but may not offer the same performance benefits.
33 |
34 | ## Connecting to MCP Clients
35 |
36 | 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.
37 |
38 | 
39 |
40 | > **Note:** The `PRIVATE_KEY_WIF` environment variable is now optional. Without it, the server runs in limited mode with educational resources and non-wallet tools available. Wallet and MNEE token operations require a valid private key. You can also set the `IDENTITY_KEY_WIF` environment variable to enable sigma-protocol signing of ordinals inscriptions for authentication, curation, and web-of-trust.
41 |
42 | ### Cursor
43 |
44 | To use the BSV MCP server with [Cursor](https://cursor.sh/):
45 |
46 | 1. Install Cursor if you haven't already
47 | 2. Open Cursor and navigate to Settings → Extensions → Model Context Protocol
48 | 3. Click "Add a new global MCP server"
49 | 4. Enter the following configuration in JSON format:
50 |
51 | ```json
52 | {
53 | "mcpServers": {
54 | "Bitcoin SV": {
55 | "command": "bunx",
56 | "args": [
57 | "bsv-mcp@latest"
58 | ],
59 | "env": {
60 | "PRIVATE_KEY_WIF": "<your_private_key_wif>",
61 | "IDENTITY_KEY_WIF": "<your_identity_key_wif>"
62 | }
63 | }
64 | }
65 | }
66 | ```
67 |
68 | 5. Replace `<your_private_key_wif>` with your actual private key WIF (keep this secure!) If you dont have one you can leave this off for now but you wont be able to use tools that require a wallet. `<your_identity_key_wif>` is also optional. It will sign 1Sat Ordinals with Sigma protocol using the provided identity key.
69 |
70 | 6. Click "Save"
71 |
72 | The BSV tools will now be available to Cursor's AI assistant under the "Bitcoin SV" namespace.
73 |
74 | #### Alternative for npm users
75 |
76 | If you prefer to use npm instead of Bun:
77 |
78 | ```json
79 | {
80 | "mcpServers": {
81 | "Bitcoin SV": {
82 | "command": "npx",
83 | "args": [
84 | "bsv-mcp@latest"
85 | ],
86 | "env": {
87 | "PRIVATE_KEY_WIF": "<your_private_key_wif>",
88 | "IDENTITY_KEY_WIF": "<your_identity_key_wif>"
89 | }
90 | }
91 | }
92 | }
93 | ```
94 |
95 | ### Claude for Desktop
96 |
97 | To connect this server to Claude for Desktop:
98 |
99 | 1. Open [Claude for Desktop](https://claude.ai/desktop) and go to Claude > Settings > Developer
100 | 2. Click "Edit Config".
101 |
102 | Open the Claude configuration json file in your favorite text editor. If you prefer to do it from the cli:
103 |
104 | ```bash
105 | # macOS/Linux
106 | code ~/Library/Application\ Support/Claude/claude_desktop_config.json
107 |
108 | # Windows
109 | code %APPDATA%\Claude\claude_desktop_config.json
110 | ```
111 |
112 | 3. Add the BSV MCP server to your configuration:
113 | ```json
114 | {
115 | "mcpServers": {
116 | "Bitcoin SV": {
117 | "command": "bun",
118 | "args": [
119 | "run", "bsv-mcp@latest"
120 | ],
121 | "env": {
122 | "PRIVATE_KEY_WIF": "<your_private_key_wif>",
123 | "IDENTITY_KEY_WIF": "<your_identity_key_wif>"
124 | }
125 | }
126 | }
127 | }
128 | ```
129 | 4. Replace `<your_private_key_wif>` with your actual private key WIF
130 | 5. Save the file and restart Claude for Desktop
131 | 6. The BSV tools will appear when you click the tools icon (hammer) in Claude for Desktop
132 |
133 | #### Alternative for npm users (Claude)
134 |
135 | If you prefer to use npm instead of Bun, replace the "command" field with "npx".
136 |
137 | ## Available Tools
138 |
139 | The toolkit is organized into several categories:
140 |
141 | ### Wallet Tools
142 |
143 | Wallet tools provide core BSV wallet functionality:
144 |
145 | | Tool Name | Description | Example Output |
146 | | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
147 | | `wallet_getPublicKey` | Retrieves a public key for a specified protocol and key ID | `{"publicKey":"032d0c73eb9270e9e009fd1f9dd77e19cf764fbad5f799560c4e8fd414e40d6fc2"}` |
148 | | `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,...]}` |
149 | | `wallet_verifySignature` | Verifies a cryptographic signature against the provided data | `{"isValid":true}` |
150 | | `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"}` |
151 | | `wallet_getAddress` | Returns a BSV address for the current wallet or a derived path | `{"address":"1ExampleBsvAddressXXXXXXXXXXXXXXXXX","status":"ok"}` |
152 | | `wallet_sendToAddress` | Sends BSV to a specified address (supports BSV or USD amounts) | `{"status":"success","txid":"a1b2c3d4e5f6...","satoshis":1000000}` |
153 | | `wallet_purchaseListing` | Purchases NFTs or BSV-20/BSV-21 tokens from marketplace listings | `{"status":"success","txid":"a1b2c3d4e5f6...","type":"nft","origin":"abcdef123456..."}` |
154 | | `wallet_createOrdinals` | Creates and inscribes ordinals on the BSV blockchain | `{"txid":"a1b2c3d4e5f6...","inscriptionAddress":"1ExampleAddress...","contentType":"image/png"}` |
155 |
156 | ### BSV Tools
157 |
158 | Tools for interacting with the BSV blockchain and network:
159 |
160 | | Tool Name | Description | Example Output |
161 | | ----------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
162 | | `bsv_getPrice` | Gets the current BSV price from an exchange API | `Current BSV price: $38.75 USD` |
163 | | `bsv_decodeTransaction` | Decodes a BSV transaction and returns detailed information | `{"txid":"a1b2c3d4e5f6...","version":1,"locktime":0,"size":225,"inputs":[...],"outputs":[...]}` |
164 | | `bsv_explore` | Comprehensive blockchain explorer tool accessing WhatsOnChain API endpoints | `{"chain_info":{"chain":"main","blocks":826458,"headers":826458,"bestblockhash":"0000000000..."}}` |
165 |
166 | ### Ordinals Tools
167 |
168 | Tools for working with ordinals (NFTs) on BSV:
169 |
170 | | Tool Name | Description | Example Output |
171 | | ------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
172 | | `ordinals_getInscription` | Retrieves detailed information about a specific inscription | `{"id":"a1b2c3d4e5f6...","origin":"a1b2c3d4e5f6...","contentType":"image/png","content":"iVBORw0KGgoAAA..."}` |
173 | | `ordinals_searchInscriptions` | Searches for inscriptions based on various criteria | `{"results":[{"id":"a1b2c3...","contentType":"image/png","owner":"1Example..."},...]}` |
174 | | `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},...]}` |
175 | | `ordinals_marketSales` | Gets information about BSV-20 and BSV-21 token market sales | `{"results":[{"txid":"a1b2c3...","price":34710050,"tick":"$BTC","sale":true},...]}` |
176 | | `ordinals_getTokenByIdOrTicker` | Retrieves details about a specific BSV20 token by ID | `{"tick":"PEPE","max":"21000000","lim":"1000","dec":"2"}` |
177 |
178 | ### Utility Tools
179 |
180 | General-purpose utility functions:
181 |
182 | | Tool Name | Description | Example Output |
183 | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
184 | | `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) |
185 |
186 | ### MNEE Tools
187 |
188 | Tools for working with MNEE tokens:
189 |
190 | | Tool Name | Description | Example Output |
191 | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
192 | | `mnee_getBalance` | Retrieves the current MNEE token balance for the wallet | `{"balance": {"amount": 2900, "decimalAmount": 0.029}}` |
193 | | `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"}` |
194 | | `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}]}` |
195 |
196 | ## Using the Tools with MCP
197 |
198 | Once connected, you can use natural language to interact with Bitcoin SV through your AI assistant. Here are some example prompts:
199 |
200 | ### Wallet Operations
201 |
202 | - "Get my Bitcoin SV address"
203 | - "Send 0.01 BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
204 | - "Send $5 USD worth of BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
205 | - "Send 0.01 MNEE to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
206 | - "Check my MNEE balance"
207 | - "Parse this MNEE transaction: txid"
208 | - "Encrypt this message using my wallet's keys"
209 | - "Decrypt this data that was previously encrypted for me"
210 | - "Purchase this NFT listing: txid_vout"
211 | - "Purchase this BSV-20 token listing: txid_vout"
212 |
213 | ### Ordinals (NFTs)
214 |
215 | - "Show me information about the NFT with outpoint 6a89047af2cfac96da17d51ae8eb62c5f1d982be2bc4ba0d0cd2084b7ffed325_0"
216 | - "Search for Pixel Zoide NFTs"
217 | - "Show me the current marketplace listings for BSV NFTs"
218 | - "Show me BSV-20 token listings for ticker PEPE"
219 | - "Get recent BSV-20 token sales"
220 |
221 | ### Blockchain Operations
222 |
223 | - "What is the current BSV price?"
224 | - "Decode this BSV transaction: (transaction hex or ID)"
225 | - "Get the latest Bitcoin SV chain information"
226 | - "Show me block details for height 800000"
227 | - "Explore transaction history for address 1ExampleBsvAddressXXXX"
228 | - "Check unspent outputs (UTXOs) for my wallet address"
229 | - "Get details for transaction with hash a1b2c3d4e5f6..."
230 |
231 | ### Data Conversion
232 |
233 | - "Convert 'Hello World' from UTF-8 to hex format"
234 |
235 | ## MCP Prompts and Resources
236 |
237 | 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.
238 |
239 | ### Available Prompts
240 |
241 | The server provides the following educational prompts that can be accessed directly via the MCP protocol:
242 |
243 | #### Ordinals Prompt
244 | - **Identifier**: `bitcoin_sv_ordinals`
245 | - **Description**: Comprehensive information about Bitcoin SV ordinals, including what they are, how they work, and how to use them.
246 | - **Usage**: Ask the assistant about "Bitcoin SV ordinals" or "1Sat Ordinals" to access this information.
247 |
248 | #### BSV SDK Prompts
249 | A collection of prompts providing detailed information about the Bitcoin SV SDK:
250 |
251 | - **Overview**
252 | - **Identifier**: `bitcoin_sv_sdk_overview`
253 | - **Description**: General overview of the Bitcoin SV SDK, including its purpose and main components.
254 | - **Usage**: "Tell me about the BSV SDK" or "What is the Bitcoin SV SDK?"
255 |
256 | - **Wallet Operations**
257 | - **Identifier**: `bitcoin_sv_sdk_wallet`
258 | - **Description**: Information about wallet operations in the BSV SDK.
259 | - **Usage**: "How do wallet operations work in the BSV SDK?"
260 |
261 | - **Transaction Building**
262 | - **Identifier**: `bitcoin_sv_sdk_transaction`
263 | - **Description**: Details about transaction creation and manipulation.
264 | - **Usage**: "Explain BSV SDK transaction building" or "How do I create transactions with BSV SDK?"
265 |
266 | - **Authentication**
267 | - **Identifier**: `bitcoin_sv_sdk_auth`
268 | - **Description**: Authentication and identity protocols in BSV SDK.
269 | - **Usage**: "How does authentication work with BSV SDK?"
270 |
271 | - **Cryptography**
272 | - **Identifier**: `bitcoin_sv_sdk_cryptography`
273 | - **Description**: Signing, encryption, and verification functionality.
274 | - **Usage**: "Explain BSV SDK cryptography features"
275 |
276 | - **Scripting**
277 | - **Identifier**: `bitcoin_sv_sdk_script`
278 | - **Description**: Bitcoin scripting and contract capabilities.
279 | - **Usage**: "How do I work with Bitcoin scripts using the BSV SDK?"
280 |
281 | - **Primitives**
282 | - **Identifier**: `bitcoin_sv_sdk_primitives`
283 | - **Description**: Core data types and structures in the BSV SDK.
284 | - **Usage**: "What primitives are available in the BSV SDK?"
285 |
286 | ### Available Resources
287 |
288 | The server also provides access to Bitcoin Request for Comments (BRC) specifications and documentation:
289 |
290 | #### Changelog Resource
291 | - **Identifier**: `bsv-mcp-changelog`
292 | - **Description**: Version history and changelog for the BSV MCP server.
293 | - **Usage**: "Show me the BSV MCP changelog" or "What's new in the latest version?"
294 |
295 | #### BRC Resources
296 | - **BRCs Overview**
297 | - **Identifier**: `brcs_readme`
298 | - **Description**: Overview of all Bitcoin SV protocol specifications in the BRCs repository.
299 | - **Usage**: "Show me the Bitcoin SV BRCs overview"
300 |
301 | - **BRCs Summary**
302 | - **Identifier**: `brcs_summary`
303 | - **Description**: Table of contents for all Bitcoin SV BRCs.
304 | - **Usage**: "Give me a summary of Bitcoin SV BRCs"
305 |
306 | - **Specific BRC Specifications**
307 | - **Identifier**: `brc_spec`
308 | - **Description**: Access specific BRC specifications by category and number.
309 | - **Usage**: "Show me BRC 8 on Transaction Envelopes" or "What does BRC 1 specify?"
310 |
311 | #### BRC Categories
312 | The BRC specifications are organized into the following categories:
313 | - Wallet
314 | - Transactions
315 | - Scripts
316 | - Tokens
317 | - Overlays
318 | - Payments
319 | - Peer-to-Peer
320 | - Key Derivation
321 | - Outpoints
322 | - Opinions
323 | - State Machines
324 | - Apps
325 |
326 | ### Using Prompts and Resources
327 |
328 | AI models can use these prompts and resources to provide more accurate and detailed responses about Bitcoin SV technologies. As a user, you can:
329 |
330 | 1. **Ask about a specific topic**: "Tell me about Bitcoin SV ordinals" or "Explain BSV SDK transaction building"
331 | 2. **Request specific BRC details**: "What does BRC 8 specify?" or "Show me the BRC on Transaction Creation"
332 | 3. **Get general overviews**: "What is the BSV SDK?" or "Show me a summary of all BRCs"
333 |
334 | These prompts and resources enhance the AI's knowledge base, enabling more technical and accurate responses even for complex Bitcoin SV topics.
335 |
336 | ## How MCP Works
337 |
338 | When you interact with an MCP-enabled AI assistant:
339 |
340 | 1. The AI analyzes your request and decides which tools to use
341 | 2. With your approval, it calls the appropriate BSV MCP tool
342 | 3. The server executes the requested operation on the Bitcoin SV blockchain
343 | 4. The results are returned to the AI assistant
344 | 5. The assistant presents the information in a natural, conversational way
345 |
346 | ## Customization Options
347 |
348 | The BSV MCP server can be customized using environment variables to enable or disable specific components:
349 |
350 | ### Component Configuration
351 |
352 | | Environment Variable | Default | Description |
353 | | -------------------- | ------- | ----------- |
354 | | `DISABLE_PROMPTS` | `false` | Set to `true` to disable all educational prompts |
355 | | `DISABLE_RESOURCES` | `false` | Set to `true` to disable all resources (BRCs, changelog) |
356 | | `DISABLE_TOOLS` | `false` | Set to `true` to disable all tools |
357 |
358 | ### Tool-Specific Configuration
359 |
360 | | Environment Variable | Default | Description |
361 | | -------------------- | ------- | ----------- |
362 | | `DISABLE_WALLET_TOOLS` | `false` | Set to `true` to disable Bitcoin wallet tools |
363 | | `DISABLE_MNEE_TOOLS` | `false` | Set to `true` to disable MNEE token tools |
364 | | `DISABLE_BSV_TOOLS` | `false` | Set to `true` to disable BSV blockchain tools |
365 | | `DISABLE_ORDINALS_TOOLS` | `false` | Set to `true` to disable Ordinals/NFT tools |
366 | | `DISABLE_UTILS_TOOLS` | `false` | Set to `true` to disable utility tools |
367 | | `IDENTITY_KEY_WIF` | `not set` | Optional WIF for identity key; if set, ordinals inscriptions will be signed with sigma-protocol for authentication, curation, and web-of-trust. |
368 | | `DISABLE_BROADCASTING` | `false` | Set to `true` to disable transaction broadcasting; returns raw transaction hex instead - useful for testing and transaction review before broadcasting |
369 |
370 | ### Examples
371 |
372 | Run with only educational resources and prompts, no tools:
373 |
374 | ```bash
375 | DISABLE_TOOLS=true bunx bsv-mcp@latest
376 | ```
377 |
378 | Run with only BSV tools, no wallet or other functionality:
379 |
380 | ```bash
381 | DISABLE_PROMPTS=true DISABLE_RESOURCES=true DISABLE_WALLET_TOOLS=true DISABLE_MNEE_TOOLS=true DISABLE_ORDINALS_TOOLS=true DISABLE_UTILS_TOOLS=true bunx bsv-mcp@latest
382 | ```
383 |
384 | Use all tools except wallet operations:
385 |
386 | ```bash
387 | DISABLE_WALLET_TOOLS=true bunx bsv-mcp@latest
388 | ```
389 |
390 | Create transactions without broadcasting them (dry-run mode):
391 |
392 | ```bash
393 | DISABLE_BROADCASTING=true bunx bsv-mcp@latest
394 | ```
395 |
396 | ## Troubleshooting
397 |
398 | If you're having issues with the BSV MCP server:
399 |
400 | ### Connection Issues
401 |
402 | 1. Make sure Bun or Node.js is installed on your system
403 | 2. Verify your WIF private key is correctly set in the environment
404 | 3. Check that your client supports MCP and is properly configured
405 | 4. Look for error messages in the client's console output
406 |
407 | ### Keeping Bun Up to Date
408 |
409 | It's important to keep Bun updated to the latest version to ensure compatibility:
410 |
411 | ```bash
412 | # Update Bun to the latest version
413 | bun upgrade
414 | ```
415 |
416 | To verify your current Bun version:
417 | ```bash
418 | bun --version
419 | ```
420 |
421 | ### Logging and Debugging
422 |
423 | For Claude for Desktop, check the logs at:
424 |
425 | ```bash
426 | # macOS/Linux
427 | tail -n 20 -f ~/Library/Logs/Claude/mcp*.log
428 |
429 | # Windows
430 | type %APPDATA%\Claude\Logs\mcp*.log
431 | ```
432 |
433 | For Cursor, check the Cursor MCP logs in Settings → Extensions → Model Context Protocol.
434 |
435 | ## Recent Updates
436 |
437 | - **Transaction Broadcast Control**: Added `DISABLE_BROADCASTING` environment variable to prevent transactions from being broadcast to the network
438 | - **Blockchain Explorer**: Added `bsv_explore` tool for WhatsOnChain API access with mainnet/testnet support
439 | - **Unified Tools**: Merged `wallet_encrypt`/`wallet_decrypt` into single `wallet_encryption` tool
440 | - **Enhanced Marketplace**: Support for NFTs, BSV-20/21 tokens in listings, sales and purchases
441 | - **Performance**: Added price caching and optimized API endpoint structure
442 | - **Improved Validation**: Better error handling for private keys and parameters
443 |
444 | ## Bitcoin SV Blockchain Explorer
445 |
446 | The `bsv_explore` tool provides comprehensive access to the Bitcoin SV blockchain through the WhatsOnChain API. This powerful explorer tool allows you to query various aspects of the blockchain, including chain data, blocks, transactions, and address information.
447 |
448 | ### Available Endpoints
449 |
450 | The tool supports the following endpoint categories and specific endpoints:
451 |
452 | #### Chain Data
453 |
454 | | Endpoint | Description | Required Parameters | Example Response |
455 | | -------------------- | ----------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------- |
456 | | `chain_info` | Network statistics, difficulty, and chain work | None | `{"chain":"main","blocks":826458,"headers":826458,"bestblockhash":"0000000000..."}` |
457 | | `chain_tips` | Current chain tips including heights and states | None | `[{"height":826458,"hash":"000000000000...","branchlen":0,"status":"active"}]` |
458 | | `circulating_supply` | Current BSV circulating supply | None | `{"bsv":21000000}` |
459 | | `peer_info` | Connected peer statistics | None | `[{"addr":"1.2.3.4:8333","services":"000000000000...","lastsend":1621234567}]` |
460 |
461 | #### Block Data
462 |
463 | | Endpoint | Description | Required Parameters | Example Response |
464 | | --------------------- | --------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------- |
465 | | `block_by_hash` | Complete block data via hash | `blockHash` | `{"hash":"000000000000...","confirmations":1000,"size":1000000,...}` |
466 | | `block_by_height` | Complete block data via height | `blockHeight` | `{"hash":"000000000000...","confirmations":1000,"size":1000000,...}` |
467 | | `tag_count_by_height` | Stats on tag count for a specific block | `blockHeight` | `{"tags":{"amp":3,"bitkey":5,"metanet":12,"planaria":7,"b":120}}` |
468 | | `block_headers` | Retrieves the last 10 block headers | None | `[{"hash":"000000000000...","height":826458,"version":536870912,...},...]` |
469 | | `block_pages` | Retrieves pages of transaction IDs for large blocks | `blockHash`, optional: `pageNumber` | `["tx1hash","tx2hash","tx3hash",...]` |
470 |
471 | #### Stats Data
472 |
473 | | Endpoint | Description | Required Parameters | Example Response |
474 | | ----------------------- | ----------------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------- |
475 | | `block_stats_by_height` | Block statistics for a specific height | `blockHeight` | `{"size":123456,"txCount":512,"outputTotal":54.12345678,"outputTotalUsd":2345.67,...}` |
476 | | `block_miner_stats` | Block mining statistics for a time period | optional: `days` (default 7) | `{"blocks":{"miner1":412,"miner2":208,...},"total":1008}` |
477 | | `miner_summary_stats` | Summary of mining statistics | optional: `days` (default 7) | `{"totalBlocks":1008,"totalFees":1.23456789,"totalFeesUsd":53.67,...}` |
478 |
479 | #### Transaction Data
480 |
481 | | Endpoint | Description | Required Parameters | Example Response |
482 | | ----------------- | --------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
483 | | `tx_by_hash` | Detailed transaction data | `txHash` | `{"txid":"a1b2c3d4e5f6...","version":1,"locktime":0,"size":225,...}` |
484 | | `tx_raw` | Raw transaction hex data | `txHash` | `"01000000012345abcdef..."` |
485 | | `tx_receipt` | Transaction receipt | `txHash` | `{"blockHash":"000000000000...","blockHeight":800000,"confirmations":26458}` |
486 | | `bulk_tx_details` | Retrieve multiple transactions in one request | `txids` (array) | `[{"txid":"a1b2c3d4e5f6...","version":1,...}, {"txid":"b2c3d4e5f6a7...","version":1,...}]` |
487 |
488 | #### Address Data
489 |
490 | | Endpoint | Description | Required Parameters | Example Response |
491 | | ----------------- | ------------------------------- | ---------------------------- | --------------------------------------------------------------- |
492 | | `address_history` | Transaction history for address | `address`, optional: `limit` | `[{"tx_hash":"a1b2c3d4e5f6...","height":800000},...]` |
493 | | `address_utxos` | Unspent outputs for address | `address` | `[{"tx_hash":"a1b2c3d4e5f6...","tx_pos":0,"value":100000},...]` |
494 |
495 | #### Network
496 |
497 | | Endpoint | Description | Required Parameters | Example Response |
498 | | -------- | ---------------- | ------------------- | --------------------- |
499 | | `health` | API health check | None | `{"status":"synced"}` |
500 |
501 | ### Usage Examples
502 |
503 | The `bsv_explore` tool can be used with natural language prompts like:
504 |
505 | ```
506 | "Get the current Bitcoin SV blockchain information"
507 | "Show me block #800000 details"
508 | "Get tag count statistics for block #800000"
509 | "Fetch transaction history for address 1ExampleBsvAddressXXXXXXXX"
510 | "Get unspent outputs for my wallet address"
511 | "Check transaction details for txid a1b2c3d4e5f6..."
512 | "What is the current BSV circulating supply?"
513 | "Show me the latest block headers"
514 | "Get transaction IDs for page 2 of a large block"
515 | "Show me block statistics for height 800000"
516 | "What are the mining statistics for the last 14 days?"
517 | "Get a summary of mining activity over the past 30 days"
518 | "Retrieve details for multiple transactions in a single query"
519 | ```
520 |
521 | Under the hood, the tool accepts parameters to specify which data to retrieve:
522 |
523 | - `endpoint`: The specific WhatsOnChain endpoint to query (e.g., `chain_info`, `tx_by_hash`)
524 | - `network`: The BSV network to use (`main` or `test`)
525 | - Additional parameters as required by the specific endpoint:
526 | - `blockHash`: For block_by_hash and block_pages endpoints
527 | - `blockHeight`: For block_by_height, tag_count_by_height, and block_stats_by_height endpoints
528 | - `pageNumber`: For block_pages endpoint (pagination)
529 | - `days`: For block_miner_stats and miner_summary_stats endpoints (defaults to 7)
530 | - `txHash`: For transaction-related endpoints (tx_by_hash, tx_raw, tx_receipt)
531 | - `txids`: For bulk_tx_details endpoint (array of transaction IDs)
532 | - `address`: For address-related endpoints
533 | - `limit`: Optional pagination limit for address_history
534 |
535 | ### Network Options
536 |
537 | The tool supports both mainnet and testnet:
538 |
539 | - `main`: Bitcoin SV mainnet (default)
540 | - `test`: Bitcoin SV testnet
541 |
542 | # Development
543 |
544 | ## Project Setup
545 |
546 | If you want to contribute to the project or run it locally:
547 |
548 | 1. Clone the repository:
549 | ```bash
550 | git clone https://github.com/b-open-io/bsv-mcp.git
551 | cd bsv-mcp
552 | ```
553 |
554 | 2. Install dependencies:
555 | ```bash
556 | bun install
557 | # or with npm
558 | npm install
559 | ```
560 |
561 | ## Running the Server
562 |
563 | ```bash
564 | bun run index.ts
565 | # or with npm
566 | npm run start
567 | ```
568 |
569 | ### Running Tests
570 |
571 | ```bash
572 | bun test
573 | # or with npm
574 | npm test
575 | ```
576 |
577 | ## License
578 |
579 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
580 |
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | # Use the official Bun image
2 | FROM oven/bun:1
3 |
4 | # Set working directory
5 | WORKDIR /app
6 |
7 | # Copy all application code first
8 | COPY . .
9 |
10 | # Install dependencies
11 | RUN bun install --frozen-lockfile
12 |
13 | # Set user for security
14 | USER bun
15 |
16 | # Expose port (if needed)
17 | EXPOSE 3000
18 |
19 | # Run the application
20 | CMD ["bun", "run", "index.ts"]
```
--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "vcs": {
4 | "enabled": false,
5 | "clientKind": "git",
6 | "useIgnoreFile": false
7 | },
8 | "files": {
9 | "ignoreUnknown": false,
10 | "ignore": ["./dist"]
11 | },
12 | "formatter": {
13 | "enabled": true,
14 | "indentStyle": "tab"
15 | },
16 | "organizeImports": {
17 | "enabled": true
18 | },
19 | "linter": {
20 | "enabled": true,
21 | "rules": {
22 | "recommended": true
23 | }
24 | },
25 | "javascript": {
26 | "formatter": {
27 | "quoteStyle": "double"
28 | }
29 | }
30 | }
31 |
```
--------------------------------------------------------------------------------
/tools/bsv/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { registerDecodeTransactionTool } from "./decodeTransaction";
3 | import { registerExploreTool } from "./explore";
4 | import { registerGetPriceTool } from "./getPrice";
5 |
6 | /**
7 | * Register all BSV tools with the MCP server
8 | * @param server The MCP server instance
9 | */
10 | export function registerBsvTools(server: McpServer): void {
11 | // Register BSV-related tools
12 | registerGetPriceTool(server);
13 | registerDecodeTransactionTool(server);
14 | registerExploreTool(server);
15 | }
16 |
```
--------------------------------------------------------------------------------
/tools/constants.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Constants for BSV MCP Tools
3 | */
4 |
5 | /**
6 | * Market fee percentage applied to all marketplace purchases
7 | * Expressed as a decimal (e.g., 0.03 = 3%)
8 | */
9 | export const MARKET_FEE_PERCENTAGE = 0.03;
10 |
11 | /**
12 | * Market wallet address where fees are sent
13 | * This is the recipient address for marketplace fees
14 | */
15 | export const MARKET_WALLET_ADDRESS = "15q8YQSqUa9uTh6gh4AVixxq29xkpBBP9z";
16 |
17 | /**
18 | * Minimum fee in satoshis
19 | * Market fee will never be less than this amount
20 | */
21 | export const MINIMUM_MARKET_FEE_SATOSHIS = 10000; // 10000 satoshis = 0.0001 BSV
22 |
```
--------------------------------------------------------------------------------
/prompts/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { registerAllBsvSdkPrompts } from "./bsvSdk";
3 | import { registerOrdinalsPrompt } from "./ordinals";
4 |
5 | /**
6 | * Register all prompts with the MCP server
7 | * @param server The MCP server instance
8 | */
9 | export function registerAllPrompts(server: McpServer): void {
10 | // Register Ordinals prompt
11 | registerOrdinalsPrompt(server);
12 |
13 | // Register all BSV SDK prompts
14 | registerAllBsvSdkPrompts(server);
15 |
16 | // Add more prompts registration here as needed
17 | }
18 |
19 | /**
20 | * Export all prompt constants
21 | */
22 | export * from "./ordinals";
23 | export * from "./bsvSdk";
24 |
```
--------------------------------------------------------------------------------
/tools/mnee/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import Mnee from "mnee";
3 | import { registerGetBalanceTool } from "./getBalance";
4 | import { registerParseTxTool } from "./parseTx";
5 | import { registerSendMneeTool } from "./sendMnee";
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 | }
22 |
```
--------------------------------------------------------------------------------
/resources/resources.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { registerBRCsResources } from "./brcs";
3 | import { registerChangelogResource } from "./changelog";
4 | import { registerJungleBusResource } from "./junglebus";
5 |
6 | /**
7 | * Register all resources with the MCP server
8 | * @param server The MCP server instance
9 | */
10 | export function registerResources(server: McpServer): void {
11 | // Register BRC-related resources
12 | registerBRCsResources(server);
13 |
14 | // Register changelog resource
15 | registerChangelogResource(server);
16 |
17 | // Register JungleBus API documentation resource
18 | registerJungleBusResource(server);
19 |
20 | // Add more resource categories here as needed
21 | }
22 |
```
--------------------------------------------------------------------------------
/tools/ordinals/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { registerGetInscriptionTool } from "./getInscription";
3 | import { registerGetTokenByIdOrTickerTool } from "./getTokenByIdOrTicker";
4 | import { registerMarketListingsTool } from "./marketListings";
5 | import { registerMarketSalesTool } from "./marketSales";
6 | import { registerSearchInscriptionsTool } from "./searchInscriptions";
7 |
8 | /**
9 | * Register all Ordinals tools with the MCP server
10 | * @param server The MCP server instance
11 | */
12 | export function registerOrdinalsTools(server: McpServer): void {
13 | // Register Ordinals-related tools
14 | registerGetInscriptionTool(server);
15 | registerSearchInscriptionsTool(server);
16 | registerMarketListingsTool(server);
17 | registerMarketSalesTool(server);
18 | registerGetTokenByIdOrTickerTool(server);
19 | }
20 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | // Environment setup & latest features
4 | "lib": ["ESNext"],
5 | "target": "ESNext",
6 | "module": "ESNext",
7 | "moduleDetection": "force",
8 | "jsx": "react-jsx",
9 | "allowJs": true,
10 |
11 | // Bundler mode
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "verbatimModuleSyntax": true,
15 | "noEmit": true,
16 |
17 | // Best practices
18 | "strict": true,
19 | "skipLibCheck": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedIndexedAccess": true,
22 |
23 | // Some stricter flags (disabled by default)
24 | "noUnusedLocals": false,
25 | "noUnusedParameters": false,
26 | "noPropertyAccessFromIndexSignature": false
27 | },
28 | "include": [
29 | "package.json",
30 | "*.ts",
31 | "tools/*.ts",
32 | "tools/**/*.ts",
33 | "prompts/*.ts",
34 | "prompts/**/*.ts",
35 | "resources/*.ts",
36 | "resources/**/*.ts",
37 | "LICENSE",
38 | "README.md",
39 | "CHANGELOG.md",
40 | "smithery.yaml"
41 | ]
42 | }
43 |
```
--------------------------------------------------------------------------------
/tools/a2b/call.ts:
--------------------------------------------------------------------------------
```typescript
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 | }
33 |
```
--------------------------------------------------------------------------------
/tools/wallet/getPublicKey.ts:
--------------------------------------------------------------------------------
```typescript
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 | }
36 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "bsv-mcp",
3 | "module": "dist/index.js",
4 | "type": "module",
5 | "version": "0.0.36",
6 | "license": "MIT",
7 | "author": "satchmo",
8 | "description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/b-open-io/bsv-mcp"
12 | },
13 | "keywords": [
14 | "bitcoin",
15 | "bsv",
16 | "bitcoin-sv",
17 | "wallet",
18 | "ordinals",
19 | "blockchain",
20 | "1sat-ordinals",
21 | "explorer",
22 | "block explorer"
23 | ],
24 | "files": [
25 | "dist/**/*.js",
26 | "package.json",
27 | "*.ts",
28 | "tools/*.ts",
29 | "tools/**/*.ts",
30 | "prompts/*.ts",
31 | "prompts/**/*.ts",
32 | "resources/*.ts",
33 | "resources/**/*.ts",
34 | "LICENSE",
35 | "README.md",
36 | "CHANGELOG.md",
37 | "smithery.yaml"
38 | ],
39 | "bin": {
40 | "bsv-mcp": "./dist/index.js"
41 | },
42 | "private": false,
43 | "devDependencies": {
44 | "@biomejs/biome": "^1.9.4",
45 | "@types/bun": "latest"
46 | },
47 | "peerDependencies": {
48 | "typescript": "^5.8.3"
49 | },
50 | "dependencies": {
51 | "@bsv/sdk": "^1.4.20",
52 | "@modelcontextprotocol/sdk": "^1.10.2",
53 | "@types/node": "^22.15.2",
54 | "bun": "^1.2.10",
55 | "js-1sat-ord": "^0.1.81",
56 | "mnee": "^2.0.0",
57 | "satoshi-token": "^0.0.4",
58 | "sigma-protocol": "^0.1.6",
59 | "zod": "^3.24.3"
60 | },
61 | "scripts": {
62 | "build": "bun build ./index.ts --outdir ./dist --target node",
63 | "lint": "biome check .",
64 | "lint:fix": "biome check . --write"
65 | }
66 | }
67 |
```
--------------------------------------------------------------------------------
/prompts/bsvSdk/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 |
3 | import { registerAuthPrompt } from "./auth";
4 | import { registerCryptographyPrompt } from "./cryptography";
5 | // Import all prompt registration functions
6 | import { registerOverviewPrompt } from "./overview";
7 | import { registerPrimitivesPrompt } from "./primitives";
8 | import { registerScriptPrompt } from "./script";
9 | import { registerTransactionPrompt } from "./transaction";
10 | import { registerWalletPrompt } from "./wallet";
11 |
12 | /**
13 | * Register all BSV SDK prompts with the MCP server
14 | * @param server The MCP server instance
15 | */
16 | export function registerAllBsvSdkPrompts(server: McpServer): void {
17 | // Register all BSV SDK related prompts
18 | registerOverviewPrompt(server);
19 | registerWalletPrompt(server);
20 | registerTransactionPrompt(server);
21 | registerAuthPrompt(server);
22 | registerCryptographyPrompt(server);
23 | registerScriptPrompt(server);
24 | registerPrimitivesPrompt(server);
25 | }
26 |
27 | // Export all prompts
28 | export { registerOverviewPrompt } from "./overview";
29 | export { registerWalletPrompt } from "./wallet";
30 | export { registerTransactionPrompt } from "./transaction";
31 | export { registerAuthPrompt } from "./auth";
32 | export { registerCryptographyPrompt } from "./cryptography";
33 | export { registerScriptPrompt } from "./script";
34 | export { registerPrimitivesPrompt } from "./primitives";
35 |
```
--------------------------------------------------------------------------------
/resources/changelog.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 |
3 | const CHANGELOG_URL =
4 | "https://raw.githubusercontent.com/b-open-io/bsv-mcp/master/CHANGELOG.md";
5 |
6 | /**
7 | * Fetches the changelog from GitHub
8 | * @returns Promise that resolves to the changelog content
9 | */
10 | async function fetchChangelog(): Promise<string> {
11 | try {
12 | const response = await fetch(CHANGELOG_URL);
13 |
14 | if (!response.ok) {
15 | throw new Error(
16 | `Failed to fetch changelog: ${response.status} ${response.statusText}`,
17 | );
18 | }
19 |
20 | return await response.text();
21 | } catch (error) {
22 | console.error("Error fetching changelog:", error);
23 | return "# BSV MCP Server Changelog\n\nError: Could not load changelog content.\nPlease check the server logs for more details.";
24 | }
25 | }
26 |
27 | /**
28 | * Register the changelog resource with the MCP server
29 | * @param server The MCP server instance
30 | */
31 | export function registerChangelogResource(server: McpServer): void {
32 | server.resource(
33 | "bsv-mcp-changelog",
34 | "https://github.com/b-open-io/bsv-mcp/blob/main/CHANGELOG.md",
35 | {
36 | title: "BSV MCP Server Changelog",
37 | description: "Version history and changelog for the BSV MCP server",
38 | },
39 | async (uri) => {
40 | const changelogContent = await fetchChangelog();
41 | return {
42 | contents: [
43 | {
44 | uri: uri.href,
45 | text: changelogContent,
46 | },
47 | ],
48 | };
49 | },
50 | );
51 | }
52 |
```
--------------------------------------------------------------------------------
/tools/utils/conversion.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { Utils } from "@bsv/sdk";
2 | import { z } from "zod";
3 | const {
4 | toArray: bsvToArray,
5 | toBase64: bsvToBase64,
6 | toHex: bsvToHex,
7 | toUTF8: bsvToUTF8,
8 | } = Utils;
9 |
10 | const encodingSchema = z.enum(["utf8", "hex", "base64", "binary"]);
11 |
12 | /**
13 | * Convert data between hex, base64, utf8, and binary (number array) formats.
14 | * @param data - The input data as a string (hex, base64, utf8, or JSON array string for binary)
15 | * @param from - The encoding of the input data
16 | * @param to - The desired encoding of the output data
17 | * @returns The converted data as a string (except for binary, which is a JSON array string)
18 | */
19 | export function convertData({
20 | data,
21 | from,
22 | to,
23 | }: {
24 | data: string;
25 | from: "hex" | "base64" | "utf8" | "binary";
26 | to: "hex" | "base64" | "utf8" | "binary";
27 | }): string {
28 | encodingSchema.parse(from);
29 | encodingSchema.parse(to);
30 |
31 | let arr: number[];
32 | if (from === "binary") {
33 | try {
34 | arr = JSON.parse(data);
35 | if (!Array.isArray(arr) || !arr.every((n) => typeof n === "number")) {
36 | throw new Error();
37 | }
38 | } catch {
39 | throw new Error("Invalid binary input: must be a JSON array of numbers");
40 | }
41 | } else {
42 | arr = bsvToArray(data, from);
43 | }
44 |
45 | if (to === "binary") {
46 | return JSON.stringify(arr);
47 | }
48 | if (to === "hex") {
49 | return bsvToHex(arr);
50 | }
51 | if (to === "base64") {
52 | return bsvToBase64(arr);
53 | }
54 | if (to === "utf8") {
55 | return bsvToUTF8(arr);
56 | }
57 | throw new Error("Invalid 'to' encoding");
58 | }
59 |
```
--------------------------------------------------------------------------------
/tools/mnee/parseTx.ts:
--------------------------------------------------------------------------------
```typescript
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 {
5 | ServerNotification,
6 | ServerRequest,
7 | } from "@modelcontextprotocol/sdk/types.js";
8 | import type { MneeInterface, ParseTxResponse } from "mnee";
9 | import { z } from "zod";
10 |
11 | /**
12 | * Schema for the parseTx tool arguments.
13 | */
14 | export const parseTxArgsSchema = z.object({
15 | txid: z.string().describe("Transaction ID to parse"),
16 | });
17 |
18 | export type ParseTxArgs = z.infer<typeof parseTxArgsSchema>;
19 |
20 | export function registerParseTxTool(
21 | server: McpServer,
22 | mnee: MneeInterface,
23 | ): void {
24 | server.tool(
25 | "mnee_parseTx",
26 | "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).",
27 | { args: parseTxArgsSchema },
28 | async (
29 | { args }: { args: ParseTxArgs },
30 | extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
31 | ): Promise<CallToolResult> => {
32 | try {
33 | const result: ParseTxResponse = await mnee.parseTx(args.txid);
34 |
35 | return {
36 | content: [
37 | {
38 | type: "text",
39 | text: JSON.stringify(result, null, 2),
40 | },
41 | ],
42 | };
43 | } catch (error) {
44 | const msg = error instanceof Error ? error.message : String(error);
45 | return { content: [{ type: "text", text: msg }], isError: true };
46 | }
47 | },
48 | );
49 | }
50 |
```
--------------------------------------------------------------------------------
/tools/wallet/getAddress.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { PrivateKey } from "@bsv/sdk";
2 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3 | import { z } from "zod";
4 |
5 | /**
6 | * Register the tool to get the wallet address
7 | * @param server The MCP server instance
8 | */
9 | export function registerGetAddressTool(server: McpServer): void {
10 | server.tool(
11 | "wallet_getAddress",
12 | "Retrieves the current wallet's Bitcoin SV address. This address can be used to receive BSV, ordinals, or tokens, and is derived from the wallet's private key.",
13 | {
14 | args: z
15 | .object({})
16 | .optional()
17 | .describe(
18 | "No parameters required - simply returns the current wallet address",
19 | ),
20 | },
21 | async () => {
22 | try {
23 | const wif = process.env.PRIVATE_KEY_WIF;
24 | if (!wif) {
25 | return {
26 | content: [
27 | {
28 | type: "text",
29 | text: JSON.stringify({
30 | error: "No private key available",
31 | message:
32 | "Please set PRIVATE_KEY_WIF environment variable with a valid Bitcoin SV private key in WIF format.",
33 | status: "error",
34 | }),
35 | },
36 | ],
37 | isError: true,
38 | };
39 | }
40 |
41 | const privKey = PrivateKey.fromWif(wif);
42 | const address = privKey.toAddress();
43 | return {
44 | content: [
45 | {
46 | type: "text",
47 | text: JSON.stringify({ address, status: "ok" }),
48 | },
49 | ],
50 | };
51 | } catch (err: unknown) {
52 | const msg = err instanceof Error ? err.message : String(err);
53 | return {
54 | content: [
55 | {
56 | type: "text",
57 | text: JSON.stringify({
58 | error: "Invalid private key",
59 | message: msg,
60 | status: "error",
61 | }),
62 | },
63 | ],
64 | isError: true,
65 | };
66 | }
67 | },
68 | );
69 | }
70 |
```
--------------------------------------------------------------------------------
/tools/mnee/getBalance.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { PrivateKey } 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 type { MneeInterface } from "mnee";
9 | import { z } from "zod";
10 |
11 | export const getBalanceArgsSchema = z.object({});
12 |
13 | export type GetBalanceArgs = z.infer<typeof getBalanceArgsSchema>;
14 |
15 | export function registerGetBalanceTool(
16 | server: McpServer,
17 | mnee: MneeInterface,
18 | ): void {
19 | server.tool(
20 | "mnee_getBalance",
21 | "Retrieves the current MNEE token balance for the wallet. Returns the balance in MNEE tokens.",
22 | {
23 | args: getBalanceArgsSchema,
24 | },
25 | async (
26 | { args }: { args: GetBalanceArgs },
27 | extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
28 | ) => {
29 | try {
30 | // Get private key from wallet
31 | const privateKeyWif = process.env.PRIVATE_KEY_WIF;
32 | if (!privateKeyWif) {
33 | throw new Error(
34 | "Private key WIF not available in environment variables",
35 | );
36 | }
37 | const privateKey = PrivateKey.fromWif(privateKeyWif);
38 | if (!privateKey) {
39 | throw new Error("No private key available");
40 | }
41 |
42 | const address = privateKey.toAddress().toString();
43 | const balance = await mnee.balance(address);
44 |
45 | return {
46 | content: [
47 | {
48 | type: "text",
49 | text: JSON.stringify({ balance }, null, 2),
50 | },
51 | ],
52 | };
53 | } catch (error) {
54 | return {
55 | content: [
56 | {
57 | type: "text",
58 | text: error instanceof Error ? error.message : String(error),
59 | },
60 | ],
61 | isError: true,
62 | };
63 | }
64 | },
65 | );
66 | }
67 |
```
--------------------------------------------------------------------------------
/tools/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { registerA2aCallTool } from "./a2b/call";
3 | import { registerA2bDiscoverTool } from "./a2b/discover";
4 | import { registerBsvTools } from "./bsv";
5 | import { registerOrdinalsTools } from "./ordinals";
6 | import { registerUtilsTools } from "./utils";
7 |
8 | /**
9 | * Configuration options for tools
10 | *
11 | * These options can be controlled through environment variables:
12 | * - enableBsvTools: controlled by DISABLE_BSV_TOOLS
13 | * - enableOrdinalsTools: controlled by DISABLE_ORDINALS_TOOLS
14 | * - enableUtilsTools: controlled by DISABLE_UTILS_TOOLS
15 | * - enableA2bTools: controlled by DISABLE_A2B_TOOLS
16 | */
17 | export interface ToolsConfig {
18 | enableBsvTools?: boolean;
19 | enableOrdinalsTools?: boolean;
20 | enableUtilsTools?: boolean;
21 | enableA2bTools?: boolean;
22 | }
23 |
24 | /**
25 | * Register all tools with the MCP server based on configuration
26 | * @param server The MCP server instance
27 | * @param config Configuration options
28 | */
29 | export function registerAllTools(
30 | server: McpServer,
31 | config: ToolsConfig = {
32 | enableBsvTools: true,
33 | enableOrdinalsTools: true,
34 | enableUtilsTools: true,
35 | enableA2bTools: false,
36 | },
37 | ): void {
38 | const {
39 | enableBsvTools = true,
40 | enableOrdinalsTools = true,
41 | enableUtilsTools = true,
42 | enableA2bTools = false,
43 | } = config;
44 |
45 | // Register BSV-related tools
46 | if (enableBsvTools) {
47 | registerBsvTools(server);
48 | }
49 |
50 | // Register Ordinals-related tools
51 | if (enableOrdinalsTools) {
52 | registerOrdinalsTools(server);
53 | }
54 |
55 | // Register utility tools
56 | if (enableUtilsTools) {
57 | registerUtilsTools(server);
58 | }
59 |
60 | // Register agent-to-blockchain tools
61 | if (enableA2bTools) {
62 | // Register agent-to-blockchain discovery tool
63 | registerA2bDiscoverTool(server);
64 |
65 | // Register agent-to-agent call tool
66 | // registerA2aCallTool(server);
67 | }
68 |
69 | // Add more tool categories as needed
70 | }
71 |
```
--------------------------------------------------------------------------------
/prompts/bsvSdk/auth.ts:
--------------------------------------------------------------------------------
```typescript
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 |
8 | /**
9 | * BSV SDK Authentication Prompt
10 | *
11 | * Provides detailed information about the authentication functionality in the BSV SDK,
12 | * including identity protocols, certificates, and session management.
13 | */
14 | export const BSV_SDK_AUTH_PROMPT = `
15 | # BSV SDK - Authentication Module
16 |
17 | The Authentication module in the BSV SDK provides robust mechanisms for identity management, peer authentication, and certificate handling on the Bitcoin SV blockchain.
18 |
19 | ## Key Components
20 |
21 | This section includes a placeholder for detailed content about the BSV SDK authentication mechanisms.
22 |
23 | ## Core Features
24 |
25 | - Identity management
26 | - Certificate handling
27 | - Peer authentication
28 | - Session management
29 |
30 | ## Best Practices
31 |
32 | 1. **Security**: Follow best practices for authentication security
33 | 2. **Testing**: Test authentication flows thoroughly before production use
34 | 3. **Error Handling**: Implement proper error handling
35 |
36 | For complete API documentation and additional authentication features, refer to the official BSV SDK documentation.
37 | `;
38 |
39 | /**
40 | * Register the BSV SDK Authentication prompt with the MCP server
41 | * @param server The MCP server instance
42 | */
43 | export function registerAuthPrompt(server: McpServer): void {
44 | server.prompt(
45 | "bitcoin_sv_sdk_auth",
46 | "Detailed information about the authentication functionality in the BSV SDK, including identity protocols, certificates, and session management.",
47 | async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
48 | return {
49 | messages: [
50 | {
51 | role: "assistant",
52 | content: {
53 | type: "text",
54 | text: BSV_SDK_AUTH_PROMPT,
55 | },
56 | },
57 | ],
58 | };
59 | },
60 | );
61 | }
62 |
```
--------------------------------------------------------------------------------
/prompts/bsvSdk/cryptography.ts:
--------------------------------------------------------------------------------
```typescript
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 |
8 | /**
9 | * BSV SDK Cryptography Prompt
10 | *
11 | * Provides detailed information about the cryptographic functionality in the BSV SDK,
12 | * including key generation, signing, encryption, and hashing.
13 | */
14 | export const BSV_SDK_CRYPTOGRAPHY_PROMPT = `
15 | # BSV SDK - Cryptography Module
16 |
17 | The Cryptography module in the BSV SDK provides comprehensive tools for handling cryptographic operations required for secure Bitcoin transactions and applications.
18 |
19 | ## Key Cryptographic Operations
20 |
21 | This section includes a placeholder for detailed content about the BSV SDK cryptographic operations.
22 |
23 | ## Core Features
24 |
25 | - Key generation and management
26 | - Digital signatures (ECDSA)
27 | - Message signing and verification
28 | - Encryption and decryption
29 | - Hash functions (SHA-256, RIPEMD-160, etc.)
30 |
31 | ## Best Practices
32 |
33 | 1. **Key Security**: Always handle private keys securely
34 | 2. **Random Number Generation**: Use cryptographically secure random number generation
35 | 3. **Testing**: Verify cryptographic operations with known test vectors
36 |
37 | For complete API documentation and additional cryptographic features, refer to the official BSV SDK documentation.
38 | `;
39 |
40 | /**
41 | * Register the BSV SDK Cryptography prompt with the MCP server
42 | * @param server The MCP server instance
43 | */
44 | export function registerCryptographyPrompt(server: McpServer): void {
45 | server.prompt(
46 | "bitcoin_sv_sdk_cryptography",
47 | "Detailed information about the cryptographic functionality in the BSV SDK, including key generation, signing, encryption, and hashing.",
48 | async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
49 | return {
50 | messages: [
51 | {
52 | role: "assistant",
53 | content: {
54 | type: "text",
55 | text: BSV_SDK_CRYPTOGRAPHY_PROMPT,
56 | },
57 | },
58 | ],
59 | };
60 | },
61 | );
62 | }
63 |
```
--------------------------------------------------------------------------------
/prompts/bsvSdk/primitives.ts:
--------------------------------------------------------------------------------
```typescript
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 |
8 | /**
9 | * BSV SDK Primitives Prompt
10 | *
11 | * Provides detailed information about the primitive data types and structures in the BSV SDK,
12 | * including Binary, Hex, Points, and other fundamental types.
13 | */
14 | export const BSV_SDK_PRIMITIVES_PROMPT = `
15 | # BSV SDK - Primitives Module
16 |
17 | The Primitives module in the BSV SDK provides fundamental data types and structures that form the building blocks for working with Bitcoin transactions and blockchain data.
18 |
19 | ## Core Primitive Types
20 |
21 | This section includes a placeholder for detailed content about the primitive types available in the BSV SDK.
22 |
23 | ## Key Primitives
24 |
25 | - Binary data handling
26 | - Hex string conversion
27 | - Point and curve operations
28 | - Bitcoin-specific data structures
29 | - Network message formats
30 |
31 | ## Common Operations
32 |
33 | - Serialization and deserialization
34 | - Type conversion
35 | - Data validation
36 | - Encoding and decoding
37 |
38 | ## Best Practices
39 |
40 | 1. **Type Safety**: Use appropriate types for Bitcoin operations
41 | 2. **Validation**: Validate input data before processing
42 | 3. **Performance**: Consider performance implications when working with large data structures
43 |
44 | For complete API documentation and additional information about primitives, refer to the official BSV SDK documentation.
45 | `;
46 |
47 | /**
48 | * Register the BSV SDK Primitives prompt with the MCP server
49 | * @param server The MCP server instance
50 | */
51 | export function registerPrimitivesPrompt(server: McpServer): void {
52 | server.prompt(
53 | "bitcoin_sv_sdk_primitives",
54 | "Detailed information about the primitive data types and structures in the BSV SDK, including Binary, Hex, Points, and other fundamental types.",
55 | async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
56 | return {
57 | messages: [
58 | {
59 | role: "assistant",
60 | content: {
61 | type: "text",
62 | text: BSV_SDK_PRIMITIVES_PROMPT,
63 | },
64 | },
65 | ],
66 | };
67 | },
68 | );
69 | }
70 |
```
--------------------------------------------------------------------------------
/prompts/bsvSdk/script.ts:
--------------------------------------------------------------------------------
```typescript
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 |
8 | /**
9 | * BSV SDK Script Prompt
10 | *
11 | * Provides detailed information about the script functionality in the BSV SDK,
12 | * including Bitcoin Script operations, locking and unlocking scripts, and OP_CODES.
13 | */
14 | export const BSV_SDK_SCRIPT_PROMPT = `
15 | # BSV SDK - Script Module
16 |
17 | The Script module in the BSV SDK provides comprehensive tools for working with Bitcoin Script, the programming language used to specify conditions for spending Bitcoin.
18 |
19 | ## Bitcoin Script Basics
20 |
21 | This section includes a placeholder for detailed content about Bitcoin Script and its implementation in the BSV SDK.
22 |
23 | ## Core Features
24 |
25 | - Creating and manipulating scripts
26 | - Locking script (scriptPubKey) creation
27 | - Unlocking script (scriptSig) creation
28 | - Script verification and execution
29 | - Support for all Bitcoin OP_CODES
30 |
31 | ## Common Script Types
32 |
33 | - P2PKH (Pay to Public Key Hash)
34 | - P2PK (Pay to Public Key)
35 | - P2MS (Multi-signature)
36 | - OP_RETURN (Data storage)
37 | - Custom scripts
38 |
39 | ## Best Practices
40 |
41 | 1. **Testing**: Test scripts thoroughly before production use
42 | 2. **Security**: Be aware of potential script vulnerabilities
43 | 3. **Compatibility**: Ensure scripts are compatible with network rules
44 |
45 | For complete API documentation and additional script features, refer to the official BSV SDK documentation.
46 | `;
47 |
48 | /**
49 | * Register the BSV SDK Script prompt with the MCP server
50 | * @param server The MCP server instance
51 | */
52 | export function registerScriptPrompt(server: McpServer): void {
53 | server.prompt(
54 | "bitcoin_sv_sdk_script",
55 | "Detailed information about the script functionality in the BSV SDK, including Bitcoin Script operations, locking and unlocking scripts, and OP_CODES.",
56 | async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
57 | return {
58 | messages: [
59 | {
60 | role: "assistant",
61 | content: {
62 | type: "text",
63 | text: BSV_SDK_SCRIPT_PROMPT,
64 | },
65 | },
66 | ],
67 | };
68 | },
69 | );
70 | }
71 |
```
--------------------------------------------------------------------------------
/tools/bsv/getPrice.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { z } from "zod";
3 |
4 | // Define cache duration (5 minutes in milliseconds)
5 | const PRICE_CACHE_DURATION = 5 * 60 * 1000;
6 |
7 | // Cache object to store price data
8 | let cachedPrice: { value: number; timestamp: number } | null = null;
9 |
10 | /**
11 | * Get the BSV price with caching mechanism
12 | * @returns The current BSV price in USD
13 | */
14 | async function getBsvPriceWithCache(): Promise<number> {
15 | // Return cached price if it's still valid
16 | if (
17 | cachedPrice &&
18 | Date.now() - cachedPrice.timestamp < PRICE_CACHE_DURATION
19 | ) {
20 | return cachedPrice.value;
21 | }
22 |
23 | // If no valid cache, fetch new price
24 | const res = await fetch(
25 | "https://api.whatsonchain.com/v1/bsv/main/exchangerate",
26 | );
27 | if (!res.ok) throw new Error("Failed to fetch price");
28 |
29 | const data = (await res.json()) as {
30 | currency: string;
31 | rate: string;
32 | time: number;
33 | };
34 |
35 | const price = Number(data.rate);
36 | if (Number.isNaN(price) || price <= 0)
37 | throw new Error("Invalid price received");
38 |
39 | // Update cache
40 | cachedPrice = {
41 | value: price,
42 | timestamp: Date.now(),
43 | };
44 |
45 | return price;
46 | }
47 |
48 | /**
49 | * Register the BSV price lookup tool
50 | * @param server The MCP server instance
51 | */
52 | export function registerGetPriceTool(server: McpServer): void {
53 | server.tool(
54 | "bsv_getPrice",
55 | "Retrieves the current price of Bitcoin SV (BSV) in USD from a reliable exchange API. This tool provides real-time market data that can be used for calculating transaction values, monitoring market conditions, or converting between BSV and fiat currencies.",
56 | {
57 | args: z
58 | .object({})
59 | .optional()
60 | .describe(
61 | "No parameters required - simply returns the current BSV price in USD",
62 | ),
63 | },
64 | async () => {
65 | try {
66 | const price = await getBsvPriceWithCache();
67 | return {
68 | content: [
69 | {
70 | type: "text",
71 | text: `Current BSV price: $${price.toFixed(2)} USD`,
72 | },
73 | ],
74 | };
75 | } catch (err) {
76 | return {
77 | content: [{ type: "text", text: "Error fetching BSV price." }],
78 | isError: true,
79 | };
80 | }
81 | },
82 | );
83 | }
84 |
85 | // Export the cached price getter for use in other modules
86 | export { getBsvPriceWithCache };
87 |
```
--------------------------------------------------------------------------------
/tools/utils/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import { z } from "zod";
3 | import { convertData } from "./conversion";
4 |
5 | const encodingSchema = z.enum(["utf8", "hex", "base64", "binary"]);
6 |
7 | /**
8 | * Register the unified conversion tool with the MCP server
9 | * @param server The MCP server instance
10 | */
11 | export function registerUtilsTools(server: McpServer): void {
12 | server.tool(
13 | "utils_convertData",
14 | "Converts data between different encodings (utf8, hex, base64, binary). Useful for transforming data formats when working with blockchain data, encryption, or file processing.\n\n" +
15 | "Parameters:\n" +
16 | "- data (required): The string to convert\n" +
17 | "- from (required): Source encoding format (utf8, hex, base64, or binary)\n" +
18 | "- to (required): Target encoding format (utf8, hex, base64, or binary)\n\n" +
19 | "Example usage:\n" +
20 | '- UTF-8 to hex: {"data": "hello world", "from": "utf8", "to": "hex"} → 68656c6c6f20776f726c64\n' +
21 | '- UTF-8 to base64: {"data": "Hello World", "from": "utf8", "to": "base64"} → SGVsbG8gV29ybGQ=\n' +
22 | '- base64 to UTF-8: {"data": "SGVsbG8gV29ybGQ=", "from": "base64", "to": "utf8"} → Hello World\n' +
23 | '- hex to base64: {"data": "68656c6c6f20776f726c64", "from": "hex", "to": "base64"} → aGVsbG8gd29ybGQ=\n\n' +
24 | "Notes:\n" +
25 | "- All parameters are required\n" +
26 | "- The tool returns the converted data as a string\n" +
27 | "- For binary conversion, data is represented as an array of byte values",
28 | {
29 | args: z.object({
30 | data: z.string().describe("The data string to be converted"),
31 | from: encodingSchema.describe(
32 | "Source encoding format (utf8, hex, base64, or binary)",
33 | ),
34 | to: encodingSchema.describe(
35 | "Target encoding format to convert to (utf8, hex, base64, or binary)",
36 | ),
37 | }),
38 | },
39 | async ({ args }) => {
40 | try {
41 | const result = convertData({
42 | data: args.data,
43 | from: args.from,
44 | to: args.to,
45 | });
46 | return {
47 | content: [
48 | {
49 | type: "text",
50 | text: result,
51 | },
52 | ],
53 | };
54 | } catch (err: unknown) {
55 | const msg = err instanceof Error ? err.message : String(err);
56 | return {
57 | content: [
58 | {
59 | type: "text",
60 | text: `Error: ${msg}`,
61 | },
62 | ],
63 | isError: true,
64 | };
65 | }
66 | },
67 | );
68 | }
69 |
```
--------------------------------------------------------------------------------
/tools/wallet/refreshUtxos.ts:
--------------------------------------------------------------------------------
```typescript
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 { toBitcoin } from "satoshi-token";
8 | import { z } from "zod";
9 | import type { Wallet } from "./wallet";
10 |
11 | const refreshUtxosArgsSchema = z.object({});
12 | type RefreshUtxosArgs = z.infer<typeof refreshUtxosArgsSchema>;
13 |
14 | /**
15 | * Registers the wallet_refreshUtxos tool that refreshes and returns the UTXOs for the wallet
16 | */
17 | export function registerRefreshUtxosTool(server: McpServer, wallet: Wallet) {
18 | server.tool(
19 | "wallet_refreshUtxos",
20 | "Refreshes and returns the wallet's UTXOs. This is useful for debugging UTXO issues, ensuring the wallet has the latest transaction outputs, and for verifying available funds before making transactions.",
21 | { args: refreshUtxosArgsSchema },
22 | async (
23 | { args }: { args: RefreshUtxosArgs },
24 | _extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
25 | ) => {
26 | try {
27 | // Force refresh the UTXOs
28 | await wallet.refreshUtxos();
29 |
30 | // Get the refreshed UTXOs
31 | const { paymentUtxos, nftUtxos } = await wallet.getUtxos();
32 |
33 | // Calculate total satoshis in payment UTXOs
34 | const totalSatoshis = paymentUtxos.reduce(
35 | (sum, utxo) => sum + utxo.satoshis,
36 | 0,
37 | );
38 |
39 | // Format the response
40 | return {
41 | content: [
42 | {
43 | type: "text",
44 | text: JSON.stringify(
45 | {
46 | status: "success",
47 | paymentUtxos: paymentUtxos.map((utxo) => ({
48 | txid: utxo.txid,
49 | vout: utxo.vout,
50 | satoshis: utxo.satoshis,
51 | outpoint: `${utxo.txid}_${utxo.vout}`,
52 | })),
53 | nftUtxos: nftUtxos.map((utxo) => ({
54 | txid: utxo.txid,
55 | vout: utxo.vout,
56 | origin: utxo.origin,
57 | outpoint: `${utxo.txid}_${utxo.vout}`,
58 | })),
59 | totalPaymentUtxos: paymentUtxos.length,
60 | totalNftUtxos: nftUtxos.length,
61 | totalSatoshis: totalSatoshis,
62 | totalBsv: toBitcoin(totalSatoshis),
63 | },
64 | null,
65 | 2,
66 | ),
67 | },
68 | ],
69 | };
70 | } catch (err: unknown) {
71 | const msg = err instanceof Error ? err.message : String(err);
72 | return { content: [{ type: "text", text: msg }], isError: true };
73 | }
74 | },
75 | );
76 | }
77 |
```
--------------------------------------------------------------------------------
/tools/wallet/sendToAddress.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { P2PKH } 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 { toSatoshi } from "satoshi-token";
5 | import type { z } from "zod";
6 | import { getBsvPriceWithCache } from "../bsv/getPrice";
7 | import { sendToAddressArgsSchema } from "./schemas";
8 | import type { Wallet } from "./wallet";
9 |
10 | // Use the schema imported from schemas.ts
11 | export type SendToAddressArgs = z.infer<typeof sendToAddressArgsSchema>;
12 |
13 | /**
14 | * Register the sendToAddress tool
15 | */
16 | export function registerSendToAddressTool(server: McpServer, wallet: Wallet) {
17 | server.tool(
18 | "wallet_sendToAddress",
19 | "Sends Bitcoin SV (BSV) to a specified address. This tool supports payments in both BSV and USD amounts (with automatic conversion using current exchange rates). Transaction fees are automatically calculated and a confirmation with transaction ID is returned upon success.",
20 | {
21 | args: sendToAddressArgsSchema,
22 | },
23 | async (
24 | { args }: { args: SendToAddressArgs },
25 | extra: RequestHandlerExtra,
26 | ) => {
27 | try {
28 | const {
29 | address,
30 | amount,
31 | currency = "BSV",
32 | description = "Send to address",
33 | } = args;
34 |
35 | // Convert to satoshis
36 | let satoshis: number;
37 | if (currency === "USD") {
38 | // Get current BSV price
39 | const bsvPriceUsd = await getBsvPriceWithCache();
40 |
41 | // Convert USD to BSV
42 | const bsvAmount = amount / bsvPriceUsd;
43 |
44 | // Convert BSV to satoshis using the library
45 | satoshis = toSatoshi(bsvAmount);
46 | } else {
47 | // Convert BSV to satoshis using the library
48 | satoshis = toSatoshi(amount);
49 | }
50 |
51 | // Create P2PKH script from address
52 | const lockingScript = new P2PKH().lock(address);
53 |
54 | // Create the transaction
55 | const tx = await wallet.createAction({
56 | description,
57 | outputs: [
58 | {
59 | lockingScript: lockingScript.toHex(),
60 | satoshis,
61 | outputDescription: `Payment to ${address}`,
62 | },
63 | ],
64 | });
65 |
66 | return {
67 | content: [
68 | {
69 | type: "text",
70 | text: JSON.stringify({
71 | status: "success",
72 | txid: tx.txid,
73 | satoshis,
74 | }),
75 | },
76 | ],
77 | };
78 | } catch (error) {
79 | return {
80 | content: [
81 | {
82 | type: "text",
83 | text: error instanceof Error ? error.message : String(error),
84 | },
85 | ],
86 | isError: true,
87 | };
88 | }
89 | },
90 | );
91 | }
92 |
```
--------------------------------------------------------------------------------
/tools/ordinals/getInscription.ts:
--------------------------------------------------------------------------------
```typescript
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 get inscription arguments
6 | export const getInscriptionArgsSchema = z.object({
7 | outpoint: z.string().describe("Outpoint in format 'txid_vout'"),
8 | });
9 |
10 | export type GetInscriptionArgs = z.infer<typeof getInscriptionArgsSchema>;
11 |
12 | // Inscription API response type
13 | interface InscriptionResponse {
14 | outpoint: string;
15 | origin: {
16 | outpoint: string;
17 | data?: {
18 | insc?: {
19 | text?: string;
20 | json?: unknown;
21 | file?: {
22 | hash?: string;
23 | size?: number;
24 | type?: string;
25 | };
26 | };
27 | };
28 | };
29 | height?: number;
30 | idx?: number;
31 | satoshis?: number;
32 | script?: string;
33 | spend?: string;
34 | [key: string]: unknown;
35 | }
36 |
37 | /**
38 | * Register the Ordinals inscription lookup tool
39 | */
40 | export function registerGetInscriptionTool(server: McpServer): void {
41 | server.tool(
42 | "ordinals_getInscription",
43 | "Retrieves detailed information about a specific ordinal inscription by its outpoint. Returns complete inscription data including content type, file information, inscription origin, and current status. Useful for verifying NFT authenticity or retrieving metadata about digital artifacts.",
44 | {
45 | args: getInscriptionArgsSchema,
46 | },
47 | async (
48 | { args }: { args: GetInscriptionArgs },
49 | extra: RequestHandlerExtra,
50 | ) => {
51 | try {
52 | const { outpoint } = args;
53 |
54 | // Validate outpoint format
55 | if (!/^[0-9a-f]{64}_\d+$/i.test(outpoint)) {
56 | throw new Error("Invalid outpoint format. Expected 'txid_vout'");
57 | }
58 |
59 | // Fetch inscription data from GorillaPool API
60 | const response = await fetch(
61 | `https://ordinals.gorillapool.io/api/inscriptions/${outpoint}`,
62 | );
63 |
64 | if (response.status === 404) {
65 | return {
66 | content: [
67 | {
68 | type: "text",
69 | text: JSON.stringify({ error: "Inscription not found" }),
70 | },
71 | ],
72 | };
73 | }
74 |
75 | if (!response.ok) {
76 | throw new Error(
77 | `API error: ${response.status} ${response.statusText}`,
78 | );
79 | }
80 |
81 | const data = (await response.json()) as InscriptionResponse;
82 |
83 | return {
84 | content: [
85 | {
86 | type: "text",
87 | text: JSON.stringify(data, null, 2),
88 | },
89 | ],
90 | };
91 | } catch (error) {
92 | return {
93 | content: [
94 | {
95 | type: "text",
96 | text: error instanceof Error ? error.message : String(error),
97 | },
98 | ],
99 | isError: true,
100 | };
101 | }
102 | },
103 | );
104 | }
105 |
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
1 | build:
2 | dockerBuildPath: ./
3 |
4 | startCommand:
5 | type: stdio
6 | configSchema:
7 | type: object
8 | properties:
9 | privateKeyWif:
10 | type: string
11 | title: "Private Key (WIF)"
12 | description: "The private key WIF (Wallet Import Format) for Bitcoin SV transactions. Optional but required for wallet operations. Without this, the server runs in limited mode with only educational resources and non-wallet tools."
13 | disablePrompts:
14 | type: boolean
15 | title: "Disable Prompts"
16 | description: "Set to true to disable all educational prompts"
17 | default: false
18 | disableResources:
19 | type: boolean
20 | title: "Disable Resources"
21 | description: "Set to true to disable all resources (BRCs, changelog)"
22 | default: false
23 | disableTools:
24 | type: boolean
25 | title: "Disable All Tools"
26 | description: "Set to true to disable all tools"
27 | default: false
28 | disableWalletTools:
29 | type: boolean
30 | title: "Disable Wallet Tools"
31 | description: "Set to true to disable Bitcoin wallet tools"
32 | default: false
33 | disableMneeTools:
34 | type: boolean
35 | title: "Disable MNEE Tools"
36 | description: "Set to true to disable MNEE token tools"
37 | default: false
38 | disableBsvTools:
39 | type: boolean
40 | title: "Disable BSV Blockchain Tools"
41 | description: "Set to true to disable BSV blockchain tools"
42 | default: false
43 | disableOrdinalsTools:
44 | type: boolean
45 | title: "Disable Ordinals Tools"
46 | description: "Set to true to disable Ordinals/NFT tools"
47 | default: false
48 | disableUtilsTools:
49 | type: boolean
50 | title: "Disable Utility Tools"
51 | description: "Set to true to disable utility tools"
52 | default: false
53 | additionalProperties: false
54 | commandFunction: |
55 | (config) => {
56 | const env = {};
57 |
58 | // Add private key if provided
59 | if (config.privateKeyWif) {
60 | env.PRIVATE_KEY_WIF = config.privateKeyWif;
61 | }
62 |
63 | // Map boolean config options to environment variables
64 | if (config.disablePrompts) env.DISABLE_PROMPTS = 'true';
65 | if (config.disableResources) env.DISABLE_RESOURCES = 'true';
66 | if (config.disableTools) env.DISABLE_TOOLS = 'true';
67 | if (config.disableWalletTools) env.DISABLE_WALLET_TOOLS = 'true';
68 | if (config.disableMneeTools) env.DISABLE_MNEE_TOOLS = 'true';
69 | if (config.disableBsvTools) env.DISABLE_BSV_TOOLS = 'true';
70 | if (config.disableOrdinalsTools) env.DISABLE_ORDINALS_TOOLS = 'true';
71 | if (config.disableUtilsTools) env.DISABLE_UTILS_TOOLS = 'true';
72 |
73 | return {
74 | command: 'bun',
75 | args: ['run', 'index.ts'],
76 | env
77 | };
78 | }
79 |
```
--------------------------------------------------------------------------------
/tools/mnee/sendMnee.ts:
--------------------------------------------------------------------------------
```typescript
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 {
5 | ServerNotification,
6 | ServerRequest,
7 | } from "@modelcontextprotocol/sdk/types.js";
8 | import type Mnee from "mnee";
9 | import type { SendMNEE, TransferResponse } from "mnee";
10 | import { z } from "zod";
11 |
12 | /**
13 | * Schema for the sendMnee tool arguments.
14 | */
15 | export const sendMneeArgsSchema = z.object({
16 | address: z.string().describe("The recipient's address"),
17 | amount: z.number().describe("Amount to send"),
18 | currency: z
19 | .enum(["MNEE", "USD"])
20 | .default("MNEE")
21 | .describe("Currency of the amount (MNEE or USD)"),
22 | });
23 |
24 | export type SendMneeArgs = z.infer<typeof sendMneeArgsSchema>;
25 |
26 | /**
27 | * Format a number as USD
28 | */
29 | function formatUSD(amount: number): string {
30 | return new Intl.NumberFormat("en-US", {
31 | style: "currency",
32 | currency: "USD",
33 | minimumFractionDigits: 2,
34 | maximumFractionDigits: 2,
35 | }).format(amount);
36 | }
37 |
38 | /**
39 | * Registers the mnee_sendMnee tool for sending MNEE tokens
40 | */
41 | export function registerSendMneeTool(server: McpServer, mnee: Mnee): void {
42 | server.tool(
43 | "mnee_sendMnee",
44 | "Send MNEE tokens to a specified address",
45 | { args: sendMneeArgsSchema },
46 | async (
47 | { args }: { args: SendMneeArgs },
48 | extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
49 | ): Promise<CallToolResult> => {
50 | try {
51 | // Since 1 MNEE = $1, the amount is the same in both currencies
52 | const mneeAmount = args.amount;
53 |
54 | const transferRequest: SendMNEE[] = [
55 | {
56 | address: args.address,
57 | amount: mneeAmount,
58 | },
59 | ];
60 |
61 | // Get WIF from environment
62 | const wif = process.env.PRIVATE_KEY_WIF;
63 | if (!wif) {
64 | return {
65 | content: [
66 | {
67 | type: "text",
68 | text: JSON.stringify(
69 | {
70 | success: false,
71 | error: "No private key available",
72 | message:
73 | "Please set PRIVATE_KEY_WIF environment variable with a valid Bitcoin SV private key in WIF format.",
74 | },
75 | null,
76 | 2,
77 | ),
78 | },
79 | ],
80 | isError: true,
81 | };
82 | }
83 |
84 | const result: TransferResponse = await mnee.transfer(
85 | transferRequest,
86 | wif,
87 | );
88 |
89 | if (result.error) {
90 | throw new Error(result.error);
91 | }
92 |
93 | return {
94 | content: [
95 | {
96 | type: "text",
97 | text: JSON.stringify(
98 | {
99 | success: true,
100 | txid: result.txid,
101 | rawtx: result.rawtx,
102 | mneeAmount: mneeAmount,
103 | usdAmount: formatUSD(mneeAmount),
104 | recipient: args.address,
105 | },
106 | null,
107 | 2,
108 | ),
109 | },
110 | ],
111 | };
112 | } catch (error) {
113 | const msg = error instanceof Error ? error.message : String(error);
114 | return { content: [{ type: "text", text: msg }], isError: true };
115 | }
116 | },
117 | );
118 | }
119 |
```
--------------------------------------------------------------------------------
/tools/ordinals/getTokenByIdOrTicker.ts:
--------------------------------------------------------------------------------
```typescript
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 get token by ID or ticker arguments
6 | export const getTokenByIdOrTickerArgsSchema = z
7 | .object({
8 | id: z
9 | .string()
10 | .optional()
11 | .describe("BSV20 token ID in outpoint format (txid_vout)"),
12 | tick: z.string().optional().describe("BSV20 token ticker symbol"),
13 | })
14 | .refine((data) => data.id || data.tick, {
15 | message: "Either id or tick must be provided",
16 | });
17 |
18 | export type GetTokenByIdOrTickerArgs = z.infer<
19 | typeof getTokenByIdOrTickerArgsSchema
20 | >;
21 |
22 | // BSV20 token response type
23 | interface TokenResponse {
24 | id: string;
25 | tick?: string;
26 | sym?: string;
27 | max?: string;
28 | lim?: string;
29 | dec?: number;
30 | supply?: string;
31 | amt?: string;
32 | status?: number;
33 | icon?: string;
34 | height?: number;
35 | [key: string]: unknown;
36 | }
37 |
38 | /**
39 | * Register the BSV20 token lookup tool
40 | */
41 | export function registerGetTokenByIdOrTickerTool(server: McpServer): void {
42 | server.tool(
43 | "ordinals_getTokenByIdOrTicker",
44 | "Retrieves detailed information about a specific BSV-20 token by its ID or ticker symbol. Returns complete token data including ticker symbol, supply information, decimals, and current status. This tool is useful for verifying token authenticity or checking supply metrics.",
45 | {
46 | args: getTokenByIdOrTickerArgsSchema,
47 | },
48 | async (
49 | { args }: { args: GetTokenByIdOrTickerArgs },
50 | extra: RequestHandlerExtra,
51 | ) => {
52 | try {
53 | const { id, tick } = args;
54 |
55 | // Validate that at least one of id or tick is provided
56 | if (!id && !tick) {
57 | throw new Error("Either token ID or ticker symbol must be provided");
58 | }
59 |
60 | // Validate ID format if provided
61 | if (id && !/^[0-9a-f]{64}_\d+$/i.test(id)) {
62 | throw new Error("Invalid BSV20 ID format. Expected 'txid_vout'");
63 | }
64 |
65 | // Determine which endpoint to use based on provided parameters
66 | let endpoint: string;
67 | if (id) {
68 | endpoint = `https://ordinals.gorillapool.io/api/bsv20/id/${id}`;
69 | } else {
70 | endpoint = `https://ordinals.gorillapool.io/api/bsv20/tick/${tick}`;
71 | }
72 |
73 | // Fetch BSV20 token data from GorillaPool API
74 | const response = await fetch(endpoint);
75 |
76 | if (response.status === 404) {
77 | return {
78 | content: [
79 | {
80 | type: "text",
81 | text: JSON.stringify({ error: "BSV20 token not found" }),
82 | },
83 | ],
84 | };
85 | }
86 |
87 | if (!response.ok) {
88 | throw new Error(
89 | `API error: ${response.status} ${response.statusText}`,
90 | );
91 | }
92 |
93 | const data = (await response.json()) as TokenResponse;
94 |
95 | return {
96 | content: [
97 | {
98 | type: "text",
99 | text: JSON.stringify(data, null, 2),
100 | },
101 | ],
102 | };
103 | } catch (error) {
104 | return {
105 | content: [
106 | {
107 | type: "text",
108 | text: error instanceof Error ? error.message : String(error),
109 | },
110 | ],
111 | isError: true,
112 | };
113 | }
114 | },
115 | );
116 | }
117 |
```
--------------------------------------------------------------------------------
/prompts/bsvSdk/overview.ts:
--------------------------------------------------------------------------------
```typescript
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 |
8 | /**
9 | * BSV SDK Overview Prompt
10 | *
11 | * Provides a general overview of the Bitcoin SV SDK,
12 | * including what it is, its purpose, and its main components.
13 | */
14 | export const BSV_SDK_OVERVIEW_PROMPT = `
15 | # BSV SDK - Overview
16 |
17 | The BSV SDK is a comprehensive TypeScript/JavaScript library designed to provide a unified and modern
18 | layer for developing scalable applications on the Bitcoin SV blockchain. This SDK addresses limitations
19 | of previous tools by offering a fresh approach that adheres to the principles of SPV (Simplified Payment
20 | Verification) while ensuring privacy and scalability.
21 |
22 | ## Core Objectives
23 |
24 | - Provide a unified, modern API for Bitcoin SV development
25 | - Enable secure, peer-to-peer operations
26 | - Support SPV (Simplified Payment Verification) principles
27 | - Ensure privacy and scalability in blockchain applications
28 | - Simplify integration with the Bitcoin SV ecosystem
29 |
30 | ## Main Components
31 |
32 | The BSV SDK is organized into several key modules:
33 |
34 | 1. **Wallet**: Manage keys, addresses, and UTXOs
35 | 2. **Transaction**: Build and manipulate Bitcoin transactions
36 | 3. **Auth**: Authentication and identity protocols
37 | 4. **Cryptography**: Signing, encryption, and verification
38 | 5. **Script**: Bitcoin scripting and contract capabilities
39 | 6. **Primitives**: Core data types and structures
40 | 7. **Messages**: Network message handling
41 | 8. **Overlay Tools**: Additional utilities and extensions
42 |
43 | ## Getting Started
44 |
45 | To use the BSV SDK in your project:
46 |
47 | \`\`\`bash
48 | # Install with npm
49 | npm install @bsv/sdk
50 |
51 | # Or with yarn
52 | yarn add @bsv/sdk
53 | \`\`\`
54 |
55 | Then import the components you need:
56 |
57 | \`\`\`typescript
58 | import { PrivateKey, Transaction } from "@bsv/sdk";
59 | \`\`\`
60 |
61 | ## Use Cases
62 |
63 | - Wallet applications
64 | - Payment systems
65 | - Smart contract platforms
66 | - Token systems
67 | - Identity solutions
68 | - Data storage and verification
69 |
70 | ## Additional Resources
71 |
72 | For detailed information about specific components, please see the dedicated prompts for each module:
73 | - Wallet operations: Use prompt "bitcoin_sv_sdk_wallet"
74 | - Transaction building: Use prompt "bitcoin_sv_sdk_transaction"
75 | - Authentication: Use prompt "bitcoin_sv_sdk_auth"
76 | - Cryptography: Use prompt "bitcoin_sv_sdk_cryptography"
77 | - Scripting: Use prompt "bitcoin_sv_sdk_script"
78 | - Primitives: Use prompt "bitcoin_sv_sdk_primitives"
79 |
80 | For official documentation, visit the BSV Blockchain Libraries Project repository.
81 | `;
82 |
83 | /**
84 | * Register the BSV SDK Overview prompt with the MCP server
85 | * @param server The MCP server instance
86 | */
87 | export function registerOverviewPrompt(server: McpServer): void {
88 | server.prompt(
89 | "bitcoin_sv_sdk_overview",
90 | "General overview of the Bitcoin SV SDK, including its purpose and main components.",
91 | async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
92 | return {
93 | messages: [
94 | {
95 | role: "assistant",
96 | content: {
97 | type: "text",
98 | text: BSV_SDK_OVERVIEW_PROMPT,
99 | },
100 | },
101 | ],
102 | };
103 | },
104 | );
105 | }
106 |
```
--------------------------------------------------------------------------------
/tools/wallet/fetchPaymentUtxos.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { P2PKH, Transaction, Utils } from "@bsv/sdk";
2 | import type { Utxo } from "js-1sat-ord";
3 | const { toBase64 } = Utils;
4 |
5 | /**
6 | * Type definition for WhatsOnChain UTXO response
7 | */
8 | interface WhatsOnChainUtxo {
9 | tx_hash: string;
10 | tx_pos: number;
11 | value: number;
12 | height: number;
13 | address?: string;
14 | }
15 |
16 | /**
17 | * Fetches unspent transaction outputs (UTXOs) for a given address.
18 | * Only returns confirmed unspent outputs.
19 | *
20 | * @param address - The address to fetch UTXOs for
21 | * @returns Array of UTXOs or undefined if an error occurs
22 | */
23 | export async function fetchPaymentUtxos(
24 | address: string,
25 | ): Promise<Utxo[] | undefined> {
26 | if (!address) {
27 | console.error("fetchPaymentUtxos: No address provided");
28 | return undefined;
29 | }
30 |
31 | try {
32 | // Fetch UTXOs from WhatsOnChain API
33 | const response = await fetch(
34 | `https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent`,
35 | );
36 |
37 | if (!response.ok) {
38 | console.error(
39 | `WhatsOnChain API error: ${response.status} ${response.statusText}`,
40 | );
41 | return undefined;
42 | }
43 |
44 | const data = (await response.json()) as WhatsOnChainUtxo[];
45 |
46 | // Validate response format
47 | if (!Array.isArray(data)) {
48 | console.error("Invalid response format from WhatsOnChain API");
49 | return undefined;
50 | }
51 |
52 | // For testing purposes (FOR TESTING ONLY - REMOVE IN PRODUCTION)
53 | // const limitUTXOs = data.slice(0, 2);
54 |
55 | // Process each UTXO
56 | const utxos: (Utxo | null)[] = await Promise.all(
57 | data.map(async (utxo: WhatsOnChainUtxo) => {
58 | // Get the transaction hex to extract the correct script
59 | const script = await getScriptFromTransaction(
60 | utxo.tx_hash,
61 | utxo.tx_pos,
62 | );
63 |
64 | if (!script) {
65 | console.warn(
66 | `Could not get script for UTXO: ${utxo.tx_hash}:${utxo.tx_pos}`,
67 | );
68 | return null;
69 | }
70 |
71 | return {
72 | txid: utxo.tx_hash,
73 | vout: utxo.tx_pos,
74 | satoshis: utxo.value,
75 | script: script,
76 | };
77 | }),
78 | );
79 |
80 | // Filter out any null entries from failed processing
81 | const validUtxos = utxos.filter((utxo) => utxo !== null) as Utxo[];
82 |
83 | return validUtxos;
84 | } catch (error) {
85 | console.error("Error fetching payment UTXOs:", error);
86 | return undefined;
87 | }
88 | }
89 |
90 | /**
91 | * Gets the script from a transaction for a specific output index
92 | *
93 | * @param txid - The transaction ID
94 | * @param vout - The output index
95 | * @returns The script as hex string or undefined if an error occurs
96 | */
97 | async function getScriptFromTransaction(
98 | txid: string,
99 | vout: number,
100 | ): Promise<string | undefined> {
101 | try {
102 | const response = await fetch(
103 | `https://api.whatsonchain.com/v1/bsv/main/tx/${txid}/hex`,
104 | );
105 |
106 | if (!response.ok) {
107 | console.error(
108 | `WhatsOnChain API error fetching tx hex: ${response.status} ${response.statusText}`,
109 | );
110 | return undefined;
111 | }
112 |
113 | const txHex = await response.text();
114 | const tx = Transaction.fromHex(txHex);
115 | const output = tx.outputs[vout];
116 |
117 | if (!output) {
118 | console.error(`Output index ${vout} not found in transaction ${txid}`);
119 | return undefined;
120 | }
121 |
122 | return toBase64(output.lockingScript.toBinary());
123 | } catch (error) {
124 | console.error(
125 | `Error getting script for transaction ${txid}:${vout}:`,
126 | error,
127 | );
128 | return undefined;
129 | }
130 | }
131 |
```
--------------------------------------------------------------------------------
/prompts/ordinals.ts:
--------------------------------------------------------------------------------
```typescript
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 |
8 | /**
9 | * 1Sat Ordinals Prompt
10 | *
11 | * Provides comprehensive information about Bitcoin SV ordinals,
12 | * including what they are, how they work, and how to use them.
13 | */
14 | export const ORDINALS_PROMPT = `
15 | # 1Sat Ordinals - Comprehensive Guide
16 |
17 | Ordinals are a way to uniquely identify and track specific satoshis (the smallest unit of Bitcoin)
18 | on the blockchain. This concept allows for "inscriptions" - embedding data directly into a satoshi,
19 | effectively creating NFT-like functionality native to the Bitcoin protocol.
20 |
21 | ## Key Concepts
22 |
23 | 1. **Ordinal Theory**: Each satoshi has a unique position in the Bitcoin ledger, determined by the order
24 | in which they were mined.
25 |
26 | 2. **Inscriptions**: Content embedded directly into a specific satoshi. Can be any valid content type.
27 |
28 | 3. **On-chain Storage**: All ordinal data is stored immutably on the blockchain.
29 |
30 | ## BSV Ordinals (1Sat Ordinals) vs. BTC Ordinals
31 |
32 | - 1Sat Ordinals leverage the larger block sizes and lower fees of Bitcoin SV, making them more practical
33 | for storing meaningful data and media.
34 |
35 | - 1Sat Ordinals can store much larger inscriptions compared to BTC, enabling richer media and applications.
36 |
37 | - 1Sat Ordinals typically cost a fraction of what BTC ordinals cost to create and transfer.
38 |
39 | ## Creating Ordinals
40 |
41 | To create a BSV ordinal:
42 |
43 | 1. Choose the content to inscribe (image, text, audio, etc.)
44 | 2. Use a compatible wallet or service that supports ordinal creation
45 | 3. Pay the transaction fee to inscribe your content on-chain
46 | 4. Receive a unique ordinal ID that references your specific satoshi
47 |
48 | ## Transferring Ordinals
49 |
50 | Ordinals are transferred by sending the specific satoshi that contains the inscription. Compatible wallets
51 | ensure that when you transfer an ordinal, the specific satoshi containing the inscription is included in
52 | the transaction.
53 |
54 | ## Viewing Ordinals
55 |
56 | Ordinal inscriptions can be viewed through:
57 |
58 | 1. Specialized ordinal explorers
59 | 2. Compatible wallets with ordinal support
60 | 3. Marketplaces that support BSV ordinals
61 |
62 | ## Use Cases
63 |
64 | - Digital Art and Collectibles
65 | - Certificates of Authenticity
66 | - Domain Names
67 | - Documentation and Verification
68 | - Gaming Assets
69 | - Media Distribution
70 |
71 | ## Best Practices
72 |
73 | - Verify file sizes and transaction costs before inscribing
74 | - Use appropriate file formats optimized for on-chain storage
75 | - Keep private keys secure to maintain ownership of valuable ordinals
76 | - Consider using a specialized wallet for managing valuable ordinal collections
77 |
78 | For technical implementation details, refer to the official documentation and BSV ordinals standards.
79 | `;
80 |
81 | /**
82 | * Register the Ordinals prompt with the MCP server
83 | * @param server The MCP server instance
84 | */
85 | export function registerOrdinalsPrompt(server: McpServer): void {
86 | server.prompt(
87 | "bitcoin_sv_ordinals",
88 | "Comprehensive information about Bitcoin SV ordinals, including what they are, how they work, and how to use them.",
89 | async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
90 | return {
91 | messages: [
92 | {
93 | role: "assistant",
94 | content: {
95 | type: "text",
96 | text: ORDINALS_PROMPT,
97 | },
98 | },
99 | ],
100 | };
101 | },
102 | );
103 | }
104 |
```
--------------------------------------------------------------------------------
/tools/ordinals/searchInscriptions.ts:
--------------------------------------------------------------------------------
```typescript
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 search inscriptions arguments
6 | export const searchInscriptionsArgsSchema = z.object({
7 | limit: z
8 | .number()
9 | .int()
10 | .min(1)
11 | .max(100)
12 | .default(20)
13 | .describe("Number of results (1-100, default 20)"),
14 | offset: z.number().int().min(0).default(0).describe("Pagination offset"),
15 | dir: z
16 | .enum(["asc", "desc"])
17 | .default("desc")
18 | .describe("Sort direction (asc or desc)"),
19 | num: z.string().optional().describe("Inscription number"),
20 | origin: z.string().optional().describe("Origin outpoint"),
21 | address: z.string().optional().describe("Bitcoin address"),
22 | map: z.string().optional().describe("Map field"),
23 | terms: z.string().optional().describe("Search terms"),
24 | mime: z.string().optional().describe("MIME type filter"),
25 | });
26 |
27 | export type SearchInscriptionsArgs = z.infer<
28 | typeof searchInscriptionsArgsSchema
29 | >;
30 |
31 | // Simplified inscription response type
32 | interface InscriptionSearchResponse {
33 | results: Array<{
34 | outpoint: string;
35 | origin: {
36 | outpoint: string;
37 | data?: {
38 | insc?: {
39 | text?: string;
40 | file?: {
41 | type?: string;
42 | size?: number;
43 | };
44 | };
45 | };
46 | };
47 | height?: number;
48 | satoshis?: number;
49 | [key: string]: unknown;
50 | }>;
51 | total: number;
52 | }
53 |
54 | /**
55 | * Register the Ordinals search inscriptions tool
56 | */
57 | export function registerSearchInscriptionsTool(server: McpServer): void {
58 | server.tool(
59 | "ordinals_searchInscriptions",
60 | "Searches for Bitcoin SV ordinal inscriptions using flexible criteria. This powerful search tool supports filtering by address, inscription content, MIME type, MAP fields, and other parameters. Results include detailed information about each matched inscription. Ideal for discovering NFTs and exploring the ordinals ecosystem.",
61 | {
62 | args: searchInscriptionsArgsSchema,
63 | },
64 | async (
65 | { args }: { args: SearchInscriptionsArgs },
66 | extra: RequestHandlerExtra,
67 | ) => {
68 | try {
69 | const { limit, offset, dir, num, origin, address, map, terms, mime } =
70 | args;
71 |
72 | // Build the URL with query parameters
73 | const url = new URL(
74 | "https://ordinals.gorillapool.io/api/inscriptions/search",
75 | );
76 | url.searchParams.append("limit", limit.toString());
77 | url.searchParams.append("offset", offset.toString());
78 | url.searchParams.append("dir", dir);
79 |
80 | if (num) url.searchParams.append("num", num);
81 | if (origin) url.searchParams.append("origin", origin);
82 | if (address) url.searchParams.append("address", address);
83 | if (map) url.searchParams.append("map", map);
84 | if (terms) url.searchParams.append("terms", terms);
85 | if (mime) url.searchParams.append("mime", mime);
86 |
87 | // Fetch inscriptions data from GorillaPool API
88 | const response = await fetch(url.toString());
89 |
90 | if (!response.ok) {
91 | throw new Error(
92 | `API error: ${response.status} ${response.statusText}`,
93 | );
94 | }
95 |
96 | const data = (await response.json()) as InscriptionSearchResponse;
97 |
98 | return {
99 | content: [
100 | {
101 | type: "text",
102 | text: JSON.stringify(data, null, 2),
103 | },
104 | ],
105 | };
106 | } catch (error) {
107 | return {
108 | content: [
109 | {
110 | type: "text",
111 | text: error instanceof Error ? error.message : String(error),
112 | },
113 | ],
114 | isError: true,
115 | };
116 | }
117 | },
118 | );
119 | }
120 |
```
--------------------------------------------------------------------------------
/tools/ordinals/marketSales.ts:
--------------------------------------------------------------------------------
```typescript
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 unified market sales arguments
6 | export const marketSalesArgsSchema = z.object({
7 | // Common parameters
8 | limit: z
9 | .number()
10 | .int()
11 | .min(1)
12 | .max(100)
13 | .default(20)
14 | .describe("Number of results (1-100, default 20)"),
15 | offset: z.number().int().min(0).default(0).describe("Pagination offset"),
16 | dir: z
17 | .enum(["asc", "desc"])
18 | .default("desc")
19 | .describe("Sort direction (asc or desc)"),
20 | address: z.string().optional().describe("Bitcoin address"),
21 |
22 | // Token-specific parameters
23 | tokenType: z
24 | .enum(["bsv20", "bsv21", "all"])
25 | .default("bsv20")
26 | .describe("Type of token to search for (bsv20, bsv21, or all)"),
27 | id: z.string().optional().describe("Token ID in outpoint format"),
28 | tick: z.string().optional().describe("Token ticker symbol"),
29 | pending: z
30 | .boolean()
31 | .default(false)
32 | .optional()
33 | .describe("Include pending sales"),
34 | });
35 |
36 | export type MarketSalesArgs = z.infer<typeof marketSalesArgsSchema>;
37 |
38 | // Unified response type for token sales
39 | interface MarketSaleResponse {
40 | results: Array<{
41 | outpoint: string;
42 | data?: {
43 | bsv20?: {
44 | id?: string;
45 | tick?: string;
46 | sym?: string;
47 | amt?: string;
48 | op?: string;
49 | };
50 | list?: {
51 | price?: number;
52 | payout?: string;
53 | sale?: boolean;
54 | };
55 | };
56 | satoshis?: number;
57 | height?: number;
58 | owner?: string;
59 | spend?: string;
60 | spendHeight?: number;
61 | spendIdx?: string;
62 | [key: string]: unknown;
63 | }>;
64 | total: number;
65 | }
66 |
67 | /**
68 | * Register the unified market sales tool
69 | * Handles BSV20 and BSV21 token sales
70 | */
71 | export function registerMarketSalesTool(server: McpServer): void {
72 | server.tool(
73 | "ordinals_marketSales",
74 | "Retrieves recent sales data for BSV-20 and BSV-21 tokens on the ordinals marketplace. This tool provides insights into market activity, including sale prices, transaction details, and token information. Supports filtering by token ID, ticker symbol, or seller address to help analyze market trends and track specific token sales.",
75 | {
76 | args: marketSalesArgsSchema,
77 | },
78 | async ({ args }: { args: MarketSalesArgs }, extra: RequestHandlerExtra) => {
79 | try {
80 | const { limit, offset, dir, tokenType, id, tick, pending, address } =
81 | args;
82 |
83 | // Determine the API endpoint based on tokenType
84 | let baseUrl = "https://ordinals.gorillapool.io/api";
85 |
86 | // Default to BSV20 for all token types
87 | baseUrl += "/bsv20/market/sales";
88 |
89 | // Build the URL with query parameters
90 | const url = new URL(baseUrl);
91 | url.searchParams.append("limit", limit.toString());
92 | url.searchParams.append("offset", offset.toString());
93 | url.searchParams.append("dir", dir);
94 |
95 | // Add type parameter for bsv21 if needed
96 | if (tokenType === "bsv21") {
97 | url.searchParams.append("type", "v2");
98 | }
99 |
100 | if (id) url.searchParams.append("id", id);
101 | if (tick) url.searchParams.append("tick", tick);
102 | if (pending !== undefined)
103 | url.searchParams.append("pending", pending.toString());
104 | if (address) url.searchParams.append("address", address);
105 |
106 | // Fetch market sales from GorillaPool API
107 | const response = await fetch(url.toString());
108 |
109 | if (!response.ok) {
110 | throw new Error(
111 | `API error: ${response.status} ${response.statusText}`,
112 | );
113 | }
114 |
115 | const data = (await response.json()) as MarketSaleResponse;
116 |
117 | return {
118 | content: [
119 | {
120 | type: "text",
121 | text: JSON.stringify(data, null, 2),
122 | },
123 | ],
124 | };
125 | } catch (error) {
126 | return {
127 | content: [
128 | {
129 | type: "text",
130 | text: error instanceof Error ? error.message : String(error),
131 | },
132 | ],
133 | isError: true,
134 | };
135 | }
136 | },
137 | );
138 | }
139 |
```
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env bun
2 | import { PrivateKey } from "@bsv/sdk";
3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5 | import { registerAllPrompts } from "./prompts/index.ts";
6 | import { registerResources } from "./resources/resources.ts";
7 | import { registerAllTools } from "./tools/index.ts";
8 | import { registerMneeTools } from "./tools/mnee/index.ts";
9 | import { registerWalletTools } from "./tools/wallet/tools.ts";
10 | import { Wallet } from "./tools/wallet/wallet.ts";
11 |
12 | /**
13 | * Configuration options from environment variables
14 | */
15 | const CONFIG = {
16 | // Whether to load various components
17 | loadPrompts: process.env.DISABLE_PROMPTS !== "true",
18 | loadResources: process.env.DISABLE_RESOURCES !== "true",
19 | loadTools: process.env.DISABLE_TOOLS !== "true",
20 |
21 | // Fine-grained tool category control
22 | loadWalletTools: process.env.DISABLE_WALLET_TOOLS !== "true",
23 | loadMneeTools: process.env.DISABLE_MNEE_TOOLS !== "true",
24 | loadBsvTools: process.env.DISABLE_BSV_TOOLS !== "true",
25 | loadOrdinalsTools: process.env.DISABLE_ORDINALS_TOOLS !== "true",
26 | loadUtilsTools: process.env.DISABLE_UTILS_TOOLS !== "true",
27 | loadA2bTools: process.env.ENABLE_A2B_TOOLS === "true",
28 |
29 | // Transaction broadcasting control
30 | disableBroadcasting: process.env.DISABLE_BROADCASTING === "true",
31 | };
32 |
33 | /**
34 | * Try to initialize the private key from environment variables
35 | * Returns the private key if valid, or undefined if not present or invalid
36 | */
37 | function initializePrivateKey(): PrivateKey | undefined {
38 | const privateKeyWif = process.env.PRIVATE_KEY_WIF;
39 |
40 | // Check if private key is set
41 | if (!privateKeyWif) {
42 | // stdio protocol you cant send anything to console!!
43 | // console.warn(
44 | // "\x1b[33mWarning: PRIVATE_KEY_WIF environment variable is not set\x1b[0m",
45 | // );
46 | // console.warn(
47 | // "The server will run, but wallet operations requiring a private key will return errors.",
48 | // );
49 | // console.warn(
50 | // "Set this variable with a valid Bitcoin SV private key in WIF format to enable all features:",
51 | // );
52 | // console.warn(
53 | // "Example: PRIVATE_KEY_WIF=your_private_key_wif bun run index.ts",
54 | // );
55 | return undefined;
56 | }
57 |
58 | // Validate the private key format
59 | try {
60 | return PrivateKey.fromWif(privateKeyWif);
61 | } catch (error) {
62 | // console.warn("\x1b[33mWarning: Invalid private key format\x1b[0m");
63 | // console.warn(
64 | // "The PRIVATE_KEY_WIF provided is not a valid Bitcoin SV private key in WIF format.",
65 | // );
66 | // console.warn(
67 | // "The server will run, but wallet operations requiring a private key will return errors.",
68 | // );
69 | return undefined;
70 | }
71 | }
72 |
73 | // Try to initialize private key but don't stop the server if missing or invalid
74 | const privKey = initializePrivateKey();
75 |
76 | const server = new McpServer(
77 | { name: "Bitcoin SV", version: "0.0.36" },
78 | {
79 | capabilities: {
80 | prompts: {},
81 | resources: {},
82 | tools: {},
83 | },
84 | instructions: `
85 | This server exposes Bitcoin SV helpers.
86 | Tools are idempotent unless marked destructive.
87 | `,
88 | },
89 | );
90 |
91 | // Initialize wallet and register tools based on configuration
92 | let wallet: Wallet | null = null;
93 |
94 | if (CONFIG.loadTools) {
95 | if (privKey) {
96 | // Register MNEE tools if enabled
97 | if (CONFIG.loadMneeTools) {
98 | registerMneeTools(server);
99 | }
100 |
101 | // Initialize wallet with the private key if wallet tools are enabled
102 | if (CONFIG.loadWalletTools) {
103 | wallet = new Wallet(privKey);
104 | registerWalletTools(server, wallet, {
105 | disableBroadcasting: CONFIG.disableBroadcasting,
106 | enableA2bTools: CONFIG.loadA2bTools,
107 | });
108 | }
109 | }
110 |
111 | // Register all other tools based on configuration
112 | registerAllTools(server, {
113 | enableBsvTools: CONFIG.loadBsvTools,
114 | enableOrdinalsTools: CONFIG.loadOrdinalsTools,
115 | enableUtilsTools: CONFIG.loadUtilsTools,
116 | enableA2bTools: CONFIG.loadA2bTools,
117 | });
118 | }
119 |
120 | // Register resources if enabled
121 | if (CONFIG.loadResources) {
122 | registerResources(server);
123 | }
124 |
125 | // Register prompts if enabled
126 | if (CONFIG.loadPrompts) {
127 | registerAllPrompts(server);
128 | }
129 |
130 | // Connect to the transport
131 | const transport = new StdioServerTransport();
132 | await server.connect(transport);
133 | console.error("BSV MCP Server running on stdio");
134 |
```
--------------------------------------------------------------------------------
/tools/wallet/transferOrdToken.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { PrivateKey } 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 {
9 | type Distribution,
10 | type LocalSigner,
11 | type Payment,
12 | type TokenChangeResult,
13 | TokenInputMode,
14 | TokenSelectionStrategy,
15 | TokenType,
16 | type TokenUtxo,
17 | type TransferOrdTokensConfig,
18 | type Utxo,
19 | selectTokenUtxos,
20 | transferOrdTokens,
21 | } from "js-1sat-ord";
22 | import { z } from "zod";
23 | import type { Wallet } from "./wallet";
24 |
25 | // Schema for BSV-20/BSV-21 token transfer arguments
26 | export const transferOrdTokenArgsSchema = z.object({
27 | protocol: z.enum(["bsv-20", "bsv-21"]),
28 | tokenID: z.string(),
29 | sendAmount: z.number(),
30 | paymentUtxos: z.array(
31 | z.object({
32 | txid: z.string(),
33 | vout: z.number(),
34 | satoshis: z.number(),
35 | script: z.string(),
36 | }),
37 | ),
38 | tokenUtxos: z.array(
39 | z.object({
40 | txid: z.string(),
41 | vout: z.number(),
42 | satoshis: z.literal(1),
43 | script: z.string(),
44 | amt: z.string(),
45 | id: z.string(),
46 | }),
47 | ),
48 | distributions: z.array(z.object({ address: z.string(), tokens: z.number() })),
49 | decimals: z.number(),
50 | additionalPayments: z
51 | .array(z.object({ to: z.string(), amount: z.number() }))
52 | .optional(),
53 | });
54 | export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>;
55 |
56 | /**
57 | * Register the wallet_transferOrdToken tool for transferring BSV tokens.
58 | */
59 | export function registerTransferOrdTokenTool(
60 | server: McpServer,
61 | wallet: Wallet,
62 | ) {
63 | server.tool(
64 | "wallet_transferOrdToken",
65 | "Transfers BSV-20 or BSV-21 tokens from your wallet via js-1sat-ord transferOrdTokens.",
66 | { args: transferOrdTokenArgsSchema },
67 | async (
68 | { args }: { args: TransferOrdTokenArgs },
69 | extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
70 | ) => {
71 | try {
72 | // fetch keys
73 | const paymentPk = wallet.getPrivateKey();
74 | if (!paymentPk) throw new Error("No private key available");
75 | const ordPk = paymentPk;
76 | const changeAddress = paymentPk.toAddress().toString();
77 | const ordAddress = changeAddress;
78 |
79 | // select token UTXOs
80 | const { selectedUtxos: inputTokens } = selectTokenUtxos(
81 | args.tokenUtxos as TokenUtxo[],
82 | args.sendAmount,
83 | args.decimals,
84 | {
85 | inputStrategy: TokenSelectionStrategy.SmallestFirst,
86 | outputStrategy: TokenSelectionStrategy.LargestFirst,
87 | },
88 | );
89 |
90 | // build config
91 | const config: TransferOrdTokensConfig = {
92 | protocol:
93 | args.protocol === "bsv-20" ? TokenType.BSV20 : TokenType.BSV21,
94 | tokenID: args.tokenID,
95 | utxos: args.paymentUtxos as Utxo[],
96 | inputTokens,
97 | distributions: args.distributions as Distribution[],
98 | tokenChangeAddress: ordAddress,
99 | changeAddress,
100 | paymentPk,
101 | ordPk,
102 | additionalPayments: (args.additionalPayments as Payment[]) || [],
103 | decimals: args.decimals,
104 | inputMode: TokenInputMode.Needed,
105 | splitConfig: {
106 | outputs: inputTokens.length === 1 ? 2 : 1,
107 | threshold: args.sendAmount,
108 | },
109 | };
110 |
111 | const identityPk = process.env.IDENTITY_KEY_WIF
112 | ? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
113 | : undefined;
114 |
115 | if (identityPk) {
116 | config.signer = {
117 | idKey: identityPk,
118 | } as LocalSigner;
119 | }
120 |
121 | // execute transfer
122 | const result: TokenChangeResult = await transferOrdTokens(config);
123 | const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
124 | if (!disableBroadcasting) {
125 | await result.tx.broadcast();
126 |
127 | // refresh UTXOs
128 | try {
129 | await wallet.refreshUtxos();
130 | } catch {}
131 |
132 | // respond
133 | return {
134 | content: [
135 | {
136 | type: "text",
137 | text: JSON.stringify({
138 | txid: result.tx.id("hex"),
139 | spentOutpoints: result.spentOutpoints,
140 | payChange: result.payChange,
141 | tokenChange: result.tokenChange,
142 | }),
143 | },
144 | ],
145 | };
146 | }
147 | return {
148 | content: [
149 | {
150 | type: "text",
151 | text: result.tx.toHex(),
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 | }
162 |
```
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
```markdown
1 | # BSV MCP Server Changelog
2 |
3 | ## v0.0.36 - A2B Overlay Integration & Improved MCP Server Publishing
4 |
5 | ### Features
6 | - **A2B Overlay Integration**: Implemented a robust connection to the A2B Overlay API
7 | - Updated `a2b_discover` tool to search for on-chain MCP servers and agents
8 | - Enhanced search capabilities with relevance-based result ranking
9 | - User-friendly formatted output with command suggestions
10 | - Support for filtering by type (agent/tool/all), block range, and free text search
11 | - **Improved MCP Server Publishing**: Enhanced the wallet_a2bPublishMcp tool
12 | - Better identity key integration via LocalSigner
13 | - More robust configuration for sigma signing
14 | - Improved transaction handling and error reporting
15 |
16 | ### Technical Improvements
17 | - Updated API endpoint to use production overlay service
18 | - Implemented enhanced search for better relevance scoring
19 | - Better error handling for API responses
20 | - Improved response formatting for readability
21 | - Updated type definitions for consistency
22 |
23 | ## v0.0.34 - Transaction Broadcast Control
24 |
25 | ### Features
26 | - Added `DISABLE_BROADCASTING` environment variable to control transaction broadcasting behavior
27 | - When set to "true", transactions are created but not broadcast to the network
28 | - Returns raw transaction hex instead of broadcasting, useful for testing and review
29 | - Code cleanup and organization improvements
30 |
31 | ## v0.0.33 - Identity Key Sigma Signing
32 |
33 | ### Features
34 | - Added optional `IDENTITY_KEY_WIF` environment variable for sigma-protocol signing.
35 | - `wallet_createOrdinals`, and `wallet_purchaseListing` tools now support signing with an identity key.
36 | - Updated `README.md` to document `IDENTITY_KEY_WIF` usage and JSON configuration examples.
37 |
38 | ## v0.0.32 - Reliability Improvements
39 |
40 | ### Bug Fixes
41 | - **Changelog Loading**: Improved changelog loading mechanism with multiple path resolution strategies
42 | - Added detailed logging for debugging path resolution issues
43 | - Better error reporting for troubleshooting in production environments
44 | - **Package Structure**: Enhanced package.json to include all necessary files in npm package
45 | - Added prompts, resources, and CHANGELOG.md to the published files list
46 | - Fixed import issues when running from installed npm package
47 |
48 | ## v0.0.29 - Enhanced Server Configuration & Maintenance
49 |
50 | ### Major Changes
51 | - **Improved Changelog Management**: Added changelog as a MCP resource so you can just ask what has changed between versions
52 | - Simplified maintenance with single source of truth for version history
53 | - Automatic updates to MCP resources when changelog is modified
54 | - **Expanded Component Configuration**: Can now configure which components of the MCP are loaded by setting env vars. See the readme for more information.
55 |
56 | ### Technical Improvements
57 | - Removed duplicate changelog content in code
58 | - Better error handling for resource loading
59 | - Code cleanup and organization improvements
60 |
61 | ## v0.0.25 - Improved Error Handling & Optional Private Key
62 |
63 | ### Major Changes
64 | - **Optional Private Key**: Server now starts without a PRIVATE_KEY_WIF environment variable
65 | - Educational resources and non-wallet tools remain available in limited mode
66 | - Wallet and MNEE tools gracefully fail with helpful error messages when no private key is provided
67 | - **Component Configuration**: Added environment variables to enable/disable specific components
68 | - Selectively enable/disable prompts, resources, or tools
69 | - Fine-grained control over which tool categories are loaded
70 | - **MNEE Token Support**: Added dedicated tools for MNEE token operations
71 | - Get balance, send tokens, and parse transactions
72 | - **Enhanced Documentation**: Added detailed prompt examples and improved troubleshooting guidance
73 | - **Resource Improvements**: Added BRC specifications and other reference materials
74 |
75 | ### Technical Improvements
76 | - Improved error handling throughout the codebase
77 | - Better initialization process for wallet component
78 | - Standardized error messages across tools
79 | - Expanded README with installation instructions for different platforms
80 | - Added npm alternatives to Bun commands
81 | - Added modular loading with configurable components
82 |
83 | ## v0.0.24 - Initial Public Release
84 |
85 | ### Features
86 | - Bitcoin SV wallet operations (send, receive, manage keys)
87 | - Ordinals creation and management
88 | - BSV blockchain interaction (transactions, blocks, addresses)
89 | - Cryptographic operations (signing, verification, encryption)
90 | - Educational prompts for BSV SDK and Ordinals
91 |
92 | ### Toolkit Overview
93 | - Wallet tools for core BSV operations
94 | - Ordinals tools for NFT functionality
95 | - BSV tools for blockchain interaction
96 | - MNEE token tools
97 | - Utility tools for data conversion
```
--------------------------------------------------------------------------------
/tools/bsv/token.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 | import {
3 | ReturnTypes,
4 | toBitcoin,
5 | toSatoshi,
6 | toToken,
7 | toTokenSat,
8 | } from "satoshi-token";
9 | import { z } from "zod";
10 |
11 | /**
12 | * Register token conversion tools for BSV
13 | * @param server The MCP server instance
14 | */
15 | export function registerTokenTools(server: McpServer): void {
16 | // Convert Bitcoin to Satoshis
17 | server.tool(
18 | "bsv_toSatoshi",
19 | {
20 | args: z.object({
21 | bitcoin: z.union([z.number(), z.string()]),
22 | returnType: z.enum(["number", "string", "bigint"]).optional(),
23 | }),
24 | },
25 | async ({ args }) => {
26 | try {
27 | const { bitcoin, returnType } = args;
28 | let result: number | string | bigint;
29 |
30 | switch (returnType) {
31 | case "bigint":
32 | result = toSatoshi(bitcoin, ReturnTypes.BigInt);
33 | return { content: [{ type: "text", text: result.toString() }] };
34 | case "string":
35 | result = toSatoshi(bitcoin, ReturnTypes.String);
36 | return { content: [{ type: "text", text: result }] };
37 | default:
38 | result = toSatoshi(bitcoin);
39 | return { content: [{ type: "text", text: result.toString() }] };
40 | }
41 | } catch (err: unknown) {
42 | const msg = err instanceof Error ? err.message : String(err);
43 | return { content: [{ type: "text", text: msg }], isError: true };
44 | }
45 | },
46 | );
47 |
48 | // Convert Satoshis to Bitcoin
49 | server.tool(
50 | "bsv_toBitcoin",
51 | {
52 | args: z.object({
53 | satoshis: z.union([z.number(), z.string(), z.bigint()]),
54 | returnType: z.enum(["number", "string", "bigint"]).optional(),
55 | }),
56 | },
57 | async ({ args }) => {
58 | try {
59 | const { satoshis, returnType } = args;
60 | let result: number | string | bigint;
61 |
62 | switch (returnType) {
63 | case "bigint":
64 | try {
65 | result = toBitcoin(satoshis, ReturnTypes.BigInt);
66 | return { content: [{ type: "text", text: result.toString() }] };
67 | } catch (e) {
68 | return {
69 | content: [
70 | {
71 | type: "text",
72 | text: "Error: Cannot return Bitcoin amount as BigInt if it has decimal part",
73 | },
74 | ],
75 | isError: true,
76 | };
77 | }
78 | case "string":
79 | result = toBitcoin(satoshis, ReturnTypes.String);
80 | return { content: [{ type: "text", text: result }] };
81 | default:
82 | result = toBitcoin(satoshis);
83 | return { content: [{ type: "text", text: result.toString() }] };
84 | }
85 | } catch (err: unknown) {
86 | const msg = err instanceof Error ? err.message : String(err);
87 | return { content: [{ type: "text", text: msg }], isError: true };
88 | }
89 | },
90 | );
91 |
92 | // Generic token conversion (for tokens with custom decimal places)
93 | server.tool(
94 | "bsv_toTokenSatoshi",
95 | {
96 | args: z.object({
97 | token: z.union([z.number(), z.string(), z.bigint()]),
98 | decimals: z.number().int().min(0),
99 | returnType: z.enum(["number", "string", "bigint"]).optional(),
100 | }),
101 | },
102 | async ({ args }) => {
103 | try {
104 | const { token, decimals, returnType } = args;
105 | let result: number | string | bigint;
106 |
107 | switch (returnType) {
108 | case "bigint":
109 | result = toTokenSat(token, decimals, ReturnTypes.BigInt);
110 | return { content: [{ type: "text", text: result.toString() }] };
111 | case "string":
112 | result = toTokenSat(token, decimals, ReturnTypes.String);
113 | return { content: [{ type: "text", text: result }] };
114 | default:
115 | result = toTokenSat(token, decimals);
116 | return { content: [{ type: "text", text: result.toString() }] };
117 | }
118 | } catch (err: unknown) {
119 | const msg = err instanceof Error ? err.message : String(err);
120 | return { content: [{ type: "text", text: msg }], isError: true };
121 | }
122 | },
123 | );
124 |
125 | // Generic token conversion (for tokens with custom decimal places)
126 | server.tool(
127 | "bsv_toToken",
128 | {
129 | args: z.object({
130 | tokenSatoshi: z.union([z.number(), z.string(), z.bigint()]),
131 | decimals: z.number().int().min(0),
132 | returnType: z.enum(["number", "string", "bigint"]).optional(),
133 | }),
134 | },
135 | async ({ args }) => {
136 | try {
137 | const { tokenSatoshi, decimals, returnType } = args;
138 | let result: number | string | bigint;
139 |
140 | switch (returnType) {
141 | case "bigint":
142 | try {
143 | result = toToken(tokenSatoshi, decimals, ReturnTypes.BigInt);
144 | return { content: [{ type: "text", text: result.toString() }] };
145 | } catch (e) {
146 | return {
147 | content: [
148 | {
149 | type: "text",
150 | text: "Error: Cannot return token amount as BigInt if it has decimal part",
151 | },
152 | ],
153 | isError: true,
154 | };
155 | }
156 | case "string":
157 | result = toToken(tokenSatoshi, decimals, ReturnTypes.String);
158 | return { content: [{ type: "text", text: result }] };
159 | default:
160 | result = toToken(tokenSatoshi, decimals);
161 | return { content: [{ type: "text", text: result.toString() }] };
162 | }
163 | } catch (err: unknown) {
164 | const msg = err instanceof Error ? err.message : String(err);
165 | return { content: [{ type: "text", text: msg }], isError: true };
166 | }
167 | },
168 | );
169 | }
170 |
```
--------------------------------------------------------------------------------
/tools/wallet/sendOrdinals.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { PrivateKey } 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 | CallToolResult,
6 | ServerNotification,
7 | ServerRequest,
8 | } from "@modelcontextprotocol/sdk/types.js";
9 | import { sendOrdinals } from "js-1sat-ord";
10 | import type {
11 | ChangeResult,
12 | LocalSigner,
13 | SendOrdinalsConfig,
14 | } from "js-1sat-ord";
15 | import { Sigma } from "sigma-protocol";
16 | import { z } from "zod";
17 | import type { Wallet } from "./wallet";
18 |
19 | /**
20 | * Schema for the sendOrdinals tool arguments
21 | */
22 | export const sendOrdinalsArgsSchema = z.object({
23 | // Outpoint of the inscription to send (txid_vout format)
24 | inscriptionOutpoint: z
25 | .string()
26 | .describe("Inscription outpoint in format txid_vout"),
27 | // Destination address to send the inscription to
28 | destinationAddress: z
29 | .string()
30 | .describe("Destination address for the inscription"),
31 | // Optional metadata for the ordinal transfer
32 | metadata: z
33 | .any()
34 | .optional()
35 | .describe("Optional MAP metadata for the transfer"),
36 | });
37 |
38 | export type SendOrdinalsArgs = z.infer<typeof sendOrdinalsArgsSchema>;
39 |
40 | /**
41 | * Registers the wallet_sendOrdinals tool for transferring ordinals
42 | */
43 | export function registerSendOrdinalsTool(server: McpServer, wallet: Wallet) {
44 | server.tool(
45 | "wallet_sendOrdinals",
46 | "Transfers ordinals (NFTs) from your wallet to another address on the Bitcoin SV blockchain. This tool enables sending inscriptions you own to any valid BSV address. The transaction is created, signed, and broadcast automatically, with appropriate fee calculation and change handling.",
47 | { args: sendOrdinalsArgsSchema },
48 | async (
49 | { args }: { args: SendOrdinalsArgs },
50 | extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
51 | ): Promise<CallToolResult> => {
52 | try {
53 | // 1. Get private key from wallet
54 | const paymentPk = wallet.getPrivateKey();
55 | if (!paymentPk) {
56 | throw new Error("No private key available in wallet");
57 | }
58 |
59 | // 2. Get payment UTXOs from wallet
60 | const { paymentUtxos, nftUtxos } = await wallet.getUtxos();
61 | if (!paymentUtxos || paymentUtxos.length === 0) {
62 | throw new Error(
63 | "No payment UTXOs available to fund this transaction",
64 | );
65 | }
66 |
67 | // 3. Get the wallet address for change
68 | const walletAddress = paymentPk.toAddress().toString();
69 |
70 | // 4. Parse the inscription outpoint
71 | const [txid, voutStr] = args.inscriptionOutpoint.split("_");
72 | if (!txid || !voutStr) {
73 | throw new Error(
74 | "Invalid inscription outpoint format. Expected txid_vout",
75 | );
76 | }
77 | const vout = Number.parseInt(voutStr, 10);
78 |
79 | // 5. Find the inscription in nftUtxos
80 | const inscription = nftUtxos.find(
81 | (utxo) => utxo.txid === txid && utxo.vout === vout,
82 | );
83 |
84 | if (!inscription) {
85 | throw new Error(
86 | `Inscription ${args.inscriptionOutpoint} not found in your wallet`,
87 | );
88 | }
89 |
90 | // 6. Create config and transfer the inscription
91 | const sendOrdinalsConfig: SendOrdinalsConfig = {
92 | paymentPk,
93 | paymentUtxos,
94 | ordinals: [inscription],
95 | destinations: [{ address: args.destinationAddress }],
96 | changeAddress: walletAddress,
97 | };
98 |
99 | const identityKeyWif = process.env.IDENTITY_KEY_WIF;
100 | const identityPk = identityKeyWif
101 | ? PrivateKey.fromWif(identityKeyWif)
102 | : undefined;
103 | if (identityPk) {
104 | sendOrdinalsConfig.signer = {
105 | idKey: identityPk,
106 | } as LocalSigner;
107 | }
108 |
109 | // Add metadata if provided
110 | if (args.metadata) {
111 | sendOrdinalsConfig.metaData = args.metadata;
112 | }
113 |
114 | // Using the wallet's key for both payment and ordinals
115 | sendOrdinalsConfig.ordPk = paymentPk;
116 |
117 | const result = await sendOrdinals(sendOrdinalsConfig);
118 | const changeResult = result as ChangeResult;
119 | // no signing when you send since we don't emit an inscription
120 |
121 | // 7. Broadcast the transaction
122 | const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
123 | if (!disableBroadcasting) {
124 | await changeResult.tx.broadcast();
125 |
126 | // 8. Refresh the wallet's UTXOs after spending
127 | try {
128 | await wallet.refreshUtxos();
129 | } catch (refreshError) {
130 | console.warn(
131 | "Failed to refresh UTXOs after transaction:",
132 | refreshError,
133 | );
134 | }
135 |
136 | // 9. Return transaction details
137 | return {
138 | content: [
139 | {
140 | type: "text",
141 | text: JSON.stringify({
142 | txid: changeResult.tx.id("hex"),
143 | spentOutpoints: changeResult.spentOutpoints,
144 | payChange: changeResult.payChange,
145 | inscriptionOutpoint: args.inscriptionOutpoint,
146 | destinationAddress: args.destinationAddress,
147 | }),
148 | },
149 | ],
150 | };
151 | }
152 |
153 | return {
154 | content: [
155 | {
156 | type: "text",
157 | text: changeResult.tx.toHex(),
158 | },
159 | ],
160 | };
161 | } catch (err: unknown) {
162 | const msg = err instanceof Error ? err.message : String(err);
163 | return { content: [{ type: "text", text: msg }], isError: true };
164 | }
165 | },
166 | );
167 | }
168 |
```
--------------------------------------------------------------------------------
/prompts/bsvSdk/wallet.ts:
--------------------------------------------------------------------------------
```typescript
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 |
8 | /**
9 | * BSV SDK Wallet Prompt
10 | *
11 | * Provides detailed information about the wallet functionality in the BSV SDK,
12 | * including key management, address handling, and UTXO management.
13 | */
14 | export const BSV_SDK_WALLET_PROMPT = `
15 | # BSV SDK - Wallet Module
16 |
17 | The wallet module in BSV SDK provides comprehensive functionality for managing Bitcoin keys, addresses, and UTXOs (Unspent Transaction Outputs). It forms the foundation for creating and managing Bitcoin wallets in your applications.
18 |
19 | ## Key Classes and Interfaces
20 |
21 | ### ProtoWallet
22 |
23 | The \`ProtoWallet\` class provides a basic implementation of the wallet interface with core functionality:
24 |
25 | \`\`\`typescript
26 | import { PrivateKey, ProtoWallet } from "@bsv/sdk";
27 |
28 | // Create a new wallet with a random private key
29 | const privateKey = PrivateKey.fromRandom();
30 | const wallet = new ProtoWallet(privateKey);
31 | \`\`\`
32 |
33 | ### WalletInterface
34 |
35 | The \`WalletInterface\` defines the standard interface that wallet implementations should follow. It includes methods for:
36 |
37 | - Key management
38 | - Cryptographic operations
39 | - Transaction creation and signing
40 | - Output management
41 | - Certificate handling
42 |
43 | ## Key Management
44 |
45 | ### Generating Keys
46 |
47 | \`\`\`typescript
48 | import { PrivateKey } from "@bsv/sdk";
49 |
50 | // Generate a random private key
51 | const privateKey = PrivateKey.fromRandom();
52 |
53 | // Generate from a WIF (Wallet Import Format) string
54 | const importedKey = PrivateKey.fromWif("your-wif-string");
55 |
56 | // Generate from a seed
57 | const seedKey = PrivateKey.fromSeed(Buffer.from("your-seed-data"));
58 |
59 | // Get the corresponding public key
60 | const publicKey = privateKey.toPublicKey();
61 | \`\`\`
62 |
63 | ### KeyDeriver & CachedKeyDeriver
64 |
65 | For HD (Hierarchical Deterministic) wallet functionality:
66 |
67 | \`\`\`typescript
68 | import { KeyDeriver } from "@bsv/sdk";
69 |
70 | // Create a key deriver with a seed
71 | const deriver = new KeyDeriver(seed);
72 |
73 | // Derive a key at a specific path
74 | const derivedKey = await deriver.deriveKey("m/44'/0'/0'/0/0");
75 | \`\`\`
76 |
77 | ## Address Management
78 |
79 | \`\`\`typescript
80 | // Get the address for a private key
81 | const address = privateKey.toAddress();
82 |
83 | // Get the address for a public key
84 | const address = publicKey.toAddress();
85 |
86 | // Get the address string
87 | const addressString = address.toString();
88 | \`\`\`
89 |
90 | ## UTXO Management
91 |
92 | Managing UTXOs (Unspent Transaction Outputs) is a critical part of wallet functionality:
93 |
94 | \`\`\`typescript
95 | // Example of tracking UTXOs
96 | class MyWallet extends ProtoWallet {
97 | private utxos = [];
98 |
99 | async refreshUtxos(address) {
100 | // Fetch UTXOs from a service or API
101 | this.utxos = await fetchUtxosFromService(address);
102 | }
103 |
104 | getAvailableUtxos() {
105 | return this.utxos.filter(utxo => !utxo.spent);
106 | }
107 |
108 | getBalance() {
109 | return this.getAvailableUtxos().reduce((sum, utxo) => sum + utxo.satoshis, 0);
110 | }
111 | }
112 | \`\`\`
113 |
114 | ## Cryptographic Operations
115 |
116 | The wallet module provides various cryptographic operations:
117 |
118 | \`\`\`typescript
119 | // Signing data
120 | const signature = await wallet.createSignature({
121 | data: [1, 2, 3, 4], // Data to sign
122 | protocolID: [1, "ecdsa"], // Protocol to use
123 | keyID: "default" // Key identifier
124 | });
125 |
126 | // Verifying signatures
127 | const isValid = await wallet.verifySignature({
128 | data: [1, 2, 3, 4], // Original data
129 | signature: signatureBytes, // Signature to verify
130 | protocolID: [1, "ecdsa"],
131 | keyID: "default"
132 | });
133 |
134 | // Encryption and decryption
135 | const encrypted = await wallet.encrypt({
136 | plaintext: [1, 2, 3, 4],
137 | protocolID: [1, "aes256"],
138 | keyID: "default"
139 | });
140 |
141 | const decrypted = await wallet.decrypt({
142 | ciphertext: encrypted.ciphertext,
143 | protocolID: [1, "aes256"],
144 | keyID: "default"
145 | });
146 | \`\`\`
147 |
148 | ## Best Practices
149 |
150 | 1. **Key Security**: Always handle private keys securely and never expose them unnecessarily
151 | 2. **UTXO Management**: Maintain accurate UTXO information for wallet functionality
152 | 3. **Error Handling**: Implement proper error handling for all wallet operations
153 | 4. **Testing**: Test wallet functionality thoroughly on testnet before deploying to mainnet
154 | 5. **Backup**: Provide key backup and recovery mechanisms for users
155 |
156 | ## Advanced Topics
157 |
158 | - **Multi-signature wallets**: Implementing wallets requiring multiple signatures
159 | - **HD Wallets**: Creating hierarchical deterministic wallets for key derivation
160 | - **Watch-only wallets**: Tracking addresses without private keys
161 | - **Hardware wallet integration**: Connecting to hardware security devices
162 |
163 | For complete API documentation and additional wallet features, refer to the official BSV SDK documentation.
164 | `;
165 |
166 | /**
167 | * Register the BSV SDK Wallet prompt with the MCP server
168 | * @param server The MCP server instance
169 | */
170 | export function registerWalletPrompt(server: McpServer): void {
171 | server.prompt(
172 | "bitcoin_sv_sdk_wallet",
173 | "Detailed information about the wallet functionality in the BSV SDK, including key management, address handling, and UTXO management.",
174 | async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
175 | return {
176 | messages: [
177 | {
178 | role: "assistant",
179 | content: {
180 | type: "text",
181 | text: BSV_SDK_WALLET_PROMPT,
182 | },
183 | },
184 | ],
185 | };
186 | },
187 | );
188 | }
189 |
```
--------------------------------------------------------------------------------
/tools/wallet/createOrdinals.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { PrivateKey } 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 | CallToolResult,
6 | ServerNotification,
7 | ServerRequest,
8 | } from "@modelcontextprotocol/sdk/types.js";
9 | import { createOrdinals } from "js-1sat-ord";
10 | import type {
11 | ChangeResult,
12 | CreateOrdinalsCollectionItemMetadata,
13 | CreateOrdinalsCollectionMetadata,
14 | CreateOrdinalsConfig,
15 | Destination,
16 | Inscription,
17 | LocalSigner,
18 | PreMAP,
19 | } from "js-1sat-ord";
20 | import { Sigma } from "sigma-protocol";
21 | import { z } from "zod";
22 | import type { Wallet } from "./wallet";
23 |
24 | /**
25 | * Schema for the createOrdinals tool arguments.
26 | * This is a simplified interface compared to the full CreateOrdinalsConfig
27 | * in js-1sat-ord. The wallet will provide UTXOs, private key, etc.
28 | */
29 | export const createOrdinalsArgsSchema = z.object({
30 | // Base64-encoded data to inscribe
31 | dataB64: z.string().describe("Base64-encoded content to inscribe"),
32 | // Content type (e.g., "image/jpeg", "text/plain", etc.)
33 | contentType: z.string().describe("MIME type of the content"),
34 | // Optional destination address (if not provided, uses the wallet's address)
35 | destinationAddress: z
36 | .string()
37 | .optional()
38 | .describe("Optional destination address for the ordinal"),
39 | // Optional metadata for the inscription
40 | metadata: z
41 | .any()
42 | .optional()
43 | .describe("Optional MAP metadata for the inscription"),
44 | });
45 |
46 | export type CreateOrdinalsArgs = z.infer<typeof createOrdinalsArgsSchema>;
47 |
48 | /**
49 | * Registers the wallet_createOrdinals tool for minting ordinals/inscriptions
50 | */
51 | export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
52 | server.tool(
53 | "wallet_createOrdinals",
54 | "Creates and inscribes ordinals (NFTs) on the Bitcoin SV blockchain. This tool lets you mint new digital artifacts by encoding data directly into the blockchain. Supports various content types including images, text, JSON, and HTML. The tool handles transaction creation, fee calculation, and broadcasting.",
55 | { args: createOrdinalsArgsSchema },
56 | async (
57 | { args }: { args: CreateOrdinalsArgs },
58 | extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
59 | ): Promise<CallToolResult> => {
60 | try {
61 | // Load optional identity key for sigma signing
62 | const identityKeyWif = process.env.IDENTITY_KEY_WIF;
63 | let identityPk: PrivateKey | undefined;
64 | if (identityKeyWif) {
65 | try {
66 | identityPk = PrivateKey.fromWif(identityKeyWif);
67 | } catch (e) {
68 | console.warn(
69 | "Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
70 | e,
71 | );
72 | }
73 | }
74 |
75 | // 1. Get private key from wallet
76 | const paymentPk = wallet.getPrivateKey();
77 | if (!paymentPk) {
78 | throw new Error("No private key available in wallet");
79 | }
80 |
81 | // 2. Get payment UTXOs from wallet
82 | const { paymentUtxos } = await wallet.getUtxos();
83 | if (!paymentUtxos || paymentUtxos.length === 0) {
84 | throw new Error(
85 | "No payment UTXOs available to fund this inscription",
86 | );
87 | }
88 |
89 | // 3. Get the wallet address for change/destination if not provided
90 | const walletAddress = paymentPk.toAddress().toString();
91 |
92 | // 4. Create the inscription object
93 | const inscription: Inscription = {
94 | dataB64: args.dataB64,
95 | contentType: args.contentType,
96 | };
97 |
98 | // 5. Create the destination
99 | const destinations: Destination[] = [
100 | {
101 | address: args.destinationAddress || walletAddress,
102 | inscription,
103 | },
104 | ];
105 |
106 | const createOrdinalsConfig: CreateOrdinalsConfig = {
107 | utxos: paymentUtxos,
108 | destinations,
109 | paymentPk,
110 | changeAddress: walletAddress,
111 | metaData: args.metadata as
112 | | PreMAP
113 | | CreateOrdinalsCollectionMetadata
114 | | CreateOrdinalsCollectionItemMetadata,
115 | };
116 |
117 | if (identityPk) {
118 | createOrdinalsConfig.signer = {
119 | idKey: identityPk,
120 | } as LocalSigner;
121 | }
122 |
123 | // 6. Create and broadcast the transaction
124 | const result = await createOrdinals(createOrdinalsConfig);
125 |
126 | const changeResult = result as ChangeResult;
127 |
128 | // 7. Optionally sign with identity key and broadcast the transaction
129 |
130 | const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
131 | if (!disableBroadcasting) {
132 | await changeResult.tx.broadcast();
133 |
134 | // 8. Refresh the wallet's UTXOs after spending
135 | try {
136 | await wallet.refreshUtxos();
137 | } catch (refreshError) {
138 | console.warn(
139 | "Failed to refresh UTXOs after transaction:",
140 | refreshError,
141 | );
142 | }
143 |
144 | // 9. Return transaction details
145 | return {
146 | content: [
147 | {
148 | type: "text",
149 | text: JSON.stringify({
150 | txid: changeResult.tx.id("hex"),
151 | spentOutpoints: changeResult.spentOutpoints,
152 | payChange: changeResult.payChange,
153 | inscriptionAddress: args.destinationAddress || walletAddress,
154 | contentType: args.contentType,
155 | }),
156 | },
157 | ],
158 | };
159 | }
160 |
161 | return {
162 | content: [
163 | {
164 | type: "text",
165 | text: changeResult.tx.toHex(),
166 | },
167 | ],
168 | };
169 | } catch (err: unknown) {
170 | const msg = err instanceof Error ? err.message : String(err);
171 | return { content: [{ type: "text", text: msg }], isError: true };
172 | }
173 | },
174 | );
175 | }
176 |
```
--------------------------------------------------------------------------------
/resources/junglebus.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2 |
3 | // Main JungleBus API documentation URL
4 | const JUNGLEBUS_DOCS_URL = "https://junglebus.gorillapool.io/docs/";
5 |
6 | /**
7 | * Manually structured JungleBus API documentation
8 | * Provides a clean summary of key JungleBus API endpoints and functionality
9 | */
10 | export function getJungleBusDocumentation(): string {
11 | return `# JungleBus API Documentation
12 |
13 | ## Overview
14 |
15 | JungleBus is a transaction monitoring service for Bitcoin SV that allows applications to subscribe to transaction events and filter them based on specific criteria. The API provides both subscription and querying capabilities.
16 |
17 | ## API Endpoints
18 |
19 | ### Base URL
20 | \`\`\`
21 | https://junglebus.gorillapool.io/v1
22 | \`\`\`
23 |
24 | ### Transaction API
25 |
26 | #### Get Transaction by ID
27 | \`\`\`
28 | GET /transaction/get/{txid}
29 | \`\`\`
30 |
31 | Returns detailed information about a specific transaction, including:
32 | - Transaction data (hex format)
33 | - Block information (hash, height, time)
34 | - Input and output details
35 | - Address information
36 |
37 | #### Get Transactions by Block Hash
38 | \`\`\`
39 | GET /block/{blockhash}/transactions
40 | \`\`\`
41 |
42 | Returns all transactions within a specific block.
43 |
44 | ### Subscription API
45 |
46 | #### Create Subscription
47 | \`\`\`
48 | POST /subscribe
49 | \`\`\`
50 |
51 | Create a new subscription to monitor transactions based on filtering criteria.
52 |
53 | Example request body:
54 | \`\`\`json
55 | {
56 | "callback": "https://your-callback-url.com",
57 | "fromBlock": 0,
58 | "query": {
59 | "find": {
60 | "out.tape.cell.s": "BEEF"
61 | }
62 | }
63 | }
64 | \`\`\`
65 |
66 | #### Delete Subscription
67 | \`\`\`
68 | DELETE /subscribe/{id}
69 | \`\`\`
70 |
71 | Deletes an existing subscription.
72 |
73 | ### Network API
74 |
75 | #### Get Network Info
76 | \`\`\`
77 | GET /network/info
78 | \`\`\`
79 |
80 | Returns current blockchain network information, including block height and other statistics.
81 |
82 | ## Client Implementations
83 |
84 | ### TypeScript Client
85 |
86 | Installation:
87 | \`\`\`bash
88 | $ npm install @gorillapool/js-junglebus
89 | \`\`\`
90 |
91 | Usage:
92 | \`\`\`javascript
93 | import { JungleBusClient } from '@gorillapool/js-junglebus';
94 |
95 | const server = "junglebus.gorillapool.io";
96 | const jungleBusClient = new JungleBusClient(server, {
97 | onConnected(ctx) {
98 | // add your own code here
99 | console.log(ctx);
100 | },
101 | onConnecting(ctx) {
102 | // add your own code here
103 | console.log(ctx);
104 | },
105 | onDisconnected(ctx) {
106 | // add your own code here
107 | console.log(ctx);
108 | },
109 | onError(ctx) {
110 | // add your own code here
111 | console.error(ctx);
112 | }
113 | });
114 |
115 | // create subscriptions in the dashboard of the JungleBus website
116 | const subId = "...."; // fill in the ID for the subscription
117 | const fromBlock = 750000;
118 |
119 | const subscription = jungleBusClient.Subscribe(
120 | subId,
121 | fromBlock,
122 | onPublish(tx) => {
123 | // add your own code here
124 | console.log(tx);
125 | },
126 | onStatus(ctx) => {
127 | // add your own code here
128 | console.log(ctx);
129 | },
130 | onError(ctx) => {
131 | // add your own code here
132 | console.log(ctx);
133 | },
134 | onMempool(tx) => {
135 | // add your own code here
136 | console.log(tx);
137 | }
138 | );
139 |
140 | // For lite mode (transaction hash and block height only)
141 | await client.Subscribe("a5e2fa655c41753331539a2a86546bf9335ff6d9b7a512dc9acddb00ab9985c0", 1550000, onPublish, onStatus, onError, onMempool, true);
142 | \`\`\`
143 |
144 | ### Go Client
145 |
146 | Installation:
147 | \`\`\`bash
148 | go get github.com/GorillaPool/go-junglebus
149 | \`\`\`
150 |
151 | Usage:
152 | \`\`\`go
153 | package main
154 |
155 | import (
156 | "context"
157 | "log"
158 | "sync"
159 | "github.com/GorillaPool/go-junglebus"
160 | "github.com/GorillaPool/go-junglebus/models"
161 | )
162 |
163 | func main() {
164 | wg := &sync.WaitGroup{}
165 |
166 | junglebusClient, err := junglebus.New(
167 | junglebus.WithHTTP("https://junglebus.gorillapool.io"),
168 | )
169 | if err != nil {
170 | log.Fatalln(err.Error())
171 | }
172 |
173 | subscriptionID := "..." // fill in the ID for the subscription
174 | fromBlock := uint64(750000)
175 |
176 | eventHandler := junglebus.EventHandler{
177 | // do not set this function to leave out mined transactions
178 | OnTransaction: func(tx *models.TransactionResponse) {
179 | log.Printf("[TX]: %d: %v", tx.BlockHeight, tx.Id)
180 | },
181 | // do not set this function to leave out mempool transactions
182 | OnMempool: func(tx *models.TransactionResponse) {
183 | log.Printf("[MEMPOOL TX]: %v", tx.Id)
184 | },
185 | OnStatus: func(status *models.ControlResponse) {
186 | log.Printf("[STATUS]: %v", status)
187 | },
188 | OnError: func(err error) {
189 | log.Printf("[ERROR]: %v", err)
190 | },
191 | }
192 |
193 | var subscription *junglebus.Subscription
194 | if subscription, err = junglebusClient.Subscribe(context.Background(), subscriptionID, fromBlock, eventHandler); err != nil {
195 | log.Printf("ERROR: failed getting subscription %s", err.Error())
196 | }
197 |
198 | // For lite mode
199 | if subscription, err := junglebusClient.SubscribeWithQueue(context.Background(), subscriptionID, fromBlock, 0, eventHandler, &junglebus.SubscribeOptions{
200 | QueueSize: 100000,
201 | LiteMode: true,
202 | }); err != nil {
203 | log.Printf("ERROR: failed getting subscription %s", err.Error())
204 | }
205 |
206 | wg.Add(1)
207 | wg.Wait()
208 | }
209 | \`\`\`
210 |
211 | ## Further Reading
212 |
213 | For complete API documentation, visit [JungleBus Docs](${JUNGLEBUS_DOCS_URL})
214 | `;
215 | }
216 |
217 | /**
218 | * Register the JungleBus API documentation resource with the MCP server
219 | * @param server The MCP server instance
220 | */
221 | export function registerJungleBusResource(server: McpServer): void {
222 | server.resource(
223 | "junglebus-api-docs",
224 | JUNGLEBUS_DOCS_URL,
225 | {
226 | title: "JungleBus API Documentation",
227 | description:
228 | "API documentation for JungleBus, a transaction monitoring service for Bitcoin SV",
229 | },
230 | async (uri) => {
231 | const documentationContent = getJungleBusDocumentation();
232 | return {
233 | contents: [
234 | {
235 | uri: uri.href,
236 | text: documentationContent,
237 | },
238 | ],
239 | };
240 | },
241 | );
242 | }
243 |
```
--------------------------------------------------------------------------------
/tools/ordinals/marketListings.ts:
--------------------------------------------------------------------------------
```typescript
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 | // Enhanced schema for unified market listings arguments
6 | export const marketListingsArgsSchema = z.object({
7 | // Common parameters
8 | limit: z
9 | .number()
10 | .int()
11 | .min(1)
12 | .max(100)
13 | .default(20)
14 | .describe("Number of results (1-100, default 20)"),
15 | offset: z.number().int().min(0).default(0).describe("Pagination offset"),
16 | dir: z
17 | .enum(["asc", "desc"])
18 | .default("desc")
19 | .describe("Sort direction (asc or desc)"),
20 | address: z.string().optional().describe("Bitcoin address"),
21 |
22 | // NFT-specific parameters
23 | origin: z.string().optional().describe("Origin outpoint"),
24 | mime: z.string().optional().describe("MIME type filter"),
25 | num: z.string().optional().describe("Inscription number"),
26 |
27 | // General market parameters
28 | minPrice: z.number().optional().describe("Minimum price in satoshis"),
29 | maxPrice: z.number().optional().describe("Maximum price in satoshis"),
30 |
31 | // Token-specific parameters
32 | tokenType: z
33 | .enum(["nft", "bsv20", "bsv21", "all"])
34 | .default("all")
35 | .describe("Type of token to search for (nft, bsv20, bsv21, or all)"),
36 | sort: z
37 | .enum(["recent", "price", "num", "height", "price_per_token"])
38 | .default("recent")
39 | .describe("Sort method (recent, price, num, height, price_per_token)"),
40 | id: z.string().optional().describe("Token ID in outpoint format"),
41 | tick: z.string().optional().describe("Token ticker symbol"),
42 | pending: z
43 | .boolean()
44 | .default(false)
45 | .optional()
46 | .describe("Include pending sales"),
47 | });
48 |
49 | export type MarketListingsArgs = z.infer<typeof marketListingsArgsSchema>;
50 |
51 | // Unified response type that covers all token types
52 | interface MarketListingResponse {
53 | results: Array<{
54 | outpoint: string;
55 | origin?: {
56 | outpoint: string;
57 | data?: {
58 | insc?: {
59 | text?: string;
60 | file?: {
61 | type?: string;
62 | size?: number;
63 | };
64 | };
65 | };
66 | };
67 | data?: {
68 | list?: {
69 | price?: number;
70 | payout?: string;
71 | sale?: boolean;
72 | price_per_token?: number;
73 | };
74 | bsv20?: {
75 | id?: string;
76 | tick?: string;
77 | sym?: string;
78 | amt?: string;
79 | op?: string;
80 | };
81 | };
82 | satoshis?: number;
83 | height?: number;
84 | [key: string]: unknown;
85 | }>;
86 | total: number;
87 | }
88 |
89 | /**
90 | * Register the unified Ordinals market listings tool
91 | * Handles NFTs, BSV20, and BSV21 tokens
92 | */
93 | export function registerMarketListingsTool(server: McpServer): void {
94 | server.tool(
95 | "ordinals_marketListings",
96 | "Retrieves current marketplace listings for Bitcoin SV ordinals with flexible filtering. Supports multiple asset types (NFTs, BSV-20 tokens, BSV-21 tokens) through a unified interface. Results include listing prices, details about the assets, and seller information.",
97 | {
98 | args: marketListingsArgsSchema,
99 | },
100 | async (
101 | { args }: { args: MarketListingsArgs },
102 | extra: RequestHandlerExtra,
103 | ) => {
104 | try {
105 | const {
106 | limit,
107 | offset,
108 | sort,
109 | dir,
110 | address,
111 | origin,
112 | mime,
113 | num,
114 | minPrice,
115 | maxPrice,
116 | tokenType,
117 | id,
118 | tick,
119 | pending,
120 | } = args;
121 |
122 | // Determine the API endpoint based on tokenType
123 | let baseUrl = "https://ordinals.gorillapool.io/api";
124 | let useTokenParams = false;
125 |
126 | if (tokenType === "bsv20" || tokenType === "bsv21") {
127 | baseUrl += "/bsv20/market";
128 | useTokenParams = true;
129 | } else if (tokenType === "nft" || tokenType === "all") {
130 | baseUrl += "/market";
131 | }
132 |
133 | // Build the URL with query parameters
134 | const url = new URL(baseUrl);
135 | url.searchParams.append("limit", limit.toString());
136 | url.searchParams.append("offset", offset.toString());
137 | url.searchParams.append("dir", dir);
138 |
139 | // Add sort parameter based on token type
140 | if (useTokenParams) {
141 | // BSV20/BSV21 specific sort options
142 | if (
143 | sort === "height" ||
144 | sort === "price" ||
145 | sort === "price_per_token"
146 | ) {
147 | url.searchParams.append("sort", sort);
148 | }
149 | // Add token-specific parameters
150 | if (tokenType === "bsv21") {
151 | url.searchParams.append("type", "v2");
152 | }
153 | if (id) url.searchParams.append("id", id);
154 | if (tick) url.searchParams.append("tick", tick);
155 | if (pending !== undefined)
156 | url.searchParams.append("pending", pending.toString());
157 | // For BSV20/21, min/max price parameters have slightly different names
158 | if (minPrice !== undefined)
159 | url.searchParams.append("min_price", minPrice.toString());
160 | if (maxPrice !== undefined)
161 | url.searchParams.append("max_price", maxPrice.toString());
162 | } else {
163 | // NFT specific sort options
164 | if (sort === "recent" || sort === "price" || sort === "num") {
165 | url.searchParams.append("sort", sort);
166 | }
167 | // Add NFT-specific parameters
168 | if (origin) url.searchParams.append("origin", origin);
169 | if (mime) url.searchParams.append("mime", mime);
170 | if (num) url.searchParams.append("num", num);
171 | // For NFTs, min/max price parameters use short names
172 | if (minPrice !== undefined)
173 | url.searchParams.append("min", minPrice.toString());
174 | if (maxPrice !== undefined)
175 | url.searchParams.append("max", maxPrice.toString());
176 | }
177 |
178 | // Add common parameters
179 | if (address) url.searchParams.append("address", address);
180 |
181 | // Make the fetch request
182 | const response = await fetch(url.toString());
183 |
184 | if (!response.ok) {
185 | throw new Error(
186 | `API error: ${response.status} ${response.statusText}`,
187 | );
188 | }
189 |
190 | const data = (await response.json()) as MarketListingResponse;
191 |
192 | return {
193 | content: [
194 | {
195 | type: "text",
196 | text: JSON.stringify(data, null, 2),
197 | },
198 | ],
199 | };
200 | } catch (error) {
201 | return {
202 | content: [
203 | {
204 | type: "text",
205 | text: error instanceof Error ? error.message : String(error),
206 | },
207 | ],
208 | isError: true,
209 | };
210 | }
211 | },
212 | );
213 | }
214 |
```