@iriseller/mcp-server
Version:
Model Context Protocol (MCP) server providing access to IRISeller's AI sales intelligence platform with 7 AI agents, multi-CRM integration, advanced sales workflows, email automation, Rosa demo functionality with action scoring, DNC compliance checking, G
229 lines (228 loc) • 9.63 kB
JavaScript
import { WebSearchService } from './web-search-service.js';
export class StockDataService {
webSearchService;
constructor() {
this.webSearchService = new WebSearchService();
}
isAvailable() {
return this.webSearchService.isAvailable();
}
async getStockData(request) {
if (!this.webSearchService.isAvailable()) {
throw new Error('Web search service not available. Please check Claude API configuration.');
}
const { symbol, company_name, include_history = true, history_period = '3m' } = request;
try {
// Create financial data focused web search query
const searchQuery = company_name
? `${company_name} ${symbol} stock price current market data financial information trading volume market cap`
: `${symbol} stock price current market data financial information trading volume market cap`;
// Removed debug output to prevent JSON-RPC contamination
// Use the existing MCP web search service with financial domains
const webSearchRequest = {
arguments: {
query: searchQuery,
max_uses: 3,
allowed_domains: [
'finance.yahoo.com',
'bloomberg.com',
'marketwatch.com',
'reuters.com',
'investing.com',
'nasdaq.com',
'cnbc.com'
]
}
};
const searchResult = await this.webSearchService.search(webSearchRequest.arguments);
if (!searchResult || !searchResult.answer) {
throw new Error('Web search failed: No results returned');
}
// Parse the search results to extract stock data
const stockData = this.extractStockDataFromSearchResults(searchResult, symbol, company_name);
// Validate and format the response
const validatedData = {
success: true,
symbol: symbol,
company_name: stockData.company_name || company_name || symbol,
current_data: {
current_price: stockData.current_price || 0,
change: stockData.change || 0,
change_percent: stockData.change_percent || 0,
volume: stockData.volume || 0,
market_cap: stockData.market_cap || 'N/A',
pe_ratio: stockData.pe_ratio,
day_high: stockData.day_high || 0,
day_low: stockData.day_low || 0,
fifty_two_week_high: stockData.fifty_two_week_high || 0,
fifty_two_week_low: stockData.fifty_two_week_low || 0
},
timestamp: new Date().toISOString(),
source: 'claude_web_search_financial'
};
if (include_history && stockData.history && Array.isArray(stockData.history)) {
validatedData.history = stockData.history;
}
// Successfully extracted data - removed debug output
return validatedData;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
// Error logged internally - removed console output
return {
success: false,
symbol,
company_name: company_name || symbol,
current_data: {
current_price: 0,
change: 0,
change_percent: 0,
volume: 0,
market_cap: 'N/A',
day_high: 0,
day_low: 0,
fifty_two_week_high: 0,
fifty_two_week_low: 0
},
timestamp: new Date().toISOString(),
source: 'claude_web_search_financial',
error: errorMessage
};
}
}
extractStockDataFromSearchResults(searchResult, symbol, company_name) {
// Extract stock data from web search results
const searchText = searchResult.results?.map((r) => r.snippet || r.content || '').join(' ') ||
searchResult.answer ||
JSON.stringify(searchResult);
// Parsing search results - removed debug output
const data = {
symbol,
company_name: company_name || symbol,
current_price: 0,
change: 0,
change_percent: 0,
volume: 0,
market_cap: 'N/A',
day_high: 0,
day_low: 0,
fifty_two_week_high: 0,
fifty_two_week_low: 0
};
// Extract current price (pattern: $123.45 or 123.45)
const pricePattern = /(?:price|trading|quote)[\s:]*\$?(\d+\.?\d*)/i;
const priceMatch = searchText.match(pricePattern) || searchText.match(/\$(\d+\.?\d*)/);
if (priceMatch) {
data.current_price = parseFloat(priceMatch[1]);
}
// Extract change (pattern: +1.23 or -1.23)
const changePattern = /([\+\-]\$?\d+\.?\d*)/;
const changeMatch = searchText.match(changePattern);
if (changeMatch) {
data.change = parseFloat(changeMatch[1].replace(/[^\d.-]/g, ''));
}
// Extract percentage change (pattern: +1.23% or -1.23%)
const percentPattern = /([\+\-]?\d+\.?\d*)%/;
const percentMatch = searchText.match(percentPattern);
if (percentMatch) {
data.change_percent = parseFloat(percentMatch[1]);
}
// Extract volume (pattern: Volume: 1,234,567)
const volumePattern = /volume[\s:]*(\d+[\d,]*)/i;
const volumeMatch = searchText.match(volumePattern);
if (volumeMatch) {
data.volume = parseInt(volumeMatch[1].replace(/,/g, ''));
}
// Extract market cap (pattern: Market Cap: $1.2T)
const marketCapPattern = /market cap[\s:]*\$?(\d+\.?\d*[KMBT]?)/i;
const marketCapMatch = searchText.match(marketCapPattern);
if (marketCapMatch) {
data.market_cap = '$' + marketCapMatch[1];
}
// Extract day high/low (pattern: High: 125.00, Low: 122.00)
const highPattern = /high[\s:]*\$?(\d+\.?\d*)/i;
const lowPattern = /low[\s:]*\$?(\d+\.?\d*)/i;
const highMatch = searchText.match(highPattern);
const lowMatch = searchText.match(lowPattern);
if (highMatch)
data.day_high = parseFloat(highMatch[1]);
if (lowMatch)
data.day_low = parseFloat(lowMatch[1]);
// Extract 52-week high/low
const fiftyTwoWeekHighPattern = /52.week high[\s:]*\$?(\d+\.?\d*)/i;
const fiftyTwoWeekLowPattern = /52.week low[\s:]*\$?(\d+\.?\d*)/i;
const fiftyTwoHighMatch = searchText.match(fiftyTwoWeekHighPattern);
const fiftyTwoLowMatch = searchText.match(fiftyTwoWeekLowPattern);
if (fiftyTwoHighMatch)
data.fifty_two_week_high = parseFloat(fiftyTwoHighMatch[1]);
if (fiftyTwoLowMatch)
data.fifty_two_week_low = parseFloat(fiftyTwoLowMatch[1]);
// Extract P/E ratio
const pePattern = /p\/e ratio[\s:]*(\d+\.?\d*)/i;
const peMatch = searchText.match(pePattern);
if (peMatch) {
data.pe_ratio = parseFloat(peMatch[1]);
}
return data;
}
parseStockDataFromText(text, symbol, company_name) {
// Fallback text parsing for when JSON parsing fails
const data = {
symbol,
company_name: company_name || symbol,
current_data: {}
};
// Extract price patterns
const priceMatch = text.match(/\$?(\d+\.?\d*)/);
if (priceMatch) {
data.current_data.current_price = parseFloat(priceMatch[1]);
}
// Extract change patterns
const changeMatch = text.match(/[\+\-]?\$?(\d+\.?\d*)/);
if (changeMatch) {
data.current_data.change = parseFloat(changeMatch[1]);
}
// Extract percentage patterns
const percentMatch = text.match(/[\+\-]?(\d+\.?\d*)%/);
if (percentMatch) {
data.current_data.change_percent = parseFloat(percentMatch[1]);
}
return data;
}
parseFloat(value) {
if (typeof value === 'number')
return value;
if (typeof value === 'string') {
const parsed = parseFloat(value.replace(/[^\d.-]/g, ''));
return isNaN(parsed) ? undefined : parsed;
}
return undefined;
}
parseInt(value) {
if (typeof value === 'number')
return Math.floor(value);
if (typeof value === 'string') {
const parsed = parseInt(value.replace(/[^\d]/g, ''), 10);
return isNaN(parsed) ? undefined : parsed;
}
return undefined;
}
getConfiguration() {
return {
service: 'stock_data',
version: '1.0.0',
provider: 'claude_web_search',
available: this.isAvailable(),
supported_symbols: 'All major stock exchanges',
data_sources: [
'finance.yahoo.com',
'bloomberg.com',
'marketwatch.com',
'reuters.com',
'investing.com',
'nasdaq.com',
'cnbc.com'
]
};
}
}