|
1 | 1 | import { Client } from "@modelcontextprotocol/sdk/client/index.js" |
| 2 | +import { PEARAI_URL } from "../../shared/api" |
2 | 3 | import { StdioClientTransport, StdioServerParameters } from "@modelcontextprotocol/sdk/client/stdio.js" |
3 | 4 | import { |
4 | 5 | CallToolResultSchema, |
@@ -55,11 +56,13 @@ export class McpHub { |
55 | 56 | private disposables: vscode.Disposable[] = [] |
56 | 57 | private settingsWatcher?: vscode.FileSystemWatcher |
57 | 58 | private fileWatchers: Map<string, FSWatcher> = new Map() |
| 59 | + private context: vscode.ExtensionContext |
58 | 60 | connections: McpConnection[] = [] |
59 | 61 | isConnecting: boolean = false |
60 | 62 |
|
61 | | - constructor(provider: ClineProvider) { |
| 63 | + constructor(provider: ClineProvider, context: vscode.ExtensionContext) { |
62 | 64 | this.providerRef = new WeakRef(provider) |
| 65 | + this.context = context |
63 | 66 | this.watchMcpSettingsFile() |
64 | 67 | this.initializeMcpServers() |
65 | 68 | } |
@@ -98,7 +101,7 @@ export class McpHub { |
98 | 101 | mcpSettingsFilePath, |
99 | 102 | `{ |
100 | 103 | "mcpServers": { |
101 | | - |
| 104 | +
|
102 | 105 | } |
103 | 106 | }`, |
104 | 107 | ) |
@@ -136,12 +139,64 @@ export class McpHub { |
136 | 139 | ) |
137 | 140 | } |
138 | 141 |
|
| 142 | + private async fetchDefaultSettings(): Promise<Record<string, any>> { |
| 143 | + try { |
| 144 | + const response = await fetch(`${PEARAI_URL}/getDefaultAgentMCPSettings`) |
| 145 | + if (!response.ok) { |
| 146 | + throw new Error(`HTTP error! status: ${response.status}`) |
| 147 | + } |
| 148 | + const data = await response.json() |
| 149 | + if (data && data.mcpServers) { |
| 150 | + return data.mcpServers |
| 151 | + } |
| 152 | + return {} |
| 153 | + } catch (error) { |
| 154 | + console.error("Failed to fetch default MCP settings:", error) |
| 155 | + return {} |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + private async getPearAiApiKey(): Promise<string | null> { |
| 160 | + try { |
| 161 | + const token = await this.context.secrets.get("pearai-token") |
| 162 | + return token || null |
| 163 | + } catch (error) { |
| 164 | + console.error("Failed to get PearAI token from secrets:", error) |
| 165 | + return null |
| 166 | + } |
| 167 | + } |
| 168 | + |
139 | 169 | private async initializeMcpServers(): Promise<void> { |
140 | 170 | try { |
141 | 171 | const settingsPath = await this.getMcpSettingsFilePath() |
142 | 172 | const content = await fs.readFile(settingsPath, "utf-8") |
143 | 173 | const config = JSON.parse(content) |
144 | | - await this.updateServerConnections(config.mcpServers || {}) |
| 174 | + |
| 175 | + // Fetch default settings |
| 176 | + const defaultSettings = await this.fetchDefaultSettings() |
| 177 | + // Only add new servers from default settings that don't exist in current settings |
| 178 | + const mergedServers = { ...(config.mcpServers || {}) } |
| 179 | + for (const [serverName, serverConfig] of Object.entries(defaultSettings)) { |
| 180 | + if (!mergedServers[serverName]) { |
| 181 | + mergedServers[serverName] = serverConfig |
| 182 | + } |
| 183 | + |
| 184 | + // If this is the pearai server, check login status and update API key |
| 185 | + if (serverName === "pearai") { |
| 186 | + const apiKey = await this.getPearAiApiKey() |
| 187 | + if (apiKey) { |
| 188 | + mergedServers[serverName] = { |
| 189 | + ...serverConfig, |
| 190 | + args: ["pearai-mcp", apiKey], |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | + } |
| 195 | + |
| 196 | + // Update the settings file with merged settings |
| 197 | + await fs.writeFile(settingsPath, JSON.stringify({ mcpServers: mergedServers }, null, 2)) |
| 198 | + |
| 199 | + await this.updateServerConnections(mergedServers) |
145 | 200 | } catch (error) { |
146 | 201 | console.error("Failed to initialize MCP servers:", error) |
147 | 202 | } |
@@ -450,6 +505,52 @@ export class McpHub { |
450 | 505 | }) |
451 | 506 | } |
452 | 507 |
|
| 508 | + public async clearPearAiApiKey(): Promise<void> { |
| 509 | + try { |
| 510 | + const settingsPath = await this.getMcpSettingsFilePath() |
| 511 | + const content = await fs.readFile(settingsPath, "utf-8") |
| 512 | + const config = JSON.parse(content) |
| 513 | + |
| 514 | + if (config.mcpServers?.pearai) { |
| 515 | + config.mcpServers.pearai = { |
| 516 | + ...config.mcpServers.pearai, |
| 517 | + args: ["pearai-mcp", "<PEARAI_API_KEY>"], |
| 518 | + } |
| 519 | + |
| 520 | + await fs.writeFile(settingsPath, JSON.stringify(config, null, 2)) |
| 521 | + await this.updateServerConnections(config.mcpServers) |
| 522 | + vscode.window.showInformationMessage("PearAI API key cleared successfully") |
| 523 | + } |
| 524 | + } catch (error) { |
| 525 | + console.error("Failed to clear PearAI API key:", error) |
| 526 | + vscode.window.showErrorMessage("Failed to clear PearAI API key") |
| 527 | + throw error |
| 528 | + } |
| 529 | + } |
| 530 | + |
| 531 | + public async updatePearAiApiKey(apiKey: string): Promise<void> { |
| 532 | + try { |
| 533 | + const settingsPath = await this.getMcpSettingsFilePath() |
| 534 | + const content = await fs.readFile(settingsPath, "utf-8") |
| 535 | + const config = JSON.parse(content) |
| 536 | + |
| 537 | + if (config.mcpServers?.pearai) { |
| 538 | + config.mcpServers.pearai = { |
| 539 | + ...config.mcpServers.pearai, |
| 540 | + args: ["pearai-mcp", apiKey], |
| 541 | + } |
| 542 | + |
| 543 | + await fs.writeFile(settingsPath, JSON.stringify(config, null, 2)) |
| 544 | + await this.updateServerConnections(config.mcpServers) |
| 545 | + vscode.window.showInformationMessage("PearAI API key updated successfully") |
| 546 | + } |
| 547 | + } catch (error) { |
| 548 | + console.error("Failed to update PearAI API key:", error) |
| 549 | + vscode.window.showErrorMessage("Failed to update PearAI API key") |
| 550 | + throw error |
| 551 | + } |
| 552 | + } |
| 553 | + |
453 | 554 | public async toggleServerDisabled(serverName: string, disabled: boolean): Promise<void> { |
454 | 555 | let settingsPath: string |
455 | 556 | try { |
|
0 commit comments