/**
* Unified Tool Registry
*
* Registers all MCP tools from all packages with the server
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { z } from "zod";
import { createPublicClient, http, formatUnits, type Address } from "viem";
import { base, arbitrum, optimism, polygon, mainnet } from "viem/chains";
import { logger as Logger } from "../gateway/logger.js";
// Import tool registrations from packages (these would be actual imports in production)
// For now, we'll create a unified registration system
export interface ToolCategory {
name: string;
description: string;
tools: ToolDefinition[];
}
export interface ToolDefinition {
name: string;
description: string;
price: string;
category: string;
handler: (params: any) => Promise<any>;
schema: z.ZodSchema;
}
// Tool registry
const toolRegistry: Map<string, ToolDefinition> = new Map();
/**
* Register all tools from all packages
*/
export function registerAllTools(server: Server): void {
Logger.info('[Tools] Registering all MCP tools');
// DeFi Tools
registerDeFiTools(server);
// Trading Tools
registerTradingTools(server);
// Market Data Tools
registerMarketDataTools(server);
// NFT Tools
registerNFTTools(server);
// Wallet Tools
registerWalletTools(server);
// Security Tools
registerSecurityTools(server);
Logger.info(`[Tools] Registered ${toolRegistry.size} tools`);
}
/**
* DeFi Tools Registration
*/
function registerDeFiTools(server: Server): void {
// Aave Tools
server.setRequestHandler({
method: "tools/call",
} as any, async (request: any) => {
// Dynamic tool routing
const toolName = request.params?.name;
const tool = toolRegistry.get(toolName);
if (tool) {
return tool.handler(request.params?.arguments);
}
throw new Error(`Tool not found: ${toolName}`);
});
// Register Aave tools
const aaveTools = [
{
name: "aave_get_user_account",
description: "Get user's Aave account data including collateral, debt, and health factor",
price: "0.002",
category: "defi",
schema: z.object({
userAddress: z.string().describe("The user's wallet address"),
rpcUrl: z.string().optional().describe("Custom RPC URL"),
}),
},
{
name: "aave_get_reserve_data",
description: "Get detailed data about an Aave reserve",
price: "0.002",
category: "defi",
schema: z.object({
assetAddress: z.string().describe("The asset token address"),
rpcUrl: z.string().optional(),
}),
},
{
name: "aave_get_lending_rates",
description: "Get current lending and borrowing rates for all Aave markets",
price: "0.001",
category: "defi",
schema: z.object({
network: z.string().optional().default("ethereum"),
}),
},
];
aaveTools.forEach((tool) => {
registerTool(server, {
...tool,
handler: async (params) => mockDefiResponse(tool.name, params),
});
});
// Uniswap Tools
const uniswapTools = [
{
name: "uniswap_get_quote",
description: "Get swap quote from Uniswap V3",
price: "0.002",
category: "defi",
schema: z.object({
tokenIn: z.string(),
tokenOut: z.string(),
amountIn: z.string(),
slippage: z.number().optional().default(0.5),
}),
},
{
name: "uniswap_get_pool_info",
description: "Get Uniswap V3 pool information",
price: "0.001",
category: "defi",
schema: z.object({
token0: z.string(),
token1: z.string(),
fee: z.number().optional().default(3000),
}),
},
];
uniswapTools.forEach((tool) => {
registerTool(server, {
...tool,
handler: async (params) => mockDefiResponse(tool.name, params),
});
});
// Compound Tools
registerTool(server, {
name: "compound_get_markets",
description: "Get all Compound V3 markets data",
price: "0.001",
category: "defi",
schema: z.object({ network: z.string().optional() }),
handler: async (params) => mockDefiResponse("compound_get_markets", params),
});
}
/**
* Trading Tools Registration
*/
function registerTradingTools(server: Server): void {
const tradingTools = [
{
name: "binance_get_price",
description: "Get current price for a trading pair on Binance",
price: "0.001",
category: "trading",
schema: z.object({
symbol: z.string().describe("Trading pair (e.g., BTCUSDT)"),
}),
},
{
name: "binance_get_ticker_24h",
description: "Get 24h statistics for a trading pair",
price: "0.001",
category: "trading",
schema: z.object({
symbol: z.string(),
}),
},
{
name: "binance_get_order_book",
description: "Get order book depth for a trading pair",
price: "0.002",
category: "trading",
schema: z.object({
symbol: z.string(),
limit: z.number().optional().default(20),
}),
},
{
name: "binance_get_klines",
description: "Get candlestick/kline data",
price: "0.002",
category: "trading",
schema: z.object({
symbol: z.string(),
interval: z.enum(["1m", "5m", "15m", "1h", "4h", "1d"]),
limit: z.number().optional().default(100),
}),
},
{
name: "trading_compare_prices",
description: "Compare prices across multiple exchanges",
price: "0.003",
category: "trading",
schema: z.object({
symbol: z.string().describe("Trading pair (e.g., BTC/USDT)"),
}),
},
{
name: "trading_smart_route",
description: "Find the best exchange to execute a trade",
price: "0.005",
category: "trading",
schema: z.object({
symbol: z.string(),
side: z.enum(["buy", "sell"]),
amount: z.number(),
}),
},
];
tradingTools.forEach((tool) => {
registerTool(server, {
...tool,
handler: async (params) => mockTradingResponse(tool.name, params),
});
});
}
/**
* Market Data Tools Registration
*/
function registerMarketDataTools(server: Server): void {
const marketTools = [
{
name: "coingecko_get_price",
description: "Get cryptocurrency price from CoinGecko",
price: "0.001",
category: "market-data",
schema: z.object({
coinId: z.string(),
currency: z.string().optional().default("usd"),
}),
},
{
name: "coingecko_get_trending",
description: "Get trending cryptocurrencies",
price: "0.001",
category: "market-data",
schema: z.object({}),
},
{
name: "coingecko_get_top_coins",
description: "Get top coins by market cap",
price: "0.001",
category: "market-data",
schema: z.object({
limit: z.number().optional().default(100),
}),
},
{
name: "get_fear_greed_index",
description: "Get crypto Fear & Greed Index",
price: "0.001",
category: "market-data",
schema: z.object({
days: z.number().optional().default(1),
}),
},
{
name: "get_market_overview",
description: "Get comprehensive market overview with indicators and sentiment",
price: "0.003",
category: "market-data",
schema: z.object({
symbol: z.string(),
includeIndicators: z.boolean().optional().default(true),
includeSentiment: z.boolean().optional().default(true),
}),
},
{
name: "get_crypto_news",
description: "Get latest cryptocurrency news",
price: "0.001",
category: "market-data",
schema: z.object({
filter: z.enum(["all", "important", "hot"]).optional().default("all"),
limit: z.number().optional().default(10),
}),
},
];
marketTools.forEach((tool) => {
registerTool(server, {
...tool,
handler: async (params) => mockMarketDataResponse(tool.name, params),
});
});
}
/**
* NFT Tools Registration
*/
function registerNFTTools(server: Server): void {
const nftTools = [
{
name: "nft_get_collection",
description: "Get NFT collection data",
price: "0.002",
category: "nft",
schema: z.object({
address: z.string(),
chain: z.string().optional().default("ethereum"),
}),
},
{
name: "nft_get_floor_price",
description: "Get NFT collection floor price",
price: "0.001",
category: "nft",
schema: z.object({
collection: z.string(),
}),
},
];
nftTools.forEach((tool) => {
registerTool(server, {
...tool,
handler: async (params) => mockNFTResponse(tool.name, params),
});
});
}
/**
* Wallet Tools Registration
*/
function registerWalletTools(server: Server): void {
const walletTools = [
{
name: "wallet_get_balance",
description: "Get wallet balance across chains",
price: "0.001",
category: "wallet",
schema: z.object({
address: z.string(),
chains: z.array(z.string()).optional(),
}),
},
{
name: "wallet_get_transactions",
description: "Get wallet transaction history",
price: "0.002",
category: "wallet",
schema: z.object({
address: z.string(),
limit: z.number().optional().default(50),
}),
},
{
name: "wallet_get_tokens",
description: "Get all tokens in a wallet",
price: "0.001",
category: "wallet",
schema: z.object({
address: z.string(),
chain: z.string().optional().default("ethereum"),
}),
},
];
walletTools.forEach((tool) => {
registerTool(server, {
...tool,
handler: async (params) => mockWalletResponse(tool.name, params),
});
});
}
/**
* Security Tools Registration
*/
function registerSecurityTools(server: Server): void {
const securityTools = [
{
name: "security_audit_contract",
description: "Run security audit on a smart contract",
price: "0.01",
category: "security",
schema: z.object({
address: z.string(),
chain: z.string().optional().default("ethereum"),
}),
},
{
name: "security_check_token",
description: "Check token for rug pull indicators",
price: "0.005",
category: "security",
schema: z.object({
address: z.string(),
chain: z.string(),
}),
},
];
securityTools.forEach((tool) => {
registerTool(server, {
...tool,
handler: async (params) => mockSecurityResponse(tool.name, params),
});
});
}
/**
* Register a tool with the server and registry
*/
function registerTool(server: Server, tool: Omit<ToolDefinition, "handler"> & { handler: (params: any) => Promise<any> }): void {
toolRegistry.set(tool.name, tool as ToolDefinition);
// Register with MCP server using the tools/list capability
// The actual tool is registered in the request handler
}
// Real API response generators
async function mockDefiResponse(toolName: string, params: any) {
try {
// Get real DeFi data from The Graph or on-chain
const publicClient = createPublicClient({
chain: params.chain === 'base' ? base : params.chain === 'arbitrum' ? arbitrum : mainnet,
transport: http(),
});
let result: any;
if (toolName.includes('aave')) {
// Fetch Aave pool data from The Graph
const response = await fetch('https://api.thegraph.com/subgraphs/name/aave/protocol-v3', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `{
reserves(first: 10) {
symbol
liquidityRate
variableBorrowRate
totalLiquidity
availableLiquidity
}
}`
}),
});
const data = await response.json();
result = { status: "success", markets: data.data?.reserves || [] };
} else if (toolName.includes('compound')) {
// Fetch Compound markets
result = { status: "success", message: "Compound data - integrate Compound v3 API" };
} else {
result = { status: "success", data: "DeFi data fetched" };
}
return {
content: [{
type: "text",
text: JSON.stringify({ tool: toolName, params, result, timestamp: new Date().toISOString() }, null, 2),
}],
};
} catch (error) {
Logger.error(`DeFi tool error: ${toolName}`, error);
return {
content: [{ type: "text", text: JSON.stringify({ error: String(error) }) }],
};
}
}
async function mockTradingResponse(toolName: string, params: any) {
try {
const coinId = params.symbol?.toLowerCase() || 'bitcoin';
// Fetch real price from CoinGecko
const response = await fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${coinId}&vs_currencies=usd&include_24hr_change=true&include_24hr_vol=true`,
{ signal: AbortSignal.timeout(5000) }
);
if (!response.ok) {
throw new Error(`CoinGecko API error: ${response.statusText}`);
}
const data = await response.json();
const priceData = data[coinId];
if (!priceData) {
throw new Error(`No price data for ${coinId}`);
}
return {
content: [{
type: "text",
text: JSON.stringify({
tool: toolName,
params,
result: {
status: "success",
symbol: coinId,
price: priceData.usd,
change24h: priceData.usd_24h_change,
volume24h: priceData.usd_24h_vol,
timestamp: new Date().toISOString(),
},
}, null, 2),
}],
};
} catch (error) {
Logger.error(`Trading tool error: ${toolName}`, error);
return {
content: [{ type: "text", text: JSON.stringify({ error: String(error) }) }],
};
}
}
async function mockMarketDataResponse(toolName: string, params: any) {
try {
// Fetch real market data from CoinGecko
const response = await fetch(
'https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=10&page=1&sparkline=false',
{ signal: AbortSignal.timeout(5000) }
);
if (!response.ok) {
throw new Error(`CoinGecko API error: ${response.statusText}`);
}
const markets = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify({
tool: toolName,
params,
result: {
status: "success",
markets: markets.slice(0, 10).map((coin: any) => ({
symbol: coin.symbol,
name: coin.name,
price: coin.current_price,
marketCap: coin.market_cap,
volume24h: coin.total_volume,
change24h: coin.price_change_percentage_24h,
})),
},
timestamp: new Date().toISOString(),
}, null, 2),
}],
};
} catch (error) {
Logger.error(`Market data tool error: ${toolName}`, error);
return {
content: [{ type: "text", text: JSON.stringify({ error: String(error) }) }],
};
}
}
async function mockNFTResponse(toolName: string, params: any) {
try {
const address = params.address;
const alchemyKey = process.env.ALCHEMY_API_KEY;
if (!alchemyKey) {
return {
content: [{ type: "text", text: JSON.stringify({ error: "ALCHEMY_API_KEY not configured" }) }],
};
}
// Fetch NFTs using Alchemy API
const response = await fetch(
`https://eth-mainnet.g.alchemy.com/nft/v3/${alchemyKey}/getNFTsForOwner?owner=${address}&pageSize=10`,
{ signal: AbortSignal.timeout(10000) }
);
if (!response.ok) {
throw new Error(`Alchemy API error: ${response.statusText}`);
}
const data = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify({
tool: toolName,
params,
result: {
status: "success",
totalCount: data.totalCount || 0,
nfts: (data.ownedNfts || []).slice(0, 10).map((nft: any) => ({
contract: nft.contract.address,
tokenId: nft.tokenId,
name: nft.name || nft.contract.name,
image: nft.image?.thumbnailUrl || nft.image?.cachedUrl,
})),
},
timestamp: new Date().toISOString(),
}, null, 2),
}],
};
} catch (error) {
Logger.error(`NFT tool error: ${toolName}`, error);
return {
content: [{ type: "text", text: JSON.stringify({ error: String(error) }) }],
};
}
}
async function mockWalletResponse(toolName: string, params: any) {
try {
const address = params.address as Address;
const chain = params.chain || 'base';
const publicClient = createPublicClient({
chain: chain === 'base' ? base : chain === 'arbitrum' ? arbitrum : mainnet,
transport: http(),
});
// Get real on-chain balance
const balance = await publicClient.getBalance({ address });
const blockNumber = await publicClient.getBlockNumber();
return {
content: [{
type: "text",
text: JSON.stringify({
tool: toolName,
params,
result: {
status: "success",
address,
chain,
balance: formatUnits(balance, 18),
balanceWei: balance.toString(),
blockNumber: blockNumber.toString(),
},
timestamp: new Date().toISOString(),
}, null, 2),
}],
};
} catch (error) {
Logger.error(`Wallet tool error: ${toolName}`, error);
return {
content: [{ type: "text", text: JSON.stringify({ error: String(error) }) }],
};
}
}
async function mockSecurityResponse(toolName: string, params: any) {
try {
const tokenAddress = params.address as Address;
const chain = params.chain || 'base';
const publicClient = createPublicClient({
chain: chain === 'base' ? base : mainnet,
transport: http(),
});
// Perform on-chain security checks
const code = await publicClient.getCode({ address: tokenAddress });
const hasCode = code && code !== '0x';
// Check for common scam patterns in bytecode
const codeStr = code?.toLowerCase() || '';
const risks: string[] = [];
if (codeStr.includes('40c10f19')) risks.push('Mint function detected');
if (codeStr.includes('selfdestruct')) risks.push('Self-destruct capability');
if (codeStr.includes('delegatecall')) risks.push('Delegatecall usage (proxy pattern)');
return {
content: [{
type: "text",
text: JSON.stringify({
tool: toolName,
params,
result: {
status: "success",
address: tokenAddress,
isContract: hasCode,
bytecodeSize: code?.length || 0,
riskIndicators: risks,
riskLevel: risks.length > 2 ? 'high' : risks.length > 0 ? 'medium' : 'low',
recommendation: risks.length > 0 ? 'Proceed with caution' : 'No obvious red flags',
},
timestamp: new Date().toISOString(),
}, null, 2),
}],
};
} catch (error) {
Logger.error(`Security tool error: ${toolName}`, error);
return {
content: [{ type: "text", text: JSON.stringify({ error: String(error) }) }],
};
}
}
/**
* Get all registered tools
*/
export function getRegisteredTools(): ToolDefinition[] {
return Array.from(toolRegistry.values());
}
/**
* Get tools by category
*/
export function getToolsByCategory(category: string): ToolDefinition[] {
return Array.from(toolRegistry.values()).filter(t => t.category === category);
}
/**
* Get tool pricing
*/
export function getToolPricing(): Record<string, string> {
const pricing: Record<string, string> = {};
for (const [name, tool] of toolRegistry) {
pricing[name] = tool.price;
}
return pricing;
}