;
+}
+
+export type MCPServerTools = {
+ available: MCPAgentTool[];
+ selected: string[];
+}
+
+export type MCPServer = {
+ name: string;
+ endpoint: string;
+ apiKey: string | null;
+ secretArn: string | null;
+ tools: MCPServerTools;
+}
+
+export type MCPConfig = {
+ toolType: "mcp";
+ name: string;
+ description: string;
+ mcpServers: MCPServer[];
+};
+
+export type AgentTool = InternetAgentTool | PlainAgentTool | BedrockAgentTool | MCPConfig;
export type Agent = {
tools: AgentTool[];
diff --git a/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx b/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx
index db5d655e4..05eaa3a81 100644
--- a/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx
+++ b/frontend/src/features/knowledgeBase/pages/BotKbEditPage.tsx
@@ -79,7 +79,6 @@ const BotKbEditPage: React.FC = () => {
const navigate = useNavigate();
const { botId: paramsBotId } = useParams();
const { getMyBot, registerBot, updateBot } = useBot();
- const { availableTools } = useAgent();
const { getGlobalConfig } = useGlobalConfig();
const { data: globalConfig } = getGlobalConfig();
@@ -391,6 +390,8 @@ const BotKbEditPage: React.FC = () => {
return isNewBot ? ulid() : (paramsBotId ?? '');
}, [isNewBot, paramsBotId]);
+ const { availableTools } = useAgent(botId);
+
const onChangeIncludePattern = useCallback(
(pattern: string, idx: number) => {
setWebCrawlingFilters(
@@ -1535,9 +1536,12 @@ const BotKbEditPage: React.FC = () => {
diff --git a/frontend/src/hooks/useBot.ts b/frontend/src/hooks/useBot.ts
index 5d37d3f47..522776522 100644
--- a/frontend/src/hooks/useBot.ts
+++ b/frontend/src/hooks/useBot.ts
@@ -1,4 +1,5 @@
import { RegisterBotRequest, UpdateBotRequest } from '../@types/bot';
+import { MCPServer } from '../features/agent/types';
import useBotApi from './useBotApi';
import { produce } from 'immer';
@@ -226,6 +227,11 @@ const useBot = (shouldAutoRefreshMyBots?: boolean) => {
deleteUploadedFile: (botId: string, filename: string) => {
return api.deleteUploadedFile(botId, filename);
},
+ testMcpServerConnection: (botId: string, params: MCPServer) => {
+ return api.testMcpServerConnection(botId, params).finally(() => {
+ mutateMyBots();
+ });
+ }
};
};
diff --git a/frontend/src/hooks/useBotApi.ts b/frontend/src/hooks/useBotApi.ts
index d21e3e56b..dcf0b9028 100644
--- a/frontend/src/hooks/useBotApi.ts
+++ b/frontend/src/hooks/useBotApi.ts
@@ -16,6 +16,7 @@ import {
UpdateBotSharedScopeResponse,
} from '../@types/bot';
import useHttp from './useHttp';
+import { MCPServer } from '../features/agent/types';
const useBotApi = () => {
const http = useHttp();
@@ -112,6 +113,9 @@ const useBotApi = () => {
filename,
});
},
+ testMcpServerConnection: (botId: string, params: MCPServer) => {
+ return http.post(`bot/${botId}/agent/mcp-config`, params);
+ },
};
};
diff --git a/frontend/src/hooks/usePostMessageStreaming.ts b/frontend/src/hooks/usePostMessageStreaming.ts
index 40784e1c7..579a5ef62 100644
--- a/frontend/src/hooks/usePostMessageStreaming.ts
+++ b/frontend/src/hooks/usePostMessageStreaming.ts
@@ -43,6 +43,7 @@ const usePostMessageStreaming = create<{
const ws = new WebSocket(WS_ENDPOINT);
ws.onopen = () => {
+ console.log('[FRONTEND_WS] WebSocket connection opened');
ws.send(
JSON.stringify({
step: PostStreamingStatus.START,
@@ -53,6 +54,7 @@ const usePostMessageStreaming = create<{
ws.onmessage = (message) => {
try {
+ console.log('[FRONTEND_WS] Received message:', message.data);
if (
message.data === '' ||
message.data === 'Message sent.' ||
@@ -87,8 +89,10 @@ const usePostMessageStreaming = create<{
}
const data = JSON.parse(message.data);
+ console.log('[FRONTEND_WS] Parsed data:', data);
if (data.status) {
+ console.log('[FRONTEND_WS] Processing status:', data.status);
switch (data.status) {
case PostStreamingStatus.AGENT_THINKING:
if (completion.length > 0) {
@@ -139,12 +143,36 @@ const usePostMessageStreaming = create<{
}
break;
case PostStreamingStatus.STREAMING_END:
- thinkingDispatch({
- type: 'goodbye',
- });
- reasoningDispatch({ type: 'end' });
+ console.log(
+ '[FRONTEND_WS] Received STREAMING_END, ending thinking state'
+ );
+ try {
+ console.log(
+ '[FRONTEND_WS] Calling thinkingDispatch goodbye'
+ );
+ thinkingDispatch({
+ type: 'goodbye',
+ });
+ console.log(
+ '[FRONTEND_WS] thinkingDispatch goodbye completed'
+ );
- ws.close();
+ console.log('[FRONTEND_WS] Calling reasoningDispatch end');
+ reasoningDispatch({ type: 'end' });
+ console.log(
+ '[FRONTEND_WS] reasoningDispatch end completed'
+ );
+
+ console.log('[FRONTEND_WS] Closing WebSocket');
+ ws.close();
+ console.log('[FRONTEND_WS] WebSocket closed successfully');
+ } catch (error) {
+ console.error(
+ '[FRONTEND_WS] Error in STREAMING_END handling:',
+ error
+ );
+ ws.close();
+ }
break;
case PostStreamingStatus.ERROR:
ws.close();
@@ -166,17 +194,26 @@ const usePostMessageStreaming = create<{
throw new Error(i18next.t('error.predict.invalidResponse'));
}
} catch (e) {
- console.error(e);
+ console.error('[FRONTEND_WS] Error in onmessage handler:', e);
+ console.error(
+ '[FRONTEND_WS] Message data that caused error:',
+ message.data
+ );
reject(i18next.t('error.predict.general'));
}
};
ws.onerror = (e) => {
+ console.error('[FRONTEND_WS] WebSocket error:', e);
ws.close();
- console.error(e);
reject(i18next.t('error.predict.general'));
};
- ws.onclose = () => {
+ ws.onclose = (event) => {
+ console.log(
+ '[FRONTEND_WS] WebSocket closed:',
+ event.code,
+ event.reason
+ );
resolve(completion);
};
});
diff --git a/frontend/src/i18n/en/index.ts b/frontend/src/i18n/en/index.ts
index 929f53818..bb1de0371 100644
--- a/frontend/src/i18n/en/index.ts
+++ b/frontend/src/i18n/en/index.ts
@@ -210,6 +210,31 @@ const translation = {
placeholder: 'Enter Alias ID',
},
},
+ mcp: {
+ name: 'MCP',
+ description: 'Connect to remote MCP servers',
+ config: {
+ addServer: 'Add MCP server',
+ noServers: 'No MCP servers configured',
+ addFirstServer: 'Add first MCP server',
+ server: 'MCP Server',
+ remove: 'Remove',
+ name: 'Server name',
+ namePlaceholder: 'Enter MCP server name',
+ endpoint: 'API endpoint',
+ endpointPlaceholder: 'Enter API endpoint',
+ apiKey: 'API Key (Optional)',
+ apiKeyPlaceholder: 'Enter API Key',
+ connect: 'Connect MCP Server',
+ tools: 'Tools',
+ available: 'Available Tools',
+ selected: 'Selected Tools',
+ noTools: 'No tools available',
+ noToolsSelected: 'No tools selected',
+ noToolsAvailable: 'No tools available',
+ noToolsSelectedAvailable: 'No tools selected or available'
+ }
+ }
},
},
bot: {