nbulian/bitcoin-mcp-server
If you are the rightful owner of bitcoin-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.
A comprehensive Model Context Protocol (MCP) server for Bitcoin blockchain connectivity and analysis, providing seamless integration with Bitcoin nodes and external APIs for comprehensive blockchain data access.
blockchain.py
Blockchain query tools (blocks, transactions)
network.py
Network monitoring tools (mempool, mining)
address.py
Address analysis tools (balance, UTXOs)
market.py
Market data tools (price, stats)
Bitcoin MCP Server
A comprehensive Model Context Protocol (MCP) server for Bitcoin blockchain connectivity and analysis, providing seamless integration with Bitcoin nodes and external APIs for comprehensive blockchain data access.
🏗️ Project Structure
bitcoin-mcp-server/
├── src/ # Main source code directory
│ ├── __init__.py # Package initialization
│ ├── main.py # FastAPI application entry point and MCP routing
│ ├── config.py # Configuration management with Pydantic
│ ├── bitcoin_client.py # Bitcoin RPC client with connection management
│ ├── mcp_protocol.py # MCP Protocol implementation
│ ├── tools/ # MCP tool implementations
│ │ ├── __init__.py # Tools package initialization
│ │ ├── blockchain.py # Blockchain query tools (blocks, transactions)
│ │ ├── network.py # Network monitoring tools (mempool, mining)
│ │ ├── address.py # Address analysis tools (balance, UTXOs)
│ │ └── market.py # Market data tools (price, stats)
│ └── utils/ # Utility modules
│ ├── __init__.py # Utils package initialization
│ ├── validation.py # Input validation utilities
│ └── errors.py # Custom error classes and handling
├── requirements.txt # Python dependencies
├── Dockerfile # Docker container configuration
├── docker-compose.yml # Docker Compose for easy deployment
├── .env.example # Environment variables template
└── README.md # Project documentation
📦 Dependencies
Core Dependencies
- FastAPI (0.104.1): Modern web framework for building APIs
- Uvicorn (0.24.0): ASGI server for running FastAPI applications
- httpx (0.25.2): Async HTTP client for external API calls
- Pydantic (1.10.13): Data validation and settings management
- python-dotenv (1.0.0): Environment variable loading
Bitcoin-Specific Dependencies
- base58 (2.1.1): Bitcoin address validation and encoding utilities
- Used for validating Bitcoin addresses across different formats (P2PKH, P2SH, Bech32, Bech32m)
- Provides robust address format checking for mainnet, testnet, and regtest networks
External API Dependencies
The server integrates with several external APIs to provide comprehensive Bitcoin data:
- Bitcoin RPC Node: Primary data source for blockchain information
- Mempool.space API: Address balance and transaction history
- CoinGecko API: Market price data and statistics
- Alternative.me API: Fear & Greed Index for market sentiment
🔧 MCP Protocol Compliance
This server is fully compliant with the Model Context Protocol (MCP) specification. It implements all required MCP methods:
Core MCP Methods
initialize
: Server initialization and capability negotiationtools/list
: List available tools with schemastools/call
: Execute tool calls with proper parameter validationresources/list
: List available resourcesresources/read
: Read resource content
Tool Definitions
All tools are defined with proper JSON schemas including:
- name: Tool identifier
- description: Human-readable description
- inputSchema: JSON schema for input parameters
- outputSchema: JSON schema for output format
Resource Definitions
Resources are defined with:
- uri: Resource identifier
- name: Human-readable name
- description: Resource description
- mimeType: Content type
📁 File Descriptions
Core Application Files
src/main.py
Purpose: FastAPI application entry point and MCP protocol routing
Key Features:
- MCP protocol method routing (
initialize
,tools/list
,tools/call
, etc.) - JSON-RPC 2.0 protocol implementation
- Legacy method support for backward compatibility
- Global error handling and logging
- Application lifecycle management
- CORS middleware configuration
Developer Notes: This is the main entry point. MCP methods are routed through the MCPProtocol class.
src/mcp_protocol.py
Purpose: MCP Protocol implementation and tool/resource definitions
Key Features:
- Complete MCP protocol method implementations
- Tool definitions with JSON schemas
- Resource definitions with metadata
- Tool execution routing
- Parameter validation
Developer Notes: Add new tools and resources here. All tools must have proper schemas defined.
src/config.py
Purpose: Centralized configuration management using Pydantic
Key Features:
- Environment variable loading
- Configuration validation
- Network type enumeration (mainnet, testnet, regtest)
- API endpoints and credentials management
Developer Notes: Add new configuration parameters here. All configs are validated on startup.
src/bitcoin_client.py
Purpose: Async Bitcoin RPC client with advanced features
Key Features:
- HTTP connection management with authentication
- Rate limiting and retry logic
- Exponential backoff for failed requests
- JSON-RPC 1.0 protocol implementation for Bitcoin Core
- Context manager for proper resource cleanup
Developer Notes: This handles all Bitcoin node communication. Add new RPC methods as async functions.
Tool Modules
src/tools/blockchain.py
Purpose: Blockchain data retrieval and analysis
Available MCP Tools:
- get_blockchain_info(): Network statistics and status
- get_block_by_height(): Block data by height
- get_block_by_hash(): Block data by hash
- get_transaction(): Transaction details
- get_latest_blocks(): Recent blocks list
- search_blocks(): Block range queries
Developer Notes: Add new blockchain query methods here. Ensure proper validation for all inputs.
src/tools/network.py
Purpose: Bitcoin network monitoring and status
Available MCP Tools:
- get_network_status(): Comprehensive network overview
- get_mempool_stats(): Mempool statistics and fee estimates
- get_mining_info(): Mining difficulty and hash rate
- get_peer_info(): Network peer information
Developer Notes: Network tools primarily use Bitcoin RPC. Fee estimation targets can be customized.
src/tools/address.py
Purpose: Bitcoin address analysis and UTXO management
Available MCP Tools:
- validate_address(): Address format validation
- get_address_balance(): Balance checking via external APIs
- get_address_transactions(): Transaction history
- get_address_utxos(): Unspent transaction outputs
- analyze_address_activity(): Comprehensive address analysis
Developer Notes: Uses mempool.space API for address data. Bitcoin Core doesn't track arbitrary addresses without wallet functionality.
src/tools/market.py
Purpose: Bitcoin market data and price information
Available MCP Tools:
- get_current_price(): Real-time price in various currencies
- get_price_history(): Historical price data
- get_market_stats(): Comprehensive market statistics
- get_fear_greed_index(): Market sentiment indicator
Developer Notes: Uses CoinGecko and Alternative.me APIs. API keys can be added for higher rate limits.
Utility Modules
src/utils/validation.py
Purpose: Input validation for Bitcoin-specific data types
Key Functions:
- validate_bitcoin_address(): Multi-format address validation
- validate_transaction_hash(): 64-character hex validation
- validate_block_hash(): Block hash format validation
- validate_block_height(): Block height range validation
Developer Notes: Add new validation functions here. Uses base58 for address validation.
src/utils/errors.py
Purpose: Custom exception classes and JSON-RPC error formatting
Key Classes:
- BitcoinMCPError: Base exception with JSON-RPC formatting
- BitcoinRPCError: Bitcoin node communication errors
- ValidationError: Input validation failures
- NetworkError: Network connectivity issues
- RateLimitError: Rate limiting violations
Developer Notes: All custom errors should inherit from BitcoinMCPError for consistent handling.
🚀 Features
Core Capabilities
- MCP Protocol Compliance: Full implementation of MCP specification
- Blockchain Queries: Complete block and transaction data access
- Network Monitoring: Real-time network status and mempool analysis
- Address Analysis: Balance checking, transaction history, UTXO tracking
- Market Data: Price feeds, historical data, market sentiment
- Rate Limiting: Configurable request rate limiting
- Error Handling: Comprehensive error handling with detailed messages
- Docker Support: Production-ready containerization
- Backward Compatibility: Legacy method support
MCP Protocol Methods
Core Protocol
initialize
: Server initialization and capability negotiationtools/list
: List available tools with schemastools/call
: Execute tool calls with parameter validationresources/list
: List available resourcesresources/read
: Read resource content
Available Tools (20 total)
- Blockchain: 6 tools (info, blocks, transactions, search)
- Network: 4 tools (status, mempool, mining, peers)
- Address: 5 tools (validation, balance, transactions, UTXOs, analysis)
- Market: 5 tools (price, history, stats, sentiment, current)
🔧 Error Handling & Response Codes
JSON-RPC Error Codes
Code | Name | Description |
---|---|---|
-32000 | BitcoinMCPError | General Bitcoin MCP server error |
-32001 | BitcoinRPCError | Bitcoin node RPC communication error |
-32002 | ValidationError | Input validation failure |
-32003 | NetworkError | Network connectivity issue |
-32004 | RateLimitError | Rate limit exceeded |
-32600 | Invalid Request | JSON-RPC format error |
-32601 | Method not found | Unknown method requested |
-32602 | Invalid params | Invalid method parameters |
-32603 | Internal error | Server internal error |
-32700 | Parse error | Invalid JSON format |
Error Response Format
{
"jsonrpc": "2.0",
"error": {
"code": -32002,
"message": "Invalid Bitcoin address format",
"data": {
"field": "address"
}
},
"id": 1
}
Rate Limiting
The server implements configurable rate limiting to protect the Bitcoin node:
- Default: 60 requests per minute per client
- Configurable: Via
RATE_LIMIT_PER_MINUTE
environment variable - Error Response: Returns
-32004
error code when exceeded - Implementation: Sliding window rate limiting in
BitcoinRPCClient
📊 API Response Examples
Successful Response Format
{
"jsonrpc": "2.0",
"result": {
// Method-specific data
},
"id": 1
}
Blockchain Info Response
{
"jsonrpc": "2.0",
"result": {
"chain": "main",
"blocks": 800000,
"headers": 800000,
"bestblockhash": "0000000000000000000...",
"difficulty": 123456789.123456789,
"mediantime": 1640995200,
"verificationprogress": 0.999999,
"initialblockdownload": false,
"chainwork": "0000000000000000000...",
"size_on_disk": 123456789,
"pruned": false,
"pruneheight": 0,
"automatic_pruning": false,
"prune_target_size": 0,
"softforks": {...},
"bip9_softforks": {...},
"warnings": ""
},
"id": 1
}
Address Validation Response
{
"jsonrpc": "2.0",
"result": {
"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
"is_valid": true,
"is_script": false,
"is_witness": false,
"witness_version": null,
"witness_program": null,
"script_type": "pubkeyhash",
"address_type": "P2PKH"
},
"id": 2
}
Market Price Response
{
"jsonrpc": "2.0",
"result": {
"currency": "usd",
"price": 45000.50,
"change_24h": 2.5,
"change_percentage_24h": 5.88,
"market_cap": 850000000000,
"volume_24h": 25000000000,
"last_updated": "2024-01-01T12:00:00Z"
},
"id": 3
}
🛠️ Developer Guide
Adding New Methods
Create the method implementation in the appropriate tool module:
async def new_method(self, param1: str, param2: int) -> Dict[str, Any]:
"""Method description."""
# Validate inputs
if not param1:
raise ValidationError("Missing parameter", "param1")
# Implementation
result = await self.client.some_rpc_call(param1, param2)
return result
Add method routing in src/main.py:
elif method == "new_method":
param1 = params.get("param1")
param2 = params.get("param2", default_value)
if not param1:
raise ValidationError("Missing 'param1' parameter", "param1")
return await tool_instance.new_method(param1, param2)
Add method to available methods list in get_server_status():
"available_methods": [
# ... existing methods
"new_method",
]
Architecture Patterns
Error Handling Strategy
# Always use custom exceptions for consistent error responses
try:
result = await some_operation()
return result
except SomeSpecificError as e:
raise ValidationError(f"Operation failed: {str(e)}", "field_name")
except Exception as e:
raise BitcoinRPCError(f"Unexpected error: {str(e)}")
Async Context Managers
# Use context managers for resource cleanup
class NewTool:
async def __aenter__(self):
self.client = httpx.AsyncClient()
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
if hasattr(self, 'client'):
await self.client.aclose()
Configuration Management
# Add new config parameters in config.py
class Config(BaseSettings):
NEW_API_KEY: Optional[str] = None
NEW_API_URL: str = "https://api.example.com"
@validator('NEW_API_URL')
def validate_new_api_url(cls, v):
if not v.startswith('http'):
raise ValueError('API URL must start with http')
return v
Testing Strategy
Manual Testing with curl
# Test blockchain info
curl -X POST http://localhost:8000/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "get_blockchain_info",
"params": {},
"id": 1
}'
# Test address validation
curl -X POST http://localhost:8000/ \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "validate_address",
"params": {"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"},
"id": 2
}'
# Test rate limiting
for i in {1..65}; do
curl -X POST http://localhost:8000/ \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "get_blockchain_info", "params": {}, "id": '$i'}'
done
Unit Testing Framework
# Example test structure (add to tests/ directory)
import pytest
import httpx
from src.main import app
@pytest.fixture
async def client():
async with httpx.AsyncClient(app=app, base_url="http://test") as client:
yield client
async def test_get_blockchain_info(client):
response = await client.post("/", json={
"jsonrpc": "2.0",
"method": "get_blockchain_info",
"params": {},
"id": 1
})
assert response.status_code == 200
data = response.json()
assert data["jsonrpc"] == "2.0"
assert "result" in data
async def test_invalid_method(client):
response = await client.post("/", json={
"jsonrpc": "2.0",
"method": "invalid_method",
"params": {},
"id": 1
})
assert response.status_code == 200
data = response.json()
assert data["error"]["code"] == -32601
Performance Considerations
Rate Limiting Implementation
- Default: 60 requests per minute per client
- Configurable via RATE_LIMIT_PER_MINUTE environment variable
- Implemented in BitcoinRPCClient._check_rate_limit()
Connection Pooling
- HTTP clients use connection pooling automatically
- Bitcoin RPC client reuses connections within context
- External API clients have separate connection pools
Caching Strategy
# Add caching for expensive operations (future enhancement)
from functools import lru_cache
from datetime import datetime, timedelta
class CachedTool:
def __init__(self):
self.cache = {}
self.cache_ttl = timedelta(minutes=5)
async def cached_method(self, key: str):
now = datetime.utcnow()
if key in self.cache:
data, timestamp = self.cache[key]
if now - timestamp < self.cache_ttl:
return data
# Fetch new data
result = await self.expensive_operation(key)
self.cache[key] = (result, now)
return result
Security Best Practices
Environment Variables
# Never commit sensitive data
BITCOIN_RPC_PASSWORD=your_secure_password
API_KEYS=your_api_keys
# Use different credentials for different environments
BITCOIN_RPC_URL_MAINNET=https://mainnet.node.com/
BITCOIN_RPC_URL_TESTNET=https://testnet.node.com/
Input Sanitization
# Always validate and sanitize inputs
def sanitize_input(value: str, max_length: int = 100) -> str:
if not isinstance(value, str):
raise ValidationError("Input must be string")
# Remove potentially dangerous characters
sanitized = re.sub(r'[^\w\-.]', '', value)
if len(sanitized) > max_length:
raise ValidationError(f"Input too long, max {max_length} characters")
return sanitized
🚀 Quick Start
Development Setup
Clone and setup:
git clone <repository-url>
cd bitcoin-mcp-server
cp .env.example .env
Install dependencies:
pip install -r requirements.txt
Configure environment variables:
# Edit .env file with your Bitcoin RPC credentials
BITCOIN_RPC_URL=https://bitcoin.two.nono.casa/
BITCOIN_RPC_USER=user
BITCOIN_RPC_PASSWORD=pass
Run development server:
python -m uvicorn src.main:app --reload --host 0.0.0.0 --port 8000
Production Deployment with Docker
Build and run:
docker-compose up -d
Check health:
curl http://localhost:8000/health
View logs:
docker-compose logs -f bitcoin-mcp-server
Deploy to Render.com
Create render.yaml:
services:
- type: web
name: bitcoin-mcp-server
env: docker
dockerfilePath: ./Dockerfile
plan: starter
envVars:
- key: PORT
value: 10000
- key: BITCOIN_RPC_URL
value: https://bitcoin.two.nono.casa/
- key: BITCOIN_RPC_USER
value: user
- key: BITCOIN_RPC_PASSWORD
value: pass
healthCheckPath: /health
Deploy:
- Connect your GitHub repository to Render
- Set environment variables in Render dashboard
- Deploy automatically on git push
📊 API Examples
Get Current Bitcoin Price
{
"jsonrpc": "2.0",
"method": "get_current_price",
"params": {"currency": "usd"},
"id": 1
}
Analyze Bitcoin Address
{
"jsonrpc": "2.0",
"method": "analyze_address_activity",
"params": {"address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"},
"id": 2
}
Get Latest Blocks
{
"jsonrpc": "2.0",
"method": "get_latest_blocks",
"params": {"count": 5},
"id": 3
}
🔧 Configuration Options
Variable | Default | Description |
---|---|---|
HOST | 0.0.0.0 | Server bind address |
PORT | 8000 | Server port |
DEBUG | false | Enable debug logging |
BITCOIN_RPC_URL | https://bitcoin.two.nono.casa/ | Bitcoin RPC endpoint |
BITCOIN_RPC_USER | user | Bitcoin RPC username |
BITCOIN_RPC_PASSWORD | pass | Bitcoin RPC password |
BITCOIN_NETWORK | mainnet | Network type (mainnet/testnet/regtest) |
REQUEST_TIMEOUT | 30 | HTTP request timeout (seconds) |
MAX_RETRIES | 3 | Maximum retry attempts |
RATE_LIMIT_PER_MINUTE | 60 | Requests per minute limit |
MEMPOOL_SPACE_API_URL | https://mempool.space/api | Mempool.space API endpoint |
BLOCKCHAIR_API_KEY | null | Optional Blockchair API key |
📈 Monitoring and Logging
Health Check Endpoint
# Check server health
curl http://localhost:8000/health
# Response format
{
"status": "healthy",
"bitcoin_connected": true,
"timestamp": "2024-01-01T12:00:00.000Z"
}
Logging Configuration
# Logs are structured with timestamps and levels
2024-01-01 12:00:00,000 - bitcoin_mcp - INFO - Bitcoin MCP Server started on 0.0.0.0:8000
2024-01-01 12:00:01,000 - bitcoin_mcp - ERROR - Bitcoin RPC Error: Connection refused
2024-01-01 12:00:02,000 - bitcoin_mcp - DEBUG - Rate limit check passed for client
Docker Logs
# View real-time logs
docker-compose logs -f bitcoin-mcp-server
# View last 100 lines
docker-compose logs --tail=100 bitcoin-mcp-server
🔍 Troubleshooting Guide
Common Issues
Bitcoin RPC Connection Failed
# Check if Bitcoin node is accessible
curl --user user:pass --data-binary '{"jsonrpc":"1.0","id":"test","method":"getblockchaininfo","params":[]}' \
-H 'content-type: text/plain;' https://bitcoin.two.nono.casa/
# If this fails, check:
# 1. Network connectivity
# 2. RPC credentials
# 3. Node availability
Rate Limit Exceeded
{
"jsonrpc": "2.0",
"error": {
"code": -32004,
"message": "Rate limit of 60 requests per minute exceeded"
},
"id": 1
}
// Solution: Increase RATE_LIMIT_PER_MINUTE or implement client-side throttling
Address Validation Failed
# Common causes:
# 1. Invalid address format
# 2. Wrong network (mainnet address on testnet)
# 3. Unsupported address type
# Debug with validate_address method
{
"jsonrpc": "2.0",
"method": "validate_address",
"params": {"address": "your_address_here"},
"id": 1
}
Development Debugging
Enable Debug Mode
# In .env file
DEBUG=true
# Or via environment
DEBUG=true python -m uvicorn src.main:app --reload
Add Custom Logging
import logging
logger = logging.getLogger(__name__)
async def your_method(self):
logger.debug(f"Processing request with params: {params}")
try:
result = await some_operation()
logger.info(f"Operation successful: {result}")
return result
except Exception as e:
logger.error(f"Operation failed: {str(e)}", exc_info=True)
raise
Network Security
# Add IP whitelisting for production
ALLOWED_IPS = ["127.0.0.1", "10.0.0.0/8", "192.168.0.0/16"]
@app.middleware("http")
async def ip_whitelist_middleware(request: Request, call_next):
client_ip = request.client.host
if client_ip not in ALLOWED_IPS:
return JSONResponse(
status_code=403,
content={"error": "Access denied"}
)
return await call_next(request)
Input Validation Security
# Implement comprehensive input validation
def validate_amount(amount: Union[str, int, float]) -> float:
try:
amount_float = float(amount)
if amount_float < 0:
raise ValidationError("Amount cannot be negative")
if amount_float > 21_000_000: # Max Bitcoin supply
raise ValidationError("Amount exceeds maximum Bitcoin supply")
return amount_float
except (ValueError, TypeError):
raise ValidationError("Invalid amount format")
# Sanitize string inputs
def sanitize_string(value: str, max_length: int = 255) -> str:
if not isinstance(value, str):
raise ValidationError("Value must be a string")
# Remove potentially dangerous characters
sanitized = re.sub(r'[<>"\']', '', value.strip())
if len(sanitized) > max_length:
raise ValidationError(f"String too long (max {max_length} chars)")
return sanitized
# Validate pagination parameters
def validate_pagination(offset: int = 0, limit: int = 25) -> tuple:
if offset < 0:
raise ValidationError("Offset cannot be negative")
if limit <= 0 or limit > 100:
raise ValidationError("Limit must be between 1 and 100")
return offset, limit
API Rate Limiting
# Advanced rate limiting with Redis (optional enhancement)
import redis
from datetime import datetime, timedelta
class AdvancedRateLimiter:
def __init__(self, redis_client):
self.redis = redis_client
async def check_rate_limit(self, client_id: str, limit: int = 60, window: int = 60):
key = f"rate_limit:{client_id}"
current_time = datetime.utcnow()
# Sliding window rate limiting
pipe = self.redis.pipeline()
pipe.zremrangebyscore(key, 0, current_time.timestamp() - window)
pipe.zcard(key)
pipe.zadd(key, {str(current_time.timestamp()): current_time.timestamp()})
pipe.expire(key, window)
results = pipe.execute()
request_count = results[1]
if request_count >= limit:
raise RateLimitError(f"Rate limit exceeded: {request_count}/{limit} requests")
🤝 Contributing
Development Workflow
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-method
- Follow coding standards:
- Use async/await patterns consistently
- Implement proper error handling
- Add input validation for all parameters
- Follow the established tool module structure
- Add tests for new functionality
- Update documentation for new methods
- Submit a pull request
Code Style Guidelines
- Python: Follow PEP 8 with async/await patterns
- Error Handling: Use custom exceptions from
src/utils/errors.py
- Validation: Implement input validation using
src/utils/validation.py
- Documentation: Add docstrings for all public methods
- Type Hints: Use type hints for all function parameters and return values
Testing Guidelines
- Unit Tests: Test individual methods and error conditions
- Integration Tests: Test JSON-RPC request/response cycles
- Error Testing: Test all error conditions and edge cases
- Rate Limiting: Test rate limiting behavior
- Validation: Test input validation for all parameters
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- Bitcoin Core for the RPC interface
- Mempool.space for address and transaction data
- CoinGecko for market price data
- Alternative.me for Fear & Greed Index
- FastAPI for the excellent web framework
- Pydantic for data validation and settings management
Tool definitions with schemas
TOOLS = { "get_blockchain_info": { "name": "get_blockchain_info", "description": "Get comprehensive blockchain information...", "inputSchema": { "type": "object", "properties": {}, "required": [] } }, "get_block_by_height": { "name": "get_block_by_height", "description": "Get block information by height with optional transaction details", "inputSchema": { "type": "object", "properties": { "height": { "type": "integer", "description": "Block height to retrieve" }, "include_transactions": { "type": "boolean", "description": "Whether to include full transaction data", "default": False } }, "required": ["height"] } } # ... 20 total tools defined }
Resource definitions
RESOURCES = { "bitcoin:blockchain:info": { "uri": "bitcoin:blockchain:info", "name": "Bitcoin Blockchain Information", "description": "Current blockchain status and statistics", "mimeType": "application/json" } # ... 3 resources defined }