#
tokens: 48930/50000 53/59 files (page 1/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 2. Use http://codebase.md/b-open-io/bsv-mcp?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:
--------------------------------------------------------------------------------

```
# dependencies (bun install)
node_modules

# output
out
dist
*.tgz

# code coverage
coverage
*.lcov

# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# caches
.eslintcache
.cache
*.tsbuildinfo

# IntelliJ based IDEs
.idea

# Finder (MacOS) folder config
.DS_Store
todo.md
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Bitcoin SV MCP Server

[![smithery badge](https://smithery.ai/badge/@b-open-io/bsv-mcp)](https://smithery.ai/server/@b-open-io/bsv-mcp)

> **⚠️ NOTICE: Experimental Work in Progress**  
> This project is in an early experimental stage. Features may change, and the API is not yet stable.
> Contributions, feedback, and bug reports are welcome! Feel free to open issues or submit pull requests.

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.

## Installation and Setup

### Use Bun (Optional but recommended)

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.

#### Installing Bun

**macOS (using Homebrew):**
```bash
brew install oven-sh/bun/bun
```

**macOS/Linux/WSL (using installer script):**
```bash
curl -fsSL https://bun.sh/install | bash
```

**Windows:**
Windows users should use WSL (Windows Subsystem for Linux) or Docker to run Bun.

Node.js and npm will also work but may not offer the same performance benefits.

## Connecting to MCP Clients

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.

![MCP Configuration Example](docs/images/mcp-config-example.png)

> **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.

### Cursor

To use the BSV MCP server with [Cursor](https://cursor.sh/):

1. Install Cursor if you haven't already
2. Open Cursor and navigate to Settings → Extensions → Model Context Protocol
3. Click "Add a new global MCP server"
4. Enter the following configuration in JSON format:

```json
{
  "mcpServers": {
    "Bitcoin SV": {
      "command": "bunx",
      "args": [
        "bsv-mcp@latest"
      ],
      "env": {
        "PRIVATE_KEY_WIF": "<your_private_key_wif>",
        "IDENTITY_KEY_WIF": "<your_identity_key_wif>"
      }
    }
  }
}
```

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.

6. Click "Save"

The BSV tools will now be available to Cursor's AI assistant under the "Bitcoin SV" namespace.

#### Alternative for npm users

If you prefer to use npm instead of Bun:

```json
{
  "mcpServers": {
    "Bitcoin SV": {
      "command": "npx",
      "args": [
        "bsv-mcp@latest"
      ],
      "env": {
        "PRIVATE_KEY_WIF": "<your_private_key_wif>",
        "IDENTITY_KEY_WIF": "<your_identity_key_wif>"
      }
    }
  }
}
```

### Claude for Desktop

To connect this server to Claude for Desktop:

1. Open [Claude for Desktop](https://claude.ai/desktop) and go to Claude > Settings > Developer
2. Click "Edit Config". 

Open the Claude configuration json file in your favorite text editor. If you prefer to do it from the cli:

   ```bash
   # macOS/Linux
   code ~/Library/Application\ Support/Claude/claude_desktop_config.json

   # Windows
   code %APPDATA%\Claude\claude_desktop_config.json
   ```

3. Add the BSV MCP server to your configuration:
   ```json
   {
     "mcpServers": {
       "Bitcoin SV": {
         "command": "bun",
         "args": [
           "run", "bsv-mcp@latest"
         ],
         "env": {
           "PRIVATE_KEY_WIF": "<your_private_key_wif>",
           "IDENTITY_KEY_WIF": "<your_identity_key_wif>"
         }
       }
     }
   }
   ```
4. Replace `<your_private_key_wif>` with your actual private key WIF
5. Save the file and restart Claude for Desktop
6. The BSV tools will appear when you click the tools icon (hammer) in Claude for Desktop

#### Alternative for npm users (Claude)

If you prefer to use npm instead of Bun, replace the "command" field with "npx".

## Available Tools

The toolkit is organized into several categories:

### Wallet Tools

Wallet tools provide core BSV wallet functionality:

| Tool Name                | Description                                                                                                                                                                                                                                           | Example Output                                                                                            |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `wallet_getPublicKey`    | Retrieves a public key for a specified protocol and key ID                                                                                                                                                                                            | `{"publicKey":"032d0c73eb9270e9e009fd1f9dd77e19cf764fbad5f799560c4e8fd414e40d6fc2"}`                      |
| `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,...]}`                            |
| `wallet_verifySignature` | Verifies a cryptographic signature against the provided data                                                                                                                                                                                          | `{"isValid":true}`                                                                                        |
| `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"}` |
| `wallet_getAddress`      | Returns a BSV address for the current wallet or a derived path                                                                                                                                                                                        | `{"address":"1ExampleBsvAddressXXXXXXXXXXXXXXXXX","status":"ok"}`                                         |
| `wallet_sendToAddress`   | Sends BSV to a specified address (supports BSV or USD amounts)                                                                                                                                                                                        | `{"status":"success","txid":"a1b2c3d4e5f6...","satoshis":1000000}`                                        |
| `wallet_purchaseListing` | Purchases NFTs or BSV-20/BSV-21 tokens from marketplace listings                                                                                                                                                                                      | `{"status":"success","txid":"a1b2c3d4e5f6...","type":"nft","origin":"abcdef123456..."}`                   |
| `wallet_createOrdinals`  | Creates and inscribes ordinals on the BSV blockchain                                                                                                                                                                                                  | `{"txid":"a1b2c3d4e5f6...","inscriptionAddress":"1ExampleAddress...","contentType":"image/png"}`          |

### BSV Tools

Tools for interacting with the BSV blockchain and network:

| Tool Name               | Description                                                                 | Example Output                                                                                     |
| ----------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `bsv_getPrice`          | Gets the current BSV price from an exchange API                             | `Current BSV price: $38.75 USD`                                                                    |
| `bsv_decodeTransaction` | Decodes a BSV transaction and returns detailed information                  | `{"txid":"a1b2c3d4e5f6...","version":1,"locktime":0,"size":225,"inputs":[...],"outputs":[...]}`    |
| `bsv_explore`           | Comprehensive blockchain explorer tool accessing WhatsOnChain API endpoints | `{"chain_info":{"chain":"main","blocks":826458,"headers":826458,"bestblockhash":"0000000000..."}}` |

### Ordinals Tools

Tools for working with ordinals (NFTs) on BSV:

| Tool Name                       | Description                                                                          | Example Output                                                                                                |
| ------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `ordinals_getInscription`       | Retrieves detailed information about a specific inscription                          | `{"id":"a1b2c3d4e5f6...","origin":"a1b2c3d4e5f6...","contentType":"image/png","content":"iVBORw0KGgoAAA..."}` |
| `ordinals_searchInscriptions`   | Searches for inscriptions based on various criteria                                  | `{"results":[{"id":"a1b2c3...","contentType":"image/png","owner":"1Example..."},...]}`                        |
| `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},...]}`                         |
| `ordinals_marketSales`          | Gets information about BSV-20 and BSV-21 token market sales                          | `{"results":[{"txid":"a1b2c3...","price":34710050,"tick":"$BTC","sale":true},...]}`                           |
| `ordinals_getTokenByIdOrTicker` | Retrieves details about a specific BSV20 token by ID                                 | `{"tick":"PEPE","max":"21000000","lim":"1000","dec":"2"}`                                                     |

### Utility Tools

General-purpose utility functions:

| Tool Name           | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Example Output                                                 |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
| `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) |

### MNEE Tools

Tools for working with MNEE tokens:

| Tool Name         | Description                                                                                                                                                                           | Example Output                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mnee_getBalance` | Retrieves the current MNEE token balance for the wallet                                                                                                                               | `{"balance": {"amount": 2900, "decimalAmount": 0.029}}`                                                                                                                                                                                                                                                                                                                                                                             |
| `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"}`                                                                                                                                                                                              |
| `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}]}` |

## Using the Tools with MCP

Once connected, you can use natural language to interact with Bitcoin SV through your AI assistant. Here are some example prompts:

### Wallet Operations

- "Get my Bitcoin SV address"
- "Send 0.01 BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
- "Send $5 USD worth of BSV to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
- "Send 0.01 MNEE to 1ExampleBsvAddressXXXXXXXXXXXXXXXXX"
- "Check my MNEE balance"
- "Parse this MNEE transaction: txid"
- "Encrypt this message using my wallet's keys"
- "Decrypt this data that was previously encrypted for me"
- "Purchase this NFT listing: txid_vout"
- "Purchase this BSV-20 token listing: txid_vout"

### Ordinals (NFTs)

- "Show me information about the NFT with outpoint 6a89047af2cfac96da17d51ae8eb62c5f1d982be2bc4ba0d0cd2084b7ffed325_0"
- "Search for Pixel Zoide NFTs"
- "Show me the current marketplace listings for BSV NFTs"
- "Show me BSV-20 token listings for ticker PEPE"
- "Get recent BSV-20 token sales"

### Blockchain Operations

- "What is the current BSV price?"
- "Decode this BSV transaction: (transaction hex or ID)"
- "Get the latest Bitcoin SV chain information"
- "Show me block details for height 800000"
- "Explore transaction history for address 1ExampleBsvAddressXXXX"
- "Check unspent outputs (UTXOs) for my wallet address"
- "Get details for transaction with hash a1b2c3d4e5f6..."

### Data Conversion

- "Convert 'Hello World' from UTF-8 to hex format"

## MCP Prompts and Resources

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.

### Available Prompts

The server provides the following educational prompts that can be accessed directly via the MCP protocol:

#### Ordinals Prompt
- **Identifier**: `bitcoin_sv_ordinals`
- **Description**: Comprehensive information about Bitcoin SV ordinals, including what they are, how they work, and how to use them.
- **Usage**: Ask the assistant about "Bitcoin SV ordinals" or "1Sat Ordinals" to access this information.

#### BSV SDK Prompts
A collection of prompts providing detailed information about the Bitcoin SV SDK:

- **Overview**
  - **Identifier**: `bitcoin_sv_sdk_overview`
  - **Description**: General overview of the Bitcoin SV SDK, including its purpose and main components.
  - **Usage**: "Tell me about the BSV SDK" or "What is the Bitcoin SV SDK?"

- **Wallet Operations**
  - **Identifier**: `bitcoin_sv_sdk_wallet`
  - **Description**: Information about wallet operations in the BSV SDK.
  - **Usage**: "How do wallet operations work in the BSV SDK?"

- **Transaction Building**
  - **Identifier**: `bitcoin_sv_sdk_transaction`
  - **Description**: Details about transaction creation and manipulation.
  - **Usage**: "Explain BSV SDK transaction building" or "How do I create transactions with BSV SDK?"

- **Authentication**
  - **Identifier**: `bitcoin_sv_sdk_auth`
  - **Description**: Authentication and identity protocols in BSV SDK.
  - **Usage**: "How does authentication work with BSV SDK?"

- **Cryptography**
  - **Identifier**: `bitcoin_sv_sdk_cryptography`
  - **Description**: Signing, encryption, and verification functionality.
  - **Usage**: "Explain BSV SDK cryptography features"

- **Scripting**
  - **Identifier**: `bitcoin_sv_sdk_script`
  - **Description**: Bitcoin scripting and contract capabilities.
  - **Usage**: "How do I work with Bitcoin scripts using the BSV SDK?"

- **Primitives**
  - **Identifier**: `bitcoin_sv_sdk_primitives`
  - **Description**: Core data types and structures in the BSV SDK.
  - **Usage**: "What primitives are available in the BSV SDK?"

### Available Resources

The server also provides access to Bitcoin Request for Comments (BRC) specifications and documentation:

#### Changelog Resource
- **Identifier**: `bsv-mcp-changelog`
- **Description**: Version history and changelog for the BSV MCP server.
- **Usage**: "Show me the BSV MCP changelog" or "What's new in the latest version?"

#### BRC Resources
- **BRCs Overview**
  - **Identifier**: `brcs_readme`
  - **Description**: Overview of all Bitcoin SV protocol specifications in the BRCs repository.
  - **Usage**: "Show me the Bitcoin SV BRCs overview"

- **BRCs Summary**
  - **Identifier**: `brcs_summary`
  - **Description**: Table of contents for all Bitcoin SV BRCs.
  - **Usage**: "Give me a summary of Bitcoin SV BRCs"

- **Specific BRC Specifications**
  - **Identifier**: `brc_spec`
  - **Description**: Access specific BRC specifications by category and number.
  - **Usage**: "Show me BRC 8 on Transaction Envelopes" or "What does BRC 1 specify?"

#### BRC Categories
The BRC specifications are organized into the following categories:
- Wallet
- Transactions
- Scripts
- Tokens
- Overlays
- Payments
- Peer-to-Peer
- Key Derivation
- Outpoints
- Opinions
- State Machines
- Apps

### Using Prompts and Resources

AI models can use these prompts and resources to provide more accurate and detailed responses about Bitcoin SV technologies. As a user, you can:

1. **Ask about a specific topic**: "Tell me about Bitcoin SV ordinals" or "Explain BSV SDK transaction building"
2. **Request specific BRC details**: "What does BRC 8 specify?" or "Show me the BRC on Transaction Creation"
3. **Get general overviews**: "What is the BSV SDK?" or "Show me a summary of all BRCs"

These prompts and resources enhance the AI's knowledge base, enabling more technical and accurate responses even for complex Bitcoin SV topics.

## How MCP Works

When you interact with an MCP-enabled AI assistant:

1. The AI analyzes your request and decides which tools to use
2. With your approval, it calls the appropriate BSV MCP tool
3. The server executes the requested operation on the Bitcoin SV blockchain
4. The results are returned to the AI assistant
5. The assistant presents the information in a natural, conversational way

## Customization Options

The BSV MCP server can be customized using environment variables to enable or disable specific components:

### Component Configuration

| Environment Variable | Default | Description |
| -------------------- | ------- | ----------- |
| `DISABLE_PROMPTS` | `false` | Set to `true` to disable all educational prompts |
| `DISABLE_RESOURCES` | `false` | Set to `true` to disable all resources (BRCs, changelog) |
| `DISABLE_TOOLS` | `false` | Set to `true` to disable all tools |

### Tool-Specific Configuration

| Environment Variable | Default | Description |
| -------------------- | ------- | ----------- |
| `DISABLE_WALLET_TOOLS` | `false` | Set to `true` to disable Bitcoin wallet tools |
| `DISABLE_MNEE_TOOLS` | `false` | Set to `true` to disable MNEE token tools |
| `DISABLE_BSV_TOOLS` | `false` | Set to `true` to disable BSV blockchain tools |
| `DISABLE_ORDINALS_TOOLS` | `false` | Set to `true` to disable Ordinals/NFT tools |
| `DISABLE_UTILS_TOOLS` | `false` | Set to `true` to disable utility tools |
| `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. |
| `DISABLE_BROADCASTING` | `false` | Set to `true` to disable transaction broadcasting; returns raw transaction hex instead - useful for testing and transaction review before broadcasting |

### Examples

Run with only educational resources and prompts, no tools:

```bash
DISABLE_TOOLS=true bunx bsv-mcp@latest
```

Run with only BSV tools, no wallet or other functionality:

```bash
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
```

Use all tools except wallet operations:

```bash
DISABLE_WALLET_TOOLS=true bunx bsv-mcp@latest
```

Create transactions without broadcasting them (dry-run mode):

```bash
DISABLE_BROADCASTING=true bunx bsv-mcp@latest
```

## Troubleshooting

If you're having issues with the BSV MCP server:

### Connection Issues

1. Make sure Bun or Node.js is installed on your system
2. Verify your WIF private key is correctly set in the environment
3. Check that your client supports MCP and is properly configured
4. Look for error messages in the client's console output

### Keeping Bun Up to Date

It's important to keep Bun updated to the latest version to ensure compatibility:

```bash
# Update Bun to the latest version
bun upgrade
```

To verify your current Bun version:
```bash
bun --version
```

### Logging and Debugging

For Claude for Desktop, check the logs at:

```bash
# macOS/Linux
tail -n 20 -f ~/Library/Logs/Claude/mcp*.log

# Windows
type %APPDATA%\Claude\Logs\mcp*.log
```

For Cursor, check the Cursor MCP logs in Settings → Extensions → Model Context Protocol.

## Recent Updates

- **Transaction Broadcast Control**: Added `DISABLE_BROADCASTING` environment variable to prevent transactions from being broadcast to the network
- **Blockchain Explorer**: Added `bsv_explore` tool for WhatsOnChain API access with mainnet/testnet support
- **Unified Tools**: Merged `wallet_encrypt`/`wallet_decrypt` into single `wallet_encryption` tool
- **Enhanced Marketplace**: Support for NFTs, BSV-20/21 tokens in listings, sales and purchases
- **Performance**: Added price caching and optimized API endpoint structure
- **Improved Validation**: Better error handling for private keys and parameters

## Bitcoin SV Blockchain Explorer

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.

### Available Endpoints

The tool supports the following endpoint categories and specific endpoints:

#### Chain Data

| Endpoint             | Description                                     | Required Parameters | Example Response                                                                      |
| -------------------- | ----------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------- |
| `chain_info`         | Network statistics, difficulty, and chain work  | None                | `{"chain":"main","blocks":826458,"headers":826458,"bestblockhash":"0000000000..."}` |
| `chain_tips`         | Current chain tips including heights and states | None                | `[{"height":826458,"hash":"000000000000...","branchlen":0,"status":"active"}]`        |
| `circulating_supply` | Current BSV circulating supply                  | None                | `{"bsv":21000000}`                                                                    |
| `peer_info`          | Connected peer statistics                       | None                | `[{"addr":"1.2.3.4:8333","services":"000000000000...","lastsend":1621234567}]`        |

#### Block Data

| Endpoint              | Description                                         | Required Parameters                 | Example Response                                                           |
| --------------------- | --------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------- |
| `block_by_hash`       | Complete block data via hash                        | `blockHash`                         | `{"hash":"000000000000...","confirmations":1000,"size":1000000,...}`       |
| `block_by_height`     | Complete block data via height                      | `blockHeight`                       | `{"hash":"000000000000...","confirmations":1000,"size":1000000,...}`       |
| `tag_count_by_height` | Stats on tag count for a specific block             | `blockHeight`                       | `{"tags":{"amp":3,"bitkey":5,"metanet":12,"planaria":7,"b":120}}`          |
| `block_headers`       | Retrieves the last 10 block headers                 | None                                | `[{"hash":"000000000000...","height":826458,"version":536870912,...},...]` |
| `block_pages`         | Retrieves pages of transaction IDs for large blocks | `blockHash`, optional: `pageNumber` | `["tx1hash","tx2hash","tx3hash",...]`                                      |

#### Stats Data

| Endpoint                | Description                               | Required Parameters          | Example Response                                                                       |
| ----------------------- | ----------------------------------------- | ---------------------------- | -------------------------------------------------------------------------------------- |
| `block_stats_by_height` | Block statistics for a specific height    | `blockHeight`                | `{"size":123456,"txCount":512,"outputTotal":54.12345678,"outputTotalUsd":2345.67,...}` |
| `block_miner_stats`     | Block mining statistics for a time period | optional: `days` (default 7) | `{"blocks":{"miner1":412,"miner2":208,...},"total":1008}`                              |
| `miner_summary_stats`   | Summary of mining statistics              | optional: `days` (default 7) | `{"totalBlocks":1008,"totalFees":1.23456789,"totalFeesUsd":53.67,...}`                 |

#### Transaction Data

| Endpoint          | Description                                   | Required Parameters | Example Response                                                                           |
| ----------------- | --------------------------------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
| `tx_by_hash`      | Detailed transaction data                     | `txHash`            | `{"txid":"a1b2c3d4e5f6...","version":1,"locktime":0,"size":225,...}`                       |
| `tx_raw`          | Raw transaction hex data                      | `txHash`            | `"01000000012345abcdef..."`                                                                |
| `tx_receipt`      | Transaction receipt                           | `txHash`            | `{"blockHash":"000000000000...","blockHeight":800000,"confirmations":26458}`               |
| `bulk_tx_details` | Retrieve multiple transactions in one request | `txids` (array)     | `[{"txid":"a1b2c3d4e5f6...","version":1,...}, {"txid":"b2c3d4e5f6a7...","version":1,...}]` |

#### Address Data

| Endpoint          | Description                     | Required Parameters          | Example Response                                                |
| ----------------- | ------------------------------- | ---------------------------- | --------------------------------------------------------------- |
| `address_history` | Transaction history for address | `address`, optional: `limit` | `[{"tx_hash":"a1b2c3d4e5f6...","height":800000},...]`           |
| `address_utxos`   | Unspent outputs for address     | `address`                    | `[{"tx_hash":"a1b2c3d4e5f6...","tx_pos":0,"value":100000},...]` |

#### Network

| Endpoint | Description      | Required Parameters | Example Response      |
| -------- | ---------------- | ------------------- | --------------------- |
| `health` | API health check | None                | `{"status":"synced"}` |

### Usage Examples

The `bsv_explore` tool can be used with natural language prompts like:

```
"Get the current Bitcoin SV blockchain information"
"Show me block #800000 details"
"Get tag count statistics for block #800000"
"Fetch transaction history for address 1ExampleBsvAddressXXXXXXXX"
"Get unspent outputs for my wallet address"
"Check transaction details for txid a1b2c3d4e5f6..."
"What is the current BSV circulating supply?"
"Show me the latest block headers"
"Get transaction IDs for page 2 of a large block"
"Show me block statistics for height 800000"
"What are the mining statistics for the last 14 days?"
"Get a summary of mining activity over the past 30 days"
"Retrieve details for multiple transactions in a single query"
```

Under the hood, the tool accepts parameters to specify which data to retrieve:

- `endpoint`: The specific WhatsOnChain endpoint to query (e.g., `chain_info`, `tx_by_hash`)
- `network`: The BSV network to use (`main` or `test`)
- Additional parameters as required by the specific endpoint:
  - `blockHash`: For block_by_hash and block_pages endpoints
  - `blockHeight`: For block_by_height, tag_count_by_height, and block_stats_by_height endpoints
  - `pageNumber`: For block_pages endpoint (pagination)
  - `days`: For block_miner_stats and miner_summary_stats endpoints (defaults to 7)
  - `txHash`: For transaction-related endpoints (tx_by_hash, tx_raw, tx_receipt)
  - `txids`: For bulk_tx_details endpoint (array of transaction IDs)
  - `address`: For address-related endpoints
  - `limit`: Optional pagination limit for address_history

### Network Options

The tool supports both mainnet and testnet:

- `main`: Bitcoin SV mainnet (default)
- `test`: Bitcoin SV testnet

# Development

## Project Setup

If you want to contribute to the project or run it locally:

1. Clone the repository:
   ```bash
   git clone https://github.com/b-open-io/bsv-mcp.git
   cd bsv-mcp
   ```

2. Install dependencies:
   ```bash
   bun install
   # or with npm
   npm install
   ```

## Running the Server

```bash
bun run index.ts
# or with npm
npm run start
```

### Running Tests

```bash
bun test
# or with npm
npm test
```

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# Use the official Bun image
FROM oven/bun:1

# Set working directory
WORKDIR /app

# Copy all application code first
COPY . .

# Install dependencies
RUN bun install --frozen-lockfile

# Set user for security
USER bun

# Expose port (if needed)
EXPOSE 3000

# Run the application
CMD ["bun", "run", "index.ts"] 
```

