feat: support MCP sse client (#2880)
* ✨ feat: add Model Context Protocol (MCP) server configuration (main) - Added `@modelcontextprotocol/sdk` dependency for MCP integration. - Introduced MCP server configuration UI in settings with add, edit, delete, and activation functionalities. - Created `useMCPServers` hook to manage MCP server state and actions. - Added i18n support for MCP settings with translation keys. - Integrated MCP settings into the application's settings navigation and routing. - Implemented Redux state management for MCP servers. - Updated `yarn.lock` with new dependencies and their resolutions. * 🌟 feat: implement mcp service and integrate with ipc handlers - Added `MCPService` class to manage Model Context Protocol servers. - Implemented various handlers in `ipc.ts` for managing MCP servers including listing, adding, updating, deleting, and activating/deactivating servers. - Integrated MCP related types into existing type declarations for consistency across the application. - Updated `preload` to expose new MCP related APIs to the renderer process. - Enhanced `MCPSettings` component to interact directly with the new MCP service for adding, updating, deleting servers and setting their active states. - Introduced selectors in the MCP Redux slice for fetching active and all servers from the store. - Moved MCP types to a centralized location in `@renderer/types` for reuse across different parts of the application. * feat: enhance MCPService initialization to prevent recursive calls and improve error handling * feat: enhance MCP integration by adding MCPTool type and updating related methods * feat: implement streaming support for tool calls in OpenAIProvider and enhance message processing * feat: Enhance MCPServer and MCPTool interfaces with optional properties and unique IDs * fix(mcp): Refactor SSE transport initialization to use URL object * fix(OpenAIProvider): correct inputSchema properties reference in tool parameters * feat(MCPSettings): enhance server settings UI with new fields and improved layout * feat(MCPSettings): add multilingual support for MCP server settings * fix: remove unnecessary console log statements
This commit is contained in:
+42
-18
@@ -2,6 +2,7 @@ import { MCPServer, MCPTool } from '@types'
|
||||
import log from 'electron-log'
|
||||
import Store from 'electron-store'
|
||||
import { EventEmitter } from 'events'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
const store = new Store()
|
||||
|
||||
@@ -9,7 +10,8 @@ export default class MCPService extends EventEmitter {
|
||||
private activeServers: Map<string, any> = new Map()
|
||||
private clients: { [key: string]: any } = {}
|
||||
private Client: any
|
||||
private Transport: any
|
||||
private stoioTransport: any
|
||||
private sseTransport: any
|
||||
private initialized = false
|
||||
private initPromise: Promise<void> | null = null
|
||||
|
||||
@@ -35,7 +37,8 @@ export default class MCPService extends EventEmitter {
|
||||
try {
|
||||
log.info('[MCP] Starting initialization')
|
||||
this.Client = await this.importClient()
|
||||
this.Transport = await this.importTransport()
|
||||
this.stoioTransport = await this.importStdioClientTransport()
|
||||
this.sseTransport = await this.importSSEClientTransport()
|
||||
|
||||
// Mark as initialized before loading servers to prevent recursive initialization
|
||||
this.initialized = true
|
||||
@@ -64,7 +67,7 @@ export default class MCPService extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private async importTransport() {
|
||||
private async importStdioClientTransport() {
|
||||
try {
|
||||
const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js')
|
||||
return StdioClientTransport
|
||||
@@ -74,6 +77,16 @@ export default class MCPService extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private async importSSEClientTransport() {
|
||||
try {
|
||||
const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js')
|
||||
return SSEClientTransport
|
||||
} catch (err) {
|
||||
log.error('[MCP] Failed to import Transport:', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
public async listAvailableServices(): Promise<MCPServer[]> {
|
||||
await this.ensureInitialized()
|
||||
return this.getServersFromStore()
|
||||
@@ -175,21 +188,36 @@ export default class MCPService extends EventEmitter {
|
||||
public async activate(server: MCPServer): Promise<void> {
|
||||
await this.ensureInitialized()
|
||||
try {
|
||||
const { name, command, args, env } = server
|
||||
const { name, baseUrl, command, args, env } = server
|
||||
|
||||
if (this.clients[name]) {
|
||||
log.info(`[MCP] Server ${name} is already running`)
|
||||
return
|
||||
}
|
||||
|
||||
let cmd: string = command
|
||||
if (command === 'npx') {
|
||||
cmd = process.platform === 'win32' ? `${command}.cmd` : command
|
||||
}
|
||||
let transport: any = null
|
||||
|
||||
const mergedEnv = {
|
||||
...env,
|
||||
PATH: process.env.PATH
|
||||
if (baseUrl) {
|
||||
transport = new this.sseTransport(new URL(baseUrl))
|
||||
} else if (command) {
|
||||
let cmd: string = command
|
||||
if (command === 'npx') {
|
||||
cmd = process.platform === 'win32' ? `${command}.cmd` : command
|
||||
}
|
||||
|
||||
const mergedEnv = {
|
||||
...env,
|
||||
PATH: process.env.PATH
|
||||
}
|
||||
|
||||
transport = new this.stoioTransport({
|
||||
command: cmd,
|
||||
args,
|
||||
stderr: process.platform === 'win32' ? 'pipe' : 'inherit',
|
||||
env: mergedEnv
|
||||
})
|
||||
} else {
|
||||
throw new Error('Either baseUrl or command must be provided')
|
||||
}
|
||||
|
||||
const client = new this.Client(
|
||||
@@ -202,13 +230,6 @@ export default class MCPService extends EventEmitter {
|
||||
}
|
||||
)
|
||||
|
||||
const transport = new this.Transport({
|
||||
command: cmd,
|
||||
args,
|
||||
stderr: process.platform === 'win32' ? 'pipe' : 'inherit',
|
||||
env: mergedEnv
|
||||
})
|
||||
|
||||
await client.connect(transport)
|
||||
this.clients[name] = client
|
||||
this.activeServers.set(name, { client, server })
|
||||
@@ -248,6 +269,8 @@ export default class MCPService extends EventEmitter {
|
||||
}
|
||||
const { tools } = await this.clients[serverName].listTools()
|
||||
return tools.map((tool: any) => {
|
||||
tool.serverName = serverName
|
||||
tool.id = uuidv4()
|
||||
return tool
|
||||
})
|
||||
} else {
|
||||
@@ -259,6 +282,7 @@ export default class MCPService extends EventEmitter {
|
||||
allTools = allTools.concat(
|
||||
tools.map((tool: MCPTool) => {
|
||||
tool.serverName = clientName
|
||||
tool.id = uuidv4()
|
||||
return tool
|
||||
})
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user