@codesys/mcp-toolkit 1.4.1 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4.2] - 2025-05-02
9
+ ### Fixed
10
+ - Enhanced session communication with better logging and debugging
11
+ - Improved command file handling and error recovery
12
+ - Fixed path handling for session files
13
+ - Added JSON fallback parser for IronPython 2.7
14
+ - Ensured synchronous script execution to prevent conflicts
15
+ - Increased reliability of session status detection
16
+
8
17
  ## [1.4.1] - 2025-05-02
9
18
  ### Fixed
10
19
  - Fixed IronPython 2.7 compatibility for session management scripts
@@ -6,7 +6,56 @@ try:
6
6
  import json
7
7
  except ImportError:
8
8
  # For IronPython 2.7, we need to use simplejson instead
9
- import simplejson as json
9
+ try:
10
+ import simplejson as json
11
+ except ImportError:
12
+ print("Neither json nor simplejson could be imported. Check IronPython installation.")
13
+ # Provide a minimal fallback implementation for basic needs
14
+ class DummyJson:
15
+ @staticmethod
16
+ def load(file_obj):
17
+ content = file_obj.read()
18
+ # Very basic parsing for our limited needs
19
+ if content.strip().startswith("{") and content.strip().endswith("}"):
20
+ result = {}
21
+ parts = content.strip()[1:-1].split(",")
22
+ for part in parts:
23
+ if ":" in part:
24
+ k, v = part.split(":", 1)
25
+ k = k.strip().strip('"\'')
26
+ v = v.strip().strip('"\'')
27
+ result[k] = v
28
+ return result
29
+ return {}
30
+
31
+ @staticmethod
32
+ def loads(s):
33
+ if s.strip().startswith("{") and s.strip().endswith("}"):
34
+ result = {}
35
+ parts = s.strip()[1:-1].split(",")
36
+ for part in parts:
37
+ if ":" in part:
38
+ k, v = part.split(":", 1)
39
+ k = k.strip().strip('"\'')
40
+ v = v.strip().strip('"\'')
41
+ result[k] = v
42
+ return result
43
+ return {}
44
+
45
+ @staticmethod
46
+ def dump(obj, file_obj, indent=None):
47
+ if isinstance(obj, dict):
48
+ pairs = []
49
+ for k, v in obj.items():
50
+ if isinstance(v, str):
51
+ pairs.append('"{0}": "{1}"'.format(k, v))
52
+ else:
53
+ pairs.append('"{0}": {1}'.format(k, v))
54
+ file_obj.write("{" + ", ".join(pairs) + "}")
55
+ else:
56
+ file_obj.write(str(obj))
57
+
58
+ json = DummyJson()
10
59
 
11
60
  # Define markers for script results
12
61
  SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
@@ -19,6 +68,15 @@ COMMAND_FILE = os.path.join(SESSION_DIR, "command.json")
19
68
  RESULT_FILE = os.path.join(SESSION_DIR, "result.json")
20
69
  SESSION_LOCK_FILE = os.path.join(SESSION_DIR, "session.lock")
21
70
 
71
+ # Print key information at start
72
+ print("Session status check script starting")
73
+ print("tempdir: {0}".format(tempfile.gettempdir()))
74
+ print("SESSION_DIR: {0}".format(SESSION_DIR))
75
+ print("SESSION_STATE_FILE: {0}".format(SESSION_STATE_FILE))
76
+ print("COMMAND_FILE: {0}".format(COMMAND_FILE))
77
+ print("RESULT_FILE: {0}".format(RESULT_FILE))
78
+ print("SESSION_LOCK_FILE: {0}".format(SESSION_LOCK_FILE))
79
+
22
80
  def check_session_status():
23
81
  """Check the status of a CODESYS session"""
24
82
  try:
@@ -5,16 +5,71 @@ try:
5
5
  import json
6
6
  except ImportError:
7
7
  # For IronPython 2.7, we need to use simplejson instead
