improve: add abort btn to abort the mcp add request. (#284)

This commit is contained in:
Abeautifulsnow
2025-06-26 08:51:46 +08:00
committed by GitHub
parent aa06cd6fb6
commit 9c2d4724e3
2 changed files with 25 additions and 4 deletions

View File

@@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { useCallback, useState } from "react"; import { useCallback, useRef, useState } from "react";
import { Button } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
import { import {
@@ -34,6 +34,8 @@ export function AddMCPServerDialog({
const [validationError, setValidationError] = useState<string | null>(""); const [validationError, setValidationError] = useState<string | null>("");
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [processing, setProcessing] = useState(false); const [processing, setProcessing] = useState(false);
const abortControllerRef = useRef<AbortController | null>(null);
const handleChange = useCallback((value: string) => { const handleChange = useCallback((value: string) => {
setInput(value); setInput(value);
if (!value.trim()) { if (!value.trim()) {
@@ -74,7 +76,9 @@ export function AddMCPServerDialog({
return; return;
} }
}, []); }, []);
const handleAdd = useCallback(async () => { const handleAdd = useCallback(async () => {
abortControllerRef.current = new AbortController();
const config = MCPConfigSchema.parse(JSON.parse(input)); const config = MCPConfigSchema.parse(JSON.parse(input));
setInput(JSON.stringify(config, null, 2)); setInput(JSON.stringify(config, null, 2));
const addingServers: SimpleMCPServerMetadata[] = []; const addingServers: SimpleMCPServerMetadata[] = [];
@@ -105,7 +109,7 @@ export function AddMCPServerDialog({
setError(null); setError(null);
for (const server of addingServers) { for (const server of addingServers) {
processingServer = server.name; processingServer = server.name;
const metadata = await queryMCPServerMetadata(server); const metadata = await queryMCPServerMetadata(server, abortControllerRef.current.signal);
results.push({ ...metadata, name: server.name, enabled: true }); results.push({ ...metadata, name: server.name, enabled: true });
} }
if (results.length > 0) { if (results.length > 0) {
@@ -115,12 +119,23 @@ export function AddMCPServerDialog({
setOpen(false); setOpen(false);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
setError(`Failed to add server: ${processingServer}`); if (e instanceof Error && e.name === 'AbortError') {
setError(`Request was cancelled`);
} else {
setError(`Failed to add server: ${processingServer}`);
}
} finally { } finally {
setProcessing(false); setProcessing(false);
abortControllerRef.current = null;
} }
}, [input, onAdd]); }, [input, onAdd]);
const handleAbort = () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
};
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
@@ -165,6 +180,11 @@ export function AddMCPServerDialog({
{processing && <Loader2 className="animate-spin" />} {processing && <Loader2 className="animate-spin" />}
Add Add
</Button> </Button>
{
processing && (
<Button variant="destructive" onClick={handleAbort}>Abort</Button>
)
}
</div> </div>
</div> </div>
</DialogFooter> </DialogFooter>

View File

@@ -5,13 +5,14 @@ import type { SimpleMCPServerMetadata } from "../mcp";
import { resolveServiceURL } from "./resolve-service-url"; import { resolveServiceURL } from "./resolve-service-url";
export async function queryMCPServerMetadata(config: SimpleMCPServerMetadata) { export async function queryMCPServerMetadata(config: SimpleMCPServerMetadata, signal?: AbortSignal) {
const response = await fetch(resolveServiceURL("mcp/server/metadata"), { const response = await fetch(resolveServiceURL("mcp/server/metadata"), {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(config), body: JSON.stringify(config),
signal,
}); });
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);