From 91608a0b97f814417a032c6a94ee714f310861b0 Mon Sep 17 00:00:00 2001 From: Jerome Date: Fri, 13 Jun 2025 19:07:01 +0100 Subject: [PATCH] Fix MCP message monitor layout and add collapsible sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Made MCP tab sections (Summary, Add Servers, Server List) collapsible to save space - Expanded sidebar width limit from 600px to 80% of window width for better debugging - Fixed message monitor overflow by establishing proper height constraint chain - Removed auto-scroll and show/hide toggle for message monitor - always visible now - Server list now has fixed height (max-h-60) with internal scroll - Message monitor takes remaining space with proper internal scrolling - Used h-full chain instead of flex-1 to establish explicit height constraints - Message monitor now properly scrolls within its container instead of at app level 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/components/ConversationApp.tsx | 4 +- src/components/MCPMessageMonitor.tsx | 161 +++++++++++++++++++++++++++ src/components/MCPTab.tsx | 128 ++++++++++++++------- src/components/TabbedSidebar.tsx | 6 +- src/contexts/MCPContext.tsx | 51 +++++++++ src/mcp/connection.ts | 81 +++++--------- src/mcp/debugTransport.ts | 63 +++++++++++ src/types/mcp.ts | 16 +++ 8 files changed, 409 insertions(+), 101 deletions(-) create mode 100644 src/components/MCPMessageMonitor.tsx create mode 100644 src/mcp/debugTransport.ts diff --git a/src/components/ConversationApp.tsx b/src/components/ConversationApp.tsx index bcdd268..c1faafc 100644 --- a/src/components/ConversationApp.tsx +++ b/src/components/ConversationApp.tsx @@ -21,7 +21,7 @@ export function ConversationApp() { if (!isResizing.current) return; const newWidth = e.clientX; - if (newWidth >= 240 && newWidth <= 600) { + if (newWidth >= 240 && newWidth <= window.innerWidth * 0.8) { setSidebarWidth(newWidth); } }, []); @@ -39,7 +39,7 @@ export function ConversationApp() {
{/* Left Sidebar - Tabbed Interface */}
diff --git a/src/components/MCPMessageMonitor.tsx b/src/components/MCPMessageMonitor.tsx new file mode 100644 index 0000000..8aefdc3 --- /dev/null +++ b/src/components/MCPMessageMonitor.tsx @@ -0,0 +1,161 @@ +// Component to monitor and display MCP messages + +import { useState, useEffect, useRef } from 'react'; +import { useMCP } from '@/contexts/MCPContext'; +import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; + +interface MCPMessageEntry { + id: string; + timestamp: Date; + connectionId: string; + connectionName: string; + direction: 'sent' | 'received'; + message: JSONRPCMessage; + extra?: any; +} + +export function MCPMessageMonitor() { + const { addMessageCallback, removeMessageCallback, connections } = useMCP(); + const [messages, setMessages] = useState([]); + const [maxMessages, setMaxMessages] = useState(50); + const messagesEndRef = useRef(null); + + // Remove auto-scroll - let user control scroll position + + // Register message callback + useEffect(() => { + const callbackId = addMessageCallback((connectionId, _client, message, direction, extra) => { + const connection = connections.find(c => c.id === connectionId); + + setMessages(prev => { + const newMessage: MCPMessageEntry = { + id: `${Date.now()}-${Math.random()}`, + timestamp: new Date(), + connectionId, + connectionName: connection?.name || 'Unknown', + direction, + message, + extra, + }; + + // Keep only the latest maxMessages + const newMessages = [...prev, newMessage]; + return newMessages.slice(-maxMessages); + }); + }); + + return () => { + removeMessageCallback(callbackId); + }; + }, [addMessageCallback, removeMessageCallback, connections, maxMessages]); + + const formatMessage = (msg: JSONRPCMessage) => { + if ('method' in msg) { + return `${msg.method}${'id' in msg ? ` (${msg.id})` : ''}`; + } + if ('id' in msg && 'result' in msg) { + return `Response (${msg.id})`; + } + if ('id' in msg && 'error' in msg) { + return `Error (${msg.id})`; + } + return 'Unknown'; + }; + + const getMessageTypeColor = (msg: JSONRPCMessage, direction: string) => { + const baseClass = direction === 'sent' + ? 'bg-blue-50 dark:bg-blue-900/30 border-blue-200 dark:border-blue-800' + : 'bg-green-50 dark:bg-green-900/30 border-green-200 dark:border-green-800'; + + if ('error' in msg) { + return 'bg-red-50 dark:bg-red-900/30 border-red-200 dark:border-red-800'; + } + + return baseClass; + }; + + const clearMessages = () => { + setMessages([]); + }; + + return ( +
+
+

+ Message Monitor ({messages.length}) +

+ +
+ +
+ {messages.length === 0 ? ( +
+ No messages yet. Messages will appear here as they're sent/received. +
+ ) : ( +
+ {messages.map((entry) => ( +
+
+
+ + {entry.direction === 'sent' ? '→' : '←'} + + + {formatMessage(entry.message)} + + + {entry.connectionName} + +
+ + {entry.timestamp.toLocaleTimeString()} + +
+
+ + Details + +
+                    {JSON.stringify(entry.message, null, 2)}
+                  
+
+
+ ))} +
+
+ )} +
+ + {/* Controls */} +
+
+ + +
+
+ Live monitoring active +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/MCPTab.tsx b/src/components/MCPTab.tsx index febfc2a..c77f497 100644 --- a/src/components/MCPTab.tsx +++ b/src/components/MCPTab.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { useMCP } from '@/contexts/MCPContext'; +import { MCPMessageMonitor } from './MCPMessageMonitor'; export function MCPTab() { const { connections, addMcpServer, removeMcpServer, reconnectServer, isLoading } = useMCP(); @@ -9,6 +10,11 @@ export function MCPTab() { const [newServerName, setNewServerName] = useState(''); const [newServerUrl, setNewServerUrl] = useState(''); const [authType, setAuthType] = useState<'none' | 'oauth'>('none'); + + // Collapsible section states + const [showSummary, setShowSummary] = useState(true); + const [showAddServers, setShowAddServers] = useState(true); + const [showServerList, setShowServerList] = useState(true); const handleAddExampleServer = async () => { try { @@ -73,50 +79,75 @@ export function MCPTab() { const allTools = connections.flatMap(conn => conn.tools); return ( -
+
{/* Summary */} -
-
-

- MCP Summary -

-
-

- {connectedServers.length} servers connected -

-

- {allTools.length} tools available -

+
+
+ + {showSummary && ( +
+
+
+

+ {connectedServers.length} servers connected +

+

+ {allTools.length} tools available +

+
+
+
+ )}
{/* Add Servers Section */} -
-

- Add Servers -

- - {/* Example Server Button */} +
+ {showAddServers && ( +
+ {/* Example Server Button */} + - {/* Custom Server Form Toggle */} - + {/* Custom Server Form Toggle */} + - {/* Custom Server Form */} - {showAddForm && ( -
+ {/* Custom Server Form */} + {showAddForm && ( +
+ )}
{/* Server List */} -
-
-

- Servers ({connections.length}) -

-
+
+ + {showServerList && ( +
{connections.length === 0 ? (

@@ -258,7 +301,12 @@ export function MCPTab() { ))}

)} +
+ )}
+ + {/* Message Monitor */} +
); } \ No newline at end of file diff --git a/src/components/TabbedSidebar.tsx b/src/components/TabbedSidebar.tsx index a827a5f..89565ae 100644 --- a/src/components/TabbedSidebar.tsx +++ b/src/components/TabbedSidebar.tsx @@ -17,9 +17,9 @@ export function TabbedSidebar() { ]; return ( -
+
{/* Tab Headers */} -
+
{tabs.map((tab) => (