What Are MCP Servers?
Model Context Protocol (MCP) servers are lightweight HTTP endpoints that extend Claude's tool-using capabilities. By implementing the MCP spec, these servers allow Claude models (like Opus, Sonnet, or Haiku) to fetch real-time data from external sources—such as blockchain APIs or databases—without embedding credentials in prompts. This is ideal for agentic applications where Claude needs dynamic context.
Unlike standard API calls, MCP servers standardize responses in a Claude-friendly JSON format, enabling seamless tool chaining. They're perfect for developers building with the Claude API, Claude Code CLI, or integrations like n8n.
Key Benefits:
- Secure: Keep API keys server-side.
- Scalable: Deploy anywhere (Vercel, Fly.io).
- Claude-Native: Responses parse directly into Claude's context.
Prerequisites
Before diving in:
- Node.js 18+ installed.
- Familiarity with Claude's tool use documentation.
- Accounts for:
- Alchemy or Infura (Web3 RPC).
- Supabase or PostgreSQL (database).
- Claude API key from Anthropic Console.
- Optional: Claude Code CLI (
npm i -g @anthropic-ai/claude-code).
Step 1: Building a Web3 MCP Server
We'll create an Ethereum MCP server for querying wallet balances and transaction history using ethers.js.
Project Setup
mkdir claude-web3-mcp
cd claude-web3-mcp
npm init -y
npm i express ethers cors helmet dotenv
Create .env:
ALCHEMY_KEY=your_alchemy_key
PORT=3000
Server Code (server.js)
const express = require('express');
const { ethers } = require('ethers');
const cors = require('cors');
const helmet = require('helmet');
require('dotenv').config();
const app = express();
app.use(helmet());
app.use(cors());
app.use(express.json());
const provider = new ethers.JsonRpcProvider(`https://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`);
// MCP Endpoint: /mcp/:tool
app.post('/mcp/getBalance', async (req, res) => {
try {
const { address } = req.body;
if (!ethers.isAddress(address)) {
return res.status(400).json({ error: 'Invalid address' });
}
const balance = await provider.getBalance(address);
res.json({
success: true,
data: {
balance: ethers.formatEther(balance),
address
}
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.post('/mcp/getTxHistory', async (req, res) => {
// Similar implementation for recent transactions
// Use provider.getHistory(address)
});
app.listen(process.env.PORT, () => {
console.log(`Web3 MCP Server running on port ${process.env.PORT}`);
});
Run locally: node server.js.
Test with curl:
curl -X POST http://localhost:3000/mcp/getBalance \
-H "Content-Type: application/json" \
-d '{"address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"}'
Step 2: Building a Database MCP Server
Next, a Supabase MCP server for querying user data.
Setup
mkdir claude-db-mcp
cd claude-db-mcp
npm init -y
npm i express @supabase/supabase-js cors helmet dotenv
.env:
SUPABASE_URL=your_supabase_url
SUPABASE_KEY=your_service_role_key
PORT=3001
Server Code (db-server.js)
const express = require('express');
const { createClient } = require('@supabase/supabase-js');
const cors = require('cors');
const helmet = require('helmet');
require('dotenv').config();
const app = express();
app.use(helmet());
app.use(cors());
app.use(express.json());
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY);
app.post('/mcp/queryUsers', async (req, res) => {
try {
const { email } = req.body;
const { data, error } = await supabase
.from('users')
.select('*')
.eq('email', email)
.limit(1);
if (error) throw error;
res.json({
success: true,
data: data[0] || null
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(process.env.PORT, () => {
console.log(`DB MCP Server on port ${process.env.PORT}`);
});
Test similarly with curl.
Step 3: Defining Tools in Claude Prompts
Claude uses XML for tools. Here's how to define your MCP tools:
<tool_use>
<name>get_wallet_balance</name>
<input>
<address>0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045</address>
</input>
</tool_use>
Tool Schema (in system prompt):
You have access to these MCP tools:
- get_wallet_balance(address: string) -> POST to http://your-mcp-server/mcp/getBalance
Returns: {balance: string, address: string}
- query_user(email: string) -> POST to http://db-mcp-server/mcp/queryUsers
Returns: user object or null
Step 4: Integrating with Claude API
Use the Claude SDK to call tools:
const Anthropic = require('@anthropic-ai/sdk');
const anthropic = new Anthropic({ apiKey: 'your-claude-key' });
async function agentQuery() {
const msg = await anthropic.messages.create({
model: 'claude-3-5-sonnet-20240620',
max_tokens: 1024,
tools: [
{
name: 'get_wallet_balance',
description: 'Get ETH balance for an address',
input_schema: {
type: 'object',
properties: { address: { type: 'string' } }
}
},
// Add DB tool
],
messages: [{ role: 'user', content: 'Check balance of vitalik.eth and find user claude@anthropic.com' }]
});
// Handle tool_use blocks, call MCP servers, feed back
console.log(msg.content);
}
Claude will output tool calls; execute them against your MCP endpoints and append results.
Step 5: Using with Claude Code CLI
In Claude Code:
claude-code init- Add MCP URLs to your project config.
- Prompt: "Build an agent that checks Web3 balance then queries DB for KYC."
Claude Code auto-detects and uses local/remote MCP servers.
Step 6: Deployment
Vercel (serverless):
vercel.json:
{
"functions": {
"api/mcp/**/*.js": { "runtime": "nodejs18.x" }
}
}
vercel deploy → Get HTTPS URL for Claude tools.
Fly.io (persistent): fly launch for low-latency DB/Web3.
Secure with API keys in headers for production.
Building an Agentic App: Example
Scenario: DeFi KYC Agent.
- User inputs wallet address.
- Claude calls Web3 MCP → gets balance.
- Queries DB MCP → checks user status.
- Approves loan if balance > 1 ETH and verified.
Full Agent Loop (Node.js):
// Pseudo-code for tool execution loop
async function runAgent(messages) {
let finalMsg;
while (true) {
const response = await anthropic.messages.create({ /* ... */ });
if (!response.content.some(c => c.type === 'tool_use')) break;
for (const tool of response.content.filter(c => c.type === 'tool_use')) {
const result = await callMcp(tool); // POST to MCP
messages.push({ role: 'user', content: [{ type: 'tool_result', tool_use_id: tool.id, content: result }] });
}
}
return response;
}
Deploy as n8n workflow or Slack bot.
Best Practices & Troubleshooting
- Rate Limiting: Add Redis for caching.
- Security: Validate inputs, use HTTPS.
- Errors: Always return {success: false, error: msg}.
- Advanced: Chain MCPs (Web3 → DB lookups).
- Common Issues:
- CORS: Enable in Express.
- Claude Parsing: Stick to flat JSON.
Word Count: ~1450. Experiment with Opus for complex agents.
Next Steps
Fork repos on GitHub, integrate with Zapier for no-code. Share your MCP servers in Claude Directory forums!
Stay ahead of the AI curve
The most important updates, news, and content — delivered in one weekly newsletter.