Skip to main content
Glama
mcp-service.js20 kB
#!/usr/bin/env node 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 dotenv from 'dotenv'; import { config, logger } from '../core/config.js'; import { IGService } from './ig-service.js'; dotenv.config(); const igService = new IGService(); // Initialize configuration from environment or config file const initializeConfig = () => { try { // Try to load from environment variables first config.credentials = { apiKey: process.env.IG_API_KEY, identifier: process.env.IG_IDENTIFIER, password: process.env.IG_PASSWORD, isDemo: process.env.IG_DEMO?.toLowerCase() === 'true' }; // Validate that we have minimum required credentials if (!config.credentials.apiKey || !config.credentials.identifier || !config.credentials.password) { logger.warn('IG credentials not fully configured. Some tools may not work.'); } } catch (error) { logger.warn('Failed to initialize config:', error.message); } }; // Create MCP server const server = new Server( { name: 'ig-trading-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Tool definitions const TOOLS = [ // Account Management Tools { name: 'ig_login', description: 'Login to IG Trading account', inputSchema: { type: 'object', properties: { apiKey: { type: 'string', description: 'IG API Key (optional if set in environment)', }, identifier: { type: 'string', description: 'IG Account Identifier (optional if set in environment)', }, password: { type: 'string', description: 'IG Account Password (optional if set in environment)', }, isDemo: { type: 'boolean', description: 'Use demo account (default: true)', default: true, }, useEncryption: { type: 'boolean', description: 'Use encrypted login (default: true)', default: true, }, }, }, }, { name: 'ig_logout', description: 'Logout from IG Trading account', inputSchema: { type: 'object', properties: {}, }, }, { name: 'ig_get_accounts', description: 'Get list of all trading accounts', inputSchema: { type: 'object', properties: {}, }, }, { name: 'ig_switch_account', description: 'Switch to a different trading account', inputSchema: { type: 'object', properties: { accountId: { type: 'string', description: 'Account ID to switch to', }, }, required: ['accountId'], }, }, { name: 'ig_get_account_activity', description: 'Get account activity history', inputSchema: { type: 'object', properties: { from: { type: 'string', description: 'Start date (YYYY-MM-DD)', }, to: { type: 'string', description: 'End date (YYYY-MM-DD)', }, detailed: { type: 'boolean', description: 'Include detailed information', default: false, }, pageSize: { type: 'number', description: 'Number of results per page', default: 500, }, }, }, }, // Position Management Tools { name: 'ig_get_positions', description: 'Get all open positions', inputSchema: { type: 'object', properties: {}, }, }, { name: 'ig_create_position', description: 'Create a new trading position', inputSchema: { type: 'object', properties: { epic: { type: 'string', description: 'Market epic code', }, direction: { type: 'string', enum: ['BUY', 'SELL'], description: 'Trade direction', }, size: { type: 'number', description: 'Position size', }, currencyCode: { type: 'string', description: 'Currency code (e.g., GBP, USD)', }, expiry: { type: 'string', description: 'Contract expiry (e.g., DFB for daily funded bet)', }, orderType: { type: 'string', enum: ['MARKET', 'LIMIT'], description: 'Order type', }, level: { type: 'number', description: 'Price level (required for LIMIT orders)', }, stopLevel: { type: 'number', description: 'Stop loss level', }, limitLevel: { type: 'number', description: 'Take profit level', }, guaranteedStop: { type: 'boolean', description: 'Use guaranteed stop', default: false, }, forceOpen: { type: 'boolean', description: 'Force open a new position', default: true, }, timeInForce: { type: 'string', enum: ['FILL_OR_KILL', 'EXECUTE_AND_ELIMINATE'], description: 'Time in force', }, }, required: ['epic', 'direction', 'size', 'currencyCode', 'expiry', 'orderType', 'timeInForce'], }, }, { name: 'ig_close_position', description: 'Close an open position', inputSchema: { type: 'object', properties: { dealId: { type: 'string', description: 'Deal ID of the position to close', }, }, required: ['dealId'], }, }, { name: 'ig_close_all_positions', description: 'Close all open positions', inputSchema: { type: 'object', properties: {}, }, }, { name: 'ig_update_position', description: 'Update stop/limit levels for an open position', inputSchema: { type: 'object', properties: { dealId: { type: 'string', description: 'Deal ID of the position to update', }, stopLevel: { type: 'number', description: 'New stop loss level', }, limitLevel: { type: 'number', description: 'New take profit level', }, trailingStop: { type: 'boolean', description: 'Enable trailing stop', }, trailingStopDistance: { type: 'number', description: 'Trailing stop distance', }, }, required: ['dealId'], }, }, // Working Orders Tools { name: 'ig_get_working_orders', description: 'Get all working orders', inputSchema: { type: 'object', properties: {}, }, }, { name: 'ig_create_working_order', description: 'Create a working order', inputSchema: { type: 'object', properties: { epic: { type: 'string', description: 'Market epic code', }, direction: { type: 'string', enum: ['BUY', 'SELL'], description: 'Trade direction', }, size: { type: 'number', description: 'Order size', }, currencyCode: { type: 'string', description: 'Currency code', }, expiry: { type: 'string', description: 'Contract expiry', }, type: { type: 'string', enum: ['LIMIT', 'STOP'], description: 'Order type', }, level: { type: 'number', description: 'Entry level', }, stopLevel: { type: 'number', description: 'Stop loss level', }, limitLevel: { type: 'number', description: 'Take profit level', }, guaranteedStop: { type: 'boolean', description: 'Use guaranteed stop', default: false, }, forceOpen: { type: 'boolean', description: 'Force open a new position', default: true, }, timeInForce: { type: 'string', enum: ['GOOD_TILL_CANCELLED', 'GOOD_TILL_DATE'], description: 'Time in force', }, goodTillDate: { type: 'string', description: 'Expiry date for GOOD_TILL_DATE orders', }, }, required: ['epic', 'direction', 'size', 'currencyCode', 'expiry', 'type', 'level', 'timeInForce'], }, }, { name: 'ig_delete_working_order', description: 'Delete a working order', inputSchema: { type: 'object', properties: { dealId: { type: 'string', description: 'Deal ID of the order to delete', }, }, required: ['dealId'], }, }, // Market Data Tools { name: 'ig_search_markets', description: 'Search for tradeable markets', inputSchema: { type: 'object', properties: { searchTerm: { type: 'string', description: 'Search term (e.g., "Oil", "EUR/USD", "Apple")', }, }, required: ['searchTerm'], }, }, { name: 'ig_get_market_details', description: 'Get detailed information about a market', inputSchema: { type: 'object', properties: { epics: { type: 'array', items: { type: 'string', }, description: 'List of market epic codes (max 50)', }, }, required: ['epics'], }, }, { name: 'ig_get_historical_prices', description: 'Get historical price data', inputSchema: { type: 'object', properties: { epic: { type: 'string', description: 'Market epic code', }, resolution: { type: 'string', enum: ['SECOND', 'MINUTE', 'MINUTE_2', 'MINUTE_3', 'MINUTE_5', 'MINUTE_10', 'MINUTE_15', 'MINUTE_30', 'HOUR', 'HOUR_2', 'HOUR_3', 'HOUR_4', 'DAY', 'WEEK', 'MONTH'], description: 'Time resolution', }, max: { type: 'number', description: 'Maximum number of data points', default: 10, }, from: { type: 'string', description: 'Start date/time (YYYY-MM-DDTHH:MM:SS)', }, to: { type: 'string', description: 'End date/time (YYYY-MM-DDTHH:MM:SS)', }, }, required: ['epic', 'resolution'], }, }, { name: 'ig_get_client_sentiment', description: 'Get client sentiment for markets', inputSchema: { type: 'object', properties: { marketIds: { type: 'array', items: { type: 'string', }, description: 'List of market IDs', }, }, required: ['marketIds'], }, }, // Watchlist Tools { name: 'ig_get_watchlists', description: 'Get all watchlists', inputSchema: { type: 'object', properties: {}, }, }, { name: 'ig_get_watchlist', description: 'Get details of a specific watchlist', inputSchema: { type: 'object', properties: { watchlistId: { type: 'string', description: 'Watchlist ID', }, }, required: ['watchlistId'], }, }, { name: 'ig_create_watchlist', description: 'Create a new watchlist', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Watchlist name', }, epics: { type: 'array', items: { type: 'string', }, description: 'Initial epics to add', default: [], }, }, required: ['name'], }, }, { name: 'ig_add_to_watchlist', description: 'Add an epic to a watchlist', inputSchema: { type: 'object', properties: { watchlistId: { type: 'string', description: 'Watchlist ID', }, epic: { type: 'string', description: 'Epic to add', }, }, required: ['watchlistId', 'epic'], }, }, ]; // Handle tool listing server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: TOOLS, }; }); // Handle tool execution server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { // Handle credential updates if provided if (args.apiKey || args.identifier || args.password) { config.credentials = { apiKey: args.apiKey || config.credentials?.apiKey, identifier: args.identifier || config.credentials?.identifier, password: args.password || config.credentials?.password, isDemo: args.isDemo !== undefined ? args.isDemo : config.credentials?.isDemo, }; } switch (name) { // Account Management case 'ig_login': const loginResult = await igService.login(args.useEncryption !== false); return { content: [ { type: 'text', text: JSON.stringify({ success: true, accountId: loginResult.currentAccountId, accountType: loginResult.accountType, currency: loginResult.currencyIsoCode, }, null, 2), }, ], }; case 'ig_logout': await igService.logout(); return { content: [ { type: 'text', text: 'Successfully logged out', }, ], }; case 'ig_get_accounts': const accounts = await igService.getAccounts(); return { content: [ { type: 'text', text: JSON.stringify(accounts, null, 2), }, ], }; case 'ig_switch_account': const switchResult = await igService.switchAccount(args.accountId); return { content: [ { type: 'text', text: JSON.stringify(switchResult, null, 2), }, ], }; case 'ig_get_account_activity': const activity = await igService.getAccountActivity(args); return { content: [ { type: 'text', text: JSON.stringify(activity, null, 2), }, ], }; // Position Management case 'ig_get_positions': const positions = await igService.getPositions(); return { content: [ { type: 'text', text: JSON.stringify(positions, null, 2), }, ], }; case 'ig_create_position': const positionResult = await igService.createPosition(args); return { content: [ { type: 'text', text: JSON.stringify(positionResult, null, 2), }, ], }; case 'ig_close_position': const closeResult = await igService.closePosition(args.dealId); return { content: [ { type: 'text', text: JSON.stringify(closeResult, null, 2), }, ], }; case 'ig_close_all_positions': const closeAllResult = await igService.closeAllPositions(); return { content: [ { type: 'text', text: JSON.stringify(closeAllResult, null, 2), }, ], }; case 'ig_update_position': const updateResult = await igService.updatePosition(args.dealId, args); return { content: [ { type: 'text', text: JSON.stringify(updateResult, null, 2), }, ], }; // Working Orders case 'ig_get_working_orders': const orders = await igService.getWorkingOrders(); return { content: [ { type: 'text', text: JSON.stringify(orders, null, 2), }, ], }; case 'ig_create_working_order': const orderResult = await igService.createWorkingOrder(args); return { content: [ { type: 'text', text: JSON.stringify(orderResult, null, 2), }, ], }; case 'ig_delete_working_order': const deleteResult = await igService.deleteWorkingOrder(args.dealId); return { content: [ { type: 'text', text: JSON.stringify(deleteResult, null, 2), }, ], }; // Market Data case 'ig_search_markets': const searchResults = await igService.searchMarkets(args.searchTerm); return { content: [ { type: 'text', text: JSON.stringify(searchResults, null, 2), }, ], }; case 'ig_get_market_details': const marketDetails = await igService.getMarketDetails(args.epics); return { content: [ { type: 'text', text: JSON.stringify(marketDetails, null, 2), }, ], }; case 'ig_get_historical_prices': const prices = await igService.getHistoricalPrices( args.epic, args.resolution, { max: args.max, from: args.from, to: args.to, } ); return { content: [ { type: 'text', text: JSON.stringify(prices, null, 2), }, ], }; case 'ig_get_client_sentiment': const sentiment = await igService.getClientSentiment(args.marketIds); return { content: [ { type: 'text', text: JSON.stringify(sentiment, null, 2), }, ], }; // Watchlists case 'ig_get_watchlists': const watchlists = await igService.getWatchlists(); return { content: [ { type: 'text', text: JSON.stringify(watchlists, null, 2), }, ], }; case 'ig_get_watchlist': const watchlist = await igService.getWatchlist(args.watchlistId); return { content: [ { type: 'text', text: JSON.stringify(watchlist, null, 2), }, ], }; case 'ig_create_watchlist': const newWatchlist = await igService.createWatchlist(args.name, args.epics || []); return { content: [ { type: 'text', text: JSON.stringify(newWatchlist, null, 2), }, ], }; case 'ig_add_to_watchlist': const addResult = await igService.addToWatchlist(args.watchlistId, args.epic); return { content: [ { type: 'text', text: JSON.stringify(addResult, null, 2), }, ], }; default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { logger.error(`Tool execution failed for ${name}:`, error); return { content: [ { type: 'text', text: `Error: ${error.message}`, }, ], isError: true, }; } }); // Initialize and start server async function main() { initializeConfig(); const transport = new StdioServerTransport(); await server.connect(transport); logger.info('IG Trading MCP Server started'); } main().catch((error) => { logger.error('Failed to start MCP server:', error); process.exit(1); });

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kea0811/ig-trading-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server