Skip to content

Commit 6575697

Browse files
committed
refactor to not use custom websocket protocol
1 parent 20a2dbe commit 6575697

File tree

9 files changed

+312
-169
lines changed

9 files changed

+312
-169
lines changed

client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"class-variance-authority": "^0.7.0",
1818
"clsx": "^2.1.1",
1919
"lucide-react": "^0.447.0",
20+
"mcp-typescript": "file:../packages/mcp-typescript",
2021
"react": "^18.3.1",
2122
"react-dom": "^18.3.1",
2223
"tailwind-merge": "^2.5.3",

client/src/App.tsx

Lines changed: 116 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
import { useState, useEffect } from "react";
1+
import { Client } from "mcp-typescript/client/index.js";
2+
import { SSEClientTransport } from "mcp-typescript/client/sse.js";
3+
import {
4+
ListResourcesResultSchema,
5+
GetPromptResultSchema,
6+
ListToolsResultSchema,
7+
ReadResourceResultSchema,
8+
CallToolResultSchema,
9+
ListPromptsResultSchema,
10+
} from "mcp-typescript/types.js";
11+
import { useState } from "react";
212
import {
313
Send,
414
Bell,
@@ -22,7 +32,6 @@ import ToolsTab, { Tool as ToolType } from "./components/ToolsTab";
2232
import History from "./components/History";
2333

2434
const App = () => {
25-
const [socket, setSocket] = useState<WebSocket | null>(null);
2635
const [connectionStatus, setConnectionStatus] = useState<
2736
"disconnected" | "connected" | "error"
2837
>("disconnected");
@@ -39,121 +48,132 @@ const App = () => {
3948
const [args, setArgs] = useState<string>(
4049
"/Users/ashwin/code/example-servers/build/everything/index.js",
4150
);
42-
const [mcpConnected, setMcpConnected] = useState<boolean>(false);
4351
const [requestHistory, setRequestHistory] = useState<
44-
Array<{ request: string; response: string | null }>
52+
{ request: string; response: string }[]
4553
>([]);
54+
const [mcpClient, setMcpClient] = useState<Client | null>(null);
4655

47-
useEffect(() => {
48-
const ws = new WebSocket("ws://localhost:3000");
49-
50-
ws.onopen = () => {
51-
console.log("Connected to WebSocket server");
52-
setConnectionStatus("connected");
53-
setSocket(ws);
54-
};
55-
56-
ws.onmessage = (event) => {
57-
const message = JSON.parse(event.data);
58-
console.log("Received message:", message);
59-
if (message.type === "resources") {
60-
setResources(message.data.resources);
61-
setError(null);
62-
} else if (message.type === "resource") {
63-
setResourceContent(JSON.stringify(message.data, null, 2));
64-
setError(null);
65-
} else if (message.type === "prompts") {
66-
setPrompts(message.data.prompts);
67-
setError(null);
68-
} else if (message.type === "prompt") {
69-
setPromptContent(JSON.stringify(message.data, null, 2));
70-
setError(null);
71-
} else if (message.type === "tools") {
72-
setTools(message.data.tools);
73-
setError(null);
74-
} else if (message.type === "toolResult") {
75-
setToolResult(JSON.stringify(message.data, null, 2));
76-
setError(null);
77-
} else if (message.type === "error") {
78-
setError(message.message);
79-
} else if (message.type === "connected") {
80-
setMcpConnected(true);
81-
}
82-
83-
updateRequestHistory(message);
84-
};
85-
86-
ws.onerror = () => {
87-
setConnectionStatus("error");
88-
};
56+
const [selectedResource, setSelectedResource] = useState<Resource | null>(
57+
null,
58+
);
59+
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
60+
const [selectedTool, setSelectedTool] = useState<ToolType | null>(null);
8961

90-
ws.onclose = () => {
91-
setConnectionStatus("disconnected");
92-
setMcpConnected(false);
93-
};
62+
const pushHistory = (request: object, response: object) => {
63+
setRequestHistory((prev) => [
64+
...prev,
65+
{ request: JSON.stringify(request), response: JSON.stringify(response) },
66+
]);
67+
};
9468

95-
return () => ws.close();
96-
}, []);
69+
const makeRequest = async (
70+
request: Parameters<Client["request"]>[0],
71+
schema: Parameters<Client["request"]>[1],
72+
): Promise<ReturnType<Client["request"]>> => {
73+
if (!mcpClient) {
74+
throw new Error("MCP client not connected");
75+
}
9776

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-
});
77+
try {
78+
const response = await mcpClient.request(request, schema);
79+
pushHistory(request, response);
80+
return response;
81+
} catch (e: unknown) {
82+
setError((e as Error).message);
83+
throw e;
84+
}
11185
};
11286

113-
const sendWebSocketMessage = (message: object) => {
114-
if (socket) {
115-
console.log("Sending WebSocket message:", message);
116-
socket.send(JSON.stringify(message));
117-
setRequestHistory((prev) => [
118-
...prev,
119-
{ request: JSON.stringify(message), response: null },
120-
]);
87+
const listResources = async () => {
88+
const response = await makeRequest(
89+
{
90+
method: "resources/list" as const,
91+
},
92+
ListResourcesResultSchema,
93+
);
94+
if (response.resources) {
95+
setResources(response.resources);
12196
}
12297
};
12398

124-
const [selectedResource, setSelectedResource] = useState<Resource | null>(
125-
null,
126-
);
127-
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
128-
const [selectedTool, setSelectedTool] = useState<ToolType | null>(null);
129-
130-
const listResources = () => {
131-
sendWebSocketMessage({ type: "listResources" });
99+
const readResource = async (uri: string) => {
100+
const response = await makeRequest(
101+
{
102+
method: "resources/read" as const,
103+
params: { uri },
104+
},
105+
ReadResourceResultSchema,
106+
);
107+
setResourceContent(JSON.stringify(response, null, 2));
132108
};
133109

134-
const readResource = (uri: string) => {
135-
sendWebSocketMessage({ type: "readResource", uri });
110+
const listPrompts = async () => {
111+
const response = await makeRequest(
112+
{
113+
method: "prompts/list" as const,
114+
},
115+
ListPromptsResultSchema,
116+
);
117+
if (response.prompts) {
118+
setPrompts(response.prompts);
119+
}
136120
};
137121

138-
const listPrompts = () => {
139-
sendWebSocketMessage({ type: "listPrompts" });
122+
const getPrompt = async (name: string, args: Record<string, string> = {}) => {
123+
const response = await makeRequest(
124+
{
125+
method: "prompts/get" as const,
126+
params: { name, arguments: args },
127+
},
128+
GetPromptResultSchema,
129+
);
130+
setPromptContent(JSON.stringify(response, null, 2));
140131
};
141132

142-
const getPrompt = (name: string, args: Record<string, unknown> = {}) => {
143-
sendWebSocketMessage({ type: "getPrompt", name, args });
133+
const listTools = async () => {
134+
const response = await makeRequest(
135+
{
136+
method: "tools/list" as const,
137+
},
138+
ListToolsResultSchema,
139+
);
140+
if (response.tools) {
141+
setTools(response.tools);
142+
}
144143
};
145144

146-
const listTools = () => {
147-
sendWebSocketMessage({ type: "listTools" });
145+
const callTool = async (name: string, params: Record<string, unknown>) => {
146+
const response = await makeRequest(
147+
{
148+
method: "tools/call" as const,
149+
params: { name, arguments: params },
150+
},
151+
CallToolResultSchema,
152+
);
153+
setToolResult(JSON.stringify(response.toolResult, null, 2));
148154
};
149155

150-
const callTool = (name: string, params: Record<string, unknown>) => {
151-
sendWebSocketMessage({ type: "callTool", name, params });
152-
};
156+
const connectMcpServer = async () => {
157+
try {
158+
const client = new Client({
159+
name: "mcp-inspector",
160+
version: "0.0.1",
161+
});
162+
163+
const clientTransport = new SSEClientTransport();
164+
const url = new URL("http://localhost:3000/sse");
165+
url.searchParams.append("command", encodeURIComponent(command));
166+
url.searchParams.append("args", encodeURIComponent(args));
167+
await clientTransport.connect(url);
168+
169+
await client.connect(clientTransport);
153170

154-
const connectMcpServer = () => {
155-
const argsArray = args.split(" ").filter((arg) => arg.trim() !== "");
156-
sendWebSocketMessage({ type: "connect", command, args: argsArray });
171+
setMcpClient(client);
172+
setConnectionStatus("connected");
173+
} catch (e) {
174+
console.error(e);
175+
setConnectionStatus("error");
176+
}
157177
};
158178

159179
return (
@@ -182,7 +202,7 @@ const App = () => {
182202
</Button>
183203
</div>
184204
</div>
185-
{mcpConnected ? (
205+
{mcpClient ? (
186206
<Tabs defaultValue="resources" className="w-full p-4">
187207
<TabsList className="mb-4 p-0">
188208
<TabsTrigger value="resources">

client/src/components/History.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const History = ({
3838
>
3939
<span className="font-mono">
4040
{requestHistory.length - index}.{" "}
41-
{JSON.parse(request.request).type}
41+
{JSON.parse(request.request).method}
4242
</span>
4343
<span>
4444
{expandedRequests[requestHistory.length - 1 - index]

client/src/components/ToolsTab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import ListPane from "./ListPane";
99

1010
export type Tool = {
1111
name: string;
12-
description: string;
12+
description?: string | undefined;
1313
inputSchema: {
1414
type: string;
15-
properties: Record<string, { type: string; description: string }>;
15+
properties?: Record<string, { type: string; description: string }>;
1616
};
1717
};
1818

0 commit comments

Comments
 (0)