authenticate
Initiate authentication with Interactive Brokers to access trading accounts, market data, and execute trades through the MCP server.
Instructions
Authenticate with Interactive Brokers. Usage: { "confirm": true }.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | Yes |
Implementation Reference
- src/tool-handlers.ts:157-299 (handler)Main execution handler for the 'authenticate' tool. Manages Interactive Brokers Gateway authentication by either opening browser for manual login or performing headless authentication using provided credentials.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-14 (schema)Input schema shape (Zod) for the 'authenticate' tool, used in tool registration. Requires { confirm: true }.export const AuthenticateZodShape = { confirm: z.literal(true) };
- src/tools.ts:39-47 (registration)MCP tool registration for 'authenticate'. Conditionally registers if not in headless mode, linking schema and handler.// Register authenticate tool (skip if 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/tool-definitions.ts:158-158 (schema)TypeScript input type for authenticate tool, inferred from Zod schema.export type AuthenticateInput = z.infer<typeof AuthenticateZodSchema>;
- src/headless-auth.ts:26-194 (helper)Supporting headless authentication utility called by main handler in headless mode. Uses Playwright to automate browser login.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}` }; } }