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:
parent
05b3810d4a
commit
a1ae55b29d
@ -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,13 +188,18 @@ 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 transport: any = null
|
||||
|
||||
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
|
||||
@ -192,6 +210,16 @@ export default class MCPService extends EventEmitter {
|
||||
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(
|
||||
{
|
||||
name: name,
|
||||
@ -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
|
||||
})
|
||||
)
|
||||
|
||||
@ -867,6 +867,8 @@
|
||||
"addServer": "Add Server",
|
||||
"editServer": "Edit Server",
|
||||
"name": "Name",
|
||||
"type": "Type",
|
||||
"url": "URL",
|
||||
"command": "Command",
|
||||
"args": "Arguments",
|
||||
"argsTooltip": "Each argument on a new line",
|
||||
|
||||
@ -859,6 +859,34 @@
|
||||
"blacklist_tooltip": "以下の形式を使用してください(改行区切り)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"search_max_result": "検索結果の数",
|
||||
"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": {
|
||||
|
||||
@ -859,6 +859,34 @@
|
||||
"blacklist_tooltip": "Пожалуйста, используйте следующий формат (разделенный переносами строк)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"search_max_result": "Количество результатов поиска",
|
||||
"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": {
|
||||
|
||||
@ -859,6 +859,34 @@
|
||||
"title": "Tavily"
|
||||
},
|
||||
"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": {
|
||||
|
||||
@ -859,7 +859,35 @@
|
||||
"search_max_result": "搜索結果個數",
|
||||
"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": {
|
||||
"any.language": "任意語言",
|
||||
|
||||
@ -3,18 +3,20 @@ import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { addMCPServer, deleteMCPServer, setMCPServerActive, updateMCPServer } from '@renderer/store/mcp'
|
||||
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 { FC, useState } from 'react'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '.'
|
||||
|
||||
interface MCPFormValues {
|
||||
name: string
|
||||
command: string
|
||||
description?: string
|
||||
args: string
|
||||
serverType: 'sse' | 'stdio'
|
||||
baseUrl?: string
|
||||
command?: string
|
||||
args?: string
|
||||
env?: string
|
||||
isActive: boolean
|
||||
}
|
||||
@ -30,20 +32,37 @@ const MCPSettings: FC = () => {
|
||||
const [editingServer, setEditingServer] = useState<MCPServer | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
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 = () => {
|
||||
form.resetFields()
|
||||
form.setFieldsValue({ serverType: 'stdio', isActive: true })
|
||||
setServerType('stdio')
|
||||
setEditingServer(null)
|
||||
setIsModalVisible(true)
|
||||
}
|
||||
|
||||
const showEditModal = (server: MCPServer) => {
|
||||
setEditingServer(server)
|
||||
// Determine server type based on server properties
|
||||
const serverType = server.baseUrl ? 'sse' : 'stdio'
|
||||
setServerType(serverType)
|
||||
|
||||
form.setFieldsValue({
|
||||
name: server.name,
|
||||
command: server.command,
|
||||
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
|
||||
? Object.entries(server.env)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
@ -64,7 +83,17 @@ const MCPSettings: FC = () => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
const args = values.args ? values.args.split('\n').filter((arg) => arg.trim() !== '') : []
|
||||
const mcpServer: MCPServer = {
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
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) {
|
||||
@ -77,14 +106,7 @@ const MCPSettings: FC = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const mcpServer: MCPServer = {
|
||||
name: values.name,
|
||||
command: values.command,
|
||||
description: values.description,
|
||||
args,
|
||||
env: Object.keys(env).length > 0 ? env : undefined,
|
||||
isActive: values.isActive
|
||||
mcpServer.env = Object.keys(env).length > 0 ? env : undefined
|
||||
}
|
||||
|
||||
if (editingServer) {
|
||||
@ -167,26 +189,49 @@ const MCPSettings: FC = () => {
|
||||
title: t('settings.mcp.name'),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '20%',
|
||||
width: '10%',
|
||||
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'),
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
width: '40%',
|
||||
render: (text: string) =>
|
||||
text || (
|
||||
width: '50%',
|
||||
render: (text: string) => {
|
||||
if (!text) {
|
||||
return (
|
||||
<Text type="secondary" italic>
|
||||
{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'),
|
||||
dataIndex: 'isActive',
|
||||
key: 'isActive',
|
||||
width: '15%',
|
||||
width: '5%',
|
||||
render: (isActive: boolean, record: MCPServer) => (
|
||||
<Switch checked={isActive} onChange={(checked) => handleToggleActive(record.name, checked)} />
|
||||
)
|
||||
@ -194,7 +239,7 @@ const MCPSettings: FC = () => {
|
||||
{
|
||||
title: t('settings.mcp.actions'),
|
||||
key: 'actions',
|
||||
width: '25%',
|
||||
width: '10%',
|
||||
render: (_: any, record: MCPServer) => (
|
||||
<Space>
|
||||
<Tooltip title={t('common.edit')}>
|
||||
@ -271,20 +316,48 @@ const MCPSettings: FC = () => {
|
||||
<TextArea rows={2} placeholder={t('common.description')} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="serverType"
|
||||
label={t('settings.mcp.type')}
|
||||
rules={[{ required: true }]}
|
||||
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>
|
||||
|
||||
{serverType === 'sse' && (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{serverType === 'stdio' && (
|
||||
<>
|
||||
<Form.Item
|
||||
name="command"
|
||||
label={t('settings.mcp.command')}
|
||||
rules={[{ required: true, message: t('settings.mcp.commandRequired') }]}>
|
||||
<Input placeholder="python script.py" />
|
||||
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="{--param1}\n{--param2 value}" style={{ fontFamily: 'monospace' }} />
|
||||
<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}>
|
||||
<Switch />
|
||||
|
||||
@ -230,30 +230,27 @@ export default class OpenAIProvider extends BaseProvider {
|
||||
return mcpTools.map((tool) => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: `mcp.${tool.serverName}.${tool.name}`,
|
||||
name: tool.id,
|
||||
description: tool.description,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: tool.inputSchema
|
||||
properties: tool.inputSchema.properties
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private openAIToolsToMcpTool(tool: ChatCompletionMessageToolCall): MCPTool | undefined {
|
||||
const parts = tool.function.name.split('.')
|
||||
if (parts[0] !== 'mcp') {
|
||||
console.log('Invalid tool name', tool.function.name)
|
||||
private openAIToolsToMcpTool(
|
||||
mcpTools: MCPTool[] | undefined,
|
||||
llmTool: ChatCompletionMessageToolCall
|
||||
): MCPTool | undefined {
|
||||
if (!mcpTools) return undefined
|
||||
const tool = mcpTools.find((tool) => tool.id === llmTool.function.name)
|
||||
if (!tool) {
|
||||
return undefined
|
||||
}
|
||||
const serverName = parts[1]
|
||||
const name = parts[2]
|
||||
|
||||
return {
|
||||
serverName: serverName,
|
||||
name: name,
|
||||
inputSchema: JSON.parse(tool.function.arguments)
|
||||
} as MCPTool
|
||||
tool.inputSchema = JSON.parse(llmTool.function.arguments)
|
||||
return tool
|
||||
}
|
||||
|
||||
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 { 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(
|
||||
Boolean
|
||||
@ -401,11 +398,9 @@ export default class OpenAIProvider extends BaseProvider {
|
||||
} as ChatCompletionAssistantMessageParam)
|
||||
|
||||
for (const toolCall of toolCalls) {
|
||||
const mcpTool = this.openAIToolsToMcpTool(toolCall)
|
||||
console.log('mcpTool', JSON.stringify(mcpTool, null, 2))
|
||||
const mcpTool = this.openAIToolsToMcpTool(mcpTools, toolCall)
|
||||
|
||||
if (!mcpTool) {
|
||||
console.log('Invalid tool', toolCall)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -415,9 +410,6 @@ export default class OpenAIProvider extends BaseProvider {
|
||||
args: mcpTool.inputSchema
|
||||
})
|
||||
|
||||
console.log(`Tool ${mcpTool.serverName} - ${mcpTool.name} Call Response:`)
|
||||
console.log(toolCallResponse)
|
||||
|
||||
reqMessages.push({
|
||||
role: 'tool',
|
||||
content: JSON.stringify(toolCallResponse, null, 2),
|
||||
|
||||
@ -78,9 +78,6 @@ export async function fetchChatCompletion({
|
||||
}
|
||||
|
||||
const allMCPTools = await window.api.mcp.listTools()
|
||||
if (allMCPTools.length > 0) {
|
||||
console.log('Available MCP tools:', allMCPTools)
|
||||
}
|
||||
await AI.completions({
|
||||
messages: filterUsefulMessages(messages),
|
||||
assistant,
|
||||
|
||||
@ -315,9 +315,10 @@ export interface MCPServerParameter {
|
||||
|
||||
export interface MCPServer {
|
||||
name: string
|
||||
command: string
|
||||
description?: string
|
||||
args: string[]
|
||||
baseUrl?: string
|
||||
command?: string
|
||||
args?: string[]
|
||||
env?: Record<string, string>
|
||||
isActive: boolean
|
||||
}
|
||||
@ -331,6 +332,7 @@ export interface MCPToolInputSchema {
|
||||
}
|
||||
|
||||
export interface MCPTool {
|
||||
id: string
|
||||
serverName: string
|
||||
name: string
|
||||
description?: string
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user