UNPKG

copilot-mcp-server

Version:

MCP server that integrates with GitHub Copilot to provide code assistance

301 lines 13.7 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { CopilotClient } from './copilot-client.js'; import { CopilotChatRequestSchema, CopilotExplainRequestSchema, CopilotSuggestRequestSchema, CopilotReviewRequestSchema } from './types.js'; export class CopilotMCPServer { server; copilotClient; constructor(config) { this.server = new Server({ name: 'copilot-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, resources: {}, }, }); this.copilotClient = new CopilotClient(config.github); this.setupHandlers(); } setupHandlers() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'copilot_chat', description: 'Chat with GitHub Copilot AI models for general programming assistance', inputSchema: { type: 'object', properties: { message: { type: 'string', description: 'Your message or question for Copilot', minLength: 1, maxLength: 10000 }, context: { type: 'string', description: 'Optional context to provide (e.g., current code, file contents)', optional: true }, model: { type: 'string', enum: ['gpt-4o', 'claude-3-5-sonnet', 'gemini-2.0-flash'], description: 'AI model to use (optional)', optional: true }, temperature: { type: 'number', minimum: 0, maximum: 2, description: 'Response creativity (0=focused, 2=creative)', optional: true } }, required: ['message'] } }, { name: 'copilot_explain', description: 'Get detailed explanations of code from GitHub Copilot', inputSchema: { type: 'object', properties: { code: { type: 'string', description: 'Code to explain', minLength: 1 }, language: { type: 'string', description: 'Programming language (auto-detected if not provided)', optional: true }, context: { type: 'string', description: 'Additional context about the code', optional: true } }, required: ['code'] } }, { name: 'copilot_suggest', description: 'Get code suggestions and completions from GitHub Copilot', inputSchema: { type: 'object', properties: { prompt: { type: 'string', description: 'Description of what code you want to generate', minLength: 1 }, language: { type: 'string', description: 'Target programming language', optional: true }, context: { type: 'string', description: 'Existing code context or constraints', optional: true }, maxSuggestions: { type: 'number', minimum: 1, maximum: 10, description: 'Maximum number of suggestions to return', optional: true } }, required: ['prompt'] } }, { name: 'copilot_review', description: 'Get code review and improvement suggestions from GitHub Copilot', inputSchema: { type: 'object', properties: { code: { type: 'string', description: 'Code to review', minLength: 1 }, language: { type: 'string', description: 'Programming language', optional: true }, reviewType: { type: 'string', enum: ['security', 'performance', 'style', 'general'], description: 'Type of review to perform', optional: true } }, required: ['code'] } } ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'copilot_chat': { const parsed = CopilotChatRequestSchema.parse(args); const response = await this.copilotClient.chat(parsed); return { content: [ { type: 'text', text: response.content, }, ], }; } case 'copilot_explain': { const parsed = CopilotExplainRequestSchema.parse(args); const response = await this.copilotClient.explain(parsed); return { content: [ { type: 'text', text: response.content, }, ], }; } case 'copilot_suggest': { const parsed = CopilotSuggestRequestSchema.parse(args); const response = await this.copilotClient.suggest(parsed); return { content: [ { type: 'text', text: response.content, }, ], }; } case 'copilot_review': { const parsed = CopilotReviewRequestSchema.parse(args); const response = await this.copilotClient.review(parsed); return { content: [ { type: 'text', text: response.content, }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: 'text', text: `Error: ${errorMessage}`, }, ], isError: true, }; } }); this.server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'copilot://models', name: 'Available Copilot Models', description: 'List of available GitHub Copilot AI models', mimeType: 'application/json', }, { uri: 'copilot://usage', name: 'Usage Statistics', description: 'Current Copilot usage metrics and limits', mimeType: 'application/json', }, ], }; }); this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; switch (uri) { case 'copilot://models': return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify({ models: [ { name: 'gpt-4o', description: 'OpenAI GPT-4 Omni - Advanced multimodal model', provider: 'OpenAI' }, { name: 'claude-3-5-sonnet', description: 'Anthropic Claude 3.5 Sonnet - Fast and capable', provider: 'Anthropic' }, { name: 'gemini-2.0-flash', description: 'Google Gemini 2.0 Flash - Fast and efficient', provider: 'Google' } ] }, null, 2), }, ], }; case 'copilot://usage': try { const usage = await this.copilotClient.getUsage(); return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify(usage, null, 2), }, ], }; } catch (error) { return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify({ error: 'Unable to fetch usage data' }, null, 2), }, ], }; } default: throw new Error(`Unknown resource: ${uri}`); } }); } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error('Copilot MCP Server started on stdio'); // Keep the process alive process.stdin.resume(); } } //# sourceMappingURL=server.js.map