--------------------------------------------------------------------------------
/biome.json:
--------------------------------------------------------------------------------

```json
{
	"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
	"vcs": {
		"enabled": false,
		"clientKind": "git",
		"useIgnoreFile": false
	},
	"files": {
		"ignoreUnknown": false,
		"ignore": ["./dist"]
	},
	"formatter": {
		"enabled": true,
		"indentStyle": "tab"
	},
	"organizeImports": {
		"enabled": true
	},
	"linter": {
		"enabled": true,
		"rules": {
			"recommended": true
		}
	},
	"javascript": {
		"formatter": {
			"quoteStyle": "double"
		}
	}
}

```

--------------------------------------------------------------------------------
/tools/bsv/index.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerDecodeTransactionTool } from "./decodeTransaction";
import { registerExploreTool } from "./explore";
import { registerGetPriceTool } from "./getPrice";

/**
 * Register all BSV tools with the MCP server
 * @param server The MCP server instance
 */
export function registerBsvTools(server: McpServer): void {
	// Register BSV-related tools
	registerGetPriceTool(server);
	registerDecodeTransactionTool(server);
	registerExploreTool(server);
}

```

--------------------------------------------------------------------------------
/tools/constants.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Constants for BSV MCP Tools
 */

/**
 * Market fee percentage applied to all marketplace purchases
 * Expressed as a decimal (e.g., 0.03 = 3%)
 */
export const MARKET_FEE_PERCENTAGE = 0.03;

/**
 * Market wallet address where fees are sent
 * This is the recipient address for marketplace fees
 */
export const MARKET_WALLET_ADDRESS = "15q8YQSqUa9uTh6gh4AVixxq29xkpBBP9z";

/**
 * Minimum fee in satoshis
 * Market fee will never be less than this amount
 */
export const MINIMUM_MARKET_FEE_SATOSHIS = 10000; // 10000 satoshis = 0.0001 BSV

```

--------------------------------------------------------------------------------
/prompts/index.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerAllBsvSdkPrompts } from "./bsvSdk";
import { registerOrdinalsPrompt } from "./ordinals";

/**
 * Register all prompts with the MCP server
 * @param server The MCP server instance
 */
export function registerAllPrompts(server: McpServer): void {
	// Register Ordinals prompt
	registerOrdinalsPrompt(server);

	// Register all BSV SDK prompts
	registerAllBsvSdkPrompts(server);

	// Add more prompts registration here as needed
}

/**
 * Export all prompt constants
 */
export * from "./ordinals";
export * from "./bsvSdk";

```

--------------------------------------------------------------------------------
/tools/mnee/index.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import Mnee from "mnee";
import { registerGetBalanceTool } from "./getBalance";
import { registerParseTxTool } from "./parseTx";
import { registerSendMneeTool } from "./sendMnee";

const mnee = new Mnee({
	environment: "production",
});
/**
 * Register all MNEE tools with the MCP server
 * @param server The MCP server instance
 */
export function registerMneeTools(server: McpServer): void {
	// Register MNEE-related tools
	registerGetBalanceTool(server, mnee);

	registerSendMneeTool(server, mnee);

	registerParseTxTool(server, mnee);
}

```

--------------------------------------------------------------------------------
/resources/resources.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerBRCsResources } from "./brcs";
import { registerChangelogResource } from "./changelog";
import { registerJungleBusResource } from "./junglebus";

/**
 * Register all resources with the MCP server
 * @param server The MCP server instance
 */
export function registerResources(server: McpServer): void {
	// Register BRC-related resources
	registerBRCsResources(server);

	// Register changelog resource
	registerChangelogResource(server);

	// Register JungleBus API documentation resource
	registerJungleBusResource(server);

	// Add more resource categories here as needed
}

```

--------------------------------------------------------------------------------
/tools/ordinals/index.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerGetInscriptionTool } from "./getInscription";
import { registerGetTokenByIdOrTickerTool } from "./getTokenByIdOrTicker";
import { registerMarketListingsTool } from "./marketListings";
import { registerMarketSalesTool } from "./marketSales";
import { registerSearchInscriptionsTool } from "./searchInscriptions";

/**
 * Register all Ordinals tools with the MCP server
 * @param server The MCP server instance
 */
export function registerOrdinalsTools(server: McpServer): void {
	// Register Ordinals-related tools
	registerGetInscriptionTool(server);
	registerSearchInscriptionsTool(server);
	registerMarketListingsTool(server);
	registerMarketSalesTool(server);
	registerGetTokenByIdOrTickerTool(server);
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
	"compilerOptions": {
		// Environment setup & latest features
		"lib": ["ESNext"],
		"target": "ESNext",
		"module": "ESNext",
		"moduleDetection": "force",
		"jsx": "react-jsx",
		"allowJs": true,

		// Bundler mode
		"moduleResolution": "bundler",
		"allowImportingTsExtensions": true,
		"verbatimModuleSyntax": true,
		"noEmit": true,

		// Best practices
		"strict": true,
		"skipLibCheck": true,
		"noFallthroughCasesInSwitch": true,
		"noUncheckedIndexedAccess": true,

		// Some stricter flags (disabled by default)
		"noUnusedLocals": false,
		"noUnusedParameters": false,
		"noPropertyAccessFromIndexSignature": false
	},
	"include": [
		"package.json",
		"*.ts",
		"tools/*.ts",
		"tools/**/*.ts",
		"prompts/*.ts",
		"prompts/**/*.ts",
		"resources/*.ts",
		"resources/**/*.ts",
		"LICENSE",
		"README.md",
		"CHANGELOG.md",
		"smithery.yaml"
	]
}

```

--------------------------------------------------------------------------------
/tools/a2b/call.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { z } from "zod";

// Schema for invoking another agent via A2A protocol
export const a2aCallArgsSchema = z.object({
	url: z.string().url().describe("Full agent-to-agent endpoint URL"),
	method: z.string().describe("A2A method name to invoke"),
	params: z
		.record(z.any())
		.optional()
		.describe("Payload parameters for the A2A call"),
});
export type A2aCallArgs = z.infer<typeof a2aCallArgsSchema>;

/**
 * Registers the a2a_call tool for agent-to-agent HTTP/SSE calls
 */
