diff --git a/src/extension.ts b/src/extension.ts index ef8895ee24b..5dcfd3169e5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -67,6 +67,14 @@ export function activate(context: vscode.ExtensionContext) { console.dir(data) context.secrets.store("pearai-token", data.accessToken) context.secrets.store("pearai-refresh", data.refreshToken) + // Update MCP server with new token + const provider = await ClineProvider.getInstance() + if (provider) { + const mcpHub = provider.getMcpHub() + if (mcpHub) { + await mcpHub.updatePearAiApiKey(data.accessToken) + } + } vscode.commands.executeCommand("roo-cline.plusButtonClicked") }), ) @@ -76,6 +84,14 @@ export function activate(context: vscode.ExtensionContext) { console.dir("Logged out of PearAI:") context.secrets.delete("pearai-token") context.secrets.delete("pearai-refresh") + // Clear MCP server token + const provider = await ClineProvider.getInstance() + if (provider) { + const mcpHub = provider.getMcpHub() + if (mcpHub) { + await mcpHub.clearPearAiApiKey() + } + } }), ) diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 6c906c7cf89..050e7ae2ba9 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -1,4 +1,5 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js" +import { PEARAI_URL } from "../../shared/api" import { StdioClientTransport, StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js" import { CallToolResultSchema, @@ -55,11 +56,13 @@ export class McpHub { private disposables: vscode.Disposable[] = [] private settingsWatcher?: vscode.FileSystemWatcher private fileWatchers: Map = new Map() + private context: vscode.ExtensionContext connections: McpConnection[] = [] isConnecting: boolean = false - constructor(provider: ClineProvider) { + constructor(provider: ClineProvider, context: vscode.ExtensionContext) { this.providerRef = new WeakRef(provider) + this.context = context this.watchMcpSettingsFile() this.initializeMcpServers() } @@ -98,7 +101,7 @@ export class McpHub { mcpSettingsFilePath, `{ "mcpServers": { - + } }`, ) @@ -136,12 +139,64 @@ export class McpHub { ) } + private async fetchDefaultSettings(): Promise> { + try { + const response = await fetch(`${PEARAI_URL}/getDefaultAgentMCPSettings`) + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + const data = await response.json() + if (data && data.mcpServers) { + return data.mcpServers + } + return {} + } catch (error) { + console.error("Failed to fetch default MCP settings:", error) + return {} + } + } + + private async getPearAiApiKey(): Promise { + try { + const token = await this.context.secrets.get("pearai-token") + return token || null + } catch (error) { + console.error("Failed to get PearAI token from secrets:", error) + return null + } + } + private async initializeMcpServers(): Promise { try { const settingsPath = await this.getMcpSettingsFilePath() const content = await fs.readFile(settingsPath, "utf-8") const config = JSON.parse(content) - await this.updateServerConnections(config.mcpServers || {}) + + // Fetch default settings + const defaultSettings = await this.fetchDefaultSettings() + // Only add new servers from default settings that don't exist in current settings + const mergedServers = { ...(config.mcpServers || {}) } + for (const [serverName, serverConfig] of Object.entries(defaultSettings)) { + if (!mergedServers[serverName]) { + mergedServers[serverName] = serverConfig + } + + // If this is the pearai server, check login status and update API key + if (serverName === "pearai") { + const apiKey = await this.getPearAiApiKey() + if (apiKey) { + mergedServers[serverName] = { + ...serverConfig, + args: ["pearai-mcp", apiKey], + } + } + } + } + + // Update the settings file with merged settings + await fs.writeFile(settingsPath, JSON.stringify({ mcpServers: mergedServers }, null, 2)) + + await this.updateServerConnections(mergedServers) } catch (error) { console.error("Failed to initialize MCP servers:", error) } @@ -450,6 +505,52 @@ export class McpHub { }) } + public async clearPearAiApiKey(): Promise { + try { + const settingsPath = await this.getMcpSettingsFilePath() + const content = await fs.readFile(settingsPath, "utf-8") + const config = JSON.parse(content) + + if (config.mcpServers?.pearai) { + config.mcpServers.pearai = { + ...config.mcpServers.pearai, + args: ["pearai-mcp", ""], + } + + await fs.writeFile(settingsPath, JSON.stringify(config, null, 2)) + await this.updateServerConnections(config.mcpServers) + vscode.window.showInformationMessage("PearAI API key cleared successfully") + } + } catch (error) { + console.error("Failed to clear PearAI API key:", error) + vscode.window.showErrorMessage("Failed to clear PearAI API key") + throw error + } + } + + public async updatePearAiApiKey(apiKey: string): Promise { + try { + const settingsPath = await this.getMcpSettingsFilePath() + const content = await fs.readFile(settingsPath, "utf-8") + const config = JSON.parse(content) + + if (config.mcpServers?.pearai) { + config.mcpServers.pearai = { + ...config.mcpServers.pearai, + args: ["pearai-mcp", apiKey], + } + + await fs.writeFile(settingsPath, JSON.stringify(config, null, 2)) + await this.updateServerConnections(config.mcpServers) + vscode.window.showInformationMessage("PearAI API key updated successfully") + } + } catch (error) { + console.error("Failed to update PearAI API key:", error) + vscode.window.showErrorMessage("Failed to update PearAI API key") + throw error + } + } + public async toggleServerDisabled(serverName: string, disabled: boolean): Promise { let settingsPath: string try { diff --git a/src/services/mcp/McpServerManager.ts b/src/services/mcp/McpServerManager.ts index e15f9db0a7a..54e8c88aa57 100644 --- a/src/services/mcp/McpServerManager.ts +++ b/src/services/mcp/McpServerManager.ts @@ -36,7 +36,7 @@ export class McpServerManager { try { // Double-check instance in case it was created while we were waiting if (!this.instance) { - this.instance = new McpHub(provider) + this.instance = new McpHub(provider, context) // Store a unique identifier in global state to track the primary instance await context.globalState.update(this.GLOBAL_STATE_KEY, Date.now().toString()) } diff --git a/src/shared/globalFileNames.ts b/src/shared/globalFileNames.ts index 6088e95d999..9eec311b909 100644 --- a/src/shared/globalFileNames.ts +++ b/src/shared/globalFileNames.ts @@ -4,6 +4,6 @@ export const GlobalFileNames = { glamaModels: "glama_models.json", openRouterModels: "openrouter_models.json", requestyModels: "requesty_models.json", - mcpSettings: "cline_mcp_settings.json", + mcpSettings: "pearai_agent_mcp_settings.json", unboundModels: "unbound_models.json", }