0
0
Fork 0

feat(auto-mirror): wrap 13 modifying tools so --auto-mirror actually works

The 75cf74d scaffold added the helpers; this commit makes them
load-bearing by switching every modifying tool's formatToolResponse
call to formatModifyingResponse. Without --auto-mirror, behaviour is
unchanged. With it: mirror_export runs after each successful edit and
'code --add <mirror>' fires once per project to surface the diff in
VSCode's Source Control panel.
This commit is contained in:
Karstein Phobic Nyvold Kvistad 2026-04-27 21:53:10 +02:00
parent 4f5c80e591
commit 0fa9b3852b
2 changed files with 69 additions and 23 deletions

View file

@ -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 `<projectDir>/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 <mirrorDir>` 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 <projectDir>/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)

View file

@ -839,7 +839,7 @@ export async function startMcpServer(config: ServerConfig): Promise<void> {
'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<void> {
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<void> {
'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<void> {
['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<void> {
['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<void> {
['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<void> {
['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<void> {
['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<void> {
['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<void> {
['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<void> {
['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<void> {
['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<void> {
['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
);
}
);