Integrate Copilot With DocumentDB MCP In VS Code

by Ahmed Latif 49 views

Hey everyone! We're diving into an exciting task: integrating Microsoft Copilot support with the existing DocumentDB MCP functionality directly into our VS Code extension. This is a Minimum Viable Product (MVP) implementation, focusing on a demo-ready state. So, let's keep it practical and efficient – think hardcoding if necessary!

Understanding the Goal

Our main goal is to seamlessly incorporate Copilot's capabilities within our DocumentDB extension, leveraging the existing DocumentDB Management Control Plane (MCP). For those unfamiliar, the DocumentDB MCP is implemented in Python and can be found here. We need to bridge this functionality into our VS Code environment.

Key Objectives

  • Integrate DocumentDB MCP: We need to bring the DocumentDB MCP into the vscode-documentdb extension. This involves either directly referencing the existing Python implementation or translating it into TypeScript.
  • Host MCP Server: The MCP server should run within the vscode-documentdb extension itself.
  • Configure Azure OpenAI Key: We need a mechanism to configure the Azure OpenAI key so the MCP can connect to Large Language Models (LLMs). A simple configuration file will do for this MVP.
  • Implement Link MCP Command: A new command, Link MCP, should start the MCP server and establish the connection with the LLM once clicked.
  • Add MCP Chat Command: When right-clicking on a vCore cluster, an MCP Chat command should appear. For this initial MVP, this command will simply list collections, but it sets the stage for more advanced functionality later.

Step-by-Step Implementation

Let's break down the implementation into manageable steps.

1. MCP Integration: Python or TypeScript?

The first big decision is whether to directly integrate the existing Python MCP or translate it into TypeScript. Each approach has its pros and cons.

  • Direct Python Integration: This approach minimizes code translation but introduces the complexity of managing Python dependencies and processes within a VS Code extension. We might need to use inter-process communication (IPC) to communicate between the extension's TypeScript code and the Python MCP server.
  • TypeScript Translation: Translating the MCP into TypeScript offers better integration with the existing extension codebase and eliminates the need for Python runtime management. However, it requires significant effort to rewrite the Python code in TypeScript.

For an MVP, a pragmatic approach is key. If the core MCP logic is relatively self-contained, translating it to TypeScript might be the cleaner long-term solution. However, if the Python code is extensive and complex, initially integrating it directly (even with the overhead of IPC) could be faster for this MVP.

Let's assume we opt for translating to TypeScript for better maintainability and integration.

2. Setting up the MCP Server in VS Code Extension

First, we need to create a dedicated module or service within the vscode-documentdb extension to house the MCP server logic. This service will be responsible for:

  • Initializing the MCP.
  • Handling requests from the VS Code extension.
  • Communicating with the configured LLM.

Detailed Steps:

  1. Create MCP Service: Create a new directory (e.g., mcp) within the extension's src directory. Inside, create files like mcpService.ts, mcpHandler.ts, and mcpTypes.ts to organize the MCP-related code.
  2. Translate Python MCP Logic: Start translating the core functionalities of the Python MCP into TypeScript classes and functions within these files. Focus on the essential parts first, like database connection, collection listing, and basic query execution.
  3. Implement Server Startup: Within mcpService.ts, create a function (e.g., startMcpServer) that initializes the MCP and starts listening for requests. This might involve setting up an Express server or using VS Code's built-in messaging APIs.
  4. Handle Requests: Create handler functions in mcpHandler.ts to process incoming requests from the extension. These handlers will interact with the translated MCP logic to perform actions like listing collections or executing queries.

3. Configuring the Azure OpenAI Key

To enable the MCP to interact with LLMs, we need a way to securely configure the Azure OpenAI key. For this MVP, a simple configuration file will suffice.

Steps:

  1. Create Configuration File: Create a configuration file (e.g., config.json) in the extension's root directory. This file should contain a field for the Azure OpenAI key:

    {
      "azureOpenAIKey": "YOUR_AZURE_OPENAI_KEY"
    }
    
  2. Load Configuration: In mcpService.ts, add logic to load this configuration file when the MCP server starts. Use Node.js's fs module or a library like dotenv to read the file.

  3. Access the Key: Store the loaded key in a variable within the mcpService so that it can be used when communicating with the LLM. Make sure you handle this key securely and don't commit it to your repository!

4. Implementing the Link MCP Command

We'll add a command to the VS Code command palette to start the MCP server. This command will be triggered by the user and will initiate the MCP and the connection to the LLM.

Steps:

  1. Register Command: In the extension's activate function (usually in extension.ts), register a new command using vscode.commands.registerCommand. The command ID could be something like documentdb.linkMcp.
  2. Implement Command Handler: Create a function to handle the command. This function should:
    • Call the startMcpServer function in mcpService.ts.
    • Display a message to the user indicating that the MCP server has started (e.g., using vscode.window.showInformationMessage).
  3. Add to package.json: Add the command to the commands section of the package.json file so that VS Code recognizes it.
  4. Add to Menu: Add the command to the menus section of the package.json file so it appears in the Command Palette.

5. Adding the MCP Chat Command to vCore Cluster Context Menu

Finally, we need to add the MCP Chat command to the context menu that appears when a user right-clicks on a vCore cluster in the VS Code Explorer.

