feat(ui): mcp_connection_status to shadcn

- antd Button/Spin/Alert/Collapse + @tremor Card/Title/Text +
  @ant-design Check/Exclamation/Reload/Tool \u2192 shadcn Card/Button/
  Accordion + lucide CheckCircle2/AlertTriangle/RefreshCcw/Wrench/
  Loader2.
- Error state renders a destructive-tinted panel with Retry button
  and collapsible stack trace inside an Accordion.
- Categorical blue (info/connecting), emerald (connected), and
  destructive (failed) palettes are already covered by existing
  overrides.

Gates: TS \u2713 | Build \u2713.

Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-04-23 12:53:51 +00:00
parent 2b5034cab3
commit 6905f05ef0
No known key found for this signature in database

View file

@ -1,10 +1,24 @@
import React from "react";
import { Button, Spin, Alert, Collapse } from "antd";
import { CheckCircleOutlined, ExclamationCircleOutlined, ReloadOutlined, ToolOutlined } from "@ant-design/icons";
import { Card, Title, Text } from "@tremor/react";
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {
AlertTriangle,
CheckCircle2,
Loader2,
RefreshCcw,
Wrench,
} from "lucide-react";
interface MCPConnectionStatusProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formValues: Record<string, any>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tools: any[];
isLoadingTools: boolean;
toolsError: string | null;
@ -22,26 +36,26 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
canFetchTools,
fetchTools,
}) => {
// Don't show anything if required fields aren't filled
if (!canFetchTools && !formValues.url && !formValues.spec_path) {
return null;
}
return (
<Card>
<Card className="p-4">
<div className="space-y-4">
<div className="flex items-center gap-2">
<CheckCircleOutlined className="text-blue-600" />
<Title>Connection Status</Title>
<CheckCircle2 className="h-4 w-4 text-blue-600 dark:text-blue-400" />
<h3 className="text-lg font-semibold">Connection Status</h3>
</div>
{!canFetchTools && (formValues.url || formValues.spec_path) && (
<div className="text-center py-6 text-gray-400 border rounded-lg border-dashed">
<ToolOutlined className="text-2xl mb-2" />
<Text>Complete required fields to test connection</Text>
<br />
<Text className="text-sm">Fill in URL, Transport, and Authentication to test MCP server connection</Text>
<div className="text-center py-6 text-muted-foreground border border-dashed border-border rounded-lg">
<Wrench className="h-6 w-6 mx-auto mb-2" />
<div>Complete required fields to test connection</div>
<div className="text-sm">
Fill in URL, Transport, and Authentication to test MCP server
connection
</div>
</div>
)}
@ -49,7 +63,7 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
<div>
<div className="flex items-center justify-between mb-4">
<div>
<Text className="text-gray-700 font-medium">
<div className="text-foreground font-medium">
{isLoadingTools
? "Testing connection to MCP server..."
: tools.length > 0
@ -57,91 +71,87 @@ const MCPConnectionStatus: React.FC<MCPConnectionStatusProps> = ({
: toolsError
? "Connection failed"
: "Ready to test connection"}
</Text>
<br />
<Text className="text-gray-500 text-sm">Server: {formValues.url || formValues.spec_path}</Text>
</div>
<div className="text-muted-foreground text-sm">
Server: {formValues.url || formValues.spec_path}
</div>
</div>
{isLoadingTools && (
<div className="flex items-center text-blue-600">
<Spin size="small" className="mr-2" />
<Text className="text-blue-600">Connecting...</Text>
<div className="flex items-center text-blue-600 dark:text-blue-400">
<Loader2 className="h-4 w-4 animate-spin mr-2" />
<span>Connecting...</span>
</div>
)}
{!isLoadingTools && !toolsError && tools.length > 0 && (
<div className="flex items-center text-green-600">
<CheckCircleOutlined className="mr-1" />
<Text className="text-green-600 font-medium">Connected</Text>
<div className="flex items-center text-emerald-600 dark:text-emerald-400">
<CheckCircle2 className="h-4 w-4 mr-1" />
<span className="font-medium">Connected</span>
</div>
)}
{toolsError && (
<div className="flex items-center text-red-600">
<ExclamationCircleOutlined className="mr-1" />
<Text className="text-red-600 font-medium">Failed</Text>
<div className="flex items-center text-destructive">
<AlertTriangle className="h-4 w-4 mr-1" />
<span className="font-medium">Failed</span>
</div>
)}
</div>
{isLoadingTools && (
<div className="flex items-center justify-center py-6">
<Spin size="large" />
<Text className="ml-3">Testing connection and loading tools...</Text>
<div className="flex items-center justify-center py-6 gap-3">
<Loader2 className="h-6 w-6 animate-spin text-primary" />
<span>Testing connection and loading tools...</span>
</div>
)}
{toolsError && (
<Alert
message="Connection Failed"
description={
<div>
<div>{toolsError}</div>
{toolsErrorStackTrace && (
<Collapse
items={[
{
key: "stack-trace",
label: "Stack Trace",
children: (
<pre style={{
whiteSpace: "pre-wrap",
wordBreak: "break-word",
fontSize: "12px",
fontFamily: "monospace",
margin: 0,
padding: "8px",
backgroundColor: "#f5f5f5",
borderRadius: "4px",
maxHeight: "400px",
overflow: "auto"
}}>
<div className="p-3 bg-destructive/10 border border-destructive/30 rounded-md">
<div className="flex justify-between items-start gap-2">
<div className="flex gap-2 flex-1">
<AlertTriangle className="h-4 w-4 mt-0.5 text-destructive shrink-0" />
<div className="flex-1 text-sm">
<div className="font-semibold text-destructive">
Connection Failed
</div>
<div>{toolsError}</div>
{toolsErrorStackTrace && (
<Accordion type="single" collapsible className="mt-3">
<AccordionItem value="stack-trace">
<AccordionTrigger>Stack Trace</AccordionTrigger>
<AccordionContent>
<pre className="whitespace-pre-wrap break-words text-xs font-mono m-0 p-2 bg-muted rounded max-h-[400px] overflow-auto">
{toolsErrorStackTrace}
</pre>
),
},
]}
style={{ marginTop: "12px" }}
/>
)}
</AccordionContent>
</AccordionItem>
</Accordion>
)}
</div>
</div>
}
type="error"
showIcon
action={
<Button icon={<ReloadOutlined />} onClick={fetchTools} size="small">
<Button
size="sm"
variant="outline"
onClick={fetchTools}
className="shrink-0"
>
<RefreshCcw className="h-3 w-3" />
Retry
</Button>
}
/>
</div>
</div>
)}
{!isLoadingTools && tools.length === 0 && !toolsError && (
<div className="text-center py-6 text-gray-500 border rounded-lg border-dashed">
<CheckCircleOutlined className="text-2xl mb-2 text-green-500" />
<Text className="text-green-600 font-medium">Connection successful!</Text>
<br />
<Text className="text-gray-500">No tools found for this MCP server</Text>
<div className="text-center py-6 text-muted-foreground border border-dashed border-border rounded-lg">
<CheckCircle2 className="h-6 w-6 mx-auto mb-2 text-emerald-500" />
<div className="text-emerald-600 dark:text-emerald-400 font-medium">
Connection successful!
</div>
<div className="text-muted-foreground">
No tools found for this MCP server
</div>
</div>
)}
</div>