Integrate Copilot With DocumentDB MCP In VS Code
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, anMCP 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:
- Create MCP Service: Create a new directory (e.g.,
mcp
) within the extension'ssrc
directory. Inside, create files likemcpService.ts
,mcpHandler.ts
, andmcpTypes.ts
to organize the MCP-related code. - 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.
- 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. - 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:
-
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" }
-
Load Configuration: In
mcpService.ts
, add logic to load this configuration file when the MCP server starts. Use Node.js'sfs
module or a library likedotenv
to read the file. -
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:
- Register Command: In the extension's
activate
function (usually inextension.ts
), register a new command usingvscode.commands.registerCommand
. The command ID could be something likedocumentdb.linkMcp
. - Implement Command Handler: Create a function to handle the command. This function should:
- Call the
startMcpServer
function inmcpService.ts
. - Display a message to the user indicating that the MCP server has started (e.g., using
vscode.window.showInformationMessage
).
- Call the
- Add to
package.json
: Add the command to thecommands
section of thepackage.json
file so that VS Code recognizes it. - Add to Menu: Add the command to the
menus
section of thepackage.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:
- Add to
menus
inpackage.json
: In themenus
section ofpackage.json
, add a new entry undereditor/context
that specifies the command ID (e.g.,documentdb.mcpChat
) and thewhen
clause to make it appear only for vCore cluster items. This typically involves checking the item's type or context value. - Register Command: In the extension's
activate
function, register a new command usingvscode.commands.registerCommand
. The command ID should match the one specified inpackage.json
. - 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!