codesysultra 1.0.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.
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +72 -0
- package/LICENSE +190 -0
- package/README.md +156 -0
- package/dist/bin.js +71 -0
- package/dist/codesys_interop.js +270 -0
- package/dist/handlers/resources.js +193 -0
- package/dist/handlers/tools.js +332 -0
- package/dist/server.js +122 -0
- package/dist/templates/check_status.py +21 -0
- package/dist/templates/compile_project.py +55 -0
- package/dist/templates/create_method.py +68 -0
- package/dist/templates/create_pou.py +68 -0
- package/dist/templates/create_project.py +54 -0
- package/dist/templates/create_property.py +66 -0
- package/dist/templates/ensure_project_open.py +154 -0
- package/dist/templates/find_object_by_path.py +111 -0
- package/dist/templates/get_pou_code.py +73 -0
- package/dist/templates/get_project_structure.py +64 -0
- package/dist/templates/open_project.py +19 -0
- package/dist/templates/save_project.py +23 -0
- package/dist/templates/set_pou_code.py +90 -0
- package/dist/templates.js +42 -0
- package/dist/types.js +8 -0
- package/dist/utils.js +29 -0
- package/docs/configuration.md +141 -0
- package/docs/index.md +26 -0
- package/docs/installation.md +84 -0
- package/examples/README.md +78 -0
- package/examples/sample_config.json +32 -0
- package/package.json +48 -0
- package/scripts/copy-templates.js +31 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import sys, scriptengine as script_engine, os, traceback
|
|
2
|
+
{{ENSURE_PROJECT_OPEN_PYTHON_SNIPPET}}
|
|
3
|
+
{{FIND_OBJECT_BY_PATH_PYTHON_SNIPPET}}
|
|
4
|
+
POU_FULL_PATH = "{POU_FULL_PATH}" # Expecting format like "Application/MyPOU" or "Folder/SubFolder/MyPOU"
|
|
5
|
+
DECLARATION_CONTENT = """{DECLARATION_CONTENT}"""
|
|
6
|
+
IMPLEMENTATION_CONTENT = """{IMPLEMENTATION_CONTENT}"""
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
print("DEBUG: set_pou_code script: POU_FULL_PATH='%s', Project='%s'" % (POU_FULL_PATH, PROJECT_FILE_PATH))
|
|
10
|
+
primary_project = ensure_project_open(PROJECT_FILE_PATH)
|
|
11
|
+
if not POU_FULL_PATH: raise ValueError("POU full path empty.")
|
|
12
|
+
|
|
13
|
+
# Find the target POU/Method/Property object
|
|
14
|
+
target_object = find_object_by_path_robust(primary_project, POU_FULL_PATH, "target object")
|
|
15
|
+
if not target_object: raise ValueError("Target object not found using path: %s" % POU_FULL_PATH)
|
|
16
|
+
|
|
17
|
+
target_name = getattr(target_object, 'get_name', lambda: POU_FULL_PATH)()
|
|
18
|
+
print("DEBUG: Found target object: %s" % target_name)
|
|
19
|
+
|
|
20
|
+
# --- Set Declaration Part ---
|
|
21
|
+
declaration_updated = False
|
|
22
|
+
# Check if the content is actually provided (might be None/empty if only impl is set)
|
|
23
|
+
has_declaration_content = 'DECLARATION_CONTENT' in locals() or 'DECLARATION_CONTENT' in globals()
|
|
24
|
+
if has_declaration_content and DECLARATION_CONTENT is not None: # Check not None
|
|
25
|
+
if hasattr(target_object, 'textual_declaration'):
|
|
26
|
+
decl_obj = target_object.textual_declaration
|
|
27
|
+
if decl_obj and hasattr(decl_obj, 'replace'):
|
|
28
|
+
try:
|
|
29
|
+
print("DEBUG: Accessing textual_declaration...")
|
|
30
|
+
decl_obj.replace(DECLARATION_CONTENT)
|
|
31
|
+
print("DEBUG: Set declaration text using replace().")
|
|
32
|
+
declaration_updated = True
|
|
33
|
+
except Exception as decl_err:
|
|
34
|
+
print("ERROR: Failed to set declaration text: %s" % decl_err)
|
|
35
|
+
traceback.print_exc() # Print stack trace for detailed error
|
|
36
|
+
else:
|
|
37
|
+
print("WARN: Target '%s' textual_declaration attribute is None or does not have replace(). Skipping declaration update." % target_name)
|
|
38
|
+
else:
|
|
39
|
+
print("WARN: Target '%s' does not have textual_declaration attribute. Skipping declaration update." % target_name)
|
|
40
|
+
else:
|
|
41
|
+
print("DEBUG: Declaration content not provided or is None. Skipping declaration update.")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# --- Set Implementation Part ---
|
|
45
|
+
implementation_updated = False
|
|
46
|
+
has_implementation_content = 'IMPLEMENTATION_CONTENT' in locals() or 'IMPLEMENTATION_CONTENT' in globals()
|
|
47
|
+
if has_implementation_content and IMPLEMENTATION_CONTENT is not None: # Check not None
|
|
48
|
+
if hasattr(target_object, 'textual_implementation'):
|
|
49
|
+
impl_obj = target_object.textual_implementation
|
|
50
|
+
if impl_obj and hasattr(impl_obj, 'replace'):
|
|
51
|
+
try:
|
|
52
|
+
print("DEBUG: Accessing textual_implementation...")
|
|
53
|
+
impl_obj.replace(IMPLEMENTATION_CONTENT)
|
|
54
|
+
print("DEBUG: Set implementation text using replace().")
|
|
55
|
+
implementation_updated = True
|
|
56
|
+
except Exception as impl_err:
|
|
57
|
+
print("ERROR: Failed to set implementation text: %s" % impl_err)
|
|
58
|
+
traceback.print_exc() # Print stack trace for detailed error
|
|
59
|
+
else:
|
|
60
|
+
print("WARN: Target '%s' textual_implementation attribute is None or does not have replace(). Skipping implementation update." % target_name)
|
|
61
|
+
else:
|
|
62
|
+
print("WARN: Target '%s' does not have textual_implementation attribute. Skipping implementation update." % target_name)
|
|
63
|
+
else:
|
|
64
|
+
print("DEBUG: Implementation content not provided or is None. Skipping implementation update.")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# --- SAVE THE PROJECT TO PERSIST THE CODE CHANGE ---
|
|
68
|
+
# Only save if something was actually updated to avoid unnecessary saves
|
|
69
|
+
if declaration_updated or implementation_updated:
|
|
70
|
+
try:
|
|
71
|
+
print("DEBUG: Saving Project (after code change)...")
|
|
72
|
+
primary_project.save() # Save the overall project file
|
|
73
|
+
print("DEBUG: Project saved successfully after code change.")
|
|
74
|
+
except Exception as save_err:
|
|
75
|
+
print("ERROR: Failed to save Project after setting code: %s" % save_err)
|
|
76
|
+
detailed_error = traceback.format_exc()
|
|
77
|
+
error_message = "Error saving Project after code change for '%s': %s\\n%s" % (target_name, save_err, detailed_error)
|
|
78
|
+
print(error_message); print("SCRIPT_ERROR: %s" % error_message); sys.exit(1)
|
|
79
|
+
else:
|
|
80
|
+
print("DEBUG: No code parts were updated, skipping project save.")
|
|
81
|
+
# --- END SAVING ---
|
|
82
|
+
|
|
83
|
+
print("Code Set For: %s" % target_name)
|
|
84
|
+
print("Path: %s" % POU_FULL_PATH)
|
|
85
|
+
print("SCRIPT_SUCCESS: Declaration and/or implementation set successfully."); sys.exit(0)
|
|
86
|
+
|
|
87
|
+
except Exception as e:
|
|
88
|
+
detailed_error = traceback.format_exc()
|
|
89
|
+
error_message = "Error setting code for object '%s' in project '%s': %s\\n%s" % (POU_FULL_PATH, PROJECT_FILE_PATH, e, detailed_error)
|
|
90
|
+
print(error_message); print("SCRIPT_ERROR: %s" % error_message); sys.exit(1)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GET_POU_CODE_SCRIPT_TEMPLATE = exports.CREATE_METHOD_SCRIPT_TEMPLATE = exports.CREATE_PROPERTY_SCRIPT_TEMPLATE = exports.SET_POU_CODE_SCRIPT_TEMPLATE = exports.CREATE_POU_SCRIPT_TEMPLATE = exports.GET_PROJECT_STRUCTURE_SCRIPT_TEMPLATE = exports.COMPILE_PROJECT_SCRIPT_TEMPLATE = exports.SAVE_PROJECT_SCRIPT_TEMPLATE = exports.OPEN_PROJECT_SCRIPT_TEMPLATE = exports.CREATE_PROJECT_SCRIPT_TEMPLATE = exports.CHECK_STATUS_SCRIPT = exports.FIND_OBJECT_BY_PATH_PYTHON_SNIPPET = exports.ENSURE_PROJECT_OPEN_PYTHON_SNIPPET = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const templatesDir = path_1.default.join(__dirname, 'templates');
|
|
10
|
+
function readTemplate(filename) {
|
|
11
|
+
const filePath = path_1.default.join(templatesDir, filename);
|
|
12
|
+
try {
|
|
13
|
+
return fs_1.default.readFileSync(filePath, 'utf-8');
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
console.error(`Error reading template file: ${filePath}`, error);
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// --- Base Snippets ---
|
|
21
|
+
exports.ENSURE_PROJECT_OPEN_PYTHON_SNIPPET = readTemplate('ensure_project_open.py');
|
|
22
|
+
exports.FIND_OBJECT_BY_PATH_PYTHON_SNIPPET = readTemplate('find_object_by_path.py');
|
|
23
|
+
// --- Independent Scripts ---
|
|
24
|
+
exports.CHECK_STATUS_SCRIPT = readTemplate('check_status.py');
|
|
25
|
+
exports.CREATE_PROJECT_SCRIPT_TEMPLATE = readTemplate('create_project.py');
|
|
26
|
+
// --- Dependent Scripts ---
|
|
27
|
+
// Helper to inject dependencies safely
|
|
28
|
+
function injectDependencies(content) {
|
|
29
|
+
// Use split/join to replace all occurrences and avoid special character issues in replacement string
|
|
30
|
+
let result = content.split('{{ENSURE_PROJECT_OPEN_PYTHON_SNIPPET}}').join(exports.ENSURE_PROJECT_OPEN_PYTHON_SNIPPET);
|
|
31
|
+
result = result.split('{{FIND_OBJECT_BY_PATH_PYTHON_SNIPPET}}').join(exports.FIND_OBJECT_BY_PATH_PYTHON_SNIPPET);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
exports.OPEN_PROJECT_SCRIPT_TEMPLATE = injectDependencies(readTemplate('open_project.py'));
|
|
35
|
+
exports.SAVE_PROJECT_SCRIPT_TEMPLATE = injectDependencies(readTemplate('save_project.py'));
|
|
36
|
+
exports.COMPILE_PROJECT_SCRIPT_TEMPLATE = injectDependencies(readTemplate('compile_project.py'));
|
|
37
|
+
exports.GET_PROJECT_STRUCTURE_SCRIPT_TEMPLATE = injectDependencies(readTemplate('get_project_structure.py'));
|
|
38
|
+
exports.CREATE_POU_SCRIPT_TEMPLATE = injectDependencies(readTemplate('create_pou.py'));
|
|
39
|
+
exports.SET_POU_CODE_SCRIPT_TEMPLATE = injectDependencies(readTemplate('set_pou_code.py'));
|
|
40
|
+
exports.CREATE_PROPERTY_SCRIPT_TEMPLATE = injectDependencies(readTemplate('create_property.py'));
|
|
41
|
+
exports.CREATE_METHOD_SCRIPT_TEMPLATE = injectDependencies(readTemplate('create_method.py'));
|
|
42
|
+
exports.GET_POU_CODE_SCRIPT_TEMPLATE = injectDependencies(readTemplate('get_pou_code.py'));
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImplementationLanguageEnum = exports.PouTypeEnum = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
// --- Zod Schemas ---
|
|
6
|
+
exports.PouTypeEnum = zod_1.z.enum(["Program", "FunctionBlock", "Function"]);
|
|
7
|
+
exports.ImplementationLanguageEnum = zod_1.z.enum(["ST", "LD", "FBD", "SFC", "IL", "CFC", "StructuredText", "LadderDiagram", "FunctionBlockDiagram", "SequentialFunctionChart", "InstructionList", "ContinuousFunctionChart"]);
|
|
8
|
+
// --- End Zod Schemas ---
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.fileExists = fileExists;
|
|
13
|
+
const promises_1 = require("fs/promises");
|
|
14
|
+
// --- Helper Function (fileExists - async version) ---
|
|
15
|
+
function fileExists(filePath) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
try {
|
|
18
|
+
yield (0, promises_1.stat)(filePath);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.code === 'ENOENT') {
|
|
23
|
+
return false; // File does not exist
|
|
24
|
+
}
|
|
25
|
+
throw error; // Other error
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
// --- End Helper Function ---
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Configuration Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to configure the CODESYS MCP Toolkit for use with MCP clients, specifically focusing on Claude Desktop.
|
|
4
|
+
|
|
5
|
+
## Claude Desktop Configuration
|
|
6
|
+
|
|
7
|
+
Claude Desktop is a popular MCP client that can use the CODESYS MCP Toolkit.
|
|
8
|
+
|
|
9
|
+
### Finding the Configuration File
|
|
10
|
+
|
|
11
|
+
The configuration file is typically located at:
|
|
12
|
+
* **Windows:** `C:\Users\<YourUsername>\AppData\Roaming\Claude\settings.json` (or `claude_desktop_config.json` in older versions)
|
|
13
|
+
* **macOS:** `~/Library/Application Support/Claude/settings.json` (or `claude_desktop_config.json`)
|
|
14
|
+
* **Linux:** `~/.config/Claude/settings.json` (or `claude_desktop_config.json`)
|
|
15
|
+
|
|
16
|
+
*(If you can't find `settings.json`, look for `claude_desktop_config.json`)*
|
|
17
|
+
|
|
18
|
+
### Recommended Configuration: Direct Command
|
|
19
|
+
|
|
20
|
+
Due to potential environment variable issues (especially with `PATH`) when launching Node.js tools via wrappers like `npx` within certain host applications (e.g., Claude Desktop), it is **strongly recommended** to configure Claude Desktop to run the installed command `codesys-mcp-tool` **directly**.
|
|
21
|
+
|
|
22
|
+
Edit the configuration file and add the CODESYS MCP server to the `mcpServers` object:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
// ... other servers ...
|
|
28
|
+
"codesys_local": {
|
|
29
|
+
"command": "codesys-mcp-tool", // <<< Use the direct command name
|
|
30
|
+
"args": [
|
|
31
|
+
// Pass arguments directly to the tool using flags
|
|
32
|
+
"--codesys-path", "C:\\Program Files\\Path\\To\\Your\\CODESYS\\Common\\CODESYS.exe", // Customize this path!
|
|
33
|
+
"--codesys-profile", "Your CODESYS Profile Name" // Customize this profile name!
|
|
34
|
+
// Optional: Add --workspace "/path/to/your/projects" if needed
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
// ... other servers ...
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Why Direct Command is Recommended
|
|
43
|
+
|
|
44
|
+
Launching with `npx` (`"command": "npx"`) has been observed to cause errors (`'C:\Program' is not recognized...` or profile errors) when run from within applications like Claude Desktop. This is likely due to `npx` altering the execution environment (like the `PATH` variable) in a way that interferes with how CODESYS finds its own components or parses arguments when run non-interactively. Using the globally installed `codesys-mcp-tool` command directly avoids this interference.
|
|
45
|
+
|
|
46
|
+
### Customizing for Your Environment
|
|
47
|
+
|
|
48
|
+
You **must** customize:
|
|
49
|
+
1. `--codesys-path`: Set this value to the **full and correct path** of your specific `CODESYS.exe` executable file.
|
|
50
|
+
2. `--codesys-profile`: Set this value to the **exact name** of the CODESYS profile you want to use (this name is visible in the CODESYS UI or start menu).
|
|
51
|
+
|
|
52
|
+
**Important Notes:**
|
|
53
|
+
* Use double backslashes (`\\`) for paths in the JSON file if you are on Windows.
|
|
54
|
+
* The profile name must match *exactly*, including capitalization and spacing.
|
|
55
|
+
* Ensure `codesys-mcp-tool` is accessible in the system PATH where Claude Desktop runs. Global installation via `npm install -g` usually handles this.
|
|
56
|
+
|
|
57
|
+
### Applying Changes
|
|
58
|
+
|
|
59
|
+
After modifying the configuration:
|
|
60
|
+
1. Save the `settings.json` file.
|
|
61
|
+
2. **Restart Claude Desktop completely** (ensure it's fully closed and reopened).
|
|
62
|
+
3. Claude should now attempt to connect to the CODESYS MCP server using the direct command.
|
|
63
|
+
|
|
64
|
+
### Alternative (Not Recommended): Using `npx`
|
|
65
|
+
|
|
66
|
+
If you cannot use the direct command method for some reason, you can try `npx`, but be aware of the potential issues mentioned above.
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
// Example using npx (POTENTIALLY PROBLEMATIC - USE WITH CAUTION):
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"codesys_local": {
|
|
73
|
+
"command": "npx",
|
|
74
|
+
"args": [
|
|
75
|
+
"-y", // Tells npx to install temporarily if not found globally
|
|
76
|
+
"@codesys/mcp-toolkit",
|
|
77
|
+
// Arguments for the tool MUST come AFTER the package name
|
|
78
|
+
"--codesys-path", "C:\\Program Files\\Path\\To\\Your\\CODESYS\\Common\\CODESYS.exe",
|
|
79
|
+
"--codesys-profile", "Your CODESYS Profile Name"
|
|
80
|
+
// Optional: "--workspace", "/path/to/your/projects"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
*If you encounter errors with `npx`, switch to the recommended Direct Command method.*
|
|
87
|
+
|
|
88
|
+
## Command-Line Options (for `codesys-mcp-tool`)
|
|
89
|
+
|
|
90
|
+
When running `codesys-mcp-tool` directly or configuring it in your MCP client, you can use these arguments:
|
|
91
|
+
|
|
92
|
+
* `-p, --codesys-path <path>`: Full path to `CODESYS.exe`. (Required, overrides `CODESYS_PATH` env var, has a default but relying on it is not recommended).
|
|
93
|
+
* `-f, --codesys-profile <profile>`: Name of the CODESYS profile. (Required, overrides `CODESYS_PROFILE` env var, has a default but relying on it is not recommended).
|
|
94
|
+
* `-w, --workspace <dir>`: Workspace directory for resolving relative project paths passed to tools. Defaults to the directory where the command was launched (which might be unpredictable when run by another application). Setting this explicitly might be needed if using relative paths.
|
|
95
|
+
* `-V, --version`: Output the version number.
|
|
96
|
+
* `-h, --help`: Display help for command.
|
|
97
|
+
|
|
98
|
+
## Verifying Configuration
|
|
99
|
+
|
|
100
|
+
To verify your configuration within Claude Desktop:
|
|
101
|
+
|
|
102
|
+
1. After restarting Claude with the correct configuration, wait for the server to connect (check logs if needed).
|
|
103
|
+
2. Ask Claude:
|
|
104
|
+
```
|
|
105
|
+
Can you please check the status of the CODESYS Local server?
|
|
106
|
+
```
|
|
107
|
+
(Replace `CODESYS Local` if you used a different key in `mcpServers`).
|
|
108
|
+
|
|
109
|
+
3. Claude should use the `codesys://project/status` resource. A successful response will show information like `Scripting OK: true`, `Project Open: ...`, etc. An error indicates a problem with the connection, CODESYS setup, or the script execution itself (check logs).
|
|
110
|
+
|
|
111
|
+
## Multiple CODESYS Installations
|
|
112
|
+
|
|
113
|
+
If you have multiple CODESYS installations, you can create multiple server entries in `mcpServers` with different keys (names) and point each to the specific installation:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"mcpServers": {
|
|
118
|
+
"codesys_sp21": { // Unique key for this server
|
|
119
|
+
"command": "codesys-mcp-tool", // Direct command
|
|
120
|
+
"args": [
|
|
121
|
+
"--codesys-path", "C:\\Program Files\\CODESYS 3.5.21.0\\CODESYS\\Common\\CODESYS.exe",
|
|
122
|
+
"--codesys-profile", "CODESYS V3.5 SP21"
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"codesys_sp19": { // Unique key for this server
|
|
126
|
+
"command": "codesys-mcp-tool", // Direct command
|
|
127
|
+
"args": [
|
|
128
|
+
"--codesys-path", "C:\\Program Files\\CODESYS 3.5.19.0\\CODESYS\\Common\\CODESYS.exe",
|
|
129
|
+
"--codesys-profile", "CODESYS V3.5 SP19"
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
// ... other servers ...
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Next Steps
|
|
138
|
+
|
|
139
|
+
* Try some [basic examples](examples.md)
|
|
140
|
+
* Learn about the [available tools and resources](api.md)
|
|
141
|
+
* Check the [troubleshooting guide](troubleshooting.md) if you encounter issues
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# CODESYS MCP Toolkit Documentation
|
|
2
|
+
|
|
3
|
+
Welcome to the documentation for the CODESYS MCP Toolkit!
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The CODESYS MCP Toolkit provides a bridge between Model Context Protocol (MCP) clients and CODESYS V3 programming environments. This toolkit enables seamless interaction for automating project management, POU creation, code editing, and compilation tasks.
|
|
8
|
+
|
|
9
|
+
## Components
|
|
10
|
+
|
|
11
|
+
- **MCP Server**: Core component that handles MCP protocol communication
|
|
12
|
+
- **CODESYS Script Integration**: Interface with CODESYS scripting engine
|
|
13
|
+
- **Command-Line Interface**: For direct usage and configuration
|
|
14
|
+
|
|
15
|
+
## Getting Started
|
|
16
|
+
|
|
17
|
+
- [Installation](installation.md)
|
|
18
|
+
- [Configuration](configuration.md)
|
|
19
|
+
- [Usage Examples](examples.md)
|
|
20
|
+
- [API Reference](api.md)
|
|
21
|
+
|
|
22
|
+
## Advanced Topics
|
|
23
|
+
|
|
24
|
+
- [Contributing Guidelines](../CONTRIBUTING.md)
|
|
25
|
+
- [Troubleshooting](troubleshooting.md)
|
|
26
|
+
- [Custom Extensions](extensions.md)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Installation Guide
|
|
2
|
+
|
|
3
|
+
This guide covers how to install the CODESYS MCP Toolkit on your system.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
Before installing, ensure you have:
|
|
8
|
+
|
|
9
|
+
1. **Node.js**: Version 18 or later
|
|
10
|
+
- Download from [nodejs.org](https://nodejs.org/)
|
|
11
|
+
- Verify installation with `node --version`
|
|
12
|
+
|
|
13
|
+
2. **CODESYS V3**: A working CODESYS V3 installation
|
|
14
|
+
- Tested with version 3.5 SP21
|
|
15
|
+
- Must have the **Scripting Engine** component enabled
|
|
16
|
+
- Verify by opening CODESYS and checking if scripting commands are available
|
|
17
|
+
|
|
18
|
+
3. **MCP Client**: An MCP-enabled application (e.g., Claude Desktop)
|
|
19
|
+
|
|
20
|
+
## Installation Methods
|
|
21
|
+
|
|
22
|
+
### Method 1: Install from npm (Recommended)
|
|
23
|
+
|
|
24
|
+
The simplest way to install is directly from npm:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g @codesys/mcp-toolkit
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This installs the package globally on your system, making the `codesys-mcp-toolkit` command available in your terminal.
|
|
31
|
+
|
|
32
|
+
Verify the installation by running:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
codesys-mcp-toolkit --version
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Method 2: Install from Source
|
|
39
|
+
|
|
40
|
+
For development or to get the latest unreleased changes:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Clone the repository
|
|
44
|
+
git clone https://github.com/yourusername/codesys-mcp-toolkit.git
|
|
45
|
+
cd codesys-mcp-toolkit
|
|
46
|
+
|
|
47
|
+
# Install dependencies
|
|
48
|
+
npm install
|
|
49
|
+
|
|
50
|
+
# Build the project
|
|
51
|
+
npm run build
|
|
52
|
+
|
|
53
|
+
# Install globally
|
|
54
|
+
npm install -g .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Verify the installation using the same command as above.
|
|
58
|
+
|
|
59
|
+
## Next Steps
|
|
60
|
+
|
|
61
|
+
After installation:
|
|
62
|
+
|
|
63
|
+
1. [Configure your MCP client](configuration.md) to use the CODESYS MCP Toolkit
|
|
64
|
+
2. Try some [basic examples](examples.md)
|
|
65
|
+
3. Learn about the [available tools and resources](api.md)
|
|
66
|
+
|
|
67
|
+
## Troubleshooting Installation Issues
|
|
68
|
+
|
|
69
|
+
### Common Issues
|
|
70
|
+
|
|
71
|
+
- **Permission errors**: You may need to run npm with administrative privileges
|
|
72
|
+
```bash
|
|
73
|
+
sudo npm install -g @codesys/mcp-toolkit # On Linux/macOS
|
|
74
|
+
# or use PowerShell as Administrator on Windows
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- **Path issues**: Ensure the npm global bin directory is in your PATH
|
|
78
|
+
|
|
79
|
+
- **Node.js version**: Verify you have Node.js 18 or later
|
|
80
|
+
```bash
|
|
81
|
+
node --version
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If you encounter other issues, please check the [troubleshooting guide](troubleshooting.md) or [open an issue](https://github.com/yourusername/codesys-mcp-toolkit/issues) on GitHub.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# CODESYS MCP Toolkit Examples
|
|
2
|
+
|
|
3
|
+
This directory contains examples of how to use the CODESYS MCP toolkit effectively.
|
|
4
|
+
|
|
5
|
+
## Example Configuration Files
|
|
6
|
+
|
|
7
|
+
### Basic MCP Configuration
|
|
8
|
+
|
|
9
|
+
`claude_desktop_config.json` - Example configuration for Claude Desktop:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"codesys_local": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": [
|
|
17
|
+
"-y",
|
|
18
|
+
"@codesys/mcp-toolkit",
|
|
19
|
+
"--codesys-path", "C:\\Program Files\\CODESYS 3.5.21.0\\CODESYS\\Common\\CODESYS.exe",
|
|
20
|
+
"--codesys-profile", "CODESYS V3.5 SP21"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Example Prompts
|
|
28
|
+
|
|
29
|
+
Here are some example prompts you can use with Claude Desktop:
|
|
30
|
+
|
|
31
|
+
### Create a Motor Controller Function Block
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
Using the CODESYS Local server, please create a MotorController function block with the following:
|
|
35
|
+
|
|
36
|
+
1. Properties:
|
|
37
|
+
- SpeedSetpoint (INT)
|
|
38
|
+
- ActualSpeed (INT)
|
|
39
|
+
- IsRunning (BOOL)
|
|
40
|
+
|
|
41
|
+
2. Methods:
|
|
42
|
+
- Start() - Sets IsRunning to TRUE
|
|
43
|
+
- Stop() - Sets IsRunning to FALSE
|
|
44
|
+
- SetSpeed(speed: INT) - Sets SpeedSetpoint to the given value
|
|
45
|
+
|
|
46
|
+
Create this in a new project at C:/MyProjects/MotorControl.project
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Open and Analyze an Existing Project
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Using the CODESYS Local server, please open the project at C:/MyProjects/ExistingProject.project and show me its structure. Then, show me the code for the main POU.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Compile a Project
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Using the CODESYS Local server, please open the project at C:/MyProjects/MyProject.project and compile it. Let me know if there are any errors.
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Programmatic Usage Examples
|
|
62
|
+
|
|
63
|
+
If you're integrating directly with the server code, here are some examples:
|
|
64
|
+
|
|
65
|
+
### CLI Usage
|
|
66
|
+
|
|
67
|
+
Run the server directly:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
codesys-mcp-toolkit --codesys-path "C:\Program Files\CODESYS 3.5.21.0\CODESYS\Common\CODESYS.exe" --codesys-profile "CODESYS V3.5 SP21"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Use Cases
|
|
74
|
+
|
|
75
|
+
* Automated PLC project creation
|
|
76
|
+
* Standardized Function Block library management
|
|
77
|
+
* Continuous integration for CODESYS projects
|
|
78
|
+
* Documentation generation from CODESYS projects
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"codesys_local": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": [
|
|
6
|
+
"-y",
|
|
7
|
+
"@codesys/mcp-toolkit",
|
|
8
|
+
"--codesys-path", "C:\\Program Files\\CODESYS 3.5.21.0\\CODESYS\\Common\\CODESYS.exe",
|
|
9
|
+
"--codesys-profile", "CODESYS V3.5 SP21"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"filesystem": {
|
|
13
|
+
"command": "npx",
|
|
14
|
+
"args": [
|
|
15
|
+
"-y",
|
|
16
|
+
"@modelcontextprotocol/server-filesystem",
|
|
17
|
+
"C:/MyProjects"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"memory": {
|
|
21
|
+
"command": "npx",
|
|
22
|
+
"args": [
|
|
23
|
+
"-y",
|
|
24
|
+
"@modelcontextprotocol/server-memory"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"puppeteer": {
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": ["-y", "@modelcontextprotocol/server-puppeteer"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codesysultra",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Model Context Protocol (MCP) server for CODESYS automation platform",
|
|
5
|
+
"main": "dist/server.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"codesys-mcp-tool": "./dist/bin.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"build": "tsc && node scripts/copy-templates.js",
|
|
12
|
+
"start": "node ./dist/bin.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"codesys",
|
|
16
|
+
"mcp",
|
|
17
|
+
"modelcontextprotocol",
|
|
18
|
+
"plc",
|
|
19
|
+
"automation",
|
|
20
|
+
"programming"
|
|
21
|
+
],
|
|
22
|
+
"author": "Nirithy",
|
|
23
|
+
"license": "Apache-2.0",
|
|
24
|
+
"type": "commonjs",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/johannesPettersson80/codesys-mcp-toolkit.git"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/johannesPettersson80/codesys-mcp-toolkit#readme",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/johannesPettersson80/codesys-mcp-toolkit/issues"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.10.2",
|
|
38
|
+
"axios": "^1.6.8",
|
|
39
|
+
"commander": "^11.1.0",
|
|
40
|
+
"yargs": "^17.7.2",
|
|
41
|
+
"zod": "^3.24.3"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^20.14.1",
|
|
45
|
+
"@types/yargs": "^17.0.33",
|
|
46
|
+
"typescript": "^5.5.3"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const srcDir = path.join(__dirname, '../src/templates');
|
|
5
|
+
const distDir = path.join(__dirname, '../dist/templates');
|
|
6
|
+
|
|
7
|
+
console.log(`Copying templates from ${srcDir} to ${distDir}...`);
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(srcDir)) {
|
|
10
|
+
console.error(`Source directory ${srcDir} does not exist.`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(distDir)) {
|
|
15
|
+
fs.mkdirSync(distDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const files = fs.readdirSync(srcDir);
|
|
19
|
+
let copiedCount = 0;
|
|
20
|
+
|
|
21
|
+
files.forEach(file => {
|
|
22
|
+
if (file.endsWith('.py')) {
|
|
23
|
+
const srcFile = path.join(srcDir, file);
|
|
24
|
+
const distFile = path.join(distDir, file);
|
|
25
|
+
fs.copyFileSync(srcFile, distFile);
|
|
26
|
+
console.log(`Copied ${file}`);
|
|
27
|
+
copiedCount++;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
console.log(`Successfully copied ${copiedCount} template files.`);
|