UNPKG

@grebyn/toolflow-mcp-server

Version:

MCP server for managing other MCP servers - discover, install, organize into bundles, and automate with workflows. Uses StreamableHTTP transport with dual OAuth/API key authentication.

170 lines • 8.05 kB
/** * Write Client Config Tool * Write an updated MCP configuration to a client using the toolflow CLI */ import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); export const writeClientConfigTool = { name: 'write_client_config', description: 'Write an updated MCP configuration to a client. Uses the CLIENT environment variable by default, or a custom path if provided. **CRITICAL: Always preserve existing working MCP servers unless explicitly asked to remove them.** First read the current config, then modify it to add/update/remove only the requested changes. The CLI automatically creates backups. If servers require API keys/tokens (indicated by env properties), guide the user to obtain credentials.', inputSchema: { type: 'object', properties: { config: { type: 'object', description: 'Complete configuration object to write. MUST include all existing servers you want to keep, plus any new/modified servers. Do NOT remove existing servers unless explicitly requested.', additionalProperties: true }, config_path: { type: 'string', description: 'Custom configuration file path (overrides CLIENT environment variable)' } }, required: ['config'] }, async execute(args, context) { try { // Validate config is provided if (!args.config || typeof args.config !== 'object') { throw new Error('Invalid configuration: config object is required'); } // Escape the JSON for shell command const configJson = JSON.stringify(args.config); const escapedConfig = configJson.replace(/"/g, '\\"').replace(/\$/g, '\\$'); let command; let configTarget; if (args.config_path) { // Custom path override command = `npx @grebyn/toolflow-cli@latest write "${escapedConfig}" --path "${args.config_path}"`; configTarget = `custom path: ${args.config_path}`; } else { // Use CLIENT environment variable const client = process.env.CLIENT; if (!client) { throw new Error('CLIENT environment variable not set. Please configure your MCP client connection with CLIENT environment variable.'); } command = `npx @grebyn/toolflow-cli@latest write ${client} "${escapedConfig}"`; configTarget = `client: ${client}`; } // Execute the CLI command const { stdout, stderr } = await execAsync(command, { timeout: 30000, // 30 second timeout maxBuffer: 1024 * 1024 // 1MB buffer }); if (stderr && stderr.includes('Error')) { throw new Error(`CLI error: ${stderr}`); } // Count servers in the written config let serverCount = 0; const config = args.config; if (config.mcpServers) { serverCount = Object.keys(config.mcpServers).length; } else if (config.context_servers) { serverCount = Object.keys(config.context_servers).length; } else if (config.servers) { serverCount = Object.keys(config.servers).length; } // Check for environment variables in config const envInfo = hasEnvironmentVariables(config); return { content: [ { type: 'text', text: formatWriteResult(configTarget, serverCount, stdout, envInfo) } ] }; } catch (error) { console.error(`Failed to write client config: ${error.message}`); const errorMessage = error.message; let troubleshooting = ''; if (errorMessage.includes('CLIENT environment variable not set')) { troubleshooting = `\n\n**Setup Required:**\n- Ensure your MCP client configuration includes: \`"env": { "CLIENT": "your-client-name" }\`\n- Supported clients: claude-desktop, vscode, cursor, and others`; } else if (errorMessage.includes('permission') || errorMessage.includes('EACCES')) { troubleshooting = `\n\n**Possible Issues:**\n- File permission problems\n- Client configuration file may be locked\n- Try closing the client application before writing`; } else if (errorMessage.includes('Invalid configuration')) { troubleshooting = `\n\n**Configuration Issues:**\n- Ensure the config object is valid JSON\n- Check that the configuration format matches the client type`; } return { content: [ { type: 'text', text: `❌ **Failed to Write Client Configuration**\n\nError: ${errorMessage}${troubleshooting}\n\n**Recovery:** The CLI automatically creates backups before writing. Check the backup if needed.` } ] }; } } }; /** * Check if configuration contains servers with environment variables */ function hasEnvironmentVariables(config) { const serversWithEnv = []; // Check different config formats const serverSections = [config.mcpServers, config.servers, config.context_servers]; for (const servers of serverSections) { if (servers && typeof servers === 'object') { for (const [serverName, serverConfig] of Object.entries(servers)) { const server = serverConfig; if (server.env && typeof server.env === 'object' && Object.keys(server.env).length > 0) { serversWithEnv.push(serverName); } } } } return { hasEnv: serversWithEnv.length > 0, serverNames: serversWithEnv }; } /** * Format write result for display */ function formatWriteResult(configTarget, serverCount, cliOutput, envInfo) { let output = `💾 **Configuration Successfully Written**\n\n`; output += `**Target**: ${configTarget}\n`; output += `**Servers Configured**: ${serverCount}\n`; // Include any relevant CLI output if (cliOutput && cliOutput.includes('Backup created')) { const backupMatch = cliOutput.match(/Backup created: (.+)/); if (backupMatch) { output += `**Backup Created**: ${backupMatch[1]}\n`; } } // Check for environment variables if (envInfo.hasEnv) { output += `\n🔑 **Environment Variables Required**\n`; output += `The following servers use environment variables: ${envInfo.serverNames.join(', ')}\n`; output += `⚠️ **Ensure these environment variables are properly configured for the servers to work.**\n`; } // Note about restart requirements const clientName = process.env.CLIENT || 'the client'; const requiresRestart = !['cursor'].includes(clientName.toLowerCase()); if (requiresRestart) { output += `\n⚠️ **Restart Required**: ${clientName} must be restarted for changes to take effect.\n`; } else { output += `\n✅ **No Restart Required**: Configuration changes should take effect immediately.\n`; } output += `\n**Next Steps:**\n`; let stepNum = 1; if (requiresRestart) { output += `${stepNum}. Restart ${clientName} to load the new configuration\n`; stepNum++; } if (envInfo.hasEnv) { output += `${stepNum}. **Configure environment variables** for: ${envInfo.serverNames.join(', ')}\n`; stepNum++; } output += `${stepNum}. Use \`test_mcp_server\` to verify servers are working correctly\n`; return output; } //# sourceMappingURL=write-client-config.js.map