@codesys/mcp-toolkit 1.5.7 → 2.0.0-alpha.0

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +153 -183
  2. package/LICENSE +21 -21
  3. package/README.md +224 -107
  4. package/dist/actions.d.ts +28 -0
  5. package/dist/actions.js +98 -0
  6. package/dist/actions.js.map +1 -0
  7. package/dist/bridge/bridge.d.ts +105 -0
  8. package/dist/bridge/bridge.js +400 -0
  9. package/dist/bridge/bridge.js.map +1 -0
  10. package/dist/cli.d.ts +2 -0
  11. package/dist/cli.js +196 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/codesys/cache.d.ts +48 -0
  14. package/dist/codesys/cache.js +82 -0
  15. package/dist/codesys/cache.js.map +1 -0
  16. package/dist/codesys/changes.d.ts +57 -0
  17. package/dist/codesys/changes.js +133 -0
  18. package/dist/codesys/changes.js.map +1 -0
  19. package/dist/codesys/decl.d.ts +45 -0
  20. package/dist/codesys/decl.js +251 -0
  21. package/dist/codesys/decl.js.map +1 -0
  22. package/dist/codesys/devices.d.ts +34 -0
  23. package/dist/codesys/devices.js +82 -0
  24. package/dist/codesys/devices.js.map +1 -0
  25. package/dist/codesys/diff.d.ts +13 -0
  26. package/dist/codesys/diff.js +61 -0
  27. package/dist/codesys/diff.js.map +1 -0
  28. package/dist/codesys/install.d.ts +40 -0
  29. package/dist/codesys/install.js +144 -0
  30. package/dist/codesys/install.js.map +1 -0
  31. package/dist/codesys/library-browse.d.ts +19 -0
  32. package/dist/codesys/library-browse.js +57 -0
  33. package/dist/codesys/library-browse.js.map +1 -0
  34. package/dist/codesys/outline.d.ts +32 -0
  35. package/dist/codesys/outline.js +219 -0
  36. package/dist/codesys/outline.js.map +1 -0
  37. package/dist/codesys/simulation.d.ts +34 -0
  38. package/dist/codesys/simulation.js +57 -0
  39. package/dist/codesys/simulation.js.map +1 -0
  40. package/dist/codesys/st.d.ts +36 -0
  41. package/dist/codesys/st.js +69 -0
  42. package/dist/codesys/st.js.map +1 -0
  43. package/dist/daemon.d.ts +46 -0
  44. package/dist/daemon.js +173 -0
  45. package/dist/daemon.js.map +1 -0
  46. package/dist/driver.d.ts +25 -0
  47. package/dist/driver.js +33 -0
  48. package/dist/driver.js.map +1 -0
  49. package/dist/panel/sim-panel.html +357 -0
  50. package/dist/panel/viewer.html +126 -0
  51. package/dist/panel-types.d.ts +20 -0
  52. package/dist/panel-types.js +3 -0
  53. package/dist/panel-types.js.map +1 -0
  54. package/dist/panel.d.ts +27 -0
  55. package/dist/panel.js +208 -0
  56. package/dist/panel.js.map +1 -0
  57. package/dist/server.d.ts +5 -0
  58. package/dist/server.js +33 -941
  59. package/dist/server.js.map +1 -0
  60. package/dist/tools.d.ts +103 -0
  61. package/dist/tools.js +793 -0
  62. package/dist/tools.js.map +1 -0
  63. package/dist/viewer.d.ts +14 -0
  64. package/dist/viewer.js +125 -0
  65. package/dist/viewer.js.map +1 -0
  66. package/package.json +37 -21
  67. package/watcher/mcp_compat.py +147 -0
  68. package/watcher/mcp_ops.py +1178 -0
  69. package/watcher/mcp_sim.py +246 -0
  70. package/watcher/watcher.py +255 -0
  71. package/.claude/settings.local.json +0 -29
  72. package/CLAUDE.md +0 -26
  73. package/CONTRIBUTING.md +0 -72
  74. package/README.md.enhanced +0 -155
  75. package/codesys-mcp-toolkit-1.2.0.tgz +0 -0
  76. package/codesys-mcp-toolkit-1.2.1.tgz +0 -0
  77. package/codesys-mcp-toolkit-1.5.7.tgz +0 -0
  78. package/dist/bin.js +0 -75
  79. package/dist/check_session_paths.js +0 -149
  80. package/dist/codesys_interop.js +0 -435
  81. package/dist/direct_session_command.js +0 -293
  82. package/dist/scriptLoader.js +0 -165
  83. package/dist/scripts/check_status.py +0 -21
  84. package/dist/scripts/compile_project.py +0 -65
  85. package/dist/scripts/create_method.py +0 -80
  86. package/dist/scripts/create_pou.py +0 -102
  87. package/dist/scripts/create_project.py +0 -116
  88. package/dist/scripts/create_property.py +0 -78
  89. package/dist/scripts/ensure_project_open.py +0 -154
  90. package/dist/scripts/find_object_by_path.py +0 -111
  91. package/dist/scripts/get_pou_code.py +0 -91
  92. package/dist/scripts/get_project_structure.py +0 -72
  93. package/dist/scripts/open_project.py +0 -25
  94. package/dist/scripts/save_project.py +0 -29
  95. package/dist/scripts/session_status.py +0 -281
  96. package/dist/scripts/set_pou_code.py +0 -109
  97. package/dist/scripts/start_session.py +0 -809
  98. package/dist/scripts/stop_session.py +0 -324
  99. package/dist/sessionManager.js +0 -798
  100. package/docs/configuration.md +0 -141
  101. package/docs/index.md +0 -26
  102. package/docs/installation.md +0 -84
  103. package/docs/session_management.md +0 -110
  104. package/examples/README.md +0 -78
  105. package/examples/sample_config.json +0 -32
  106. package/replacements_session_status.txt +0 -16
  107. package/scriptDoc.txt +0 -8679
  108. package/scripts/build.js +0 -25
  109. package/scripts/fix_fstrings.py +0 -76
