Skip to main content
Glama
index.ts6.34 kB
/** * MCP Workspace Server * Main entry point for the Model Context Protocol server */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { loadConfig, ServerConfig } from './config.js'; import { Logger } from './utils/logging.js'; import { classifyError, logError, formatErrorForUser, ErrorCode as WorkspaceErrorCode } from './utils/errors.js'; // Import all tools import { listFilesTool, executeListFiles } from './tools/listFiles.js'; import { readFileTool, executeReadFile } from './tools/readFile.js'; import { writeFileTool, executeWriteFile } from './tools/writeFile.js'; import { deleteFileTool, executeDeleteFile } from './tools/deleteFile.js'; import { createFolderTool, executeCreateFolder } from './tools/createFolder.js'; import { applyPatchTool, executeApplyPatch } from './tools/applyPatch.js'; import { runCommandTool, executeRunCommand } from './tools/runCommand.js'; /** * Main MCP Workspace Server class */ class WorkspaceMCPServer { private server: Server; private config: ServerConfig; private logger: Logger; constructor() { // Load configuration this.config = loadConfig(); // Initialize logger this.logger = new Logger(this.config.logLevel); // Create MCP server instance this.server = new Server( { name: 'mcp-workspace-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.logger.info('MCP Workspace Server initialized', { workspaceRoot: this.config.workspaceRoot, readOnly: this.config.readOnly, allowedCommands: this.config.allowedCommands, }); // Register tools and handlers this.registerTools(); this.setupHandlers(); } /** * Registers all available tools with the MCP server */ private registerTools(): void { // Register list_tools handler this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ listFilesTool, readFileTool, writeFileTool, deleteFileTool, createFolderTool, applyPatchTool, runCommandTool, ], }; }); this.logger.debug('Registered 7 tools'); } /** * Sets up request handlers for tool execution */ private setupHandlers(): void { // Register call_tool handler this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; this.logger.debug('Tool invocation', { tool: name, args }); try { const result = await this.handleToolCall(name, args || {}); this.logger.debug('Tool execution successful', { tool: name }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { return this.handleError(error, name, args); } }); } /** * Routes tool calls to appropriate handlers * @param name - Tool name * @param args - Tool arguments * @returns Tool execution result */ private async handleToolCall(name: string, args: unknown): Promise<unknown> { switch (name) { case 'list_files': return await executeListFiles(args as any, this.config); case 'read_file': return await executeReadFile(args as any, this.config); case 'write_file': return await executeWriteFile(args as any, this.config); case 'delete_file': return await executeDeleteFile(args as any, this.config); case 'create_folder': return await executeCreateFolder(args as any, this.config); case 'apply_patch': return await executeApplyPatch(args as any, this.config); case 'run_command': return await executeRunCommand(args as any, this.config); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } /** * Handles errors and formats error responses * @param error - The error that occurred * @param toolName - Name of the tool that failed * @param input - The input that caused the error * @returns Formatted error response */ private handleError(error: unknown, toolName: string, input?: unknown): never { // Classify the error into a WorkspaceError const workspaceError = classifyError(error, toolName); // Log the error with full context logError(this.logger, workspaceError, toolName, input); // Map WorkspaceError codes to MCP error codes let mcpErrorCode = ErrorCode.InternalError; switch (workspaceError.code) { case WorkspaceErrorCode.SECURITY_VIOLATION: case WorkspaceErrorCode.NOT_FOUND: case WorkspaceErrorCode.READ_ONLY_MODE: case WorkspaceErrorCode.COMMAND_NOT_ALLOWED: case WorkspaceErrorCode.PATCH_FAILED: mcpErrorCode = ErrorCode.InvalidRequest; break; case WorkspaceErrorCode.INVALID_INPUT: mcpErrorCode = ErrorCode.InvalidParams; break; case WorkspaceErrorCode.COMMAND_TIMEOUT: case WorkspaceErrorCode.FILESYSTEM_ERROR: case WorkspaceErrorCode.UNEXPECTED_ERROR: mcpErrorCode = ErrorCode.InternalError; break; } // Format user-friendly message const userMessage = formatErrorForUser(workspaceError); throw new McpError(mcpErrorCode, userMessage); } /** * Starts the MCP server listening on stdio */ async start(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); this.logger.info('MCP Workspace Server started and listening on stdio'); } } /** * Main entry point */ async function main() { try { const server = new WorkspaceMCPServer(); await server.start(); } catch (error) { console.error('Failed to start MCP Workspace Server:', error); process.exit(1); } } // Start the server main();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ShayYeffet/mcp_server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server