stock-market-mcp-server
Version:
Financial Datasets MCP Server for Stock Market API
1,062 lines (1,061 loc) ⢠151 kB
JavaScript
/**
* Financial Datasets MCP Server
* A low-level server implementation using Model Context Protocol for Stock Market API
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourceTemplatesRequestSchema, CompleteRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import axios from 'axios';
const logFile = path.join(os.tmpdir(), 'financial-mcp.log');
fs.writeFileSync(logFile, `[INFO] ${new Date().toISOString()} - Starting Financial Datasets MCP Server...\n`);
console.log = (...args) => {
const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
fs.appendFileSync(logFile, `[INFO] ${new Date().toISOString()} - ${message}\n`);
};
console.error = (...args) => {
const message = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : arg).join(' ');
fs.appendFileSync(logFile, `[ERROR] ${new Date().toISOString()} - ${message}\n`);
};
const logger = {
log: (message) => {
fs.appendFileSync(logFile, `[INFO] ${new Date().toISOString()} - ${message}\n`);
},
error: (message, error) => {
fs.appendFileSync(logFile, `[ERROR] ${new Date().toISOString()} - ${message}\n`);
if (error) {
fs.appendFileSync(logFile, `${error.stack || error}\n`);
}
}
};
let financialConfig;
let apiClient;
/**
* Helper function to safely extract parameters from request arguments
*/
function safeGetArgs(args, defaultValues) {
if (!args || typeof args !== 'object') {
return defaultValues;
}
const result = { ...defaultValues };
for (const key in defaultValues) {
if (args[key] !== undefined) {
result[key] = args[key];
}
}
return result;
}
/**
* Helper function to recursively find text content in a response object
*/
function findTextInObject(obj) {
if (typeof obj === 'string') {
return obj;
}
if (typeof obj === 'object' && obj !== null) {
// Check common text properties
if (obj.text && typeof obj.text === 'string') {
return obj.text;
}
if (obj.content && typeof obj.content === 'string') {
return obj.content;
}
if (obj.message && typeof obj.message === 'string') {
return obj.message;
}
// Recursively search in nested objects
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const result = findTextInObject(obj[key]);
if (result) {
return result;
}
}
}
}
return null;
}
/**
* Helper function to request LLM sampling from the client
*/
async function requestSampling(server, messages, systemPrompt, modelPreferences) {
console.log('š [TRANSPORT] === STARTING SAMPLING REQUEST ===');
try {
logger.log('Initiating sampling request to client');
const samplingParams = {
messages,
systemPrompt: systemPrompt || "You are a financial analysis expert. Provide clear, accurate, and actionable insights.",
modelPreferences: modelPreferences || {
hints: [
{ name: "claude-3-sonnet" },
{ name: "gpt-4" },
{ name: "claude" }
],
intelligencePriority: 0.9,
speedPriority: 0.6,
costPriority: 0.4
},
maxTokens: 4000
};
logger.log('Sending sampling request with params: ' + JSON.stringify(samplingParams, null, 2));
// TRANSPORT LOG: Sampling request being sent to client
console.log('\nš” [TRANSPORT] SENDING SAMPLING REQUEST TO CLIENT:');
console.log('=====================================');
console.log('Method: sampling/createMessage');
console.log('Message Count:', samplingParams.messages.length);
console.log('System Prompt Length:', samplingParams.systemPrompt?.length || 0, 'characters');
console.log('Model Preferences:', JSON.stringify(samplingParams.modelPreferences, null, 2));
console.log('Max Tokens:', samplingParams.maxTokens);
console.log('Message Preview (first 200 chars):', samplingParams.messages[0]?.content?.text?.substring(0, 200) + '...');
console.log('=====================================\n');
// Send the sampling request to the client
let response;
try {
console.log('š [TRANSPORT] CALLING server.request() for sampling...');
console.log('š [TRANSPORT] Server object type:', typeof server);
console.log('š [TRANSPORT] Server has request method:', typeof server.request);
console.log('š [TRANSPORT] Server methods:', Object.getOwnPropertyNames(server));
console.log('š [TRANSPORT] Server prototype methods:', Object.getOwnPropertyNames(Object.getPrototypeOf(server)));
console.log('š [TRANSPORT] Server constructor name:', server.constructor.name);
// Check available methods for sampling
console.log('š [TRANSPORT] Checking for sampling methods...');
console.log('š [TRANSPORT] server.request:', typeof server.request);
console.log('š [TRANSPORT] server.requestSampling:', typeof server.requestSampling);
console.log('š [TRANSPORT] server.createMessage:', typeof server.createMessage);
console.log('š [TRANSPORT] server.sendRequest:', typeof server.sendRequest);
// Try different methods based on what's available
let requestMethod = null;
let methodName = '';
if (typeof server.requestSampling === 'function') {
requestMethod = server.requestSampling.bind(server);
methodName = 'requestSampling';
}
else if (typeof server.createMessage === 'function') {
requestMethod = server.createMessage.bind(server);
methodName = 'createMessage';
}
else if (typeof server.sendRequest === 'function') {
requestMethod = server.sendRequest.bind(server);
methodName = 'sendRequest';
}
else if (typeof server.request === 'function') {
requestMethod = server.request.bind(server);
methodName = 'request';
}
else {
console.error('ā [TRANSPORT] Server does not have any request methods');
throw new Error('Server instance does not support making requests to client');
}
console.log(`ā
[TRANSPORT] Using method: ${methodName}`);
// Create the request object
const requestObject = {
method: "sampling/createMessage",
params: samplingParams
};
console.log('š¤ [TRANSPORT] Request object:', JSON.stringify(requestObject, null, 2));
// Use the appropriate method based on what's available
if (methodName === 'requestSampling') {
console.log('š [TRANSPORT] Using requestSampling method...');
response = await requestMethod(samplingParams);
}
else if (methodName === 'createMessage') {
console.log('š [TRANSPORT] Using createMessage method...');
response = await requestMethod(samplingParams);
}
else {
console.log(`š [TRANSPORT] Using ${methodName} method...`);
response = await requestMethod(requestObject);
}
console.log('ā
[TRANSPORT] SAMPLING REQUEST COMPLETED SUCCESSFULLY');
}
catch (requestError) {
console.error('ā [TRANSPORT] SAMPLING REQUEST FAILED:', requestError);
console.error('Error details:', {
name: requestError instanceof Error ? requestError.name : 'Unknown',
message: requestError instanceof Error ? requestError.message : String(requestError),
stack: requestError instanceof Error ? requestError.stack : 'No stack trace'
});
// Check if it's the specific parsing error
if (requestError instanceof Error && requestError.message.includes('Cannot read properties of undefined')) {
console.error('šØ [TRANSPORT] DETECTED PARSING ERROR - This might be an MCP SDK issue');
console.error('Full error object:', JSON.stringify(requestError, null, 2));
// For now, return a placeholder indicating sampling is not working
console.log('ā ļø [TRANSPORT] SAMPLING NOT WORKING - RETURNING PLACEHOLDER');
return "ā ļø **AI Analysis Not Available** ā ļø\n\nSampling request failed due to MCP SDK limitations. The server can send sampling requests to the client, but cannot receive the responses properly.\n\n**Technical Details:**\n- Client successfully receives sampling requests\n- Client generates LLM responses\n- Server fails to parse the response due to MCP SDK issue\n\n**Workaround needed:** This requires fixing the MCP transport layer or using a different approach for server-to-client sampling requests.";
}
throw new Error(`Sampling request failed: ${requestError instanceof Error ? requestError.message : 'Unknown error'}`);
}
// TRANSPORT LOG: Raw sampling response received from client
console.log('\nš [TRANSPORT] SAMPLING RESPONSE RECEIVED FROM CLIENT:');
console.log('=====================================');
console.log(JSON.stringify(response, null, 2));
console.log('=====================================\n');
logger.log('Received sampling response: ' + JSON.stringify(response));
// Extract the text content from the response
let extractedText;
// Handle different possible response formats
try {
if (response && typeof response === 'object') {
// Check for standard MCP sampling response format
if (response.content && response.content.type === 'text' && response.content.text) {
extractedText = response.content.text;
console.log('š [TRANSPORT] EXTRACTED TEXT FROM SAMPLING RESPONSE (content.text):');
}
// Check for direct text property
else if (response.text && typeof response.text === 'string') {
extractedText = response.text;
console.log('š [TRANSPORT] EXTRACTED TEXT FROM SAMPLING RESPONSE (.text):');
}
// Check for result property (some MCP implementations)
else if (response.result && response.result.content && response.result.content.text) {
extractedText = response.result.content.text;
console.log('š [TRANSPORT] EXTRACTED TEXT FROM SAMPLING RESPONSE (result.content.text):');
}
// Check if response itself has the text directly
else if (typeof response.content === 'string') {
extractedText = response.content;
console.log('š [TRANSPORT] EXTRACTED TEXT FROM SAMPLING RESPONSE (content as string):');
}
else {
// Try to find any text property recursively
const textValue = findTextInObject(response);
if (textValue) {
extractedText = textValue;
console.log('š [TRANSPORT] EXTRACTED TEXT FROM SAMPLING RESPONSE (found recursively):');
}
else {
console.error('ā [TRANSPORT] NO TEXT FOUND IN SAMPLING RESPONSE:', JSON.stringify(response, null, 2));
throw new Error('No text content found in sampling response');
}
}
}
// Handle string response
else if (typeof response === 'string') {
extractedText = response;
console.log('š [TRANSPORT] RESPONSE IS STRING:');
}
else {
console.error('ā [TRANSPORT] UNEXPECTED SAMPLING RESPONSE FORMAT:', response);
throw new Error('Received unexpected response format from sampling request');
}
// Log the extracted text details
console.log('Text Length:', extractedText.length, 'characters');
console.log('First 200 chars:', extractedText.substring(0, 200) + (extractedText.length > 200 ? '...' : ''));
console.log('Last 100 chars:', extractedText.length > 100 ? '...' + extractedText.substring(extractedText.length - 100) : extractedText);
console.log('');
return extractedText;
}
catch (parseError) {
console.error('ā [TRANSPORT] ERROR PARSING SAMPLING RESPONSE:', parseError);
console.error('Raw response:', JSON.stringify(response, null, 2));
throw new Error(`Failed to parse sampling response: ${parseError instanceof Error ? parseError.message : 'Unknown parsing error'}`);
}
}
catch (error) {
logger.error('š„ [TRANSPORT] === SAMPLING REQUEST FAILED ===');
logger.error('Error type:', typeof error);
logger.error('Error constructor:', error?.constructor?.name);
logger.error('Error message:', error instanceof Error ? error.message : String(error));
logger.error('Error stack:', error instanceof Error ? error.stack : 'No stack');
logger.error('Full error object:', JSON.stringify(error, null, 2));
logger.error('Sampling request failed T:', error);
throw new Error(`Failed to request LLM analysis: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
finally {
console.log('š [TRANSPORT] === SAMPLING REQUEST COMPLETE ===\n');
}
}
function parseArgs() {
// First check for environment variables (used when running in npx mode)
if (process.env.FINANCIAL_API_KEY) {
logger.log('Using API key from environment variable');
return {
apiKey: process.env.FINANCIAL_API_KEY,
baseUrl: process.env.FINANCIAL_API_BASE_URL || 'https://api.financialdatasets.ai'
};
}
// Otherwise parse from command line
const args = process.argv.slice(2);
const config = {
baseUrl: 'https://api.financialdatasets.ai',
apiKey: ''
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--api-key' && i + 1 < args.length) {
config.apiKey = args[++i];
}
else if (arg === '--base-url' && i + 1 < args.length) {
config.baseUrl = args[++i];
}
}
if (!config.apiKey) {
throw new Error('Financial Datasets API key is required. Use --api-key argument or set FINANCIAL_API_KEY environment variable.');
}
return config;
}
function initApiClient(config) {
return axios.create({
baseURL: config.baseUrl,
headers: {
'X-API-KEY': config.apiKey,
'Content-Type': 'application/json'
},
timeout: 10000
});
}
const server = new Server({
name: "financial-mcp-server",
version: "1.0.0"
}, {
capabilities: {
resources: {
read: true,
list: true,
templates: true
},
tools: {
list: true,
call: true
},
prompts: {
list: true,
get: true,
listChanged: true
},
completion: {
complete: true
},
sampling: {
createMessage: true
}
}
});
async function fetchAvailableTickers(limit = 20) {
try {
logger.log('Fetching available tickers...');
// This is a hypothetical endpoint - adjust as needed
const response = await apiClient.get('/company/facts/tickers/');
const tickers = response.data.tickers || [];
logger.log(`Found ${tickers.length} tickers`);
return tickers.slice(0, limit);
}
catch (error) {
logger.error('Error fetching tickers:', error);
// Return some common tickers if API fails
return ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META'];
}
}
async function fetchAvailableCryptoTickers(limit = 10) {
try {
logger.log('Fetching available crypto tickers...');
const response = await apiClient.get('/crypto/prices/tickers/');
const tickers = response.data.tickers || [];
logger.log(`Found ${tickers.length} crypto tickers`);
return tickers.slice(0, limit);
}
catch (error) {
logger.error('Error fetching crypto tickers:', error);
// Return some common crypto tickers if API fails
return ['BTC-USD', 'ETH-USD', 'SOL-USD', 'DOGE-USD', 'XRP-USD'];
}
}
// === FRESH RESOURCES IMPLEMENTATION ===
// Following MCP 2025-06-18 specification exactly
/**
* List available tools for interacting with financial data.
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
"name": "get_company_facts",
"description": "Get company facts including name, CIK, market cap, employees, etc.",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company (e.g., AAPL)."
},
"cik": {
"type": "string",
"description": "The CIK (Central Index Key) of the company."
}
},
"required": []
}
},
{
"name": "get_stock_prices",
"description": "Get historical stock prices for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company (e.g., AAPL)."
},
"period": {
"type": "string",
"description": "Time period for the data (e.g., '1d', '1m', '1y')."
},
"limit": {
"type": "integer",
"description": "Maximum number of records to return.",
"default": 30
}
},
"required": ["ticker"]
}
},
{
"name": "search_companies",
"description": "Search for companies by name or industry",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query for company name, industry, or sector."
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return.",
"default": 10
}
},
"required": ["query"]
}
},
{
"name": "get_crypto_prices",
"description": "Get historical price data for a cryptocurrency",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The cryptocurrency ticker symbol (e.g., BTC-USD)."
},
"interval": {
"type": "string",
"description": "The time interval for price data ('minute', 'day', 'week', 'month', 'year').",
"enum": ["minute", "day", "week", "month", "year"]
},
"interval_multiplier": {
"type": "integer",
"description": "The multiplier for the interval (e.g., 5 for every 5 minutes).",
"minimum": 1
},
"start_date": {
"type": "string",
"description": "The start date for price data (format: YYYY-MM-DD)."
},
"end_date": {
"type": "string",
"description": "The end date for price data (format: YYYY-MM-DD)."
},
"limit": {
"type": "integer",
"description": "Maximum number of price records to return.",
"default": 1000,
"maximum": 5000,
"minimum": 1
}
},
"required": ["ticker", "interval", "interval_multiplier", "start_date", "end_date"]
}
},
{
"name": "get_crypto_snapshot",
"description": "Get the current price snapshot for a cryptocurrency",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The cryptocurrency ticker symbol (e.g., BTC-USD)."
}
},
"required": ["ticker"]
}
},
{
"name": "get_earnings_press_releases",
"description": "Get earnings press releases for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
}
},
"required": ["ticker"]
}
},
{
"name": "get_financial_metrics",
"description": "Get historical financial metrics for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"period": {
"type": "string",
"description": "The time period for the data ('annual', 'quarterly', 'ttm').",
"enum": ["annual", "quarterly", "ttm"]
},
"limit": {
"type": "integer",
"description": "Maximum number of records to return.",
"default": 4
},
"report_period_gte": {
"type": "string",
"description": "Filter for report periods greater than or equal to date (format: YYYY-MM-DD)."
},
"report_period_lte": {
"type": "string",
"description": "Filter for report periods less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["ticker", "period"]
}
},
{
"name": "get_financial_metrics_snapshot",
"description": "Get current financial metrics snapshot for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
}
},
"required": ["ticker"]
}
},
{
"name": "get_income_statements",
"description": "Get income statements for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"cik": {
"type": "string",
"description": "Alternative: The CIK of the company."
},
"period": {
"type": "string",
"description": "The time period for the data ('annual', 'quarterly', 'ttm').",
"enum": ["annual", "quarterly", "ttm"]
},
"limit": {
"type": "integer",
"description": "Maximum number of records to return.",
"default": 4
},
"report_period_gte": {
"type": "string",
"description": "Filter for report periods greater than or equal to date (format: YYYY-MM-DD)."
},
"report_period_lte": {
"type": "string",
"description": "Filter for report periods less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["ticker", "period"]
}
},
{
"name": "get_balance_sheets",
"description": "Get balance sheets for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"cik": {
"type": "string",
"description": "Alternative: The CIK of the company."
},
"period": {
"type": "string",
"description": "The time period for the data ('annual', 'quarterly', 'ttm').",
"enum": ["annual", "quarterly", "ttm"]
},
"limit": {
"type": "integer",
"description": "Maximum number of records to return.",
"default": 4
},
"report_period_gte": {
"type": "string",
"description": "Filter for report periods greater than or equal to date (format: YYYY-MM-DD)."
},
"report_period_lte": {
"type": "string",
"description": "Filter for report periods less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["ticker", "period"]
}
},
{
"name": "get_cash_flow_statements",
"description": "Get cash flow statements for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"cik": {
"type": "string",
"description": "Alternative: The CIK of the company."
},
"period": {
"type": "string",
"description": "The time period for the data ('annual', 'quarterly', 'ttm').",
"enum": ["annual", "quarterly", "ttm"]
},
"limit": {
"type": "integer",
"description": "Maximum number of records to return.",
"default": 4
},
"report_period_gte": {
"type": "string",
"description": "Filter for report periods greater than or equal to date (format: YYYY-MM-DD)."
},
"report_period_lte": {
"type": "string",
"description": "Filter for report periods less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["ticker", "period"]
}
},
{
"name": "get_all_financial_statements",
"description": "Get all financial statements for a company in one call",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"cik": {
"type": "string",
"description": "Alternative: The CIK of the company."
},
"period": {
"type": "string",
"description": "The time period for the data ('annual', 'quarterly', 'ttm').",
"enum": ["annual", "quarterly", "ttm"]
},
"limit": {
"type": "integer",
"description": "Maximum number of records to return.",
"default": 4
},
"report_period_gte": {
"type": "string",
"description": "Filter for report periods greater than or equal to date (format: YYYY-MM-DD)."
},
"report_period_lte": {
"type": "string",
"description": "Filter for report periods less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["ticker", "period"]
}
},
{
"name": "get_insider_trades",
"description": "Get insider trades (buys and sells) for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"limit": {
"type": "integer",
"description": "Maximum number of trades to return.",
"default": 100,
"maximum": 1000
},
"filing_date_gte": {
"type": "string",
"description": "Filter for filing dates greater than or equal to date (format: YYYY-MM-DD)."
},
"filing_date_lte": {
"type": "string",
"description": "Filter for filing dates less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["ticker"]
}
},
{
"name": "get_institutional_ownership_by_investor",
"description": "Get institutional ownership data by investor",
"inputSchema": {
"type": "object",
"properties": {
"investor": {
"type": "string",
"description": "The name of the investment manager (e.g., BERKSHIRE_HATHAWAY_INC)."
},
"limit": {
"type": "integer",
"description": "Maximum number of holdings to return.",
"default": 10
},
"report_period_gte": {
"type": "string",
"description": "Filter for report periods greater than or equal to date (format: YYYY-MM-DD)."
},
"report_period_lte": {
"type": "string",
"description": "Filter for report periods less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["investor"]
}
},
{
"name": "get_institutional_ownership_by_ticker",
"description": "Get institutional ownership data by ticker",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"limit": {
"type": "integer",
"description": "Maximum number of institutional owners to return.",
"default": 10
},
"report_period_gte": {
"type": "string",
"description": "Filter for report periods greater than or equal to date (format: YYYY-MM-DD)."
},
"report_period_lte": {
"type": "string",
"description": "Filter for report periods less than or equal to date (format: YYYY-MM-DD)."
}
},
"required": ["ticker"]
}
},
{
"name": "get_company_news",
"description": "Get news articles for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"start_date": {
"type": "string",
"description": "The start date for news articles (format: YYYY-MM-DD)."
},
"end_date": {
"type": "string",
"description": "The end date for news articles (format: YYYY-MM-DD)."
},
"limit": {
"type": "integer",
"description": "Maximum number of news articles to return.",
"default": 100,
"maximum": 100,
"minimum": 1
}
},
"required": ["ticker"]
}
},
{
"name": "search_financials",
"description": "Search for financial data across income statements, balance sheets, and cash flow statements",
"inputSchema": {
"type": "object",
"properties": {
"search_type": {
"type": "string",
"description": "Type of search to perform: 'filters' for filtering by metrics or 'line_items' for retrieving specific data points.",
"enum": ["filters", "line_items"]
},
"filters": {
"type": "array",
"description": "Array of filter objects to apply (required when search_type=filters).",
"items": {
"type": "object",
"properties": {
"field": {
"type": "string",
"description": "The financial metric to filter on (e.g., revenue, total_debt, capital_expenditure)."
},
"operator": {
"type": "string",
"description": "The comparison operator.",
"enum": ["eq", "gt", "gte", "lt", "lte"]
},
"value": {
"type": "number",
"description": "The value to compare against."
}
},
"required": ["field", "operator", "value"]
}
},
"line_items": {
"type": "array",
"description": "Array of financial metrics to retrieve (required when search_type=line_items).",
"items": {
"type": "string"
}
},
"tickers": {
"type": "array",
"description": "Array of ticker symbols (required when search_type=line_items).",
"items": {
"type": "string"
}
},
"period": {
"type": "string",
"description": "The time period for the financial data.",
"enum": ["annual", "quarterly", "ttm"],
"default": "ttm"
},
"limit": {
"type": "integer",
"description": "Maximum number of results to return.",
"default": 100
},
"currency": {
"type": "string",
"description": "The currency for the financial data.",
"enum": ["USD", "EUR", "GBP", "JPY", "CHF", "AUD", "CAD", "SEK"]
},
"order_by": {
"type": "string",
"description": "Field to order results by.",
"enum": ["ticker", "-ticker", "report_period", "-report_period"],
"default": "ticker"
}
},
"required": ["search_type"]
}
},
{
"name": "get_stock_price_snapshot",
"description": "Get the real-time price snapshot for a stock ticker",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
}
},
"required": ["ticker"]
}
},
{
"name": "get_stock_prices_advanced",
"description": "Get ranged price data for a ticker with customizable intervals",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"interval": {
"type": "string",
"description": "The time interval for price data.",
"enum": ["second", "minute", "day", "week", "month", "year"]
},
"interval_multiplier": {
"type": "integer",
"description": "The multiplier for the interval (e.g., 5 for every 5 minutes).",
"minimum": 1
},
"start_date": {
"type": "string",
"description": "The start date for price data (format: YYYY-MM-DD)."
},
"end_date": {
"type": "string",
"description": "The end date for price data (format: YYYY-MM-DD)."
},
"limit": {
"type": "integer",
"description": "Maximum number of price records to return.",
"default": 5000,
"maximum": 5000,
"minimum": 1
}
},
"required": ["ticker", "interval", "interval_multiplier", "start_date", "end_date"]
}
},
{
"name": "get_sec_filing_items",
"description": "Get specific sections (items) from a company's SEC filings",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"filing_type": {
"type": "string",
"description": "The type of filing to extract items from.",
"enum": ["10-K", "10-Q"]
},
"year": {
"type": "integer",
"description": "The year of the filing."
},
"quarter": {
"type": "integer",
"description": "The quarter of the filing if 10-Q.",
"minimum": 1,
"maximum": 4
},
"items": {
"type": "array",
"description": "Specific items to extract from the filing. If not provided, all items are returned.",
"items": {
"type": "string",
"enum": ["Item-1", "Item-1A", "Item-1B", "Item-2", "Item-3", "Item-4", "Item-5", "Item-6", "Item-7", "Item-7A", "Item-8", "Item-9", "Item-9A", "Item-9B", "Item-10", "Item-11", "Item-12", "Item-13", "Item-14", "Item-15", "Item-16"]
}
}
},
"required": ["ticker", "filing_type", "year"]
}
},
{
"name": "get_segmented_revenues",
"description": "Get detailed, segmented revenue data for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"cik": {
"type": "string",
"description": "Alternative: The CIK of the company."
},
"period": {
"type": "string",
"description": "The time period for the data.",
"enum": ["annual", "quarterly"]
},
"limit": {
"type": "integer",
"description": "Maximum number of reports to return.",
"default": 4
}
},
"required": ["period"]
}
},
{
"name": "get_sec_filings",
"description": "Get a list of SEC filings for a company",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company."
},
"cik": {
"type": "string",
"description": "Alternative: The CIK of the company."
},
"filing_type": {
"type": "string",
"description": "Type of filing to filter results.",
"enum": ["10-K", "10-Q", "8-K", "4", "144"]
}
},
"required": []
}
},
{
"name": "ai_financial_analysis",
"description": "Get AI-powered financial analysis and insights using LLM sampling",
"inputSchema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The ticker symbol of the company to analyze"
},
"analysis_type": {
"type": "string",
"description": "Type of analysis to perform",
"enum": ["comprehensive", "valuation", "risks", "opportunities", "comparison"]
},
"context": {
"type": "string",
"description": "Additional context or specific questions for the analysis"
}
},
"required": ["ticker", "analysis_type"]
}
},
{
"name": "ai_market_insights",
"description": "Get AI-powered market insights and trend analysis using LLM sampling",
"inputSchema": {
"type": "object",
"properties": {
"focus_area": {
"type": "string",
"description": "Market focus area",
"enum": ["overall_market", "sector_analysis", "economic_indicators", "risk_assessment"]
},
"tickers": {
"type": "array",
"description": "Optional list of ticker symbols to focus on",
"items": {
"type": "string"
}
},
"context": {
"type": "string",
"description": "Specific questions or context for the market analysis"
}
},