react front end show available tools from mcp

This commit is contained in:
2026-03-05 19:32:46 +08:00
parent eb7e85e4e6
commit 3fc3d7288c
4 changed files with 76 additions and 0 deletions

View File

@@ -6,6 +6,7 @@ import {
getGraphDefaultConfig, getGraphDefaultConfig,
getPipelineDefaultConfig, getPipelineDefaultConfig,
getMcpToolConfig, getMcpToolConfig,
listMcpAvailableTools,
listAvailableGraphs, listAvailableGraphs,
listGraphConfigs, listGraphConfigs,
listPipelines, listPipelines,
@@ -403,6 +404,8 @@ export default function App() {
const [mcpConfigPath, setMcpConfigPath] = useState<string>(""); const [mcpConfigPath, setMcpConfigPath] = useState<string>("");
const [mcpEntries, setMcpEntries] = useState<McpEntry[]>([]); const [mcpEntries, setMcpEntries] = useState<McpEntry[]>([]);
const [mcpToolKeys, setMcpToolKeys] = useState<string[]>([]); const [mcpToolKeys, setMcpToolKeys] = useState<string[]>([]);
const [mcpToolsByServer, setMcpToolsByServer] = useState<Record<string, string[]>>({});
const [mcpErrorsByServer, setMcpErrorsByServer] = useState<Record<string, string>>({});
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
const configKeySet = useMemo( const configKeySet = useMemo(
@@ -654,6 +657,7 @@ export default function App() {
setStatusMessage((error as Error).message); setStatusMessage((error as Error).message);
return; return;
} }
await refreshMcpAvailableTools();
setStatusMessage("MCP config loaded."); setStatusMessage("MCP config loaded.");
} catch (error) { } catch (error) {
setStatusMessage((error as Error).message); setStatusMessage((error as Error).message);
@@ -693,6 +697,7 @@ export default function App() {
const resp = await updateMcpToolConfig({ raw_content: rawContent }); const resp = await updateMcpToolConfig({ raw_content: rawContent });
setMcpConfigPath(resp.path || ""); setMcpConfigPath(resp.path || "");
setMcpToolKeys(resp.tool_keys || []); setMcpToolKeys(resp.tool_keys || []);
await refreshMcpAvailableTools();
setStatusMessage("MCP config saved."); setStatusMessage("MCP config saved.");
} catch (error) { } catch (error) {
setStatusMessage((error as Error).message); setStatusMessage((error as Error).message);
@@ -701,6 +706,27 @@ export default function App() {
} }
} }
async function refreshMcpAvailableTools(): Promise<void> {
try {
const resp = await listMcpAvailableTools();
const nextTools: Record<string, string[]> = {};
const nextErrors: Record<string, string> = {};
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 { function addMcpEntry(): void {
setMcpEntries((prev) => [...prev, createEmptyMcpEntry()]); setMcpEntries((prev) => [...prev, createEmptyMcpEntry()]);
} }
@@ -1149,6 +1175,23 @@ export default function App() {
Remove Remove
</button> </button>
</div> </div>
{entry.name.trim() ? (
<div className="mcp-tools-inline">
<p className="empty">
Available tools:{" "}
{(mcpToolsByServer[entry.name.trim()] || []).length > 0
? mcpToolsByServer[entry.name.trim()].join(", ")
: "(none)"}
</p>
{mcpErrorsByServer[entry.name.trim()] ? (
<p className="mcp-tools-error">
Error: {mcpErrorsByServer[entry.name.trim()]}
</p>
) : null}
</div>
) : (
<p className="empty">Set MCP Name, then Save/Reload to fetch tools.</p>
)}
<div className="mcp-entry-grid"> <div className="mcp-entry-grid">
<label> <label>
MCP Name MCP Name
@@ -1229,6 +1272,9 @@ export default function App() {
)) ))
)} )}
</div> </div>
{mcpErrorsByServer._global ? (
<p className="mcp-tools-error">Error: {mcpErrorsByServer._global}</p>
) : null}
</section> </section>
)} )}
</main> </main>

View File

@@ -4,6 +4,7 @@ import type {
GraphConfigReadResponse, GraphConfigReadResponse,
GraphConfigUpsertRequest, GraphConfigUpsertRequest,
GraphConfigUpsertResponse, GraphConfigUpsertResponse,
McpAvailableToolsResponse,
McpToolConfigResponse, McpToolConfigResponse,
McpToolConfigUpdateRequest, McpToolConfigUpdateRequest,
McpToolConfigUpdateResponse, McpToolConfigUpdateResponse,
@@ -114,6 +115,10 @@ export function updateMcpToolConfig(
}); });
} }
export function listMcpAvailableTools(): Promise<McpAvailableToolsResponse> {
return fetchJson("/v1/tool-configs/mcp/tools");
}
export function createPipeline( export function createPipeline(
payload: PipelineCreateRequest payload: PipelineCreateRequest
): Promise<PipelineCreateResponse> { ): Promise<PipelineCreateResponse> {

View File

@@ -317,6 +317,19 @@ button:disabled {
margin-top: 10px; 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 { .mcp-entry-card {
background: #fff; background: #fff;
border: 1px solid #d7e6f6; border: 1px solid #d7e6f6;

View File

@@ -105,3 +105,15 @@ export type McpToolConfigUpdateResponse = {
tool_keys: string[]; tool_keys: string[];
}; };
export type McpAvailableToolsResponse = {
available_tools: string[];
errors: string[];
servers: Record<
string,
{
tools: string[];
error?: string | null;
}
>;
};