feat: implement MCP UIs

This commit is contained in:
Li Xin
2025-04-24 15:41:33 +08:00
parent d9ffb19950
commit 10b1d63834
32 changed files with 1419 additions and 321 deletions

View File

@@ -1,11 +1,11 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { env } from "~/env";
import type { MCPServerMetadata } from "../mcp";
import { fetchStream } from "../sse";
import { sleep } from "../utils";
import { resolveServiceURL } from "./resolve-service-url";
import type { ChatEvent } from "./types";
export function chatStream(
@@ -15,23 +15,29 @@ export function chatStream(
max_plan_iterations: number;
max_step_num: number;
interrupt_feedback?: string;
mcp_settings?: {
servers: Record<
string,
MCPServerMetadata & {
enabled_tools: string[];
add_to_agents: string[];
}
>;
};
},
options: { abortSignal?: AbortSignal } = {},
) {
if (location.search.includes("mock")) {
return chatStreamMock(userMessage, params, options);
}
return fetchStream<ChatEvent>(
(env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000/api") + "/chat/stream",
{
body: JSON.stringify({
messages: [{ role: "user", content: userMessage }],
auto_accepted_plan: false,
...params,
}),
signal: options.abortSignal,
},
);
return fetchStream<ChatEvent>(resolveServiceURL("chat/stream"), {
body: JSON.stringify({
messages: [{ role: "user", content: userMessage }],
auto_accepted_plan: false,
...params,
}),
signal: options.abortSignal,
});
}
async function* chatStreamMock(

View File

@@ -2,4 +2,6 @@
// SPDX-License-Identifier: MIT
export * from "./chat";
export * from "./mcp";
export * from "./podcast";
export * from "./types";

20
web/src/core/api/mcp.ts Normal file
View File

@@ -0,0 +1,20 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import type { SimpleMCPServerMetadata } from "../mcp";
import { resolveServiceURL } from "./resolve-service-url";
export async function queryMCPServerMetadata(config: SimpleMCPServerMetadata) {
const response = await fetch(resolveServiceURL("mcp/server/metadata"), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(config),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}

View File

@@ -1,20 +1,16 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { env } from "~/env";
import { resolveServiceURL } from "./resolve-service-url";
export async function generatePodcast(content: string) {
const response = await fetch(
(env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000/api") +
"/podcast/generate",
{
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ content }),
const response = await fetch(resolveServiceURL("podcast/generate"), {
method: "post",
headers: {
"Content-Type": "application/json",
},
);
body: JSON.stringify({ content }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
// SPDX-License-Identifier: MIT
import { env } from "~/env";
export function resolveServiceURL(path: string) {
let BASE_URL = env.NEXT_PUBLIC_API_URL ?? "http://localhost:8000/api/";
if (!BASE_URL.endsWith("/")) {
BASE_URL += "/";
}
return new URL(path, BASE_URL).toString();
}