8
- import simplejson as json
8
+ try:
9
+ import simplejson as json
10
+ except ImportError:
11
+ print("Neither json nor simplejson could be imported. Check IronPython installation.")
12
+ # Provide a minimal fallback implementation for basic needs
13
+ class DummyJson:
14
+ @staticmethod
15
+ def load(file_obj):
16
+ content = file_obj.read()
17
+ # Very basic parsing for our limited needs
18
+ if content.strip().startswith("{") and content.strip().endswith("}"):
19
+ result = {}
20
+ parts = content.strip()[1:-1].split(",")
21
+ for part in parts:
22
+ if ":" in part:
23
+ k, v = part.split(":", 1)
24
+ k = k.strip().strip('"\'')
25
+ v = v.strip().strip('"\'')
26
+ result[k] = v
27
+ return result
28
+ return {}
29
+
30
+ @staticmethod
31
+ def loads(s):
32
+ if s.strip().startswith("{") and s.strip().endswith("}"):
33
+ result = {}
34
+ parts = s.strip()[1:-1].split(",")
35
+ for part in parts:
36
+ if ":" in part:
37
+ k, v = part.split(":", 1)
38
+ k = k.strip().strip('"\'')
39
+ v = v.strip().strip('"\'')
40
+ result[k] = v
41
+ return result
42
+ return {}
43
+
44
+ @staticmethod
45
+ def dump(obj, file_obj, indent=None):
46
+ if isinstance(obj, dict):
47
+ pairs = []
48
+ for k, v in obj.items():
49
+ if isinstance(v, str):
50
+ pairs.append('"{0}": "{1}"'.format(k, v))
51
+ else:
52
+ pairs.append('"{0}": {1}'.format(k, v))
53
+ file_obj.write("{" + ", ".join(pairs) + "}")
54
+ else:
55
+ file_obj.write(str(obj))
56
+
57
+ json = DummyJson()
9
58
 
10
59
  # Import scriptengine
11
60
  try:
12
61
  import scriptengine as script_engine
62
+ print("Successfully imported scriptengine module")
13
63
  except ImportError:
14
64
  # Log the error and continue - we'll handle this gracefully
15
65
  print("Error importing scriptengine. This script must be run within CODESYS environment.")
16
66
  script_engine = None
17
67
 
68
+ # Print Python and platform info
69
+ print("Python version: {0}.{1}.{2}".format(sys.version_info[0], sys.version_info[1], sys.version_info[2]))
70
+ print("Platform: {0}".format(sys.platform))
71
+ print("Executable: {0}".format(sys.executable))
72
+
18
73
  # Define markers for script results
19
74
  SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
20
75
  SCRIPT_ERROR_MARKER = "SCRIPT_ERROR"
@@ -255,67 +310,128 @@ def check_command():
255
310
  try:
256
311
  # If command file doesn't exist, return immediately
257
312
  if not os.path.exists(COMMAND_FILE):
313
+ print("Command file does not exist at {0}".format(COMMAND_FILE))
258
314
  return False
259
315
 
260
316
  # Read the command file
261
317
  try:
318
+ print("Reading command file: {0}".format(COMMAND_FILE))
262
319
  with open(COMMAND_FILE, 'r') as f:
263
- command_data = json.load(f)
264
- except json.JSONDecodeError:
265
- # If command file is corrupted, reset it
320
+ file_content = f.read()
321
+ print("Raw command file content: {0}".format(file_content[:100] if len(file_content) > 100 else file_content))
322
+
323
+ # Check if file is empty
324
+ if not file_content.strip():
325
+ print("Command file is empty, resetting")
326
+ command_data = {"status": "ready", "command": None, "timestamp": time.time()}
327
+ with open(COMMAND_FILE, 'w') as fw:
328
+ json.dump(command_data, fw, indent=2)
329
+ return False
330
+
331
+ # Try to parse JSON
332
+ try:
333
+ command_data = json.loads(file_content)
334
+ except Exception as json_err:
335
+ print("Error parsing command file JSON: {0}".format(json_err))
336
+ # If command file is corrupted, reset it
337
+ command_data = {"status": "ready", "command": None, "timestamp": time.time()}
338
+ with open(COMMAND_FILE, 'w') as fw:
339
+ json.dump(command_data, fw, indent=2)
340
+ return False
341
+ except Exception as read_err:
342
+ print("Error reading command file: {0}".format(read_err))
343
+ # Reset the command file
266
344
  command_data = {"status": "ready", "command": None, "timestamp": time.time()}
267
- with open(COMMAND_FILE, 'w') as f:
268
- json.dump(command_data, f, indent=2)
345
+ try:
346
+ with open(COMMAND_FILE, 'w') as fw:
347
+ json.dump(command_data, fw, indent=2)
348
+ except Exception as write_err:
349
+ print("Error writing reset command file: {0}".format(write_err))
269
350
  return False
