codex-linux 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +10 -0
- package/.eslintrc.json +27 -0
- package/.github/workflows/ci.yml +156 -0
- package/.huskyrc +7 -0
- package/.lintstagedrc +13 -0
- package/.prettierrc +12 -0
- package/CLAUDE.md +163 -0
- package/DESIGN_SUPERIOR.md +73 -0
- package/Dockerfile +64 -0
- package/INSTALLATION.md +152 -0
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/assets/skills/code-review/instructions.md +102 -0
- package/assets/skills/code-review/skill.yaml +15 -0
- package/assets/skills/refactoring/instructions.md +149 -0
- package/assets/skills/refactoring/skill.yaml +15 -0
- package/assets/skills/testing/skill.yaml +15 -0
- package/commitlint.config.js +23 -0
- package/dist/main/DatabaseManager.js +763 -0
- package/dist/main/DatabaseManager.js.map +1 -0
- package/dist/main/SettingsManager.js +61 -0
- package/dist/main/SettingsManager.js.map +1 -0
- package/dist/main/agents/AgentOrchestrator.js +787 -0
- package/dist/main/agents/AgentOrchestrator.js.map +1 -0
- package/dist/main/agents/AgentSDK.js +219 -0
- package/dist/main/agents/AgentSDK.js.map +1 -0
- package/dist/main/agents/AgentTools.js +348 -0
- package/dist/main/agents/AgentTools.js.map +1 -0
- package/dist/main/agents/CodeIndex.js +233 -0
- package/dist/main/agents/CodeIndex.js.map +1 -0
- package/dist/main/agents/EmbeddingService.js +80 -0
- package/dist/main/agents/EmbeddingService.js.map +1 -0
- package/dist/main/agents/NativeToolCalling.js +206 -0
- package/dist/main/agents/NativeToolCalling.js.map +1 -0
- package/dist/main/api/APIServer.js +278 -0
- package/dist/main/api/APIServer.js.map +1 -0
- package/dist/main/api/RateLimiter.js +138 -0
- package/dist/main/api/RateLimiter.js.map +1 -0
- package/dist/main/api/WebSocketManager.js +300 -0
- package/dist/main/api/WebSocketManager.js.map +1 -0
- package/dist/main/assistant/ContextOptimizer.js +192 -0
- package/dist/main/assistant/ContextOptimizer.js.map +1 -0
- package/dist/main/assistant/PredictedOutputManager.js +172 -0
- package/dist/main/assistant/PredictedOutputManager.js.map +1 -0
- package/dist/main/assistant/PromptCacheManager.js +193 -0
- package/dist/main/assistant/PromptCacheManager.js.map +1 -0
- package/dist/main/assistant/PromptOptimizer.js +626 -0
- package/dist/main/assistant/PromptOptimizer.js.map +1 -0
- package/dist/main/assistant/SmartCodeAssistant.js +224 -0
- package/dist/main/assistant/SmartCodeAssistant.js.map +1 -0
- package/dist/main/auth/SessionManager.js +300 -0
- package/dist/main/auth/SessionManager.js.map +1 -0
- package/dist/main/automations/AdvancedWebhookSystem.js +212 -0
- package/dist/main/automations/AdvancedWebhookSystem.js.map +1 -0
- package/dist/main/automations/AutomationScheduler.js +269 -0
- package/dist/main/automations/AutomationScheduler.js.map +1 -0
- package/dist/main/automations/BatchProcessingSystem.js +159 -0
- package/dist/main/automations/BatchProcessingSystem.js.map +1 -0
- package/dist/main/automations/BrowserAutomationManager.js +195 -0
- package/dist/main/automations/BrowserAutomationManager.js.map +1 -0
- package/dist/main/automations/GitHubActionsManager.js +129 -0
- package/dist/main/automations/GitHubActionsManager.js.map +1 -0
- package/dist/main/automations/GitLabCIManager.js +122 -0
- package/dist/main/automations/GitLabCIManager.js.map +1 -0
- package/dist/main/automations/PriorityQueueManager.js +240 -0
- package/dist/main/automations/PriorityQueueManager.js.map +1 -0
- package/dist/main/background/BackgroundModeManager.js +117 -0
- package/dist/main/background/BackgroundModeManager.js.map +1 -0
- package/dist/main/backup/BackupManager.js +254 -0
- package/dist/main/backup/BackupManager.js.map +1 -0
- package/dist/main/backup/MigrationManager.js +114 -0
- package/dist/main/backup/MigrationManager.js.map +1 -0
- package/dist/main/commands/SlashCommandManager.js +399 -0
- package/dist/main/commands/SlashCommandManager.js.map +1 -0
- package/dist/main/config/ClaudeMdParser.js +519 -0
- package/dist/main/config/ClaudeMdParser.js.map +1 -0
- package/dist/main/config/CustomizationManager.js +381 -0
- package/dist/main/config/CustomizationManager.js.map +1 -0
- package/dist/main/config/LaunchConfigManager.js +211 -0
- package/dist/main/config/LaunchConfigManager.js.map +1 -0
- package/dist/main/config/SettingsManager.js +166 -0
- package/dist/main/config/SettingsManager.js.map +1 -0
- package/dist/main/connectors/ConnectorManager.js +151 -0
- package/dist/main/connectors/ConnectorManager.js.map +1 -0
- package/dist/main/connectors/DatabaseConnector.js +222 -0
- package/dist/main/connectors/DatabaseConnector.js.map +1 -0
- package/dist/main/cowork/CoworkManager.js +324 -0
- package/dist/main/cowork/CoworkManager.js.map +1 -0
- package/dist/main/evals/AgentEvalFramework.js +538 -0
- package/dist/main/evals/AgentEvalFramework.js.map +1 -0
- package/dist/main/evals/GraderManager.js +285 -0
- package/dist/main/evals/GraderManager.js.map +1 -0
- package/dist/main/git/GitWorktreeManager.js +214 -0
- package/dist/main/git/GitWorktreeManager.js.map +1 -0
- package/dist/main/github/GitHubPRMonitor.js +244 -0
- package/dist/main/github/GitHubPRMonitor.js.map +1 -0
- package/dist/main/ide/ContinueInManager.js +181 -0
- package/dist/main/ide/ContinueInManager.js.map +1 -0
- package/dist/main/ide/IDEIntegration.js +277 -0
- package/dist/main/ide/IDEIntegration.js.map +1 -0
- package/dist/main/integrations/LinearManager.js +252 -0
- package/dist/main/integrations/LinearManager.js.map +1 -0
- package/dist/main/integrations/SlackBotManager.js +247 -0
- package/dist/main/integrations/SlackBotManager.js.map +1 -0
- package/dist/main/lsp/LSPManager.js +394 -0
- package/dist/main/lsp/LSPManager.js.map +1 -0
- package/dist/main/main.js +1087 -0
- package/dist/main/main.js.map +1 -0
- package/dist/main/mcp/MCPConfigurationManager.js +281 -0
- package/dist/main/mcp/MCPConfigurationManager.js.map +1 -0
- package/dist/main/mcp/MCPManager.js +710 -0
- package/dist/main/mcp/MCPManager.js.map +1 -0
- package/dist/main/mcp/MCPRegistry.js +272 -0
- package/dist/main/mcp/MCPRegistry.js.map +1 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js +268 -0
- package/dist/main/monitoring/ErrorRecoveryManager.js.map +1 -0
- package/dist/main/monitoring/ErrorTracker.js +57 -0
- package/dist/main/monitoring/ErrorTracker.js.map +1 -0
- package/dist/main/monitoring/MetricsCollector.js +155 -0
- package/dist/main/monitoring/MetricsCollector.js.map +1 -0
- package/dist/main/monitoring/TraceGradingSystem.js +148 -0
- package/dist/main/monitoring/TraceGradingSystem.js.map +1 -0
- package/dist/main/notifications/NotificationManager.js +67 -0
- package/dist/main/notifications/NotificationManager.js.map +1 -0
- package/dist/main/pair/AIPairProgramming.js +200 -0
- package/dist/main/pair/AIPairProgramming.js.map +1 -0
- package/dist/main/plugins/PluginManager.js +222 -0
- package/dist/main/plugins/PluginManager.js.map +1 -0
- package/dist/main/plugins/PluginMarketplace.js +237 -0
- package/dist/main/plugins/PluginMarketplace.js.map +1 -0
- package/dist/main/preload.js +189 -0
- package/dist/main/preload.js.map +1 -0
- package/dist/main/preview/PreviewSessionManager.js +170 -0
- package/dist/main/preview/PreviewSessionManager.js.map +1 -0
- package/dist/main/providers/AIProviderManager.js +327 -0
- package/dist/main/providers/AIProviderManager.js.map +1 -0
- package/dist/main/providers/FineTuningManager.js +276 -0
- package/dist/main/providers/FineTuningManager.js.map +1 -0
- package/dist/main/providers/FreeModelsProvider.js +1104 -0
- package/dist/main/providers/FreeModelsProvider.js.map +1 -0
- package/dist/main/realtime/RealtimeManager.js +116 -0
- package/dist/main/realtime/RealtimeManager.js.map +1 -0
- package/dist/main/remote/CloudEnvironmentManager.js +232 -0
- package/dist/main/remote/CloudEnvironmentManager.js.map +1 -0
- package/dist/main/remote/RemoteSessionManager.js +255 -0
- package/dist/main/remote/RemoteSessionManager.js.map +1 -0
- package/dist/main/search/DeepResearchManager.js +335 -0
- package/dist/main/search/DeepResearchManager.js.map +1 -0
- package/dist/main/search/WebSearchIntegration.js +147 -0
- package/dist/main/search/WebSearchIntegration.js.map +1 -0
- package/dist/main/security/AdminConsoleManager.js +223 -0
- package/dist/main/security/AdminConsoleManager.js.map +1 -0
- package/dist/main/security/AuditLogger.js +136 -0
- package/dist/main/security/AuditLogger.js.map +1 -0
- package/dist/main/security/PermissionManager.js +144 -0
- package/dist/main/security/PermissionManager.js.map +1 -0
- package/dist/main/security/SSOManager.js +173 -0
- package/dist/main/security/SSOManager.js.map +1 -0
- package/dist/main/security/SecurityManager.js +152 -0
- package/dist/main/security/SecurityManager.js.map +1 -0
- package/dist/main/skills/SkillsManager.js +223 -0
- package/dist/main/skills/SkillsManager.js.map +1 -0
- package/dist/main/ssh/SSHManager.js +65 -0
- package/dist/main/ssh/SSHManager.js.map +1 -0
- package/dist/main/streaming/StreamingManager.js +225 -0
- package/dist/main/streaming/StreamingManager.js.map +1 -0
- package/dist/main/sync/CloudSyncManager.js +422 -0
- package/dist/main/sync/CloudSyncManager.js.map +1 -0
- package/dist/main/types.js +28 -0
- package/dist/main/types.js.map +1 -0
- package/dist/main/verification/AutoVerifyManager.js +235 -0
- package/dist/main/verification/AutoVerifyManager.js.map +1 -0
- package/dist/main/vision/ComputerUseManager.js +376 -0
- package/dist/main/vision/ComputerUseManager.js.map +1 -0
- package/dist/main/vision/ImageVideoGenerationManager.js +401 -0
- package/dist/main/vision/ImageVideoGenerationManager.js.map +1 -0
- package/dist/main/vision/VisionManager.js +172 -0
- package/dist/main/vision/VisionManager.js.map +1 -0
- package/dist/renderer/assets/main-DJlZQBCA.js +304 -0
- package/dist/renderer/assets/main-N33ZXEr8.css +1 -0
- package/dist/renderer/index.html +21 -0
- package/dist/renderer/manifest.json +42 -0
- package/dist/renderer/sw.ts +109 -0
- package/dist/shared/types.js +35 -0
- package/dist/shared/types.js.map +1 -0
- package/docker-compose.yml +65 -0
- package/docs/API.md +307 -0
- package/docs/USER_GUIDE.md +476 -0
- package/examples/plugins/sample-plugin/package.json +41 -0
- package/examples/plugins/sample-plugin/src/index.ts +75 -0
- package/index.html +20 -0
- package/jest.config.js +39 -0
- package/package.json +180 -0
- package/packages/cli/package.json +29 -0
- package/packages/cli/src/commands/agents.ts +199 -0
- package/packages/cli/src/commands/tasks.ts +61 -0
- package/packages/cli/src/index.ts +91 -0
- package/packages/cli/src/utils/api.ts +45 -0
- package/packages/cli/src/utils/config.ts +61 -0
- package/packages/npm-installer/bin/codex-linux +126 -0
- package/packages/npm-installer/lib/download.js +273 -0
- package/packages/npm-installer/package.json +42 -0
- package/packages/vscode-extension/package.json +167 -0
- package/packages/vscode-extension/src/api.ts +68 -0
- package/packages/vscode-extension/src/extension.ts +161 -0
- package/packages/vscode-extension/src/panels/chatPanel.ts +265 -0
- package/packages/vscode-extension/src/panels/createAgentPanel.ts +227 -0
- package/packages/vscode-extension/src/providers/agentsProvider.ts +80 -0
- package/postcss.config.js +6 -0
- package/public/manifest.json +42 -0
- package/public/sw.ts +109 -0
- package/scripts/install-dev.sh +103 -0
- package/scripts/install.sh +275 -0
- package/src/main/DatabaseManager.ts +950 -0
- package/src/main/SettingsManager.ts +63 -0
- package/src/main/agents/AgentOrchestrator.ts +930 -0
- package/src/main/agents/AgentSDK.ts +269 -0
- package/src/main/agents/AgentTools.ts +380 -0
- package/src/main/agents/CodeIndex.ts +240 -0
- package/src/main/agents/EmbeddingService.ts +88 -0
- package/src/main/agents/NativeToolCalling.ts +245 -0
- package/src/main/api/APIServer.ts +316 -0
- package/src/main/api/RateLimiter.ts +165 -0
- package/src/main/api/WebSocketManager.ts +398 -0
- package/src/main/assistant/ContextOptimizer.ts +214 -0
- package/src/main/assistant/PredictedOutputManager.ts +265 -0
- package/src/main/assistant/PromptCacheManager.ts +280 -0
- package/src/main/assistant/PromptOptimizer.ts +746 -0
- package/src/main/assistant/SmartCodeAssistant.ts +234 -0
- package/src/main/auth/SessionManager.ts +415 -0
- package/src/main/automations/AdvancedWebhookSystem.ts +281 -0
- package/src/main/automations/AutomationScheduler.ts +272 -0
- package/src/main/automations/BatchProcessingSystem.ts +207 -0
- package/src/main/automations/BrowserAutomationManager.ts +203 -0
- package/src/main/automations/GitHubActionsManager.ts +151 -0
- package/src/main/automations/GitLabCIManager.ts +206 -0
- package/src/main/automations/PriorityQueueManager.ts +328 -0
- package/src/main/background/BackgroundModeManager.ts +130 -0
- package/src/main/backup/BackupManager.ts +287 -0
- package/src/main/backup/MigrationManager.ts +132 -0
- package/src/main/commands/SlashCommandManager.ts +407 -0
- package/src/main/config/ClaudeMdParser.ts +539 -0
- package/src/main/config/CustomizationManager.ts +493 -0
- package/src/main/config/LaunchConfigManager.ts +212 -0
- package/src/main/config/SettingsManager.ts +163 -0
- package/src/main/connectors/ConnectorManager.ts +175 -0
- package/src/main/connectors/DatabaseConnector.ts +212 -0
- package/src/main/cowork/CoworkManager.ts +431 -0
- package/src/main/evals/AgentEvalFramework.ts +665 -0
- package/src/main/evals/GraderManager.ts +417 -0
- package/src/main/git/GitWorktreeManager.ts +211 -0
- package/src/main/github/GitHubPRMonitor.ts +317 -0
- package/src/main/ide/ContinueInManager.ts +180 -0
- package/src/main/ide/IDEIntegration.ts +288 -0
- package/src/main/integrations/LinearManager.ts +327 -0
- package/src/main/integrations/SlackBotManager.ts +312 -0
- package/src/main/lsp/LSPManager.ts +445 -0
- package/src/main/main.ts +1221 -0
- package/src/main/mcp/MCPConfigurationManager.ts +281 -0
- package/src/main/mcp/MCPManager.ts +799 -0
- package/src/main/mcp/MCPRegistry.ts +273 -0
- package/src/main/monitoring/ErrorRecoveryManager.ts +359 -0
- package/src/main/monitoring/ErrorTracker.ts +60 -0
- package/src/main/monitoring/MetricsCollector.ts +196 -0
- package/src/main/monitoring/TraceGradingSystem.ts +196 -0
- package/src/main/notifications/NotificationManager.ts +96 -0
- package/src/main/pair/AIPairProgramming.ts +290 -0
- package/src/main/plugins/PluginManager.ts +266 -0
- package/src/main/plugins/PluginMarketplace.ts +318 -0
- package/src/main/preload.ts +215 -0
- package/src/main/preview/PreviewSessionManager.ts +186 -0
- package/src/main/providers/AIProviderManager.ts +394 -0
- package/src/main/providers/FineTuningManager.ts +390 -0
- package/src/main/providers/FreeModelsProvider.ts +1156 -0
- package/src/main/realtime/RealtimeManager.ts +147 -0
- package/src/main/remote/CloudEnvironmentManager.ts +253 -0
- package/src/main/remote/RemoteSessionManager.ts +323 -0
- package/src/main/search/DeepResearchManager.ts +458 -0
- package/src/main/search/WebSearchIntegration.ts +203 -0
- package/src/main/security/AdminConsoleManager.ts +244 -0
- package/src/main/security/AuditLogger.ts +143 -0
- package/src/main/security/PermissionManager.ts +184 -0
- package/src/main/security/SSOManager.ts +241 -0
- package/src/main/security/SecurityManager.ts +139 -0
- package/src/main/skills/SkillsManager.ts +218 -0
- package/src/main/ssh/SSHManager.ts +86 -0
- package/src/main/streaming/StreamingManager.ts +306 -0
- package/src/main/sync/CloudSyncManager.ts +532 -0
- package/src/main/verification/AutoVerifyManager.ts +285 -0
- package/src/main/vision/ComputerUseManager.ts +475 -0
- package/src/main/vision/ImageVideoGenerationManager.ts +526 -0
- package/src/main/vision/VisionManager.ts +186 -0
- package/src/renderer/App.tsx +314 -0
- package/src/renderer/components/AdvancedSettingsPanel.tsx +225 -0
- package/src/renderer/components/AgentPanel.tsx +760 -0
- package/src/renderer/components/AppPreview.tsx +220 -0
- package/src/renderer/components/AuditTrailPanel.tsx +148 -0
- package/src/renderer/components/AutomationPanel.tsx +220 -0
- package/src/renderer/components/ChatInterface.tsx +595 -0
- package/src/renderer/components/ChatTab.tsx +296 -0
- package/src/renderer/components/CodeEditor.tsx +257 -0
- package/src/renderer/components/CodeReviewPanel.tsx +256 -0
- package/src/renderer/components/CodeWorkspace.tsx +192 -0
- package/src/renderer/components/CodebaseDashboard.tsx +295 -0
- package/src/renderer/components/ComputerUsePanel.tsx +262 -0
- package/src/renderer/components/ConnectorsPanel.tsx +471 -0
- package/src/renderer/components/ContextMenu.tsx +155 -0
- package/src/renderer/components/ContextUsageDisplay.tsx +248 -0
- package/src/renderer/components/CoworkPanel.tsx +415 -0
- package/src/renderer/components/DiffViewer.tsx +452 -0
- package/src/renderer/components/ErrorBoundary.tsx +273 -0
- package/src/renderer/components/ExtendedThinkingToggle.tsx +244 -0
- package/src/renderer/components/FileAttachments.tsx +247 -0
- package/src/renderer/components/FileExplorer.tsx +242 -0
- package/src/renderer/components/FileExplorerPanel.tsx +302 -0
- package/src/renderer/components/GitPanel.tsx +154 -0
- package/src/renderer/components/Header.tsx +113 -0
- package/src/renderer/components/MCPPanel.tsx +326 -0
- package/src/renderer/components/MentionAutocomplete.tsx +239 -0
- package/src/renderer/components/PermissionPanel.tsx +159 -0
- package/src/renderer/components/PermissionSelector.tsx +203 -0
- package/src/renderer/components/PluginMarketplace.tsx +325 -0
- package/src/renderer/components/PromptOptimizerPanel.tsx +399 -0
- package/src/renderer/components/SearchPanel.tsx +173 -0
- package/src/renderer/components/SearchReplace.tsx +284 -0
- package/src/renderer/components/SessionSidebar.tsx +367 -0
- package/src/renderer/components/SettingsPanel.tsx +426 -0
- package/src/renderer/components/Sidebar.tsx +100 -0
- package/src/renderer/components/SkillsPanel.tsx +245 -0
- package/src/renderer/components/SplitPane.tsx +173 -0
- package/src/renderer/components/Terminal.tsx +190 -0
- package/src/renderer/components/VoiceCommand.tsx +129 -0
- package/src/renderer/components/WorktreePanel.tsx +163 -0
- package/src/renderer/components/ui/AriaComponents.tsx +193 -0
- package/src/renderer/components/ui/Button.tsx +68 -0
- package/src/renderer/components/ui/Card.tsx +102 -0
- package/src/renderer/components/ui/Input.tsx +44 -0
- package/src/renderer/components/ui/Skeleton.tsx +55 -0
- package/src/renderer/components/ui/VirtualList.tsx +196 -0
- package/src/renderer/i18n/I18nProvider.tsx +101 -0
- package/src/renderer/i18n/de.ts +161 -0
- package/src/renderer/i18n/en.ts +163 -0
- package/src/renderer/i18n/es.ts +161 -0
- package/src/renderer/i18n/fr.ts +161 -0
- package/src/renderer/i18n/index.ts +44 -0
- package/src/renderer/index.css +129 -0
- package/src/renderer/lib/accessibility.tsx +287 -0
- package/src/renderer/lib/hooks.ts +304 -0
- package/src/renderer/lib/utils.ts +6 -0
- package/src/renderer/main.tsx +25 -0
- package/src/renderer/styles/minimalist.css +539 -0
- package/src/renderer/sw.ts +180 -0
- package/src/renderer/types.d.ts +138 -0
- package/src/shared/types.ts +813 -0
- package/supabase/schema.sql +234 -0
- package/tailwind.config.js +78 -0
- package/tests/e2e/package.json +15 -0
- package/tests/e2e/playwright.config.ts +31 -0
- package/tests/e2e/specs/app.spec.ts +194 -0
- package/tests/setup.ts +99 -0
- package/tests/unit/AgentOrchestrator.test.ts +274 -0
- package/tests/unit/DatabaseManager.test.ts +262 -0
- package/tests/unit/GitWorktreeManager.test.ts +150 -0
- package/tests/unit/SecurityManager.test.ts +110 -0
- package/tsconfig.main.json +22 -0
- package/tsconfig.renderer.json +27 -0
- package/vite.config.ts +28 -0
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { spawn, ChildProcess } from 'child_process';
|
|
3
|
+
import * as http from 'http';
|
|
4
|
+
import * as url from 'url';
|
|
5
|
+
import log from 'electron-log';
|
|
6
|
+
import fetch from 'node-fetch';
|
|
7
|
+
import {
|
|
8
|
+
MCPServerDefinition,
|
|
9
|
+
MCPTool,
|
|
10
|
+
MCPResource,
|
|
11
|
+
MCPPrompt,
|
|
12
|
+
MCPScope,
|
|
13
|
+
MCPAuthToken,
|
|
14
|
+
MCPSearchResult
|
|
15
|
+
} from '../../shared/types';
|
|
16
|
+
import { MCPRegistry } from './MCPRegistry';
|
|
17
|
+
import { MCPConfigurationManager } from './MCPConfigurationManager';
|
|
18
|
+
|
|
19
|
+
interface ServerInstance {
|
|
20
|
+
config: MCPServerDefinition;
|
|
21
|
+
process: ChildProcess | null;
|
|
22
|
+
tools: MCPTool[];
|
|
23
|
+
resources: MCPResource[];
|
|
24
|
+
prompts: MCPPrompt[];
|
|
25
|
+
status: 'stopped' | 'starting' | 'running' | 'error';
|
|
26
|
+
lastError?: string;
|
|
27
|
+
authToken?: MCPAuthToken;
|
|
28
|
+
toolsLoaded: boolean;
|
|
29
|
+
toolsByCategory: Map<string, MCPTool[]>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class MCPManager extends EventEmitter {
|
|
33
|
+
private servers: Map<string, ServerInstance> = new Map();
|
|
34
|
+
private registry: MCPRegistry;
|
|
35
|
+
private configManager: MCPConfigurationManager;
|
|
36
|
+
private messageId = 0;
|
|
37
|
+
private pendingRequests: Map<number, { resolve: Function; reject: Function; timeout: NodeJS.Timeout }> = new Map();
|
|
38
|
+
private toolSearchCache: Map<string, { tools: MCPTool[]; timestamp: number }> = new Map();
|
|
39
|
+
private toolSearchTTL = 5 * 60 * 1000;
|
|
40
|
+
|
|
41
|
+
constructor() {
|
|
42
|
+
super();
|
|
43
|
+
this.registry = new MCPRegistry();
|
|
44
|
+
this.configManager = new MCPConfigurationManager();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async initialize(projectPath?: string): Promise<void> {
|
|
48
|
+
await this.registry.initialize();
|
|
49
|
+
await this.configManager.initialize(projectPath);
|
|
50
|
+
|
|
51
|
+
// Load configured servers
|
|
52
|
+
await this.loadConfiguredServers();
|
|
53
|
+
|
|
54
|
+
log.info('MCP Manager initialized with registry and config support');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private async loadConfiguredServers(): Promise<void> {
|
|
58
|
+
const servers = this.configManager.getAllServers();
|
|
59
|
+
|
|
60
|
+
for (const [id, config] of servers) {
|
|
61
|
+
this.servers.set(id, {
|
|
62
|
+
config,
|
|
63
|
+
process: null,
|
|
64
|
+
tools: [],
|
|
65
|
+
resources: [],
|
|
66
|
+
prompts: [],
|
|
67
|
+
status: 'stopped',
|
|
68
|
+
toolsLoaded: false,
|
|
69
|
+
toolsByCategory: new Map(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Auto-start enabled servers
|
|
73
|
+
if (!config.disabled) {
|
|
74
|
+
try {
|
|
75
|
+
await this.startServer(id);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
log.warn(`Failed to auto-start MCP server ${id}:`, error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async addServer(config: MCPServerDefinition): Promise<void> {
|
|
84
|
+
// Save to configuration
|
|
85
|
+
await this.configManager.addServer(config);
|
|
86
|
+
|
|
87
|
+
// Register in memory
|
|
88
|
+
this.servers.set(config.id, {
|
|
89
|
+
config,
|
|
90
|
+
process: null,
|
|
91
|
+
tools: [],
|
|
92
|
+
resources: [],
|
|
93
|
+
prompts: [],
|
|
94
|
+
status: 'stopped',
|
|
95
|
+
toolsLoaded: false,
|
|
96
|
+
toolsByCategory: new Map(),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
this.emit('server:added', config);
|
|
100
|
+
log.info(`Registered MCP server: ${config.name} (${config.scope})`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async removeServer(serverId: string): Promise<void> {
|
|
104
|
+
const server = this.servers.get(serverId);
|
|
105
|
+
if (!server) return;
|
|
106
|
+
|
|
107
|
+
// Stop if running
|
|
108
|
+
if (server.status === 'running') {
|
|
109
|
+
await this.stopServer(serverId);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Remove from config
|
|
113
|
+
await this.configManager.removeServer(serverId, server.config.scope);
|
|
114
|
+
|
|
115
|
+
// Remove from memory
|
|
116
|
+
this.servers.delete(serverId);
|
|
117
|
+
this.emit('server:removed', serverId);
|
|
118
|
+
log.info(`Removed MCP server: ${serverId}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async startServer(serverId: string): Promise<void> {
|
|
122
|
+
const server = this.servers.get(serverId);
|
|
123
|
+
if (!server) throw new Error(`Server ${serverId} not found`);
|
|
124
|
+
if (server.config.disabled) throw new Error(`Server ${serverId} is disabled`);
|
|
125
|
+
if (server.status === 'running') return;
|
|
126
|
+
|
|
127
|
+
server.status = 'starting';
|
|
128
|
+
this.emit('server:starting', serverId);
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
if (server.config.transport === 'stdio' && server.config.command) {
|
|
132
|
+
await this.startStdioServer(server);
|
|
133
|
+
} else if ((server.config.transport === 'http' || server.config.transport === 'streamable-http') && server.config.url) {
|
|
134
|
+
await this.startHttpServer(server);
|
|
135
|
+
} else if (server.config.transport === 'sse' && server.config.url) {
|
|
136
|
+
await this.startSseServer(server);
|
|
137
|
+
} else {
|
|
138
|
+
throw new Error(`Unsupported transport type: ${server.config.transport}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Initialize connection
|
|
142
|
+
await this.initializeConnection(serverId);
|
|
143
|
+
|
|
144
|
+
// Fetch tools, resources, and prompts
|
|
145
|
+
await this.discoverCapabilities(serverId);
|
|
146
|
+
|
|
147
|
+
server.status = 'running';
|
|
148
|
+
this.emit('server:started', serverId);
|
|
149
|
+
log.info(`MCP server started: ${server.config.name}`);
|
|
150
|
+
|
|
151
|
+
} catch (error: any) {
|
|
152
|
+
server.status = 'error';
|
|
153
|
+
server.lastError = error.message;
|
|
154
|
+
this.emit('server:error', { serverId, error });
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async startStdioServer(server: ServerInstance): Promise<void> {
|
|
160
|
+
const { command, args, env } = server.config;
|
|
161
|
+
|
|
162
|
+
server.process = spawn(command!, args || [], {
|
|
163
|
+
env: { ...process.env, ...env },
|
|
164
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Handle stdout
|
|
168
|
+
server.process.stdout?.on('data', (data) => {
|
|
169
|
+
this.handleServerOutput(server.config.id, data);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Handle stderr
|
|
173
|
+
server.process.stderr?.on('data', (data) => {
|
|
174
|
+
log.error(`MCP server ${server.config.id} stderr:`, data.toString());
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Handle exit
|
|
178
|
+
server.process.on('exit', (code) => {
|
|
179
|
+
server.status = code === 0 ? 'stopped' : 'error';
|
|
180
|
+
this.emit('server:stopped', { serverId: server.config.id, code });
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private async startHttpServer(server: ServerInstance): Promise<void> {
|
|
185
|
+
// For HTTP servers, we just verify connectivity
|
|
186
|
+
const response = await fetch(`${server.config.url}/health`, {
|
|
187
|
+
method: 'GET',
|
|
188
|
+
headers: server.config.headers,
|
|
189
|
+
}).catch(() => null);
|
|
190
|
+
|
|
191
|
+
if (!response || !response.ok) {
|
|
192
|
+
log.warn(`HTTP MCP server ${server.config.id} health check failed, will retry on use`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private async startSseServer(server: ServerInstance): Promise<void> {
|
|
197
|
+
// SSE servers establish connection on first use
|
|
198
|
+
log.info(`SSE MCP server ${server.config.id} registered, connection on first use`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private async initializeConnection(serverId: string): Promise<void> {
|
|
202
|
+
const response = await this.sendMessage(serverId, {
|
|
203
|
+
jsonrpc: '2.0',
|
|
204
|
+
id: this.getNextMessageId(),
|
|
205
|
+
method: 'initialize',
|
|
206
|
+
params: {
|
|
207
|
+
protocolVersion: '2024-11-05',
|
|
208
|
+
capabilities: {
|
|
209
|
+
tools: {},
|
|
210
|
+
resources: {},
|
|
211
|
+
prompts: {},
|
|
212
|
+
logging: {},
|
|
213
|
+
},
|
|
214
|
+
clientInfo: {
|
|
215
|
+
name: 'codex-linux',
|
|
216
|
+
version: '1.0.0',
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
if (response.error) {
|
|
222
|
+
throw new Error(`Initialize failed: ${response.error.message}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Send initialized notification
|
|
226
|
+
await this.sendNotification(serverId, 'initialized', {});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private async discoverCapabilities(serverId: string): Promise<void> {
|
|
230
|
+
const server = this.servers.get(serverId);
|
|
231
|
+
if (!server) return;
|
|
232
|
+
|
|
233
|
+
// Discover tools
|
|
234
|
+
try {
|
|
235
|
+
const toolsResponse = await this.sendMessage(serverId, {
|
|
236
|
+
jsonrpc: '2.0',
|
|
237
|
+
id: this.getNextMessageId(),
|
|
238
|
+
method: 'tools/list',
|
|
239
|
+
params: {},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
if (toolsResponse.result?.tools) {
|
|
243
|
+
server.tools = toolsResponse.result.tools.map((t: any) => ({
|
|
244
|
+
...t,
|
|
245
|
+
serverId,
|
|
246
|
+
}));
|
|
247
|
+
|
|
248
|
+
// Build category index for lazy loading
|
|
249
|
+
this.buildToolsCategoryIndex(server);
|
|
250
|
+
server.toolsLoaded = true;
|
|
251
|
+
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
log.warn(`Failed to discover tools for ${serverId}:`, error);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Discover resources
|
|
257
|
+
try {
|
|
258
|
+
const resourcesResponse = await this.sendMessage(serverId, {
|
|
259
|
+
jsonrpc: '2.0',
|
|
260
|
+
id: this.getNextMessageId(),
|
|
261
|
+
method: 'resources/list',
|
|
262
|
+
params: {},
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
if (resourcesResponse.result?.resources) {
|
|
266
|
+
server.resources = resourcesResponse.result.resources.map((r: any) => ({
|
|
267
|
+
...r,
|
|
268
|
+
serverId,
|
|
269
|
+
}));
|
|
270
|
+
}
|
|
271
|
+
} catch (error) {
|
|
272
|
+
log.warn(`Failed to discover resources for ${serverId}:`, error);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Discover prompts
|
|
276
|
+
try {
|
|
277
|
+
const promptsResponse = await this.sendMessage(serverId, {
|
|
278
|
+
jsonrpc: '2.0',
|
|
279
|
+
id: this.getNextMessageId(),
|
|
280
|
+
method: 'prompts/list',
|
|
281
|
+
params: {},
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
if (promptsResponse.result?.prompts) {
|
|
285
|
+
server.prompts = promptsResponse.result.prompts.map((p: any) => ({
|
|
286
|
+
...p,
|
|
287
|
+
serverId,
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
log.warn(`Failed to discover prompts for ${serverId}:`, error);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this.emit('server:capabilities', {
|
|
295
|
+
serverId,
|
|
296
|
+
tools: server.tools,
|
|
297
|
+
resources: server.resources,
|
|
298
|
+
prompts: server.prompts,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async stopServer(serverId: string): Promise<void> {
|
|
303
|
+
const server = this.servers.get(serverId);
|
|
304
|
+
if (!server) return;
|
|
305
|
+
|
|
306
|
+
if (server.process) {
|
|
307
|
+
server.process.kill();
|
|
308
|
+
server.process = null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
server.status = 'stopped';
|
|
312
|
+
server.tools = [];
|
|
313
|
+
server.resources = [];
|
|
314
|
+
server.prompts = [];
|
|
315
|
+
|
|
316
|
+
this.emit('server:stopped', serverId);
|
|
317
|
+
log.info(`MCP server stopped: ${server.config.name}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private async sendMessage(serverId: string, message: any): Promise<any> {
|
|
321
|
+
const server = this.servers.get(serverId);
|
|
322
|
+
if (!server) throw new Error(`Server ${serverId} not found`);
|
|
323
|
+
|
|
324
|
+
if (server.config.transport === 'stdio') {
|
|
325
|
+
return this.sendStdioMessage(server, message);
|
|
326
|
+
} else {
|
|
327
|
+
return this.sendHttpMessage(server, message);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private async sendStdioMessage(server: ServerInstance, message: any): Promise<any> {
|
|
332
|
+
if (!server.process?.stdin) {
|
|
333
|
+
throw new Error(`Server ${server.config.id} not running`);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return new Promise((resolve, reject) => {
|
|
337
|
+
const timeout = setTimeout(() => {
|
|
338
|
+
this.pendingRequests.delete(message.id);
|
|
339
|
+
reject(new Error('MCP request timeout'));
|
|
340
|
+
}, 30000);
|
|
341
|
+
|
|
342
|
+
this.pendingRequests.set(message.id, { resolve, reject, timeout });
|
|
343
|
+
server.process!.stdin!.write(JSON.stringify(message) + '\n');
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
private async sendHttpMessage(server: ServerInstance, message: any): Promise<any> {
|
|
348
|
+
const response = await fetch(`${server.config.url}/mcp`, {
|
|
349
|
+
method: 'POST',
|
|
350
|
+
headers: {
|
|
351
|
+
'Content-Type': 'application/json',
|
|
352
|
+
...server.config.headers,
|
|
353
|
+
},
|
|
354
|
+
body: JSON.stringify(message),
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
if (!response.ok) {
|
|
358
|
+
throw new Error(`HTTP error: ${response.status}`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return response.json();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private async sendNotification(serverId: string, method: string, params: any): Promise<void> {
|
|
365
|
+
await this.sendMessage(serverId, {
|
|
366
|
+
jsonrpc: '2.0',
|
|
367
|
+
method,
|
|
368
|
+
params,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private handleServerOutput(serverId: string, data: Buffer): void {
|
|
373
|
+
const messages = data.toString().trim().split('\n');
|
|
374
|
+
|
|
375
|
+
for (const messageStr of messages) {
|
|
376
|
+
if (!messageStr) continue;
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
const message = JSON.parse(messageStr);
|
|
380
|
+
|
|
381
|
+
// Handle responses
|
|
382
|
+
if (message.id !== undefined && this.pendingRequests.has(message.id)) {
|
|
383
|
+
const pending = this.pendingRequests.get(message.id)!;
|
|
384
|
+
clearTimeout(pending.timeout);
|
|
385
|
+
this.pendingRequests.delete(message.id);
|
|
386
|
+
|
|
387
|
+
if (message.error) {
|
|
388
|
+
pending.reject(new Error(message.error.message));
|
|
389
|
+
} else {
|
|
390
|
+
pending.resolve(message);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Handle notifications
|
|
395
|
+
if (message.method) {
|
|
396
|
+
this.handleNotification(serverId, message);
|
|
397
|
+
}
|
|
398
|
+
} catch (error) {
|
|
399
|
+
log.debug(`MCP server ${serverId} output:`, messageStr);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private handleNotification(serverId: string, message: any): void {
|
|
405
|
+
const server = this.servers.get(serverId);
|
|
406
|
+
if (!server) return;
|
|
407
|
+
|
|
408
|
+
switch (message.method) {
|
|
409
|
+
case 'notifications/tools/list_changed':
|
|
410
|
+
this.discoverCapabilities(serverId);
|
|
411
|
+
break;
|
|
412
|
+
case 'notifications/resources/list_changed':
|
|
413
|
+
this.discoverCapabilities(serverId);
|
|
414
|
+
break;
|
|
415
|
+
case 'notifications/prompts/list_changed':
|
|
416
|
+
this.discoverCapabilities(serverId);
|
|
417
|
+
break;
|
|
418
|
+
case 'notifications/message':
|
|
419
|
+
log.info(`MCP server ${serverId}:`, message.params);
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async callTool(serverId: string, toolName: string, args: any): Promise<any> {
|
|
425
|
+
const response = await this.sendMessage(serverId, {
|
|
426
|
+
jsonrpc: '2.0',
|
|
427
|
+
id: this.getNextMessageId(),
|
|
428
|
+
method: 'tools/call',
|
|
429
|
+
params: {
|
|
430
|
+
name: toolName,
|
|
431
|
+
arguments: args,
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
if (response.error) {
|
|
436
|
+
throw new Error(response.error.message);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return response.result;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async readResource(serverId: string, uri: string): Promise<any> {
|
|
443
|
+
const response = await this.sendMessage(serverId, {
|
|
444
|
+
jsonrpc: '2.0',
|
|
445
|
+
id: this.getNextMessageId(),
|
|
446
|
+
method: 'resources/read',
|
|
447
|
+
params: { uri },
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
if (response.error) {
|
|
451
|
+
throw new Error(response.error.message);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return response.result;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async getPrompt(serverId: string, promptName: string, args?: Record<string, string>): Promise<any> {
|
|
458
|
+
const response = await this.sendMessage(serverId, {
|
|
459
|
+
jsonrpc: '2.0',
|
|
460
|
+
id: this.getNextMessageId(),
|
|
461
|
+
method: 'prompts/get',
|
|
462
|
+
params: {
|
|
463
|
+
name: promptName,
|
|
464
|
+
arguments: args,
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
if (response.error) {
|
|
469
|
+
throw new Error(response.error.message);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return response.result;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Tool Search Optimization
|
|
476
|
+
async searchTools(query: string): Promise<MCPSearchResult> {
|
|
477
|
+
const result: MCPSearchResult = {
|
|
478
|
+
query,
|
|
479
|
+
tools: [],
|
|
480
|
+
resources: [],
|
|
481
|
+
prompts: [],
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const lowerQuery = query.toLowerCase();
|
|
485
|
+
|
|
486
|
+
for (const server of this.servers.values()) {
|
|
487
|
+
if (server.status !== 'running') continue;
|
|
488
|
+
|
|
489
|
+
// Search tools
|
|
490
|
+
for (const tool of server.tools) {
|
|
491
|
+
if (
|
|
492
|
+
tool.name.toLowerCase().includes(lowerQuery) ||
|
|
493
|
+
tool.description?.toLowerCase().includes(lowerQuery)
|
|
494
|
+
) {
|
|
495
|
+
result.tools.push(tool);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Search resources
|
|
500
|
+
for (const resource of server.resources) {
|
|
501
|
+
if (
|
|
502
|
+
resource.name.toLowerCase().includes(lowerQuery) ||
|
|
503
|
+
resource.uri.toLowerCase().includes(lowerQuery)
|
|
504
|
+
) {
|
|
505
|
+
result.resources.push(resource);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Search prompts
|
|
510
|
+
for (const prompt of server.prompts) {
|
|
511
|
+
if (
|
|
512
|
+
prompt.name.toLowerCase().includes(lowerQuery) ||
|
|
513
|
+
prompt.description?.toLowerCase().includes(lowerQuery)
|
|
514
|
+
) {
|
|
515
|
+
result.prompts.push(prompt);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return result;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// OAuth Authentication
|
|
524
|
+
async authenticateServer(serverId: string): Promise<boolean> {
|
|
525
|
+
const server = this.servers.get(serverId);
|
|
526
|
+
if (!server?.config.oauth) {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const { clientId, callbackPort = 8080 } = server.config.oauth;
|
|
531
|
+
|
|
532
|
+
// Start local HTTP server for OAuth callback
|
|
533
|
+
const redirectUri = `http://localhost:${callbackPort}/callback`;
|
|
534
|
+
|
|
535
|
+
return new Promise((resolve, reject) => {
|
|
536
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
537
|
+
const parsedUrl = url.parse(req.url || '', true);
|
|
538
|
+
|
|
539
|
+
if (parsedUrl.pathname === '/callback') {
|
|
540
|
+
const code = parsedUrl.query.code as string;
|
|
541
|
+
|
|
542
|
+
if (code) {
|
|
543
|
+
try {
|
|
544
|
+
// Exchange code for token (implementation depends on OAuth provider)
|
|
545
|
+
// This is a simplified version
|
|
546
|
+
const token: MCPAuthToken = {
|
|
547
|
+
serverId,
|
|
548
|
+
accessToken: code, // In real implementation, exchange code
|
|
549
|
+
expiresAt: new Date(Date.now() + 3600 * 1000), // 1 hour
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
server.authToken = token;
|
|
553
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
554
|
+
res.end('<h1>Authentication successful!</h1><p>You can close this window.</p>');
|
|
555
|
+
httpServer.close();
|
|
556
|
+
resolve(true);
|
|
557
|
+
} catch (error) {
|
|
558
|
+
res.writeHead(500, { 'Content-Type': 'text/html' });
|
|
559
|
+
res.end('<h1>Authentication failed</h1>');
|
|
560
|
+
httpServer.close();
|
|
561
|
+
reject(error);
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
565
|
+
res.end('<h1>Authentication failed</h1><p>No authorization code received.</p>');
|
|
566
|
+
httpServer.close();
|
|
567
|
+
resolve(false);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
httpServer.listen(callbackPort, () => {
|
|
573
|
+
log.info(`OAuth callback server started on port ${callbackPort}`);
|
|
574
|
+
|
|
575
|
+
// Open browser for OAuth (would need electron shell.openExternal)
|
|
576
|
+
const authUrl = `${server.config.url}/auth?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code`;
|
|
577
|
+
log.info(`Please visit: ${authUrl}`);
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// Timeout after 5 minutes
|
|
581
|
+
setTimeout(() => {
|
|
582
|
+
httpServer.close();
|
|
583
|
+
reject(new Error('OAuth timeout'));
|
|
584
|
+
}, 5 * 60 * 1000);
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
getAllTools(): MCPTool[] {
|
|
589
|
+
const tools: MCPTool[] = [];
|
|
590
|
+
for (const server of this.servers.values()) {
|
|
591
|
+
if (server.status === 'running') {
|
|
592
|
+
tools.push(...server.tools);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return tools;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
getAllResources(): MCPResource[] {
|
|
599
|
+
const resources: MCPResource[] = [];
|
|
600
|
+
for (const server of this.servers.values()) {
|
|
601
|
+
if (server.status === 'running') {
|
|
602
|
+
resources.push(...server.resources);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return resources;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
getAllPrompts(): MCPPrompt[] {
|
|
609
|
+
const prompts: MCPPrompt[] = [];
|
|
610
|
+
for (const server of this.servers.values()) {
|
|
611
|
+
if (server.status === 'running') {
|
|
612
|
+
prompts.push(...server.prompts);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return prompts;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
getServerStatus(serverId: string): string {
|
|
619
|
+
return this.servers.get(serverId)?.status || 'stopped';
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
getServers(): Array<{
|
|
623
|
+
id: string;
|
|
624
|
+
name: string;
|
|
625
|
+
status: string;
|
|
626
|
+
disabled?: boolean;
|
|
627
|
+
scope: MCPScope;
|
|
628
|
+
transport: string;
|
|
629
|
+
tools: number;
|
|
630
|
+
resources: number;
|
|
631
|
+
}> {
|
|
632
|
+
return Array.from(this.servers.values()).map(s => ({
|
|
633
|
+
id: s.config.id,
|
|
634
|
+
name: s.config.name,
|
|
635
|
+
status: s.status,
|
|
636
|
+
disabled: s.config.disabled,
|
|
637
|
+
scope: s.config.scope,
|
|
638
|
+
transport: s.config.transport,
|
|
639
|
+
tools: s.tools.length,
|
|
640
|
+
resources: s.resources.length,
|
|
641
|
+
}));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
getRegistry(): MCPRegistry {
|
|
645
|
+
return this.registry;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
getConfigManager(): MCPConfigurationManager {
|
|
649
|
+
return this.configManager;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Lazy Loading - Build category index for tools
|
|
653
|
+
private buildToolsCategoryIndex(server: ServerInstance): void {
|
|
654
|
+
server.toolsByCategory.clear();
|
|
655
|
+
|
|
656
|
+
for (const tool of server.tools) {
|
|
657
|
+
const category = this.categorizeTool(tool);
|
|
658
|
+
const existing = server.toolsByCategory.get(category) || [];
|
|
659
|
+
existing.push(tool);
|
|
660
|
+
server.toolsByCategory.set(category, existing);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
private categorizeTool(tool: MCPTool): string {
|
|
665
|
+
const name = tool.name.toLowerCase();
|
|
666
|
+
const inputSchema = JSON.stringify(tool.inputSchema).toLowerCase();
|
|
667
|
+
|
|
668
|
+
if (name.includes('file') || name.includes('read') || name.includes('write') || inputSchema.includes('path')) {
|
|
669
|
+
return 'filesystem';
|
|
670
|
+
}
|
|
671
|
+
if (name.includes('git') || name.includes('branch') || name.includes('commit')) {
|
|
672
|
+
return 'git';
|
|
673
|
+
}
|
|
674
|
+
if (name.includes('search') || name.includes('find') || name.includes('query')) {
|
|
675
|
+
return 'search';
|
|
676
|
+
}
|
|
677
|
+
if (name.includes('db') || name.includes('database') || name.includes('sql')) {
|
|
678
|
+
return 'database';
|
|
679
|
+
}
|
|
680
|
+
if (name.includes('api') || name.includes('http') || name.includes('fetch')) {
|
|
681
|
+
return 'api';
|
|
682
|
+
}
|
|
683
|
+
if (name.includes('run') || name.includes('execute') || name.includes('command')) {
|
|
684
|
+
return 'execution';
|
|
685
|
+
}
|
|
686
|
+
return 'other';
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Get tools by category (lazy loaded)
|
|
690
|
+
async getToolsByCategory(serverId: string, category: string): Promise<MCPTool[]> {
|
|
691
|
+
const server = this.servers.get(serverId);
|
|
692
|
+
if (!server) return [];
|
|
693
|
+
|
|
694
|
+
if (!server.toolsLoaded) {
|
|
695
|
+
await this.discoverCapabilities(serverId);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return server.toolsByCategory.get(category) || [];
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Smart tool retrieval - only loads tools when needed
|
|
702
|
+
async getRelevantTools(serverId: string, context: string): Promise<MCPTool[]> {
|
|
703
|
+
const server = this.servers.get(serverId);
|
|
704
|
+
if (!server) return [];
|
|
705
|
+
|
|
706
|
+
if (!server.toolsLoaded) {
|
|
707
|
+
await this.discoverCapabilities(serverId);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const contextLower = context.toLowerCase();
|
|
711
|
+
const relevantTools: MCPTool[] = [];
|
|
712
|
+
|
|
713
|
+
for (const tool of server.tools) {
|
|
714
|
+
const name = tool.name.toLowerCase();
|
|
715
|
+
const desc = (tool.description || '').toLowerCase();
|
|
716
|
+
|
|
717
|
+
// Score tool relevance
|
|
718
|
+
let score = 0;
|
|
719
|
+
if (name.includes(contextLower) || desc.includes(contextLower)) {
|
|
720
|
+
score = 3;
|
|
721
|
+
} else if (this.categorizeTool(tool) === this.inferCategoryFromContext(contextLower)) {
|
|
722
|
+
score = 2;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
if (score > 0) {
|
|
726
|
+
relevantTools.push(tool);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// If no specific tools found, return all (for broader context)
|
|
731
|
+
return relevantTools.length > 0 ? relevantTools : server.tools.slice(0, 10);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
private inferCategoryFromContext(context: string): string {
|
|
735
|
+
if (context.includes('file') || context.includes('folder') || context.includes('directory')) {
|
|
736
|
+
return 'filesystem';
|
|
737
|
+
}
|
|
738
|
+
if (context.includes('git') || context.includes('commit') || context.includes('branch')) {
|
|
739
|
+
return 'git';
|
|
740
|
+
}
|
|
741
|
+
if (context.includes('search') || context.includes('find')) {
|
|
742
|
+
return 'search';
|
|
743
|
+
}
|
|
744
|
+
if (context.includes('database') || context.includes('query') || context.includes('sql')) {
|
|
745
|
+
return 'database';
|
|
746
|
+
}
|
|
747
|
+
return 'other';
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// Cached search with TTL
|
|
751
|
+
async searchToolsCached(query: string): Promise<MCPSearchResult> {
|
|
752
|
+
const cached = this.toolSearchCache.get(query);
|
|
753
|
+
if (cached && Date.now() - cached.timestamp < this.toolSearchTTL) {
|
|
754
|
+
return {
|
|
755
|
+
query,
|
|
756
|
+
tools: cached.tools,
|
|
757
|
+
resources: [],
|
|
758
|
+
prompts: [],
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const result = await this.searchTools(query);
|
|
763
|
+
this.toolSearchCache.set(query, {
|
|
764
|
+
tools: result.tools,
|
|
765
|
+
timestamp: Date.now(),
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
return result;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Clear tool cache
|
|
772
|
+
clearToolCache(): void {
|
|
773
|
+
this.toolSearchCache.clear();
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Get tools count for UI
|
|
777
|
+
getToolsCount(): number {
|
|
778
|
+
let count = 0;
|
|
779
|
+
for (const server of this.servers.values()) {
|
|
780
|
+
if (server.status === 'running') {
|
|
781
|
+
count += server.tools.length;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return count;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
private getNextMessageId(): number {
|
|
788
|
+
return ++this.messageId;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
async cleanup(): Promise<void> {
|
|
792
|
+
for (const [serverId] of this.servers) {
|
|
793
|
+
await this.stopServer(serverId);
|
|
794
|
+
}
|
|
795
|
+
this.pendingRequests.clear();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
export const mcpManager = new MCPManager();
|