add tab to modify mcp_config.json in front end
This commit is contained in:
@@ -4,10 +4,12 @@ import {
|
||||
deleteGraphConfig,
|
||||
getGraphConfig,
|
||||
getGraphDefaultConfig,
|
||||
getMcpToolConfig,
|
||||
listAvailableGraphs,
|
||||
listGraphConfigs,
|
||||
listPipelines,
|
||||
stopPipeline,
|
||||
updateMcpToolConfig,
|
||||
upsertGraphConfig,
|
||||
} from "./api/frontApis";
|
||||
import type {
|
||||
@@ -37,6 +39,8 @@ type LaunchCredentials = {
|
||||
authKeyMasked: string;
|
||||
};
|
||||
|
||||
type ActiveTab = "agents" | "mcp";
|
||||
|
||||
const DEFAULT_ENTRY_POINT = "fastapi_server/server_dashscope.py";
|
||||
const DEFAULT_LLM_NAME = "qwen-plus";
|
||||
const DEFAULT_PORT = 8100;
|
||||
@@ -118,6 +122,7 @@ function toEditable(
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [activeTab, setActiveTab] = useState<ActiveTab>("agents");
|
||||
const [graphs, setGraphs] = useState<string[]>([]);
|
||||
const [configItems, setConfigItems] = useState<GraphConfigListItem[]>([]);
|
||||
const [running, setRunning] = useState<PipelineRunInfo[]>([]);
|
||||
@@ -126,6 +131,9 @@ export default function App() {
|
||||
const [editor, setEditor] = useState<EditableAgent | null>(null);
|
||||
const [statusMessage, setStatusMessage] = useState<string>("");
|
||||
const [launchCredentials, setLaunchCredentials] = useState<LaunchCredentials | null>(null);
|
||||
const [mcpConfigPath, setMcpConfigPath] = useState<string>("");
|
||||
const [mcpConfigRaw, setMcpConfigRaw] = useState<string>("");
|
||||
const [mcpToolKeys, setMcpToolKeys] = useState<string[]>([]);
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const configKeySet = useMemo(
|
||||
@@ -208,6 +216,16 @@ export default function App() {
|
||||
}
|
||||
}, [selectedId, configKeySet]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab !== "mcp") {
|
||||
return;
|
||||
}
|
||||
if (mcpConfigRaw) {
|
||||
return;
|
||||
}
|
||||
reloadMcpConfig().catch(() => undefined);
|
||||
}, [activeTab]);
|
||||
|
||||
async function selectExisting(item: GraphConfigListItem): Promise<void> {
|
||||
const id = makeAgentKey(item.pipeline_id, item.prompt_set_id);
|
||||
setSelectedId(id);
|
||||
@@ -321,6 +339,37 @@ export default function App() {
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadMcpConfig(): Promise<void> {
|
||||
setBusy(true);
|
||||
setStatusMessage("Loading MCP config...");
|
||||
try {
|
||||
const resp = await getMcpToolConfig();
|
||||
setMcpConfigPath(resp.path || "");
|
||||
setMcpConfigRaw(resp.raw_content || "");
|
||||
setMcpToolKeys(resp.tool_keys || []);
|
||||
setStatusMessage("MCP config loaded.");
|
||||
} catch (error) {
|
||||
setStatusMessage((error as Error).message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveMcpConfig(): Promise<void> {
|
||||
setBusy(true);
|
||||
setStatusMessage("Saving MCP config...");
|
||||
try {
|
||||
const resp = await updateMcpToolConfig({ raw_content: mcpConfigRaw });
|
||||
setMcpConfigPath(resp.path || "");
|
||||
setMcpToolKeys(resp.tool_keys || []);
|
||||
setStatusMessage("MCP config saved.");
|
||||
} catch (error) {
|
||||
setStatusMessage((error as Error).message);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function saveConfig(): Promise<void> {
|
||||
if (!editor) {
|
||||
return;
|
||||
@@ -507,8 +556,11 @@ export default function App() {
|
||||
}
|
||||
}
|
||||
|
||||
const showSidebar = activeTab === "agents";
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<div className={`app ${showSidebar ? "" : "full-width"}`}>
|
||||
{showSidebar ? (
|
||||
<aside className="sidebar">
|
||||
<div className="sidebar-header">
|
||||
<h2>Agents</h2>
|
||||
@@ -543,10 +595,34 @@ export default function App() {
|
||||
{rows.length === 0 ? <p className="empty">No agents configured yet.</p> : null}
|
||||
</div>
|
||||
</aside>
|
||||
) : null}
|
||||
|
||||
<main className="content">
|
||||
<header className="content-header">
|
||||
<h1>Agent Configuration</h1>
|
||||
<h1>Agent Manager</h1>
|
||||
<div className="tabs">
|
||||
<button
|
||||
type="button"
|
||||
className={`tab-button ${activeTab === "agents" ? "active" : ""}`}
|
||||
onClick={() => setActiveTab("agents")}
|
||||
disabled={busy}
|
||||
>
|
||||
Agents
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`tab-button ${activeTab === "mcp" ? "active" : ""}`}
|
||||
onClick={() => setActiveTab("mcp")}
|
||||
disabled={busy}
|
||||
>
|
||||
MCP Config
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{statusMessage ? <p className="status">{statusMessage}</p> : null}
|
||||
{activeTab === "agents" ? (
|
||||
<div className="tab-pane">
|
||||
<div className="header-actions">
|
||||
<button onClick={saveConfig} disabled={busy || !editor}>
|
||||
Save
|
||||
@@ -561,9 +637,7 @@ export default function App() {
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{statusMessage ? <p className="status">{statusMessage}</p> : null}
|
||||
{launchCredentials ? (
|
||||
<div className="launch-credentials">
|
||||
<h3>Access Credentials (shown once)</h3>
|
||||
@@ -746,6 +820,41 @@ export default function App() {
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<section className="mcp-config-section tab-pane">
|
||||
<div className="mcp-config-header">
|
||||
<h3>Edit MCP Tool Options</h3>
|
||||
<div className="header-actions">
|
||||
<button type="button" onClick={reloadMcpConfig} disabled={busy}>
|
||||
Reload
|
||||
</button>
|
||||
<button type="button" onClick={saveMcpConfig} disabled={busy}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="empty">
|
||||
This tab edits <code>configs/mcp_config.json</code> directly (comments supported).
|
||||
</p>
|
||||
{mcpConfigPath ? (
|
||||
<p className="empty">
|
||||
File: <code>{mcpConfigPath}</code>
|
||||
</p>
|
||||
) : null}
|
||||
<p className="empty">
|
||||
Tool options detected: {mcpToolKeys.length ? mcpToolKeys.join(", ") : "(none)"}
|
||||
</p>
|
||||
<textarea
|
||||
className="mcp-config-editor"
|
||||
value={mcpConfigRaw}
|
||||
onChange={(e) => setMcpConfigRaw(e.target.value)}
|
||||
rows={18}
|
||||
spellCheck={false}
|
||||
disabled={busy}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,9 @@ import type {
|
||||
GraphConfigReadResponse,
|
||||
GraphConfigUpsertRequest,
|
||||
GraphConfigUpsertResponse,
|
||||
McpToolConfigResponse,
|
||||
McpToolConfigUpdateRequest,
|
||||
McpToolConfigUpdateResponse,
|
||||
PipelineCreateRequest,
|
||||
PipelineCreateResponse,
|
||||
PipelineListResponse,
|
||||
@@ -85,6 +88,19 @@ export function deleteGraphConfig(
|
||||
});
|
||||
}
|
||||
|
||||
export function getMcpToolConfig(): Promise<McpToolConfigResponse> {
|
||||
return fetchJson("/v1/tool-configs/mcp");
|
||||
}
|
||||
|
||||
export function updateMcpToolConfig(
|
||||
payload: McpToolConfigUpdateRequest
|
||||
): Promise<McpToolConfigUpdateResponse> {
|
||||
return fetchJson("/v1/tool-configs/mcp", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export function createPipeline(
|
||||
payload: PipelineCreateRequest
|
||||
): Promise<PipelineCreateResponse> {
|
||||
|
||||
@@ -24,6 +24,10 @@ body {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.app.full-width {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
border-right: 1px solid #dbe2ea;
|
||||
background: #ffffff;
|
||||
@@ -94,6 +98,21 @@ button:disabled {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
background: #edf3ff;
|
||||
border-color: #4d7ef3;
|
||||
color: #1a4fc5;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
@@ -139,6 +158,10 @@ button:disabled {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
@@ -206,6 +229,35 @@ button:disabled {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mcp-config-section {
|
||||
background: #f7fbff;
|
||||
border: 1px solid #d7e6f6;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.mcp-config-header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mcp-config-header h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mcp-config-editor {
|
||||
border: 1px solid #c9d4e2;
|
||||
border-radius: 8px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 13px;
|
||||
margin-top: 8px;
|
||||
padding: 10px;
|
||||
resize: vertical;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: #687788;
|
||||
margin: 6px 0;
|
||||
|
||||
@@ -85,3 +85,19 @@ export type PipelineStopResponse = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type McpToolConfigResponse = {
|
||||
path: string;
|
||||
raw_content: string;
|
||||
tool_keys: string[];
|
||||
};
|
||||
|
||||
export type McpToolConfigUpdateRequest = {
|
||||
raw_content: string;
|
||||
};
|
||||
|
||||
export type McpToolConfigUpdateResponse = {
|
||||
status: string;
|
||||
path: string;
|
||||
tool_keys: string[];
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user