export function registerA2aCallTool(server: McpServer) {
	server.tool(
		"a2a_call",
		"Invoke a remote agent's A2A endpoint via HTTP/SSE",
		{ args: a2aCallArgsSchema },
		async ({ args }: { args: A2aCallArgs }, extra: RequestHandlerExtra) => {
			// TODO: implement HTTP request logic (e.g., fetch, SSE)
			return {
				content: [{ type: "text", text: "Not implemented" }],
				isError: true,
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/getPublicKey.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import type { z } from "zod";
import { getPublicKeyArgsSchema } from "./schemas";
import type { Wallet } from "./wallet";

// Use the schema imported from schemas.ts
export type GetPublicKeyArgs = z.infer<typeof getPublicKeyArgsSchema>;

/**
 * Register the getPublicKey tool
 */
export function registerGetPublicKeyTool(server: McpServer, wallet: Wallet) {
	server.tool(
		"wallet_getPublicKey",
		"Retrieves the current wallet's public key. This public key can be used for cryptographic operations like signature verification or encryption.",
		{ args: getPublicKeyArgsSchema },
		async (
			{ args }: { args: GetPublicKeyArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		) => {
			try {
				const result = await wallet.getPublicKey(args);
				return { content: [{ type: "text", text: JSON.stringify(result) }] };
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
	"name": "bsv-mcp",
	"module": "dist/index.js",
	"type": "module",
	"version": "0.0.36",
	"license": "MIT",
	"author": "satchmo",
	"description": "A collection of Bitcoin SV (BSV) tools for the Model Context Protocol (MCP) framework",
	"repository": {
		"type": "git",
		"url": "https://github.com/b-open-io/bsv-mcp"
	},
	"keywords": [
		"bitcoin",
		"bsv",
		"bitcoin-sv",
		"wallet",
		"ordinals",
		"blockchain",
		"1sat-ordinals",
		"explorer",
		"block explorer"
	],
	"files": [
		"dist/**/*.js",
		"package.json",
		"*.ts",
		"tools/*.ts",
		"tools/**/*.ts",
		"prompts/*.ts",
		"prompts/**/*.ts",
		"resources/*.ts",
		"resources/**/*.ts",
		"LICENSE",
		"README.md",
		"CHANGELOG.md",
		"smithery.yaml"
	],
	"bin": {
		"bsv-mcp": "./dist/index.js"
	},
	"private": false,
	"devDependencies": {
		"@biomejs/biome": "^1.9.4",
		"@types/bun": "latest"
	},
	"peerDependencies": {
		"typescript": "^5.8.3"
	},
	"dependencies": {
		"@bsv/sdk": "^1.4.20",
		"@modelcontextprotocol/sdk": "^1.10.2",
		"@types/node": "^22.15.2",
		"bun": "^1.2.10",
		"js-1sat-ord": "^0.1.81",
		"mnee": "^2.0.0",
		"satoshi-token": "^0.0.4",
		"sigma-protocol": "^0.1.6",
		"zod": "^3.24.3"
	},
	"scripts": {
		"build": "bun build ./index.ts --outdir ./dist --target node",
		"lint": "biome check .",
		"lint:fix": "biome check . --write"
	}
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/index.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

import { registerAuthPrompt } from "./auth";
import { registerCryptographyPrompt } from "./cryptography";
// Import all prompt registration functions
import { registerOverviewPrompt } from "./overview";
import { registerPrimitivesPrompt } from "./primitives";
import { registerScriptPrompt } from "./script";
import { registerTransactionPrompt } from "./transaction";
import { registerWalletPrompt } from "./wallet";

/**
 * Register all BSV SDK prompts with the MCP server
 * @param server The MCP server instance
 */
export function registerAllBsvSdkPrompts(server: McpServer): void {
	// Register all BSV SDK related prompts
	registerOverviewPrompt(server);
	registerWalletPrompt(server);
	registerTransactionPrompt(server);
	registerAuthPrompt(server);
	registerCryptographyPrompt(server);
	registerScriptPrompt(server);
	registerPrimitivesPrompt(server);
}

// Export all prompts
export { registerOverviewPrompt } from "./overview";
export { registerWalletPrompt } from "./wallet";
export { registerTransactionPrompt } from "./transaction";
export { registerAuthPrompt } from "./auth";
export { registerCryptographyPrompt } from "./cryptography";
export { registerScriptPrompt } from "./script";
export { registerPrimitivesPrompt } from "./primitives";

```

--------------------------------------------------------------------------------
/resources/changelog.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const CHANGELOG_URL =
	"https://raw.githubusercontent.com/b-open-io/bsv-mcp/master/CHANGELOG.md";

/**
 * Fetches the changelog from GitHub
 * @returns Promise that resolves to the changelog content
 */
async function fetchChangelog(): Promise<string> {
	try {
		const response = await fetch(CHANGELOG_URL);

		if (!response.ok) {
			throw new Error(
				`Failed to fetch changelog: ${response.status} ${response.statusText}`,
			);
		}

		return await response.text();
	} catch (error) {
		console.error("Error fetching changelog:", error);
		return "# BSV MCP Server Changelog\n\nError: Could not load changelog content.\nPlease check the server logs for more details.";
	}
}

/**
 * Register the changelog resource with the MCP server
 * @param server The MCP server instance
 */
export function registerChangelogResource(server: McpServer): void {
	server.resource(
		"bsv-mcp-changelog",
		"https://github.com/b-open-io/bsv-mcp/blob/main/CHANGELOG.md",
		{
			title: "BSV MCP Server Changelog",
			description: "Version history and changelog for the BSV MCP server",
		},
		async (uri) => {
			const changelogContent = await fetchChangelog();
			return {
				contents: [
					{
						uri: uri.href,
						text: changelogContent,
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/utils/conversion.ts:
--------------------------------------------------------------------------------

```typescript
import { Utils } from "@bsv/sdk";
import { z } from "zod";
const {
	toArray: bsvToArray,
	toBase64: bsvToBase64,
	toHex: bsvToHex,
	toUTF8: bsvToUTF8,
} = Utils;

const encodingSchema = z.enum(["utf8", "hex", "base64", "binary"]);

/**
 * Convert data between hex, base64, utf8, and binary (number array) formats.
 * @param data - The input data as a string (hex, base64, utf8, or JSON array string for binary)
 * @param from - The encoding of the input data
 * @param to - The desired encoding of the output data
 * @returns The converted data as a string (except for binary, which is a JSON array string)
 */
export function convertData({
	data,
	from,
	to,
}: {
	data: string;
	from: "hex" | "base64" | "utf8" | "binary";
	to: "hex" | "base64" | "utf8" | "binary";
}): string {
	encodingSchema.parse(from);
	encodingSchema.parse(to);

	let arr: number[];
	if (from === "binary") {
		try {
			arr = JSON.parse(data);
			if (!Array.isArray(arr) || !arr.every((n) => typeof n === "number")) {
				throw new Error();
			}
		} catch {
			throw new Error("Invalid binary input: must be a JSON array of numbers");
		}
	} else {
		arr = bsvToArray(data, from);
	}

	if (to === "binary") {
		return JSON.stringify(arr);
	}
	if (to === "hex") {
		return bsvToHex(arr);
	}
	if (to === "base64") {
		return bsvToBase64(arr);
	}
	if (to === "utf8") {
		return bsvToUTF8(arr);
	}
	throw new Error("Invalid 'to' encoding");
}

```

--------------------------------------------------------------------------------
/tools/mnee/parseTx.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import type { MneeInterface, ParseTxResponse } from "mnee";
import { z } from "zod";

/**
 * Schema for the parseTx tool arguments.
 */
export const parseTxArgsSchema = z.object({
	txid: z.string().describe("Transaction ID to parse"),
});

export type ParseTxArgs = z.infer<typeof parseTxArgsSchema>;

export function registerParseTxTool(
	server: McpServer,
	mnee: MneeInterface,
): void {
	server.tool(
		"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).",
		{ args: parseTxArgsSchema },
		async (
			{ args }: { args: ParseTxArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		): Promise<CallToolResult> => {
			try {
				const result: ParseTxResponse = await mnee.parseTx(args.txid);

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(result, null, 2),
						},
					],
				};
			} catch (error) {
				const msg = error instanceof Error ? error.message : String(error);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/getAddress.ts:
--------------------------------------------------------------------------------

```typescript
import { PrivateKey } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

/**
 * Register the tool to get the wallet address
 * @param server The MCP server instance
 */
export function registerGetAddressTool(server: McpServer): void {
	server.tool(
		"wallet_getAddress",
		"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.",
		{
			args: z
				.object({})
				.optional()
				.describe(
					"No parameters required - simply returns the current wallet address",
				),
		},
		async () => {
			try {
				const wif = process.env.PRIVATE_KEY_WIF;
				if (!wif) {
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify({
									error: "No private key available",
									message:
										"Please set PRIVATE_KEY_WIF environment variable with a valid Bitcoin SV private key in WIF format.",
									status: "error",
								}),
							},
						],
						isError: true,
					};
				}

				const privKey = PrivateKey.fromWif(wif);
				const address = privKey.toAddress();
				return {
					content: [
						{
							type: "text",
							text: JSON.stringify({ address, status: "ok" }),
						},
					],
				};
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return {
					content: [
						{
							type: "text",
							text: JSON.stringify({
								error: "Invalid private key",
								message: msg,
								status: "error",
							}),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/mnee/getBalance.ts:
--------------------------------------------------------------------------------

```typescript
import { PrivateKey } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import type { MneeInterface } from "mnee";
import { z } from "zod";

export const getBalanceArgsSchema = z.object({});

export type GetBalanceArgs = z.infer<typeof getBalanceArgsSchema>;

export function registerGetBalanceTool(
	server: McpServer,
	mnee: MneeInterface,
): void {
	server.tool(
		"mnee_getBalance",
		"Retrieves the current MNEE token balance for the wallet. Returns the balance in MNEE tokens.",
		{
			args: getBalanceArgsSchema,
		},
		async (
			{ args }: { args: GetBalanceArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		) => {
			try {
				// Get private key from wallet
				const privateKeyWif = process.env.PRIVATE_KEY_WIF;
				if (!privateKeyWif) {
					throw new Error(
						"Private key WIF not available in environment variables",
					);
				}
				const privateKey = PrivateKey.fromWif(privateKeyWif);
				if (!privateKey) {
					throw new Error("No private key available");
				}

				const address = privateKey.toAddress().toString();
				const balance = await mnee.balance(address);

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify({ balance }, null, 2),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/index.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerA2aCallTool } from "./a2b/call";
import { registerA2bDiscoverTool } from "./a2b/discover";
import { registerBsvTools } from "./bsv";
import { registerOrdinalsTools } from "./ordinals";
import { registerUtilsTools } from "./utils";

/**
 * Configuration options for tools
 *
 * These options can be controlled through environment variables:
 * - enableBsvTools: controlled by DISABLE_BSV_TOOLS
 * - enableOrdinalsTools: controlled by DISABLE_ORDINALS_TOOLS
 * - enableUtilsTools: controlled by DISABLE_UTILS_TOOLS
 * - enableA2bTools: controlled by DISABLE_A2B_TOOLS
 */
export interface ToolsConfig {
	enableBsvTools?: boolean;
	enableOrdinalsTools?: boolean;
	enableUtilsTools?: boolean;
	enableA2bTools?: boolean;
}

/**
 * Register all tools with the MCP server based on configuration
 * @param server The MCP server instance
 * @param config Configuration options
 */
export function registerAllTools(
	server: McpServer,
	config: ToolsConfig = {
		enableBsvTools: true,
		enableOrdinalsTools: true,
		enableUtilsTools: true,
		enableA2bTools: false,
	},
): void {
	const {
		enableBsvTools = true,
		enableOrdinalsTools = true,
		enableUtilsTools = true,
		enableA2bTools = false,
	} = config;

	// Register BSV-related tools
	if (enableBsvTools) {
		registerBsvTools(server);
	}

	// Register Ordinals-related tools
	if (enableOrdinalsTools) {
		registerOrdinalsTools(server);
	}

	// Register utility tools
	if (enableUtilsTools) {
		registerUtilsTools(server);
	}

	// Register agent-to-blockchain tools
	if (enableA2bTools) {
		// Register agent-to-blockchain discovery tool
		registerA2bDiscoverTool(server);

		// Register agent-to-agent call tool
		// registerA2aCallTool(server);
	}

	// Add more tool categories as needed
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/auth.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * BSV SDK Authentication Prompt
 *
 * Provides detailed information about the authentication functionality in the BSV SDK,
 * including identity protocols, certificates, and session management.
 */
export const BSV_SDK_AUTH_PROMPT = `
# BSV SDK - Authentication Module

The Authentication module in the BSV SDK provides robust mechanisms for identity management, peer authentication, and certificate handling on the Bitcoin SV blockchain.

## Key Components

This section includes a placeholder for detailed content about the BSV SDK authentication mechanisms.

## Core Features

- Identity management
- Certificate handling
- Peer authentication
- Session management

## Best Practices

1. **Security**: Follow best practices for authentication security
2. **Testing**: Test authentication flows thoroughly before production use
3. **Error Handling**: Implement proper error handling

For complete API documentation and additional authentication features, refer to the official BSV SDK documentation.
`;

/**
 * Register the BSV SDK Authentication prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerAuthPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_sdk_auth",
		"Detailed information about the authentication functionality in the BSV SDK, including identity protocols, certificates, and session management.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: BSV_SDK_AUTH_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/cryptography.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * BSV SDK Cryptography Prompt
 *
 * Provides detailed information about the cryptographic functionality in the BSV SDK,
 * including key generation, signing, encryption, and hashing.
 */
export const BSV_SDK_CRYPTOGRAPHY_PROMPT = `
# BSV SDK - Cryptography Module

The Cryptography module in the BSV SDK provides comprehensive tools for handling cryptographic operations required for secure Bitcoin transactions and applications.

## Key Cryptographic Operations

This section includes a placeholder for detailed content about the BSV SDK cryptographic operations.

## Core Features

- Key generation and management
- Digital signatures (ECDSA)
- Message signing and verification
- Encryption and decryption
- Hash functions (SHA-256, RIPEMD-160, etc.)

## Best Practices

1. **Key Security**: Always handle private keys securely
2. **Random Number Generation**: Use cryptographically secure random number generation
3. **Testing**: Verify cryptographic operations with known test vectors

For complete API documentation and additional cryptographic features, refer to the official BSV SDK documentation.
`;

/**
 * Register the BSV SDK Cryptography prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerCryptographyPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_sdk_cryptography",
		"Detailed information about the cryptographic functionality in the BSV SDK, including key generation, signing, encryption, and hashing.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: BSV_SDK_CRYPTOGRAPHY_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/primitives.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * BSV SDK Primitives Prompt
 *
 * Provides detailed information about the primitive data types and structures in the BSV SDK,
 * including Binary, Hex, Points, and other fundamental types.
 */
export const BSV_SDK_PRIMITIVES_PROMPT = `
# BSV SDK - Primitives Module

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.

## Core Primitive Types

This section includes a placeholder for detailed content about the primitive types available in the BSV SDK.

## Key Primitives

- Binary data handling
- Hex string conversion
- Point and curve operations
- Bitcoin-specific data structures
- Network message formats

## Common Operations

- Serialization and deserialization
- Type conversion
- Data validation
- Encoding and decoding

## Best Practices

1. **Type Safety**: Use appropriate types for Bitcoin operations
2. **Validation**: Validate input data before processing
3. **Performance**: Consider performance implications when working with large data structures

For complete API documentation and additional information about primitives, refer to the official BSV SDK documentation.
`;

/**
 * Register the BSV SDK Primitives prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerPrimitivesPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_sdk_primitives",
		"Detailed information about the primitive data types and structures in the BSV SDK, including Binary, Hex, Points, and other fundamental types.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: BSV_SDK_PRIMITIVES_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/script.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * BSV SDK Script Prompt
 *
 * Provides detailed information about the script functionality in the BSV SDK,
 * including Bitcoin Script operations, locking and unlocking scripts, and OP_CODES.
 */
export const BSV_SDK_SCRIPT_PROMPT = `
# BSV SDK - Script Module

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.

## Bitcoin Script Basics

This section includes a placeholder for detailed content about Bitcoin Script and its implementation in the BSV SDK.

## Core Features

- Creating and manipulating scripts
- Locking script (scriptPubKey) creation
- Unlocking script (scriptSig) creation
- Script verification and execution
- Support for all Bitcoin OP_CODES

## Common Script Types

- P2PKH (Pay to Public Key Hash)
- P2PK (Pay to Public Key)
- P2MS (Multi-signature)
- OP_RETURN (Data storage)
- Custom scripts

## Best Practices

1. **Testing**: Test scripts thoroughly before production use
2. **Security**: Be aware of potential script vulnerabilities
3. **Compatibility**: Ensure scripts are compatible with network rules

For complete API documentation and additional script features, refer to the official BSV SDK documentation.
`;

/**
 * Register the BSV SDK Script prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerScriptPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_sdk_script",
		"Detailed information about the script functionality in the BSV SDK, including Bitcoin Script operations, locking and unlocking scripts, and OP_CODES.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: BSV_SDK_SCRIPT_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/bsv/getPrice.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

// Define cache duration (5 minutes in milliseconds)
const PRICE_CACHE_DURATION = 5 * 60 * 1000;

// Cache object to store price data
let cachedPrice: { value: number; timestamp: number } | null = null;

/**
 * Get the BSV price with caching mechanism
 * @returns The current BSV price in USD
 */
async function getBsvPriceWithCache(): Promise<number> {
	// Return cached price if it's still valid
	if (
		cachedPrice &&
		Date.now() - cachedPrice.timestamp < PRICE_CACHE_DURATION
	) {
		return cachedPrice.value;
	}

	// If no valid cache, fetch new price
	const res = await fetch(
		"https://api.whatsonchain.com/v1/bsv/main/exchangerate",
	);
	if (!res.ok) throw new Error("Failed to fetch price");

	const data = (await res.json()) as {
		currency: string;
		rate: string;
		time: number;
	};

	const price = Number(data.rate);
	if (Number.isNaN(price) || price <= 0)
		throw new Error("Invalid price received");

	// Update cache
	cachedPrice = {
		value: price,
		timestamp: Date.now(),
	};

	return price;
}

/**
 * Register the BSV price lookup tool
 * @param server The MCP server instance
 */
export function registerGetPriceTool(server: McpServer): void {
	server.tool(
		"bsv_getPrice",
		"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.",
		{
			args: z
				.object({})
				.optional()
				.describe(
					"No parameters required - simply returns the current BSV price in USD",
				),
		},
		async () => {
			try {
				const price = await getBsvPriceWithCache();
				return {
					content: [
						{
							type: "text",
							text: `Current BSV price: $${price.toFixed(2)} USD`,
						},
					],
				};
			} catch (err) {
				return {
					content: [{ type: "text", text: "Error fetching BSV price." }],
					isError: true,
				};
			}
		},
	);
}

// Export the cached price getter for use in other modules
export { getBsvPriceWithCache };

```

--------------------------------------------------------------------------------
/tools/utils/index.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { convertData } from "./conversion";

const encodingSchema = z.enum(["utf8", "hex", "base64", "binary"]);

/**
 * Register the unified conversion tool with the MCP server
 * @param server The MCP server instance
 */
export function registerUtilsTools(server: McpServer): void {
	server.tool(
		"utils_convertData",
		"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" +
			"Parameters:\n" +
			"- data (required): The string to convert\n" +
			"- from (required): Source encoding format (utf8, hex, base64, or binary)\n" +
			"- to (required): Target encoding format (utf8, hex, base64, or binary)\n\n" +
			"Example usage:\n" +
			'- UTF-8 to hex: {"data": "hello world", "from": "utf8", "to": "hex"} → 68656c6c6f20776f726c64\n' +
			'- UTF-8 to base64: {"data": "Hello World", "from": "utf8", "to": "base64"} → SGVsbG8gV29ybGQ=\n' +
			'- base64 to UTF-8: {"data": "SGVsbG8gV29ybGQ=", "from": "base64", "to": "utf8"} → Hello World\n' +
			'- hex to base64: {"data": "68656c6c6f20776f726c64", "from": "hex", "to": "base64"} → aGVsbG8gd29ybGQ=\n\n' +
			"Notes:\n" +
			"- All parameters are required\n" +
			"- The tool returns the converted data as a string\n" +
			"- For binary conversion, data is represented as an array of byte values",
		{
			args: z.object({
				data: z.string().describe("The data string to be converted"),
				from: encodingSchema.describe(
					"Source encoding format (utf8, hex, base64, or binary)",
				),
				to: encodingSchema.describe(
					"Target encoding format to convert to (utf8, hex, base64, or binary)",
				),
			}),
		},
		async ({ args }) => {
			try {
				const result = convertData({
					data: args.data,
					from: args.from,
					to: args.to,
				});
				return {
					content: [
						{
							type: "text",
							text: result,
						},
					],
				};
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return {
					content: [
						{
							type: "text",
							text: `Error: ${msg}`,
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/refreshUtxos.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import { toBitcoin } from "satoshi-token";
import { z } from "zod";
import type { Wallet } from "./wallet";

const refreshUtxosArgsSchema = z.object({});
type RefreshUtxosArgs = z.infer<typeof refreshUtxosArgsSchema>;

/**
 * Registers the wallet_refreshUtxos tool that refreshes and returns the UTXOs for the wallet
 */
export function registerRefreshUtxosTool(server: McpServer, wallet: Wallet) {
	server.tool(
		"wallet_refreshUtxos",
		"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.",
		{ args: refreshUtxosArgsSchema },
		async (
			{ args }: { args: RefreshUtxosArgs },
			_extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		) => {
			try {
				// Force refresh the UTXOs
				await wallet.refreshUtxos();

				// Get the refreshed UTXOs
				const { paymentUtxos, nftUtxos } = await wallet.getUtxos();

				// Calculate total satoshis in payment UTXOs
				const totalSatoshis = paymentUtxos.reduce(
					(sum, utxo) => sum + utxo.satoshis,
					0,
				);

				// Format the response
				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(
								{
									status: "success",
									paymentUtxos: paymentUtxos.map((utxo) => ({
										txid: utxo.txid,
										vout: utxo.vout,
										satoshis: utxo.satoshis,
										outpoint: `${utxo.txid}_${utxo.vout}`,
									})),
									nftUtxos: nftUtxos.map((utxo) => ({
										txid: utxo.txid,
										vout: utxo.vout,
										origin: utxo.origin,
										outpoint: `${utxo.txid}_${utxo.vout}`,
									})),
									totalPaymentUtxos: paymentUtxos.length,
									totalNftUtxos: nftUtxos.length,
									totalSatoshis: totalSatoshis,
									totalBsv: toBitcoin(totalSatoshis),
								},
								null,
								2,
							),
						},
					],
				};
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/sendToAddress.ts:
--------------------------------------------------------------------------------

```typescript
import { P2PKH } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { toSatoshi } from "satoshi-token";
import type { z } from "zod";
import { getBsvPriceWithCache } from "../bsv/getPrice";
import { sendToAddressArgsSchema } from "./schemas";
import type { Wallet } from "./wallet";

// Use the schema imported from schemas.ts
export type SendToAddressArgs = z.infer<typeof sendToAddressArgsSchema>;

/**
 * Register the sendToAddress tool
 */
export function registerSendToAddressTool(server: McpServer, wallet: Wallet) {
	server.tool(
		"wallet_sendToAddress",
		"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.",
		{
			args: sendToAddressArgsSchema,
		},
		async (
			{ args }: { args: SendToAddressArgs },
			extra: RequestHandlerExtra,
		) => {
			try {
				const {
					address,
					amount,
					currency = "BSV",
					description = "Send to address",
				} = args;

				// Convert to satoshis
				let satoshis: number;
				if (currency === "USD") {
					// Get current BSV price
					const bsvPriceUsd = await getBsvPriceWithCache();

					// Convert USD to BSV
					const bsvAmount = amount / bsvPriceUsd;

					// Convert BSV to satoshis using the library
					satoshis = toSatoshi(bsvAmount);
				} else {
					// Convert BSV to satoshis using the library
					satoshis = toSatoshi(amount);
				}

				// Create P2PKH script from address
				const lockingScript = new P2PKH().lock(address);

				// Create the transaction
				const tx = await wallet.createAction({
					description,
					outputs: [
						{
							lockingScript: lockingScript.toHex(),
							satoshis,
							outputDescription: `Payment to ${address}`,
						},
					],
				});

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify({
								status: "success",
								txid: tx.txid,
								satoshis,
							}),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/ordinals/getInscription.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { z } from "zod";

// Schema for get inscription arguments
export const getInscriptionArgsSchema = z.object({
	outpoint: z.string().describe("Outpoint in format 'txid_vout'"),
});

export type GetInscriptionArgs = z.infer<typeof getInscriptionArgsSchema>;

// Inscription API response type
interface InscriptionResponse {
	outpoint: string;
	origin: {
		outpoint: string;
		data?: {
			insc?: {
				text?: string;
				json?: unknown;
				file?: {
					hash?: string;
					size?: number;
					type?: string;
				};
			};
		};
	};
	height?: number;
	idx?: number;
	satoshis?: number;
	script?: string;
	spend?: string;
	[key: string]: unknown;
}

/**
 * Register the Ordinals inscription lookup tool
 */
export function registerGetInscriptionTool(server: McpServer): void {
	server.tool(
		"ordinals_getInscription",
		"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.",
		{
			args: getInscriptionArgsSchema,
		},
		async (
			{ args }: { args: GetInscriptionArgs },
			extra: RequestHandlerExtra,
		) => {
			try {
				const { outpoint } = args;

				// Validate outpoint format
				if (!/^[0-9a-f]{64}_\d+$/i.test(outpoint)) {
					throw new Error("Invalid outpoint format. Expected 'txid_vout'");
				}

				// Fetch inscription data from GorillaPool API
				const response = await fetch(
					`https://ordinals.gorillapool.io/api/inscriptions/${outpoint}`,
				);

				if (response.status === 404) {
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify({ error: "Inscription not found" }),
							},
						],
					};
				}

				if (!response.ok) {
					throw new Error(
						`API error: ${response.status} ${response.statusText}`,
					);
				}

				const data = (await response.json()) as InscriptionResponse;

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(data, null, 2),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
build:
  dockerBuildPath: ./ 
  
startCommand:
  type: stdio
  configSchema:
    type: object
    properties:
      privateKeyWif:
        type: string
        title: "Private Key (WIF)"
        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."
      disablePrompts:
        type: boolean
        title: "Disable Prompts"
        description: "Set to true to disable all educational prompts"
        default: false
      disableResources:
        type: boolean
        title: "Disable Resources"
        description: "Set to true to disable all resources (BRCs, changelog)"
        default: false
      disableTools:
        type: boolean
        title: "Disable All Tools"
        description: "Set to true to disable all tools"
        default: false
      disableWalletTools:
        type: boolean
        title: "Disable Wallet Tools"
        description: "Set to true to disable Bitcoin wallet tools"
        default: false
      disableMneeTools:
        type: boolean
        title: "Disable MNEE Tools"
        description: "Set to true to disable MNEE token tools"
        default: false
      disableBsvTools:
        type: boolean
        title: "Disable BSV Blockchain Tools"
        description: "Set to true to disable BSV blockchain tools"
        default: false
      disableOrdinalsTools:
        type: boolean
        title: "Disable Ordinals Tools"
        description: "Set to true to disable Ordinals/NFT tools"
        default: false
      disableUtilsTools:
        type: boolean
        title: "Disable Utility Tools"
        description: "Set to true to disable utility tools"
        default: false
    additionalProperties: false
  commandFunction: |
    (config) => {
      const env = {};
      
      // Add private key if provided
      if (config.privateKeyWif) {
        env.PRIVATE_KEY_WIF = config.privateKeyWif;
      }
      
      // Map boolean config options to environment variables
      if (config.disablePrompts) env.DISABLE_PROMPTS = 'true';
      if (config.disableResources) env.DISABLE_RESOURCES = 'true';
      if (config.disableTools) env.DISABLE_TOOLS = 'true';
      if (config.disableWalletTools) env.DISABLE_WALLET_TOOLS = 'true';
      if (config.disableMneeTools) env.DISABLE_MNEE_TOOLS = 'true';
      if (config.disableBsvTools) env.DISABLE_BSV_TOOLS = 'true';
      if (config.disableOrdinalsTools) env.DISABLE_ORDINALS_TOOLS = 'true';
      if (config.disableUtilsTools) env.DISABLE_UTILS_TOOLS = 'true';
      
      return { 
        command: 'bun', 
        args: ['run', 'index.ts'],
        env
      };
    }

```

--------------------------------------------------------------------------------
/tools/mnee/sendMnee.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import type Mnee from "mnee";
import type { SendMNEE, TransferResponse } from "mnee";
import { z } from "zod";

/**
 * Schema for the sendMnee tool arguments.
 */
export const sendMneeArgsSchema = z.object({
	address: z.string().describe("The recipient's address"),
	amount: z.number().describe("Amount to send"),
	currency: z
		.enum(["MNEE", "USD"])
		.default("MNEE")
		.describe("Currency of the amount (MNEE or USD)"),
});

export type SendMneeArgs = z.infer<typeof sendMneeArgsSchema>;

/**
 * Format a number as USD
 */
function formatUSD(amount: number): string {
	return new Intl.NumberFormat("en-US", {
		style: "currency",
		currency: "USD",
		minimumFractionDigits: 2,
		maximumFractionDigits: 2,
	}).format(amount);
}

/**
 * Registers the mnee_sendMnee tool for sending MNEE tokens
 */
export function registerSendMneeTool(server: McpServer, mnee: Mnee): void {
	server.tool(
		"mnee_sendMnee",
		"Send MNEE tokens to a specified address",
		{ args: sendMneeArgsSchema },
		async (
			{ args }: { args: SendMneeArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		): Promise<CallToolResult> => {
			try {
				// Since 1 MNEE = $1, the amount is the same in both currencies
				const mneeAmount = args.amount;

				const transferRequest: SendMNEE[] = [
					{
						address: args.address,
						amount: mneeAmount,
					},
				];

				// Get WIF from environment
				const wif = process.env.PRIVATE_KEY_WIF;
				if (!wif) {
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify(
									{
										success: false,
										error: "No private key available",
										message:
											"Please set PRIVATE_KEY_WIF environment variable with a valid Bitcoin SV private key in WIF format.",
									},
									null,
									2,
								),
							},
						],
						isError: true,
					};
				}

				const result: TransferResponse = await mnee.transfer(
					transferRequest,
					wif,
				);

				if (result.error) {
					throw new Error(result.error);
				}

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(
								{
									success: true,
									txid: result.txid,
									rawtx: result.rawtx,
									mneeAmount: mneeAmount,
									usdAmount: formatUSD(mneeAmount),
									recipient: args.address,
								},
								null,
								2,
							),
						},
					],
				};
			} catch (error) {
				const msg = error instanceof Error ? error.message : String(error);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/ordinals/getTokenByIdOrTicker.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { z } from "zod";

// Schema for get token by ID or ticker arguments
export const getTokenByIdOrTickerArgsSchema = z
	.object({
		id: z
			.string()
			.optional()
			.describe("BSV20 token ID in outpoint format (txid_vout)"),
		tick: z.string().optional().describe("BSV20 token ticker symbol"),
	})
	.refine((data) => data.id || data.tick, {
		message: "Either id or tick must be provided",
	});

export type GetTokenByIdOrTickerArgs = z.infer<
	typeof getTokenByIdOrTickerArgsSchema
>;

// BSV20 token response type
interface TokenResponse {
	id: string;
	tick?: string;
	sym?: string;
	max?: string;
	lim?: string;
	dec?: number;
	supply?: string;
	amt?: string;
	status?: number;
	icon?: string;
	height?: number;
	[key: string]: unknown;
}

/**
 * Register the BSV20 token lookup tool
 */
export function registerGetTokenByIdOrTickerTool(server: McpServer): void {
	server.tool(
		"ordinals_getTokenByIdOrTicker",
		"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.",
		{
			args: getTokenByIdOrTickerArgsSchema,
		},
		async (
			{ args }: { args: GetTokenByIdOrTickerArgs },
			extra: RequestHandlerExtra,
		) => {
			try {
				const { id, tick } = args;

				// Validate that at least one of id or tick is provided
				if (!id && !tick) {
					throw new Error("Either token ID or ticker symbol must be provided");
				}

				// Validate ID format if provided
				if (id && !/^[0-9a-f]{64}_\d+$/i.test(id)) {
					throw new Error("Invalid BSV20 ID format. Expected 'txid_vout'");
				}

				// Determine which endpoint to use based on provided parameters
				let endpoint: string;
				if (id) {
					endpoint = `https://ordinals.gorillapool.io/api/bsv20/id/${id}`;
				} else {
					endpoint = `https://ordinals.gorillapool.io/api/bsv20/tick/${tick}`;
				}

				// Fetch BSV20 token data from GorillaPool API
				const response = await fetch(endpoint);

				if (response.status === 404) {
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify({ error: "BSV20 token not found" }),
							},
						],
					};
				}

				if (!response.ok) {
					throw new Error(
						`API error: ${response.status} ${response.statusText}`,
					);
				}

				const data = (await response.json()) as TokenResponse;

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(data, null, 2),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/overview.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * BSV SDK Overview Prompt
 *
 * Provides a general overview of the Bitcoin SV SDK,
 * including what it is, its purpose, and its main components.
 */
export const BSV_SDK_OVERVIEW_PROMPT = `
# BSV SDK - Overview

The BSV SDK is a comprehensive TypeScript/JavaScript library designed to provide a unified and modern 
layer for developing scalable applications on the Bitcoin SV blockchain. This SDK addresses limitations 
of previous tools by offering a fresh approach that adheres to the principles of SPV (Simplified Payment 
Verification) while ensuring privacy and scalability.

## Core Objectives

- Provide a unified, modern API for Bitcoin SV development
- Enable secure, peer-to-peer operations
- Support SPV (Simplified Payment Verification) principles
- Ensure privacy and scalability in blockchain applications
- Simplify integration with the Bitcoin SV ecosystem

## Main Components

The BSV SDK is organized into several key modules:

1. **Wallet**: Manage keys, addresses, and UTXOs
2. **Transaction**: Build and manipulate Bitcoin transactions
3. **Auth**: Authentication and identity protocols
4. **Cryptography**: Signing, encryption, and verification
5. **Script**: Bitcoin scripting and contract capabilities
6. **Primitives**: Core data types and structures
7. **Messages**: Network message handling
8. **Overlay Tools**: Additional utilities and extensions

## Getting Started

To use the BSV SDK in your project:

\`\`\`bash
# Install with npm
npm install @bsv/sdk

# Or with yarn
yarn add @bsv/sdk
\`\`\`

Then import the components you need:

\`\`\`typescript
import { PrivateKey, Transaction } from "@bsv/sdk";
\`\`\`

## Use Cases

- Wallet applications
- Payment systems
- Smart contract platforms
- Token systems
- Identity solutions
- Data storage and verification

## Additional Resources

For detailed information about specific components, please see the dedicated prompts for each module:
- Wallet operations: Use prompt "bitcoin_sv_sdk_wallet"
- Transaction building: Use prompt "bitcoin_sv_sdk_transaction"
- Authentication: Use prompt "bitcoin_sv_sdk_auth"
- Cryptography: Use prompt "bitcoin_sv_sdk_cryptography"
- Scripting: Use prompt "bitcoin_sv_sdk_script"
- Primitives: Use prompt "bitcoin_sv_sdk_primitives"

For official documentation, visit the BSV Blockchain Libraries Project repository.
`;

/**
 * Register the BSV SDK Overview prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerOverviewPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_sdk_overview",
		"General overview of the Bitcoin SV SDK, including its purpose and main components.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: BSV_SDK_OVERVIEW_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/fetchPaymentUtxos.ts:
--------------------------------------------------------------------------------

```typescript
import { P2PKH, Transaction, Utils } from "@bsv/sdk";
import type { Utxo } from "js-1sat-ord";
const { toBase64 } = Utils;

/**
 * Type definition for WhatsOnChain UTXO response
 */
interface WhatsOnChainUtxo {
	tx_hash: string;
	tx_pos: number;
	value: number;
	height: number;
	address?: string;
}

/**
 * Fetches unspent transaction outputs (UTXOs) for a given address.
 * Only returns confirmed unspent outputs.
 *
 * @param address - The address to fetch UTXOs for
 * @returns Array of UTXOs or undefined if an error occurs
 */
export async function fetchPaymentUtxos(
	address: string,
): Promise<Utxo[] | undefined> {
	if (!address) {
		console.error("fetchPaymentUtxos: No address provided");
		return undefined;
	}

	try {
		// Fetch UTXOs from WhatsOnChain API
		const response = await fetch(
			`https://api.whatsonchain.com/v1/bsv/main/address/${address}/unspent`,
		);

		if (!response.ok) {
			console.error(
				`WhatsOnChain API error: ${response.status} ${response.statusText}`,
			);
			return undefined;
		}

		const data = (await response.json()) as WhatsOnChainUtxo[];

		// Validate response format
		if (!Array.isArray(data)) {
			console.error("Invalid response format from WhatsOnChain API");
			return undefined;
		}

		// For testing purposes (FOR TESTING ONLY - REMOVE IN PRODUCTION)
		// const limitUTXOs = data.slice(0, 2);

		// Process each UTXO
		const utxos: (Utxo | null)[] = await Promise.all(
			data.map(async (utxo: WhatsOnChainUtxo) => {
				// Get the transaction hex to extract the correct script
				const script = await getScriptFromTransaction(
					utxo.tx_hash,
					utxo.tx_pos,
				);

				if (!script) {
					console.warn(
						`Could not get script for UTXO: ${utxo.tx_hash}:${utxo.tx_pos}`,
					);
					return null;
				}

				return {
					txid: utxo.tx_hash,
					vout: utxo.tx_pos,
					satoshis: utxo.value,
					script: script,
				};
			}),
		);

		// Filter out any null entries from failed processing
		const validUtxos = utxos.filter((utxo) => utxo !== null) as Utxo[];

		return validUtxos;
	} catch (error) {
		console.error("Error fetching payment UTXOs:", error);
		return undefined;
	}
}

/**
 * Gets the script from a transaction for a specific output index
 *
 * @param txid - The transaction ID
 * @param vout - The output index
 * @returns The script as hex string or undefined if an error occurs
 */
async function getScriptFromTransaction(
	txid: string,
	vout: number,
): Promise<string | undefined> {
	try {
		const response = await fetch(
			`https://api.whatsonchain.com/v1/bsv/main/tx/${txid}/hex`,
		);

		if (!response.ok) {
			console.error(
				`WhatsOnChain API error fetching tx hex: ${response.status} ${response.statusText}`,
			);
			return undefined;
		}

		const txHex = await response.text();
		const tx = Transaction.fromHex(txHex);
		const output = tx.outputs[vout];

		if (!output) {
			console.error(`Output index ${vout} not found in transaction ${txid}`);
			return undefined;
		}

		return toBase64(output.lockingScript.toBinary());
	} catch (error) {
		console.error(
			`Error getting script for transaction ${txid}:${vout}:`,
			error,
		);
		return undefined;
	}
}

```

--------------------------------------------------------------------------------
/prompts/ordinals.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * 1Sat Ordinals Prompt
 *
 * Provides comprehensive information about Bitcoin SV ordinals,
 * including what they are, how they work, and how to use them.
 */
export const ORDINALS_PROMPT = `
# 1Sat Ordinals - Comprehensive Guide

Ordinals are a way to uniquely identify and track specific satoshis (the smallest unit of Bitcoin) 
on the blockchain. This concept allows for "inscriptions" - embedding data directly into a satoshi, 
effectively creating NFT-like functionality native to the Bitcoin protocol.

## Key Concepts

1. **Ordinal Theory**: Each satoshi has a unique position in the Bitcoin ledger, determined by the order 
   in which they were mined.

2. **Inscriptions**: Content embedded directly into a specific satoshi. Can be any valid content type.

3. **On-chain Storage**: All ordinal data is stored immutably on the blockchain.

## BSV Ordinals (1Sat Ordinals) vs. BTC Ordinals

- 1Sat Ordinals leverage the larger block sizes and lower fees of Bitcoin SV, making them more practical
  for storing meaningful data and media.
  
- 1Sat Ordinals can store much larger inscriptions compared to BTC, enabling richer media and applications.

- 1Sat Ordinals typically cost a fraction of what BTC ordinals cost to create and transfer.

## Creating Ordinals

To create a BSV ordinal:

1. Choose the content to inscribe (image, text, audio, etc.)
2. Use a compatible wallet or service that supports ordinal creation
3. Pay the transaction fee to inscribe your content on-chain
4. Receive a unique ordinal ID that references your specific satoshi

## Transferring Ordinals

Ordinals are transferred by sending the specific satoshi that contains the inscription. Compatible wallets
ensure that when you transfer an ordinal, the specific satoshi containing the inscription is included in
the transaction.

## Viewing Ordinals

Ordinal inscriptions can be viewed through:

1. Specialized ordinal explorers
2. Compatible wallets with ordinal support
3. Marketplaces that support BSV ordinals

## Use Cases

- Digital Art and Collectibles
- Certificates of Authenticity
- Domain Names
- Documentation and Verification
- Gaming Assets
- Media Distribution

## Best Practices

- Verify file sizes and transaction costs before inscribing
- Use appropriate file formats optimized for on-chain storage
- Keep private keys secure to maintain ownership of valuable ordinals
- Consider using a specialized wallet for managing valuable ordinal collections

For technical implementation details, refer to the official documentation and BSV ordinals standards.
`;

/**
 * Register the Ordinals prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerOrdinalsPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_ordinals",
		"Comprehensive information about Bitcoin SV ordinals, including what they are, how they work, and how to use them.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: ORDINALS_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/ordinals/searchInscriptions.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { z } from "zod";

// Schema for search inscriptions arguments
export const searchInscriptionsArgsSchema = z.object({
	limit: z
		.number()
		.int()
		.min(1)
		.max(100)
		.default(20)
		.describe("Number of results (1-100, default 20)"),
	offset: z.number().int().min(0).default(0).describe("Pagination offset"),
	dir: z
		.enum(["asc", "desc"])
		.default("desc")
		.describe("Sort direction (asc or desc)"),
	num: z.string().optional().describe("Inscription number"),
	origin: z.string().optional().describe("Origin outpoint"),
	address: z.string().optional().describe("Bitcoin address"),
	map: z.string().optional().describe("Map field"),
	terms: z.string().optional().describe("Search terms"),
	mime: z.string().optional().describe("MIME type filter"),
});

export type SearchInscriptionsArgs = z.infer<
	typeof searchInscriptionsArgsSchema
>;

// Simplified inscription response type
interface InscriptionSearchResponse {
	results: Array<{
		outpoint: string;
		origin: {
			outpoint: string;
			data?: {
				insc?: {
					text?: string;
					file?: {
						type?: string;
						size?: number;
					};
				};
			};
		};
		height?: number;
		satoshis?: number;
		[key: string]: unknown;
	}>;
	total: number;
}

/**
 * Register the Ordinals search inscriptions tool
 */
export function registerSearchInscriptionsTool(server: McpServer): void {
	server.tool(
		"ordinals_searchInscriptions",
		"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.",
		{
			args: searchInscriptionsArgsSchema,
		},
		async (
			{ args }: { args: SearchInscriptionsArgs },
			extra: RequestHandlerExtra,
		) => {
			try {
				const { limit, offset, dir, num, origin, address, map, terms, mime } =
					args;

				// Build the URL with query parameters
				const url = new URL(
					"https://ordinals.gorillapool.io/api/inscriptions/search",
				);
				url.searchParams.append("limit", limit.toString());
				url.searchParams.append("offset", offset.toString());
				url.searchParams.append("dir", dir);

				if (num) url.searchParams.append("num", num);
				if (origin) url.searchParams.append("origin", origin);
				if (address) url.searchParams.append("address", address);
				if (map) url.searchParams.append("map", map);
				if (terms) url.searchParams.append("terms", terms);
				if (mime) url.searchParams.append("mime", mime);

				// Fetch inscriptions data from GorillaPool API
				const response = await fetch(url.toString());

				if (!response.ok) {
					throw new Error(
						`API error: ${response.status} ${response.statusText}`,
					);
				}

				const data = (await response.json()) as InscriptionSearchResponse;

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(data, null, 2),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/ordinals/marketSales.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { z } from "zod";

// Schema for unified market sales arguments
export const marketSalesArgsSchema = z.object({
	// Common parameters
	limit: z
		.number()
		.int()
		.min(1)
		.max(100)
		.default(20)
		.describe("Number of results (1-100, default 20)"),
	offset: z.number().int().min(0).default(0).describe("Pagination offset"),
	dir: z
		.enum(["asc", "desc"])
		.default("desc")
		.describe("Sort direction (asc or desc)"),
	address: z.string().optional().describe("Bitcoin address"),

	// Token-specific parameters
	tokenType: z
		.enum(["bsv20", "bsv21", "all"])
		.default("bsv20")
		.describe("Type of token to search for (bsv20, bsv21, or all)"),
	id: z.string().optional().describe("Token ID in outpoint format"),
	tick: z.string().optional().describe("Token ticker symbol"),
	pending: z
		.boolean()
		.default(false)
		.optional()
		.describe("Include pending sales"),
});

export type MarketSalesArgs = z.infer<typeof marketSalesArgsSchema>;

// Unified response type for token sales
interface MarketSaleResponse {
	results: Array<{
		outpoint: string;
		data?: {
			bsv20?: {
				id?: string;
				tick?: string;
				sym?: string;
				amt?: string;
				op?: string;
			};
			list?: {
				price?: number;
				payout?: string;
				sale?: boolean;
			};
		};
		satoshis?: number;
		height?: number;
		owner?: string;
		spend?: string;
		spendHeight?: number;
		spendIdx?: string;
		[key: string]: unknown;
	}>;
	total: number;
}

/**
 * Register the unified market sales tool
 * Handles BSV20 and BSV21 token sales
 */
export function registerMarketSalesTool(server: McpServer): void {
	server.tool(
		"ordinals_marketSales",
		"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.",
		{
			args: marketSalesArgsSchema,
		},
		async ({ args }: { args: MarketSalesArgs }, extra: RequestHandlerExtra) => {
			try {
				const { limit, offset, dir, tokenType, id, tick, pending, address } =
					args;

				// Determine the API endpoint based on tokenType
				let baseUrl = "https://ordinals.gorillapool.io/api";

				// Default to BSV20 for all token types
				baseUrl += "/bsv20/market/sales";

				// Build the URL with query parameters
				const url = new URL(baseUrl);
				url.searchParams.append("limit", limit.toString());
				url.searchParams.append("offset", offset.toString());
				url.searchParams.append("dir", dir);

				// Add type parameter for bsv21 if needed
				if (tokenType === "bsv21") {
					url.searchParams.append("type", "v2");
				}

				if (id) url.searchParams.append("id", id);
				if (tick) url.searchParams.append("tick", tick);
				if (pending !== undefined)
					url.searchParams.append("pending", pending.toString());
				if (address) url.searchParams.append("address", address);

				// Fetch market sales from GorillaPool API
				const response = await fetch(url.toString());

				if (!response.ok) {
					throw new Error(
						`API error: ${response.status} ${response.statusText}`,
					);
				}

				const data = (await response.json()) as MarketSaleResponse;

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(data, null, 2),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env bun
import { PrivateKey } from "@bsv/sdk";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { registerAllPrompts } from "./prompts/index.ts";
import { registerResources } from "./resources/resources.ts";
import { registerAllTools } from "./tools/index.ts";
import { registerMneeTools } from "./tools/mnee/index.ts";
import { registerWalletTools } from "./tools/wallet/tools.ts";
import { Wallet } from "./tools/wallet/wallet.ts";

/**
 * Configuration options from environment variables
 */
const CONFIG = {
	// Whether to load various components
	loadPrompts: process.env.DISABLE_PROMPTS !== "true",
	loadResources: process.env.DISABLE_RESOURCES !== "true",
	loadTools: process.env.DISABLE_TOOLS !== "true",

	// Fine-grained tool category control
	loadWalletTools: process.env.DISABLE_WALLET_TOOLS !== "true",
	loadMneeTools: process.env.DISABLE_MNEE_TOOLS !== "true",
	loadBsvTools: process.env.DISABLE_BSV_TOOLS !== "true",
	loadOrdinalsTools: process.env.DISABLE_ORDINALS_TOOLS !== "true",
	loadUtilsTools: process.env.DISABLE_UTILS_TOOLS !== "true",
	loadA2bTools: process.env.ENABLE_A2B_TOOLS === "true",

	// Transaction broadcasting control
	disableBroadcasting: process.env.DISABLE_BROADCASTING === "true",
};

/**
 * Try to initialize the private key from environment variables
 * Returns the private key if valid, or undefined if not present or invalid
 */
function initializePrivateKey(): PrivateKey | undefined {
	const privateKeyWif = process.env.PRIVATE_KEY_WIF;

	// Check if private key is set
	if (!privateKeyWif) {
		// stdio protocol you cant send anything to console!!
		// console.warn(
		// 	"\x1b[33mWarning: PRIVATE_KEY_WIF environment variable is not set\x1b[0m",
		// );
		// console.warn(
		// 	"The server will run, but wallet operations requiring a private key will return errors.",
		// );
		// console.warn(
		// 	"Set this variable with a valid Bitcoin SV private key in WIF format to enable all features:",
		// );
		// console.warn(
		// 	"Example: PRIVATE_KEY_WIF=your_private_key_wif bun run index.ts",
		// );
		return undefined;
	}

	// Validate the private key format
	try {
		return PrivateKey.fromWif(privateKeyWif);
	} catch (error) {
		// console.warn("\x1b[33mWarning: Invalid private key format\x1b[0m");
		// console.warn(
		// 	"The PRIVATE_KEY_WIF provided is not a valid Bitcoin SV private key in WIF format.",
		// );
		// console.warn(
		// 	"The server will run, but wallet operations requiring a private key will return errors.",
		// );
		return undefined;
	}
}

// Try to initialize private key but don't stop the server if missing or invalid
const privKey = initializePrivateKey();

const server = new McpServer(
	{ name: "Bitcoin SV", version: "0.0.36" },
	{
		capabilities: {
			prompts: {},
			resources: {},
			tools: {},
		},
		instructions: `
			This server exposes Bitcoin SV helpers.
			Tools are idempotent unless marked destructive.
		`,
	},
);

// Initialize wallet and register tools based on configuration
let wallet: Wallet | null = null;

if (CONFIG.loadTools) {
	if (privKey) {
		// Register MNEE tools if enabled
		if (CONFIG.loadMneeTools) {
			registerMneeTools(server);
		}

		// Initialize wallet with the private key if wallet tools are enabled
		if (CONFIG.loadWalletTools) {
			wallet = new Wallet(privKey);
			registerWalletTools(server, wallet, {
				disableBroadcasting: CONFIG.disableBroadcasting,
				enableA2bTools: CONFIG.loadA2bTools,
			});
		}
	}

	// Register all other tools based on configuration
	registerAllTools(server, {
		enableBsvTools: CONFIG.loadBsvTools,
		enableOrdinalsTools: CONFIG.loadOrdinalsTools,
		enableUtilsTools: CONFIG.loadUtilsTools,
		enableA2bTools: CONFIG.loadA2bTools,
	});
}

// Register resources if enabled
if (CONFIG.loadResources) {
	registerResources(server);
}

// Register prompts if enabled
if (CONFIG.loadPrompts) {
	registerAllPrompts(server);
}

// Connect to the transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("BSV MCP Server running on stdio");

```

--------------------------------------------------------------------------------
/tools/wallet/transferOrdToken.ts:
--------------------------------------------------------------------------------

```typescript
import { PrivateKey } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import {
	type Distribution,
	type LocalSigner,
	type Payment,
	type TokenChangeResult,
	TokenInputMode,
	TokenSelectionStrategy,
	TokenType,
	type TokenUtxo,
	type TransferOrdTokensConfig,
	type Utxo,
	selectTokenUtxos,
	transferOrdTokens,
} from "js-1sat-ord";
import { z } from "zod";
import type { Wallet } from "./wallet";

// Schema for BSV-20/BSV-21 token transfer arguments
export const transferOrdTokenArgsSchema = z.object({
	protocol: z.enum(["bsv-20", "bsv-21"]),
	tokenID: z.string(),
	sendAmount: z.number(),
	paymentUtxos: z.array(
		z.object({
			txid: z.string(),
			vout: z.number(),
			satoshis: z.number(),
			script: z.string(),
		}),
	),
	tokenUtxos: z.array(
		z.object({
			txid: z.string(),
			vout: z.number(),
			satoshis: z.literal(1),
			script: z.string(),
			amt: z.string(),
			id: z.string(),
		}),
	),
	distributions: z.array(z.object({ address: z.string(), tokens: z.number() })),
	decimals: z.number(),
	additionalPayments: z
		.array(z.object({ to: z.string(), amount: z.number() }))
		.optional(),
});
export type TransferOrdTokenArgs = z.infer<typeof transferOrdTokenArgsSchema>;

/**
 * Register the wallet_transferOrdToken tool for transferring BSV tokens.
 */
export function registerTransferOrdTokenTool(
	server: McpServer,
	wallet: Wallet,
) {
	server.tool(
		"wallet_transferOrdToken",
		"Transfers BSV-20 or BSV-21 tokens from your wallet via js-1sat-ord transferOrdTokens.",
		{ args: transferOrdTokenArgsSchema },
		async (
			{ args }: { args: TransferOrdTokenArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		) => {
			try {
				// fetch keys
				const paymentPk = wallet.getPrivateKey();
				if (!paymentPk) throw new Error("No private key available");
				const ordPk = paymentPk;
				const changeAddress = paymentPk.toAddress().toString();
				const ordAddress = changeAddress;

				// select token UTXOs
				const { selectedUtxos: inputTokens } = selectTokenUtxos(
					args.tokenUtxos as TokenUtxo[],
					args.sendAmount,
					args.decimals,
					{
						inputStrategy: TokenSelectionStrategy.SmallestFirst,
						outputStrategy: TokenSelectionStrategy.LargestFirst,
					},
				);

				// build config
				const config: TransferOrdTokensConfig = {
					protocol:
						args.protocol === "bsv-20" ? TokenType.BSV20 : TokenType.BSV21,
					tokenID: args.tokenID,
					utxos: args.paymentUtxos as Utxo[],
					inputTokens,
					distributions: args.distributions as Distribution[],
					tokenChangeAddress: ordAddress,
					changeAddress,
					paymentPk,
					ordPk,
					additionalPayments: (args.additionalPayments as Payment[]) || [],
					decimals: args.decimals,
					inputMode: TokenInputMode.Needed,
					splitConfig: {
						outputs: inputTokens.length === 1 ? 2 : 1,
						threshold: args.sendAmount,
					},
				};

				const identityPk = process.env.IDENTITY_KEY_WIF
					? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
					: undefined;

				if (identityPk) {
					config.signer = {
						idKey: identityPk,
					} as LocalSigner;
				}

				// execute transfer
				const result: TokenChangeResult = await transferOrdTokens(config);
				const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
				if (!disableBroadcasting) {
					await result.tx.broadcast();

					// refresh UTXOs
					try {
						await wallet.refreshUtxos();
					} catch {}

					// respond
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify({
									txid: result.tx.id("hex"),
									spentOutpoints: result.spentOutpoints,
									payChange: result.payChange,
									tokenChange: result.tokenChange,
								}),
							},
						],
					};
				}
				return {
					content: [
						{
							type: "text",
							text: result.tx.toHex(),
						},
					],
				};
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# BSV MCP Server Changelog

## v0.0.36 - A2B Overlay Integration & Improved MCP Server Publishing

### Features
- **A2B Overlay Integration**: Implemented a robust connection to the A2B Overlay API
  - Updated `a2b_discover` tool to search for on-chain MCP servers and agents
  - Enhanced search capabilities with relevance-based result ranking
  - User-friendly formatted output with command suggestions
  - Support for filtering by type (agent/tool/all), block range, and free text search
- **Improved MCP Server Publishing**: Enhanced the wallet_a2bPublishMcp tool
  - Better identity key integration via LocalSigner
  - More robust configuration for sigma signing
  - Improved transaction handling and error reporting

### Technical Improvements
- Updated API endpoint to use production overlay service
- Implemented enhanced search for better relevance scoring
- Better error handling for API responses
- Improved response formatting for readability
- Updated type definitions for consistency

## v0.0.34 - Transaction Broadcast Control

### Features
- Added `DISABLE_BROADCASTING` environment variable to control transaction broadcasting behavior
  - When set to "true", transactions are created but not broadcast to the network
  - Returns raw transaction hex instead of broadcasting, useful for testing and review
- Code cleanup and organization improvements

## v0.0.33 - Identity Key Sigma Signing

### Features
- Added optional `IDENTITY_KEY_WIF` environment variable for sigma-protocol signing.
-  `wallet_createOrdinals`, and `wallet_purchaseListing` tools now support signing with an identity key.
- Updated `README.md` to document `IDENTITY_KEY_WIF` usage and JSON configuration examples.

## v0.0.32 - Reliability Improvements

### Bug Fixes
- **Changelog Loading**: Improved changelog loading mechanism with multiple path resolution strategies
  - Added detailed logging for debugging path resolution issues
  - Better error reporting for troubleshooting in production environments
- **Package Structure**: Enhanced package.json to include all necessary files in npm package 
  - Added prompts, resources, and CHANGELOG.md to the published files list
  - Fixed import issues when running from installed npm package

## v0.0.29 - Enhanced Server Configuration & Maintenance

### Major Changes
- **Improved Changelog Management**: Added changelog as a MCP resource so you can just ask what has changed between versions
  - Simplified maintenance with single source of truth for version history
  - Automatic updates to MCP resources when changelog is modified
- **Expanded Component Configuration**: Can now configure which components of the MCP are loaded by setting env vars. See the readme for more information.

### Technical Improvements
- Removed duplicate changelog content in code
- Better error handling for resource loading
- Code cleanup and organization improvements

## v0.0.25 - Improved Error Handling & Optional Private Key

### Major Changes
- **Optional Private Key**: Server now starts without a PRIVATE_KEY_WIF environment variable
  - Educational resources and non-wallet tools remain available in limited mode
  - Wallet and MNEE tools gracefully fail with helpful error messages when no private key is provided
- **Component Configuration**: Added environment variables to enable/disable specific components
  - Selectively enable/disable prompts, resources, or tools
  - Fine-grained control over which tool categories are loaded
- **MNEE Token Support**: Added dedicated tools for MNEE token operations
  - Get balance, send tokens, and parse transactions
- **Enhanced Documentation**: Added detailed prompt examples and improved troubleshooting guidance
- **Resource Improvements**: Added BRC specifications and other reference materials

### Technical Improvements
- Improved error handling throughout the codebase
- Better initialization process for wallet component
- Standardized error messages across tools
- Expanded README with installation instructions for different platforms
- Added npm alternatives to Bun commands
- Added modular loading with configurable components

## v0.0.24 - Initial Public Release

### Features
- Bitcoin SV wallet operations (send, receive, manage keys)
- Ordinals creation and management
- BSV blockchain interaction (transactions, blocks, addresses)
- Cryptographic operations (signing, verification, encryption)
- Educational prompts for BSV SDK and Ordinals

### Toolkit Overview
- Wallet tools for core BSV operations
- Ordinals tools for NFT functionality
- BSV tools for blockchain interaction
- MNEE token tools
- Utility tools for data conversion 
```

--------------------------------------------------------------------------------
/tools/bsv/token.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
	ReturnTypes,
	toBitcoin,
	toSatoshi,
	toToken,
	toTokenSat,
} from "satoshi-token";
import { z } from "zod";

/**
 * Register token conversion tools for BSV
 * @param server The MCP server instance
 */
export function registerTokenTools(server: McpServer): void {
	// Convert Bitcoin to Satoshis
	server.tool(
		"bsv_toSatoshi",
		{
			args: z.object({
				bitcoin: z.union([z.number(), z.string()]),
				returnType: z.enum(["number", "string", "bigint"]).optional(),
			}),
		},
		async ({ args }) => {
			try {
				const { bitcoin, returnType } = args;
				let result: number | string | bigint;

				switch (returnType) {
					case "bigint":
						result = toSatoshi(bitcoin, ReturnTypes.BigInt);
						return { content: [{ type: "text", text: result.toString() }] };
					case "string":
						result = toSatoshi(bitcoin, ReturnTypes.String);
						return { content: [{ type: "text", text: result }] };
					default:
						result = toSatoshi(bitcoin);
						return { content: [{ type: "text", text: result.toString() }] };
				}
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);

	// Convert Satoshis to Bitcoin
	server.tool(
		"bsv_toBitcoin",
		{
			args: z.object({
				satoshis: z.union([z.number(), z.string(), z.bigint()]),
				returnType: z.enum(["number", "string", "bigint"]).optional(),
			}),
		},
		async ({ args }) => {
			try {
				const { satoshis, returnType } = args;
				let result: number | string | bigint;

				switch (returnType) {
					case "bigint":
						try {
							result = toBitcoin(satoshis, ReturnTypes.BigInt);
							return { content: [{ type: "text", text: result.toString() }] };
						} catch (e) {
							return {
								content: [
									{
										type: "text",
										text: "Error: Cannot return Bitcoin amount as BigInt if it has decimal part",
									},
								],
								isError: true,
							};
						}
					case "string":
						result = toBitcoin(satoshis, ReturnTypes.String);
						return { content: [{ type: "text", text: result }] };
					default:
						result = toBitcoin(satoshis);
						return { content: [{ type: "text", text: result.toString() }] };
				}
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);

	// Generic token conversion (for tokens with custom decimal places)
	server.tool(
		"bsv_toTokenSatoshi",
		{
			args: z.object({
				token: z.union([z.number(), z.string(), z.bigint()]),
				decimals: z.number().int().min(0),
				returnType: z.enum(["number", "string", "bigint"]).optional(),
			}),
		},
		async ({ args }) => {
			try {
				const { token, decimals, returnType } = args;
				let result: number | string | bigint;

				switch (returnType) {
					case "bigint":
						result = toTokenSat(token, decimals, ReturnTypes.BigInt);
						return { content: [{ type: "text", text: result.toString() }] };
					case "string":
						result = toTokenSat(token, decimals, ReturnTypes.String);
						return { content: [{ type: "text", text: result }] };
					default:
						result = toTokenSat(token, decimals);
						return { content: [{ type: "text", text: result.toString() }] };
				}
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);

	// Generic token conversion (for tokens with custom decimal places)
	server.tool(
		"bsv_toToken",
		{
			args: z.object({
				tokenSatoshi: z.union([z.number(), z.string(), z.bigint()]),
				decimals: z.number().int().min(0),
				returnType: z.enum(["number", "string", "bigint"]).optional(),
			}),
		},
		async ({ args }) => {
			try {
				const { tokenSatoshi, decimals, returnType } = args;
				let result: number | string | bigint;

				switch (returnType) {
					case "bigint":
						try {
							result = toToken(tokenSatoshi, decimals, ReturnTypes.BigInt);
							return { content: [{ type: "text", text: result.toString() }] };
						} catch (e) {
							return {
								content: [
									{
										type: "text",
										text: "Error: Cannot return token amount as BigInt if it has decimal part",
									},
								],
								isError: true,
							};
						}
					case "string":
						result = toToken(tokenSatoshi, decimals, ReturnTypes.String);
						return { content: [{ type: "text", text: result }] };
					default:
						result = toToken(tokenSatoshi, decimals);
						return { content: [{ type: "text", text: result.toString() }] };
				}
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/sendOrdinals.ts:
--------------------------------------------------------------------------------

```typescript
import { PrivateKey } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	CallToolResult,
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import { sendOrdinals } from "js-1sat-ord";
import type {
	ChangeResult,
	LocalSigner,
	SendOrdinalsConfig,
} from "js-1sat-ord";
import { Sigma } from "sigma-protocol";
import { z } from "zod";
import type { Wallet } from "./wallet";

/**
 * Schema for the sendOrdinals tool arguments
 */
export const sendOrdinalsArgsSchema = z.object({
	// Outpoint of the inscription to send (txid_vout format)
	inscriptionOutpoint: z
		.string()
		.describe("Inscription outpoint in format txid_vout"),
	// Destination address to send the inscription to
	destinationAddress: z
		.string()
		.describe("Destination address for the inscription"),
	// Optional metadata for the ordinal transfer
	metadata: z
		.any()
		.optional()
		.describe("Optional MAP metadata for the transfer"),
});

export type SendOrdinalsArgs = z.infer<typeof sendOrdinalsArgsSchema>;

/**
 * Registers the wallet_sendOrdinals tool for transferring ordinals
 */
export function registerSendOrdinalsTool(server: McpServer, wallet: Wallet) {
	server.tool(
		"wallet_sendOrdinals",
		"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.",
		{ args: sendOrdinalsArgsSchema },
		async (
			{ args }: { args: SendOrdinalsArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		): Promise<CallToolResult> => {
			try {
				// 1. Get private key from wallet
				const paymentPk = wallet.getPrivateKey();
				if (!paymentPk) {
					throw new Error("No private key available in wallet");
				}

				// 2. Get payment UTXOs from wallet
				const { paymentUtxos, nftUtxos } = await wallet.getUtxos();
				if (!paymentUtxos || paymentUtxos.length === 0) {
					throw new Error(
						"No payment UTXOs available to fund this transaction",
					);
				}

				// 3. Get the wallet address for change
				const walletAddress = paymentPk.toAddress().toString();

				// 4. Parse the inscription outpoint
				const [txid, voutStr] = args.inscriptionOutpoint.split("_");
				if (!txid || !voutStr) {
					throw new Error(
						"Invalid inscription outpoint format. Expected txid_vout",
					);
				}
				const vout = Number.parseInt(voutStr, 10);

				// 5. Find the inscription in nftUtxos
				const inscription = nftUtxos.find(
					(utxo) => utxo.txid === txid && utxo.vout === vout,
				);

				if (!inscription) {
					throw new Error(
						`Inscription ${args.inscriptionOutpoint} not found in your wallet`,
					);
				}

				// 6. Create config and transfer the inscription
				const sendOrdinalsConfig: SendOrdinalsConfig = {
					paymentPk,
					paymentUtxos,
					ordinals: [inscription],
					destinations: [{ address: args.destinationAddress }],
					changeAddress: walletAddress,
				};

				const identityKeyWif = process.env.IDENTITY_KEY_WIF;
				const identityPk = identityKeyWif
					? PrivateKey.fromWif(identityKeyWif)
					: undefined;
				if (identityPk) {
					sendOrdinalsConfig.signer = {
						idKey: identityPk,
					} as LocalSigner;
				}

				// Add metadata if provided
				if (args.metadata) {
					sendOrdinalsConfig.metaData = args.metadata;
				}

				// Using the wallet's key for both payment and ordinals
				sendOrdinalsConfig.ordPk = paymentPk;

				const result = await sendOrdinals(sendOrdinalsConfig);
				const changeResult = result as ChangeResult;
				// no signing when you send since we don't emit an inscription

				// 7. Broadcast the transaction
				const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
				if (!disableBroadcasting) {
					await changeResult.tx.broadcast();

					// 8. Refresh the wallet's UTXOs after spending
					try {
						await wallet.refreshUtxos();
					} catch (refreshError) {
						console.warn(
							"Failed to refresh UTXOs after transaction:",
							refreshError,
						);
					}

					// 9. Return transaction details
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify({
									txid: changeResult.tx.id("hex"),
									spentOutpoints: changeResult.spentOutpoints,
									payChange: changeResult.payChange,
									inscriptionOutpoint: args.inscriptionOutpoint,
									destinationAddress: args.destinationAddress,
								}),
							},
						],
					};
				}

				return {
					content: [
						{
							type: "text",
							text: changeResult.tx.toHex(),
						},
					],
				};
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/wallet.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * BSV SDK Wallet Prompt
 *
 * Provides detailed information about the wallet functionality in the BSV SDK,
 * including key management, address handling, and UTXO management.
 */
export const BSV_SDK_WALLET_PROMPT = `
# BSV SDK - Wallet Module

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.

## Key Classes and Interfaces

### ProtoWallet

The \`ProtoWallet\` class provides a basic implementation of the wallet interface with core functionality:

\`\`\`typescript
import { PrivateKey, ProtoWallet } from "@bsv/sdk";

// Create a new wallet with a random private key
const privateKey = PrivateKey.fromRandom();
const wallet = new ProtoWallet(privateKey);
\`\`\`

### WalletInterface

The \`WalletInterface\` defines the standard interface that wallet implementations should follow. It includes methods for:

- Key management
- Cryptographic operations
- Transaction creation and signing
- Output management
- Certificate handling

## Key Management

### Generating Keys

\`\`\`typescript
import { PrivateKey } from "@bsv/sdk";

// Generate a random private key
const privateKey = PrivateKey.fromRandom();

// Generate from a WIF (Wallet Import Format) string
const importedKey = PrivateKey.fromWif("your-wif-string");

// Generate from a seed
const seedKey = PrivateKey.fromSeed(Buffer.from("your-seed-data"));

// Get the corresponding public key
const publicKey = privateKey.toPublicKey();
\`\`\`

### KeyDeriver & CachedKeyDeriver

For HD (Hierarchical Deterministic) wallet functionality:

\`\`\`typescript
import { KeyDeriver } from "@bsv/sdk";

// Create a key deriver with a seed
const deriver = new KeyDeriver(seed);

// Derive a key at a specific path
const derivedKey = await deriver.deriveKey("m/44'/0'/0'/0/0");
\`\`\`

## Address Management

\`\`\`typescript
// Get the address for a private key
const address = privateKey.toAddress();

// Get the address for a public key
const address = publicKey.toAddress();

// Get the address string
const addressString = address.toString();
\`\`\`

## UTXO Management

Managing UTXOs (Unspent Transaction Outputs) is a critical part of wallet functionality:

\`\`\`typescript
// Example of tracking UTXOs
class MyWallet extends ProtoWallet {
  private utxos = [];
  
  async refreshUtxos(address) {
    // Fetch UTXOs from a service or API
    this.utxos = await fetchUtxosFromService(address);
  }
  
  getAvailableUtxos() {
    return this.utxos.filter(utxo => !utxo.spent);
  }
  
  getBalance() {
    return this.getAvailableUtxos().reduce((sum, utxo) => sum + utxo.satoshis, 0);
  }
}
\`\`\`

## Cryptographic Operations

The wallet module provides various cryptographic operations:

\`\`\`typescript
// Signing data
const signature = await wallet.createSignature({
  data: [1, 2, 3, 4],  // Data to sign
  protocolID: [1, "ecdsa"],  // Protocol to use
  keyID: "default"  // Key identifier
});

// Verifying signatures
const isValid = await wallet.verifySignature({
  data: [1, 2, 3, 4],  // Original data
  signature: signatureBytes,  // Signature to verify
  protocolID: [1, "ecdsa"],
  keyID: "default"
});

// Encryption and decryption
const encrypted = await wallet.encrypt({
  plaintext: [1, 2, 3, 4],
  protocolID: [1, "aes256"],
  keyID: "default"
});

const decrypted = await wallet.decrypt({
  ciphertext: encrypted.ciphertext,
  protocolID: [1, "aes256"],
  keyID: "default"
});
\`\`\`

## Best Practices

1. **Key Security**: Always handle private keys securely and never expose them unnecessarily
2. **UTXO Management**: Maintain accurate UTXO information for wallet functionality
3. **Error Handling**: Implement proper error handling for all wallet operations
4. **Testing**: Test wallet functionality thoroughly on testnet before deploying to mainnet
5. **Backup**: Provide key backup and recovery mechanisms for users

## Advanced Topics

- **Multi-signature wallets**: Implementing wallets requiring multiple signatures
- **HD Wallets**: Creating hierarchical deterministic wallets for key derivation
- **Watch-only wallets**: Tracking addresses without private keys
- **Hardware wallet integration**: Connecting to hardware security devices

For complete API documentation and additional wallet features, refer to the official BSV SDK documentation.
`;

/**
 * Register the BSV SDK Wallet prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerWalletPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_sdk_wallet",
		"Detailed information about the wallet functionality in the BSV SDK, including key management, address handling, and UTXO management.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: BSV_SDK_WALLET_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/createOrdinals.ts:
--------------------------------------------------------------------------------

```typescript
import { PrivateKey } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	CallToolResult,
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import { createOrdinals } from "js-1sat-ord";
import type {
	ChangeResult,
	CreateOrdinalsCollectionItemMetadata,
	CreateOrdinalsCollectionMetadata,
	CreateOrdinalsConfig,
	Destination,
	Inscription,
	LocalSigner,
	PreMAP,
} from "js-1sat-ord";
import { Sigma } from "sigma-protocol";
import { z } from "zod";
import type { Wallet } from "./wallet";

/**
 * Schema for the createOrdinals tool arguments.
 * This is a simplified interface compared to the full CreateOrdinalsConfig
 * in js-1sat-ord. The wallet will provide UTXOs, private key, etc.
 */
export const createOrdinalsArgsSchema = z.object({
	// Base64-encoded data to inscribe
	dataB64: z.string().describe("Base64-encoded content to inscribe"),
	// Content type (e.g., "image/jpeg", "text/plain", etc.)
	contentType: z.string().describe("MIME type of the content"),
	// Optional destination address (if not provided, uses the wallet's address)
	destinationAddress: z
		.string()
		.optional()
		.describe("Optional destination address for the ordinal"),
	// Optional metadata for the inscription
	metadata: z
		.any()
		.optional()
		.describe("Optional MAP metadata for the inscription"),
});

export type CreateOrdinalsArgs = z.infer<typeof createOrdinalsArgsSchema>;

/**
 * Registers the wallet_createOrdinals tool for minting ordinals/inscriptions
 */
export function registerCreateOrdinalsTool(server: McpServer, wallet: Wallet) {
	server.tool(
		"wallet_createOrdinals",
		"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.",
		{ args: createOrdinalsArgsSchema },
		async (
			{ args }: { args: CreateOrdinalsArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		): Promise<CallToolResult> => {
			try {
				// Load optional identity key for sigma signing
				const identityKeyWif = process.env.IDENTITY_KEY_WIF;
				let identityPk: PrivateKey | undefined;
				if (identityKeyWif) {
					try {
						identityPk = PrivateKey.fromWif(identityKeyWif);
					} catch (e) {
						console.warn(
							"Warning: Invalid IDENTITY_KEY_WIF environment variable; sigma signing disabled",
							e,
						);
					}
				}

				// 1. Get private key from wallet
				const paymentPk = wallet.getPrivateKey();
				if (!paymentPk) {
					throw new Error("No private key available in wallet");
				}

				// 2. Get payment UTXOs from wallet
				const { paymentUtxos } = await wallet.getUtxos();
				if (!paymentUtxos || paymentUtxos.length === 0) {
					throw new Error(
						"No payment UTXOs available to fund this inscription",
					);
				}

				// 3. Get the wallet address for change/destination if not provided
				const walletAddress = paymentPk.toAddress().toString();

				// 4. Create the inscription object
				const inscription: Inscription = {
					dataB64: args.dataB64,
					contentType: args.contentType,
				};

				// 5. Create the destination
				const destinations: Destination[] = [
					{
						address: args.destinationAddress || walletAddress,
						inscription,
					},
				];

				const createOrdinalsConfig: CreateOrdinalsConfig = {
					utxos: paymentUtxos,
					destinations,
					paymentPk,
					changeAddress: walletAddress,
					metaData: args.metadata as
						| PreMAP
						| CreateOrdinalsCollectionMetadata
						| CreateOrdinalsCollectionItemMetadata,
				};

				if (identityPk) {
					createOrdinalsConfig.signer = {
						idKey: identityPk,
					} as LocalSigner;
				}

				// 6. Create and broadcast the transaction
				const result = await createOrdinals(createOrdinalsConfig);

				const changeResult = result as ChangeResult;

				// 7. Optionally sign with identity key and broadcast the transaction

				const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
				if (!disableBroadcasting) {
					await changeResult.tx.broadcast();

					// 8. Refresh the wallet's UTXOs after spending
					try {
						await wallet.refreshUtxos();
					} catch (refreshError) {
						console.warn(
							"Failed to refresh UTXOs after transaction:",
							refreshError,
						);
					}

					// 9. Return transaction details
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify({
									txid: changeResult.tx.id("hex"),
									spentOutpoints: changeResult.spentOutpoints,
									payChange: changeResult.payChange,
									inscriptionAddress: args.destinationAddress || walletAddress,
									contentType: args.contentType,
								}),
							},
						],
					};
				}

				return {
					content: [
						{
							type: "text",
							text: changeResult.tx.toHex(),
						},
					],
				};
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/resources/junglebus.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

// Main JungleBus API documentation URL
const JUNGLEBUS_DOCS_URL = "https://junglebus.gorillapool.io/docs/";

/**
 * Manually structured JungleBus API documentation
 * Provides a clean summary of key JungleBus API endpoints and functionality
 */
export function getJungleBusDocumentation(): string {
	return `# JungleBus API Documentation

## Overview

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.

## API Endpoints

### Base URL
\`\`\`
https://junglebus.gorillapool.io/v1
\`\`\`

### Transaction API

#### Get Transaction by ID
\`\`\`
GET /transaction/get/{txid}
\`\`\`

Returns detailed information about a specific transaction, including:
- Transaction data (hex format)
- Block information (hash, height, time)
- Input and output details
- Address information

#### Get Transactions by Block Hash
\`\`\`
GET /block/{blockhash}/transactions
\`\`\`

Returns all transactions within a specific block.

### Subscription API

#### Create Subscription
\`\`\`
POST /subscribe
\`\`\`

Create a new subscription to monitor transactions based on filtering criteria.

Example request body:
\`\`\`json
{
  "callback": "https://your-callback-url.com",
  "fromBlock": 0,
  "query": {
    "find": {
      "out.tape.cell.s": "BEEF"
    }
  }
}
\`\`\`

#### Delete Subscription
\`\`\`
DELETE /subscribe/{id}
\`\`\`

Deletes an existing subscription.

### Network API

#### Get Network Info
\`\`\`
GET /network/info
\`\`\`

Returns current blockchain network information, including block height and other statistics.

## Client Implementations

### TypeScript Client

Installation:
\`\`\`bash
$ npm install @gorillapool/js-junglebus
\`\`\`

Usage:
\`\`\`javascript
import { JungleBusClient } from '@gorillapool/js-junglebus';

const server = "junglebus.gorillapool.io";
const jungleBusClient = new JungleBusClient(server, {
  onConnected(ctx) {
    // add your own code here
    console.log(ctx);
  },
  onConnecting(ctx) {
    // add your own code here
    console.log(ctx);
  },
  onDisconnected(ctx) {
    // add your own code here
    console.log(ctx);
  },
  onError(ctx) {
    // add your own code here
    console.error(ctx);
  }
});

// create subscriptions in the dashboard of the JungleBus website
const subId = "...."; // fill in the ID for the subscription
const fromBlock = 750000;

const subscription = jungleBusClient.Subscribe(
  subId,
  fromBlock,
  onPublish(tx) => {
    // add your own code here
    console.log(tx);
  },
  onStatus(ctx) => {
    // add your own code here
    console.log(ctx);
  },
  onError(ctx) => {
    // add your own code here
    console.log(ctx);
  },
  onMempool(tx) => {
    // add your own code here
    console.log(tx);
  }
);

// For lite mode (transaction hash and block height only)
await client.Subscribe("a5e2fa655c41753331539a2a86546bf9335ff6d9b7a512dc9acddb00ab9985c0", 1550000, onPublish, onStatus, onError, onMempool, true);
\`\`\`

### Go Client

Installation:
\`\`\`bash
go get github.com/GorillaPool/go-junglebus
\`\`\`

Usage:
\`\`\`go
package main

import (
  "context"
  "log"
  "sync"
  "github.com/GorillaPool/go-junglebus"
  "github.com/GorillaPool/go-junglebus/models"
)

func main() {
  wg := &sync.WaitGroup{}
  
  junglebusClient, err := junglebus.New(
    junglebus.WithHTTP("https://junglebus.gorillapool.io"),
  )
  if err != nil {
    log.Fatalln(err.Error())
  }
  
  subscriptionID := "..." // fill in the ID for the subscription
  fromBlock := uint64(750000)
  
  eventHandler := junglebus.EventHandler{
    // do not set this function to leave out mined transactions
    OnTransaction: func(tx *models.TransactionResponse) {
      log.Printf("[TX]: %d: %v", tx.BlockHeight, tx.Id)
    },
    // do not set this function to leave out mempool transactions
    OnMempool: func(tx *models.TransactionResponse) {
      log.Printf("[MEMPOOL TX]: %v", tx.Id)
    },
    OnStatus: func(status *models.ControlResponse) {
      log.Printf("[STATUS]: %v", status)
    },
    OnError: func(err error) {
      log.Printf("[ERROR]: %v", err)
    },
  }
  
  var subscription *junglebus.Subscription
  if subscription, err = junglebusClient.Subscribe(context.Background(), subscriptionID, fromBlock, eventHandler); err != nil {
    log.Printf("ERROR: failed getting subscription %s", err.Error())
  }
  
  // For lite mode
  if subscription, err := junglebusClient.SubscribeWithQueue(context.Background(), subscriptionID, fromBlock, 0, eventHandler, &junglebus.SubscribeOptions{
    QueueSize: 100000,
    LiteMode: true,
  }); err != nil {
    log.Printf("ERROR: failed getting subscription %s", err.Error())
  }
  
  wg.Add(1)
  wg.Wait()
}
\`\`\`

## Further Reading

For complete API documentation, visit [JungleBus Docs](${JUNGLEBUS_DOCS_URL})
`;
}

/**
 * Register the JungleBus API documentation resource with the MCP server
 * @param server The MCP server instance
 */
export function registerJungleBusResource(server: McpServer): void {
	server.resource(
		"junglebus-api-docs",
		JUNGLEBUS_DOCS_URL,
		{
			title: "JungleBus API Documentation",
			description:
				"API documentation for JungleBus, a transaction monitoring service for Bitcoin SV",
		},
		async (uri) => {
			const documentationContent = getJungleBusDocumentation();
			return {
				contents: [
					{
						uri: uri.href,
						text: documentationContent,
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/ordinals/marketListings.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { z } from "zod";

// Enhanced schema for unified market listings arguments
export const marketListingsArgsSchema = z.object({
	// Common parameters
	limit: z
		.number()
		.int()
		.min(1)
		.max(100)
		.default(20)
		.describe("Number of results (1-100, default 20)"),
	offset: z.number().int().min(0).default(0).describe("Pagination offset"),
	dir: z
		.enum(["asc", "desc"])
		.default("desc")
		.describe("Sort direction (asc or desc)"),
	address: z.string().optional().describe("Bitcoin address"),

	// NFT-specific parameters
	origin: z.string().optional().describe("Origin outpoint"),
	mime: z.string().optional().describe("MIME type filter"),
	num: z.string().optional().describe("Inscription number"),

	// General market parameters
	minPrice: z.number().optional().describe("Minimum price in satoshis"),
	maxPrice: z.number().optional().describe("Maximum price in satoshis"),

	// Token-specific parameters
	tokenType: z
		.enum(["nft", "bsv20", "bsv21", "all"])
		.default("all")
		.describe("Type of token to search for (nft, bsv20, bsv21, or all)"),
	sort: z
		.enum(["recent", "price", "num", "height", "price_per_token"])
		.default("recent")
		.describe("Sort method (recent, price, num, height, price_per_token)"),
	id: z.string().optional().describe("Token ID in outpoint format"),
	tick: z.string().optional().describe("Token ticker symbol"),
	pending: z
		.boolean()
		.default(false)
		.optional()
		.describe("Include pending sales"),
});

export type MarketListingsArgs = z.infer<typeof marketListingsArgsSchema>;

// Unified response type that covers all token types
interface MarketListingResponse {
	results: Array<{
		outpoint: string;
		origin?: {
			outpoint: string;
			data?: {
				insc?: {
					text?: string;
					file?: {
						type?: string;
						size?: number;
					};
				};
			};
		};
		data?: {
			list?: {
				price?: number;
				payout?: string;
				sale?: boolean;
				price_per_token?: number;
			};
			bsv20?: {
				id?: string;
				tick?: string;
				sym?: string;
				amt?: string;
				op?: string;
			};
		};
		satoshis?: number;
		height?: number;
		[key: string]: unknown;
	}>;
	total: number;
}

/**
 * Register the unified Ordinals market listings tool
 * Handles NFTs, BSV20, and BSV21 tokens
 */
export function registerMarketListingsTool(server: McpServer): void {
	server.tool(
		"ordinals_marketListings",
		"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.",
		{
			args: marketListingsArgsSchema,
		},
		async (
			{ args }: { args: MarketListingsArgs },
			extra: RequestHandlerExtra,
		) => {
			try {
				const {
					limit,
					offset,
					sort,
					dir,
					address,
					origin,
					mime,
					num,
					minPrice,
					maxPrice,
					tokenType,
					id,
					tick,
					pending,
				} = args;

				// Determine the API endpoint based on tokenType
				let baseUrl = "https://ordinals.gorillapool.io/api";
				let useTokenParams = false;

				if (tokenType === "bsv20" || tokenType === "bsv21") {
					baseUrl += "/bsv20/market";
					useTokenParams = true;
				} else if (tokenType === "nft" || tokenType === "all") {
					baseUrl += "/market";
				}

				// Build the URL with query parameters
				const url = new URL(baseUrl);
				url.searchParams.append("limit", limit.toString());
				url.searchParams.append("offset", offset.toString());
				url.searchParams.append("dir", dir);

				// Add sort parameter based on token type
				if (useTokenParams) {
					// BSV20/BSV21 specific sort options
					if (
						sort === "height" ||
						sort === "price" ||
						sort === "price_per_token"
					) {
						url.searchParams.append("sort", sort);
					}
					// Add token-specific parameters
					if (tokenType === "bsv21") {
						url.searchParams.append("type", "v2");
					}
					if (id) url.searchParams.append("id", id);
					if (tick) url.searchParams.append("tick", tick);
					if (pending !== undefined)
						url.searchParams.append("pending", pending.toString());
					// For BSV20/21, min/max price parameters have slightly different names
					if (minPrice !== undefined)
						url.searchParams.append("min_price", minPrice.toString());
					if (maxPrice !== undefined)
						url.searchParams.append("max_price", maxPrice.toString());
				} else {
					// NFT specific sort options
					if (sort === "recent" || sort === "price" || sort === "num") {
						url.searchParams.append("sort", sort);
					}
					// Add NFT-specific parameters
					if (origin) url.searchParams.append("origin", origin);
					if (mime) url.searchParams.append("mime", mime);
					if (num) url.searchParams.append("num", num);
					// For NFTs, min/max price parameters use short names
					if (minPrice !== undefined)
						url.searchParams.append("min", minPrice.toString());
					if (maxPrice !== undefined)
						url.searchParams.append("max", maxPrice.toString());
				}

				// Add common parameters
				if (address) url.searchParams.append("address", address);

				// Make the fetch request
				const response = await fetch(url.toString());

				if (!response.ok) {
					throw new Error(
						`API error: ${response.status} ${response.statusText}`,
					);
				}

				const data = (await response.json()) as MarketListingResponse;

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(data, null, 2),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/prompts/bsvSdk/transaction.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";

/**
 * BSV SDK Transaction Prompt
 *
 * Provides detailed information about transaction building and management
 * in the BSV SDK, including input/output handling, script integration, and transaction signing.
 */
export const BSV_SDK_TRANSACTION_PROMPT = `
# BSV SDK - Transaction Module

The Transaction module in the BSV SDK provides comprehensive functionality for creating, manipulating, and signing Bitcoin transactions. It gives developers fine-grained control over transaction construction while abstracting many of the complexities.

## Key Components

### Transaction Class

The core \`Transaction\` class represents a Bitcoin transaction and provides methods for manipulating its components:

\`\`\`typescript
import { Transaction, PrivateKey, LockingScript } from "@bsv/sdk";

// Create a new transaction
const tx = new Transaction();

// Set transaction properties
tx.version = 1;
tx.lockTime = 0;
\`\`\`

## Building Transactions

### Adding Inputs

\`\`\`typescript
// Add an input by specifying the source transaction and output index
tx.addInput({
  sourceTXID: "previous_transaction_id_in_hex",
  sourceOutputIndex: 0,
  sequence: 0xffffffff // Optional, defaults to max value
});

// Add multiple inputs
const inputs = [
  { sourceTXID: "txid1", sourceOutputIndex: 0 },
  { sourceTXID: "txid2", sourceOutputIndex: 1 }
];
inputs.forEach(input => tx.addInput(input));
\`\`\`

### Adding Outputs

\`\`\`typescript
// Add an output with a locking script and amount
import { LockingScript } from "@bsv/sdk";

// Create from a Bitcoin address
const lockingScript = LockingScript.fromAddress("recipient_address");

// Add the output to the transaction
tx.addOutput({
  lockingScript,
  satoshis: 5000 // Amount in satoshis
});

// Add a data (OP_RETURN) output
const dataScript = LockingScript.fromData(Buffer.from("Hello, Bitcoin!"));
tx.addOutput({
  lockingScript: dataScript,
  satoshis: 0 // OP_RETURN outputs typically have 0 value
});
\`\`\`

### Working with UTXOs

When building transactions with existing UTXOs:

\`\`\`typescript
import { UnlockingScript } from "@bsv/sdk";

// Example UTXO data
const utxos = [
  {
    txid: "previous_tx_id_in_hex",
    vout: 0,
    satoshis: 10000,
    scriptPubKey: "locking_script_hex"
  }
];

// Create transaction using UTXOs
const tx = new Transaction();

// Add input from UTXO
utxos.forEach(utxo => {
  tx.addInput({
    sourceTXID: utxo.txid,
    sourceOutputIndex: utxo.vout
  });
});

// Add output with recipient address
tx.addOutput({
  lockingScript: LockingScript.fromAddress("recipient_address"),
  satoshis: 9000 // Sending 9000 satoshis (10000 - 1000 fee)
});
\`\`\`

## Signing Transactions

### Basic Transaction Signing

\`\`\`typescript
import { PrivateKey, SigningConfig, Utils } from "@bsv/sdk";

// Create a private key
const privateKey = PrivateKey.fromWif("your_private_key_wif");

// Sign a specific input
const inputIndex = 0;
const signingConfig: SigningConfig = {
  privateKey,
  lockingScript: LockingScript.fromAddress(privateKey.toAddress()),
  satoshis: 10000, // Original amount in the UTXO
  inputIndex,
  sigHashType: Utils.SIGHASH_ALL | Utils.SIGHASH_FORKID // Standard signing algorithm
};

// Apply the signature to the transaction
tx.sign(signingConfig);
\`\`\`

### Signing Multiple Inputs

\`\`\`typescript
// Sign multiple inputs with different keys
const keys = [privateKey1, privateKey2];
const utxos = [utxo1, utxo2];

utxos.forEach((utxo, index) => {
  const signingConfig = {
    privateKey: keys[index],
    lockingScript: LockingScript.fromHex(utxo.scriptPubKey),
    satoshis: utxo.satoshis,
    inputIndex: index,
    sigHashType: Utils.SIGHASH_ALL | Utils.SIGHASH_FORKID
  };
  
  tx.sign(signingConfig);
});
\`\`\`

## Transaction Serialization

\`\`\`typescript
// Convert transaction to binary format
const txBinary = tx.toBinary();

// Convert to hex string
const txHex = tx.toHex();

// Get transaction ID
const txid = tx.hash("hex");

// Parse an existing transaction
const parsedTx = Transaction.fromHex("transaction_hex_string");
\`\`\`

## Fee Calculation

\`\`\`typescript
// Manual fee calculation based on transaction size
const txSize = tx.toBinary().length;
const feeRate = 0.5; // satoshis per byte
const fee = Math.ceil(txSize * feeRate);

// Adjust output amount to include fee
outputAmount = inputAmount - fee;
\`\`\`

## Advanced Transaction Features

### Time Locks

\`\`\`typescript
// Set absolute locktime (by block height)
tx.lockTime = 700000; // Transaction can't be mined until block 700000

// Set relative locktime using sequence number (BIP 68)
const sequenceForBlocks = (blocks) => 0xffffffff - blocks;
tx.inputs[0].sequence = sequenceForBlocks(10); // Locked for 10 blocks
\`\`\`

### Custom Scripts

\`\`\`typescript
import { Script, OpCodes } from "@bsv/sdk";

// Create a custom script
const customScript = new Script();
customScript.add(OpCodes.OP_DUP);
customScript.add(OpCodes.OP_HASH160);
customScript.add(Buffer.from("public_key_hash", "hex"));
customScript.add(OpCodes.OP_EQUALVERIFY);
customScript.add(OpCodes.OP_CHECKSIG);

// Create a locking script from the custom script
const customLockingScript = LockingScript.fromScript(customScript);
\`\`\`

## Best Practices

1. **Fee Management**: Calculate appropriate fees based on transaction size and network conditions
2. **Input/Output Management**: Properly track inputs and outputs to avoid double-spending
3. **Change Handling**: Always account for change when not spending the full UTXO amount
4. **Testing**: Test transactions on testnet before deploying to mainnet
5. **Error Handling**: Implement proper error handling for transaction building and signing

For complete API documentation and additional transaction features, refer to the official BSV SDK documentation.
`;

/**
 * Register the BSV SDK Transaction prompt with the MCP server
 * @param server The MCP server instance
 */
export function registerTransactionPrompt(server: McpServer): void {
	server.prompt(
		"bitcoin_sv_sdk_transaction",
		"Detailed information about transaction building and management in the BSV SDK, including input/output handling, script integration, and transaction signing.",
		async (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => {
			return {
				messages: [
					{
						role: "assistant",
						content: {
							type: "text",
							text: BSV_SDK_TRANSACTION_PROMPT,
						},
					},
				],
			};
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/a2b/discover.ts:
--------------------------------------------------------------------------------

```typescript
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// API endpoint for the A2B Overlay service
const OVERLAY_API_URL = "https://a2b-overlay-production.up.railway.app/v1";

type A2BDiscoveryItem = {
	txid: string;
	outpoint: string;
	type: "agent" | "tool";
	app: string;
	serverName: string;
	command: string;
	description: string;
	keywords: string[];
	args: Record<string, string>;
	env: Record<string, string>;
	blockHeight: number;
	timestamp: string;
	tools?: string[];
	prompts?: string[];
	resources?: string[];
};

type OverlaySearchResponse = {
	items: A2BDiscoveryItem[];
	total: number;
	limit: number;
	offset: number;
	queryType: "agent" | "tool" | "all";
	query: string;
};

// Schema for agent discovery parameters
export const a2bDiscoverArgsSchema = z.object({
	queryType: z.enum(["agent", "tool"]).describe("Type of discovery to perform"),
	query: z.string().describe("Search agent or tool names, descriptions"),
	limit: z.number().optional().describe("Limit the number of results"),
	offset: z.number().optional().describe("Offset the results"),
	fromBlock: z.number().optional().describe("From block"),
	toBlock: z.number().optional().describe("To block"),
});
export type A2bDiscoverArgs = z.infer<typeof a2bDiscoverArgsSchema>;

/**
 * Format the response in a user-friendly way
 */
function formatSearchResults(data: unknown, queryType: string): string {
	// console.log(`Received data: ${JSON.stringify(data).substring(0, 200)}...`); // Debug log

	// Check if data is an object with items property
	if (!data) {
		return `No ${queryType} results found.`;
	}

	// Handle the new API format where data is an object with 'items' array
	const items = Array.isArray(data)
		? data
		: (data as { items?: A2BDiscoveryItem[] }).items;

	// Ensure items is an array
	if (!items || !Array.isArray(items) || items.length === 0) {
		return `No ${queryType} results found.`;
	}

	let result = `Found ${items.length} ${queryType === "all" ? "items" : `${queryType}s`}:\n\n`;

	items.forEach((item, index) => {
		if (!item) return;

		// Agent or MCP server name with description
		result += `${index + 1}. **${item.serverName || "Unknown"}** - ${item.description || "No description"}\n`;

		// Display command to run
		if (item.command) {
			const args = item.args ? Object.values(item.args).join(" ") : "";
			result += `   Command: \`${item.command} ${args}\`\n`;
		}

		// Add tools count if available
		if (item.tools && Array.isArray(item.tools)) {
			result += `   Tools: ${item.tools.length} available\n`;
		}

		// Add keywords if available
		if (
			item.keywords &&
			Array.isArray(item.keywords) &&
			item.keywords.length > 0
		) {
			result += `   Keywords: ${item.keywords.join(", ")}\n`;
		}

		// Add blockchain details
		if (item.outpoint) {
			result += `   Outpoint: ${item.outpoint}\n`;
		}

		if (item.blockHeight !== undefined) {
			const date = item.timestamp
				? new Date(item.timestamp).toLocaleDateString()
				: "Unknown date";
			result += `   Block: ${item.blockHeight}, ${date}\n`;
		}

		result += "\n";
	});

	return result;
}

/**
 * Registers the a2b_discover tool for on-chain agent discovery
 */
export function registerA2bDiscoverTool(server: McpServer) {
	server.tool(
		"a2b_discover",
		"Search on-chain agent and MCP tool records. Use 'agent' to search for agents, 'tool' to search for MCP tools.",
		{ args: a2bDiscoverArgsSchema },
		async (
			{ args }: { args: A2bDiscoverArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		) => {
			try {
				const params = new URLSearchParams();

				// Set query type (agent, tool, or all)
				params.set("type", args.queryType);

				// Use enhanced search for better relevance scoring
				let searchEndpoint = "/search/enhanced";

				// For empty queries, use the regular search endpoint
				if (!args.query || !args.query.trim()) {
					searchEndpoint = "/search";
				} else {
					params.set("q", args.query); // enhanced search uses 'q' parameter
				}

				// Add pagination parameters
				params.set("limit", args.limit?.toString() ?? "10");
				params.set("offset", args.offset?.toString() ?? "0");

				// Add block range if specified
				if (args.fromBlock) {
					params.set("fromBlock", args.fromBlock.toString());
				}
				if (args.toBlock) {
					params.set("toBlock", args.toBlock.toString());
				}

				// Construct the full URL
				const searchUrl = `${OVERLAY_API_URL}${searchEndpoint}?${params.toString()}`;
				//console.log(`Searching URL: ${searchUrl}`);

				// Make the request to the overlay API
				const response = await fetch(searchUrl, {
					method: "GET",
					headers: {
						Accept: "application/json",
					},
				});

				if (!response.ok) {
					throw new Error(
						`API returned status ${response.status}: ${response.statusText}`,
					);
				}

				const data = (await response.json()) as OverlaySearchResponse;

				// Format the results for better readability
				let result = "";

				if (data?.items?.length > 0) {
					result = `Found ${data.items.length} ${args.queryType}(s):\n\n`;

					data.items.forEach((item: A2BDiscoveryItem, index: number) => {
						// Server name and description
						result += `${index + 1}. **${item.serverName || "Unknown"}** - ${item.description || "No description"}\n`;

						// Command to run
						if (item.command) {
							const cmdArgs = item.args
								? Object.values(item.args).join(" ")
								: "";
							result += `   Command: \`${item.command} ${cmdArgs}\`\n`;
						}

						// Tools available
						if (item.tools?.length) {
							result += `   Tools: ${item.tools.length} available\n`;
						}

						// Keywords
						if (item.keywords?.length) {
							result += `   Keywords: ${item.keywords.join(", ")}\n`;
						}

						// Blockchain details
						if (item.outpoint) {
							result += `   Outpoint: ${item.outpoint}\n`;
						}

						if (item.blockHeight !== undefined) {
							const date = item.timestamp
								? new Date(item.timestamp).toLocaleDateString()
								: "Unknown date";
							result += `   Block: ${item.blockHeight}, ${date}\n`;
						}

						result += "\n";
					});
				} else {
					result = `No ${args.queryType} results found.`;
				}

				return {
					content: [{ type: "text", text: result }],
					isError: false,
				};
			} catch (error) {
				console.error("Search error:", error);
				return {
					content: [
						{
							type: "text",
							text: `Error querying A2B Overlay: ${error instanceof Error ? error.message : String(error)}`,
						},
					],
					isError: true,
				};
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/bsv/decodeTransaction.ts:
--------------------------------------------------------------------------------

```typescript
import { Transaction, Utils } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import { z } from "zod";

// Schema for decode transaction arguments
export const decodeTransactionArgsSchema = z.object({
	tx: z.string().describe("Transaction data or txid"),
	encoding: z
		.enum(["hex", "base64"])
		.default("hex")
		.describe("Encoding of the input data"),
});

export type DecodeTransactionArgs = z.infer<typeof decodeTransactionArgsSchema>;

// Type for JungleBus API response
interface JungleBusTransactionResponse {
	id: string;
	transaction: string;
	block_hash?: string;
	block_height?: number;
	block_time?: number;
	block_index?: number;
	addresses?: string[];
	inputs?: string[];
	outputs?: string[];
	input_types?: string[];
	output_types?: string[];
	contexts?: string[];
	sub_contexts?: string[];
	data?: string[];
	merkle_proof?: unknown;
}

// Network info response type
interface NetworkInfoResponse {
	blocks: number;
	[key: string]: unknown;
}

// Transaction input type
interface TransactionInputData {
	txid: string | undefined;
	vout: number;
	sequence: number | undefined;
	script: string;
	scriptAsm: string;
	type?: string;
	value?: number;
}

// Transaction output type
interface TransactionOutputData {
	n: number;
	value: number | undefined;
	scriptPubKey: {
		hex: string;
		asm: string;
	};
	type?: string;
}

// Transaction result type
interface TransactionResult {
	txid: string;
	version: number;
	locktime: number;
	size: number;
	inputs: TransactionInputData[];
	outputs: TransactionOutputData[];
	confirmations?: number;
	block?: {
		hash: string;
		height: number;
		time: number;
		index: number;
	} | null;
	addresses?: string[];
	fee?: number | null;
	feeRate?: number | null;
}

/**
 * Fetches transaction data from JungleBus
 */
async function fetchJungleBusData(
	txid: string,
): Promise<JungleBusTransactionResponse | null> {
	try {
		const response = await fetch(
			`https://junglebus.gorillapool.io/v1/transaction/get/${txid}`,
		);
		if (!response.ok) {
			console.error(
				`JungleBus API error: ${response.status} ${response.statusText}`,
			);
			return null;
		}
		return (await response.json()) as JungleBusTransactionResponse;
	} catch (error) {
		console.error("Error fetching from JungleBus:", error);
		return null;
	}
}

/**
 * Determines if a string is likely a txid
 */
function isTxid(str: string): boolean {
	// TX IDs are 64 characters in hex (32 bytes)
	return /^[0-9a-f]{64}$/i.test(str);
}

/**
 * Register the BSV transaction decode tool
 */
export function registerDecodeTransactionTool(server: McpServer): void {
	server.tool(
		"bsv_decodeTransaction",
		"Decodes and analyzes Bitcoin SV transactions to provide detailed insights. This powerful tool accepts either a transaction ID or raw transaction data and returns comprehensive information including inputs, outputs, fee calculations, script details, and blockchain context. Supports both hex and base64 encoded transactions and automatically fetches additional on-chain data when available.",
		{
			args: decodeTransactionArgsSchema,
		},
		async (
			{ args }: { args: DecodeTransactionArgs },
			extra: RequestHandlerExtra,
		) => {
			try {
				const { tx, encoding } = args;
				let transaction: Transaction;
				let rawTx: string;
				let txid: string;
				let junglebusData: JungleBusTransactionResponse | null = null;

				// Check if input is txid or raw transaction
				if (isTxid(tx)) {
					// It's a txid, fetch from JungleBus
					txid = tx;
					junglebusData = await fetchJungleBusData(txid);

					if (!junglebusData) {
						throw new Error(
							`Failed to fetch transaction data for txid: ${txid}`,
						);
					}

					// JungleBus returns base64, convert if needed
					rawTx = junglebusData.transaction;
					// Check if rawTx is in base64 format (common from JungleBus)
					const isBase64 = /^[A-Za-z0-9+/=]+$/.test(rawTx);

					if (isBase64) {
						const txBytes = Utils.toArray(rawTx, "base64");
						transaction = Transaction.fromBinary(txBytes);
					} else {
						transaction = Transaction.fromHex(rawTx);
					}
				} else {
					// It's a raw transaction
					let txBytes: number[];

					if (encoding === "hex") {
						txBytes = Utils.toArray(tx, "hex");
					} else {
						txBytes = Utils.toArray(tx, "base64");
					}

					transaction = Transaction.fromBinary(txBytes);
					txid = transaction.hash("hex") as string;

					// Optionally fetch additional context from JungleBus
					junglebusData = await fetchJungleBusData(txid);
				}

				// Basic transaction data
				const result: TransactionResult = {
					txid,
					version: transaction.version,
					locktime: transaction.lockTime,
					size: transaction.toBinary().length,
					inputs: transaction.inputs.map((input) => ({
						txid: input.sourceTXID,
						vout: input.sourceOutputIndex,
						sequence: input.sequence,
						script: input.unlockingScript ? input.unlockingScript.toHex() : "",
						scriptAsm: input.unlockingScript
							? input.unlockingScript.toASM()
							: "",
					})),
					outputs: transaction.outputs.map((output) => ({
						n: transaction.outputs.indexOf(output),
						value: output.satoshis,
						scriptPubKey: {
							hex: output.lockingScript.toHex(),
							asm: output.lockingScript.toASM(),
						},
					})),
				};

				// Add JungleBus context if available
				if (junglebusData) {
					result.confirmations = junglebusData.block_height
						? (await getCurrentBlockHeight()) - junglebusData.block_height + 1
						: 0;

					result.block = junglebusData.block_hash
						? {
								hash: junglebusData.block_hash,
								height: junglebusData.block_height || 0,
								time: junglebusData.block_time || 0,
								index: junglebusData.block_index || 0,
							}
						: null;

					// Add script types
					if (
						junglebusData.input_types &&
						junglebusData.input_types.length > 0
					) {
						result.inputs = result.inputs.map((input, i) => ({
							...input,
							type: junglebusData.input_types?.[i] || "unknown",
						}));
					}

					if (
						junglebusData.output_types &&
						junglebusData.output_types.length > 0
					) {
						result.outputs = result.outputs.map((output, i) => ({
							...output,
							type: junglebusData.output_types?.[i] || "unknown",
						}));
					}

					// Add addresses found in transaction
					if (junglebusData.addresses && junglebusData.addresses.length > 0) {
						result.addresses = junglebusData.addresses;
					}
				}

				// Calculate additional information
				const totalInputValue = result.inputs.reduce(
					(sum, input) => sum + (input.value || 0),
					0,
				);
				const totalOutputValue = result.outputs.reduce(
					(sum, output) => sum + (output.value || 0),
					0,
				);

				result.fee =
					totalInputValue > 0 ? totalInputValue - totalOutputValue : null;
				result.feeRate =
					result.fee !== null
						? Math.round((result.fee / result.size) * 100000000) / 100000000
						: null;

				return {
					content: [
						{
							type: "text",
							text: JSON.stringify(result, null, 2),
						},
					],
				};
			} catch (error) {
				return {
					content: [
						{
							type: "text",
							text: error instanceof Error ? error.message : String(error),
						},
					],
					isError: true,
				};
			}
		},
	);
}

/**
 * Get current block height
 */
async function getCurrentBlockHeight(): Promise<number> {
	try {
		const response = await fetch(
			"https://junglebus.gorillapool.io/v1/network/info",
		);
		if (!response.ok) {
			return 0;
		}
		const data = (await response.json()) as NetworkInfoResponse;
		return data.blocks || 0;
	} catch (error) {
		console.error("Error fetching current block height:", error);
		return 0;
	}
}

```

--------------------------------------------------------------------------------
/tools/wallet/wallet.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Wallet implementation scaffold.
 *
 * Implements WalletInterface from the ts-sdk and extends ProtoWallet.
 *
 * See: https://github.com/bitcoin-sv/ts-sdk/blob/main/src/wallet/Wallet.interfaces.ts
 */
import { LockingScript, PrivateKey, ProtoWallet, Transaction } from "@bsv/sdk";
import type {
	AbortActionArgs,
	AbortActionResult,
	AcquireCertificateArgs,
	AuthenticatedResult,
	CreateActionArgs,
	CreateActionResult,
	DiscoverByAttributesArgs,
	DiscoverByIdentityKeyArgs,
	DiscoverCertificatesResult,
	GetHeaderArgs,
	GetHeaderResult,
	GetHeightResult,
	GetNetworkResult,
	GetPublicKeyArgs,
	GetPublicKeyResult,
	GetVersionResult,
	InternalizeActionArgs,
	InternalizeActionResult,
	ListActionsArgs,
	ListActionsResult,
	ListCertificatesArgs,
	ListCertificatesResult,
	ListOutputsArgs,
	ListOutputsResult,
	ProveCertificateArgs,
	ProveCertificateResult,
	PubKeyHex,
	RelinquishCertificateArgs,
	RelinquishCertificateResult,
	RelinquishOutputArgs,
	RelinquishOutputResult,
	RevealCounterpartyKeyLinkageArgs,
	RevealCounterpartyKeyLinkageResult,
	RevealSpecificKeyLinkageArgs,
	RevealSpecificKeyLinkageResult,
	SignActionArgs,
	SignActionResult,
	WalletCertificate,
	WalletInterface,
} from "@bsv/sdk";
import { type NftUtxo, type Utxo, fetchNftUtxos } from "js-1sat-ord";
import { fetchPaymentUtxos } from "./fetchPaymentUtxos";

export class Wallet extends ProtoWallet implements WalletInterface {
	private paymentUtxos: Utxo[] = [];
	private nftUtxos: NftUtxo[] = [];
	private lastUtxoFetch = 0;
	private readonly utxoRefreshIntervalMs = 5 * 60 * 1000; // 5 minutes
	private privateKey?: PrivateKey;

	constructor(privKey?: PrivateKey) {
		super(privKey);
		this.privateKey = privKey;
		// Initialize UTXOs
		this.refreshUtxos().catch((err) =>
			console.error("Error initializing UTXOs:", err),
		);
	}

	/**
	 * Refresh UTXOs from the network
	 */
	async refreshUtxos(): Promise<void> {
		try {
			const privateKey = this.getPrivateKey();
			if (!privateKey) {
				return; // Silent fail if no private key, keep existing UTXOs
			}

			const address = privateKey.toAddress().toString();
			this.lastUtxoFetch = Date.now();

			// Payment UTXOs
			let newPaymentUtxos: Utxo[] | undefined = undefined;
			try {
				newPaymentUtxos = await fetchPaymentUtxos(address);
				// Only update if we successfully got UTXOs
				if (Array.isArray(newPaymentUtxos)) {
					this.paymentUtxos = newPaymentUtxos;
				}
			} catch (error) {
				// Keep existing UTXOs, don't clear them on error
			}

			// NFT UTXOs - keep existing if fetch fails
			let newNftUtxos: NftUtxo[] = [];
			try {
				newNftUtxos = await fetchNftUtxos(address);
				// Only update if we successfully got UTXOs
				if (Array.isArray(newNftUtxos)) {
					this.nftUtxos = newNftUtxos;
				}
			} catch (error) {
				// Keep existing UTXOs, don't clear them on error
			}
		} catch (error) {
			// Silent global error, preserve existing UTXOs
		}
	}

	/**
	 * Get payment and NFT UTXOs, refreshing if needed
	 */
	async getUtxos(): Promise<{ paymentUtxos: Utxo[]; nftUtxos: NftUtxo[] }> {
		const now = Date.now();
		if (now - this.lastUtxoFetch > this.utxoRefreshIntervalMs) {
			await this.refreshUtxos();
		}
		return { paymentUtxos: this.paymentUtxos, nftUtxos: this.nftUtxos };
	}

	/**
	 * Get the private key if available
	 */
	getPrivateKey(): PrivateKey | undefined {
		// Try to get private key from environment if not already set
		if (!this.privateKey) {
			const wif = process.env.PRIVATE_KEY_WIF;
			if (wif) {
				this.privateKey = PrivateKey.fromWif(wif);
			}
		}
		return this.privateKey;
	}

	async getPublicKey(args: GetPublicKeyArgs): Promise<GetPublicKeyResult> {
		const privateKey = this.getPrivateKey();
		if (!privateKey) {
			throw new Error("No private key available");
		}

		const publicKey = privateKey.toPublicKey();
		return {
			publicKey: publicKey.toDER("hex") as PubKeyHex,
		};
	}
	async revealCounterpartyKeyLinkage(
		args: RevealCounterpartyKeyLinkageArgs,
	): Promise<RevealCounterpartyKeyLinkageResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async revealSpecificKeyLinkage(
		args: RevealSpecificKeyLinkageArgs,
	): Promise<RevealSpecificKeyLinkageResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	// Implemented by ProtoWallet
	// async encrypt(args: WalletEncryptArgs): Promise<WalletEncryptResult> {
	// 	return this.encrypt(args);
	// }
	// async decrypt(args: WalletDecryptArgs): Promise<WalletDecryptResult> {
	// 	return this.decrypt(args);
	// }
	// async createHmac(args: CreateHmacArgs): Promise<CreateHmacResult> {
	// 	return this.createHmac(args);
	// }
	// async verifyHmac(args: VerifyHmacArgs): Promise<VerifyHmacResult> {
	// 	return this.verifyHmac(args);
	// }
	// async createSignature(
	// 	args: CreateSignatureArgs,
	// ): Promise<CreateSignatureResult> {
	// 	return Promise.reject(new Error("Not implemented"));
	// }
	// async verifySignature(
	// 	args: VerifySignatureArgs,
	// ): Promise<VerifySignatureResult> {
	// 	return Promise.reject(new Error("Not implemented"));
	// }
	async createAction(args: CreateActionArgs): Promise<CreateActionResult> {
		const tx = new Transaction();

		// Add outputs
		if (args.outputs) {
			for (const output of args.outputs) {
				const lockingScript = LockingScript.fromHex(output.lockingScript);
				tx.addOutput({
					lockingScript,
					satoshis: output.satoshis,
				});
			}
		}

		// Add inputs (if provided)
		if (args.inputs) {
			for (const input of args.inputs) {
				const [txid, outputIndexStr] = input.outpoint.split(".");
				tx.addInput({
					sourceTXID: txid,
					sourceOutputIndex: Number.parseInt(outputIndexStr || "0", 10),
				});
			}
		}

		// Set lockTime and version if provided
		if (args.lockTime !== undefined) tx.lockTime = args.lockTime;
		if (args.version !== undefined) tx.version = args.version;

		// Serialize the transaction using Utils
		const txid = tx.hash("hex") as string;
		const txArray = tx.toBinary();

		return {
			txid,
			tx: txArray,
			signableTransaction: undefined,
		};
	}
	async signAction(args: SignActionArgs): Promise<SignActionResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async abortAction(args: AbortActionArgs): Promise<AbortActionResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async listActions(args: ListActionsArgs): Promise<ListActionsResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async internalizeAction(
		args: InternalizeActionArgs,
	): Promise<InternalizeActionResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async listOutputs(args: ListOutputsArgs): Promise<ListOutputsResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async relinquishOutput(
		args: RelinquishOutputArgs,
	): Promise<RelinquishOutputResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async acquireCertificate(
		args: AcquireCertificateArgs,
	): Promise<WalletCertificate> {
		return Promise.reject(new Error("Not implemented"));
	}
	async listCertificates(
		args: ListCertificatesArgs,
	): Promise<ListCertificatesResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async proveCertificate(
		args: ProveCertificateArgs,
	): Promise<ProveCertificateResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async relinquishCertificate(
		args: RelinquishCertificateArgs,
	): Promise<RelinquishCertificateResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async discoverByIdentityKey(
		args: DiscoverByIdentityKeyArgs,
	): Promise<DiscoverCertificatesResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async discoverByAttributes(
		args: DiscoverByAttributesArgs,
	): Promise<DiscoverCertificatesResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async isAuthenticated(args: object): Promise<AuthenticatedResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async waitForAuthentication(args: object): Promise<AuthenticatedResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async getHeight(args: object): Promise<GetHeightResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async getHeaderForHeight(args: GetHeaderArgs): Promise<GetHeaderResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async getNetwork(args: object): Promise<GetNetworkResult> {
		return Promise.reject(new Error("Not implemented"));
	}
	async getVersion(args: object): Promise<GetVersionResult> {
		return Promise.reject(new Error("Not implemented"));
	}
}

export default Wallet;

```

--------------------------------------------------------------------------------
/tools/wallet/a2bPublishAgent.ts:
--------------------------------------------------------------------------------

```typescript
import { PrivateKey, Utils } from "@bsv/sdk";
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
import type {
	ServerNotification,
	ServerRequest,
} from "@modelcontextprotocol/sdk/types.js";
import { createOrdinals } from "js-1sat-ord";
import type {
	ChangeResult,
	CreateOrdinalsConfig,
	Destination,
	Inscription,
	LocalSigner,
	PreMAP,
} from "js-1sat-ord";
import { Sigma } from "sigma-protocol";
import { z } from "zod";
import type { Wallet } from "./wallet";
const { toArray, toBase64 } = Utils;

// https://raw.githubusercontent.com/google/A2A/refs/heads/main/specification/json/a2a.json

// A2A AgentCard schema (per A2A spec)
const AgentCapabilitiesSchema = z.object({
	streaming: z.boolean().default(false),
	pushNotifications: z.boolean().default(false),
	stateTransitionHistory: z.boolean().default(false),
});
const AgentSkillSchema = z.object({
	id: z.string(),
	name: z.string(),
	description: z.string().nullable(),
	tags: z.array(z.string()).nullable(),
	examples: z.array(z.string()).nullable(),
	inputModes: z.array(z.string()).nullable(),
	outputModes: z.array(z.string()).nullable(),
});

// Provider per A2A spec
const AgentProviderSchema = z
	.object({
		organization: z.string(),
		url: z.string().url().nullable().default(null),
	})
	.nullable()
	.default(null);

// Authentication per A2A spec
const AgentAuthenticationSchema = z
	.object({
		schemes: z.array(z.string()),
		credentials: z.string().nullable().default(null),
	})
	.nullable()
	.default(null);

// Pricing plan schema
export const PricingSchema = z.object({
	id: z.string(),
	description: z.string().nullable().optional(),
	currency: z.string(),
	amount: z
		.number()
		.describe(
			"Cost in standard units of currency (e.g., BSV, USD, not satoshis)",
		),
	address: z.string(),
	acceptedCurrencies: z.array(z.string()).optional(),
	skills: z.array(z.string()).optional(),
	interval: z.enum(["day", "week", "month", "year"]).nullable().optional(),
	includedCalls: z.record(z.number()).optional(),
});

export type PricingConfig = z.infer<typeof PricingSchema>;

export const AgentCardSchema = z.object({
	name: z.string(),
	description: z.string().nullable().default(null),
	url: z.string().url(),
	provider: AgentProviderSchema,
	version: z.string(),
	documentationUrl: z.string().url().nullable().default(null),
	capabilities: AgentCapabilitiesSchema,
	authentication: AgentAuthenticationSchema,
	defaultInputModes: z.array(z.string()).default(["text"]),
	defaultOutputModes: z.array(z.string()).default(["text"]),
	skills: z.array(AgentSkillSchema),
	"x-payment": z.array(PricingSchema).optional().default([]),
});

// Schema for on-chain agent publish parameters
export const a2bPublishArgsSchema = z.object({
	agentUrl: z
		.string()
		.url()
		.describe("Agent base URL (e.g. https://example.com)"),
	agentName: z.string().describe("Human-friendly agent name"),
	description: z
		.string()
		.nullable()
		.optional()
		.describe("Optional agent description"),
	providerOrganization: z
		.string()
		.optional()
		.describe("Optional provider organization name"),
	providerUrl: z.string().url().optional().describe("Optional provider URL"),
	version: z.string().optional().describe("Optional agent version"),
	documentationUrl: z
		.string()
		.url()
		.nullable()
		.optional()
		.describe("Optional documentation URL"),
	streaming: z
		.boolean()
		.default(false)
		.describe("Supports SSE (tasks/sendSubscribe)"),
	pushNotifications: z
		.boolean()
		.default(false)
		.describe("Supports push notifications"),
	stateTransitionHistory: z
		.boolean()
		.default(false)
		.describe("Supports state transition history"),
	defaultInputModes: z
		.array(z.string())
		.default(["text"])
		.describe("Default input modes"),
	defaultOutputModes: z
		.array(z.string())
		.default(["text"])
		.describe("Default output modes"),
	skills: z
		.array(AgentSkillSchema)
		.optional()
		.default([])
		.describe("List of agent skills"),
	destinationAddress: z
		.string()
		.optional()
		.describe("Optional target address for inscription"),
});
export type A2bPublishArgs = z.infer<typeof a2bPublishArgsSchema>;

/**
 * Registers the wallet_a2bPublish tool for publishing an agent record on-chain
 */
export function registerA2bPublishAgentTool(server: McpServer, wallet: Wallet) {
	server.tool(
		"wallet_a2bPublish",
		"Publish an agent.json record on-chain via Ordinal inscription",
		{ args: a2bPublishArgsSchema },
		async (
			{ args }: { args: A2bPublishArgs },
			extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
		) => {
			try {
				const paymentPk = wallet.getPrivateKey();
				if (!paymentPk) throw new Error("No private key available");
				const { paymentUtxos } = await wallet.getUtxos();
				if (!paymentUtxos?.length)
					throw new Error("No payment UTXOs available to fund inscription");

				// TODO: Get the skills from actually running the MCP instead of trusting the agent args for args.skills
				const walletAddress = paymentPk.toAddress().toString();

				// Create pricing plans using the new schema
				const pricingConfig: PricingConfig[] = [
					// {
					//   id: "subscription-premium",
					//   description: "Premium subscription with all features",
					//   currency: "USD",
					//   amount: 10,
					//   address: walletAddress,
					//   interval: "month",
					//   skills: ["wallet_a2bPublish", "wallet_a2bCall"],
					// },
					// {
					//   id: "subscription-free",
					//   description: "Free tier with limited features",
					//   currency: "USD",
					//   amount: 0,
					//   address: walletAddress,
					//   interval: "month",
					//   skills: ["wallet_a2bCall"],
					// },
					{
						id: "pay-per-call-default",
						description: "Pay-per-call for publish operations",
						currency: "USD",
						amount: 1,
						address: walletAddress,
						skills: ["wallet_a2bPublish"],
						interval: null,
					},
					// {
					//   id: "pay-per-call-royalty",
					//   description: "Royalty payment for agent",
					//   currency: "USD",
					//   amount: 1,
					//   address: "1JOExxxxxxxxxxxx",
					//   skills: ["wallet_a2bPublish"],
					//   interval: null,
					// }
				];

				// Assemble AgentCard with defaults and user overrides
				const agentCard = {
					name: args.agentName,
					description: args.description ?? null,
					url: args.agentUrl,
					provider:
						args.providerOrganization && args.providerUrl
							? {
									organization: args.providerOrganization,
									url: args.providerUrl,
								}
							: null,
					version: args.version ?? "1.0.0",
					documentationUrl: args.documentationUrl ?? null,
					capabilities: {
						streaming: args.streaming,
						pushNotifications: args.pushNotifications,
						stateTransitionHistory: args.stateTransitionHistory,
					},
					authentication: null,
					defaultInputModes: args.defaultInputModes,
					defaultOutputModes: args.defaultOutputModes,
					skills: args.skills,
					"x-payment-config": pricingConfig,
				};
				// Validate compliance
				AgentCardSchema.parse(agentCard);
				const fileContent = JSON.stringify(agentCard, null, 2);
				// Base64 payload for inscription
				const dataB64 = toBase64(toArray(fileContent));
				const inscription: Inscription = {
					dataB64,
					contentType: "application/json",
				};
				// Destination for the ordinal
				const targetAddress = args.destinationAddress ?? walletAddress;
				const destinations: Destination[] = [
					{ address: targetAddress, inscription },
				];
				// Default MAP metadata: file path, content type, encoding
				const metaData: PreMAP = { app: "bsv-mcp", type: "a2b" };

				const createOrdinalsConfig: CreateOrdinalsConfig = {
					utxos: paymentUtxos,
					destinations,
					paymentPk,
					changeAddress: walletAddress,
					metaData,
				};

				const identityPk = process.env.IDENTITY_KEY_WIF
					? PrivateKey.fromWif(process.env.IDENTITY_KEY_WIF)
					: undefined;
				if (identityPk) {
					createOrdinalsConfig.signer = {
						idKey: identityPk,
					} as LocalSigner;
				}

				// Inscribe the ordinal on-chain via js-1sat-ord
				const result = await createOrdinals(createOrdinalsConfig);
				const changeResult = result as ChangeResult;

				const disableBroadcasting = process.env.DISABLE_BROADCASTING === "true";
				if (!disableBroadcasting) {
					await changeResult.tx.broadcast();

					// Refresh UTXOs
					try {
						await wallet.refreshUtxos();
					} catch {}
					// Return transaction details
					return {
						content: [
							{
								type: "text",
								text: JSON.stringify({
									txid: changeResult.tx.id("hex"),
									spentOutpoints: changeResult.spentOutpoints,
									payChange: changeResult.payChange,
									inscriptionAddress: targetAddress,
									agentCard,
								}),
							},
						],
					};
				}

				return {
					content: [
						{
							type: "text",
							text: changeResult.tx.toHex(),
						},
					],
				};
			} catch (err: unknown) {
				const msg = err instanceof Error ? err.message : String(err);
				return { content: [{ type: "text", text: msg }], isError: true };
			}
		},
	);
}

```

--------------------------------------------------------------------------------
/tools/wallet/schemas.ts:
--------------------------------------------------------------------------------

```typescript
import type { SecurityLevel } from "@bsv/sdk";
import { z } from "zod";

// Define SecurityLevel to match the BSV SDK exactly
const SecurityLevelEnum = z.union([z.literal(0), z.literal(1), z.literal(2)]);

// Create a custom validator without tuples
export const walletProtocolSchema = z.custom<[SecurityLevel, string]>((val) => {
	return (
		Array.isArray(val) &&
		val.length === 2 &&
		(val[0] === 0 || val[0] === 1 || val[0] === 2) &&
		typeof val[1] === "string"
	);
});

// Empty args schema for functions that don't take arguments
export const emptyArgsSchema = z.object({});

// Get public key arguments
export const getPublicKeyArgsSchema = z.object({});

// Create signature arguments
export const createSignatureArgsSchema = z.object({
	data: z.array(z.number()).optional(),
	hashToDirectlySign: z.array(z.number()).optional(),
	protocolID: walletProtocolSchema,
	keyID: z.string(),
	privilegedReason: z.string().optional(),
	counterparty: z
		.union([z.string(), z.literal("self"), z.literal("anyone")])
		.optional(),
	privileged: z.boolean().optional(),
});

// Verify signature arguments
export const verifySignatureArgsSchema = z.object({
	data: z.array(z.number()).optional(),
	hashToDirectlyVerify: z.array(z.number()).optional(),
	signature: z.array(z.number()),
	protocolID: walletProtocolSchema,
	keyID: z.string(),
	privilegedReason: z.string().optional(),
	counterparty: z
		.union([z.string(), z.literal("self"), z.literal("anyone")])
		.optional(),
	forSelf: z.boolean().optional(),
	privileged: z.boolean().optional(),
});

// Wallet encryption args
export const walletEncryptArgsSchema = z.object({
	plaintext: z.array(z.number()),
	protocolID: walletProtocolSchema,
	keyID: z.string(),
	privilegedReason: z.string().optional(),
	counterparty: z
		.union([z.string(), z.literal("self"), z.literal("anyone")])
		.optional(),
	privileged: z.boolean().optional(),
});

// Wallet decryption args
export const walletDecryptArgsSchema = z.object({
	ciphertext: z.array(z.number()),
	protocolID: walletProtocolSchema,
	keyID: z.string(),
	privilegedReason: z.string().optional(),
	counterparty: z
		.union([z.string(), z.literal("self"), z.literal("anyone")])
		.optional(),
	privileged: z.boolean().optional(),
});

// Combined wallet encryption/decryption args
export const walletEncryptionArgsSchema = z
	.object({
		mode: z
			.enum(["encrypt", "decrypt"])
			.describe(
				"Operation mode: 'encrypt' to encrypt plaintext or 'decrypt' to decrypt data",
			),
		data: z
			.union([
				z.string().describe("Text data to encrypt or decrypt"),
				z.array(z.number()).describe("Binary data to encrypt or decrypt"),
			])
			.describe("Data to process: text/data for encryption or decryption"),
		encoding: z
			.enum(["utf8", "hex", "base64"])
			.optional()
			.default("utf8")
			.describe("Encoding of text data (default: utf8)"),
	})
	.describe("Schema for encryption and decryption operations");

// Create HMAC arguments
export const createHmacArgsSchema = z.object({
	message: z.string(),
	encoding: z.enum(["utf8", "hex", "base64"]).optional(),
});

// Verify HMAC arguments
export const verifyHmacArgsSchema = z.object({
	message: z.string(),
	hmac: z.string(),
	publicKey: z.string(),
	encoding: z.enum(["utf8", "hex", "base64"]).optional(),
});

// Transaction input schema
export const transactionInputSchema = z.object({
	outpoint: z.string(),
	inputDescription: z.string(), // Required field
	sequence: z.number().optional(),
});

// Transaction output schema
export const transactionOutputSchema = z.object({
	lockingScript: z.string(),
	satoshis: z.number(),
	outputDescription: z.string(), // Required field
	change: z.boolean().optional(),
});

// Create action args
export const createActionArgsSchema = z.object({
	description: z.string(),
	labels: z.array(z.string()).optional(),
	lockTime: z.number().optional(),
	version: z.number().optional(),
	inputBEEF: z.array(z.number()).optional(),
	inputs: z.array(transactionInputSchema).optional(),
	outputs: z.array(transactionOutputSchema).optional(),
	options: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),
});

// Sign action args
export const signActionArgsSchema = z.object({
	reference: z.string(),
	spends: z.record(z.any()).optional(),
	options: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),
});

// List actions args
export const listActionsArgsSchema = z.object({
	labels: z.array(z.string()),
	labelQueryMode: z.enum(["any", "all"]).optional(),
	limit: z.number().optional(),
	offset: z.number().optional(),
	includeInputs: z.boolean().optional(),
	includeOutputs: z.boolean().optional(),
	includeLabels: z.boolean().optional(),
	includeInputSourceLockingScripts: z.boolean().optional(),
	includeInputUnlockingScripts: z.boolean().optional(),
	seekPermission: z.boolean().optional(),
});

// List outputs args
export const listOutputsArgsSchema = z.object({
	basket: z.string(),
	tags: z.array(z.string()).optional(),
	tagQueryMode: z.enum(["all", "any"]).optional(),
	limit: z.number().optional(),
	offset: z.number().optional(),
	include: z.enum(["locking scripts", "entire transactions"]).optional(),
	includeLabels: z.boolean().optional(),
	includeTags: z.boolean().optional(),
	includeCustomInstructions: z.boolean().optional(),
	seekPermission: z.boolean().optional(),
});

// Reveal counterparty key linkage args
export const revealCounterpartyKeyLinkageArgsSchema = z.object({
	counterparty: z.string(),
	verifier: z.string(),
	privileged: z.boolean().optional(),
	privilegedReason: z.string().optional(),
});

// Reveal specific key linkage args
export const revealSpecificKeyLinkageArgsSchema = z.object({
	keyID: z.number(),
	verifier: z.string(),
	privileged: z.boolean().optional(),
	privilegedReason: z.string().optional(),
});

// Abort action args
export const abortActionArgsSchema = z.object({
	reference: z.string(),
});

// Internalize action args
export const internalizeActionArgsSchema = z.object({
	tx: z.array(z.number()),
	outputs: z.array(
		z.object({
			outputIndex: z.number(),
			protocol: z.enum(["wallet payment", "basket insertion"]),
			lockingScript: z.string(),
			satoshis: z.number(),
		}),
	),
	description: z.string(),
	labels: z.array(z.string()).optional(),
	seekPermission: z.boolean().optional(),
});

// Relinquish output args
export const relinquishOutputArgsSchema = z.object({
	basket: z.string(),
	output: z.string(),
});

// Acquire certificate args
export const acquireCertificateArgsSchema = z.object({
	type: z.string(),
	certifier: z.string(),
	acquisitionProtocol: z.enum(["direct", "issuance"]),
	fields: z.record(z.string()),
	certifierUrl: z.string().optional(),
	serialNumber: z.string().optional(),
	signature: z.string().optional(),
	revocationOutpoint: z.string().optional(),
	keyringForSubject: z.record(z.string()).optional(),
	keyringRevealer: z.string().optional(),
	privileged: z.boolean().optional(),
	privilegedReason: z.string().optional(),
});

// List certificates args
export const listCertificatesArgsSchema = z.object({
	certifiers: z.array(z.string()),
	types: z.array(z.string()),
	limit: z.number().optional(),
	offset: z.number().optional(),
	privileged: z.boolean().optional(),
	privilegedReason: z.string().optional(),
});

// Prove certificate args
export const proveCertificateArgsSchema = z.object({
	certificate: z.object({}),
	fieldsToReveal: z.array(z.string()),
	verifier: z.string(),
	privileged: z.boolean().optional(),
	privilegedReason: z.string().optional(),
});

// Relinquish certificate args
export const relinquishCertificateArgsSchema = z.object({
	type: z.string(),
	serialNumber: z.string(),
	certifier: z.string(),
});

// Discover by identity key args
export const discoverByIdentityKeyArgsSchema = z.object({
	identityKey: z.string(),
	limit: z.number().optional(),
	offset: z.number().optional(),
	seekPermission: z.boolean().optional(),
});

// Discover by attributes args
export const discoverByAttributesArgsSchema = z.object({
	attributes: z.record(z.string()),
	limit: z.number().optional(),
	offset: z.number().optional(),
	seekPermission: z.boolean().optional(),
});

// Get header for height args
export const getHeaderArgsSchema = z.object({
	height: z.number(),
});

// Get address args
export const getAddressArgsSchema = z.object({});

// Send to address args
export const sendToAddressArgsSchema = z.object({
	address: z.string(),
	amount: z.number(),
	currency: z.enum(["BSV", "USD"]).optional().default("BSV"),
	description: z.string().optional(),
});

/**
 * Schema for purchase listing arguments
 */
export const purchaseListingArgsSchema = z
	.object({
		listingOutpoint: z
			.string()
			.describe("The outpoint of the listing to purchase (txid_vout format)"),
		ordAddress: z
			.string()
			.describe("The ordinal address to receive the purchased item"),
		listingType: z
			.enum(["nft", "token"])
			.default("nft")
			.describe(
				"Type of listing: 'nft' for ordinal NFTs, 'token' for BSV-20 tokens",
			),
		tokenProtocol: z
			.enum(["bsv-20", "bsv-21"])
			.optional()
			.default("bsv-21")
			.describe(
				"Token protocol for token listings (required when listingType is 'token')",
			),
		tokenID: z
			.string()
			.optional()
			.describe(
				"Token ID for BSV-21 tokens or ticker for BSV-20 tokens (required when listingType is 'token')",
			),
		description: z
			.string()
			.optional()
			.describe("Optional description for the transaction"),
	})
	.describe(
		"Schema for the wallet_purchaseListing tool arguments (purchase NFTs or tokens), with detailed field descriptions.",
	);

// Export types
export type SendToAddressArgs = z.infer<typeof sendToAddressArgsSchema>;
export type PurchaseListingArgs = z.infer<typeof purchaseListingArgsSchema>;
export type WalletEncryptionArgs = z.infer<typeof walletEncryptionArgsSchema>;

```
Page 1/2FirstPrevNextLast