UNPKG

@etm-professional-control/winccoa-mcp-server

Version:

MCP Server for WinCC OA with field-specific configurations

211 lines 8.21 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { WinccoaManager } from 'winccoa-manager'; import { loadAllTools } from './tool_loader.js'; import { readFileSync } from 'fs'; import fs from 'fs/promises'; import path from 'path'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; let winccoa = null; let fieldContent = null; let activeFieldName = 'default'; let projectContent = null; let systemPrompt = null; /** * Load system prompt from systemprompt.md file * @returns {string|null} System prompt content or null if file doesn't exist */ function loadSystemPrompt() { try { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const systemPromptPath = join(__dirname, 'systemprompt.md'); console.log('🔄 Loading system prompt from:', systemPromptPath); const content = readFileSync(systemPromptPath, 'utf8'); console.log('✅ System prompt loaded successfully'); return content; } catch (error) { console.warn('⚠️ Could not load system prompt:', error.message); return null; } } /** * Load field content for the active field * @param {string} fieldName - Name of the field to load * @returns {Promise<string>} Field content */ async function loadFieldContent(fieldName) { const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const fieldPath = path.join(__dirname, 'fields', `${fieldName}.md`); try { // Read the field file const content = await fs.readFile(fieldPath, 'utf8'); console.log(`Loaded field content: ${fieldName}`); return content; } catch (error) { console.error(`Error loading field ${fieldName}:`, error.message); // Return default content if field not found return '# Default Configuration\n\nNo specific field instructions available.'; } } /** * Load project-specific content if available * @returns {Promise<string|null>} Project content or null */ async function loadProjectContent() { const projectPath = process.env.WINCCOA_PROJECT_INSTRUCTIONS; if (!projectPath) { console.log('No project configuration specified (WINCCOA_PROJECT_INSTRUCTIONS not set)'); return null; } try { // Resolve the path (could be absolute or relative) const resolvedPath = path.resolve(projectPath); // Read the project file const content = await fs.readFile(resolvedPath, 'utf8'); console.log(`Loaded project content from: ${resolvedPath}`); return content; } catch (error) { console.error(`Error loading project configuration from ${projectPath}:`, error.message); return null; } } /** * Initialize the MCP server with all tools and resources * @returns {Promise<McpServer>} Configured MCP server */ export async function initializeServer() { console.log('🔄 Starting MCP server initialization...'); try { // Initialize WinCC OA manager console.log('🔄 Initializing WinCC OA manager...'); winccoa = new WinccoaManager(); console.log('✅ WinCC OA manager initialized'); // Get active field name console.log('🔄 Getting active field...'); activeFieldName = process.env.WINCCOA_FIELD || 'default'; console.log('✅ Active field:', activeFieldName); // Load field and project content console.log('🔄 Loading field content...'); fieldContent = await loadFieldContent(activeFieldName); console.log('✅ Field content loaded'); console.log('🔄 Loading project content...'); projectContent = await loadProjectContent(); console.log('✅ Project content loaded:', projectContent ? 'YES' : 'NO'); console.log('🔄 Loading system prompt...'); systemPrompt = loadSystemPrompt(); console.log('✅ System prompt loaded:', systemPrompt ? 'SUCCESS' : 'NONE'); // Create server instance console.log('🔄 Creating MCP server instance...'); const server = new McpServer({ name: "WinCC OA Extended with CNS/UNS", version: "3.0.0", capabilities: { resources: { list: true, read: true }, tools: {}, }, }); console.log('✅ MCP server instance created'); // Create context object for sharing state console.log('🔄 Creating context object...'); const context = { winccoa, fieldContent, activeFieldName, projectContent, systemPrompt }; console.log('✅ Context object created'); // Register resources for the 3 instruction levels console.log('🔄 Registering resources...'); // Resource: System prompt if (systemPrompt) { server.resource("instructions://system", "System-level prompt and instructions", async () => { return { contents: [{ uri: "instructions://system", mimeType: "text/markdown", text: systemPrompt }] }; }); } // Resource: Field instructions server.resource("instructions://field", "Field-specific instructions", async () => { return { contents: [{ uri: "instructions://field", mimeType: "text/markdown", text: fieldContent }] }; }); // Resource: Project instructions if (projectContent) { server.resource("instructions://project", "Project-specific instructions", async () => { return { contents: [{ uri: "instructions://project", mimeType: "text/markdown", text: projectContent }] }; }); } // Resource: Combined instructions (all 3 levels merged) server.resource("instructions://combined", "All instructions combined (system + field + project)", async () => { let combined = ""; if (systemPrompt) { combined += "# System Instructions\n\n" + systemPrompt + "\n\n---\n\n"; } combined += "# Field Instructions (" + activeFieldName + ")\n\n" + fieldContent; if (projectContent) { combined += "\n\n---\n\n# Project Instructions\n\n" + projectContent; combined += "\n\n---\n\n## Note\nProject instructions take precedence over field instructions."; } return { contents: [{ uri: "instructions://combined", mimeType: "text/markdown", text: combined }] }; }); console.log('✅ Resources registered'); // Load and register all tools console.log('🔄 Loading and registering tools...'); await loadAllTools(server, context); console.log('✅ Tools loaded and registered'); console.log(`✅ MCP Server initialized successfully. Active field: ${activeFieldName}`); if (projectContent) { console.log(`✅ Project configuration loaded from: ${process.env.WINCCOA_PROJECT_INSTRUCTIONS}`); } return server; } catch (error) { console.error('❌ Error during MCP server initialization:', error); console.error('❌ Initialization error stack:', error.stack); throw error; } } /** * Get the current context (for testing or debugging) * @returns {Object} Current server context */ export function getContext() { return { winccoa, fieldContent, activeFieldName, projectContent, systemPrompt }; } //# sourceMappingURL=server.js.map