270
351
 
271
352
  # Check if there's a new command
272
- if command_data.get("status") == "pending" and command_data.get("command"):
273
- # Extract the command
274
- command = command_data.get("command")
275
-
276
- # Mark as processing
277
- command_data["status"] = "processing"
278
- with open(COMMAND_FILE, 'w') as f:
279
- json.dump(command_data, f, indent=2)
280
-
281
- # Update session state to busy
282
- update_session_state("busy")
283
-
284
- # Execute the command
285
- result, stop_requested = execute_command(command)
286
-
287
- # Write the result
288
- result_data = {
289
- "status": "complete",
290
- "result": result,
291
- "timestamp": time.time()
292
- }
293
-
294
- with open(RESULT_FILE, 'w') as f:
295
- json.dump(result_data, f, indent=2)
296
-
297
- # Reset command file
298
- command_data = {
299
- "status": "ready",
300
- "command": None,
301
- "timestamp": time.time()
302
- }
303
-
304
- with open(COMMAND_FILE, 'w') as f:
305
- json.dump(command_data, f, indent=2)
306
-
307
- # Update session state to running again
308
- if not stop_requested:
309
- update_session_state("running")
353
+ status = command_data.get("status")
354
+ command = command_data.get("command")
355
+
356
+ print("Command status: {0}".format(status))
357
+ if command:
358
+ print("Command found: {0}".format(command.get("type", "unknown")))
310
359
 
311
- # Update last activity time
312
- update_last_activity()
360
+ if status == "pending" and command:
361
+ print("Processing pending command...")
313
362
 
314
- return stop_requested
363
+ try:
364
+ # Mark as processing
365
+ command_data["status"] = "processing"
366
+ with open(COMMAND_FILE, 'w') as f:
367
+ json.dump(command_data, f, indent=2)
368
+ print("Updated command status to 'processing'")
369
+
370
+ # Update session state to busy
371
+ update_session_state("busy")
372
+
373
+ # Execute the command
374
+ print("Executing command: {0}".format(command.get("type", "unknown")))
375
+ result, stop_requested = execute_command(command)
376
+ print("Command execution complete, stop_requested: {0}".format(stop_requested))
377
+
378
+ # Write the result
379
+ result_data = {
380
+ "status": "complete",
381
+ "result": result,
382
+ "timestamp": time.time()
383
+ }
384
+
385
+ print("Writing result to {0}".format(RESULT_FILE))
386
+ with open(RESULT_FILE, 'w') as f:
387
+ json.dump(result_data, f, indent=2)
388
+
389
+ # Reset command file
390
+ command_data = {
391
+ "status": "ready",
392
+ "command": None,
393
+ "timestamp": time.time()
394
+ }
395
+
396
+ print("Resetting command file")
397
+ with open(COMMAND_FILE, 'w') as f:
398
+ json.dump(command_data, f, indent=2)
399
+
400
+ # Update session state to running again
401
+ if not stop_requested:
402
+ update_session_state("running")
403
+
404
+ # Update last activity time
405
+ update_last_activity()
406
+
407
+ return stop_requested
408
+ except Exception as cmd_err:
409
+ print("Error processing command: {0}".format(cmd_err))
410
+ traceback.print_exc()
411
+
412
+ # Try to reset the command file
413
+ try:
414
+ command_data = {
415
+ "status": "ready",
416
+ "command": None,
417
+ "timestamp": time.time()
418
+ }
419
+ with open(COMMAND_FILE, 'w') as f:
420
+ json.dump(command_data, f, indent=2)
421
+ except Exception as reset_err:
422
+ print("Error resetting command file: {0}".format(reset_err))
423
+
424
+ # Try to restore session state
425
+ try:
426
+ update_session_state("running")
427
+ except Exception as state_err:
428
+ print("Error updating session state: {0}".format(state_err))
429
+
430
+ return False
315
431
 
316
432
  return False
317
433
  except Exception as e:
318
- print("Error checking command: {0}".format(e))
434
+ print("Error in check_command: {0}".format(e))
319
435
  traceback.print_exc()
320
436
  return False
321
437
 
@@ -356,19 +472,61 @@ def command_loop():
356
472
  """Main loop to check for and execute commands"""
357
473
  stop_requested = False
358
474
  last_state_update = time.time()
