/**
* Auto-update MCP client configurations when extension is installed/updated
* Supports: VS Code MCP config
*/
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
interface McpServerConfig {
command: string;
args: string[];
}
interface VsCodeMcpConfig {
servers?: {
[key: string]: McpServerConfig;
};
inputs?: any[];
}
const SERVER_NAME = 'web-viewer';
/**
* Get the path to the MCP server script in the installed extension
*/
function getMcpServerPath(context: vscode.ExtensionContext): string {
return path.join(context.extensionPath, 'out', 'mcp', 'mcp-server.js');
}
/**
* Get all known MCP config file paths
*/
function getConfigPaths(): { name: string; path: string; type: 'json' | 'vscode' }[] {
const configs: { name: string; path: string; type: 'json' | 'vscode' }[] = [];
// VS Code MCP config (Windows)
if (process.platform === 'win32') {
const vscodeConfig = path.join(process.env.APPDATA || '', 'Code', 'User', 'mcp.json');
configs.push({ name: 'VS Code MCP', path: vscodeConfig, type: 'json' });
}
// VS Code MCP config (macOS)
if (process.platform === 'darwin') {
const vscodeConfig = path.join(process.env.HOME || '', 'Library', 'Application Support', 'Code', 'User', 'mcp.json');
configs.push({ name: 'VS Code MCP', path: vscodeConfig, type: 'json' });
}
// VS Code MCP config (Linux)
if (process.platform === 'linux') {
const vscodeConfig = path.join(process.env.HOME || '', '.config', 'Code', 'User', 'mcp.json');
configs.push({ name: 'VS Code MCP', path: vscodeConfig, type: 'json' });
}
return configs;
}
/**
* Update a single config file with the new MCP server path
*/
function updateConfigFile(configPath: string, mcpServerPath: string): { success: boolean; created: boolean } {
try {
let config: VsCodeMcpConfig = { servers: {}, inputs: [] };
let created = false;
// Read existing config if it exists
if (fs.existsSync(configPath)) {
const content = fs.readFileSync(configPath, 'utf-8');
try {
config = JSON.parse(content);
} catch {
// Invalid JSON, start fresh
config = { servers: {}, inputs: [] };
}
} else {
// Create directory if it doesn't exist
const dir = path.dirname(configPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
created = true;
}
// Ensure servers object exists
if (!config.servers) {
config.servers = {};
}
// Update the web-viewer server config
config.servers[SERVER_NAME] = {
command: 'node',
args: [mcpServerPath],
};
// Write updated config
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
return { success: true, created };
} catch (error) {
return { success: false, created: false };
}
}
/**
* Check if this is a new install or update by comparing stored version and path
*/
function isNewOrUpdated(context: vscode.ExtensionContext): boolean {
const packageJson = require(path.join(context.extensionPath, 'package.json'));
const currentVersion = packageJson.version || '0.0.0';
const currentPath = context.extensionPath;
const storedVersion = context.globalState.get<string>('installedVersion');
const storedPath = context.globalState.get<string>('installedPath');
// Update if version changed OR path changed (reinstall)
if (storedVersion !== currentVersion || storedPath !== currentPath) {
context.globalState.update('installedVersion', currentVersion);
context.globalState.update('installedPath', currentPath);
return true;
}
return false;
}
/**
* Main function to update all MCP configs
*/
export async function updateMcpConfigs(context: vscode.ExtensionContext, force: boolean = false): Promise<void> {
// Only run on new install/update unless forced
if (!force && !isNewOrUpdated(context)) {
return;
}
const mcpServerPath = getMcpServerPath(context);
// Verify MCP server exists
if (!fs.existsSync(mcpServerPath)) {
console.error('MCP server not found at:', mcpServerPath);
return;
}
const configs = getConfigPaths();
const results: { name: string; status: string }[] = [];
for (const config of configs) {
if (fs.existsSync(config.path) || config.name === 'VS Code MCP') {
const result = updateConfigFile(config.path, mcpServerPath);
if (result.success) {
results.push({
name: config.name,
status: result.created ? 'created' : 'updated',
});
}
}
}
// Show notification if any configs were updated
if (results.length > 0) {
const message = results
.map(r => `${r.name}: ${r.status}`)
.join(', ');
vscode.window.showInformationMessage(
`Web Viewer MCP: Cấu hình đã được cập nhật (${message})`,
'Xem đường dẫn'
).then(selection => {
if (selection === 'Xem đường dẫn') {
vscode.window.showInformationMessage(`MCP Server: ${mcpServerPath}`);
}
});
}
}
/**
* Command to manually trigger config update
*/
export function registerUpdateConfigCommand(context: vscode.ExtensionContext): vscode.Disposable {
return vscode.commands.registerCommand('webViewer.updateMcpConfig', async () => {
await updateMcpConfigs(context, true);
vscode.window.showInformationMessage('Web Viewer: Đã cập nhật cấu hình MCP');
});
}