码桶
发现社区成员的开源项目
McpConfig.tsx5.4 KB
import { useState, useEffect } from 'react';
import type { Bot } from '../lib/api';
interface McpServer {
id: string;
name: string;
url: string;
apiKey: string;
}
interface McpConfigProps {
bot: { id: number };
settings: Record<string, string>;
onSave: (key: string, value: string) => Promise<boolean>;
saving: boolean;
}
export default function McpConfig({ settings, onSave, saving }: McpConfigProps) {
const [servers, setServers] = useState<McpServer[]>([]);
const [dirty, setDirty] = useState(false);
useEffect(() => {
try {
const raw = settings['servers'];
if (raw) {
const parsed = JSON.parse(raw);
setServers(Array.isArray(parsed) ? parsed : []);
} else {
setServers([]);
}
} catch {
setServers([]);
}
}, [settings]);
const updateServers = (newServers: McpServer[]) => {
setServers(newServers);
setDirty(true);
};
const handleSave = async () => {
const success = await onSave('servers', JSON.stringify(servers));
if (success) setDirty(false);
};
const addServer = () => {
const id = 'svr_' + Date.now();
updateServers([...servers, { id, name: '', url: '', apiKey: '' }]);
};
const removeServer = (id: string) => {
updateServers(servers.filter(s => s.id !== id));
};
const updateServer = (id: string, field: keyof McpServer, value: string) => {
updateServers(servers.map(s => s.id === id ? { ...s, [field]: value } : s));
};
return (
<div className="space-y-6">
<div className="rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800/50 p-4">
<h4 className="font-medium text-sm mb-2">关于 MCP(Model Context Protocol)</h4>
<p className="text-sm text-gray-600 dark:text-gray-400">
MCP 是一种开放协议,允许外部服务器向 AI 暴露工具能力。
添加 MCP 服务器后,智能助手将自动获取其全部工具,无需额外配置。
</p>
<p className="text-sm text-gray-500 dark:text-gray-500 mt-2">
常见用途:接入数据库查询、API 调用、文件搜索、代码执行等第三方能力。
配置完成后可在微信中对机器人说「mcp 工具」查看可用工具列表。
</p>
</div>
{servers.length === 0 && (
<div className="text-center py-8 text-gray-400 dark:text-gray-500 text-sm">
暂无 MCP 服务器,点击下方按钮添加
</div>
)}
<div className="space-y-4">
{servers.map((server) => (
<div key={server.id} className="rounded-lg border border-gray-200 dark:border-gray-700 p-4 space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-gray-500">MCP 服务器</span>
<button
onClick={() => removeServer(server.id)}
className="text-xs text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
>
移除
</button>
</div>
<div>
<label className="text-xs text-gray-500 mb-1 block">名称(自定义标识)</label>
<input
type="text"
value={server.name}
onChange={e => updateServer(server.id, 'name', e.target.value)}
placeholder="如:我的文件服务器"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm"
/>
</div>
<div>
<label className="text-xs text-gray-500 mb-1 block">服务器 URL</label>
<input
type="text"
value={server.url}
onChange={e => updateServer(server.id, 'url', e.target.value)}
placeholder="https://your-mcp-server.com/mcp"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm font-mono"
/>
</div>
<div>
<label className="text-xs text-gray-500 mb-1 block">API Key(可选)</label>
<input
type="password"
value={server.apiKey}
onChange={e => updateServer(server.id, 'apiKey', e.target.value)}
placeholder="Bearer Token 或 API Key"
className="w-full px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm"
/>
</div>
</div>
))}
</div>
<div className="flex gap-3">
<button
onClick={addServer}
className="px-4 py-2 text-sm rounded-lg border border-gray-300 dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
添加服务器
</button>
<button
onClick={handleSave}
disabled={saving || !dirty}
className="px-4 py-2 text-sm rounded-lg bg-zinc-900 text-white hover:bg-zinc-800 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
{saving ? '保存中...' : '保存配置'}
</button>
</div>
</div>
);
}