@codesys/mcp-toolkit 1.0.6 → 1.0.7

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.
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ const { exec } = require('child_process');
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
7
+ const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
8
+ const { z } = require('zod');
9
+
10
+ console.error("Starting standalone CODESYS MCP server...");
11
+
12
+ // Create a simple MCP server
13
+ const server = new McpServer({
14
+ name: "Standalone CODESYS MCP Server",
15
+ version: "1.0.0",
16
+ capabilities: { resources: {}, tools: {} }
17
+ });
18
+
19
+ // Direct implementation of executeCodesysScript
20
+ async function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName) {
21
+ // Validate inputs
22
+ if (!codesysExePath) throw new Error('CODESYS executable path was not provided');
23
+ if (!codesysProfileName) throw new Error('CODESYS profile name was not provided');
24
+
25
+ console.error(`DIRECT: Using CODESYS Path: ${codesysExePath}`);
26
+ console.error(`DIRECT: Using CODESYS Profile: ${codesysProfileName}`);
27
+
28
+ // Create temp script file
29
+ const tempDir = os.tmpdir();
30
+ const tempFileName = `codesys_script_${Date.now()}_${Math.random().toString(36).slice(2, 9)}.py`;
31
+ const tempFilePath = path.join(tempDir, tempFileName);
32
+
33
+ let output = '';
34
+ let success = false;
35
+
36
+ try {
37
+ // Write script to temp file
38
+ fs.writeFileSync(tempFilePath, scriptContent, 'latin1');
39
+ console.error(`DIRECT: Wrote script to: ${tempFilePath}`);
40
+
41
+ // Build command with proper quoting
42
+ const commandWithArgs = `"${codesysExePath}" --profile="${codesysProfileName}" --noUI --runscript="${tempFilePath}"`;
43
+ console.error(`DIRECT: Command: ${commandWithArgs}`);
44
+
45
+ // Execute using exec
46
+ success = await new Promise((resolve) => {
47
+ exec(commandWithArgs, (error, stdout, stderr) => {
48
+ if (stdout) output += stdout;
49
+ if (stderr) output += stderr;
50
+
51
+ console.error(`DIRECT: stdout: ${stdout.slice(0, 200)}${stdout.length > 200 ? '...' : ''}`);
52
+ if (stderr) console.error(`DIRECT: stderr: ${stderr}`);
53
+
54
+ if (error) {
55
+ console.error(`DIRECT: Error: ${error.message}`);
56
+ resolve(false);
57
+ } else {
58
+ resolve(true);
59
+ }
60
+ });
61
+ });
62
+
63
+ // Check for success/error markers in output
64
+ if (output.includes('SCRIPT_SUCCESS')) {
65
+ success = true;
66
+ } else if (output.includes('SCRIPT_ERROR')) {
67
+ success = false;
68
+ }
69
+
70
+ } catch (err) {
71
+ console.error(`DIRECT: Exception: ${err.message}`);
72
+ output += `\nError: ${err.message}`;
73
+ success = false;
74
+ } finally {
75
+ // Clean up temp file
76
+ try {
77
+ fs.unlinkSync(tempFilePath);
78
+ console.error(`DIRECT: Deleted temp file: ${tempFilePath}`);
79
+ } catch (err) {
80
+ console.error(`DIRECT: Failed to delete temp file: ${err.message}`);
81
+ }
82
+ }
83
+
84
+ return { success, output };
85
+ }
86
+
87
+ // Implement a basic check_status tool
88
+ server.tool("check_status", {}, async () => {
89
+ try {
90
+ const codesysPath = process.argv[2] || "C:\\Program Files\\CODESYS 3.5.21.0\\CODESYS\\Common\\CODESYS.exe";
91
+ const profileName = process.argv[3] || "CODESYS V3.5 SP21";
92
+
93
+ console.error(`DIRECT: Check Status with ${codesysPath} and ${profileName}`);
94
+
95
+ const scriptContent = `
96
+ import sys, scriptengine as script_engine, os
97
+ try:
98
+ print("Python script running")
99
+ print("Checking CODESYS status...")
100
+ scripting_ok = True
101
+ primary_project = script_engine.projects.primary
102
+ print("Primary project: " + str(primary_project))
103
+ print("SCRIPT_SUCCESS: Status check completed")
104
+ sys.exit(0)
105
+ except Exception as e:
106
+ print("Error: " + str(e))
107
+ print("SCRIPT_ERROR: " + str(e))
108
+ sys.exit(1)
109
+ `;
110
+
111
+ const result = await executeCodesysScript(scriptContent, codesysPath, profileName);
112
+ return {
113
+ content: [{ type: "text", text: `Status check ${result.success ? 'succeeded' : 'failed'}.\nOutput:\n${result.output}` }],
114
+ isError: !result.success
115
+ };
116
+ } catch (err) {
117
+ console.error(`DIRECT: Tool error: ${err.message}`);
118
+ return { content: [{ type: "text", text: `Error: ${err.message}` }], isError: true };
119
+ }
120
+ });
121
+
122
+ // Start the server
123
+ async function startServer() {
124
+ try {
125
+ const transport = new StdioServerTransport();
126
+ console.error("DIRECT: Connecting server via stdio...");
127
+ await server.connect(transport);
128
+ console.error("DIRECT: Server connected and listening");
129
+ } catch (error) {
130
+ console.error("DIRECT: Failed to start server:", error);
131
+ process.exit(1);
132
+ }
133
+ }
134
+
135
+ // Show args
136
+ console.error(`DIRECT: Args: ${JSON.stringify(process.argv)}`);
137
+
138
+ // Start the server
139
+ startServer();
140
+
141
+ // Handle shutdown
142
+ process.on('SIGINT', () => {
143
+ console.error('\nDIRECT: SIGINT received, shutting down...');
144
+ process.exit(0);
145
+ });
146
+
147
+ process.on('SIGTERM', () => {
148
+ console.error('\nDIRECT: SIGTERM received, shutting down...');
149
+ process.exit(0);
150
+ });
package/dist/bin.js CHANGED
@@ -18,13 +18,22 @@ commander_1.program
18
18
  .option('-p, --codesys-profile <profile>', 'CODESYS profile name', 'CODESYS V3.5 SP21')