475
+ loop_count = 0
476
+ last_heartbeat = time.time()
359
477
 
360
478
  print("Starting command loop...")
361
479
  print("Command file: {0}".format(COMMAND_FILE))
362
480
  print("Result file: {0}".format(RESULT_FILE))
481
+ print("Session lock file: {0}".format(SESSION_LOCK_FILE))
482
+ print("Session state file: {0}".format(SESSION_STATE_FILE))
483
+ print("Poll interval: {0} seconds".format(POLL_INTERVAL))
484
+ print("Max inactivity time: {0} seconds".format(MAX_INACTIVITY_TIME))
485
+
486
+ # Set up initial session state files
487
+ try:
488
+ # Create initial command file if it doesn't exist
489
+ if not os.path.exists(COMMAND_FILE):
490
+ with open(COMMAND_FILE, 'w') as f:
491
+ json.dump({
492
+ "status": "ready",
493
+ "command": None,
494
+ "timestamp": time.time()
495
+ }, f, indent=2)
496
+ print("Created initial command file")
497
+
498
+ # Create initial result file if it doesn't exist
499
+ if not os.path.exists(RESULT_FILE):
500
+ with open(RESULT_FILE, 'w') as f:
501
+ json.dump({
502
+ "status": "none",
503
+ "result": None,
504
+ "timestamp": time.time()
505
+ }, f, indent=2)
506
+ print("Created initial result file")
507
+ except Exception as setup_err:
508
+ print("Error setting up initial files: {0}".format(setup_err))
509
+ traceback.print_exc()
363
510
 
364
511
  while not stop_requested:
365
512
  try:
513
+ loop_count += 1
514
+ current_time = time.time()
515
+
366
516
  # Update session lock file to indicate we're alive
367
517
  with open(SESSION_LOCK_FILE, 'w') as f:
368
- f.write(str(time.time()))
518
+ f.write(str(current_time))
519
+
520
+ # Print heartbeat message every 60 seconds
521
+ if current_time - last_heartbeat > 60:
522
+ print("Command loop heartbeat - still alive (iteration {0})".format(loop_count))
523
+ last_heartbeat = current_time
369
524
 
370
525
  # Check for new commands
526
+ print("Checking for commands...")
371
527
  stop_requested = check_command()
528
+ if stop_requested:
529
+ print("Stop was requested by a command")
372
530
 
373
531
  # Check for inactivity timeout
374
532
  if check_inactivity_timeout():
@@ -377,8 +535,8 @@ def command_loop():
377
535
  stop_requested = True
378
536
 
379
537
  # Periodically update session state (every 30 seconds)
380
- current_time = time.time()
381
538
  if (current_time - last_state_update) > 30:
539
+ print("Periodic session state update")
382
540
  update_session_state()
383
541
  last_state_update = current_time
384
542
 
@@ -388,10 +546,10 @@ def command_loop():
388
546
  except Exception as e:
389
547
  print("Error in command loop: {0}".format(e))
390
548
  traceback.print_exc()
391
- # Sleep for a bit
549
+ # Sleep for a bit before retrying
392
550
  time.sleep(POLL_INTERVAL)
393
551
 
394
- print("Command loop stopped.")
552
+ print("Command loop stopped after {0} iterations".format(loop_count))
395
553
 
396
554
  # Main script
397
555
  def main():
@@ -6,7 +6,61 @@ try:
6
6
  import json
7
7
  except ImportError:
8
8
  # For IronPython 2.7, we need to use simplejson instead
