feat: add mcp Execute Tool

This commit is contained in:
xu-ya 2025-11-18 14:30:44 +08:00
parent a9c9224835
commit f30f06f40f
3 changed files with 123 additions and 18 deletions

View File

@ -3985,7 +3985,36 @@
},
"loadError": "Get tools Error",
"noToolsAvailable": "No tools available",
"run": "Run"
"run": "Run",
"execute": {
"label": "Execute",
"button": "Execute",
"tooltip": "Execute tool with custom parameters",
"title": "Execute Tool: {{name}}",
"params": "Parameters (JSON)",
"paramsPlaceholder": "Enter JSON parameters...",
"execute": "Execute",
"copied": "Copied to clipboard",
"copyFailed": "Failed to copy",
"copy": "Copy",
"result": {
"success": "Result",
"error": "Error Result",
"text": "Text"
},
"view": {
"json": "JSON",
"formatted": "Formatted"
},
"table": {
"name": "Name",
"value": "Value"
},
"error": {
"noToolOrServer": "Tool or server not found",
"invalidJson": "Invalid JSON format: {{error}}"
}
}
},
"type": "Type",
"types": {

View File

@ -3985,7 +3985,36 @@
},
"loadError": "获取工具失败",
"noToolsAvailable": "无可用工具",
"run": "运行"
"run": "运行",
"execute": {
"label": "执行",
"button": "执行",
"tooltip": "使用自定义参数执行工具",
"title": "执行工具: {{name}}",
"params": "参数 (JSON)",
"paramsPlaceholder": "输入 JSON 参数...",
"execute": "执行",
"copied": "已复制到剪贴板",
"copyFailed": "复制失败",
"copy": "复制",
"result": {
"success": "结果",
"error": "错误结果",
"text": "文本"
},
"view": {
"json": "JSON",
"formatted": "美化"
},
"table": {
"name": "名称",
"value": "值"
},
"error": {
"noToolOrServer": "未找到工具或服务器",
"invalidJson": "无效的 JSON 格式: {{error}}"
}
}
},
"type": "类型",
"types": {

View File

@ -1,9 +1,11 @@
import type { MCPServer, MCPTool } from '@renderer/types'
import { isToolAutoApproved } from '@renderer/utils/mcp-tools'
import { Badge, Descriptions, Empty, Flex, Switch, Table, Tag, Tooltip, Typography } from 'antd'
import { Badge, Button, Descriptions, Empty, Flex, Switch, Table, Tag, Tooltip, Typography } from 'antd'
import type { ColumnsType } from 'antd/es/table'
import { Hammer, Info, Zap } from 'lucide-react'
import { Hammer, Info, Play, Zap } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import ExecuteToolModal from './ExecuteToolModal'
interface MCPToolsSectionProps {
tools: MCPTool[]
@ -14,6 +16,8 @@ interface MCPToolsSectionProps {
const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: MCPToolsSectionProps) => {
const { t } = useTranslation()
const [executeModalOpen, setExecuteModalOpen] = useState(false)
const [selectedTool, setSelectedTool] = useState<MCPTool | null>(null)
// Check if a tool is enabled (not in the disabledTools array)
const isToolEnabled = (tool: MCPTool) => {
@ -30,6 +34,12 @@ const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: M
onToggleAutoApprove(tool, checked)
}
// Handle execute tool
const handleExecuteTool = (tool: MCPTool) => {
setSelectedTool(tool)
setExecuteModalOpen(true)
}
// Render tool properties from the input schema
const renderToolProperties = (tool: MCPTool) => {
if (!tool.inputSchema?.properties) return null
@ -102,6 +112,7 @@ const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: M
}
const columns: ColumnsType<MCPTool> = [
// 工具列表列定义:可用工具、启用工具、自动批准、执行工具
{
title: <Typography.Text strong>{t('settings.mcp.tools.availableTools')}</Typography.Text>,
dataIndex: 'name',
@ -141,7 +152,7 @@ const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: M
</Flex>
),
key: 'enable',
width: 150, // Fixed width might be good for alignment
width: 130, // Fixed width might be good for alignment
align: 'center',
render: (_, tool) => (
<Switch checked={isToolEnabled(tool)} onChange={(checked) => handleToggle(tool, checked)} size="small" />
@ -155,7 +166,7 @@ const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: M
</Flex>
),
key: 'autoApprove',
width: 150, // Fixed width
width: 130, // Fixed width
align: 'center',
render: (_, tool) => (
<Tooltip
@ -175,21 +186,57 @@ const MCPToolsSection = ({ tools, server, onToggleTool, onToggleAutoApprove }: M
/>
</Tooltip>
)
},
{
title: (
<Flex align="center" justify="center" gap={4}>
<Play size={14} color="green" />
<Typography.Text strong>{t('settings.mcp.tools.execute.label', 'Execute')}</Typography.Text>
</Flex>
),
key: 'execute',
width: 130,
align: 'center',
render: (_, tool) => (
<Tooltip title={t('settings.mcp.tools.execute.tooltip', 'Execute tool with custom parameters')}>
<Button
type="primary"
size="small"
icon={<Play size={12} />}
onClick={() => handleExecuteTool(tool)}
disabled={!isToolEnabled(tool)}>
{t('settings.mcp.tools.execute.button', 'Execute')}
</Button>
</Tooltip>
)
}
]
return tools.length > 0 ? (
<Table
rowKey="id"
columns={columns}
dataSource={tools}
pagination={false}
expandable={{
expandedRowRender: (tool) => renderToolProperties(tool)
}}
/>
) : (
<Empty description={t('settings.mcp.tools.noToolsAvailable')} image={Empty.PRESENTED_IMAGE_SIMPLE} />
return (
<>
{tools.length > 0 ? (
<Table
rowKey="id"
columns={columns}
dataSource={tools}
pagination={false}
expandable={{
expandedRowRender: (tool) => renderToolProperties(tool)
}}
/>
) : (
<Empty description={t('settings.mcp.tools.noToolsAvailable')} image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
<ExecuteToolModal
open={executeModalOpen}
tool={selectedTool}
server={server}
onClose={() => {
setExecuteModalOpen(false)
setSelectedTool(null)
}}
/>
</>
)
}