diff --git a/README.md b/README.md index fb8eacb..801febe 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,32 @@ See [Installation](#installation) for source-install / upgrade / multi-install s --- +## Live source-control diff (`--auto-mirror`) + +When `--auto-mirror` is added to the server args, every successful modifying tool call (`set_pou_code`, `create_pou`, `rename_object`, `add_library`, ...) is followed by an automatic `mirror_export`. The textual `/mcp-mirror/` tree is refreshed on disk in lock-step with the binary `.project`, so an external editor watching the folder sees the change immediately. The first refresh on a given project also fires `code --add ` once, which appends the mirror folder to your active VSCode window so the diff shows up in the Source Control panel. + +Enable it by adding the flag to the relevant entry's `args` in `.mcp.json`: + +```jsonc +"args": [ + "--codesys-path", "...", + "--codesys-profile", "...", + "--mode", "persistent", + "--auto-mirror" +] +``` + +Recommended one-time setup so the Source Control panel has a baseline to diff against: + +```bash +cd /mcp-mirror +git init && git add -A && git commit -m "baseline" +``` + +After that, watch VSCode's Source Control panel as Claude edits — every tool call shows up as a real diff. The VSCode hook is best-effort: if the `code` CLI shim isn't on the standard path, the mirror still refreshes silently and the response carries an `(auto-mirror: refreshed)` hint instead of opening a window. + +--- + ## What's new in this fork ### Compatibility fixes (the headline) diff --git a/src/server.ts b/src/server.ts index 629c47a..c6c9472 100644 --- a/src/server.ts +++ b/src/server.ts @@ -839,7 +839,7 @@ export async function startMcpServer(config: ServerConfig): Promise { 'open_project', { PROJECT_FILE_PATH: escaped }, ['ensure_project_open'] ); const result = await executor.executeScript(script); - return formatToolResponse(result, `Project opened: ${args.filePath}`); + return await formatModifyingResponse(result, `Project opened: ${args.filePath}`, escaped, mirrorCtx); } ); @@ -886,7 +886,7 @@ export async function startMcpServer(config: ServerConfig): Promise { TEMPLATE_PROJECT_PATH: templatePath, }); const result = await executor.executeScript(script); - return formatToolResponse(result, `Project created from template: ${absPath}`); + return await formatModifyingResponse(result, `Project created from template: ${absPath}`, absPath, mirrorCtx); } ); @@ -902,7 +902,7 @@ export async function startMcpServer(config: ServerConfig): Promise { 'save_project', { PROJECT_FILE_PATH: escaped }, ['ensure_project_open'] ); const result = await executor.executeScript(script); - return formatToolResponse(result, `Project saved: ${args.projectFilePath}`); + return await formatModifyingResponse(result, `Project saved: ${args.projectFilePath}`, escaped, mirrorCtx); } ); @@ -933,9 +933,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `POU '${args.name}' created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.` + `POU '${args.name}' created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -992,9 +994,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `Code set for '${sanPouPath}' in ${args.projectFilePath}. Project saved.` + `Code set for '${sanPouPath}' in ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1022,9 +1026,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `Property '${args.propertyName}' created under '${sanParentPath}' in ${args.projectFilePath}. Project saved.` + `Property '${args.propertyName}' created under '${sanParentPath}' in ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1052,9 +1058,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `Method '${args.methodName}' created under '${sanParentPath}' in ${args.projectFilePath}. Project saved.` + `Method '${args.methodName}' created under '${sanParentPath}' in ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1219,9 +1227,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `DUT '${args.name}' (${args.dutType}) created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.` + `DUT '${args.name}' (${args.dutType}) created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1262,9 +1272,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `GVL '${args.name}' created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.` + `GVL '${args.name}' created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1290,9 +1302,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `Folder '${args.folderName}' created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.` + `Folder '${args.folderName}' created in '${sanParentPath}' of ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1316,9 +1330,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `Object '${sanObjPath}' deleted from ${args.projectFilePath}. Project saved.` + `Object '${sanObjPath}' deleted from ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1344,9 +1360,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open', 'find_object_by_path'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `Object '${sanObjPath}' renamed to '${args.newName}' in ${args.projectFilePath}. Project saved.` + `Object '${sanObjPath}' renamed to '${args.newName}' in ${args.projectFilePath}. Project saved.`, + escProjPath, + mirrorCtx ); } ); @@ -1792,9 +1810,11 @@ export async function startMcpServer(config: ServerConfig): Promise { ['ensure_project_open'] ); const result = await executor.executeScript(script); - return formatToolResponse( + return await formatModifyingResponse( result, - `Library '${args.libraryName}' added to ${args.projectFilePath}. Project saved.` + `Library '${args.libraryName}' added to ${args.projectFilePath}. Project saved.`, + escaped, + mirrorCtx ); } );