import { keepPreviousData, useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { CapabilityStatsResponse, CapabilityToolsResponse, McpServerConfig, type McpServerConfigUpdate, McpServerStatus, } from "@umt/contracts"; import { TTL_MINUTE, TTL_SECOND } from "~/lib/api.ts"; import { apiParsed } from "./parse.ts"; // 能力清单 / 调用统计 / 对外 MCP 服务端(AG-1):GET /capabilities/tools、/capabilities/stats、/mcp/status,PATCH /mcp/config。 const KEY = { tools: ["capabilities", "tools"] as const, stats: (days: number) => ["capabilities", "stats", days] as const, mcp: ["mcp", "status"] as const, }; /** 能力清单(描述符 + JSON Schema + 授权状态);会话页工具卡片与设置页共用,改动少,缓存久一点 */ export function useCapabilityTools(enabled = true) { return useQuery({ queryKey: KEY.tools, queryFn: ({ signal }) => apiParsed(CapabilityToolsResponse, "/capabilities/tools", { signal }), staleTime: TTL_MINUTE, enabled, }); } export function useCapabilityStats(days = 7) { return useQuery({ queryKey: KEY.stats(days), queryFn: ({ signal }) => apiParsed(CapabilityStatsResponse, "/capabilities/stats", { params: { days }, signal }), staleTime: 30 * TTL_SECOND, placeholderData: keepPreviousData, }); } export function useMcpStatus() { return useQuery({ queryKey: KEY.mcp, queryFn: ({ signal }) => apiParsed(McpServerStatus, "/mcp/status", { signal }), refetchInterval: 15 * TTL_SECOND, }); } export function useUpdateMcpConfig() { const qc = useQueryClient(); return useMutation({ mutationFn: (body: McpServerConfigUpdate) => apiParsed(McpServerConfig, "/mcp/config", { method: "PATCH", body }), onSuccess: () => qc.invalidateQueries({ queryKey: KEY.mcp }), }); } /** * 给 Claude Code 等 MCP 客户端的接入配置(Streamable HTTP)。密钥用环境变量占位,不含明文: * 客户端侧把 UMT_API_KEY 设为在「API Key」分区新建的 Key。 */ export function mcpClientConfig(origin: string, path: string, serverName: string) { return { mcpServers: { [serverName]: { type: "http", url: `${origin}${path}`, // biome-ignore lint/suspicious/noTemplateCurlyInString: 客户端侧的环境变量占位,不是本地模板 headers: { Authorization: "Bearer ${UMT_API_KEY}" }, }, }, }; }