diff --git a/src/steps/add-mcp-server-to-clients/MCPClient.ts b/src/steps/add-mcp-server-to-clients/MCPClient.ts index f193fa34..99626605 100644 --- a/src/steps/add-mcp-server-to-clients/MCPClient.ts +++ b/src/steps/add-mcp-server-to-clients/MCPClient.ts @@ -6,6 +6,7 @@ import { merge } from 'lodash'; export abstract class MCPClient { name: string; abstract getConfigPath(): Promise; + abstract getServerPropertyName(): string; abstract isServerInstalled(): Promise; abstract addServer(apiKey: string): Promise<{ success: boolean }>; abstract removeServer(): Promise<{ success: boolean }>; @@ -18,6 +19,11 @@ export abstract class DefaultMCPClient extends MCPClient { constructor() { super(); } + + getServerPropertyName(): string { + return 'mcpServers'; + } + async isServerInstalled(): Promise { try { const configPath = await this.getConfigPath(); @@ -28,8 +34,10 @@ export abstract class DefaultMCPClient extends MCPClient { const configContent = await fs.promises.readFile(configPath, 'utf8'); const config = JSON.parse(configContent); - - return 'mcpServers' in config && 'posthog' in config.mcpServers; + const serverPropertyName = this.getServerPropertyName(); + return ( + serverPropertyName in config && 'posthog' in config[serverPropertyName] + ); } catch { return false; } @@ -49,8 +57,9 @@ export abstract class DefaultMCPClient extends MCPClient { await fs.promises.mkdir(configDir, { recursive: true }); + const serverPropertyName = this.getServerPropertyName(); const newServerConfig = { - mcpServers: { + [serverPropertyName]: { posthog: getDefaultServerConfig(apiKey, type), }, }; @@ -87,9 +96,13 @@ export abstract class DefaultMCPClient extends MCPClient { const configContent = await fs.promises.readFile(configPath, 'utf8'); const config = JSON.parse(configContent); + const serverPropertyName = this.getServerPropertyName(); - if ('mcpServers' in config && 'posthog' in config.mcpServers) { - delete config.mcpServers.posthog; + if ( + serverPropertyName in config && + 'posthog' in config[serverPropertyName] + ) { + delete config[serverPropertyName].posthog; await fs.promises.writeFile( configPath, diff --git a/src/steps/add-mcp-server-to-clients/clients/visual-studio-code.ts b/src/steps/add-mcp-server-to-clients/clients/visual-studio-code.ts new file mode 100644 index 00000000..6ce9533b --- /dev/null +++ b/src/steps/add-mcp-server-to-clients/clients/visual-studio-code.ts @@ -0,0 +1,71 @@ +import z from 'zod'; +import * as path from 'path'; +import * as os from 'os'; +import { DefaultMCPClient } from '../MCPClient'; + +export const VisualStudioCodeMCPConfig = z + .object({ + servers: z.record( + z.string(), + z.object({ + command: z.string().optional(), + args: z.array(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), + }), + ), + }) + .passthrough(); + +export type VisualStudioCodeMCPConfig = z.infer< + typeof VisualStudioCodeMCPConfig +>; + +export class VisualStudioCodeClient extends DefaultMCPClient { + name = 'Visual Studio Code'; + + getServerPropertyName(): string { + return 'servers'; + } + + async isClientSupported(): Promise { + return Promise.resolve( + process.platform === 'darwin' || + process.platform === 'win32' || + process.platform === 'linux', + ); + } + + async getConfigPath(): Promise { + const homeDir = os.homedir(); + const isWindows = process.platform === 'win32'; + const isMac = process.platform === 'darwin'; + const isLinux = process.platform === 'linux'; + + if (isMac) { + return Promise.resolve( + path.join( + homeDir, + 'Library', + 'Application Support', + 'Code', + 'User', + 'mcp.json', + ), + ); + } + + if (isWindows) { + return Promise.resolve( + path.join(process.env.APPDATA || '', 'Code', 'User', 'mcp.json'), + ); + } + + if (isLinux) { + return Promise.resolve( + path.join(homeDir, '.config', 'Code', 'User', 'mcp.json'), + ); + } + + throw new Error(`Unsupported platform: ${process.platform}`); + } +} diff --git a/src/steps/add-mcp-server-to-clients/index.ts b/src/steps/add-mcp-server-to-clients/index.ts index 3dbf14dc..51122e65 100644 --- a/src/steps/add-mcp-server-to-clients/index.ts +++ b/src/steps/add-mcp-server-to-clients/index.ts @@ -10,12 +10,14 @@ import { ClaudeMCPClient } from './clients/claude'; import { getPersonalApiKey } from '../../mcp'; import type { CloudRegion } from '../../utils/types'; import { ClaudeCodeMCPClient } from './clients/claude-code'; +import { VisualStudioCodeClient } from './clients/visual-studio-code'; export const getSupportedClients = async (): Promise => { const allClients = [ new CursorMCPClient(), new ClaudeMCPClient(), new ClaudeCodeMCPClient(), + new VisualStudioCodeClient(), ]; const supportedClients: MCPClient[] = []; @@ -146,7 +148,6 @@ export const removeMCPServerFromClientsStep = async ({ integration?: Integration; }): Promise => { const installedClients = await getInstalledClients(); - if (installedClients.length === 0) { analytics.capture('wizard interaction', { action: 'no mcp servers to remove', @@ -198,7 +199,6 @@ export const removeMCPServerFromClientsStep = async ({ export const getInstalledClients = async (): Promise => { const clients = await getSupportedClients(); - const installedClients: MCPClient[] = []; for (const client of clients) {