authenticate
Access the Interactive Brokers authentication web interface to enable AI assistants to interact with trading accounts, retrieve market data, and manage trades.
Instructions
Open Interactive Brokers authentication web interface
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| random_string | No | Dummy parameter for no-parameter tools |
Implementation Reference
- src/tool-handlers.ts:157-299 (handler)Main handler function for the 'authenticate' MCP tool. Handles both headless authentication using HeadlessAuthenticator and browser-based auth by opening the IB Gateway login URL.async authenticate(input: AuthenticateInput): Promise<ToolHandlerResult> { try { // Ensure Gateway is ready await this.ensureGatewayReady(); const port = this.context.gatewayManager ? this.context.gatewayManager.getCurrentPort() : this.context.config.IB_GATEWAY_PORT; const authUrl = `https://${this.context.config.IB_GATEWAY_HOST}:${port}`; // Check if headless mode is enabled in config if (this.context.config.IB_HEADLESS_MODE) { try { // Use headless authentication const authConfig: HeadlessAuthConfig = { url: authUrl, username: this.context.config.IB_USERNAME, password: this.context.config.IB_PASSWORD_AUTH, timeout: this.context.config.IB_AUTH_TIMEOUT, }; // Validate that we have credentials for headless mode if (!authConfig.username || !authConfig.password) { return { content: [ { type: "text", text: JSON.stringify({ success: false, message: "Headless mode enabled but authentication credentials missing", error: "Please set IB_USERNAME and IB_PASSWORD_AUTH environment variables for headless authentication", authUrl: authUrl, instructions: [ "Set environment variables: IB_USERNAME and IB_PASSWORD_AUTH", "Or disable headless mode by setting IB_HEADLESS_MODE=false", "Then try authentication again" ] }, null, 2), }, ], }; } const authenticator = new HeadlessAuthenticator(); const result = await authenticator.authenticate(authConfig); // Authentication completed (success or failure) - no separate 2FA handling needed await authenticator.close(); return { content: [ { type: "text", text: JSON.stringify({ ...result, authUrl: authUrl, mode: "headless", note: "Headless authentication completed automatically" }, null, 2), }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: "text", text: JSON.stringify({ success: false, message: "Headless authentication failed, falling back to manual browser authentication", error: errorMessage, authUrl: authUrl, mode: "fallback_to_manual", note: "Opening browser for manual authentication..." }, null, 2), }, ], }; } } // Original browser-based authentication (when headless mode is disabled or as fallback) try { await open(authUrl); return { content: [ { type: "text", text: JSON.stringify({ message: "Interactive Brokers authentication interface opened in your browser", authUrl: authUrl, mode: "browser", instructions: [ "1. The authentication page has been opened in your default browser", "2. Accept any SSL certificate warnings (this is normal for localhost)", "3. Complete the authentication process in the IB Gateway web interface", "4. Log in with your Interactive Brokers credentials", "5. Once authenticated, you can use other trading tools" ], browserOpened: true, note: "IB Gateway is running locally - your credentials stay secure on your machine" }, null, 2), }, ], }; } catch (browserError) { return { content: [ { type: "text", text: JSON.stringify({ message: "Opening Interactive Brokers authentication interface...", authUrl: authUrl, mode: "manual", instructions: [ "1. Open the authentication URL below in your browser:", ` ${authUrl}`, "2. Accept any SSL certificate warnings (this is normal for localhost)", "3. Complete the authentication process", "4. Log in with your Interactive Brokers credentials", "5. Once authenticated, you can use other trading tools" ], browserOpened: false, note: "Please open the URL manually. IB Gateway is running locally." }, null, 2), }, ], }; } } catch (error) { return { content: [ { type: "text", text: this.formatError(error), }, ], }; } }
- src/tool-definitions.ts:12-158 (schema)Zod schema and TypeScript type definition for the 'authenticate' tool input (requires { confirm: true }).export const AuthenticateZodShape = { confirm: z.literal(true) }; export const GetAccountInfoZodShape = { confirm: z.literal(true) }; export const GetPositionsZodShape = { accountId: z.string() }; export const GetMarketDataZodShape = { symbol: z.string(), exchange: z.string().optional() }; export const PlaceOrderZodShape = { accountId: z.string(), symbol: z.string(), action: z.enum(["BUY", "SELL"]), orderType: z.enum(["MKT", "LMT", "STP"]), quantity: IntegerOrStringIntegerZod, price: z.number().optional(), stopPrice: z.number().optional(), suppressConfirmations: z.boolean().optional() }; export const GetOrderStatusZodShape = { orderId: z.string() }; export const GetLiveOrdersZodShape = { accountId: z.string().optional() }; export const ConfirmOrderZodShape = { replyId: z.string(), messageIds: z.array(z.string()) }; export const GetAlertsZodShape = { accountId: z.string() }; export const CreateAlertZodShape = { accountId: z.string(), alertRequest: z.object({ orderId: z.number().optional(), alertName: z.string(), alertMessage: z.string().optional(), alertRepeatable: z.number().optional(), expireTime: z.string().optional(), outsideRth: z.number().optional(), iTWSOrdersOnly: z.number().optional(), showPopup: z.number().optional(), toolId: z.number().optional(), playAudio: z.string().optional(), emailNotification: z.string().optional(), sendMessage: z.number().optional(), tif: z.string().optional(), logicBind: z.string().optional(), conditions: z.array(z.object({ conidex: z.string(), type: z.string(), operator: z.string(), triggerMethod: z.string(), value: z.string(), logicBind: z.string().optional(), timeZone: z.string().optional() })) }) }; export const ActivateAlertZodShape = { accountId: z.string(), alertId: z.string() }; export const DeleteAlertZodShape = { accountId: z.string(), alertId: z.string() }; // Flex Query Zod Shapes export const GetFlexQueryZodShape = { queryId: z.string(), queryName: z.string().optional(), // Optional friendly name for auto-saving parseXml: z.boolean().optional().default(true) }; export const ListFlexQueriesZodShape = { confirm: z.literal(true) }; export const ForgetFlexQueryZodShape = { queryId: z.string() }; // Full Zod Schemas (for validation if needed) export const AuthenticateZodSchema = z.object(AuthenticateZodShape); export const GetAccountInfoZodSchema = z.object(GetAccountInfoZodShape); export const GetPositionsZodSchema = z.object(GetPositionsZodShape); export const GetMarketDataZodSchema = z.object(GetMarketDataZodShape); export const PlaceOrderZodSchema = z.object(PlaceOrderZodShape).refine( (data) => { if (data.orderType === "LMT" && data.price === undefined) { return false; } if (data.orderType === "STP" && data.stopPrice === undefined) { return false; } return true; }, { message: "LMT orders require price, STP orders require stopPrice", path: ["price", "stopPrice"] } ); export const GetOrderStatusZodSchema = z.object(GetOrderStatusZodShape); export const GetLiveOrdersZodSchema = z.object(GetLiveOrdersZodShape); export const ConfirmOrderZodSchema = z.object(ConfirmOrderZodShape); export const GetAlertsZodSchema = z.object(GetAlertsZodShape); export const CreateAlertZodSchema = z.object(CreateAlertZodShape); export const ActivateAlertZodSchema = z.object(ActivateAlertZodShape); export const DeleteAlertZodSchema = z.object(DeleteAlertZodShape); // Flex Query Full Schemas export const GetFlexQueryZodSchema = z.object(GetFlexQueryZodShape); export const ListFlexQueriesZodSchema = z.object(ListFlexQueriesZodShape); export const ForgetFlexQueryZodSchema = z.object(ForgetFlexQueryZodShape); // ── TypeScript types (inferred from Zod schemas) ──────────────────────────── export type AuthenticateInput = z.infer<typeof AuthenticateZodSchema>;
- src/tools.ts:40-47 (registration)MCP tool registration for 'authenticate' using McpServer.tool(), conditionally skipped in headless mode.if (!userConfig?.IB_HEADLESS_MODE) { server.tool( "authenticate", "Authenticate with Interactive Brokers. Usage: `{ \"confirm\": true }`.", AuthenticateZodShape, async (args) => await handlers.authenticate(args) ); }
- src/headless-auth.ts:26-194 (helper)HeadlessAuthenticator.authenticate() method called by the main handler in headless mode to automate browser-based login using Playwright.async authenticate(authConfig: HeadlessAuthConfig): Promise<HeadlessAuthResult> { try { Logger.info('🔐 Starting headless authentication...'); // Log the full auth config for debugging (excluding sensitive data) const logConfig = { ...authConfig }; if (logConfig.password) logConfig.password = '[REDACTED]'; Logger.info(`🔍 Authentication config: ${JSON.stringify(logConfig, null, 2)}`); // Use local browser - let Playwright handle everything Logger.info('🔧 Using local browser (Playwright default)'); this.browser = await BrowserInstaller.launchLocalBrowser(); this.page = await this.browser.newPage(); // Set a longer timeout for navigation - several minutes for full auth process this.page.setDefaultTimeout(authConfig.timeout || 300000); // 5 minutes default // Navigate to IB Gateway login page Logger.info(`🌐 Navigating to ${authConfig.url}...`); await this.page.goto(authConfig.url, { waitUntil: 'networkidle' }); // Wait for login form to be visible Logger.info('⏳ Waiting for login form...'); await this.page.waitForSelector('input[name="user"], input[id="user"], input[type="text"]', { timeout: 30000 }); // Find and fill username field const usernameSelector = 'input[name="user"], input[id="user"], input[type="text"]'; await this.page.fill(usernameSelector, authConfig.username); Logger.info('✅ Username filled'); // Find and fill password field const passwordSelector = 'input[name="password"], input[id="password"], input[type="password"]'; await this.page.fill(passwordSelector, authConfig.password); Logger.info('✅ Password filled'); // Handle paper trading toggle if specified - BEFORE submitting the form if (authConfig.paperTrading !== undefined) { try { Logger.info(`📊 Setting paper trading to ${authConfig.paperTrading ? 'enabled' : 'disabled'}...`); // Wait a moment for any dynamic content to load await this.page.waitForTimeout(1000); // Look for the specific paper trading checkbox const paperSwitchSelector = 'label[for="toggle1"]'; const element = await this.page.$(paperSwitchSelector); if (element) { const isChecked = await element.isChecked(); const shouldBeChecked = authConfig.paperTrading; if (isChecked !== shouldBeChecked) { Logger.info(`📊 Clicking paper trading checkbox to turn it ${shouldBeChecked ? 'ON' : 'OFF'}`); await element.click(); // Wait for any page updates after toggling await this.page.waitForTimeout(500); } else { Logger.info(`📊 Paper trading checkbox already in correct state: ${shouldBeChecked ? 'ON' : 'OFF'}`); } } else { Logger.warn('⚠️ Paper trading checkbox not found - may not be available for this account type'); } } catch (error) { Logger.warn('⚠️ Error while setting paper trading configuration:', error); // Continue with authentication - this shouldn't be a fatal error } } // Look for submit button and click it const submitSelector = 'input[type="submit"], button[type="submit"], button'; Logger.info('🔄 Submitting login form...'); await this.page.click(submitSelector); // Wait for the authentication process to complete using IB client polling Logger.info('⏳ Waiting for authentication to complete...'); const maxWaitTime = authConfig.timeout || 300000; // 5 minutes default const startTime = Date.now(); while (Date.now() - startTime < maxWaitTime) { await new Promise(resolve => setTimeout(resolve, 3000)); // Check every 3 seconds // Use IB Client to check authentication status if available if (authConfig.ibClient) { try { const isAuthenticated = await authConfig.ibClient.checkAuthenticationStatus(); if (isAuthenticated) { Logger.info('🎉 Authentication completed! IB Client confirmed authentication.'); await this.cleanup(); return { success: true, message: 'Headless authentication completed successfully. IB Client confirmed authentication.' }; } } catch (error) { Logger.debug('IB Client auth check failed, continuing...', error); } } // Fallback to page content checking if no IB client or client check fails try { const currentUrl = this.page.url(); const pageContent = await this.page.content(); // Check if we successfully authenticated by looking for the specific success message const authSuccess = pageContent.includes('Client login succeeds'); if (authSuccess) { Logger.info('🎉 Authentication completed! Found "Client login succeeds" message.'); await this.cleanup(); return { success: true, message: 'Headless authentication completed successfully. Client login succeeds message detected.' }; } // Check for potential 2FA or other intermediate states const has2FAIndicators = pageContent.includes('two-factor') || pageContent.includes('2FA') || pageContent.includes('authentication') || pageContent.includes('verification') || pageContent.includes('code') || currentUrl.includes('sso'); if (has2FAIndicators) { Logger.info('🔐 Two-factor authentication detected - continuing to wait...'); } else { Logger.info(`🔍 Still waiting for authentication completion... (${Math.round((Date.now() - startTime) / 1000)}s elapsed)`); } } catch (pageError) { Logger.warn('Page content check failed, continuing with IB client checks only...', pageError); // Continue with just IB client checks if page becomes unavailable } } // Timeout reached without seeing success message Logger.warn('⏰ Authentication timeout reached without seeing "Client login succeeds"'); return { success: false, message: 'Authentication timeout. Did not detect "Client login succeeds" message within the timeout period.', error: 'Authentication timeout - success message not detected' }; } catch (error) { Logger.error('❌ Headless authentication failed:', error); Logger.error('Environment info:', { platform: process.platform, arch: process.arch, nodeVersion: process.version }); await this.cleanup(); const errorMessage = error instanceof Error ? error.message : String(error); const errorDetails = error instanceof Error ? error.stack : 'No stack trace available'; return { success: false, message: 'Headless authentication failed', error: `${errorMessage}\n\nStack trace:\n${errorDetails}\n\nEnvironment: ${process.platform}-${process.arch}, Node: ${process.version}` }; } }