#!/usr/bin/env node
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
const {
CallToolRequestSchema,
ListToolsRequestSchema,
} = require("@modelcontextprotocol/sdk/types.js");
// Import Saros services
const { SarosPoolService } = require("./services/pool-service.js");
const { SarosFarmService } = require("./services/farm-service.js");
const { SarosAnalyticsService } = require("./services/analytics-service.js");
// Import tools
const { getLpPositionsTool } = require("./tools/get-lp-positions.js");
const { simulateRebalanceTool } = require("./tools/simulate-rebalance.js");
const { portfolioAnalyticsTool } = require("./tools/portfolio-analytics.js");
const { getFarmPositionsTool } = require("./tools/get-farm-positions.js");
const { swapQuoteTool } = require("./tools/swap-quote.js");
class SarosMCPServer {
constructor() {
this.server = new Server(
{
name: "saros-mcp-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Initialize services
this.poolService = new SarosPoolService();
this.farmService = new SarosFarmService();
this.analyticsService = new SarosAnalyticsService();
// Setup handlers
this.setupHandlers();
}
setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_lp_positions",
description:
"Get all liquidity pool positions for a wallet address. Returns pool details, token balances, and LP token amounts.",
inputSchema: {
type: "object",
properties: {
wallet: {
type: "string",
description: "Solana wallet address (base58 encoded)",
},
},
required: ["wallet"],
},
},
{
name: "simulate_rebalance",
description:
"Simulate rebalancing LP positions based on impermanent loss threshold. Provides recommendations for position adjustments.",
inputSchema: {
type: "object",
properties: {
wallet: {
type: "string",
description: "Solana wallet address",
},
threshold: {
type: "number",
description: "IL threshold percentage (e.g., 5 for 5%)",
minimum: 0,
maximum: 100,
},
},
required: ["wallet", "threshold"],
},
},
{
name: "portfolio_analytics",
description:
"Get comprehensive portfolio analytics including total value, IL metrics, yield performance, and position breakdown.",
inputSchema: {
type: "object",
properties: {
wallet: {
type: "string",
description: "Solana wallet address",
},
},
required: ["wallet"],
},
},
{
name: "get_farm_positions",
description:
"Get all farming positions and staking rewards for a wallet. Shows staked LP tokens and claimable rewards.",
inputSchema: {
type: "object",
properties: {
wallet: {
type: "string",
description: "Solana wallet address",
},
},
required: ["wallet"],
},
},
{
name: "swap_quote",
description:
"Get swap quote for token exchange including price impact, fees, and minimum output amount with slippage.",
inputSchema: {
type: "object",
properties: {
poolAddress: {
type: "string",
description: "Pool address for the swap",
},
fromMint: {
type: "string",
description: "Source token mint address",
},
toMint: {
type: "string",
description: "Destination token mint address",
},
amount: {
type: "number",
description: "Amount to swap (in token decimals)",
},
slippage: {
type: "number",
description: "Slippage tolerance (0-100, default 0.5)",
default: 0.5,
},
},
required: ["poolAddress", "fromMint", "toMint", "amount"],
},
},
],
}));
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "get_lp_positions":
return await getLpPositionsTool(args, this.poolService);
case "simulate_rebalance":
return await simulateRebalanceTool(args, this.poolService, this.analyticsService);
case "portfolio_analytics":
return await portfolioAnalyticsTool(args, this.poolService, this.analyticsService);
case "get_farm_positions":
return await getFarmPositionsTool(args, this.farmService);
case "swap_quote":
return await swapQuoteTool(args, this.poolService);
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error.message}`,
},
],
isError: true,
};
}
});
}
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("Saros MCP Server running on stdio");
}
}
// Start the server
const server = new SarosMCPServer();
server.start().catch(console.error);