0
0
Fork 0

fixup msp-con
Some checks failed
CI / build-and-test (18.x) (push) Has been cancelled
CI / build-and-test (20.x) (push) Has been cancelled
CI / build-and-test (22.x) (push) Has been cancelled

This commit is contained in:
steel 2026-08-03 17:45:08 +08:00
parent 2158c9f725
commit 729ba03593
8 changed files with 81 additions and 10 deletions

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "codesys-mcp-sp21-plus", "name": "codesys-mcp-sp21-plus",
"version": "0.13.0", "version": "0.15.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "codesys-mcp-sp21-plus", "name": "codesys-mcp-sp21-plus",
"version": "0.13.0", "version": "0.15.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.26.0", "@modelcontextprotocol/sdk": "^1.26.0",

View file

@ -5,6 +5,7 @@
import { program } from 'commander'; import { program } from 'commander';
import { startMcpServer } from './server'; import { startMcpServer } from './server';
import { resolveLogLevel, setLogLevel } from './logger';
import { ServerConfig, ExecutionMode } from './types'; import { ServerConfig, ExecutionMode } from './types';
import { detectInstalls, printConfig } from './detect'; import { detectInstalls, printConfig } from './detect';
import { inspectProjectFile, suggestedServerName } from './inspect'; import { inspectProjectFile, suggestedServerName } from './inspect';
@ -42,12 +43,14 @@ program
.option( .option(
'-p, --codesys-path <path>', '-p, --codesys-path <path>',
'Path to CODESYS executable', 'Path to CODESYS executable',
process.env.CODESYS_PATH || 'C:\\Program Files\\CODESYS 3.5.21.0\\CODESYS\\Common\\CODESYS.exe' //process.env.CODESYS_PATH || 'C:\\CODESYS3.5.21.40\\Codesys\\Common\\Codesys.exe'
process.env.CODESYS_PATH || 'C:\\Program Files\\MSP-Con 3.5.21.40\\MSP-Con\\Common\\MSP-Con.exe'
) )
.option( .option(
'-f, --codesys-profile <profile>', '-f, --codesys-profile <profile>',
'CODESYS profile name', 'CODESYS profile name',
process.env.CODESYS_PROFILE || 'CODESYS V3.5 SP21' //process.env.CODESYS_PROFILE || 'CODESYS V3.5 SP21 Patch 4'
process.env.CODESYS_PROFILE || 'MSP-Con V3.5 SP21 Patch 4'
) )
.option( .option(
'--codesys-additional-folder <dir>', '--codesys-additional-folder <dir>',
@ -87,6 +90,9 @@ program
const opts = program.opts(); const opts = program.opts();
const initialLogLevel = resolveLogLevel(opts.debug ? 'debug' : opts.verbose ? 'info' : undefined);
setLogLevel(initialLogLevel);
if (opts.sshVersion) { if (opts.sshVersion) {
// --ssh-version emits to stdout (pipe-friendly); errors go to stderr with exit 1. // --ssh-version emits to stdout (pipe-friendly); errors go to stderr with exit 1.
readRunningVersionSsh({ readRunningVersionSsh({

View file

@ -65,6 +65,7 @@ function findRichestAdditionalFolder(
try { try {
const xml = readFileSync(path.join(profilesDir, profile), 'utf-8'); const xml = readFileSync(path.join(profilesDir, profile), 'utf-8');
plugins += (xml.match(/<Hint>/g) ?? []).length; plugins += (xml.match(/<Hint>/g) ?? []).length;
console.log(`Found ${plugins} plugins in ${path.join(profilesDir, profile)}`);
} catch { } catch {
/* unreadable profile -- treat as contributing nothing */ /* unreadable profile -- treat as contributing nothing */
} }
@ -142,7 +143,7 @@ export function detectInstalls(
if (!fsApi.existsSync(exePath)) continue; if (!fsApi.existsSync(exePath)) continue;
if (seen.has(exePath.toLowerCase())) continue; if (seen.has(exePath.toLowerCase())) continue;
seen.add(exePath.toLowerCase()); seen.add(exePath.toLowerCase());
console.log(`Detected CODESYS install: ${exePath} (V${major}.${minor} SP${sp} Patch ${patch})`);
installs.push({ installs.push({
installDir: path.join(base, entry), installDir: path.join(base, entry),
exePath, exePath,
@ -255,6 +256,7 @@ export function printConfig(installs: CodesysInstall[], opts: PrintConfigOptions
} else { } else {
name = install.serverName; name = install.serverName;
} }
console.log(name);
let unique = name; let unique = name;
let suffix = 2; let suffix = 2;
while (usedNames.has(unique)) { while (usedNames.has(unique)) {
@ -267,6 +269,7 @@ export function printConfig(installs: CodesysInstall[], opts: PrintConfigOptions
lines.push('{'); lines.push('{');
lines.push(' "mcpServers": {'); lines.push(' "mcpServers": {');
for (const { name, install, isLast } of namedEntries) { for (const { name, install, isLast } of namedEntries) {
console.log(name);
lines.push(` ${JSON.stringify(name)}: {`); lines.push(` ${JSON.stringify(name)}: {`);
lines.push(` "command": "codesys-mcp-sp21-plus",`); lines.push(` "command": "codesys-mcp-sp21-plus",`);
lines.push(` "args": [`); lines.push(` "args": [`);

View file

@ -12,8 +12,19 @@ const LOG_LEVELS: Record<LogLevel, number> = {
error: 3, error: 3,
}; };
const VALID_LOG_LEVELS = new Set<LogLevel>(['debug', 'info', 'warn', 'error']);
let currentLevel: LogLevel = 'info'; let currentLevel: LogLevel = 'info';
export function resolveLogLevel(input: string | undefined): LogLevel {
const normalized = (input ?? '').trim().toLowerCase();
if (VALID_LOG_LEVELS.has(normalized as LogLevel)) {
return normalized as LogLevel;
}
return process.env.CODESYS_MCP_LOG_LEVEL?.trim().toLowerCase() as LogLevel | undefined
?? 'info';
}
export function setLogLevel(level: LogLevel): void { export function setLogLevel(level: LogLevel): void {
currentLevel = level; currentLevel = level;
} }

View file

@ -15,8 +15,8 @@ UI thread. The single-thread design here works on SP19, SP21, and SP22+.
{IPC_BASE_DIR} is interpolated by Node.js before launch. {IPC_BASE_DIR} is interpolated by Node.js before launch.
""" """
import sys import sys
import os
import time import time
import os
import traceback import traceback
import json import json
@ -204,7 +204,8 @@ try:
os.remove(command_path) os.remove(command_path)
sp = os.path.join(COMMANDS_DIR, "%s.py" % request_id) sp = os.path.join(COMMANDS_DIR, "%s.py" % request_id)
if os.path.exists(sp): if os.path.exists(sp):
os.remove(sp) pass
#os.remove(sp)
except: except:
pass pass

View file

@ -15,7 +15,7 @@ import { ServerConfig, IpcResult, ScriptExecutor, ExecutionMode } from './types'
import { CodesysLauncher } from './launcher'; import { CodesysLauncher } from './launcher';
import { HeadlessExecutor } from './headless'; import { HeadlessExecutor } from './headless';
import { ScriptManager } from './script-manager'; import { ScriptManager } from './script-manager';
import { serverLog, setLogLevel } from './logger'; import { serverLog, setLogLevel, resolveLogLevel } from './logger';
import { readRunningVersionSsh, formatSshVersionResult } from './ssh-version'; import { readRunningVersionSsh, formatSshVersionResult } from './ssh-version';
import { import {
restartCodesysRuntime, restartCodesysRuntime,
@ -1240,8 +1240,8 @@ function defaultLiveValuesFilePath(): string {
export async function startMcpServer(config: ServerConfig): Promise<void> { export async function startMcpServer(config: ServerConfig): Promise<void> {
// Set log level // Set log level
if (config.debug) setLogLevel('debug'); const configuredLogLevel = resolveLogLevel(config.debug ? 'debug' : config.verbose ? 'info' : undefined);
else if (config.verbose) setLogLevel('info'); setLogLevel(configuredLogLevel);
serverLog.info(`Starting CODESYS Persistent MCP Server v0.1.0`); serverLog.info(`Starting CODESYS Persistent MCP Server v0.1.0`);
serverLog.info(`Mode: ${config.mode}`); serverLog.info(`Mode: ${config.mode}`);

30
tests/unit/logger.test.ts Normal file
View file

@ -0,0 +1,30 @@
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { getLogLevel, resolveLogLevel, setLogLevel } from '../../src/logger';
describe('logger level handling', () => {
beforeEach(() => {
delete process.env.CODESYS_MCP_LOG_LEVEL;
});
afterEach(() => {
delete process.env.CODESYS_MCP_LOG_LEVEL;
setLogLevel('info');
});
it('normalizes explicit log levels', () => {
expect(resolveLogLevel('DEBUG')).toBe('debug');
expect(resolveLogLevel('warn')).toBe('warn');
expect(resolveLogLevel('ERROR')).toBe('error');
});
it('uses the environment variable when present', () => {
process.env.CODESYS_MCP_LOG_LEVEL = 'debug';
expect(resolveLogLevel('info')).toBe('debug');
});
it('applies the resolved level through the public logger API', () => {
process.env.CODESYS_MCP_LOG_LEVEL = 'warn';
setLogLevel(resolveLogLevel('info'));
expect(getLogLevel()).toBe('warn');
});
});

View file

@ -0,0 +1,20 @@
import { printConfig } from '../../src/detect';
test('printConfig generates config for one install (exercises name generation)', () => {
const installs = [
{
installDir: 'C:\\Program Files\\CODESYS 3.5.21.10',
exePath: 'C:\\Program Files\\CODESYS 3.5.21.10\\CODESYS\\Common\\CODESYS.exe',
version: '3.5.21.10',
major: 3,
minor: 5,
sp: 21,
patch: 1,
profileName: 'CODESYS V3.5 SP21 Patch 1',
serverName: 'codesys-sp21-patch1',
},
];
const out = printConfig(installs);
expect(out).toContain('mcpServers');
});