From 55442226d0aa4870470d2a9897f5538d6a0e329e Mon Sep 17 00:00:00 2001 From: Joe Danziger Date: Fri, 18 Jul 2025 13:06:56 -0400 Subject: [PATCH] fix: Update VS Code profile with MCP config transformation (#971) * remove dash in server name * add OLLAMA_API_KEY to VS Code MCP instructions * transform vscode mcp to correct format * add changeset * switch back to task-master-ai * use task-master-ai --- .changeset/gentle-beds-beam.md | 5 + src/profiles/vscode.js | 165 ++++++++++++++++++++++++++++++++- 2 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 .changeset/gentle-beds-beam.md diff --git a/.changeset/gentle-beds-beam.md b/.changeset/gentle-beds-beam.md new file mode 100644 index 00000000..fa3fe2f3 --- /dev/null +++ b/.changeset/gentle-beds-beam.md @@ -0,0 +1,5 @@ +--- +"task-master-ai": patch +--- + +Update VS Code profile with MCP config transformation diff --git a/src/profiles/vscode.js b/src/profiles/vscode.js index a5c0757c..06e28797 100644 --- a/src/profiles/vscode.js +++ b/src/profiles/vscode.js @@ -1,6 +1,162 @@ // VS Code conversion profile for rule-transformer +import path from 'path'; +import fs from 'fs'; +import { log } from '../../scripts/modules/utils.js'; import { createProfile, COMMON_TOOL_MAPPINGS } from './base-profile.js'; +/** + * Transform standard MCP config format to VS Code format + * @param {Object} mcpConfig - Standard MCP configuration object + * @returns {Object} - Transformed VS Code configuration object + */ +function transformToVSCodeFormat(mcpConfig) { + const vscodeConfig = {}; + + // Transform mcpServers to servers + if (mcpConfig.mcpServers) { + vscodeConfig.servers = {}; + + for (const [serverName, serverConfig] of Object.entries( + mcpConfig.mcpServers + )) { + // Transform server configuration + const transformedServer = { + ...serverConfig + }; + + // Add type: "stdio" after the env block + if (transformedServer.env) { + // Reorder properties: keep command, args, env, then add type + const reorderedServer = {}; + if (transformedServer.command) + reorderedServer.command = transformedServer.command; + if (transformedServer.args) + reorderedServer.args = transformedServer.args; + if (transformedServer.env) reorderedServer.env = transformedServer.env; + reorderedServer.type = 'stdio'; + + // Add any other properties that might exist + Object.keys(transformedServer).forEach((key) => { + if (!['command', 'args', 'env', 'type'].includes(key)) { + reorderedServer[key] = transformedServer[key]; + } + }); + + vscodeConfig.servers[serverName] = reorderedServer; + } else { + // If no env block, just add type at the end + transformedServer.type = 'stdio'; + vscodeConfig.servers[serverName] = transformedServer; + } + } + } + + return vscodeConfig; +} + +/** + * Lifecycle function called after MCP config generation to transform to VS Code format + * @param {string} targetDir - Target project directory + * @param {string} assetsDir - Assets directory (unused for VS Code) + */ +function onPostConvertRulesProfile(targetDir, assetsDir) { + const vscodeConfigPath = path.join(targetDir, '.vscode', 'mcp.json'); + + if (!fs.existsSync(vscodeConfigPath)) { + log('debug', '[VS Code] No .vscode/mcp.json found to transform'); + return; + } + + try { + // Read the generated standard MCP config + const mcpConfigContent = fs.readFileSync(vscodeConfigPath, 'utf8'); + const mcpConfig = JSON.parse(mcpConfigContent); + + // Check if it's already in VS Code format (has servers instead of mcpServers) + if (mcpConfig.servers) { + log( + 'info', + '[VS Code] mcp.json already in VS Code format, skipping transformation' + ); + return; + } + + // Transform to VS Code format + const vscodeConfig = transformToVSCodeFormat(mcpConfig); + + // Write back the transformed config with proper formatting + fs.writeFileSync( + vscodeConfigPath, + JSON.stringify(vscodeConfig, null, 2) + '\n' + ); + + log('info', '[VS Code] Transformed mcp.json to VS Code format'); + log('debug', `[VS Code] Renamed mcpServers->servers, added type: "stdio"`); + } catch (error) { + log('error', `[VS Code] Failed to transform mcp.json: ${error.message}`); + } +} + +/** + * Lifecycle function called when removing VS Code profile + * @param {string} targetDir - Target project directory + */ +function onRemoveRulesProfile(targetDir) { + const vscodeConfigPath = path.join(targetDir, '.vscode', 'mcp.json'); + + if (!fs.existsSync(vscodeConfigPath)) { + log('debug', '[VS Code] No .vscode/mcp.json found to clean up'); + return; + } + + try { + // Read the current config + const configContent = fs.readFileSync(vscodeConfigPath, 'utf8'); + const config = JSON.parse(configContent); + + // Check if it has the servers section and task-master-ai server + if (config.servers && config.servers['task-master-ai']) { + // Remove task-master-ai server + delete config.servers['task-master-ai']; + + // Check if there are other MCP servers + const remainingServers = Object.keys(config.servers); + + if (remainingServers.length === 0) { + // No other servers, remove entire file + fs.rmSync(vscodeConfigPath, { force: true }); + log('info', '[VS Code] Removed empty mcp.json file'); + + // Also remove .vscode directory if it's empty + const vscodeDir = path.dirname(vscodeConfigPath); + try { + const dirContents = fs.readdirSync(vscodeDir); + if (dirContents.length === 0) { + fs.rmSync(vscodeDir, { recursive: true, force: true }); + log('debug', '[VS Code] Removed empty .vscode directory'); + } + } catch (err) { + // Directory might not be empty or might not exist, that's fine + } + } else { + // Write back the modified config + fs.writeFileSync( + vscodeConfigPath, + JSON.stringify(config, null, 2) + '\n' + ); + log( + 'info', + '[VS Code] Removed TaskMaster from mcp.json, preserved other configurations' + ); + } + } else { + log('debug', '[VS Code] TaskMaster not found in mcp.json'); + } + } catch (error) { + log('error', `[VS Code] Failed to clean up mcp.json: ${error.message}`); + } +} + // Create and export vscode profile using the base factory export const vscodeProfile = createProfile({ name: 'vscode', @@ -8,6 +164,8 @@ export const vscodeProfile = createProfile({ url: 'code.visualstudio.com', docsUrl: 'code.visualstudio.com/docs', rulesDir: '.github/instructions', // VS Code instructions location + profileDir: '.vscode', // VS Code configuration directory + mcpConfigName: 'mcp.json', // VS Code uses mcp.json in .vscode directory customReplacements: [ // Core VS Code directory structure changes { from: /\.cursor\/rules/g, to: '.github/instructions' }, @@ -28,5 +186,10 @@ export const vscodeProfile = createProfile({ // VS Code specific terminology { from: /rules directory/g, to: 'instructions directory' }, { from: /cursor rules/gi, to: 'VS Code instructions' } - ] + ], + onPostConvert: onPostConvertRulesProfile, + onRemove: onRemoveRulesProfile }); + +// Export lifecycle functions separately to avoid naming conflicts +export { onPostConvertRulesProfile, onRemoveRulesProfile };