Path: blob/main/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { VSBuffer } from '../../../../../base/common/buffer.js';6import { Platform } from '../../../../../base/common/platform.js';7import { Mutable } from '../../../../../base/common/types.js';8import { URI } from '../../../../../base/common/uri.js';9import { INativeMcpDiscoveryData } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js';10import { DiscoverySource } from '../mcpConfiguration.js';11import { McpCollectionSortOrder, McpServerDefinition, McpServerLaunch, McpServerTransportType } from '../mcpTypes.js';1213export interface NativeMpcDiscoveryAdapter {14readonly remoteAuthority: string | null;15readonly id: string;16readonly order: number;17readonly discoverySource: DiscoverySource;1819getFilePath(details: INativeMcpDiscoveryData): URI | undefined;20adaptFile(contents: VSBuffer, details: INativeMcpDiscoveryData): Promise<McpServerDefinition[] | undefined>;21}2223export async function claudeConfigToServerDefinition(idPrefix: string, contents: VSBuffer, cwd?: URI) {24let parsed: {25mcpServers: Record<string, {26command: string;27args?: string[];28env?: Record<string, string>;29url?: string;30}>;31};3233try {34parsed = JSON.parse(contents.toString());35} catch {36return;37}3839return Promise.all(Object.entries(parsed.mcpServers).map(async ([name, server]): Promise<Mutable<McpServerDefinition>> => {40const launch: McpServerLaunch = server.url ? {41type: McpServerTransportType.HTTP,42uri: URI.parse(server.url),43headers: [],44} : {45type: McpServerTransportType.Stdio,46args: server.args || [],47command: server.command,48env: server.env || {},49envFile: undefined,50cwd: cwd?.fsPath,51};5253return {54id: `${idPrefix}.${name}`,55label: name,56launch,57cacheNonce: await McpServerLaunch.hash(launch),58};59}));60}6162export class ClaudeDesktopMpcDiscoveryAdapter implements NativeMpcDiscoveryAdapter {63public id: string;64public readonly order = McpCollectionSortOrder.Filesystem;65public readonly discoverySource: DiscoverySource = DiscoverySource.ClaudeDesktop;6667constructor(public readonly remoteAuthority: string | null) {68this.id = `claude-desktop.${this.remoteAuthority}`;69}7071getFilePath({ platform, winAppData, xdgHome, homedir }: INativeMcpDiscoveryData): URI | undefined {72if (platform === Platform.Windows) {73const appData = winAppData || URI.joinPath(homedir, 'AppData', 'Roaming');74return URI.joinPath(appData, 'Claude', 'claude_desktop_config.json');75} else if (platform === Platform.Mac) {76return URI.joinPath(homedir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');77} else {78const configDir = xdgHome || URI.joinPath(homedir, '.config');79return URI.joinPath(configDir, 'Claude', 'claude_desktop_config.json');80}81}8283adaptFile(contents: VSBuffer, { homedir }: INativeMcpDiscoveryData): Promise<McpServerDefinition[] | undefined> {84return claudeConfigToServerDefinition(this.id, contents, homedir);85}86}8788export class WindsurfDesktopMpcDiscoveryAdapter extends ClaudeDesktopMpcDiscoveryAdapter {89public override readonly discoverySource: DiscoverySource = DiscoverySource.Windsurf;9091constructor(remoteAuthority: string | null) {92super(remoteAuthority);93this.id = `windsurf.${this.remoteAuthority}`;94}9596override getFilePath({ homedir }: INativeMcpDiscoveryData): URI | undefined {97return URI.joinPath(homedir, '.codeium', 'windsurf', 'mcp_config.json');98}99}100101export class CursorDesktopMpcDiscoveryAdapter extends ClaudeDesktopMpcDiscoveryAdapter {102public override readonly discoverySource: DiscoverySource = DiscoverySource.CursorGlobal;103104constructor(remoteAuthority: string | null) {105super(remoteAuthority);106this.id = `cursor.${this.remoteAuthority}`;107}108109override getFilePath({ homedir }: INativeMcpDiscoveryData): URI | undefined {110return URI.joinPath(homedir, '.cursor', 'mcp.json');111}112}113114115