UNPKG

@steipete/terminator-mcp

Version:

MCP plugin to manage macOS terminal sessions.

114 lines 5.94 kB
// Placeholder for the main entry point of the @steipete/terminator-mcp package. // This file will implement the MCP server logic and interface with the Swift CLI. import { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, } from '@modelcontextprotocol/sdk/types.js'; import * as fs from 'node:fs'; import { debugLog, CURRENT_TERMINAL_APP } from './config.js'; import { SWIFT_CLI_PATH } from './swift-cli.js'; import { terminatorTool } from './tool.js'; // Assuming terminatorTool.handler is adaptable // Read package version (optional, but good practice) // import { createRequire } from 'node:module'; // const require = createRequire(import.meta.url); // const packageJson = require('../package.json'); // const SERVER_VERSION = packageJson.version || '0.1.0'; const SERVER_VERSION = '0.1.0'; // Hardcode for now to avoid json import complexities async function main() { // Startup checks for Swift CLI binary if (!fs.existsSync(SWIFT_CLI_PATH)) { console.error(`FATAL: Swift CLI binary not found at expected path: ${SWIFT_CLI_PATH}`); process.exit(1); } try { fs.accessSync(SWIFT_CLI_PATH, fs.constants.X_OK); } catch (err) { console.error(`FATAL: Swift CLI binary at ${SWIFT_CLI_PATH} is not executable. Please run 'chmod +x ${SWIFT_CLI_PATH}'.`); process.exit(1); } if (!process.env.MCP_PORT && !process.env.MCP_SERVER_VIA_STDIO) { // StdioServerTransport doesn't use MCP_PORT console.warn('MCP_PORT or MCP_SERVER_VIA_STDIO environment variable is not set. This plugin is meant to be run by an MCP host over stdio.'); // return; // Allow to run for local testing if needed } const server = new McpServer({ name: 'terminator-mcp', // Toolset name version: SERVER_VERSION, }, { capabilities: { tools: {}, // Corrected: an empty object to indicate tool support }, }); // ListTools handler server.setRequestHandler(ListToolsRequestSchema, async () => { // Adapt our terminatorTool to the Tool type expected by the SDK const dynamicDescription = `${terminatorTool.description} \nTerminator MCP ${SERVER_VERSION} using ${CURRENT_TERMINAL_APP}`; const toolDefinition = { name: terminatorTool.name, description: dynamicDescription, // Use the dynamically suffixed description inputSchema: terminatorTool.inputSchema, }; return { tools: [toolDefinition], }; }); // CallTool handler server.setRequestHandler(CallToolRequestSchema, async (request, call) => { debugLog('[MainServer] Handling CallToolRequest:', request); const toolName = request.params.name; if (toolName !== terminatorTool.name) { throw new McpError(ErrorCode.MethodNotFound, `Tool ${toolName} not found. Available: ${terminatorTool.name}`); } const receivedArgs = request.params.arguments; debugLog(`[MainServer] CallTool '${toolName}' receivedArgs (flattened):`, receivedArgs); if (!receivedArgs) { // Basic check for arguments throw new McpError(ErrorCode.InvalidParams, `Missing arguments for tool ${toolName}`); } // Validate that receivedArgs contains an 'action' (still required) if (typeof receivedArgs.action !== 'string') { debugLog(`[MainServer] CallTool '${toolName}' failed validation: typeof action = ${typeof receivedArgs?.action}`); throw new McpError(ErrorCode.InvalidParams, `Missing or invalid 'action' (string expected) in arguments for tool ${toolName}`); } // Validate that receivedArgs contains a 'project_path' (now mandatory) if (typeof receivedArgs.project_path !== 'string' || !receivedArgs.project_path.trim()) { debugLog(`[MainServer] CallTool '${toolName}' failed validation: typeof project_path = ${typeof receivedArgs?.project_path}, value = ${receivedArgs?.project_path}`); throw new McpError(ErrorCode.InvalidParams, `Missing or invalid 'project_path' (non-empty string expected) in arguments for tool ${toolName}`); } // Cast receivedArgs directly to TerminatorExecuteParams as it's now flat const toolParams = receivedArgs; try { const result = await terminatorTool.handler(toolParams, call); return { content: [{ type: 'text', text: result.message }] }; // Adapt result to ServerResult } catch (error) { debugLog('[MainServer] Error executing tool:', error); if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Terminator tool execution failed: ${error.message}`); } }); server.onerror = (error) => { console.error('[TerminatorMCP Server Error]', error); // Optionally, send a diagnostic error to the client if possible/appropriate }; process.on('SIGINT', async () => { await server.close(); process.exit(0); }); try { const transport = new StdioServerTransport(); await server.connect(transport); console.error(`Terminator MCP server v${SERVER_VERSION} running via stdio, connected to host.`); } catch (error) { console.error('Failed to start or connect Terminator MCP server:', error); process.exit(1); } } main().catch(err => { console.error("Terminator MCP plugin failed to run (uncaught error in main):", err); process.exit(1); }); debugLog('Terminator MCP Plugin (Node.js Wrapper) Initializing for ESM...'); // Initial debug logs from config.ts will cover specific config values. //# sourceMappingURL=index.js.map