Skip to main content
Glama
index.ts13.6 kB
#!/usr/bin/env node /** * DevContainer MCP Server * A comprehensive Model Context Protocol server for AI-powered DevContainer management */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { ConfigGenerator } from './config-generator.js'; import { DevContainerManager } from './devcontainer-manager.js'; import { TemplateManager } from './template-manager.js'; class DevContainerMCPServer { private server: Server; private configGenerator: ConfigGenerator; private containerManager: DevContainerManager; private templateManager: TemplateManager; constructor() { this.server = new Server( { name: 'devcontainer-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); this.configGenerator = new ConfigGenerator(); this.containerManager = new DevContainerManager(); this.templateManager = new TemplateManager(); this.setupToolHandlers(); this.setupErrorHandling(); } private setupToolHandlers() { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'generate_devcontainer', description: 'Generate devcontainer.json from natural language description', inputSchema: { type: 'object', properties: { prompt: { type: 'string', description: 'Natural language description of the development environment' }, workspaceRoot: { type: 'string', description: 'Workspace root path', default: '.' }, baseTemplate: { type: 'string', description: 'Optional template to start from' } }, required: ['prompt'] } }, { name: 'build_devcontainer', description: 'Build DevContainer from configuration', inputSchema: { type: 'object', properties: { workspaceRoot: { type: 'string', description: 'Workspace root path', default: '.' }, configPath: { type: 'string', description: 'Custom configuration file path' }, rebuild: { type: 'boolean', description: 'Force rebuild without cache', default: false } } } }, { name: 'test_devcontainer', description: 'Test DevContainer functionality', inputSchema: { type: 'object', properties: { workspaceRoot: { type: 'string', description: 'Workspace root path', default: '.' }, testCommands: { type: 'array', items: { type: 'string' }, description: 'Custom test commands to run' } } } }, { name: 'list_templates', description: 'List available DevContainer templates', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Filter templates by category' } } } }, { name: 'modify_devcontainer', description: 'Modify existing DevContainer configuration', inputSchema: { type: 'object', properties: { workspaceRoot: { type: 'string', description: 'Workspace root path', default: '.' }, modifications: { type: 'string', description: 'Natural language description of desired changes' } }, required: ['modifications'] } }, { name: 'get_devcontainer_status', description: 'Get DevContainer status and information', inputSchema: { type: 'object', properties: { workspaceRoot: { type: 'string', description: 'Workspace root path', default: '.' } } } } ] }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'generate_devcontainer': return await this.handleGenerateDevContainer(args || {}); case 'build_devcontainer': return await this.handleBuildDevContainer(args || {}); case 'test_devcontainer': return await this.handleTestDevContainer(args || {}); case 'list_templates': return await this.handleListTemplates(args || {}); case 'modify_devcontainer': return await this.handleModifyDevContainer(args || {}); case 'get_devcontainer_status': return await this.handleGetDevContainerStatus(args || {}); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: 'text', text: `Error executing ${name}: ${errorMessage}` } ] }; } }); } private async handleGenerateDevContainer(args: Record<string, unknown>) { const prompt = args.prompt as string; const workspaceRoot = (args.workspaceRoot as string) || '.'; const baseTemplate = args.baseTemplate as string | undefined; if (!prompt) { throw new Error('Prompt is required'); } const result = await this.configGenerator.generateFromPrompt(prompt, workspaceRoot, baseTemplate); return { content: [ { type: 'text', text: `✅ DevContainer configuration generated successfully! **Template Used:** ${result.template} **Configuration Path:** ${result.path} **Analysis:** - Languages: ${result.analysis.languages.join(', ') || 'None detected'} - Frameworks: ${result.analysis.frameworks.join(', ') || 'None detected'} - Databases: ${result.analysis.databases.join(', ') || 'None detected'} - Ports: ${result.analysis.ports.join(', ') || 'None detected'} **Reasoning:** ${result.reasoning} **Generated Configuration:** \`\`\`json ${JSON.stringify(result.content, null, 2)} \`\`\` You can now build the container using the \`build_devcontainer\` tool.` } ] }; } private async handleBuildDevContainer(args: Record<string, unknown>) { const workspaceRoot = (args.workspaceRoot as string) || '.'; const configPath = args.configPath as string | undefined; const rebuild = (args.rebuild as boolean) || false; const result = await this.containerManager.buildContainer(workspaceRoot, configPath, rebuild); if (result.success) { return { content: [ { type: 'text', text: `✅ DevContainer built successfully in ${Math.round(result.duration / 1000)}s! **Build Output:** \`\`\` ${result.output} \`\`\` The container is ready for development. You can test it using the \`test_devcontainer\` tool.` } ] }; } else { return { content: [ { type: 'text', text: `❌ DevContainer build failed after ${Math.round(result.duration / 1000)}s **Error:** \`\`\` ${result.error} \`\`\` **Build Output:** \`\`\` ${result.output} \`\`\` Please check the configuration and try again.` } ] }; } } private async handleTestDevContainer(args: Record<string, unknown>) { const workspaceRoot = (args.workspaceRoot as string) || '.'; const testCommands = args.testCommands as string[] | undefined; const result = await this.containerManager.testContainer(workspaceRoot, testCommands); const testDetails = result.tests.map(test => { const status = test.success ? '✅' : '❌'; const output = test.output ? `\n Output: ${test.output}` : ''; const error = test.error ? `\n Error: ${test.error}` : ''; return `${status} ${test.name}${output}${error}`; }).join('\n'); return { content: [ { type: 'text', text: `${result.success ? '✅' : '❌'} DevContainer Tests Complete **Summary:** ${result.summary} **Test Results:** ${testDetails} ${result.success ? 'All tests passed! Your DevContainer is working correctly.' : 'Some tests failed. Please review the errors and fix any issues.'}` } ] }; } private async handleListTemplates(args: Record<string, unknown>) { const category = args.category as string | undefined; const templates = this.templateManager.getTemplates(category ? { category } : undefined); const templateList = templates.map(template => { const summary = this.templateManager.getTemplateSummary(template); return `**${summary.name}** (${summary.category}) ${summary.description} Languages: ${(summary.languages as string[]).join(', ')} Frameworks: ${(summary.frameworks as string[]).join(', ')} Features: ${(summary.features as string[]).join(', ')}`; }).join('\n\n'); const categories = this.templateManager.getCategories(); return { content: [ { type: 'text', text: `📋 Available DevContainer Templates ${category ? `(${category})` : ''} **Available Categories:** ${categories.join(', ')} ${templateList} Use any template name with the \`generate_devcontainer\` tool by setting the \`baseTemplate\` parameter.` } ] }; } private async handleModifyDevContainer(args: Record<string, unknown>) { const workspaceRoot = (args.workspaceRoot as string) || '.'; const modifications = args.modifications as string; if (!modifications) { throw new Error('Modifications description is required'); } const result = await this.configGenerator.modifyConfiguration(workspaceRoot, modifications); return { content: [ { type: 'text', text: `✅ DevContainer configuration modified successfully! **Configuration Path:** ${result.path} **Changes Made:** - Languages: ${result.analysis.languages.join(', ') || 'None detected'} - Frameworks: ${result.analysis.frameworks.join(', ') || 'None detected'} - Databases: ${result.analysis.databases.join(', ') || 'None detected'} - Ports: ${result.analysis.ports.join(', ') || 'None detected'} **Reasoning:** ${result.reasoning} **Updated Configuration:** \`\`\`json ${JSON.stringify(result.content, null, 2)} \`\`\` You may need to rebuild the container for changes to take effect.` } ] }; } private async handleGetDevContainerStatus(args: Record<string, unknown>) { const workspaceRoot = (args.workspaceRoot as string) || '.'; const status = await this.containerManager.getContainerStatus(workspaceRoot); const statusIcon = status.configExists ? (status.isRunning ? '🟢' : '🟡') : '🔴'; const statusText = status.configExists ? (status.isRunning ? 'Running' : 'Stopped') : 'Not configured'; return { content: [ { type: 'text', text: `${statusIcon} DevContainer Status: ${statusText} **Configuration:** - Exists: ${status.configExists ? '✅' : '❌'} - Path: ${status.configPath || 'Not found'} **Container:** - Running: ${status.isRunning ? '✅' : '❌'} - Name: ${status.containerName || 'Unknown'} - Image: ${status.image || 'Unknown'} - Last Build: ${status.lastBuildTime ? status.lastBuildTime.toLocaleString() : 'Unknown'} ${!status.configExists ? 'Use the `generate_devcontainer` tool to create a configuration.' : !status.isRunning ? 'Use the `build_devcontainer` tool to build and start the container.' : 'Container is ready for development!'}` } ] }; } private setupErrorHandling() { this.server.onerror = (error) => { console.error('[MCP Error]', error); }; process.on('SIGINT', async () => { await this.server.close(); process.exit(0); }); } async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('DevContainer MCP Server running on stdio'); } } // Start the server if (require.main === module) { const server = new DevContainerMCPServer(); server.run().catch((error) => { console.error('Failed to start server:', error); process.exit(1); }); } export { DevContainerMCPServer };

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/Siddhant-K-code/mcp-devcontainer'

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