Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1643,7 +1643,7 @@ const App = () => {
setNotifications((prev) => [...prev, notification]);
}}
/>
<ConsoleTab />
<ConsoleTab serverLogs={notifications} />
<PingTab
onPingClick={() => {
void sendMCPRequest(
Expand Down
112 changes: 104 additions & 8 deletions client/src/components/ConsoleTab.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,108 @@
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 = () => (
<TabsContent value="console" className="h-96">
<div className="bg-gray-900 text-gray-100 p-4 rounded-lg h-full font-mono text-sm overflow-auto">
<div className="opacity-50">Welcome to MCP Client Console</div>
{/* Console output would go here */}
</div>
</TabsContent>
);
const LEVEL_COLORS: Record<string, string> = {
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<HTMLDivElement>(null);
const [clearedCount, setClearedCount] = useState(0);

const allLogEntries = serverLogs.filter(
(n) => n.method === "notifications/message",
);
Comment thread
Julien-ser marked this conversation as resolved.
const logEntries = allLogEntries.slice(clearedCount);

useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
}, [logEntries.length]);
Comment on lines +18 to +40

return (
<TabsContent value="console" className="h-96">
<div className="flex flex-col h-full bg-gray-900 rounded-lg overflow-hidden">
<div className="flex items-center justify-between px-4 py-2 border-b border-gray-700">
<span className="text-xs text-gray-400 font-mono">
Server Logs (stderr / MCP logging)
</span>
<Button
variant="ghost"
size="sm"
onClick={() => setClearedCount(allLogEntries.length)}
disabled={logEntries.length === 0}
className="text-gray-400 hover:text-gray-200 h-6 text-xs"
>
Clear
</Button>
</div>
<div className="flex-1 overflow-auto p-3 font-mono text-sm">
{logEntries.length === 0 ? (
<div className="text-gray-500 opacity-50 text-xs">
No server logs yet. Stderr output and MCP logging notifications
will appear here.
</div>
) : (
<div className="space-y-0.5">
{logEntries.map((notification, index) => {
const params = notification.params as Record<string, unknown>;
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<string, unknown>;
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 (
<div key={index} className="flex gap-2 leading-5">
<span className="text-gray-500 select-none shrink-0 text-xs">
[{level.toUpperCase().slice(0, 7).padEnd(7)}]
</span>
{logger && logger !== "stdio" && (
<span className="text-gray-600 select-none shrink-0 text-xs">
[{logger}]
</span>
)}
<span
className={`${colorClass} break-all whitespace-pre-wrap`}
>
{message}
</span>
</div>
);
})}
<div ref={bottomRef} />
</div>
)}
</div>
</div>
</TabsContent>
);
};

export default ConsoleTab;
Loading