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:
LiuVaayne 2025-03-06 11:24:13 +08:00 committed by kangfenmao
parent 05b3810d4a
commit a1ae55b29d
10 changed files with 289 additions and 87 deletions

View File

@ -2,6 +2,7 @@ import { MCPServer, MCPTool } from '@types'
import log from 'electron-log' import log from 'electron-log'
import Store from 'electron-store' import Store from 'electron-store'
import { EventEmitter } from 'events' import { EventEmitter } from 'events'
import { v4 as uuidv4 } from 'uuid'
const store = new Store() const store = new Store()
@ -9,7 +10,8 @@ export default class MCPService extends EventEmitter {
private activeServers: Map<string, any> = new Map() private activeServers: Map<string, any> = new Map()
private clients: { [key: string]: any } = {} private clients: { [key: string]: any } = {}
private Client: any private Client: any
private Transport: any private stoioTransport: any
private sseTransport: any
private initialized = false private initialized = false
private initPromise: Promise<void> | null = null private initPromise: Promise<void> | null = null
@ -35,7 +37,8 @@ export default class MCPService extends EventEmitter {
try { try {
log.info('[MCP] Starting initialization') log.info('[MCP] Starting initialization')
this.Client = await this.importClient() 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 // Mark as initialized before loading servers to prevent recursive initialization
this.initialized = true this.initialized = true
@ -64,7 +67,7 @@ export default class MCPService extends EventEmitter {
} }
} }
private async importTransport() { private async importStdioClientTransport() {
try { try {
const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js') const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js')
return StdioClientTransport 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[]> { public async listAvailableServices(): Promise<MCPServer[]> {
await this.ensureInitialized() await this.ensureInitialized()
return this.getServersFromStore() return this.getServersFromStore()
@ -175,21 +188,36 @@ export default class MCPService extends EventEmitter {
public async activate(server: MCPServer): Promise<void> { public async activate(server: MCPServer): Promise<void> {
await this.ensureInitialized() await this.ensureInitialized()
try { try {
const { name, command, args, env } = server const { name, baseUrl, command, args, env } = server
if (this.clients[name]) { if (this.clients[name]) {
log.info(`[MCP] Server ${name} is already running`) log.info(`[MCP] Server ${name} is already running`)
return return
} }
let cmd: string = command let transport: any = null
if (command === 'npx') {
cmd = process.platform === 'win32' ? `${command}.cmd` : command
}
const mergedEnv = { if (baseUrl) {
...env, transport = new this.sseTransport(new URL(baseUrl))
PATH: process.env.PATH } 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( 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) await client.connect(transport)
this.clients[name] = client this.clients[name] = client
this.activeServers.set(name, { client, server }) this.activeServers.set(name, { client, server })
@ -248,6 +269,8 @@ export default class MCPService extends EventEmitter {
} }
const { tools } = await this.clients[serverName].listTools() const { tools } = await this.clients[serverName].listTools()
return tools.map((tool: any) => { return tools.map((tool: any) => {
tool.serverName = serverName
tool.id = uuidv4()
return tool return tool
}) })
} else { } else {
@ -259,6 +282,7 @@ export default class MCPService extends EventEmitter {
allTools = allTools.concat( allTools = allTools.concat(
tools.map((tool: MCPTool) => { tools.map((tool: MCPTool) => {
tool.serverName = clientName tool.serverName = clientName
tool.id = uuidv4()
return tool return tool
}) })
) )

View File

@ -867,6 +867,8 @@
"addServer": "Add Server", "addServer": "Add Server",
"editServer": "Edit Server", "editServer": "Edit Server",
"name": "Name", "name": "Name",
"type": "Type",
"url": "URL",
"command": "Command", "command": "Command",
"args": "Arguments", "args": "Arguments",
"argsTooltip": "Each argument on a new line", "argsTooltip": "Each argument on a new line",

View File

@ -859,6 +859,34 @@
"blacklist_tooltip": "以下の形式を使用してください(改行区切り)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com", "blacklist_tooltip": "以下の形式を使用してください(改行区切り)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
"search_max_result": "検索結果の数", "search_max_result": "検索結果の数",
"search_result_default": "デフォルト" "search_result_default": "デフォルト"
},
"mcp": {
"title": "MCP サーバー",
"config_description": "モデルコンテキストプロトコルサーバーの設定",
"description": "説明",
"addServer": "サーバーを追加",
"editServer": "サーバーを編集",
"name": "名前",
"type": "タイプ",
"url": "URL",
"command": "コマンド",
"args": "引数",
"argsTooltip": "1行に1つの引数を入力してください",
"env": "環境変数",
"envTooltip": "形式: KEY=value, 1行に1つ",
"active": "有効",
"actions": "操作",
"noServers": "サーバーが設定されていません",
"nameRequired": "サーバー名を入力してください",
"commandRequired": "コマンドを入力してください",
"confirmDelete": "サーバーを削除",
"confirmDeleteMessage": "本当にこのサーバーを削除しますか?",
"addSuccess": "サーバーが正常に追加されました",
"updateSuccess": "サーバーが正常に更新されました",
"deleteSuccess": "サーバーが正常に削除されました",
"duplicateName": "同じ名前のサーバーが既に存在します",
"serverSingular": "サーバー",
"serverPlural": "サーバー"
} }
}, },
"translate": { "translate": {

View File

@ -859,6 +859,34 @@
"blacklist_tooltip": "Пожалуйста, используйте следующий формат (разделенный переносами строк)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com", "blacklist_tooltip": "Пожалуйста, используйте следующий формат (разделенный переносами строк)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
"search_max_result": "Количество результатов поиска", "search_max_result": "Количество результатов поиска",
"search_result_default": "По умолчанию" "search_result_default": "По умолчанию"
},
"mcp": {
"title": "Серверы MCP",
"config_description": "Настройка серверов протокола контекста модели",
"description": "Описание",
"addServer": "Добавить сервер",
"editServer": "Редактировать сервер",
"name": "Имя",
"type": "Тип",
"url": "URL",
"command": "Команда",
"args": "Аргументы",
"argsTooltip": "Каждый аргумент с новой строки",
"env": "Переменные окружения",
"envTooltip": "Формат: KEY=value, по одной на строку",
"active": "Активен",
"actions": "Действия",
"noServers": "Серверы не настроены",
"nameRequired": "Пожалуйста, введите имя сервера",
"commandRequired": "Пожалуйста, введите команду",
"confirmDelete": "Удалить сервер",
"confirmDeleteMessage": "Вы уверены, что хотите удалить этот сервер?",
"addSuccess": "Сервер успешно добавлен",
"updateSuccess": "Сервер успешно обновлен",
"deleteSuccess": "Сервер успешно удален",
"duplicateName": "Сервер с таким именем уже существует",
"serverSingular": "сервер",
"serverPlural": "серверы"
} }
}, },
"translate": { "translate": {

View File

@ -859,6 +859,34 @@
"title": "Tavily" "title": "Tavily"
}, },
"title": "网络搜索" "title": "网络搜索"
},
"mcp": {
"title": "MCP 服务器",
"config_description": "配置模型上下文协议服务器",
"description": "描述",
"addServer": "添加服务器",
"editServer": "编辑服务器",
"name": "名称",
"type": "类型",
"url": "URL",
"command": "命令",
"args": "参数",
"argsTooltip": "每个参数占一行",
"env": "环境变量",
"envTooltip": "格式KEY=value每行一个",
"active": "启用",
"actions": "操作",
"noServers": "未配置服务器",
"nameRequired": "请输入服务器名称",
"commandRequired": "请输入命令",
"confirmDelete": "删除服务器",
"confirmDeleteMessage": "您确定要删除该服务器吗?",
"addSuccess": "服务器添加成功",
"updateSuccess": "服务器更新成功",
"deleteSuccess": "服务器删除成功",
"duplicateName": "已存在同名服务器",
"serverSingular": "服务器",
"serverPlural": "服务器"
} }
}, },
"translate": { "translate": {

View File

@ -859,7 +859,35 @@
"search_max_result": "搜索結果個數", "search_max_result": "搜索結果個數",
"search_result_default": "預設" "search_result_default": "預設"
}, },
"display.assistant.title": "助手設定" "display.assistant.title": "助手設定",
"mcp": {
"title": "MCP 伺服器",
"config_description": "配置模型上下文協議伺服器",
"description": "描述",
"addServer": "新增伺服器",
"editServer": "編輯伺服器",
"name": "名稱",
"type": "類型",
"url": "URL",
"command": "指令",
"args": "參數",
"argsTooltip": "每個參數佔一行",
"env": "環境變數",
"envTooltip": "格式KEY=value每行一個",
"active": "啟用",
"actions": "操作",
"noServers": "未配置伺服器",
"nameRequired": "請輸入伺服器名稱",
"commandRequired": "請輸入指令",
"confirmDelete": "刪除伺服器",
"confirmDeleteMessage": "您確定要刪除該伺服器嗎?",
"addSuccess": "伺服器新增成功",
"updateSuccess": "伺服器更新成功",
"deleteSuccess": "伺服器刪除成功",
"duplicateName": "已存在同名伺服器",
"serverSingular": "伺服器",
"serverPlural": "伺服器"
}
}, },
"translate": { "translate": {
"any.language": "任意語言", "any.language": "任意語言",

View File

@ -3,18 +3,20 @@ import { useTheme } from '@renderer/context/ThemeProvider'
import { useAppDispatch, useAppSelector } from '@renderer/store' import { useAppDispatch, useAppSelector } from '@renderer/store'
import { addMCPServer, deleteMCPServer, setMCPServerActive, updateMCPServer } from '@renderer/store/mcp' import { addMCPServer, deleteMCPServer, setMCPServerActive, updateMCPServer } from '@renderer/store/mcp'
import { MCPServer } from '@renderer/types' import { MCPServer } from '@renderer/types'
import { Button, Card, Form, Input, message, Modal, Space, Switch, Table, Tooltip, Typography } from 'antd' import { Button, Card, Form, Input, message, Modal, Radio, Space, Switch, Table, Tag, Tooltip, Typography } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import { FC, useState } from 'react' import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '.' import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '.'
interface MCPFormValues { interface MCPFormValues {
name: string name: string
command: string
description?: string description?: string
args: string serverType: 'sse' | 'stdio'
baseUrl?: string
command?: string
args?: string
env?: string env?: string
isActive: boolean isActive: boolean
} }
@ -30,20 +32,37 @@ const MCPSettings: FC = () => {
const [editingServer, setEditingServer] = useState<MCPServer | null>(null) const [editingServer, setEditingServer] = useState<MCPServer | null>(null)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [form] = Form.useForm<MCPFormValues>() const [form] = Form.useForm<MCPFormValues>()
const [serverType, setServerType] = useState<'sse' | 'stdio'>('stdio')
// Watch the serverType field to update the form layout dynamically
useEffect(() => {
const type = form.getFieldValue('serverType')
if (type) {
setServerType(type)
}
}, [form])
const showAddModal = () => { const showAddModal = () => {
form.resetFields() form.resetFields()
form.setFieldsValue({ serverType: 'stdio', isActive: true })
setServerType('stdio')
setEditingServer(null) setEditingServer(null)
setIsModalVisible(true) setIsModalVisible(true)
} }
const showEditModal = (server: MCPServer) => { const showEditModal = (server: MCPServer) => {
setEditingServer(server) setEditingServer(server)
// Determine server type based on server properties
const serverType = server.baseUrl ? 'sse' : 'stdio'
setServerType(serverType)
form.setFieldsValue({ form.setFieldsValue({
name: server.name, name: server.name,
command: server.command,
description: server.description, description: server.description,
args: server.args.join('\n'), serverType: serverType,
baseUrl: server.baseUrl || '',
command: server.command || '',
args: server.args ? server.args.join('\n') : '',
env: server.env env: server.env
? Object.entries(server.env) ? Object.entries(server.env)
.map(([key, value]) => `${key}=${value}`) .map(([key, value]) => `${key}=${value}`)
@ -64,29 +83,32 @@ const MCPSettings: FC = () => {
form form
.validateFields() .validateFields()
.then((values) => { .then((values) => {
const args = values.args ? values.args.split('\n').filter((arg) => arg.trim() !== '') : []
const env: Record<string, string> = {}
if (values.env) {
values.env.split('\n').forEach((line) => {
if (line.trim()) {
const [key, value] = line.split('=')
if (key && value) {
env[key.trim()] = value.trim()
}
}
})
}
const mcpServer: MCPServer = { const mcpServer: MCPServer = {
name: values.name, name: values.name,
command: values.command,
description: values.description, description: values.description,
args,
env: Object.keys(env).length > 0 ? env : undefined,
isActive: values.isActive isActive: values.isActive
} }
if (values.serverType === 'sse') {
mcpServer.baseUrl = values.baseUrl
} else {
mcpServer.command = values.command
mcpServer.args = values.args ? values.args.split('\n').filter((arg) => arg.trim() !== '') : []
const env: Record<string, string> = {}
if (values.env) {
values.env.split('\n').forEach((line) => {
if (line.trim()) {
const [key, value] = line.split('=')
if (key && value) {
env[key.trim()] = value.trim()
}
}
})
}
mcpServer.env = Object.keys(env).length > 0 ? env : undefined
}
if (editingServer) { if (editingServer) {
window.api.mcp window.api.mcp
.updateServer(mcpServer) .updateServer(mcpServer)
@ -167,26 +189,49 @@ const MCPSettings: FC = () => {
title: t('settings.mcp.name'), title: t('settings.mcp.name'),
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
width: '20%', width: '10%',
render: (text: string, record: MCPServer) => <Text strong={record.isActive}>{text}</Text> render: (text: string, record: MCPServer) => <Text strong={record.isActive}>{text}</Text>
}, },
{
title: t('settings.mcp.type'),
key: 'type',
width: '5%',
render: (_: any, record: MCPServer) => <Tag color="cyan">{record.baseUrl ? 'SSE' : 'STDIO'}</Tag>
},
{ {
title: t('settings.mcp.description'), title: t('settings.mcp.description'),
dataIndex: 'description', dataIndex: 'description',
key: 'description', key: 'description',
width: '40%', width: '50%',
render: (text: string) => render: (text: string) => {
text || ( if (!text) {
<Text type="secondary" italic> return (
{t('common.description')} <Text type="secondary" italic>
</Text> {t('common.description')}
</Text>
)
}
return (
<Paragraph
ellipsis={{
rows: 1,
expandable: 'collapsible',
symbol: 'more',
onExpand: () => {}, // Empty callback required for proper functionality
tooltip: true
}}
style={{ marginBottom: 0 }}>
{text}
</Paragraph>
) )
}
}, },
{ {
title: t('settings.mcp.active'), title: t('settings.mcp.active'),
dataIndex: 'isActive', dataIndex: 'isActive',
key: 'isActive', key: 'isActive',
width: '15%', width: '5%',
render: (isActive: boolean, record: MCPServer) => ( render: (isActive: boolean, record: MCPServer) => (
<Switch checked={isActive} onChange={(checked) => handleToggleActive(record.name, checked)} /> <Switch checked={isActive} onChange={(checked) => handleToggleActive(record.name, checked)} />
) )
@ -194,7 +239,7 @@ const MCPSettings: FC = () => {
{ {
title: t('settings.mcp.actions'), title: t('settings.mcp.actions'),
key: 'actions', key: 'actions',
width: '25%', width: '10%',
render: (_: any, record: MCPServer) => ( render: (_: any, record: MCPServer) => (
<Space> <Space>
<Tooltip title={t('common.edit')}> <Tooltip title={t('common.edit')}>
@ -272,19 +317,47 @@ const MCPSettings: FC = () => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="command" name="serverType"
label={t('settings.mcp.command')} label={t('settings.mcp.type')}
rules={[{ required: true, message: t('settings.mcp.commandRequired') }]}> rules={[{ required: true }]}
<Input placeholder="python script.py" /> initialValue="stdio">
<Radio.Group
onChange={(e) => setServerType(e.target.value)}
options={[
{ label: 'SSE (Server-Sent Events)', value: 'sse' },
{ label: 'STDIO (Standard Input/Output)', value: 'stdio' }
]}
/>
</Form.Item> </Form.Item>
<Form.Item name="args" label={t('settings.mcp.args')} tooltip={t('settings.mcp.argsTooltip')}> {serverType === 'sse' && (
<TextArea rows={3} placeholder="{--param1}\n{--param2 value}" style={{ fontFamily: 'monospace' }} /> <Form.Item
</Form.Item> name="baseUrl"
label={t('settings.mcp.url')}
rules={[{ required: serverType === 'sse', message: t('settings.mcp.baseUrlRequired') }]}
tooltip={t('settings.mcp.baseUrlTooltip')}>
<Input placeholder="http://localhost:3000/sse" />
</Form.Item>
)}
<Form.Item name="env" label={t('settings.mcp.env')} tooltip={t('settings.mcp.envTooltip')}> {serverType === 'stdio' && (
<TextArea rows={3} placeholder="KEY1=value1\nKEY2=value2" style={{ fontFamily: 'monospace' }} /> <>
</Form.Item> <Form.Item
name="command"
label={t('settings.mcp.command')}
rules={[{ required: serverType === 'stdio', message: t('settings.mcp.commandRequired') }]}>
<Input placeholder="uvx or npx" />
</Form.Item>
<Form.Item name="args" label={t('settings.mcp.args')} tooltip={t('settings.mcp.argsTooltip')}>
<TextArea rows={3} placeholder="arg1\narg2" style={{ fontFamily: 'monospace' }} />
</Form.Item>
<Form.Item name="env" label={t('settings.mcp.env')} tooltip={t('settings.mcp.envTooltip')}>
<TextArea rows={3} placeholder="KEY1=value1\nKEY2=value2" style={{ fontFamily: 'monospace' }} />
</Form.Item>
</>
)}
<Form.Item name="isActive" label={t('settings.mcp.active')} valuePropName="checked" initialValue={true}> <Form.Item name="isActive" label={t('settings.mcp.active')} valuePropName="checked" initialValue={true}>
<Switch /> <Switch />

View File

@ -230,30 +230,27 @@ export default class OpenAIProvider extends BaseProvider {
return mcpTools.map((tool) => ({ return mcpTools.map((tool) => ({
type: 'function', type: 'function',
function: { function: {
name: `mcp.${tool.serverName}.${tool.name}`, name: tool.id,
description: tool.description, description: tool.description,
parameters: { parameters: {
type: 'object', type: 'object',
properties: tool.inputSchema properties: tool.inputSchema.properties
} }
} }
})) }))
} }
private openAIToolsToMcpTool(tool: ChatCompletionMessageToolCall): MCPTool | undefined { private openAIToolsToMcpTool(
const parts = tool.function.name.split('.') mcpTools: MCPTool[] | undefined,
if (parts[0] !== 'mcp') { llmTool: ChatCompletionMessageToolCall
console.log('Invalid tool name', tool.function.name) ): MCPTool | undefined {
if (!mcpTools) return undefined
const tool = mcpTools.find((tool) => tool.id === llmTool.function.name)
if (!tool) {
return undefined return undefined
} }
const serverName = parts[1] tool.inputSchema = JSON.parse(llmTool.function.arguments)
const name = parts[2] return tool
return {
serverName: serverName,
name: name,
inputSchema: JSON.parse(tool.function.arguments)
} as MCPTool
} }
async completions({ messages, assistant, onChunk, onFilterMessages, mcpTools }: CompletionsParams): Promise<void> { async completions({ messages, assistant, onChunk, onFilterMessages, mcpTools }: CompletionsParams): Promise<void> {
@ -328,7 +325,7 @@ export default class OpenAIProvider extends BaseProvider {
const { abortController, cleanup } = this.createAbortController(lastUserMessage?.id) const { abortController, cleanup } = this.createAbortController(lastUserMessage?.id)
const { signal } = abortController const { signal } = abortController
const tools = mcpTools ? this.mcpToolsToOpenAITools(mcpTools) : undefined const tools = mcpTools && mcpTools.length > 0 ? this.mcpToolsToOpenAITools(mcpTools) : undefined
const reqMessages: ChatCompletionMessageParam[] = [systemMessage, ...userMessages].filter( const reqMessages: ChatCompletionMessageParam[] = [systemMessage, ...userMessages].filter(
Boolean Boolean
@ -401,11 +398,9 @@ export default class OpenAIProvider extends BaseProvider {
} as ChatCompletionAssistantMessageParam) } as ChatCompletionAssistantMessageParam)
for (const toolCall of toolCalls) { for (const toolCall of toolCalls) {
const mcpTool = this.openAIToolsToMcpTool(toolCall) const mcpTool = this.openAIToolsToMcpTool(mcpTools, toolCall)
console.log('mcpTool', JSON.stringify(mcpTool, null, 2))
if (!mcpTool) { if (!mcpTool) {
console.log('Invalid tool', toolCall)
continue continue
} }
@ -415,9 +410,6 @@ export default class OpenAIProvider extends BaseProvider {
args: mcpTool.inputSchema args: mcpTool.inputSchema
}) })
console.log(`Tool ${mcpTool.serverName} - ${mcpTool.name} Call Response:`)
console.log(toolCallResponse)
reqMessages.push({ reqMessages.push({
role: 'tool', role: 'tool',
content: JSON.stringify(toolCallResponse, null, 2), content: JSON.stringify(toolCallResponse, null, 2),

View File

@ -78,9 +78,6 @@ export async function fetchChatCompletion({
} }
const allMCPTools = await window.api.mcp.listTools() const allMCPTools = await window.api.mcp.listTools()
if (allMCPTools.length > 0) {
console.log('Available MCP tools:', allMCPTools)
}
await AI.completions({ await AI.completions({
messages: filterUsefulMessages(messages), messages: filterUsefulMessages(messages),
assistant, assistant,

View File

@ -315,9 +315,10 @@ export interface MCPServerParameter {
export interface MCPServer { export interface MCPServer {
name: string name: string
command: string
description?: string description?: string
args: string[] baseUrl?: string
command?: string
args?: string[]
env?: Record<string, string> env?: Record<string, string>
isActive: boolean isActive: boolean
} }
@ -331,6 +332,7 @@ export interface MCPToolInputSchema {
} }
export interface MCPTool { export interface MCPTool {
id: string
serverName: string serverName: string
name: string name: string
description?: string description?: string