import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" import { Bot } from "lucide-react" import { useState } from "react" import { OauthService, type RegisteredClient } from "@/client" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import useCustomToast from "@/hooks/useCustomToast" import { handleError } from "@/utils" const clientsKey = ["oauth", "clients"] function when(value: string): string { return new Date(value).toLocaleString() } /** The agents that registered themselves for the MCP endpoint, and the way to * withdraw one — the alternative being to rotate the signing key, which cuts * off every agent at once. */ export function OAuthClients() { const { data } = useQuery({ queryKey: clientsKey, queryFn: () => OauthService.readClients(), }) const queryClient = useQueryClient() const { showSuccessToast, showErrorToast } = useCustomToast() const [pending, setPending] = useState(null) const revoke = useMutation({ mutationFn: (clientId: string) => OauthService.revokeClient({ clientId }), onSuccess: (message) => { showSuccessToast(message.message) setPending(null) }, onError: handleError.bind(showErrorToast), onSettled: () => { queryClient.invalidateQueries({ queryKey: clientsKey }) }, }) const clients = data?.data ?? [] return (

Agents

Clients that registered themselves to reach the MCP endpoint. Each one still needed a person to approve it before it could do anything.

{clients.length === 0 ? (

Nothing has registered yet. An agent adds itself here the first time it asks for access.

) : (
{clients.map((client) => (
{client.client_name} {client.redirect_uris.join(" ")} Registered {when(client.created_at)} {client.last_authorized_at ? ` · approved ${when(client.last_authorized_at)}` : ""}
0 ? "default" : "secondary"} > {client.active_tokens > 0 ? `${client.active_tokens} live token${ client.active_tokens === 1 ? "" : "s" }` : "Never approved"}
))}
)} !open && setPending(null)} > Revoke '{pending?.client_name}'? It loses its refresh tokens, so it can get nothing new and has to be approved again from scratch. An access token already in its hands keeps working until it expires. Every other agent is left alone.
) } export default OAuthClients