@@ -12,19 +12,19 @@ import { Logger } from "./logger.js";
1212function parseArgs ( ) : z . infer < typeof configSchema > {
1313 const args : any = { } ;
1414 const argv = process . argv . slice ( 2 ) ;
15-
15+
1616 // Log raw arguments for debugging
1717 Logger . info ( `🔍 Raw command line arguments: ${ JSON . stringify ( argv ) } ` ) ;
18-
18+
1919 for ( let i = 0 ; i < argv . length ; i ++ ) {
2020 const arg = argv [ i ] ;
21-
21+
2222 if ( arg . startsWith ( '--' ) ) {
2323 const key = arg . slice ( 2 ) ;
2424 const nextArg = argv [ i + 1 ] ;
25-
25+
2626 Logger . debug ( `🔍 Processing flag: ${ key } , nextArg: ${ nextArg } ` ) ;
27-
27+
2828 switch ( key ) {
2929 case 'ib-username' :
3030 args . IB_USERNAME = nextArg ;
@@ -63,15 +63,25 @@ function parseArgs(): z.infer<typeof configSchema> {
6363 args . IB_PAPER_TRADING = true ;
6464 Logger . debug ( `🔍 Set IB_PAPER_TRADING to: true (flag only)` ) ;
6565 }
66+ case 'ib-read-only-mode' :
67+ // Support both --ib-read-only-mode (boolean flag) and --ib-read-only-mode=true/false
68+ if ( nextArg && ! nextArg . startsWith ( '--' ) ) {
69+ args . IB_READ_ONLY_MODE = nextArg . toLowerCase ( ) === 'true' ;
70+ Logger . debug ( `🔍 Set IB_READ_ONLY_MODE to: ${ nextArg . toLowerCase ( ) === 'true' } (from arg: ${ nextArg } )` ) ;
71+ i ++ ;
72+ } else {
73+ args . IB_READ_ONLY_MODE = true ;
74+ Logger . debug ( `🔍 Set IB_READ_ONLY_MODE to: true (flag only)` ) ;
75+ }
6676 break ;
6777
6878 }
6979 } else if ( arg . includes ( '=' ) ) {
7080 const [ key , value ] = arg . split ( '=' , 2 ) ;
7181 const cleanKey = key . startsWith ( '--' ) ? key . slice ( 2 ) : key ;
72-
82+
7383 Logger . debug ( `🔍 Processing key=value: ${ cleanKey } =${ value } ` ) ;
74-
84+
7585 switch ( cleanKey ) {
7686 case 'ib-username' :
7787 args . IB_USERNAME = value ;
@@ -94,11 +104,15 @@ function parseArgs(): z.infer<typeof configSchema> {
94104 args . IB_PAPER_TRADING = value . toLowerCase ( ) === 'true' ;
95105 Logger . debug ( `🔍 Set IB_PAPER_TRADING to: ${ value . toLowerCase ( ) === 'true' } (from value: ${ value } )` ) ;
96106 break ;
107+ case 'ib-read-only-mode' :
108+ args . IB_READ_ONLY_MODE = value . toLowerCase ( ) === 'true' ;
109+ Logger . debug ( `🔍 Set IB_READ_ONLY_MODE to: ${ value . toLowerCase ( ) === 'true' } (from value: ${ value } )` ) ;
110+ break ;
97111
98112 }
99113 }
100114 }
101-
115+
102116 Logger . info ( `🔍 Parsed args: ${ JSON . stringify ( args , null , 2 ) } ` ) ;
103117 return args ;
104118}
@@ -110,10 +124,12 @@ export const configSchema = z.object({
110124 IB_PASSWORD_AUTH : z . string ( ) . optional ( ) ,
111125 IB_AUTH_TIMEOUT : z . number ( ) . optional ( ) ,
112126 IB_HEADLESS_MODE : z . boolean ( ) . optional ( ) ,
113-
127+
114128 // Paper trading configuration
115129 IB_PAPER_TRADING : z . boolean ( ) . optional ( ) ,
116130
131+ // Read-only mode configuration
132+ IB_READ_ONLY_MODE : z . boolean ( ) . optional ( ) ,
117133} ) ;
118134
119135// Global gateway manager instance
@@ -123,12 +139,12 @@ let gatewayManager: IBGatewayManager | null = null;
123139async function initializeGateway ( ibClient ?: IBClient ) {
124140 if ( ! gatewayManager ) {
125141 gatewayManager = new IBGatewayManager ( ) ;
126-
142+
127143 try {
128144 Logger . info ( '⚡ Quick Gateway initialization for MCP plugin...' ) ;
129145 await gatewayManager . quickStartGateway ( ) ;
130146 Logger . info ( '✅ Gateway initialization completed (background startup if needed)' ) ;
131-
147+
132148 // Update client port if provided
133149 if ( ibClient ) {
134150 ibClient . updatePort ( gatewayManager . getCurrentPort ( ) ) ;
@@ -147,7 +163,7 @@ async function cleanupAll(signal?: string) {
147163 if ( signal ) {
148164 Logger . info ( `🛑 Received ${ signal } , cleaning up temp files only...` ) ;
149165 }
150-
166+
151167 // Only cleanup temp files - don't shutdown gateway (leave it running for next npx process)
152168 if ( gatewayManager ) {
153169 try {
@@ -169,7 +185,7 @@ const gracefulShutdown = (signal: string) => {
169185 return ; // Silent return to avoid log spam
170186 }
171187 isShuttingDown = true ;
172-
188+
173189 // Don't use async/await here to avoid potential hanging
174190 cleanupAll ( signal ) . finally ( ( ) => {
175191 process . exit ( 0 ) ;
@@ -199,11 +215,11 @@ process.on('unhandledRejection', (reason, promise) => {
199215
200216// Check if this module is being run directly (for stdio compatibility)
201217// This handles direct execution, npx, and bin script execution
202- const isMainModule = import . meta. url === `file://${ process . argv [ 1 ] } ` ||
203- process . argv [ 1 ] ?. endsWith ( 'index.js' ) ||
204- process . argv [ 1 ] ?. endsWith ( 'dist/index.js' ) ||
205- process . argv [ 1 ] ?. endsWith ( 'ib-mcp' ) ||
206- process . argv [ 1 ] ?. includes ( '/.bin/ib-mcp' ) ;
218+ const isMainModule = import . meta. url === `file://${ process . argv [ 1 ] } ` ||
219+ process . argv [ 1 ] ?. endsWith ( 'index.js' ) ||
220+ process . argv [ 1 ] ?. endsWith ( 'dist/index.js' ) ||
221+ process . argv [ 1 ] ?. endsWith ( 'ib-mcp' ) ||
222+ process . argv [ 1 ] ?. includes ( '/.bin/ib-mcp' ) ;
207223
208224function IBMCP ( { config : userConfig } : { config : z . infer < typeof configSchema > } ) {
209225 // Merge user config with environment config
@@ -250,20 +266,20 @@ if (isMainModule) {
250266 // Suppress known problematic outputs that might interfere with JSON-RPC
251267 process . env . SUPPRESS_LOAD_MESSAGE = '1' ;
252268 process . env . NO_UPDATE_NOTIFIER = '1' ;
253-
269+
254270 // Log environment info for debugging MCP plugin issues
255271 Logger . info ( `🔍 Environment: PWD=${ process . cwd ( ) } , NODE_ENV=${ process . env . NODE_ENV || 'undefined' } ` ) ;
256272 Logger . info ( `🔍 Process: npm_execpath=${ process . env . npm_execpath || 'undefined' } , npm_command=${ process . env . npm_command || 'undefined' } ` ) ;
257-
273+
258274 // Check if we're running in npx/MCP plugin context
259275 const isNpx = process . env . npm_execpath ?. includes ( 'npx' ) || process . cwd ( ) . includes ( '.npm' ) ;
260276 if ( isNpx ) {
261277 Logger . info ( '📦 Detected npx execution - likely running via MCP community plugin' ) ;
262278 }
263-
279+
264280 // Log startup information
265281 Logger . logStartup ( ) ;
266-
282+
267283 // Parse command line arguments and merge with environment variables
268284 // Priority: args > env > defaults
269285 const argsConfig = parseArgs ( ) ;
@@ -272,39 +288,40 @@ if (isMainModule) {
272288 IB_PASSWORD_AUTH : process . env . IB_PASSWORD_AUTH || process . env . IB_PASSWORD ,
273289 IB_AUTH_TIMEOUT : process . env . IB_AUTH_TIMEOUT ? parseInt ( process . env . IB_AUTH_TIMEOUT ) : undefined ,
274290 IB_HEADLESS_MODE : process . env . IB_HEADLESS_MODE === 'true' ,
291+ IB_READ_ONLY_MODE : process . env . IB_READ_ONLY_MODE === 'true' ,
275292
276293 } ;
277-
294+
278295 // Log environment config for debugging
279296 const logEnvConfig = { ...envConfig } ;
280297 if ( logEnvConfig . IB_PASSWORD_AUTH ) logEnvConfig . IB_PASSWORD_AUTH = '[REDACTED]' ;
281298 Logger . info ( `🔍 Environment config: ${ JSON . stringify ( logEnvConfig , null , 2 ) } ` ) ;
282-
299+
283300 // Merge configs with priority: args > env > defaults
284301 const finalConfig = {
285302 ...envConfig ,
286303 ...argsConfig ,
287304 } ;
288-
305+
289306 // Log final config before cleanup
290307 const logFinalConfig = { ...finalConfig } ;
291308 if ( logFinalConfig . IB_PASSWORD_AUTH ) logFinalConfig . IB_PASSWORD_AUTH = '[REDACTED]' ;
292309 Logger . info ( `🔍 Final config before cleanup: ${ JSON . stringify ( logFinalConfig , null , 2 ) } ` ) ;
293-
310+
294311 // Remove undefined values
295312 Object . keys ( finalConfig ) . forEach ( key => {
296313 if ( finalConfig [ key as keyof typeof finalConfig ] === undefined ) {
297314 delete finalConfig [ key as keyof typeof finalConfig ] ;
298315 }
299316 } ) ;
300-
317+
301318 // Log final config after cleanup
302319 const logFinalConfigAfter = { ...finalConfig } ;
303320 if ( logFinalConfigAfter . IB_PASSWORD_AUTH ) logFinalConfigAfter . IB_PASSWORD_AUTH = '[REDACTED]' ;
304321 Logger . info ( `🔍 Final config after cleanup: ${ JSON . stringify ( logFinalConfigAfter , null , 2 ) } ` ) ;
305-
322+
306323 const stdioTransport = new StdioServerTransport ( ) ;
307- const server = IBMCP ( { config : finalConfig } )
324+ const server = IBMCP ( { config : finalConfig } )
308325 server . connect ( stdioTransport ) ;
309326}
310327
0 commit comments