Finance-dashboard presents a STDIO-only MCP server with 4 finance tools built on fastmcp. Tool naming is reasonable (verb_noun pattern: getCompanyInfo, getStockPrice, getHistoricalPrices, getAnalystRecommendations), but parameter schemas are incomplete, descriptions lack depth, and error handling is minimal. All 4 tools share a single required parameter (ticker: string) with basic descriptions. Tools return dict objects without documented output schemas. The server lacks error recovery guidance, security hardening, and proper input validation. Performance is acceptable for small financial queries, but production readiness is limited by STDIO transport and incomplete protocol support.
No documented output schemas. Tools return dicts (e.g. {ticker, name, sector, industry, website, summary} for getCompanyInfo; {ticker, price} for getStockPrice) but LLMs cannot reliably extract fields without a declared schema. This violates pattern:tool and pattern:response-shaper.
Incomplete parameter descriptions. getHistoricalPrices accepts 'period' and 'interval' parameters, but these lack descriptions in the function signature. The code mentions defaults ('7d', '1d') but LLMs cannot infer constraints without explicit param descriptions (pattern:tool-description). Schema is missing types for these parameters.
Minimal error guidance. Error responses are generic (e.g., 'Could not fetch company info for {ticker}: {str(e)}', 'No price data for {ticker}', 'No historical data for {ticker}'). These do not tell the LLM what to do next or whether the error is retryable. Violates pattern:recovery-guide and pattern:error-classification.
Recommendations
Document output schemas for each tool. For getCompanyInfo, declare: { ticker: string, name: string, sector: string, industry: string, website: string, summary: string }. For getStockPrice: { ticker: string, price: number }. For getHistoricalPrices: { ticker: string, period: string, interval: string, rows: [{ date: string (ISO 8601), open: number, high: number, low: number, close: number, volume: integer }] }. For getAnalystRecommendations: normalize to a single consistent schema and document it.
Add explicit type and description to all parameters. For getHistoricalPrices, modify the function signature to accept period and interval as Pydantic fields with descriptions: period: str = Field('7d', description='Time period for data: 1d, 5d, 1mo, 3mo, 6mo, 1y, 5y, 10y, max'); interval: str = Field('1d', description='Data point frequency: 1m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 1wk, 1mo, 3mo').
Add input validation with actionable error messages. For ticker, validate that it is 1-5 uppercase letters. Return: 'Invalid ticker symbol. Expected 1-5 uppercase letters (e.g., AAPL, MSFT). Got: "XYZ123". Ticker symbols are alphanumeric but typically uppercase.'
Enhance error responses with recovery guidance. Replace 'Could not fetch company info for {ticker}: {error}' with 'Unable to fetch company info for ticker "{ticker}". This may indicate: (1) Ticker not found, verify the symbol (e.g., is it GOOG or GOOGL?); (2) Yahoo Finance API temporarily unavailable, please retry in a moment. Call getStockPrice to verify the ticker is valid.'
No input validation or constraint documentation. The ticker parameter accepts any string; there is no validation that it matches valid stock symbols (typically 1-5 uppercase letters) or guidance on format. LLMs may pass invalid tickers like 'INVALID-SYMBOL' or '12345'. Pattern:constrained-input requires enum or regex constraints for known value sets.
Bare exception handling masks real failures. In getStockPrice, a try/except block silently falls back from fast_info to history() without logging or distinguishing transient failures from missing data. This makes it hard for the LLM to understand why a call succeeded with partial data.
Inconsistent return structure for analyst recommendations. getAnalystRecommendations returns different table schemas depending on which recommendation columns are present ('Firm'/'To Grade'/'From Grade'/'Action' vs 'period'/'strongBuy'/'buy'/'hold'/'sell'/'strongSell'). LLMs cannot reliably parse inconsistent structures; this violates pattern:response-shaper.
Tool descriptions do not explain WHEN to call each tool vs. the others or what prerequisites exist. For example, getHistoricalPrices description does not clarify that it returns tabular data (suitable for analysis) vs. getStockPrice (which returns only the latest price). This violates pattern:tool-description guidance that descriptions must answer 'What does it do? When should the LLM call it instead of a similar tool?'
No support for pagination or result limits. getHistoricalPrices returns all historical data for the specified period without page/offset/limit. If a user requests 1 year of daily data, this could return 250+ rows, overwhelming the context window. Pattern:paginated-result requires tools returning lists to support pagination.
getHistoricalPricesgetAnalystRecommendations
Normalize getAnalystRecommendations return structure. Always return a single schema with either analyst-specific recommendations (with firm/to_grade/from_grade/action) or summary counts (period/strongBuy/buy/hold/sell/strongSell). Document which format applies and under what conditions.
Add pagination support. For getHistoricalPrices and getAnalystRecommendations, add limit (default 20, max 100) and offset (default 0) parameters. Return total count and next_offset to enable multi-page queries without overwhelming the context window.
Improve tool descriptions to differentiate usage. E.g., getStockPrice: 'Fetch the most recent stock price. Use this for quick price lookups or real-time monitoring. For price trends, call getHistoricalPrices instead.' And getHistoricalPrices: 'Retrieve historical OHLCV data as a table for the specified time period. Use this for trend analysis, charting, or performance calculations. For just the current price, call getStockPrice.'
Add input validation with type coercion. Convert ticker to uppercase and strip whitespace inside the tool (already done), but also validate against a known set or at least document the expected format in the parameter description.
Consider adding rate limiting or timeout handling to prevent agent loops. If yfinance calls hang, return a clear timeout error: 'Yahoo Finance API did not respond within 5 seconds. This may indicate a service outage. Please retry in 30 seconds or try a different ticker.'
Log tool invocations (timestamp, ticker, result status) to stderr for debugging without exposing sensitive data.