Steps:

  1. Add to menus in package.json: In the menus section of package.json, add a new entry under editor/context that specifies the command ID (e.g., documentdb.mcpChat) and the when clause to make it appear only for vCore cluster items. This typically involves checking the item's type or context value.
  2. Register Command: In the extension's activate function, register a new command using vscode.commands.registerCommand. The command ID should match the one specified in package.json.
  3. Implement Command Handler: Create a function to handle the command. For this MVP, this function should:
    • Get the selected vCore cluster information from the context (VS Code usually provides this as an argument to the command handler).
    • Call a function in mcpHandler.ts to list the collections in the selected cluster.
    • Display the list of collections to the user (e.g., using vscode.window.showQuickPick or a similar UI element).

Example Code Snippets (Conceptual)

Here are some conceptual code snippets to illustrate the key steps. Note that these are simplified examples and may require adjustments based on your specific implementation.

mcpService.ts (Conceptual)

import * as vscode from 'vscode';
import * as fs from 'fs';
import { McpHandler } from './mcpHandler';

interface Config {
 azureOpenAIKey: string;
}

export class McpService {
 private config: Config;
 private mcpHandler: McpHandler;
 private isServerRunning: boolean = false;

 constructor() {
 this.mcpHandler = new McpHandler();
 }

 public async startMcpServer(): Promise<void> {
 if (this.isServerRunning) {
 vscode.window.showInformationMessage('MCP Server is already running.');
 return;
 }
 try {
 this.config = await this.loadConfig();
 // Initialize MCP logic here (translate from Python or use IPC)
 // Example: this.mcp = new TranslatedMcp(this.config.azureOpenAIKey);
 this.isServerRunning = true;
 vscode.window.showInformationMessage('MCP Server started.');
 } catch (error) {
 vscode.window.showErrorMessage(`Failed to start MCP Server: ${error}`);
 }
 }

 private async loadConfig(): Promise<Config> {
 const configPath = vscode.Uri.joinPath(extensionContext.extensionUri, 'config.json').fsPath;
 try {
 const data = fs.readFileSync(configPath, 'utf8');
 return JSON.parse(data);
 } catch (error) {
 throw new Error(`Failed to load config.json: ${error}`);
 }
 }

 public async listCollections(clusterInfo: any): Promise<string[]> {
 //For the MVP it will just return some default results
 return ['Collection1', 'Collection2', 'Collection3'];
 // Later: Use MCP logic to list collections for the cluster
 // return await this.mcpHandler.listCollections(clusterInfo, this.config.azureOpenAIKey);
 }
 }


export const mcpService = new McpService();

mcpHandler.ts (Conceptual)

import * as vscode from 'vscode';

export class McpHandler {
 public async listCollections(clusterInfo: any, azureOpenAIKey: string): Promise<string[]> {
 // Implement MCP logic here to list collections
 // This is where you would interact with the translated MCP code
 // or use IPC to communicate with the Python MCP server
 // For MVP, we will skip it, add it into mcpService.ts, and return default list of collections directly

 // Example:
 // const collections = await this.mcp.listCollections(clusterInfo);
 // return collections;

 throw new Error('Not implemented for MVP. See mcpService.ts');
 }
}

extension.ts (Conceptual)

import * as vscode from 'vscode';
import { mcpService } from './mcp/mcpService';

let extensionContext: vscode.ExtensionContext;

export function activate(context: vscode.ExtensionContext) {
 extensionContext = context;
 context.subscriptions.push(
 vscode.commands.registerCommand('documentdb.linkMcp', async () => {
 await mcpService.startMcpServer();
 })
 );

 context.subscriptions.push(
 vscode.commands.registerCommand('documentdb.mcpChat', async (clusterInfo) => {
 if (!clusterInfo) {
 vscode.window.showErrorMessage('Please select a vCore cluster.');
 return;
 }
 try {
 const collections = await mcpService.listCollections(clusterInfo);
 // Use VS Code's QuickPick to display the collections
 const selectedCollection = await vscode.window.showQuickPick(collections, {
 placeHolder: 'Select a collection',
 });
 if (selectedCollection) {
 vscode.window.showInformationMessage(`Selected collection: ${selectedCollection}`);
 }
 } catch (error) {
 vscode.window.showErrorMessage(`Failed to list collections: ${error}`);
 }
 })
 );
}

export function deactivate() {}

package.json (Conceptual Snippets)

{
 "contributes": {
 "commands": [
 {
 "command": "documentdb.linkMcp",
 "title": "Link MCP"
 },
 {
 "command": "documentdb.mcpChat",
 "title": "MCP Chat"
 }
 ],
 "menus": {
 "commandPalette": [
 {
 "command": "documentdb.linkMcp",
 "when": "true" // Always show in command palette
 }
 ],
 "editor/context": [
 {
 "command": "documentdb.mcpChat",
 "when": "resourceScheme == documentdb && resourceItemType == vcoreCluster" // Example condition
 }
 ]
 }
 }
}

Key Considerations for MVP

  • Hardcoding: Don't be afraid to hardcode values for this MVP to save time. For example, you can hardcode a list of collections for the MCP Chat command initially.
  • Error Handling: Implement basic error handling, but don't overcomplicate it. Focus on catching exceptions and displaying informative messages to the user.
  • Testing: Focus on manual testing to ensure the core functionality works. Automated testing can be added later.
  • Security: Be mindful of security, especially when handling the Azure OpenAI key. Use environment variables or a secure configuration mechanism in a production environment.

Next Steps and Beyond MVP

Once the MVP is complete, we can iterate and add more features, such as:

  • Implementing more advanced query execution and data manipulation through the MCP.
  • Integrating the LLM for natural language query generation and code completion.
  • Adding more robust error handling and logging.
  • Implementing automated tests.
  • Improving the UI and user experience.

This MVP is a crucial first step in bringing the power of Copilot and the DocumentDB MCP to our VS Code extension. Let's get started, guys!