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
This commit is contained in:
Joe Danziger
2025-07-18 13:06:56 -04:00
committed by GitHub
parent f98df5c0fd
commit 55442226d0
2 changed files with 169 additions and 1 deletions

View File

@@ -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 };