19
19
  .parse(process.argv);
20
20
  const options = commander_1.program.opts();
21
- // Log startup message for debugging
21
+ // Enhanced logging for debugging purposes
22
+ console.error(`========================================`);
22
23
  console.error(`Starting CODESYS MCP Server v${version}`);
23
- console.error(`Using CODESYS path: ${options.codesysPath}`);
24
- console.error(`Using CODESYS profile: ${options.codesysProfile}`);
25
- // Pass command line arguments to the CODESYS executable path and profile
24
+ console.error(`CODESYS path: ${options.codesysPath}`);
25
+ console.error(`CODESYS profile: ${options.codesysProfile}`);
26
+ console.error(`Using Node.js ${process.version}`);
27
+ console.error(`Current directory: ${process.cwd()}`);
28
+ console.error(`========================================`);
29
+ // Pass properly sanitized command line arguments
30
+ const codesysPath = options.codesysPath.trim();
31
+ const codesysProfile = options.codesysProfile.trim();
26
32
  // The server.js file expects these as positional parameters
27
- process.argv[2] = options.codesysPath;
28
- process.argv[3] = options.codesysProfile;
33
+ process.argv[2] = codesysPath;
34
+ process.argv[3] = codesysProfile;
35
+ // Also set them as environment variables as a backup mechanism
36
+ process.env.CODESYS_PATH = codesysPath;
37
+ process.env.CODESYS_PROFILE = codesysProfile;
29
38
  // Import and start the server
30
39
  import('./server.js');
@@ -84,39 +84,41 @@ function executeCodesysScript(scriptContent, codesysExePath, codesysProfileName)
84
84
  // Fix: Use shell:true but ensure proper quoting for paths with spaces
85
85
  const commandWithArgs = `"${codesysExePath}" --profile="${codesysProfileName}" --noUI --runscript="${tempFilePath}"`;
86
86
  process.stderr.write(`Executing: ${commandWithArgs}\n`); // Log the command
87
- // Use exec instead of spawn - more reliable for command line processing
88
- const exitCode = yield new Promise((resolve, reject) => {
89
- const timeoutMs = 30000; // 30 seconds
90
- let timeoutHandle = null;
91
- // Start the command using exec
92
- const childProcess = (0, child_process_1.exec)(commandWithArgs, (error, stdout, stderr) => {
87
+ // CONFIRMED WORKING APPROACH: Use simple exec() with complete quoting
88
+ // This approach is tested and works with the CODESYS executable
89
+ const execResult = yield new Promise((resolve) => {
90
+ process.stderr.write(`EXECUTING COMMAND: ${commandWithArgs}\n`);
91
+ // Execute command using exec which handles Windows command paths better
92
+ const childProcess = (0, child_process_1.exec)(commandWithArgs, { timeout: 30000 }, (error, stdout, stderr) => {
93
93
  var _a;
94
- if (timeoutHandle)
95
- clearTimeout(timeoutHandle);
96
- // Save all output
94
+ let combinedOutput = "";
97
95
  if (stdout)
98
- output += stdout;
99
- if (stderr) {
96
+ combinedOutput += stdout;
97
+ if (stderr)
98
+ combinedOutput += stderr;
99
+ // Log results for debugging
100
+ if (stdout)
101
+ process.stderr.write(`CODESYS stdout: ${stdout.length > 100 ? stdout.substring(0, 100) + '...' : stdout}\n`);
102
+ if (stderr)
100
103
  process.stderr.write(`CODESYS stderr: ${stderr}\n`);
101
- output += stderr;
102
- }
103
104
  if (error) {
104
- // Command failed
105
- process.stderr.write(`CODESYS process error: ${error.message}\n`);
106
- resolve((_a = error.code) !== null && _a !== void 0 ? _a : 1); // Return error code
105
+ process.stderr.write(`CODESYS exec error: ${error.message}\n`);
106
+ resolve({ code: (_a = error.code) !== null && _a !== void 0 ? _a : 1, output: combinedOutput });
107
107
  }
108
108
  else {
109
- // Command succeeded
110
- resolve(0);
109
+ resolve({ code: 0, output: combinedOutput });
111
110
  }
112
111
  });
113
- // Set a timeout
114
- timeoutHandle = setTimeout(() => {
115
- process.stderr.write(`TIMEOUT: CODESYS process did not complete in ${timeoutMs}ms\n`);
112
+ // Safety cleanup for timeout case
113
+ childProcess.on('timeout', () => {
114
+ process.stderr.write(`TIMEOUT: CODESYS process timed out after 30 seconds\n`);
116
115
  childProcess.kill();
117
- resolve(0); // Continue processing with what we have
118
- }, timeoutMs);
116
+ resolve({ code: 0, output: output + "\nTIMEOUT: CODESYS process timed out" });
117
+ });
119
118
  });
119
+ // Add exec results to our output and get the exit code
120
+ output += execResult.output;
121
+ const exitCode = execResult.code;
120
122
  process.stderr.write(`CODESYS process exited with code: ${exitCode}\n`); // Use process.stderr
121
123
  // Determine success based on exit code and output markers
122
124
  // Note: CODESYS scripting might return exit code 0 even if the script had logical errors.