diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8d81125..fe2fae4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,6 +6,7 @@ import { getGraphDefaultConfig, getPipelineDefaultConfig, getMcpToolConfig, + listMcpAvailableTools, listAvailableGraphs, listGraphConfigs, listPipelines, @@ -403,6 +404,8 @@ export default function App() { const [mcpConfigPath, setMcpConfigPath] = useState(""); const [mcpEntries, setMcpEntries] = useState([]); const [mcpToolKeys, setMcpToolKeys] = useState([]); + const [mcpToolsByServer, setMcpToolsByServer] = useState>({}); + const [mcpErrorsByServer, setMcpErrorsByServer] = useState>({}); const [busy, setBusy] = useState(false); const configKeySet = useMemo( @@ -654,6 +657,7 @@ export default function App() { setStatusMessage((error as Error).message); return; } + await refreshMcpAvailableTools(); setStatusMessage("MCP config loaded."); } catch (error) { setStatusMessage((error as Error).message); @@ -693,6 +697,7 @@ export default function App() { const resp = await updateMcpToolConfig({ raw_content: rawContent }); setMcpConfigPath(resp.path || ""); setMcpToolKeys(resp.tool_keys || []); + await refreshMcpAvailableTools(); setStatusMessage("MCP config saved."); } catch (error) { setStatusMessage((error as Error).message); @@ -701,6 +706,27 @@ export default function App() { } } + async function refreshMcpAvailableTools(): Promise { + try { + const resp = await listMcpAvailableTools(); + const nextTools: Record = {}; + const nextErrors: Record = {}; + const servers = resp.servers || {}; + for (const [serverName, info] of Object.entries(servers)) { + nextTools[serverName] = Array.isArray(info?.tools) ? info.tools : []; + if (typeof info?.error === "string" && info.error.trim()) { + nextErrors[serverName] = info.error; + } + } + setMcpToolsByServer(nextTools); + setMcpErrorsByServer(nextErrors); + } catch (error) { + const message = (error as Error).message || "Unknown error"; + setMcpToolsByServer({}); + setMcpErrorsByServer({ _global: message }); + } + } + function addMcpEntry(): void { setMcpEntries((prev) => [...prev, createEmptyMcpEntry()]); } @@ -1149,6 +1175,23 @@ export default function App() { Remove + {entry.name.trim() ? ( +
+

+ Available tools:{" "} + {(mcpToolsByServer[entry.name.trim()] || []).length > 0 + ? mcpToolsByServer[entry.name.trim()].join(", ") + : "(none)"} +

+ {mcpErrorsByServer[entry.name.trim()] ? ( +

+ Error: {mcpErrorsByServer[entry.name.trim()]} +

+ ) : null} +
+ ) : ( +

Set MCP Name, then Save/Reload to fetch tools.

+ )}
+ {mcpErrorsByServer._global ? ( +

Error: {mcpErrorsByServer._global}

+ ) : null} )} diff --git a/frontend/src/api/frontApis.ts b/frontend/src/api/frontApis.ts index 6e8eb2e..1d53dc5 100644 --- a/frontend/src/api/frontApis.ts +++ b/frontend/src/api/frontApis.ts @@ -4,6 +4,7 @@ import type { GraphConfigReadResponse, GraphConfigUpsertRequest, GraphConfigUpsertResponse, + McpAvailableToolsResponse, McpToolConfigResponse, McpToolConfigUpdateRequest, McpToolConfigUpdateResponse, @@ -114,6 +115,10 @@ export function updateMcpToolConfig( }); } +export function listMcpAvailableTools(): Promise { + return fetchJson("/v1/tool-configs/mcp/tools"); +} + export function createPipeline( payload: PipelineCreateRequest ): Promise { diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 1e76840..2b8cbd3 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -317,6 +317,19 @@ button:disabled { margin-top: 10px; } +.mcp-tools-error { + color: #a33434; + margin: 6px 0 0 0; +} + +.mcp-tools-inline { + background: #f8fbff; + border: 1px solid #d7e6f6; + border-radius: 8px; + margin: 0 0 10px 0; + padding: 8px; +} + .mcp-entry-card { background: #fff; border: 1px solid #d7e6f6; diff --git a/frontend/src/types.ts b/frontend/src/types.ts index b20e3bd..8dda2e4 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -105,3 +105,15 @@ export type McpToolConfigUpdateResponse = { tool_keys: string[]; }; +export type McpAvailableToolsResponse = { + available_tools: string[]; + errors: string[]; + servers: Record< + string, + { + tools: string[]; + error?: string | null; + } + >; +}; +