Skip to content

Commit f3da4d9

Browse files
authored
Merge pull request modelcontextprotocol#8 from modelcontextprotocol/ashwin/history
add command history
2 parents ccd5157 + 936a50b commit f3da4d9

File tree

2 files changed

+207
-85
lines changed

2 files changed

+207
-85
lines changed

client/src/App.tsx

Lines changed: 113 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import ResourcesTab, { Resource } from "./components/ResourcesTab";
1919
import NotificationsTab from "./components/NotificationsTab";
2020
import PromptsTab, { Prompt } from "./components/PromptsTab";
2121
import ToolsTab, { Tool as ToolType } from "./components/ToolsTab";
22+
import History from "./components/CommandHistory";
2223

2324
const App = () => {
2425
const [socket, setSocket] = useState<WebSocket | null>(null);
@@ -39,6 +40,9 @@ const App = () => {
3940
"/Users/ashwin/code/example-servers/build/everything/index.js",
4041
);
4142
const [mcpConnected, setMcpConnected] = useState<boolean>(false);
43+
const [requestHistory, setRequestHistory] = useState<
44+
Array<{ request: string; response: string | null }>
45+
>([]);
4246

4347
useEffect(() => {
4448
const ws = new WebSocket("ws://localhost:3000");
@@ -75,6 +79,8 @@ const App = () => {
7579
} else if (message.type === "connected") {
7680
setMcpConnected(true);
7781
}
82+
83+
updateRequestHistory(message);
7884
};
7985

8086
ws.onerror = () => {
@@ -89,10 +95,29 @@ const App = () => {
8995
return () => ws.close();
9096
}, []);
9197

98+
const updateRequestHistory = (response: unknown) => {
99+
setRequestHistory((prev) => {
100+
const lastRequest = prev[prev.length - 1];
101+
if (lastRequest && lastRequest.response === null) {
102+
const updatedHistory = [...prev];
103+
updatedHistory[updatedHistory.length - 1] = {
104+
...lastRequest,
105+
response: JSON.stringify(response),
106+
};
107+
return updatedHistory;
108+
}
109+
return prev;
110+
});
111+
};
112+
92113
const sendWebSocketMessage = (message: object) => {
93114
if (socket) {
94115
console.log("Sending WebSocket message:", message);
95116
socket.send(JSON.stringify(message));
117+
setRequestHistory((prev) => [
118+
...prev,
119+
{ request: JSON.stringify(message), response: null },
120+
]);
96121
}
97122
};
98123

@@ -136,97 +161,100 @@ const App = () => {
136161
<Sidebar connectionStatus={connectionStatus} />
137162
<div className="flex-1 flex flex-col overflow-hidden">
138163
<h1 className="text-2xl font-bold p-4">MCP Inspector</h1>
139-
<div className="flex-1 overflow-auto">
140-
<div className="p-4 bg-white shadow-md m-4 rounded-md">
141-
<h2 className="text-lg font-semibold mb-2">Connect MCP Server</h2>
142-
<div className="flex space-x-2 mb-2">
143-
<Input
144-
placeholder="Command"
145-
value={command}
146-
onChange={(e) => setCommand(e.target.value)}
147-
/>
148-
<Input
149-
placeholder="Arguments (space-separated)"
150-
value={args}
151-
onChange={(e) => setArgs(e.target.value)}
152-
/>
153-
<Button onClick={connectMcpServer}>
154-
<Play className="w-4 h-4 mr-2" />
155-
Connect
156-
</Button>
157-
</div>
158-
</div>
159-
{mcpConnected ? (
160-
<Tabs defaultValue="resources" className="w-full p-4">
161-
<TabsList className="mb-4 p-0">
162-
<TabsTrigger value="resources">
163-
<Files className="w-4 h-4 mr-2" />
164-
Resources
165-
</TabsTrigger>
166-
<TabsTrigger value="prompts">
167-
<MessageSquare className="w-4 h-4 mr-2" />
168-
Prompts
169-
</TabsTrigger>
170-
<TabsTrigger value="requests" disabled>
171-
<Send className="w-4 h-4 mr-2" />
172-
Requests
173-
</TabsTrigger>
174-
<TabsTrigger value="notifications" disabled>
175-
<Bell className="w-4 h-4 mr-2" />
176-
Notifications
177-
</TabsTrigger>
178-
<TabsTrigger value="tools" disabled>
179-
<Hammer className="w-4 h-4 mr-2" />
180-
Tools
181-
</TabsTrigger>
182-
<TabsTrigger value="console" disabled>
183-
<Terminal className="w-4 h-4 mr-2" />
184-
Console
185-
</TabsTrigger>
186-
</TabsList>
187-
188-
<div className="w-full">
189-
<ResourcesTab
190-
resources={resources}
191-
listResources={listResources}
192-
readResource={readResource}
193-
selectedResource={selectedResource}
194-
setSelectedResource={setSelectedResource}
195-
resourceContent={resourceContent}
196-
error={error}
197-
/>
198-
<NotificationsTab />
199-
<PromptsTab
200-
prompts={prompts}
201-
listPrompts={listPrompts}
202-
getPrompt={getPrompt}
203-
selectedPrompt={selectedPrompt}
204-
setSelectedPrompt={setSelectedPrompt}
205-
promptContent={promptContent}
206-
error={error}
164+
<div className="flex-1 overflow-auto flex">
165+
<div className="flex-1">
166+
<div className="p-4 bg-white shadow-md m-4 rounded-md">
167+
<h2 className="text-lg font-semibold mb-2">Connect MCP Server</h2>
168+
<div className="flex space-x-2 mb-2">
169+
<Input
170+
placeholder="Command"
171+
value={command}
172+
onChange={(e) => setCommand(e.target.value)}
207173
/>
208-
<RequestsTab />
209-
<ToolsTab
210-
tools={tools}
211-
listTools={listTools}
212-
callTool={callTool}
213-
selectedTool={selectedTool}
214-
setSelectedTool={setSelectedTool}
215-
toolResult={toolResult}
216-
error={error}
174+
<Input
175+
placeholder="Arguments (space-separated)"
176+
value={args}
177+
onChange={(e) => setArgs(e.target.value)}
217178
/>
218-
<ConsoleTab />
179+
<Button onClick={connectMcpServer}>
180+
<Play className="w-4 h-4 mr-2" />
181+
Connect
182+
</Button>
219183
</div>
220-
</Tabs>
221-
) : (
222-
<div className="flex items-center justify-center h-full">
223-
<p className="text-lg text-gray-500">
224-
Connect to an MCP server to start inspecting
225-
</p>
226184
</div>
227-
)}
185+
{mcpConnected ? (
186+
<Tabs defaultValue="resources" className="w-full p-4">
187+
<TabsList className="mb-4 p-0">
188+
<TabsTrigger value="resources">
189+
<Files className="w-4 h-4 mr-2" />
190+
Resources
191+
</TabsTrigger>
192+
<TabsTrigger value="prompts">
193+
<MessageSquare className="w-4 h-4 mr-2" />
194+
Prompts
195+
</TabsTrigger>
196+
<TabsTrigger value="requests" disabled>
197+
<Send className="w-4 h-4 mr-2" />
198+
Requests
199+
</TabsTrigger>
200+
<TabsTrigger value="notifications" disabled>
201+
<Bell className="w-4 h-4 mr-2" />
202+
Notifications
203+
</TabsTrigger>
204+
<TabsTrigger value="tools" disabled>
205+
<Hammer className="w-4 h-4 mr-2" />
206+
Tools
207+
</TabsTrigger>
208+
<TabsTrigger value="console" disabled>
209+
<Terminal className="w-4 h-4 mr-2" />
210+
Console
211+
</TabsTrigger>
212+
</TabsList>
213+
214+
<div className="w-full">
215+
<ResourcesTab
216+
resources={resources}
217+
listResources={listResources}
218+
readResource={readResource}
219+
selectedResource={selectedResource}
220+
setSelectedResource={setSelectedResource}
221+
resourceContent={resourceContent}
222+
error={error}
223+
/>
224+
<NotificationsTab />
225+
<PromptsTab
226+
prompts={prompts}
227+
listPrompts={listPrompts}
228+
getPrompt={getPrompt}
229+
selectedPrompt={selectedPrompt}
230+
setSelectedPrompt={setSelectedPrompt}
231+
promptContent={promptContent}
232+
error={error}
233+
/>
234+
<RequestsTab />
235+
<ToolsTab
236+
tools={tools}
237+
listTools={listTools}
238+
callTool={callTool}
239+
selectedTool={selectedTool}
240+
setSelectedTool={setSelectedTool}
241+
toolResult={toolResult}
242+
error={error}
243+
/>
244+
<ConsoleTab />
245+
</div>
246+
</Tabs>
247+
) : (
248+
<div className="flex items-center justify-center h-full">
249+
<p className="text-lg text-gray-500">
250+
Connect to an MCP server to start inspecting
251+
</p>
252+
</div>
253+
)}
254+
</div>
228255
</div>
229256
</div>
257+
<History requestHistory={requestHistory} />
230258
</div>
231259
);
232260
};

client/src/components/History.tsx

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { useState } from "react";
2+
import { Copy } from "lucide-react";
3+
4+
const History = ({
5+
requestHistory,
6+
}: {
7+
requestHistory: Array<{ request: string; response: string | null }>;
8+
}) => {
9+
const [expandedRequests, setExpandedRequests] = useState<{
10+
[key: number]: boolean;
11+
}>({});
12+
13+
const toggleRequestExpansion = (index: number) => {
14+
setExpandedRequests((prev) => ({ ...prev, [index]: !prev[index] }));
15+
};
16+
17+
const copyToClipboard = (text: string) => {
18+
navigator.clipboard.writeText(text);
19+
};
20+
21+
return (
22+
<div className="w-64 bg-white shadow-md p-4 overflow-y-auto">
23+
<h2 className="text-lg font-semibold mb-4">History</h2>
24+
<ul className="space-y-3">
25+
{requestHistory
26+
.slice()
27+
.reverse()
28+
.map((request, index) => (
29+
<li
30+
key={index}
31+
className="text-sm text-gray-600 bg-gray-100 p-2 rounded"
32+
>
33+
<div
34+
className="flex justify-between items-center cursor-pointer"
35+
onClick={() =>
36+
toggleRequestExpansion(requestHistory.length - 1 - index)
37+
}
38+
>
39+
<span className="font-mono">
40+
{requestHistory.length - index}.{" "}
41+
{JSON.parse(request.request).type}
42+
</span>
43+
<span>
44+
{expandedRequests[requestHistory.length - 1 - index]
45+
? "▼"
46+
: "▶"}
47+
</span>
48+
</div>
49+
{expandedRequests[requestHistory.length - 1 - index] && (
50+
<>
51+
<div className="mt-2">
52+
<div className="flex justify-between items-center mb-1">
53+
<span className="font-semibold text-blue-600">
54+
Request:
55+
</span>
56+
<button
57+
onClick={() => copyToClipboard(request.request)}
58+
className="text-blue-500 hover:text-blue-700"
59+
>
60+
<Copy size={16} />
61+
</button>
62+
</div>
63+
<pre className="whitespace-pre-wrap break-words bg-blue-50 p-2 rounded">
64+
{JSON.stringify(JSON.parse(request.request), null, 2)}
65+
</pre>
66+
</div>
67+
{request.response && (
68+
<div className="mt-2">
69+
<div className="flex justify-between items-center mb-1">
70+
<span className="font-semibold text-green-600">
71+
Response:
72+
</span>
73+
<button
74+
onClick={() => copyToClipboard(request.response!)}
75+
className="text-blue-500 hover:text-blue-700"
76+
>
77+
<Copy size={16} />
78+
</button>
79+
</div>
80+
<pre className="whitespace-pre-wrap break-words bg-green-50 p-2 rounded">
81+
{JSON.stringify(JSON.parse(request.response), null, 2)}
82+
</pre>
83+
</div>
84+
)}
85+
</>
86+
)}
87+
</li>
88+
))}
89+
</ul>
90+
</div>
91+
);
92+
};
93+
94+
export default History;

0 commit comments

Comments
 (0)