@noves/noves-mcp-server
Version:
MCP server that wraps Noves Intent service for natural language blockchain data
248 lines (236 loc) • 11.7 kB
JavaScript
import { GetCurrentTokenPriceSchema, GetHistoricalTokenPriceSchema, GetTokenPriceComparisonSchema, } from '../schemas/index.js';
/**
* Token price tool handlers
* Uses the getTokenPrice method available on IntentProvider
*/
export class TokenHandlers {
constructor(provider) {
this.provider = provider;
// Debug: Log provider initialization
console.error('🔧 TokenHandlers initialized');
console.error('🔧 Provider instance:', !!this.provider);
console.error('🔧 Provider constructor:', this.provider.constructor.name);
console.error('🔧 Available methods:', Object.getOwnPropertyNames(Object.getPrototypeOf(this.provider)));
// Check for the getTokenPrice method
console.error('🔧 Has getTokenPrice method:', typeof this.provider.getTokenPrice);
if (typeof this.provider.getTokenPrice === 'function') {
console.error('✅ getTokenPrice method exists on provider');
}
else {
console.error('❌ getTokenPrice method NOT found on provider');
console.error('🔧 Available methods:', Object.getOwnPropertyNames(this.provider));
console.error('🔧 Available prototype methods:', Object.getOwnPropertyNames(Object.getPrototypeOf(this.provider)));
}
}
/**
* Handle getting current token price using getTokenPrice
*/
async handleGetCurrentTokenPrice(args) {
console.error('🚀 handleGetCurrentTokenPrice called with args:', JSON.stringify(args, null, 2));
try {
const { chain, tokenAddress } = GetCurrentTokenPriceSchema.parse(args);
console.error('✅ Schema validation passed:', { chain, tokenAddress });
// Debug: Log the exact parameters being passed
const params = {
chain: chain,
token_address: tokenAddress
};
console.error('🔧 Calling getTokenPrice with params:', JSON.stringify(params, null, 2));
// Check if method exists before calling
if (typeof this.provider.getTokenPrice !== 'function') {
throw new Error('getTokenPrice method not available on provider instance');
}
// Get current token price using the provider
console.error('🔄 Making getTokenPrice call...');
const priceData = await this.provider.getTokenPrice(params);
console.error('✅ getTokenPrice response:', JSON.stringify(priceData, null, 2));
const response = `**Current Token Price**
**Token Address:** ${tokenAddress}
**Chain:** ${chain}
**Current Price:** ${priceData.price.amount} ${priceData.price.currency}
${priceData.token?.symbol ? `**Symbol:** ${priceData.token.symbol}` : ''}
${priceData.token?.name ? `**Name:** ${priceData.token.name}` : ''}
**Price Information:**
- Amount: ${priceData.price.amount}
- Currency: ${priceData.price.currency}
- Status: ${priceData.price.status}
- Block: ${priceData.block}
- Retrieved at: ${new Date().toISOString()}
**Pricing Details:**
- Priced by: ${priceData.pricedBy?.exchange?.name || 'Unknown'} (${priceData.pricedBy?.poolAddress || 'N/A'})
- Liquidity: ${priceData.pricedBy?.liquidity ? `$${priceData.pricedBy.liquidity.toLocaleString()}` : 'N/A'}
- Price Type: ${priceData.priceType}`;
console.error('✅ Returning successful response');
return {
content: [
{
type: 'text',
text: response,
},
],
};
}
catch (error) {
console.error('❌ Error in handleGetCurrentTokenPrice:', error);
console.error('❌ Error stack:', error instanceof Error ? error.stack : 'No stack trace');
return {
content: [
{
type: 'text',
text: `Error fetching current token price: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
},
],
};
}
}
/**
* Handle getting historical token price using getTokenPrice
*/
async handleGetHistoricalTokenPrice(args) {
console.error('🚀 handleGetHistoricalTokenPrice called with args:', JSON.stringify(args, null, 2));
try {
const { chain, tokenAddress, timestamp } = GetHistoricalTokenPriceSchema.parse(args);
console.error('✅ Schema validation passed:', { chain, tokenAddress, timestamp });
// Debug: Log the exact parameters being passed
const params = {
chain: chain,
token_address: tokenAddress,
timestamp: timestamp
};
console.error('🔧 Calling getTokenPrice with params:', JSON.stringify(params, null, 2));
// Check if method exists before calling
if (typeof this.provider.getTokenPrice !== 'function') {
throw new Error('getTokenPrice method not available on provider instance');
}
// Get historical token price using the provider
console.error('🔄 Making getTokenPrice call...');
const priceData = await this.provider.getTokenPrice(params);
console.error('✅ getTokenPrice response:', JSON.stringify(priceData, null, 2));
const timestampDate = new Date(parseInt(timestamp) * 1000);
const response = `**Historical Token Price**
**Token Address:** ${tokenAddress}
**Chain:** ${chain}
**Historical Price:** ${priceData.price.amount} ${priceData.price.currency}
**Date:** ${timestampDate.toISOString()}
${priceData.token?.symbol ? `**Symbol:** ${priceData.token.symbol}` : ''}
${priceData.token?.name ? `**Name:** ${priceData.token.name}` : ''}
**Price Information:**
- Amount: ${priceData.price.amount}
- Currency: ${priceData.price.currency}
- Status: ${priceData.price.status}
- Block: ${priceData.block}
- Timestamp: ${timestamp} (Unix)
- Date: ${timestampDate.toLocaleDateString()} ${timestampDate.toLocaleTimeString()}
**Pricing Details:**
- Priced by: ${priceData.pricedBy?.exchange?.name || 'Unknown'} (${priceData.pricedBy?.poolAddress || 'N/A'})
- Liquidity: ${priceData.pricedBy?.liquidity ? `$${priceData.pricedBy.liquidity.toLocaleString()}` : 'N/A'}
- Price Type: ${priceData.priceType}`;
console.error('✅ Returning successful response');
return {
content: [
{
type: 'text',
text: response,
},
],
};
}
catch (error) {
console.error('❌ Error in handleGetHistoricalTokenPrice:', error);
console.error('❌ Error stack:', error instanceof Error ? error.stack : 'No stack trace');
return {
content: [
{
type: 'text',
text: `Error fetching historical token price: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
},
],
};
}
}
/**
* Handle token price comparison between two timestamps
*/
async handleGetTokenPriceComparison(args) {
console.error('🚀 handleGetTokenPriceComparison called with args:', JSON.stringify(args, null, 2));
try {
const { chain, tokenAddress, fromTimestamp, toTimestamp } = GetTokenPriceComparisonSchema.parse(args);
console.error('✅ Schema validation passed:', { chain, tokenAddress, fromTimestamp, toTimestamp });
// Use current timestamp if toTimestamp is not provided
const endTimestamp = toTimestamp || Math.floor(Date.now() / 1000).toString();
// Check if method exists before calling
if (typeof this.provider.getTokenPrice !== 'function') {
throw new Error('getTokenPrice method not available on provider instance');
}
console.error('🔄 Making parallel getTokenPrice calls...');
// Get both prices
const [fromPriceData, toPriceData] = await Promise.all([
this.provider.getTokenPrice({
chain: chain,
token_address: tokenAddress,
timestamp: fromTimestamp
}),
endTimestamp === Math.floor(Date.now() / 1000).toString()
? this.provider.getTokenPrice({
chain: chain,
token_address: tokenAddress
})
: this.provider.getTokenPrice({
chain: chain,
token_address: tokenAddress,
timestamp: endTimestamp
})
]);
console.error('✅ Both getTokenPrice calls completed');
console.error('🔧 From price data:', JSON.stringify(fromPriceData, null, 2));
console.error('🔧 To price data:', JSON.stringify(toPriceData, null, 2));
const fromPrice = parseFloat(fromPriceData.price.amount);
const toPrice = parseFloat(toPriceData.price.amount);
const priceChange = toPrice - fromPrice;
const percentageChange = fromPrice !== 0 ? ((priceChange / fromPrice) * 100) : 0;
const fromDate = new Date(parseInt(fromTimestamp) * 1000);
const toDate = new Date(parseInt(endTimestamp) * 1000);
const changeDirection = priceChange >= 0 ? '📈' : '📉';
const changeColor = priceChange >= 0 ? 'increased' : 'decreased';
const response = `**Token Price Comparison**
**Token Address:** ${tokenAddress}
**Chain:** ${chain}
${fromPriceData.token?.symbol ? `**Symbol:** ${fromPriceData.token.symbol}` : ''}
${fromPriceData.token?.name ? `**Name:** ${fromPriceData.token.name}` : ''}
**Price Comparison:**
- **From:** ${fromDate.toLocaleDateString()} - ${fromPriceData.price.amount} ${fromPriceData.price.currency}
- **To:** ${toDate.toLocaleDateString()} - ${toPriceData.price.amount} ${toPriceData.price.currency}
**Price Movement:** ${changeDirection}
- **Change:** ${priceChange > 0 ? '+' : ''}${priceChange.toFixed(6)} ${fromPriceData.price.currency}
- **Percentage:** ${percentageChange > 0 ? '+' : ''}${percentageChange.toFixed(2)}%
- **Direction:** Price has ${changeColor} ${changeDirection}
**Analysis:**
The token price has ${changeColor} by ${Math.abs(percentageChange).toFixed(2)}% over the selected period.
**Pricing Details:**
- From Block: ${fromPriceData.block}
- To Block: ${toPriceData.block}
- Price Type: ${fromPriceData.priceType}`;
console.error('✅ Returning successful response');
return {
content: [
{
type: 'text',
text: response,
},
],
};
}
catch (error) {
console.error('❌ Error in handleGetTokenPriceComparison:', error);
console.error('❌ Error stack:', error instanceof Error ? error.stack : 'No stack trace');
return {
content: [
{
type: 'text',
text: `Error comparing token prices: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
},
],
};
}
}
}