package/dist/server.js CHANGED
@@ -1,942 +1,34 @@
1
- "use strict";
2
- /**
3
- * server.ts
4
- * MCP Server for interacting with CODESYS via Python scripting.
5
- * Implements all MCP resources and tools that interact with the CODESYS environment.
6
- *
7
- * IMPORTANT: This server receives configuration as parameters from bin.ts,
8
- * which helps avoid issues with command-line argument passing in different execution environments.
9
- */
10
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- var desc = Object.getOwnPropertyDescriptor(m, k);
13
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
- desc = { enumerable: true, get: function() { return m[k]; } };
15
- }
16
- Object.defineProperty(o, k2, desc);
17
- }) : (function(o, m, k, k2) {
18
- if (k2 === undefined) k2 = k;
19
- o[k2] = m[k];
20
- }));
21
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
- Object.defineProperty(o, "default", { enumerable: true, value: v });
23
- }) : function(o, v) {
24
- o["default"] = v;
25
- });
26
- var __importStar = (this && this.__importStar) || (function () {
27
- var ownKeys = function(o) {
28
- ownKeys = Object.getOwnPropertyNames || function (o) {
29
- var ar = [];
30
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
- return ar;
32
- };
33
- return ownKeys(o);
34
- };
35
- return function (mod) {
36
- if (mod && mod.__esModule) return mod;
37
- var result = {};
38
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
- __setModuleDefault(result, mod);
40
- return result;
41
- };
42
- })();
43
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
44
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
45
- return new (P || (P = Promise))(function (resolve, reject) {
46
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
47
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
48
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
49
- step((generator = generator.apply(thisArg, _arguments || [])).next());
1
+ import { McpServer } from "@modelcontextprotocol/server";
2
+ import { EXTENSION_ID } from "@modelcontextprotocol/ext-apps/server";
3
+ import { currentCodesysSim } from "./codesys/simulation.js";
4
+ import { registerTools, revertObject, versionLine } from "./tools.js";
5
+ import { registerPanel } from "./panel.js";
6
+ export const VERSION = "2.0.0-alpha.0";
7
+ const INSTRUCTIONS = `CODESYS V3.5 automation through a persistent CODESYS session.
8
+ Everything goes through the one tool named codesys, as {"action": "<name>", "args": {...}}. The names below are its actions; {"action": "help"} lists them all with their arguments.
9
+ Use batch whenever you know the next steps: it sends several actions in one call and is much faster than separate calls. For example, all the create_object and write_object calls of a step followed by compile, or codesys_sim_force, wait, codesys_sim_cycle and codesys_sim_read for one test.
10
+ Workflow: open_project (or create_project) -> get_project_tree -> read_objects -> write_object / create_object -> compile.
11
+ Object paths are relative to the active application: 'PLC_PRG', 'FB_Pump/Start' (method), 'FB_Pump/Speed/Get' (property accessor).
12
+ Only Structured Text is supported; objects with graphical bodies are marked [non-ST] and are read-only.
13
+ The first call after start can take 1-3 minutes while CODESYS starts; codesys_status shows progress without waiting.
14
+ To check that logic works, use CODESYS's own simulation (no controller, nothing extra to install, the real compiler and libraries): codesys_sim_start -> codesys_sim_control (run) or codesys_sim_cycle (exact cycles) -> codesys_sim_read / write / force / unforce -> codesys_sim_stop. The project is locked until codesys_sim_stop.
15
+ Fix code with write_object after codesys_sim_stop, then codesys_sim_start again to load it.
16
+ Right after opening or creating a project, call the separate tool open_sim_panel: it shows the user a live CODESYS-style workspace in the chat (the project tree and the code as you write it, changes, compile results and the simulation), so they can watch you work.`;
17
+ export function createServer(ctx) {
18
+ // A host treats an extension the server doesn't advertise as unsupported, and then runs
19
+ // open_sim_panel as a plain text tool and never fetches the card's page.
20
+ const server = new McpServer({ name: "codesys-mcp-toolkit", version: VERSION }, { instructions: INSTRUCTIONS, capabilities: { extensions: { [EXTENSION_ID]: {} } } });
21
+ registerTools(server, ctx);
22
+ registerPanel(server, {
23
+ cache: ctx.cache,
24
+ changes: ctx.changes,
25
+ project: () => ctx.project?.path,
26
+ versions: () => versionLine(ctx.codesys?.label, ctx.bridge.health().scripting, ctx.project?.compiler_version),
27
+ codesysSim: () => currentCodesysSim(ctx),
28
+ driver: ctx.driver,
29
+ revert: (path, to) => revertObject(ctx, path, to),
30
+ log: (m) => ctx.log?.(m),
50
31
  });
51
- };
52
- Object.defineProperty(exports, "__esModule", { value: true });
53
- exports.startMcpServer = startMcpServer;
54
- // --- Import 'os' FIRST ---
55
- const os = __importStar(require("os"));
56
- // --- End Import 'os' ---
57
- // --- STARTUP LOG ---
58
- console.error(`>>> SERVER.TS TOP LEVEL EXECUTION @ ${new Date().toISOString()} <<<`);
59
- console.error(`>>> Node: ${process.version}, Platform: ${os.platform()}, Arch: ${os.arch()}`);
60
- console.error(`>>> Initial CWD: ${process.cwd()}`);
61
- // --- End Startup Log ---
62
- // --- Necessary Imports ---
63
- const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
64
- const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
65
- const zod_1 = require("zod");
66
- const codesys_interop_1 = require("./codesys_interop");
67
- const path = __importStar(require("path"));
68
- const promises_1 = require("fs/promises");
69
- const fs = __importStar(require("fs"));
70
- const scriptLoader_1 = require("./scriptLoader");
71
- const sessionManager_1 = require("./sessionManager");
72
- const direct_session_command_1 = require("./direct_session_command");
73
- // --- Wrap server logic in an exported function ---
74
- function startMcpServer(config) {
75
- return __awaiter(this, void 0, void 0, function* () {
76
- console.error(`>>> SERVER.TS startMcpServer() CALLED @ ${new Date().toISOString()} <<<`);
77
- console.error(`>>> Config Received: ${JSON.stringify(config)}`);
78
- // --- Use config values directly ---
79
- const WORKSPACE_DIR = config.workspaceDir;
80
- const codesysExePath = config.codesysPath;
81
- const codesysProfileName = config.profileName;
82
- console.error(`SERVER.TS: Using Workspace Directory: ${WORKSPACE_DIR}`);
83
- console.error(`SERVER.TS: Using CODESYS Path: ${codesysExePath}`);
84
- console.error(`SERVER.TS: Using CODESYS Profile: ${codesysProfileName}`);
85
- // --- Sanity check - confirm the path exists if possible ---
86
- console.error(`SERVER.TS: Checking existence of CODESYS executable: ${codesysExePath}`);
87
- try {
88
- // Using sync check here as it's part of initial setup before async operations start
89
- const fsChecker = require('fs');
90
- if (!fsChecker.existsSync(codesysExePath)) {
91
- console.error(`SERVER.TS ERROR: Determined CODESYS executable path does not exist: ${codesysExePath}`);
92
- throw new Error(`CODESYS executable not found at specified path: ${codesysExePath}`);
93
- }
94
- else {
95
- console.error(`SERVER.TS: Confirmed CODESYS executable exists.`);
96
- }
97
- }
98
- catch (err) {
99
- console.error(`SERVER.TS ERROR: Error checking CODESYS path existence: ${err.message}`);
100
- throw err; // Rethrow the error to be caught by the caller (bin.ts)
101
- }
102
- // --- End Configuration Handling ---
103
- // --- Helper Function (fileExists - async version) ---
104
- function fileExists(filePath) {
105
- return __awaiter(this, void 0, void 0, function* () {
106
- try {
107
- yield (0, promises_1.stat)(filePath);
108
- return true;
109
- }
110
- catch (error) {
111
- if (error.code === 'ENOENT') {
112
- return false; // File does not exist
113
- }
114
- throw error; // Other error
115
- }
116
- });
117
- }
118
- // --- End Helper Function ---
119
- // --- Initialize Session Manager ---
120
- // Default to true if not specified
121
- const enableSessionManagement = config.enableSessionManagement !== false;
122
- console.error(`SERVER.TS: Session management is ${enableSessionManagement ? 'enabled' : 'disabled'}`);
123
- // Create session manager instance (will remain undefined if session management is disabled)
124
- let sessionManager = undefined;
125
- if (enableSessionManagement) {
126
- try {
127
- sessionManager = new sessionManager_1.CodesysSessionManager(codesysExePath, codesysProfileName);
128
- console.error('SERVER.TS: Session manager initialized');
129
- // Only start a session if explicitly enabled and manager was created successfully
130
- if (sessionManager) {
131
- try {
132
- // Check if session is already active
133
- console.error('SERVER.TS: Checking if session is already active...');
134
- const isActive = (0, direct_session_command_1.isSessionActive)();
135
- if (isActive) {
136
- console.error('SERVER.TS: Session is already active');
137
- }
138
- else {
139
- console.error('SERVER.TS: Session not active, starting new session...');
140
- console.error('SERVER.TS: IMPORTANT: If CODESYS hangs during session startup, use --no-session flag');
141
- yield sessionManager.startSession();
142
- console.error('SERVER.TS: Session start initiated');
143
- }
144
- }
145
- catch (error) {
146
- console.error(`SERVER.TS: Error starting session: ${error.message}`);
147
- console.error('SERVER.TS: Will fallback to one-time execution for operations');
148
- // Continue without session, will fallback to one-time execution
149
- }
150
- }
151
- else {
152
- console.error('SERVER.TS: Session manager creation failed');
153
- }
154
- }
155
- catch (error) {
156
- console.error(`SERVER.TS: Failed to create session manager: ${error.message}`);
157
- console.error('SERVER.TS: Will fallback to one-time execution for operations');
158
- sessionManager = undefined;
159
- }
160
- }
161
- else {
162
- console.error('SERVER.TS: Session management is disabled, will use one-time script execution');
163
- }
164
- if (!sessionManager) {
165
- console.error('SERVER.TS: Session manager is not available - one-time script execution will be used');
166
- }
167
- // --- End Session Manager Initialization ---
168
- // --- Preload script templates ---
169
- try {
170
- yield (0, scriptLoader_1.preloadAllScriptTemplates)();
171
- }
172
- catch (err) {
173
- console.error(`SERVER.TS ERROR: Error preloading script templates: ${err.message}`);
174
- // Continue execution, as script templates will be loaded on demand as needed
175
- }
176
- // --- MCP Server Initialization ---
177
- console.error("SERVER.TS: Initializing McpServer...");
178
- const server = new mcp_js_1.McpServer({
179
- name: "CODESYS Control MCP Server",
180
- version: "1.8.2", // Updated version with session safety improvements
181
- capabilities: {
182
- resources: { listChanged: true },
183
- tools: { listChanged: true }
184
- }
185
- });
186
- console.error("SERVER.TS: McpServer instance created.");
187
- // --- End MCP Server Initialization ---
188
- // --- Zod Schemas ---
189
- const PouTypeEnum = zod_1.z.enum(["Program", "FunctionBlock", "Function"]);
190
- const ImplementationLanguageEnum = zod_1.z.enum(["ST", "LD", "FBD", "SFC", "IL", "CFC", "StructuredText", "LadderDiagram", "FunctionBlockDiagram", "SequentialFunctionChart", "InstructionList", "ContinuousFunctionChart"]);
191
- // --- End Zod Schemas ---
192
- // --- MCP Resources / Tools Definitions ---
193
- console.error("SERVER.TS: Defining Resources and Tools...");
194
- // --- Resources ---
195
- server.resource("project-status", "codesys://project/status", (uri) => __awaiter(this, void 0, void 0, function* () {
196
- var _a, _b, _c, _d, _e;
197
- console.error(`SERVER.TS Resource request: ${uri.href}`);
198
- try {
199
- const scriptContent = yield (0, scriptLoader_1.loadScriptTemplate)('check_status');
200
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
201
- const outputLines = result.output.split(/[\r\n]+/).filter(line => line.trim());
202
- const statusData = {};
203
- outputLines.forEach(line => {
204
- const match = line.match(/^([^:]+):\s*(.*)$/);
205
- if (match) {
206
- statusData[match[1].trim()] = match[2].trim();
207
- }
208
- });
209
- const statusText = `CODESYS Status:\n - Scripting OK: ${(_a = statusData['Scripting OK']) !== null && _a !== void 0 ? _a : 'Unknown'}\n - Project Open: ${(_b = statusData['Project Open']) !== null && _b !== void 0 ? _b : 'Unknown'}\n - Project Name: ${(_c = statusData['Project Name']) !== null && _c !== void 0 ? _c : 'Unknown'}\n - Project Path: ${(_d = statusData['Project Path']) !== null && _d !== void 0 ? _d : 'N/A'}`;
210
- const isError = !result.success || ((_e = statusData['Scripting OK']) === null || _e === void 0 ? void 0 : _e.toLowerCase()) !== 'true';
211
- return { contents: [{ uri: uri.href, text: statusText, contentType: "text/plain" }], isError: isError };
212
- }
213
- catch (error) {
214
- console.error(`Error resource ${uri.href}:`, error);
215
- return { contents: [{ uri: uri.href, text: `Failed status script: ${error.message}`, contentType: "text/plain" }], isError: true };
216
- }
217
- }));
218
- // Templates
219
- const projectStructureTemplate = new mcp_js_1.ResourceTemplate("codesys://project/{+project_path}/structure", { list: undefined });
220
- const pouCodeTemplate = new mcp_js_1.ResourceTemplate("codesys://project/{+project_path}/pou/{+pou_path}/code", { list: undefined });
221
- server.resource("project-structure", projectStructureTemplate, (uri, params) => __awaiter(this, void 0, void 0, function* () {
222
- const projectPathParam = params.project_path;
223
- if (typeof projectPathParam !== 'string') {
224
- return {
225
- contents: [{ uri: uri.href, text: `Error: Invalid project path type (${typeof projectPathParam}).`, contentType: "text/plain" }],
226
- isError: true
227
- };
228
- }
229
- const projectPath = projectPathParam;
230
- if (!projectPath) {
231
- return {
232
- contents: [{ uri: uri.href, text: "Error: Project path missing.", contentType: "text/plain" }],
233
- isError: true
234
- };
235
- }
236
- console.error(`Resource request: project structure for ${projectPath}`);
237
- try {
238
- const absoluteProjPath = path.normalize(path.isAbsolute(projectPath) ? projectPath : path.join(WORKSPACE_DIR, projectPath));
239
- // Load script template and prepare parameters
240
- const scriptTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('get_project_structure');
241
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
242
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + scriptTemplate;
243
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
244
- 'PROJECT_FILE_PATH': absoluteProjPath.replace(/\\/g, '\\\\')
245
- });
246
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
247
- let structureText = `Error retrieving structure for ${absoluteProjPath}.\n\n${result.output}`;
248
- let isError = !result.success;
249
- if (result.success && result.output.includes("SCRIPT_SUCCESS")) {
250
- const startMarker = "--- PROJECT STRUCTURE START ---";
251
- const endMarker = "--- PROJECT STRUCTURE END ---";
252
- const startIndex = result.output.indexOf(startMarker);
253
- const endIndex = result.output.indexOf(endMarker);
254
- if (startIndex !== -1 && endIndex !== -1 && startIndex < endIndex) {
255
- structureText = result.output.substring(startIndex + startMarker.length, endIndex).replace(/\\n/g, '\n').trim();
256
- }
257
- else {
258
- console.error("Error: Could not find structure markers in script output.");
259
- structureText = `Could not parse structure markers in output for ${absoluteProjPath}.\n\nOutput:\n${result.output}`;
260
- isError = true;
261
- }
262
- }
263
- else {
264
- isError = true;
265
- }
266
- return { contents: [{ uri: uri.href, text: structureText, contentType: "text/plain" }], isError: isError };
267
- }
268
- catch (error) {
269
- console.error(`Error getting structure ${uri.href}:`, error);
270
- return { contents: [{ uri: uri.href, text: `Failed structure script for '${projectPath}': ${error.message}`, contentType: "text/plain" }], isError: true };
271
- }
272
- }));
273
- server.resource("pou-code", pouCodeTemplate, (uri, params) => __awaiter(this, void 0, void 0, function* () {
274
- const projectPathParam = params.project_path;
275
- const pouPathParam = params.pou_path;
276
- if (typeof projectPathParam !== 'string' || typeof pouPathParam !== 'string') {
277
- return {
278
- contents: [{ uri: uri.href, text: "Error: Invalid project or POU path type.", contentType: "text/plain" }],
279
- isError: true
280
- };
281
- }
282
- const projectPath = projectPathParam;
283
- const pouPath = pouPathParam;
284
- if (!projectPath || !pouPath) {
285
- return {
286
- contents: [{ uri: uri.href, text: "Error: Project or POU path missing.", contentType: "text/plain" }],
287
- isError: true
288
- };
289
- }
290
- console.error(`Resource request: POU code: Project='${projectPath}', POU='${pouPath}'`);
291
- try {
292
- const absoluteProjPath = path.normalize(path.isAbsolute(projectPath) ? projectPath : path.join(WORKSPACE_DIR, projectPath));
293
- const sanitizedPouPath = String(pouPath).replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
294
- // Load script template and prepare parameters
295
- const scriptTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('get_pou_code');
296
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
297
- const findObjectByPathTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('find_object_by_path');
298
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + findObjectByPathTemplate + '\n\n' + scriptTemplate;
299
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
300
- 'PROJECT_FILE_PATH': absoluteProjPath.replace(/\\/g, '\\\\'),
301
- 'POU_FULL_PATH': sanitizedPouPath
302
- });
303
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
304
- let codeText = `Error retrieving code for object '${sanitizedPouPath}' in project '${absoluteProjPath}'.\n\n${result.output}`;
305
- let isError = !result.success;
306
- if (result.success && result.output.includes("SCRIPT_SUCCESS")) {
307
- const declStartMarker = "### POU DECLARATION START ###";
308
- const declEndMarker = "### POU DECLARATION END ###";
309
- const implStartMarker = "### POU IMPLEMENTATION START ###";
310
- const implEndMarker = "### POU IMPLEMENTATION END ###";
311
- const declStartIdx = result.output.indexOf(declStartMarker);
312
- const declEndIdx = result.output.indexOf(declEndMarker);
313
- const implStartIdx = result.output.indexOf(implStartMarker);
314
- const implEndIdx = result.output.indexOf(implEndMarker);
315
- let declaration = "/* Declaration not found in output */";
316
- let implementation = "/* Implementation not found in output */";
317
- if (declStartIdx !== -1 && declEndIdx !== -1 && declStartIdx < declEndIdx) {
318
- declaration = result.output.substring(declStartIdx + declStartMarker.length, declEndIdx).replace(/\\n/g, '\n').trim();
319
- }
320
- else {
321
- console.error(`WARN: Declaration markers not found correctly for ${sanitizedPouPath}`);
322
- }
323
- if (implStartIdx !== -1 && implEndIdx !== -1 && implStartIdx < implEndIdx) {
324
- implementation = result.output.substring(implStartIdx + implStartMarker.length, implEndIdx).replace(/\\n/g, '\n').trim();
325
- }
326
- else {
327
- console.error(`WARN: Implementation markers not found correctly for ${sanitizedPouPath}`);
328
- }
329
- codeText = `// ----- Declaration -----\n${declaration}\n\n// ----- Implementation -----\n${implementation}`;
330
- }
331
- else {
332
- isError = true;
333
- }
334
- return { contents: [{ uri: uri.href, text: codeText, contentType: "text/plain" }], isError: isError };
335
- }
336
- catch (error) {
337
- console.error(`Error getting POU code ${uri.href}:`, error);
338
- return { contents: [{ uri: uri.href, text: `Failed POU code script for '${pouPath}' in '${projectPath}': ${error.message}`, contentType: "text/plain" }], isError: true };
339
- }
340
- }));
341
- // --- End Resources ---
342
- // --- Tools ---
343
- server.tool("open_project", "Opens an existing CODESYS project file.", {
344
- filePath: zod_1.z.string().describe("Path to the project file (e.g., 'C:/Projects/MyPLC.project' or '/Users/user/projects/my_project.project').")
345
- }, (args) => __awaiter(this, void 0, void 0, function* () {
346
- const { filePath } = args;
347
- let absPath = path.normalize(path.isAbsolute(filePath) ? filePath : path.join(WORKSPACE_DIR, filePath));
348
- console.error(`Tool call: open_project: ${absPath}`);
349
- try {
350
- // Load the script template and prepare with parameters
351
- const scriptTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('open_project');
352
- // Combine with required ensure_project_open script (will handle any imports)
353
- const ensureProjectOpenScript = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
354
- const combinedScript = ensureProjectOpenScript + '\n\n' + scriptTemplate;
355
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
356
- 'PROJECT_FILE_PATH': absPath.replace(/\\/g, '\\\\')
357
- });
358
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
359
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
360
- return {
361
- content: [{
362
- type: "text",
363
- text: success ? `Project opened: ${absPath}` : `Failed open project ${absPath}. Output:\n${result.output}`
364
- }],
365
- isError: !success
366
- };
367
- }
368
- catch (e) {
369
- console.error(`Error open_project ${absPath}: ${e}`);
370
- return {
371
- content: [{ type: "text", text: `Error: ${e.message}` }],
372
- isError: true
373
- };
374
- }
375
- }));
376
- server.tool("create_project", "Creates a new CODESYS project from the standard template.", {
377
- filePath: zod_1.z.string().describe("Path where the new project file should be created (e.g., 'C:/Projects/NewPLC.project' or '/Users/user/projects/new_project.project').")
378
- }, (args) => __awaiter(this, void 0, void 0, function* () {
379
- const { filePath } = args;
380
- let absPath = path.normalize(path.isAbsolute(filePath) ? filePath : path.join(WORKSPACE_DIR, filePath));
381
- console.error(`Tool call: create_project (copy template): ${absPath}`);
382
- let templatePath = "";
383
- try {
384
- // Template finding logic
385
- const baseDir = path.dirname(path.dirname(codesysExePath));
386
- templatePath = path.normalize(path.join(baseDir, 'Templates', 'Standard.project'));
387
- if (!(yield fileExists(templatePath))) {
388
- console.error(`WARN: Template not found relative to exe: ${templatePath}. Trying ProgramData...`);
389
- const programData = process.env.ALLUSERSPROFILE || process.env.ProgramData || 'C:\\ProgramData';
390
- const possibleTemplateDir = path.join(programData, 'CODESYS', 'CODESYS', codesysProfileName, 'Templates');
391
- let potentialTemplatePath = path.normalize(path.join(possibleTemplateDir, 'Standard.project'));
392
- if (yield fileExists(potentialTemplatePath)) {
393
- templatePath = potentialTemplatePath;
394
- console.error(`DEBUG: Found template in ProgramData: ${templatePath}`);
395
- }
396
- else {
397
- const alternativeTemplateDir = path.join(programData, 'CODESYS', 'Templates');
398
- potentialTemplatePath = path.normalize(path.join(alternativeTemplateDir, 'Standard.project'));
399
- if (yield fileExists(potentialTemplatePath)) {
400
- templatePath = potentialTemplatePath;
401
- console.error(`DEBUG: Found template in ProgramData (alternative): ${templatePath}`);
402
- }
403
- else {
404
- throw new Error(`Standard template project file not found at relative path or ProgramData locations.`);
405
- }
406
- }
407
- }
408
- else {
409
- console.error(`DEBUG: Found template relative to exe: ${templatePath}`);
410
- }
411
- }
412
- catch (e) {
413
- console.error(`Template Error: ${e.message}`);
414
- return {
415
- content: [{ type: "text", text: `Template Error: ${e.message}` }],
416
- isError: true
417
- };
418
- }
419
- try {
420
- // Load script template and interpolate parameters
421
- const scriptTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('create_project');
422
- // Normalize paths for more consistent handling between platforms
423
- const normalizedTargetPath = absPath.replace(/\\/g, '/');
424
- const normalizedTemplatePath = templatePath.replace(/\\/g, '/');
425
- console.error(`Using normalized paths for script: Target=${normalizedTargetPath}, Template=${normalizedTemplatePath}`);
426
- // When running in a session, skip the open step to avoid conflicts
427
- const inSession = sessionManager &&
428
- (sessionManager.getState() === sessionManager_1.SessionState.RUNNING ||
429
- sessionManager.getState() === sessionManager_1.SessionState.BUSY);
430
- const scriptContent = (0, scriptLoader_1.interpolateScript)(scriptTemplate, {
431
- 'PROJECT_FILE_PATH': normalizedTargetPath,
432
- 'TEMPLATE_PROJECT_PATH': normalizedTemplatePath,
433
- 'SKIP_OPEN': inSession ? 'true' : 'false'
434
- });
435
- console.error(">>> create_project (copy-then-open): PREPARED SCRIPT:", scriptContent.substring(0, 500) + "...");
436
- // Try the direct session approach first if a session is active
437
- let result;
438
- let usingDirectSession = false;
439
- // Check if there's an active session using our direct method
440
- if ((0, direct_session_command_1.isSessionActive)()) {
441
- console.error(">>> create_project: Detected active session, using direct command approach");
442
- try {
443
- // Use our direct session command approach
444
- usingDirectSession = true;
445
- const directResult = yield (0, direct_session_command_1.createProjectDirect)(normalizedTargetPath, normalizedTemplatePath);
446
- if (directResult.success) {
447
- // Format the result to match the expected format
448
- result = {
449
- success: true,
450
- output: `SCRIPT_SUCCESS: Project created from template using direct session command.\n${directResult.message}`
451
- };
452
- console.error(">>> create_project: Direct session command succeeded");
453
- }
454
- else {
455
- console.error(`>>> create_project: Direct session command failed: ${directResult.message}`);
456
- // Fall back to regular script execution
457
- usingDirectSession = false;
458
- result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName);
459
- }
460
- }
461
- catch (directError) {
462
- console.error(`>>> create_project: Error with direct session command: ${directError.message}`);
463
- // Fall back to regular script execution
464
- usingDirectSession = false;
465
- result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName);
466
- }
467
- }
468
- // If not using direct session, use the standard approach
469
- if (!usingDirectSession) {
470
- // Use a longer timeout for project creation operations (120 seconds)
471
- console.error(">>> create_project: Using standard script execution");
472
- result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
473
- }
474
- console.error(">>> create_project (copy-then-open): EXECUTION RESULT:", JSON.stringify(result));
475
- // Check if there was a timeout but the project might still have been created
476
- if (!result.success && result.output.includes("timed out") && result.output.includes("project file might still have been created")) {
477
- console.error(">>> create_project: Script timed out but project might have been created. Checking if file exists.");
478
- // Check if the project file was actually created despite the timeout
479
- const fileExists = fs.existsSync(absPath);
480
- if (fileExists) {
481
- console.error(`>>> create_project: Project file ${absPath} exists despite timeout.`);
482
- return {
483
- content: [{
484
- type: "text",
485
- text: `Project file was created at ${absPath}, but the operation timed out before completion. ` +
486
- `The project might be usable, but you may need to manually open it in CODESYS.`
487
- }],
488
- isError: true // Still mark as error because operation didn't complete properly
489
- };
490
- }
491
- }
492
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
493
- // Special handling for cases where project was created but couldn't be opened automatically
494
- if (success && inSession && result.output.includes("open operation failed")) {
495
- return {
496
- content: [{
497
- type: "text",
498
- text: `Project created from template: ${absPath}\n` +
499
- `Note: The project was created successfully, but couldn't be opened automatically because you're in a session.\n` +
500
- `You can use open_project to open it now, or create a POU directly with create_pou.`
501
- }],
502
- isError: false
503
- };
504
- }
505
- // Special handling for skipped open operations in sessions
506
- if (success && inSession && result.output.includes("open step skipped for session compatibility")) {
507
- return {
508
- content: [{
509
- type: "text",
510
- text: `Project created from template: ${absPath}\n` +
511
- `Note: The project opening step was skipped because you're in a session.\n` +
512
- `You can use open_project to open it now, or create a POU directly with create_pou.`
513
- }],
514
- isError: false
515
- };
516
- }
517
- return {
518
- content: [{
519
- type: "text",
520
- text: success ? `Project created from template: ${absPath}` : `Failed create project ${absPath} from template. Output:\n${result.output}`
521
- }],
522
- isError: !success
523
- };
524
- }
525
- catch (e) {
526
- console.error(`Error create_project ${absPath}: ${e}`);
527
- return {
528
- content: [{ type: "text", text: `Error: ${e.message}` }],
529
- isError: true
530
- };
531
- }
532
- }));
533
- server.tool("save_project", "Saves the currently open CODESYS project.", {
534
- projectFilePath: zod_1.z.string().describe("Path to the project file to ensure is open before saving (e.g., 'C:/Projects/MyPLC.project').")
535
- }, (args) => __awaiter(this, void 0, void 0, function* () {
536
- const { projectFilePath } = args;
537
- let absPath = path.normalize(path.isAbsolute(projectFilePath) ? projectFilePath : path.join(WORKSPACE_DIR, projectFilePath));
538
- console.error(`Tool call: save_project: ${absPath}`);
539
- try {
540
- // Load and combine script templates
541
- const saveProjectTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('save_project');
542
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
543
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + saveProjectTemplate;
544
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
545
- 'PROJECT_FILE_PATH': absPath.replace(/\\/g, '\\\\')
546
- });
547
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
548
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
549
- return {
550
- content: [{
551
- type: "text",
552
- text: success ? `Project saved: ${absPath}` : `Failed save project ${absPath}. Output:\n${result.output}`
553
- }],
554
- isError: !success
555
- };
556
- }
557
- catch (e) {
558
- console.error(`Error save_project ${absPath}: ${e}`);
559
- return {
560
- content: [{ type: "text", text: `Error: ${e.message}` }],
561
- isError: true
562
- };
563
- }
564
- }));
565
- server.tool("create_pou", "Creates a new Program, Function Block, or Function POU within the specified CODESYS project.", {
566
- projectFilePath: zod_1.z.string().describe("Path to the project file (e.g., 'C:/Projects/MyPLC.project')."),
567
- name: zod_1.z.string().describe("Name for the new POU (must be a valid IEC identifier)."),
568
- type: PouTypeEnum.describe("Type of POU (Program, FunctionBlock, Function)."),
569
- language: ImplementationLanguageEnum.describe("Implementation language (ST, LD, FBD, etc.). CODESYS default will be used if specific language is not set or directly supported by scripting for this POU type."),
570
- parentPath: zod_1.z.string().describe("Relative path under project root or application where the POU should be created (e.g., 'Application' or 'MyFolder/SubFolder').")
571
- }, (args) => __awaiter(this, void 0, void 0, function* () {
572
- const { projectFilePath, name, type, language, parentPath } = args;
573
- let absPath = path.normalize(path.isAbsolute(projectFilePath) ? projectFilePath : path.join(WORKSPACE_DIR, projectFilePath));
574
- const sanParentPath = parentPath.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
575
- const sanName = name.trim();
576
- console.error(`Tool call: create_pou: Name='${sanName}', Type='${type}', Lang='${language}', Parent='${sanParentPath}', Project='${absPath}'`);
577
- try {
578
- // Load and combine script templates
579
- const createPouTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('create_pou');
580
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
581
- const findObjectByPathTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('find_object_by_path');
582
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + findObjectByPathTemplate + '\n\n' + createPouTemplate;
583
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
584
- 'PROJECT_FILE_PATH': absPath.replace(/\\/g, '\\\\'),
585
- 'POU_NAME': sanName,
586
- 'POU_TYPE_STR': type,
587
- 'IMPL_LANGUAGE_STR': language,
588
- 'PARENT_PATH': sanParentPath
589
- });
590
- console.error(">>> create_pou: PREPARED SCRIPT:", scriptContent.substring(0, 500) + "...");
591
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
592
- console.error(">>> create_pou: EXECUTION RESULT:", JSON.stringify(result));
593
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
594
- return {
595
- content: [{
596
- type: "text",
597
- text: success ? `POU '${sanName}' created in '${sanParentPath}' of ${absPath}. Project saved.` : `Failed create POU '${sanName}'. Output:\n${result.output}`
598
- }],
599
- isError: !success
600
- };
601
- }
602
- catch (e) {
603
- console.error(`Error create_pou ${sanName} in ${absPath}: ${e}`);
604
- return {
605
- content: [{ type: "text", text: `Error: ${e.message}` }],
606
- isError: true
607
- };
608
- }
609
- }));
610
- server.tool("set_pou_code", "Sets the declaration and/or implementation code for a specific POU, Method, or Property.", {
611
- projectFilePath: zod_1.z.string().describe("Path to the project file (e.g., 'C:/Projects/MyPLC.project')."),
612
- pouPath: zod_1.z.string().describe("Full relative path to the target object (e.g., 'Application/MyPOU', 'MyFolder/MyFB/MyMethod', 'MyFolder/MyFB/MyProperty')."),
613
- declarationCode: zod_1.z.string().describe("Code for the declaration part (VAR...END_VAR). If omitted, the declaration is not changed.").optional(),
614
- implementationCode: zod_1.z.string().describe("Code for the implementation logic part. If omitted, the implementation is not changed.").optional()
615
- }, (args) => __awaiter(this, void 0, void 0, function* () {
616
- const { projectFilePath, pouPath, declarationCode, implementationCode } = args;
617
- if (declarationCode === undefined && implementationCode === undefined) {
618
- return {
619
- content: [{ type: "text", text: "Error: At least one of declarationCode or implementationCode must be provided." }],
620
- isError: true
621
- };
622
- }
623
- let absPath = path.normalize(path.isAbsolute(projectFilePath) ? projectFilePath : path.join(WORKSPACE_DIR, projectFilePath));
624
- const sanPouPath = pouPath.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
625
- console.error(`Tool call: set_pou_code: Target='${sanPouPath}', Project='${absPath}'`);
626
- try {
627
- // Load and combine script templates
628
- const setPouCodeTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('set_pou_code');
629
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
630
- const findObjectByPathTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('find_object_by_path');
631
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + findObjectByPathTemplate + '\n\n' + setPouCodeTemplate;
632
- // Escape content for Python triple-quoted strings
633
- const sanDeclCode = (declarationCode !== null && declarationCode !== void 0 ? declarationCode : "").replace(/\\/g, '\\\\').replace(/"""/g, '\\"\\"\\"');
634
- const sanImplCode = (implementationCode !== null && implementationCode !== void 0 ? implementationCode : "").replace(/\\/g, '\\\\').replace(/"""/g, '\\"\\"\\"');
635
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
636
- 'PROJECT_FILE_PATH': absPath.replace(/\\/g, '\\\\'),
637
- 'POU_FULL_PATH': sanPouPath,
638
- 'DECLARATION_CONTENT': sanDeclCode,
639
- 'IMPLEMENTATION_CONTENT': sanImplCode
640
- });
641
- console.error(">>> set_pou_code: PREPARED SCRIPT:", scriptContent.substring(0, 500) + "...");
642
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
643
- console.error(">>> set_pou_code: EXECUTION RESULT:", JSON.stringify(result));
644
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
645
- return {
646
- content: [{
647
- type: "text",
648
- text: success ? `Code set for '${sanPouPath}' in ${absPath}. Project saved.` : `Failed set code for '${sanPouPath}'. Output:\n${result.output}`
649
- }],
650
- isError: !success
651
- };
652
- }
653
- catch (e) {
654
- console.error(`Error set_pou_code ${sanPouPath} in ${absPath}: ${e}`);
655
- return {
656
- content: [{ type: "text", text: `Error: ${e.message}` }],
657
- isError: true
658
- };
659
- }
660
- }));
661
- server.tool("create_property", "Creates a new Property within a specific Function Block POU.", {
662
- projectFilePath: zod_1.z.string().describe("Path to the project file (e.g., 'C:/Projects/MyPLC.project')."),
663
- parentPouPath: zod_1.z.string().describe("Relative path to the parent Function Block POU (e.g., 'Application/MyFB')."),
664
- propertyName: zod_1.z.string().describe("Name for the new property (must be a valid IEC identifier)."),
665
- propertyType: zod_1.z.string().describe("Data type of the property (e.g., 'BOOL', 'INT', 'MyDUT').")
666
- }, (args) => __awaiter(this, void 0, void 0, function* () {
667
- const { projectFilePath, parentPouPath, propertyName, propertyType } = args;
668
- let absPath = path.normalize(path.isAbsolute(projectFilePath) ? projectFilePath : path.join(WORKSPACE_DIR, projectFilePath));
669
- const sanParentPath = parentPouPath.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
670
- const sanPropName = propertyName.trim();
671
- const sanPropType = propertyType.trim();
672
- console.error(`Tool call: create_property: Name='${sanPropName}', Type='${sanPropType}', ParentPOU='${sanParentPath}', Project='${absPath}'`);
673
- if (!sanPropName || !sanPropType) {
674
- return {
675
- content: [{ type: "text", text: `Error: Property name and type cannot be empty.` }],
676
- isError: true
677
- };
678
- }
679
- try {
680
- // Load and combine script templates
681
- const createPropertyTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('create_property');
682
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
683
- const findObjectByPathTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('find_object_by_path');
684
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + findObjectByPathTemplate + '\n\n' + createPropertyTemplate;
685
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
686
- 'PROJECT_FILE_PATH': absPath.replace(/\\/g, '\\\\'),
687
- 'PARENT_POU_FULL_PATH': sanParentPath,
688
- 'PROPERTY_NAME': sanPropName,
689
- 'PROPERTY_TYPE': sanPropType
690
- });
691
- console.error(">>> create_property: PREPARED SCRIPT:", scriptContent.substring(0, 500) + "...");
692
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
693
- console.error(">>> create_property: EXECUTION RESULT:", JSON.stringify(result));
694
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
695
- return {
696
- content: [{
697
- type: "text",
698
- text: success ? `Property '${sanPropName}' created under '${sanParentPath}' in ${absPath}. Project saved.` : `Failed to create property '${sanPropName}'. Output:\n${result.output}`
699
- }],
700
- isError: !success
701
- };
702
- }
703
- catch (e) {
704
- console.error(`Error create_property ${sanPropName} in ${absPath}: ${e}`);
705
- return {
706
- content: [{ type: "text", text: `Error: ${e.message}` }],
707
- isError: true
708
- };
709
- }
710
- }));
711
- server.tool("create_method", "Creates a new Method within a specific Function Block POU.", {
712
- projectFilePath: zod_1.z.string().describe("Path to the project file (e.g., 'C:/Projects/MyPLC.project')."),
713
- parentPouPath: zod_1.z.string().describe("Relative path to the parent Function Block POU (e.g., 'Application/MyFB')."),
714
- methodName: zod_1.z.string().describe("Name of the new method (must be a valid IEC identifier)."),
715
- returnType: zod_1.z.string().optional().describe("Return type (e.g., 'BOOL', 'INT'). Leave empty or omit for no return value (PROCEDURE).")
716
- }, (args) => __awaiter(this, void 0, void 0, function* () {
717
- const { projectFilePath, parentPouPath, methodName, returnType } = args;
718
- let absPath = path.normalize(path.isAbsolute(projectFilePath) ? projectFilePath : path.join(WORKSPACE_DIR, projectFilePath));
719
- const sanParentPath = parentPouPath.replace(/\\/g, '/').replace(/^\/+|\/+$/g, '');
720
- const sanMethName = methodName.trim();
721
- const sanReturnType = (returnType !== null && returnType !== void 0 ? returnType : "").trim();
722
- console.error(`Tool call: create_method: Name='${sanMethName}', Return='${sanReturnType}', ParentPOU='${sanParentPath}', Project='${absPath}'`);
723
- if (!sanMethName) {
724
- return {
725
- content: [{ type: "text", text: `Error: Method name cannot be empty.` }],
726
- isError: true
727
- };
728
- }
729
- try {
730
- // Load and combine script templates
731
- const createMethodTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('create_method');
732
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
733
- const findObjectByPathTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('find_object_by_path');
734
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + findObjectByPathTemplate + '\n\n' + createMethodTemplate;
735
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
736
- 'PROJECT_FILE_PATH': absPath.replace(/\\/g, '\\\\'),
737
- 'PARENT_POU_FULL_PATH': sanParentPath,
738
- 'METHOD_NAME': sanMethName,
739
- 'RETURN_TYPE': sanReturnType
740
- });
741
- console.error(">>> create_method: PREPARED SCRIPT:", scriptContent.substring(0, 500) + "...");
742
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
743
- console.error(">>> create_method: EXECUTION RESULT:", JSON.stringify(result));
744
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
745
- return {
746
- content: [{
747
- type: "text",
748
- text: success ? `Method '${sanMethName}' created under '${sanParentPath}' in ${absPath}. Project saved.` : `Failed to create method '${sanMethName}'. Output:\n${result.output}`
749
- }],
750
- isError: !success
751
- };
752
- }
753
- catch (e) {
754
- console.error(`Error create_method ${sanMethName} in ${absPath}: ${e}`);
755
- return {
756
- content: [{ type: "text", text: `Error: ${e.message}` }],
757
- isError: true
758
- };
759
- }
760
- }));
761
- server.tool("compile_project", "Compiles (Builds) the primary application within a CODESYS project.", {
762
- projectFilePath: zod_1.z.string().describe("Path to the project file containing the application to compile (e.g., 'C:/Projects/MyPLC.project').")
763
- }, (args) => __awaiter(this, void 0, void 0, function* () {
764
- const { projectFilePath } = args;
765
- let absPath = path.normalize(path.isAbsolute(projectFilePath) ? projectFilePath : path.join(WORKSPACE_DIR, projectFilePath));
766
- console.error(`Tool call: compile_project: ${absPath}`);
767
- try {
768
- // Load and combine script templates
769
- const compileProjectTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('compile_project');
770
- const ensureProjectOpenTemplate = yield (0, scriptLoader_1.loadScriptTemplate)('ensure_project_open');
771
- const combinedScript = ensureProjectOpenTemplate + '\n\n' + compileProjectTemplate;
772
- const scriptContent = (0, scriptLoader_1.interpolateScript)(combinedScript, {
773
- 'PROJECT_FILE_PATH': absPath.replace(/\\/g, '\\\\')
774
- });
775
- const result = yield (0, codesys_interop_1.executeCodesysScript)(scriptContent, codesysExePath, codesysProfileName, sessionManager);
776
- const success = result.success && result.output.includes("SCRIPT_SUCCESS");
777
- // Check for actual compile errors in the output log
778
- const hasCompileErrors = result.output.includes("Compile complete --") && !/ 0 error\(s\),/.test(result.output);
779
- let message = success ? `Compilation initiated for application in ${absPath}. Check CODESYS messages for results.` : `Failed initiating compilation for ${absPath}. Output:\n${result.output}`;
780
- let isError = !success; // Base error status on script success
781
- if (success && hasCompileErrors) {
782
- message += " WARNING: Build command reported errors in the output log.";
783
- console.warn("Compile project reported build errors in the output.");
784
- // Report as error if compile fails, even if script technically succeeded
785
- isError = true;
786
- }
787
- return {
788
- content: [{ type: "text", text: message }],
789
- isError: isError
790
- };
791
- }
792
- catch (e) {
793
- console.error(`Error compile_project ${absPath}: ${e}`);
794
- return {
795
- content: [{ type: "text", text: `Error: ${e.message}` }],
796
- isError: true
797
- };
798
- }
799
- }));
800
- // --- Session Management Tools ---
801
- server.tool("start_session", "Starts a persistent CODESYS session for improved performance with multiple operations", {}, () => __awaiter(this, void 0, void 0, function* () {
802
- console.error("Tool call: start_session");
803
- try {
804
- const currentState = sessionManager.getState();
805
- if (currentState === sessionManager_1.SessionState.RUNNING || currentState === sessionManager_1.SessionState.BUSY) {
806
- return {
807
- content: [{
808
- type: "text",
809
- text: "CODESYS session is already running"
810
- }],
811
- isError: false
812
- };
813
- }
814
- if (currentState === sessionManager_1.SessionState.STARTING) {
815
- return {
816
- content: [{
817
- type: "text",
818
- text: "CODESYS session is already starting"
819
- }],
820
- isError: false
821
- };
822
- }
823
- yield sessionManager.startSession();
824
- return {
825
- content: [{
826
- type: "text",
827
- text: "CODESYS session started successfully"
828
- }],
829
- isError: false
830
- };
831
- }
832
- catch (error) {
833
- console.error(`Error starting session: ${error.message}`);
834
- return {
835
- content: [{
836
- type: "text",
837
- text: `Failed to start CODESYS session: ${error.message}`
838
- }],
839
- isError: true
840
- };
841
- }
842
- }));
843
- server.tool("stop_session", "Stops the persistent CODESYS session", {}, () => __awaiter(this, void 0, void 0, function* () {
844
- console.error("Tool call: stop_session");
845
- try {
846
- const currentState = sessionManager.getState();
847
- if (currentState === sessionManager_1.SessionState.STOPPED) {
848
- return {
849
- content: [{
850
- type: "text",
851
- text: "CODESYS session is already stopped"
852
- }],
853
- isError: false
854
- };
855
- }
856
- if (currentState === sessionManager_1.SessionState.STOPPING) {
857
- return {
858
- content: [{
859
- type: "text",
860
- text: "CODESYS session is already stopping"
861
- }],
862
- isError: false
863
- };
864
- }
865
- yield sessionManager.stopSession();
866
- return {
867
- content: [{
868
- type: "text",
869
- text: "CODESYS session stopped successfully"
870
- }],
871
- isError: false
872
- };
873
- }
874
- catch (error) {
875
- console.error(`Error stopping session: ${error.message}`);
876
- return {
877
- content: [{
878
- type: "text",
879
- text: `Failed to stop CODESYS session: ${error.message}`
880
- }],
881
- isError: true
882
- };
883
- }
884
- }));
885
- server.tool("session_status", "Returns the status of the CODESYS session", {}, () => __awaiter(this, void 0, void 0, function* () {
886
- console.error("Tool call: session_status");
887
- try {
888
- const state = sessionManager.getState();
889
- return {
890
- content: [{
891
- type: "text",
892
- text: `CODESYS session status: ${state}`
893
- }],
894
- isError: false
895
- };
896
- }
897
- catch (error) {
898
- console.error(`Error getting session status: ${error.message}`);
899
- return {
900
- content: [{
901
- type: "text",
902
- text: `Failed to get CODESYS session status: ${error.message}`
903
- }],
904
- isError: true
905
- };
906
- }
907
- }));
908
- // --- End Tools ---
909
- console.error("SERVER.TS: Resources and Tools defined.");
910
- // --- End MCP Resources / Tools Definitions ---
911
- // --- Server Connection ---
912
- console.error("SERVER.TS: startServer() internal logic executing.");
913
- try {
914
- const transport = new stdio_js_1.StdioServerTransport();
915
- console.error("SERVER.TS: Connecting MCP server via stdio...");
916
- // No need to await connect here if startMcpServer is called by bin.ts which awaits it
917
- server.connect(transport); // Connect but don't await here, let bin.ts handle waiting
918
- console.error("SERVER.TS: MCP Server connection initiated via stdio.");
919
- }
920
- catch (error) {
921
- console.error("FATAL: Failed to initiate MCP server connection:", error);
922
- // Re-throw error so bin.ts can catch it
923
- throw error;
924
- }
925
- // --- End Server Connection ---
926
- });
927
- } // --- End of startMcpServer function ---
928
- // --- Graceful Shutdown / Unhandled Rejection ---
929
- // These should remain at the top level, outside startMcpServer
930
- process.on('SIGINT', () => {
931
- console.error('\nSERVER.TS: SIGINT received, shutting down...');
932
- process.exit(0);
933
- });
934
- process.on('SIGTERM', () => {
935
- console.error('\nSERVER.TS: SIGTERM received, shutting down...');
936
- process.exit(0);
937
- });
938
- process.on('unhandledRejection', (reason, promise) => {
939
- console.error('SERVER.TS: Unhandled Rejection at:', promise, 'reason:', reason);
940
- });
941
- // --- End Graceful Shutdown / Unhandled Rejection ---
942
- console.error(">>> SERVER.TS Module Parsed <<<"); // Log end of script parsing
32
+ return server;
33
+ }
34
+ //# sourceMappingURL=server.js.map