feat(launch): launch_codesys_with_project tool
Detached spawn of an arbitrary CODESYS.exe with a .project as CLI arg and optional --Profile= override. Useful when you want an SP22-saved project opened in an SP21 IDE for SIM/inspection work, or when this MCP is bound to install A but you want install B to handle the open without registering a second server. The launched IDE is not managed by this MCP: no IPC, no watcher, no shutdown_codesys. Validates the exe + project paths up front and returns the new PID.
This commit is contained in:
parent
885ef84215
commit
7b0c5db125
3 changed files with 45 additions and 3 deletions
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "codesys-mcp-sp21-plus",
|
||||
"version": "0.9.11",
|
||||
"version": "0.9.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codesys-mcp-sp21-plus",
|
||||
"version": "0.9.11",
|
||||
"version": "0.9.12",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.26.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "codesys-mcp-sp21-plus",
|
||||
"version": "0.9.11",
|
||||
"version": "0.9.12",
|
||||
"description": "Codesys-MCP-SP21+ -- fork of luke-harriman/Codesys-MCP carrying CODESYS V3.5 SP22 Patch 1 fixes (and forward-compat with later SPs): script-engine API drift, online/runtime tool auto-login, dual-SHA release classifier, set_pou_code omitted-decl wipe fix, add_library managed-overload, etc. MCP server for CODESYS with persistent UI instance and file-based IPC.",
|
||||
"main": "dist/server.js",
|
||||
"bin": {
|
||||
|
|
|
|||
|
|
@ -863,6 +863,48 @@ export async function startMcpServer(config: ServerConfig): Promise<void> {
|
|||
}
|
||||
);
|
||||
|
||||
s.tool(
|
||||
'launch_codesys_with_project',
|
||||
"Launch a (potentially different) CODESYS install and open a project in it -- detached from this MCP. Useful for cross-version inspection (e.g. opening an SP22-saved project in an SP21 IDE for a SIM workflow), or for opening a project in an install this MCP isn't bound to. The launched IDE is NOT managed by this server: no IPC, no watcher, no shutdown_codesys. This is intentional -- the goal is just to hand the user a running IDE on a chosen install.",
|
||||
{
|
||||
projectFilePath: z.string().describe("Path to the .project file to open. Forward or back slashes both work."),
|
||||
codesysPath: z.string().optional().describe("Optional override for the CODESYS.exe to launch. Defaults to this server's configured --codesys-path. Use to launch a DIFFERENT install (e.g. SP21 while this server runs SP22)."),
|
||||
profileName: z.string().optional().describe("Optional --Profile= value for the launched IDE (e.g. 'CODESYS V3.5 SP21 Patch 5'). Omit to let CODESYS pick the project's saved profile or its install default. Mismatched profiles will pop the IDE's profile-conversion dialog -- which is sometimes exactly what you want."),
|
||||
},
|
||||
async (args: { projectFilePath: string; codesysPath?: string; profileName?: string }) => {
|
||||
const targetExe = (args.codesysPath ?? config.codesysPath).trim();
|
||||
const projectPath = resolvePath(args.projectFilePath, workspaceDir);
|
||||
if (!fs.existsSync(targetExe)) {
|
||||
return { content: [{ type: 'text' as const, text: `CODESYS executable not found: ${targetExe}` }], isError: true };
|
||||
}
|
||||
// Strip the surrounding single-quotes that resolvePath() adds for use as
|
||||
// a Python string literal. The shell spawn below quotes argv itself.
|
||||
const cleanProjectPath = projectPath.replace(/^'|'$/g, '');
|
||||
if (!fs.existsSync(cleanProjectPath)) {
|
||||
return { content: [{ type: 'text' as const, text: `Project file not found: ${cleanProjectPath}` }], isError: true };
|
||||
}
|
||||
try {
|
||||
const { spawn } = require('child_process') as typeof import('child_process');
|
||||
const argv: string[] = [];
|
||||
if (args.profileName) argv.push(`--Profile=${args.profileName}`);
|
||||
argv.push(cleanProjectPath);
|
||||
const child = spawn(targetExe, argv, { detached: true, stdio: 'ignore' });
|
||||
child.unref();
|
||||
const profileHint = args.profileName ? ` --Profile="${args.profileName}"` : '';
|
||||
return {
|
||||
content: [{
|
||||
type: 'text' as const,
|
||||
text: `Launched ${targetExe}${profileHint} with project ${cleanProjectPath}. PID ${child.pid ?? 'unknown'}. The IDE is detached -- this MCP does not manage its lifecycle.`,
|
||||
}],
|
||||
isError: false,
|
||||
};
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
return { content: [{ type: 'text' as const, text: `Launch failed: ${msg}` }], isError: true };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
s.tool(
|
||||
'shutdown_codesys',
|
||||
'Shut down the persistent CODESYS instance.',
|
||||
|
|
|
|||
Loading…
Reference in a new issue