/**
* @quant-companion/mcp-tools
*
* MCP server exposing quantitative finance tools for AI assistants.
*
* This server provides tools for:
* - Option pricing (Black-Scholes, Monte Carlo)
* - Implied volatility calculation
* - Historical volatility analysis
* - Risk metrics computation
* - Strategy backtesting
* - Price simulation
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import {
setDefaultProvider,
createDefaultProvider,
} from "./marketData";
import {
// Tool definitions
getCurrentPriceDefinition,
getHistoricalPricesDefinition,
priceOptionBlackScholesDefinition,
priceOptionMonteCarloDefinition,
computeImpliedVolDefinition,
computeHistoricalVolDefinition,
computeRiskMetricsDefinition,
runBacktestDefinition,
simulatePriceDefinition,
getOptionsChainDefinition,
getVolSmileDefinition,
getVolSurfaceDefinition,
simulateWithLocalVolDefinition,
detectUnusualActivityDefinition,
summarizeVolRegimeDefinition,
compareModelsForecastDefinition,
backtestForecastAccuracyDefinition,
// Tool handlers
getCurrentPrice,
getHistoricalPrices,
priceOptionBlackScholes,
priceOptionMonteCarlo,
computeImpliedVol,
computeHistoricalVol,
computeRiskMetrics,
runBacktest,
simulatePrice,
getOptionsChain,
getVolSmile,
getVolSurface,
simulateWithLocalVolTool,
detectUnusualActivityTool,
summarizeVolRegime,
compareModelsForecast,
backtestForecastAccuracy,
// Schemas
getCurrentPriceSchema,
getHistoricalPricesSchema,
priceOptionBlackScholesSchema,
priceOptionMonteCarloSchema,
computeImpliedVolSchema,
computeHistoricalVolSchema,
computeRiskMetricsSchema,
runBacktestSchema,
simulatePriceSchema,
getOptionsChainSchema,
getVolSmileSchema,
getVolSurfaceSchema,
simulateWithLocalVolSchema,
detectUnusualActivitySchema,
summarizeVolRegimeSchema,
compareModelsForecastSchema,
backtestForecastAccuracySchema,
} from "./tools";
// Configure provider based on environment
// Priority: Polygon+Yahoo fallback (if API key) > Yahoo only
const provider = createDefaultProvider();
console.error(`[MCP] Using ${provider.name} market data provider`);
setDefaultProvider(provider);
// Create server
const server = new Server(
{
name: "quant-companion",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
// Market data
getCurrentPriceDefinition,
getHistoricalPricesDefinition,
getOptionsChainDefinition,
// Volatility analytics
getVolSmileDefinition,
getVolSurfaceDefinition,
// Option pricing
priceOptionBlackScholesDefinition,
priceOptionMonteCarloDefinition,
computeImpliedVolDefinition,
// Simulation
simulatePriceDefinition,
simulateWithLocalVolDefinition,
// Risk & analysis
computeHistoricalVolDefinition,
computeRiskMetricsDefinition,
runBacktestDefinition,
detectUnusualActivityDefinition,
// Multi-step analysis
summarizeVolRegimeDefinition,
// Model comparison & forecasting
compareModelsForecastDefinition,
backtestForecastAccuracyDefinition,
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
let result: unknown;
switch (name) {
case "get_current_price": {
const input = getCurrentPriceSchema.parse(args);
result = await getCurrentPrice(input);
break;
}
case "get_historical_prices": {
const input = getHistoricalPricesSchema.parse(args);
result = await getHistoricalPrices(input);
break;
}
case "get_options_chain": {
const input = getOptionsChainSchema.parse(args);
result = await getOptionsChain(input);
break;
}
case "get_vol_smile": {
const input = getVolSmileSchema.parse(args);
result = await getVolSmile(input);
break;
}
case "get_vol_surface": {
const input = getVolSurfaceSchema.parse(args);
result = await getVolSurface(input);
break;
}
case "simulate_price_with_local_vol": {
const input = simulateWithLocalVolSchema.parse(args);
result = await simulateWithLocalVolTool(input);
break;
}
case "detect_unusual_activity": {
const input = detectUnusualActivitySchema.parse(args);
result = await detectUnusualActivityTool(input);
break;
}
case "summarize_vol_regime": {
const input = summarizeVolRegimeSchema.parse(args);
result = await summarizeVolRegime(input);
break;
}
case "price_option_black_scholes": {
const input = priceOptionBlackScholesSchema.parse(args);
result = priceOptionBlackScholes(input);
break;
}
case "price_option_monte_carlo": {
const input = priceOptionMonteCarloSchema.parse(args);
result = priceOptionMonteCarlo(input);
break;
}
case "compute_implied_vol": {
const input = computeImpliedVolSchema.parse(args);
result = computeImpliedVol(input);
break;
}
case "compute_historical_vol": {
const input = computeHistoricalVolSchema.parse(args);
result = await computeHistoricalVol(input);
break;
}
case "compute_risk_metrics": {
const input = computeRiskMetricsSchema.parse(args);
result = computeRiskMetrics(input);
break;
}
case "run_backtest": {
const input = runBacktestSchema.parse(args);
result = await runBacktest(input);
break;
}
case "simulate_price": {
const input = simulatePriceSchema.parse(args);
result = simulatePrice(input);
break;
}
case "compare_models_forecast_distribution": {
const input = compareModelsForecastSchema.parse(args);
result = await compareModelsForecast(input);
break;
}
case "backtest_forecast_accuracy": {
const input = backtestForecastAccuracySchema.parse(args);
result = await backtestForecastAccuracy(input);
break;
}
default:
throw new Error(`Unknown tool: ${name}`);
}
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
const message = error instanceof Error ? error.message : "Unknown error occurred";
// Categorize error
let code = "COMPUTATION_ERROR";
if (message.includes("not found") || message.includes("No data")) {
code = "DATA_NOT_FOUND";
} else if (message.includes("must be") || message.includes("cannot") || message.includes("Invalid")) {
code = "INVALID_INPUT";
} else if (message.includes("converge") || message.includes("Convergence")) {
code = "CONVERGENCE_FAILED";
} else if (message.includes("limit") || message.includes("exceeded") || message.includes("maximum")) {
code = "LIMIT_EXCEEDED";
}
return {
content: [
{
type: "text",
text: JSON.stringify({
error: {
code,
message,
},
}, null, 2),
},
],
isError: true,
};
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("[MCP] Quant Companion MCP server started");
}
main().catch((error) => {
console.error("[MCP] Fatal error:", error);
process.exit(1);
});