diff --git a/client/src/App.tsx b/client/src/App.tsx index 59d15ba06..ebf9f3244 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1643,7 +1643,7 @@ const App = () => { setNotifications((prev) => [...prev, notification]); }} /> - + { void sendMCPRequest( diff --git a/client/src/components/ConsoleTab.tsx b/client/src/components/ConsoleTab.tsx index 8f05f704c..45eb213b1 100644 --- a/client/src/components/ConsoleTab.tsx +++ b/client/src/components/ConsoleTab.tsx @@ -1,12 +1,115 @@ +import { ServerNotification } from "@modelcontextprotocol/sdk/types.js"; +import { useEffect, useRef, useState } from "react"; import { TabsContent } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; -const ConsoleTab = () => ( - -
-
Welcome to MCP Client Console
- {/* Console output would go here */} -
-
-); +const LEVEL_COLORS: Record = { + debug: "text-gray-400", + info: "text-green-400", + notice: "text-blue-300", + warning: "text-yellow-400", + warn: "text-yellow-400", + error: "text-red-400", + critical: "text-red-500", + alert: "text-orange-400", + emergency: "text-red-600", +}; + +const ConsoleTab = ({ + serverLogs = [], +}: { + serverLogs?: ServerNotification[]; +}) => { + const bottomRef = useRef(null); + const [clearedCount, setClearedCount] = useState(0); + + const allLogEntries = serverLogs.filter( + (n) => n.method === "notifications/message", + ); + + useEffect(() => { + setClearedCount((currentClearedCount) => + Math.min(currentClearedCount, allLogEntries.length), + ); + }, [allLogEntries.length]); + + const logEntries = allLogEntries.slice(clearedCount); + + useEffect(() => { + bottomRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [logEntries.length]); + + return ( + +
+
+ + Server Logs (stderr / MCP logging) + + +
+
+ {logEntries.length === 0 ? ( +
+ No server logs yet. Stderr output and MCP logging notifications + will appear here. +
+ ) : ( +
+ {logEntries.map((notification, index) => { + const params = notification.params as Record; + const level = String(params?.level ?? "info").toLowerCase(); + const logger = String(params?.logger ?? ""); + const data = params?.data; + + let message: string; + if (typeof data === "string") { + message = data; + } else if (typeof data === "object" && data !== null) { + const dataObj = data as Record; + message = + typeof dataObj.message === "string" + ? dataObj.message + : JSON.stringify(data, null, 2); + } else { + message = String(data ?? ""); + } + + const colorClass = LEVEL_COLORS[level] ?? "text-gray-100"; + + return ( +
+ + [{level.toUpperCase().slice(0, 7).padEnd(7)}] + + {logger && logger !== "stdio" && ( + + [{logger}] + + )} + + {message} + +
+ ); + })} +
+
+ )} +
+
+ + ); +}; export default ConsoleTab;