Skip to content

Commit 1095e48

Browse files
committed
add ability to connect manually
1 parent 38e1c8b commit 1095e48

File tree

2 files changed

+119
-65
lines changed

2 files changed

+119
-65
lines changed

client/src/App.tsx

Lines changed: 103 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import {
66
Files,
77
MessageSquare,
88
Hammer,
9+
Play,
910
} from "lucide-react";
1011
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
12+
import { Input } from "@/components/ui/input";
13+
import { Button } from "@/components/ui/button";
1114

1215
import ConsoleTab from "./components/ConsoleTab";
1316
import Sidebar from "./components/Sidebar";
@@ -29,6 +32,13 @@ const App = () => {
2932
const [tools, setTools] = useState<ToolType[]>([]);
3033
const [toolResult, setToolResult] = useState<string>("");
3134
const [error, setError] = useState<string | null>(null);
35+
const [command, setCommand] = useState<string>(
36+
"/Users/ashwin/.nvm/versions/node/v18.20.4/bin/node",
37+
);
38+
const [args, setArgs] = useState<string>(
39+
"/Users/ashwin/code/example-servers/build/everything/index.js",
40+
);
41+
const [mcpConnected, setMcpConnected] = useState<boolean>(false);
3242

3343
useEffect(() => {
3444
const ws = new WebSocket("ws://localhost:3000");
@@ -62,6 +72,8 @@ const App = () => {
6272
setError(null);
6373
} else if (message.type === "error") {
6474
setError(message.message);
75+
} else if (message.type === "connected") {
76+
setMcpConnected(true);
6577
}
6678
};
6779

@@ -71,6 +83,7 @@ const App = () => {
7183

7284
ws.onclose = () => {
7385
setConnectionStatus("disconnected");
86+
setMcpConnected(false);
7487
};
7588

7689
return () => ws.close();
@@ -113,73 +126,105 @@ const App = () => {
113126
sendWebSocketMessage({ type: "callTool", name, params });
114127
};
115128

129+
const connectMcpServer = () => {
130+
const argsArray = args.split(" ").filter((arg) => arg.trim() !== "");
131+
sendWebSocketMessage({ type: "connect", command, args: argsArray });
132+
};
133+
116134
return (
117135
<div className="flex h-screen bg-gray-100">
118136
<Sidebar connectionStatus={connectionStatus} />
119137
<div className="flex-1 flex flex-col overflow-hidden">
120138
<h1 className="text-2xl font-bold p-4">MCP Inspector</h1>
121139
<div className="flex-1 overflow-auto">
122-
<Tabs defaultValue="requests" className="w-full p-4">
123-
<TabsList className="mb-4">
124-
<TabsTrigger value="resources">
125-
<Files className="w-4 h-4 mr-2" />
126-
Resources
127-
</TabsTrigger>
128-
<TabsTrigger value="prompts">
129-
<MessageSquare className="w-4 h-4 mr-2" />
130-
Prompts
131-
</TabsTrigger>
132-
<TabsTrigger value="requests" disabled>
133-
<Send className="w-4 h-4 mr-2" />
134-
Requests
135-
</TabsTrigger>
136-
<TabsTrigger value="notifications" disabled>
137-
<Bell className="w-4 h-4 mr-2" />
138-
Notifications
139-
</TabsTrigger>
140-
<TabsTrigger value="tools" disabled>
141-
<Hammer className="w-4 h-4 mr-2" />
142-
Tools
143-
</TabsTrigger>
144-
<TabsTrigger value="console" disabled>
145-
<Terminal className="w-4 h-4 mr-2" />
146-
Console
147-
</TabsTrigger>
148-
</TabsList>
149-
150-
<div className="w-full">
151-
<RequestsTab />
152-
<ResourcesTab
153-
resources={resources}
154-
listResources={listResources}
155-
readResource={readResource}
156-
selectedResource={selectedResource}
157-
setSelectedResource={setSelectedResource}
158-
resourceContent={resourceContent}
159-
error={error}
160-
/>
161-
<NotificationsTab />
162-
<PromptsTab
163-
prompts={prompts}
164-
listPrompts={listPrompts}
165-
getPrompt={getPrompt}
166-
selectedPrompt={selectedPrompt}
167-
setSelectedPrompt={setSelectedPrompt}
168-
promptContent={promptContent}
169-
error={error}
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)}
170147
/>
171-
<ToolsTab
172-
tools={tools}
173-
listTools={listTools}
174-
callTool={callTool}
175-
selectedTool={selectedTool}
176-
setSelectedTool={setSelectedTool}
177-
toolResult={toolResult}
178-
error={error}
148+
<Input
149+
placeholder="Arguments (space-separated)"
150+
value={args}
151+
onChange={(e) => setArgs(e.target.value)}
179152
/>
180-
<ConsoleTab />
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}
207+
/>
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}
217+
/>
218+
<ConsoleTab />
219+
</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>
181226
</div>
182-
</Tabs>
227+
)}
183228
</div>
184229
</div>
185230
</div>

server/src/index.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,25 @@ const app = express();
77
const server = http.createServer(app);
88
const wss = new WebSocketServer({ server });
99

10-
const mcpClient = new McpClient("MyApp", "1.0.0");
11-
await mcpClient.connectStdio(
12-
"/Users/ashwin/.nvm/versions/node/v18.20.4/bin/node",
13-
["/Users/ashwin/code/example-servers/build/everything/index.js"],
14-
);
10+
let mcpClient: McpClient | null = null;
1511

1612
wss.on("connection", (ws: WebSocket) => {
1713
ws.on("message", async (message: string) => {
1814
try {
1915
const command = JSON.parse(message);
2016

21-
if (command.type === "listResources") {
17+
if (command.type === "connect" && command.command && command.args) {
18+
mcpClient = new McpClient("MyApp", "1.0.0");
19+
await mcpClient.connectStdio(command.command, command.args);
20+
ws.send(JSON.stringify({ type: "connected" }));
21+
} else if (!mcpClient) {
22+
ws.send(
23+
JSON.stringify({
24+
type: "error",
25+
message: "Not connected to MCP server",
26+
}),
27+
);
28+
} else if (command.type === "listResources") {
2229
const resources = await mcpClient.listResources();
2330
ws.send(JSON.stringify({ type: "resources", data: resources }));
2431
} else if (command.type === "readResource" && command.uri) {
@@ -58,6 +65,8 @@ server.listen(PORT, () => {
5865

5966
// Close the client when the server is shutting down
6067
process.on("SIGINT", async () => {
61-
await mcpClient.close();
68+
if (mcpClient) {
69+
await mcpClient.close();
70+
}
6271
process.exit();
6372
});

0 commit comments

Comments
 (0)