|
1 | 1 | import { useState, useEffect } from 'react'; |
2 | 2 | import { invoke } from '@/lib/tauri-proxy'; |
3 | 3 | import { Button } from '@/components/ui/button'; |
4 | | -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; |
5 | | -import { Switch } from '@/components/ui/switch'; |
6 | | -import { Trash2, Plus, Edit, Save, X } from 'lucide-react'; |
| 4 | +import { Plus, Save, X } from 'lucide-react'; |
7 | 5 | import { McpServerConfig } from '@/types'; |
8 | 6 | import { toast } from 'sonner'; |
9 | | -import { McpServerForm, McpLinkerButton, DefaultMcpServers } from '@/components/mcp'; |
10 | | - |
11 | | -const getServerProtocol = (config: McpServerConfig): 'stdio' | 'http' | 'sse' => |
12 | | - config.type ?? 'stdio'; |
| 7 | +import { |
| 8 | + McpServerForm, |
| 9 | + McpLinkerButton, |
| 10 | + DefaultMcpServers, |
| 11 | + McpServerCard, |
| 12 | + getServerProtocol, |
| 13 | +} from '@/components/mcp'; |
13 | 14 |
|
14 | 15 | export default function McpPage() { |
15 | 16 | const [servers, setServers] = useState<Record<string, McpServerConfig>>({}); |
@@ -84,38 +85,6 @@ export default function McpPage() { |
84 | 85 | } |
85 | 86 | }; |
86 | 87 |
|
87 | | - const handleDeleteServer = async (name: string) => { |
88 | | - try { |
89 | | - await invoke('delete_mcp_server', { name }); |
90 | | - loadServers(); |
91 | | - } catch (error) { |
92 | | - console.error('Failed to delete MCP server:', error); |
93 | | - toast.error('Failed to delete MCP server: ' + error); |
94 | | - } |
95 | | - }; |
96 | | - |
97 | | - const handleToggleServerEnabled = async (name: string, enabled: boolean) => { |
98 | | - try { |
99 | | - await invoke('set_mcp_server_enabled', { name, enabled }); |
100 | | - setServers((prev) => { |
101 | | - const server = prev[name]; |
102 | | - if (!server) { |
103 | | - return prev; |
104 | | - } |
105 | | - return { |
106 | | - ...prev, |
107 | | - [name]: { |
108 | | - ...server, |
109 | | - enabled, |
110 | | - }, |
111 | | - }; |
112 | | - }); |
113 | | - } catch (error) { |
114 | | - console.error('Failed to update MCP server enabled flag:', error); |
115 | | - toast.error('Failed to update MCP server enabled flag: ' + error); |
116 | | - } |
117 | | - }; |
118 | | - |
119 | 88 | const handleEditServer = (name: string, config: McpServerConfig) => { |
120 | 89 | const protocol = getServerProtocol(config); |
121 | 90 | const httpUrl = protocol === 'stdio' ? '' : ('url' in config ? config.url : ''); |
@@ -197,96 +166,52 @@ export default function McpPage() { |
197 | 166 | <div> |
198 | 167 | <h3 className="text-lg font-semibold mb-4">Configured Servers</h3> |
199 | 168 | <div className="space-y-2 max-h-96 overflow-y-auto"> |
200 | | - {Object.entries(servers).map(([name, config]) => { |
201 | | - const serverType = getServerProtocol(config); |
202 | | - const isEnabled = config.enabled ?? true; |
203 | | - return ( |
204 | | - <Card key={name}> |
205 | | - {editingServer === name ? ( |
206 | | - <div className="px-4"> |
207 | | - <div className="space-y-4"> |
208 | | - <McpServerForm |
209 | | - serverName={editConfig?.name ?? ''} |
210 | | - onServerNameChange={(name) => setEditConfig(prev => prev ? { ...prev, name } : null)} |
211 | | - protocol={editConfig?.protocol ?? 'stdio'} |
212 | | - onProtocolChange={(protocol) => setEditConfig(prev => prev ? { ...prev, protocol } : null)} |
213 | | - commandConfig={editConfig?.command ?? { command: '', args: '', env: '' }} |
214 | | - onCommandConfigChange={(command) => setEditConfig(prev => prev ? { ...prev, command } : null)} |
215 | | - httpConfig={editConfig?.http ?? { url: '' }} |
216 | | - onHttpConfigChange={(http) => setEditConfig(prev => prev ? { ...prev, http } : null)} |
217 | | - isEditMode={true} |
218 | | - /> |
219 | | - |
220 | | - <div className="flex gap-2"> |
221 | | - <Button size="sm" onClick={handleSaveEdit}> |
222 | | - <Save className="h-4 w-4 mr-1" /> |
223 | | - Save |
224 | | - </Button> |
225 | | - <Button size="sm" variant="outline" onClick={handleCancelEdit}> |
226 | | - <X className="h-4 w-4 mr-1" /> |
227 | | - Cancel |
228 | | - </Button> |
229 | | - </div> |
| 169 | + {Object.entries(servers).map(([name, config]) => ( |
| 170 | + <div key={name}> |
| 171 | + {editingServer === name ? ( |
| 172 | + <div className="px-4"> |
| 173 | + <div className="space-y-4"> |
| 174 | + <McpServerForm |
| 175 | + serverName={editConfig?.name ?? ''} |
| 176 | + onServerNameChange={(name) => setEditConfig((prev) => (prev ? { ...prev, name } : null))} |
| 177 | + protocol={editConfig?.protocol ?? 'stdio'} |
| 178 | + onProtocolChange={(protocol) => |
| 179 | + setEditConfig((prev) => (prev ? { ...prev, protocol } : null)) |
| 180 | + } |
| 181 | + commandConfig={editConfig?.command ?? { command: '', args: '', env: '' }} |
| 182 | + onCommandConfigChange={(command) => |
| 183 | + setEditConfig((prev) => (prev ? { ...prev, command } : null)) |
| 184 | + } |
| 185 | + httpConfig={editConfig?.http ?? { url: '' }} |
| 186 | + onHttpConfigChange={(http) => |
| 187 | + setEditConfig((prev) => (prev ? { ...prev, http } : null)) |
| 188 | + } |
| 189 | + isEditMode={true} |
| 190 | + /> |
| 191 | + |
| 192 | + <div className="flex gap-2"> |
| 193 | + <Button size="sm" onClick={handleSaveEdit}> |
| 194 | + <Save className="h-4 w-4 mr-1" /> |
| 195 | + Save |
| 196 | + </Button> |
| 197 | + <Button size="sm" variant="outline" onClick={handleCancelEdit}> |
| 198 | + <X className="h-4 w-4 mr-1" /> |
| 199 | + Cancel |
| 200 | + </Button> |
230 | 201 | </div> |
231 | 202 | </div> |
232 | | - ) : ( |
233 | | - <> |
234 | | - <CardHeader> |
235 | | - <CardTitle className="text-sm flex items-center justify-between"> |
236 | | - {name} |
237 | | - <div className="flex gap-1 items-center"> |
238 | | - <Switch |
239 | | - checked={isEnabled} |
240 | | - onCheckedChange={(checked) => handleToggleServerEnabled(name, checked)} |
241 | | - aria-label={`Toggle ${name} server`} |
242 | | - /> |
243 | | - <Button |
244 | | - size="sm" |
245 | | - variant="ghost" |
246 | | - onClick={() => handleEditServer(name, config)} |
247 | | - > |
248 | | - <Edit className="h-4 w-4" /> |
249 | | - </Button> |
250 | | - <Button |
251 | | - size="sm" |
252 | | - variant="ghost" |
253 | | - onClick={() => handleDeleteServer(name)} |
254 | | - > |
255 | | - <Trash2 className="h-4 w-4" /> |
256 | | - </Button> |
257 | | - </div> |
258 | | - </CardTitle> |
259 | | - </CardHeader> |
260 | | - <CardContent> |
261 | | - <div className="text-xs text-gray-600"> |
262 | | - {serverType === 'stdio' && ( |
263 | | - <div> |
264 | | - <strong>Command:</strong> {'command' in config ? config.command : ''} |
265 | | - {'args' in config && config.args && config.args.length > 0 && ( |
266 | | - <div><strong>Args:</strong> {config.args.join(' ')}</div> |
267 | | - )} |
268 | | - {'env' in config && config.env && ( |
269 | | - <div><strong>Env:</strong> {Object.keys(config.env).join(', ')}</div> |
270 | | - )} |
271 | | - </div> |
272 | | - )} |
273 | | - {serverType === 'http' && 'url' in config && ( |
274 | | - <div> |
275 | | - <strong>url:</strong> {config.url} |
276 | | - </div> |
277 | | - )} |
278 | | - {serverType === 'sse' && 'url' in config && ( |
279 | | - <div> |
280 | | - <strong>url:</strong> {config.url} |
281 | | - </div> |
282 | | - )} |
283 | | - </div> |
284 | | - </CardContent> |
285 | | - </> |
286 | | - )} |
287 | | - </Card> |
288 | | - ); |
289 | | - })} |
| 203 | + </div> |
| 204 | + ) : ( |
| 205 | + <McpServerCard |
| 206 | + name={name} |
| 207 | + config={config} |
| 208 | + loadServers={loadServers} |
| 209 | + setServers={setServers} |
| 210 | + onEdit={handleEditServer} |
| 211 | + /> |
| 212 | + )} |
| 213 | + </div> |
| 214 | + ))} |
290 | 215 | {Object.keys(servers).length === 0 && ( |
291 | 216 | <div className="text-gray-500 text-center py-8"> |
292 | 217 | No MCP servers configured |
|
0 commit comments