9
- import simplejson as json
9
+ try:
10
+ import simplejson as json
11
+ except ImportError:
12
+ print("Neither json nor simplejson could be imported. Check IronPython installation.")
13
+ # Provide a minimal fallback implementation for basic needs
14
+ class DummyJson:
15
+ @staticmethod
16
+ def load(file_obj):
17
+ content = file_obj.read()
18
+ # Very basic parsing for our limited needs
19
+ if content.strip().startswith("{") and content.strip().endswith("}"):
20
+ result = {}
21
+ parts = content.strip()[1:-1].split(",")
22
+ for part in parts:
23
+ if ":" in part:
24
+ k, v = part.split(":", 1)
25
+ k = k.strip().strip('"\'')
26
+ v = v.strip().strip('"\'')
27
+ result[k] = v
28
+ return result
29
+ return {}
30
+
31
+ @staticmethod
32
+ def loads(s):
33
+ if s.strip().startswith("{") and s.strip().endswith("}"):
34
+ result = {}
35
+ parts = s.strip()[1:-1].split(",")
36
+ for part in parts:
37
+ if ":" in part:
38
+ k, v = part.split(":", 1)
39
+ k = k.strip().strip('"\'')
40
+ v = v.strip().strip('"\'')
41
+ result[k] = v
42
+ return result
43
+ return {}
44
+
45
+ @staticmethod
46
+ def dump(obj, file_obj, indent=None):
47
+ if isinstance(obj, dict):
48
+ pairs = []
49
+ for k, v in obj.items():
50
+ if isinstance(v, str):
51
+ pairs.append('"{0}": "{1}"'.format(k, v))
52
+ else:
53
+ pairs.append('"{0}": {1}'.format(k, v))
54
+ file_obj.write("{" + ", ".join(pairs) + "}")
55
+ else:
56
+ file_obj.write(str(obj))
57
+
58
+ json = DummyJson()
59
+
60
+ # Print Python and platform info
61
+ print("Python version: {0}.{1}.{2}".format(sys.version_info[0], sys.version_info[1], sys.version_info[2]))
62
+ print("Platform: {0}".format(sys.platform))
63
+ print("Executable: {0}".format(sys.executable))
10
64
 
11
65
  # Define markers for script results
12
66
  SCRIPT_SUCCESS_MARKER = "SCRIPT_SUCCESS"
@@ -19,6 +73,15 @@ COMMAND_FILE = os.path.join(SESSION_DIR, "command.json")
19
73
  RESULT_FILE = os.path.join(SESSION_DIR, "result.json")
20
74
  SESSION_LOCK_FILE = os.path.join(SESSION_DIR, "session.lock")
21
75
 
76
+ # Print key information at start
77
+ print("Session stop script starting")
78
+ print("tempdir: {0}".format(tempfile.gettempdir()))
79
+ print("SESSION_DIR: {0}".format(SESSION_DIR))
80
+ print("SESSION_STATE_FILE: {0}".format(SESSION_STATE_FILE))
81
+ print("COMMAND_FILE: {0}".format(COMMAND_FILE))
82
+ print("RESULT_FILE: {0}".format(RESULT_FILE))
83
+ print("SESSION_LOCK_FILE: {0}".format(SESSION_LOCK_FILE))
84
+
22
85
  def stop_session():
23
86
  """Stop a running CODESYS session"""
24
87
  try:
@@ -122,7 +122,8 @@ class CodesysSessionManager extends events_1.EventEmitter {
122
122
  this.idleTimeoutMs = idleTimeoutMs;
123
123
  this.sessionId = (0, uuid_1.v4)();
124
124
  // Set up session directory and files
125
- this.sessionDir = path.join(SESSION_DIR, this.sessionId);
125
+ // IMPORTANT: Don't use a subdirectory - Python scripts expect files directly in SESSION_DIR
126
+ this.sessionDir = SESSION_DIR;
126
127
  this.commandFilePath = path.join(this.sessionDir, COMMAND_FILE);
127
128
  this.resultFilePath = path.join(this.sessionDir, RESULT_FILE);
128
129
  this.stateFilePath = path.join(this.sessionDir, STATE_FILE);
@@ -442,16 +443,27 @@ class CodesysSessionManager extends events_1.EventEmitter {
442
443
  }, 2000); // Give it 2 seconds to terminate gracefully
443
444
  });
444
445
  }
445
- // Clean up session directory
446
+ // Clean up session files (not entire directory)
446
447
  try {
447
448
  if (fs.existsSync(this.sessionDir)) {
448
- console.error(`SESSION_MANAGER: Cleaning up session directory: ${this.sessionDir}`);
449
- // Use recursive: true once we're on Node.js 14+
450
- this.recursiveRemoveDir(this.sessionDir);
449
+ console.error(`SESSION_MANAGER: Cleaning up session files in: ${this.sessionDir}`);
450
+ // Remove only the session-related files, not the entire directory
451
+ const filesToRemove = [
452
+ this.commandFilePath,
453
+ this.resultFilePath,
454
+ this.stateFilePath,
455
+ this.lockFilePath
456
+ ];
457
+ for (const filePath of filesToRemove) {
458
+ if (fs.existsSync(filePath)) {
459
+ fs.unlinkSync(filePath);
460
+ console.error(`SESSION_MANAGER: Removed session file: ${filePath}`);
461
+ }
462
+ }
451
463
  }
452
464
  }
