UTXO queries support by xchainjs
This commit is contained in:
69
README.md
69
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Web3 MCP
|
# Web3 MCP
|
||||||
|
|
||||||
A Model-Context-Protocol server for interacting with multiple blockchains including Solana and Ethereum. This server provides simple RPC endpoints for common blockchain operations, allowing secure interactions with various blockchains through environment variables.
|
A Model-Context-Protocol server for interacting with multiple blockchains including Solana, Ethereum, and UTXO chains. This server provides simple RPC endpoints for common blockchain operations, allowing secure interactions with various blockchains through environment variables.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -19,6 +19,28 @@ Ethereum & EVM Chain Operations:
|
|||||||
- Send ERC-20 tokens (using private key from .env)
|
- Send ERC-20 tokens (using private key from .env)
|
||||||
- Approve ERC-20 token spending (using private key from .env)
|
- Approve ERC-20 token spending (using private key from .env)
|
||||||
|
|
||||||
|
UTXO Chain Operations:
|
||||||
|
- Bitcoin (BTC)
|
||||||
|
- Check address balances
|
||||||
|
- View transaction history
|
||||||
|
- Validate addresses
|
||||||
|
- Get network info and fees
|
||||||
|
- Litecoin (LTC)
|
||||||
|
- Check address balances
|
||||||
|
- View transaction history
|
||||||
|
- Validate addresses
|
||||||
|
- Get network info and fees
|
||||||
|
- Dogecoin (DOGE)
|
||||||
|
- Check address balances
|
||||||
|
- View transaction history
|
||||||
|
- Validate addresses
|
||||||
|
- Get network info and fees
|
||||||
|
- Bitcoin Cash (BCH)
|
||||||
|
- Check address balances
|
||||||
|
- View transaction history
|
||||||
|
- Validate addresses
|
||||||
|
- Get network info and fees
|
||||||
|
|
||||||
Supported EVM Networks:
|
Supported EVM Networks:
|
||||||
- Ethereum
|
- Ethereum
|
||||||
- Base
|
- Base
|
||||||
@@ -31,19 +53,19 @@ Supported EVM Networks:
|
|||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Clone and install dependencies:
|
1. Clone and install dependencies:
|
||||||
```bash
|
\`\`\`bash
|
||||||
git clone https://github.com/strangelove-ventures/web3-mcp.git
|
git clone https://github.com/strangelove-ventures/web3-mcp.git
|
||||||
cd web3-mcp
|
cd web3-mcp
|
||||||
npm install
|
npm install
|
||||||
```
|
\`\`\`
|
||||||
|
|
||||||
2. Create a .env file in the root directory:
|
2. Create a .env file in the root directory:
|
||||||
```bash
|
\`\`\`bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
```
|
\`\`\`
|
||||||
|
|
||||||
3. Configure your environment variables in .env:
|
3. Configure your environment variables in .env:
|
||||||
```env
|
\`\`\`env
|
||||||
# Network RPC URLs (optional - will use public endpoints if not specified)
|
# Network RPC URLs (optional - will use public endpoints if not specified)
|
||||||
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
|
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
|
||||||
ETH_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/your-api-key
|
ETH_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/your-api-key
|
||||||
@@ -57,15 +79,19 @@ AVALANCHE_RPC_URL=https://api.avax.network/ext/bc/C/rpc
|
|||||||
# Private keys (required for transactions)
|
# Private keys (required for transactions)
|
||||||
ETH_PRIVATE_KEY=your-ethereum-private-key
|
ETH_PRIVATE_KEY=your-ethereum-private-key
|
||||||
SOLANA_PRIVATE_KEY=your-base58-encoded-solana-private-key
|
SOLANA_PRIVATE_KEY=your-base58-encoded-solana-private-key
|
||||||
```
|
|
||||||
|
# Optional API keys for UTXO chain data providers
|
||||||
|
BLOCKCYPHER_API_KEY=your-blockcypher-api-key
|
||||||
|
SOCHAIN_API_KEY=your-sochain-api-key
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
4. Build the tool:
|
4. Build the tool:
|
||||||
```bash
|
\`\`\`bash
|
||||||
npm run build
|
npm run build
|
||||||
```
|
\`\`\`
|
||||||
|
|
||||||
5. Add the tool to your claude_desktop_config.json:
|
5. Add the tool to your claude_desktop_config.json:
|
||||||
```json
|
\`\`\`json
|
||||||
{
|
{
|
||||||
"web3-rpc": {
|
"web3-rpc": {
|
||||||
"command": "node",
|
"command": "node",
|
||||||
@@ -74,7 +100,7 @@ npm run build
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
\`\`\`
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
|
|
||||||
@@ -94,6 +120,13 @@ Ask Claude:
|
|||||||
- "What's the current gas price on Arbitrum?"
|
- "What's the current gas price on Arbitrum?"
|
||||||
- "Send 100 USDC to 0x556437c4d22ceaeeebf82006b85bdcc0ae67d933 on Polygon"
|
- "Send 100 USDC to 0x556437c4d22ceaeeebf82006b85bdcc0ae67d933 on Polygon"
|
||||||
|
|
||||||
|
### UTXO Chain Operations
|
||||||
|
- "What's the BTC balance of 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa?"
|
||||||
|
- "Show me the transaction history for bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"
|
||||||
|
- "Is ltc1qgj73yjsc4tm5kgv2zgf9zqt74pac4peugvxhuy a valid Litecoin address?"
|
||||||
|
- "What are the current network fees for Bitcoin Cash?"
|
||||||
|
- "Check DOGE balance for D6T2SsXPQPqFBcYzX3EXJvYG1F8BsNAk4z"
|
||||||
|
|
||||||
## Security Notes
|
## Security Notes
|
||||||
|
|
||||||
1. **Environment Variables**: All private keys are stored in the .env file and never exposed in the conversation history
|
1. **Environment Variables**: All private keys are stored in the .env file and never exposed in the conversation history
|
||||||
@@ -109,13 +142,21 @@ You can configure custom RPC endpoints in your .env file for better reliability
|
|||||||
### Network Selection
|
### Network Selection
|
||||||
For EVM operations, you can specify the network by name (ethereum, base, arbitrum, optimism, bsc, polygon, avalanche). The tool will automatically use the appropriate RPC endpoint and network configuration.
|
For EVM operations, you can specify the network by name (ethereum, base, arbitrum, optimism, bsc, polygon, avalanche). The tool will automatically use the appropriate RPC endpoint and network configuration.
|
||||||
|
|
||||||
|
### UTXO Chain Data Providers
|
||||||
|
The tool uses several data providers for UTXO chains:
|
||||||
|
- BlockCypher
|
||||||
|
- SoChain
|
||||||
|
- Haskoin (for Bitcoin Cash)
|
||||||
|
|
||||||
|
You can configure API keys for these providers in the .env file for better rate limits.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
To modify or extend the tool:
|
To modify or extend the tool:
|
||||||
|
|
||||||
1. Source code is in the `src` directory
|
1. Source code is in the \`src\` directory
|
||||||
2. Chain-specific code in `src/chains`
|
2. Chain-specific code in \`src/chains\`
|
||||||
3. Run `npm run build` after making changes
|
3. Run \`npm run build\` after making changes
|
||||||
4. Use TypeScript for all new code
|
4. Use TypeScript for all new code
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|||||||
@@ -47,9 +47,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xchainjs/xchain-bitcoin": "^1.2.2",
|
"@xchainjs/xchain-bitcoin": "^1.2.2",
|
||||||
|
"@xchainjs/xchain-bitcoincash": "^1.0.10",
|
||||||
"@xchainjs/xchain-client": "^0.16.2",
|
"@xchainjs/xchain-client": "^0.16.2",
|
||||||
"@xchainjs/xchain-crypto": "^0.3.7",
|
"@xchainjs/xchain-crypto": "^0.3.7",
|
||||||
|
"@xchainjs/xchain-doge": "^1.0.10",
|
||||||
|
"@xchainjs/xchain-litecoin": "^1.0.10",
|
||||||
"@xchainjs/xchain-util": "^0.13.1",
|
"@xchainjs/xchain-util": "^0.13.1",
|
||||||
|
"@psf/bitcoincashjs-lib": "^4.0.3",
|
||||||
|
"bchaddrjs": "^0.5.2",
|
||||||
"bitcoinjs-lib": "^6.1.5",
|
"bitcoinjs-lib": "^6.1.5",
|
||||||
"wif": "^4.0.0",
|
"wif": "^4.0.0",
|
||||||
"@jup-ag/api": "^6.0.12",
|
"@jup-ag/api": "^6.0.12",
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { Client as BitcoinClient, defaultBTCParams, BlockcypherDataProviders } from '@xchainjs/xchain-bitcoin'
|
import { Client as BitcoinClient, defaultBTCParams, BlockcypherDataProviders as BTCBlockcypherDataProviders } from '@xchainjs/xchain-bitcoin'
|
||||||
|
import { Client as LitecoinClient, defaultLtcParams } from '@xchainjs/xchain-litecoin'
|
||||||
|
import { Client as DogeClient, defaultDogeParams } from '@xchainjs/xchain-doge'
|
||||||
|
import { Client as BitcoinCashClient, defaultBchParams } from '@xchainjs/xchain-bitcoincash'
|
||||||
import { Network } from '@xchainjs/xchain-client'
|
import { Network } from '@xchainjs/xchain-client'
|
||||||
import { Asset } from '@xchainjs/xchain-util'
|
import { Asset } from '@xchainjs/xchain-util'
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
@@ -6,21 +9,35 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
|
|||||||
import { config } from 'dotenv'
|
import { config } from 'dotenv'
|
||||||
config()
|
config()
|
||||||
|
|
||||||
const BTC_ASSET: Asset = {
|
// Initialize clients with mainnet by default
|
||||||
chain: 'BTC',
|
|
||||||
symbol: 'BTC',
|
|
||||||
ticker: 'BTC',
|
|
||||||
synth: false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize Bitcoin client with mainnet by default
|
|
||||||
const bitcoinClient = new BitcoinClient({
|
const bitcoinClient = new BitcoinClient({
|
||||||
...defaultBTCParams,
|
...defaultBTCParams,
|
||||||
network: Network.Mainnet,
|
network: Network.Mainnet,
|
||||||
dataProviders: [BlockcypherDataProviders],
|
dataProviders: [BTCBlockcypherDataProviders],
|
||||||
})
|
})
|
||||||
|
|
||||||
export function registerBitcoinTools(server: McpServer) {
|
const litecoinClient = new LitecoinClient({
|
||||||
|
...defaultLtcParams,
|
||||||
|
network: Network.Mainnet,
|
||||||
|
})
|
||||||
|
|
||||||
|
const dogeClient = new DogeClient({
|
||||||
|
...defaultDogeParams,
|
||||||
|
network: Network.Mainnet,
|
||||||
|
})
|
||||||
|
|
||||||
|
const bitcoinCashClient = new BitcoinCashClient({
|
||||||
|
...defaultBchParams,
|
||||||
|
network: Network.Mainnet,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Helper function to format base amounts for all UTXO chains
|
||||||
|
function formatBaseAmount(baseAmount: any): string {
|
||||||
|
return baseAmount.amount().toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerUtxoTools(server: McpServer) {
|
||||||
|
// Bitcoin Tools
|
||||||
server.tool(
|
server.tool(
|
||||||
"getBitcoinBalance",
|
"getBitcoinBalance",
|
||||||
"Get balance for a Bitcoin address",
|
"Get balance for a Bitcoin address",
|
||||||
@@ -34,179 +51,223 @@ export function registerBitcoinTools(server: McpServer) {
|
|||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
text: `Bitcoin Balance for ${address}:\n${balances[0].amount.amount().toString()} BTC`,
|
text: `Bitcoin Balance for ${address}:\n${formatBaseAmount(balances[0].amount)} BTC`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = err as Error
|
const error = err as Error
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [{ type: "text", text: `Failed to retrieve BTC balance: ${error.message}` }],
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Failed to retrieve balance: ${error.message}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Litecoin Tools
|
||||||
server.tool(
|
server.tool(
|
||||||
"validateBitcoinAddress",
|
"getLitecoinBalance",
|
||||||
"Validate a Bitcoin address format",
|
"Get balance for a Litecoin address",
|
||||||
{
|
{
|
||||||
address: z.string().describe("Bitcoin address to validate"),
|
address: z.string().describe("Litecoin address to check"),
|
||||||
},
|
},
|
||||||
async ({ address }) => {
|
async ({ address }) => {
|
||||||
try {
|
try {
|
||||||
const isValid = bitcoinClient.validateAddress(address)
|
const balances = await litecoinClient.getBalance(address)
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
text: isValid
|
text: `Litecoin Balance for ${address}:\n${formatBaseAmount(balances[0].amount)} LTC`,
|
||||||
? `The address ${address} is a valid Bitcoin address`
|
|
||||||
: `The address ${address} is NOT a valid Bitcoin address`,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = err as Error
|
const error = err as Error
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [{ type: "text", text: `Failed to retrieve LTC balance: ${error.message}` }],
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Error validating address: ${error.message}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Dogecoin Tools
|
||||||
server.tool(
|
server.tool(
|
||||||
"getBitcoinTransactionData",
|
"getDogeBalance",
|
||||||
"Get transaction details for a Bitcoin transaction",
|
"Get balance for a Dogecoin address",
|
||||||
{
|
{
|
||||||
txId: z.string().describe("Bitcoin transaction ID/hash"),
|
address: z.string().describe("Dogecoin address to check"),
|
||||||
},
|
},
|
||||||
async ({ txId }) => {
|
async ({ address }) => {
|
||||||
try {
|
try {
|
||||||
const txData = await bitcoinClient.getTransactionData(txId)
|
const balances = await dogeClient.getBalance(address)
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Dogecoin Balance for ${address}:\n${formatBaseAmount(balances[0].amount)} DOGE`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const error = err as Error
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Failed to retrieve DOGE balance: ${error.message}` }],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Bitcoin Cash Tools
|
||||||
|
server.tool(
|
||||||
|
"getBitcoinCashBalance",
|
||||||
|
"Get balance for a Bitcoin Cash address",
|
||||||
|
{
|
||||||
|
address: z.string().describe("Bitcoin Cash address to check"),
|
||||||
|
},
|
||||||
|
async ({ address }) => {
|
||||||
|
try {
|
||||||
|
const balances = await bitcoinCashClient.getBalance(address)
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Bitcoin Cash Balance for ${address}:\n${formatBaseAmount(balances[0].amount)} BCH`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const error = err as Error
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: `Failed to retrieve BCH balance: ${error.message}` }],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Common Tools for all UTXO chains
|
||||||
|
// Transaction History
|
||||||
|
const getTransactionHistory = async (client: any, address: string, chain: string, limit = 10, offset = 0) => {
|
||||||
|
try {
|
||||||
|
const txs = await client.getTransactions({ address, limit, offset })
|
||||||
|
const txList = txs.txs.map((tx: any) => {
|
||||||
|
const fromAmount = tx.from[0]?.amount ? `${formatBaseAmount(tx.from[0].amount)} ${chain}` : 'Unknown'
|
||||||
|
const toAmount = tx.to[0]?.amount ? `${formatBaseAmount(tx.to[0].amount)} ${chain}` : 'Unknown'
|
||||||
|
|
||||||
// Format amounts if available using amount.amount()
|
return `
|
||||||
const fromAmount = txData.from[0]?.amount ? `${txData.from[0].amount.amount().toString()} BTC` : 'Unknown'
|
|
||||||
const toAmount = txData.to[0]?.amount ? `${txData.to[0].amount.amount().toString()} BTC` : 'Unknown'
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Transaction Details for ${txId}:
|
|
||||||
Hash: ${txData.hash}
|
|
||||||
From: ${txData.from[0]?.from || 'Unknown'} (${fromAmount})
|
|
||||||
To: ${txData.to[0]?.to || 'Unknown'} (${toAmount})
|
|
||||||
Asset: ${txData.asset.ticker}
|
|
||||||
Type: ${txData.type}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const error = err as Error
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Failed to retrieve transaction data: ${error.message}`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
server.tool(
|
|
||||||
"getBitcoinTransactionHistory",
|
|
||||||
"Get transaction history for a Bitcoin address",
|
|
||||||
{
|
|
||||||
address: z.string().describe("Bitcoin address to check"),
|
|
||||||
limit: z.number().optional().describe("Maximum number of transactions to return"),
|
|
||||||
offset: z.number().optional().describe("Number of transactions to skip"),
|
|
||||||
},
|
|
||||||
async ({ address, limit = 10, offset = 0 }) => {
|
|
||||||
try {
|
|
||||||
const txs = await bitcoinClient.getTransactions({
|
|
||||||
address,
|
|
||||||
limit,
|
|
||||||
offset
|
|
||||||
})
|
|
||||||
|
|
||||||
const txList = txs.txs.map(tx => {
|
|
||||||
const fromAmount = tx.from[0]?.amount ? `${tx.from[0].amount.amount().toString()} BTC` : 'Unknown'
|
|
||||||
const toAmount = tx.to[0]?.amount ? `${tx.to[0].amount.amount().toString()} BTC` : 'Unknown'
|
|
||||||
|
|
||||||
return `
|
|
||||||
Transaction: ${tx.hash}
|
Transaction: ${tx.hash}
|
||||||
Type: ${tx.type}
|
Type: ${tx.type}
|
||||||
From: ${tx.from[0]?.from || 'Unknown'} (${fromAmount})
|
From: ${tx.from[0]?.from || 'Unknown'} (${fromAmount})
|
||||||
To: ${tx.to[0]?.to || 'Unknown'} (${toAmount})
|
To: ${tx.to[0]?.to || 'Unknown'} (${toAmount})
|
||||||
Asset: ${tx.asset.ticker}
|
Asset: ${tx.asset.ticker}
|
||||||
Date: ${new Date(tx.date).toLocaleString()}`
|
Date: ${new Date(tx.date).toLocaleString()}`
|
||||||
}).join('\n---\n')
|
}).join('\n---\n')
|
||||||
|
|
||||||
|
return { success: true, content: txList }
|
||||||
|
} catch (err) {
|
||||||
|
const error = err as Error
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register history tools for each chain
|
||||||
|
const chains = [
|
||||||
|
{ name: 'Bitcoin', symbol: 'BTC', client: bitcoinClient },
|
||||||
|
{ name: 'Litecoin', symbol: 'LTC', client: litecoinClient },
|
||||||
|
{ name: 'Dogecoin', symbol: 'DOGE', client: dogeClient },
|
||||||
|
{ name: 'Bitcoin Cash', symbol: 'BCH', client: bitcoinCashClient },
|
||||||
|
]
|
||||||
|
|
||||||
|
chains.forEach(({ name, symbol, client }) => {
|
||||||
|
server.tool(
|
||||||
|
`get${symbol}TransactionHistory`,
|
||||||
|
`Get transaction history for a ${name} address`,
|
||||||
|
{
|
||||||
|
address: z.string().describe(`${name} address to check`),
|
||||||
|
limit: z.number().optional().describe("Maximum number of transactions to return"),
|
||||||
|
offset: z.number().optional().describe("Number of transactions to skip"),
|
||||||
|
},
|
||||||
|
async ({ address, limit = 10, offset = 0 }) => {
|
||||||
|
const result = await getTransactionHistory(client, address, symbol, limit, offset)
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
text: `Transaction History for ${address}:\n${txList}`,
|
text: result.success
|
||||||
},
|
? `${name} Transaction History for ${address}:\n${result.content}`
|
||||||
],
|
: `Failed to retrieve ${name} transaction history: ${result.error}`,
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const error = err as Error
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: "text",
|
|
||||||
text: `Failed to retrieve transaction history: ${error.message}`,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
|
|
||||||
server.tool(
|
// Add address validation
|
||||||
"getBitcoinNetworkInfo",
|
server.tool(
|
||||||
"Get current Bitcoin network information",
|
`validate${symbol}Address`,
|
||||||
{},
|
`Validate a ${name} address format`,
|
||||||
async () => {
|
{
|
||||||
try {
|
address: z.string().describe(`${name} address to validate`),
|
||||||
const fees = await bitcoinClient.getFeeRates()
|
},
|
||||||
return {
|
async ({ address }) => {
|
||||||
content: [
|
try {
|
||||||
{
|
const isValid = client.validateAddress(address)
|
||||||
type: "text",
|
return {
|
||||||
text: `Bitcoin Network Information:
|
content: [
|
||||||
Current Network: ${bitcoinClient.getNetwork()}
|
{
|
||||||
|
type: "text",
|
||||||
|
text: isValid
|
||||||
|
? `The address ${address} is a valid ${name} address`
|
||||||
|
: `The address ${address} is NOT a valid ${name} address`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const error = err as Error
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `Error validating ${name} address: ${error.message}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add network info
|
||||||
|
server.tool(
|
||||||
|
`get${symbol}NetworkInfo`,
|
||||||
|
`Get current ${name} network information`,
|
||||||
|
{},
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
const fees = await client.getFeeRates()
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `${name} Network Information:
|
||||||
|
Current Network: ${client.getNetwork()}
|
||||||
Fee Rates (sats/byte):
|
Fee Rates (sats/byte):
|
||||||
Fast: ${fees.fast}
|
Fast: ${fees.fast}
|
||||||
Average: ${fees.average}`,
|
Average: ${fees.average}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const error = err as Error
|
const error = err as Error
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
type: "text",
|
type: "text",
|
||||||
text: `Failed to retrieve network information: ${error.message}`,
|
text: `Failed to retrieve ${name} network information: ${error.message}`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|||||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
import { registerSolanaTools } from "./chains/solana.js";
|
import { registerSolanaTools } from "./chains/solana.js";
|
||||||
import { registerEthereumTools } from "./chains/ethereum.js";
|
import { registerEthereumTools } from "./chains/ethereum.js";
|
||||||
import { registerBitcoinTools } from "./chains/xchain.js";
|
import { registerUtxoTools } from "./chains/xchain.js";
|
||||||
|
|
||||||
// Create server instance
|
// Create server instance
|
||||||
const server = new McpServer({
|
const server = new McpServer({
|
||||||
@@ -35,7 +35,7 @@ const server = new McpServer({
|
|||||||
// Register chain-specific tools
|
// Register chain-specific tools
|
||||||
registerSolanaTools(server);
|
registerSolanaTools(server);
|
||||||
registerEthereumTools(server);
|
registerEthereumTools(server);
|
||||||
registerBitcoinTools(server);
|
registerUtxoTools(server);
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const transport = new StdioServerTransport();
|
const transport = new StdioServerTransport();
|
||||||
|
|||||||
Reference in New Issue
Block a user