453
465
  catch (error) {
454
- console.error(`SESSION_MANAGER: Error cleaning up session directory: ${error}`);
466
+ console.error(`SESSION_MANAGER: Error cleaning up session files: ${error}`);
455
467
  }
456
468
  this.codesysProcess = null;
457
469
  this.sessionState = exports.SessionState.STOPPED;
@@ -524,8 +536,6 @@ class CodesysSessionManager extends events_1.EventEmitter {
524
536
  }
525
537
  // Reset idle timeout since we're about to do work
526
538
  this.resetIdleTimeout();
527
- // Mark the session as busy
528
- this.sessionState = exports.SessionState.BUSY;
529
539
  // Check if the command file exists
530
540
  if (!fs.existsSync(this.commandFilePath)) {
531
541
  this.sessionState = exports.SessionState.ERROR;
@@ -536,6 +546,50 @@ class CodesysSessionManager extends events_1.EventEmitter {
536
546
  this.sessionState = exports.SessionState.ERROR;
537
547
  throw new Error(`Result file not found at ${this.resultFilePath}`);
538
548
  }
549
+ // IMPORTANT: Ensure only one script is executed at a time by checking if other scripts are pending
550
+ if (this.pendingScripts.size > 0) {
551
+ console.error(`SESSION_MANAGER: Waiting for ${this.pendingScripts.size} pending scripts to complete before executing new script`);
552
+ // Wait for all pending scripts to complete
553
+ yield new Promise((resolve) => {
554
+ const checkInterval = setInterval(() => {
555
+ if (this.pendingScripts.size === 0) {
556
+ clearInterval(checkInterval);
557
+ resolve();
558
+ }
559
+ }, 100);
560
+ });
561
+ console.error('SESSION_MANAGER: All pending scripts completed, proceeding with new script execution');
562
+ }
563
+ // Mark the session as busy
564
+ this.sessionState = exports.SessionState.BUSY;
565
+ // Read current command file to ensure we're not overwriting an in-progress command
566
+ try {
567
+ const currentCommandData = JSON.parse(fs.readFileSync(this.commandFilePath, 'utf8'));
568
+ if (currentCommandData.status === 'pending' || currentCommandData.status === 'processing') {
569
+ console.error(`SESSION_MANAGER: Found pending command in file, waiting for it to complete`);
570
+ // Wait for the command to complete (status to be 'ready')
571
+ yield new Promise((resolve) => {
572
+ const checkInterval = setInterval(() => {
573
+ try {
574
+ const checkData = JSON.parse(fs.readFileSync(this.commandFilePath, 'utf8'));
575
+ if (checkData.status === 'ready') {
576
+ clearInterval(checkInterval);
577
+ resolve();
578
+ }
579
+ }
580
+ catch (err) {
581
+ // If we can't read the file, just continue
582
+ console.error(`SESSION_MANAGER: Error reading command file during wait: ${err}`);
583
+ }
584
+ }, 100);
585
+ });
586
+ console.error('SESSION_MANAGER: Previous command completed, proceeding with new command');
587
+ }
588
+ }
589
+ catch (err) {
590
+ console.error(`SESSION_MANAGER: Error reading current command file: ${err}`);
591
+ // If we can't read the file, we'll try to proceed anyway
592
+ }
539
593
  // Return a promise that will be resolved when script execution completes
540
594
  return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
541
595
  try {
@@ -584,7 +638,8 @@ class CodesysSessionManager extends events_1.EventEmitter {
584
638
  timestamp: Date.now()
585
639
  };
586
640
  // Send the command by writing to the command file
587
- fs.writeFileSync(this.commandFilePath, JSON.stringify(commandData));
641
+ console.error(`SESSION_MANAGER: Writing command to file ${this.commandFilePath}`);
642
+ fs.writeFileSync(this.commandFilePath, JSON.stringify(commandData, null, 2));
588
643
  console.error(`SESSION_MANAGER: Command written to file`);
589
644
  // Start reading the result file
590
645
  this.readResultFile(scriptId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codesys/mcp-toolkit",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Model Context Protocol (MCP) server for CODESYS automation platform",
5
5
  "main": "dist/server.js",
6
6
  "bin": {