mcp-proxy 2.12.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1
- package/dist/bin/mcp-proxy.js +37 -20
- package/dist/bin/mcp-proxy.js.map +1 -1
- package/dist/chunk-BLDWVJ5G.js +461 -0
- package/dist/chunk-BLDWVJ5G.js.map +1 -0
- package/dist/index.d.ts +71 -19
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/eslint.config.js +8 -1
- package/package.json +12 -12
- package/src/InMemoryEventStore.ts +91 -0
- package/src/bin/mcp-proxy.ts +38 -20
- package/src/index.ts +3 -1
- package/src/startHTTPStreamServer.test.ts +127 -0
- package/src/startHTTPStreamServer.ts +280 -0
- package/dist/chunk-HTMOSQ6H.js +0 -202
- package/dist/chunk-HTMOSQ6H.js.map +0 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EventStore,
|
|
3
|
+
StreamableHTTPServerTransport,
|
|
4
|
+
} from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import http from "http";
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { InMemoryEventStore } from "./InMemoryEventStore.js";
|
|
10
|
+
|
|
11
|
+
export type SSEServer = {
|
|
12
|
+
close: () => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type ServerLike = {
|
|
16
|
+
connect: Server["connect"];
|
|
17
|
+
close: Server["close"];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const startHTTPStreamServer = async <T extends ServerLike>({
|
|
21
|
+
port,
|
|
22
|
+
createServer,
|
|
23
|
+
endpoint,
|
|
24
|
+
eventStore,
|
|
25
|
+
onConnect,
|
|
26
|
+
onClose,
|
|
27
|
+
onUnhandledRequest,
|
|
28
|
+
}: {
|
|
29
|
+
port: number;
|
|
30
|
+
endpoint: string;
|
|
31
|
+
createServer: (request: http.IncomingMessage) => Promise<T>;
|
|
32
|
+
eventStore?: EventStore;
|
|
33
|
+
onConnect?: (server: T) => void;
|
|
34
|
+
onClose?: (server: T) => void;
|
|
35
|
+
onUnhandledRequest?: (
|
|
36
|
+
req: http.IncomingMessage,
|
|
37
|
+
res: http.ServerResponse
|
|
38
|
+
) => Promise<void>;
|
|
39
|
+
}): Promise<SSEServer> => {
|
|
40
|
+
const activeTransports: Record<
|
|
41
|
+
string,
|
|
42
|
+
{
|
|
43
|
+
transport: StreamableHTTPServerTransport;
|
|
44
|
+
server: T;
|
|
45
|
+
}
|
|
46
|
+
> = {};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @author https://dev.classmethod.jp/articles/mcp-sse/
|
|
50
|
+
*/
|
|
51
|
+
const httpServer = http.createServer(async (req, res) => {
|
|
52
|
+
if (req.headers.origin) {
|
|
53
|
+
try {
|
|
54
|
+
const origin = new URL(req.headers.origin);
|
|
55
|
+
|
|
56
|
+
res.setHeader("Access-Control-Allow-Origin", origin.origin);
|
|
57
|
+
res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
58
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
59
|
+
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error("Error parsing origin:", error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (req.method === "OPTIONS") {
|
|
66
|
+
res.writeHead(204);
|
|
67
|
+
res.end();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (req.method === "GET" && req.url === `/ping`) {
|
|
72
|
+
res.writeHead(200).end("pong");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (
|
|
77
|
+
req.method === "POST" &&
|
|
78
|
+
new URL(req.url!, "http://localhost").pathname === endpoint
|
|
79
|
+
) {
|
|
80
|
+
try {
|
|
81
|
+
const sessionId = Array.isArray(req.headers["mcp-session-id"])
|
|
82
|
+
? req.headers["mcp-session-id"][0]
|
|
83
|
+
: req.headers["mcp-session-id"];
|
|
84
|
+
let transport: StreamableHTTPServerTransport;
|
|
85
|
+
let server: T;
|
|
86
|
+
|
|
87
|
+
const body = await getBody(req);
|
|
88
|
+
|
|
89
|
+
if (sessionId && activeTransports[sessionId]) {
|
|
90
|
+
transport = activeTransports[sessionId].transport;
|
|
91
|
+
server = activeTransports[sessionId].server;
|
|
92
|
+
} else if (!sessionId && isInitializeRequest(body)) {
|
|
93
|
+
// Create a new transport for the session
|
|
94
|
+
transport = new StreamableHTTPServerTransport({
|
|
95
|
+
sessionIdGenerator: randomUUID,
|
|
96
|
+
eventStore: eventStore || new InMemoryEventStore(),
|
|
97
|
+
onsessioninitialized: (_sessionId) => {
|
|
98
|
+
// add only when the id Sesison id is generated
|
|
99
|
+
activeTransports[_sessionId] = {
|
|
100
|
+
transport,
|
|
101
|
+
server,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Handle the server close event
|
|
107
|
+
transport.onclose = async () => {
|
|
108
|
+
const sid = transport.sessionId;
|
|
109
|
+
if (sid && activeTransports[sid]) {
|
|
110
|
+
onClose?.(server);
|
|
111
|
+
try {
|
|
112
|
+
await server.close();
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error("Error closing server:", error);
|
|
115
|
+
}
|
|
116
|
+
delete activeTransports[sid];
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Create the server
|
|
121
|
+
try {
|
|
122
|
+
server = await createServer(req);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (error instanceof Response) {
|
|
125
|
+
res.writeHead(error.status).end(error.statusText);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
res.writeHead(500).end("Error creating server");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
server.connect(transport);
|
|
133
|
+
onConnect?.(server);
|
|
134
|
+
|
|
135
|
+
await transport.handleRequest(req, res, body);
|
|
136
|
+
return;
|
|
137
|
+
} else {
|
|
138
|
+
// Error if the server is not created but the request is not an initialize request
|
|
139
|
+
res.setHeader("Content-Type", "application/json");
|
|
140
|
+
res.writeHead(400).end(
|
|
141
|
+
JSON.stringify({
|
|
142
|
+
jsonrpc: "2.0",
|
|
143
|
+
error: {
|
|
144
|
+
code: -32000,
|
|
145
|
+
message: "Bad Request: No valid session ID provided",
|
|
146
|
+
},
|
|
147
|
+
id: null,
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Handle ther request if the server is already created
|
|
155
|
+
await transport.handleRequest(req, res, body);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error("Error handling request:", error);
|
|
158
|
+
res.setHeader("Content-Type", "application/json");
|
|
159
|
+
res.writeHead(500).end(
|
|
160
|
+
JSON.stringify({
|
|
161
|
+
jsonrpc: "2.0",
|
|
162
|
+
error: { code: -32603, message: "Internal Server Error" },
|
|
163
|
+
id: null,
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (
|
|
171
|
+
req.method === "GET" &&
|
|
172
|
+
new URL(req.url!, "http://localhost").pathname === endpoint
|
|
173
|
+
) {
|
|
174
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
175
|
+
const activeTransport:
|
|
176
|
+
| {
|
|
177
|
+
transport: StreamableHTTPServerTransport;
|
|
178
|
+
server: T;
|
|
179
|
+
}
|
|
180
|
+
| undefined = sessionId ? activeTransports[sessionId] : undefined;
|
|
181
|
+
|
|
182
|
+
if (!sessionId) {
|
|
183
|
+
res.writeHead(400).end("No sessionId");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!activeTransport) {
|
|
188
|
+
res.writeHead(400).end("No active transport");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const lastEventId = req.headers["last-event-id"] as string | undefined;
|
|
193
|
+
if (lastEventId) {
|
|
194
|
+
console.log(`Client reconnecting with Last-Event-ID: ${lastEventId}`);
|
|
195
|
+
} else {
|
|
196
|
+
console.log(`Establishing new SSE stream for session ${sessionId}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
await activeTransport.transport.handleRequest(req, res);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (
|
|
204
|
+
req.method === "DELETE" &&
|
|
205
|
+
new URL(req.url!, "http://localhost").pathname === endpoint
|
|
206
|
+
) {
|
|
207
|
+
console.log("received delete request");
|
|
208
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
209
|
+
if (!sessionId) {
|
|
210
|
+
res.writeHead(400).end("Invalid or missing sessionId");
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
console.log("received delete request for session", sessionId);
|
|
215
|
+
|
|
216
|
+
const { transport, server } = activeTransports[sessionId];
|
|
217
|
+
if (!transport) {
|
|
218
|
+
res.writeHead(400).end("No active transport");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
await transport.handleRequest(req, res);
|
|
224
|
+
onClose?.(server);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error("Error handling delete request:", error);
|
|
227
|
+
res.writeHead(500).end("Error handling delete request");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (onUnhandledRequest) {
|
|
234
|
+
await onUnhandledRequest(req, res);
|
|
235
|
+
} else {
|
|
236
|
+
res.writeHead(404).end();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await new Promise((resolve) => {
|
|
241
|
+
httpServer.listen(port, "::", () => {
|
|
242
|
+
resolve(undefined);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
close: async () => {
|
|
248
|
+
for (const transport of Object.values(activeTransports)) {
|
|
249
|
+
await transport.transport.close();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
httpServer.close((error) => {
|
|
254
|
+
if (error) {
|
|
255
|
+
reject(error);
|
|
256
|
+
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
resolve();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
function getBody(request: http.IncomingMessage) {
|
|
268
|
+
return new Promise((resolve) => {
|
|
269
|
+
const bodyParts: Buffer[] = [];
|
|
270
|
+
let body: string;
|
|
271
|
+
request
|
|
272
|
+
.on("data", (chunk) => {
|
|
273
|
+
bodyParts.push(chunk);
|
|
274
|
+
})
|
|
275
|
+
.on("end", () => {
|
|
276
|
+
body = Buffer.concat(bodyParts).toString();
|
|
277
|
+
resolve(JSON.parse(body));
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}
|
package/dist/chunk-HTMOSQ6H.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
// src/proxyServer.ts
|
|
2
|
-
import {
|
|
3
|
-
CallToolRequestSchema,
|
|
4
|
-
CompleteRequestSchema,
|
|
5
|
-
GetPromptRequestSchema,
|
|
6
|
-
ListPromptsRequestSchema,
|
|
7
|
-
ListResourcesRequestSchema,
|
|
8
|
-
ListResourceTemplatesRequestSchema,
|
|
9
|
-
ListToolsRequestSchema,
|
|
10
|
-
LoggingMessageNotificationSchema,
|
|
11
|
-
ReadResourceRequestSchema,
|
|
12
|
-
SubscribeRequestSchema,
|
|
13
|
-
UnsubscribeRequestSchema,
|
|
14
|
-
ResourceUpdatedNotificationSchema
|
|
15
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
-
var proxyServer = async ({
|
|
17
|
-
server,
|
|
18
|
-
client,
|
|
19
|
-
serverCapabilities
|
|
20
|
-
}) => {
|
|
21
|
-
if (serverCapabilities?.logging) {
|
|
22
|
-
server.setNotificationHandler(
|
|
23
|
-
LoggingMessageNotificationSchema,
|
|
24
|
-
async (args) => {
|
|
25
|
-
return client.notification(args);
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
if (serverCapabilities?.prompts) {
|
|
30
|
-
server.setRequestHandler(GetPromptRequestSchema, async (args) => {
|
|
31
|
-
return client.getPrompt(args.params);
|
|
32
|
-
});
|
|
33
|
-
server.setRequestHandler(ListPromptsRequestSchema, async (args) => {
|
|
34
|
-
return client.listPrompts(args.params);
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
if (serverCapabilities?.resources) {
|
|
38
|
-
server.setRequestHandler(ListResourcesRequestSchema, async (args) => {
|
|
39
|
-
return client.listResources(args.params);
|
|
40
|
-
});
|
|
41
|
-
server.setRequestHandler(
|
|
42
|
-
ListResourceTemplatesRequestSchema,
|
|
43
|
-
async (args) => {
|
|
44
|
-
return client.listResourceTemplates(args.params);
|
|
45
|
-
}
|
|
46
|
-
);
|
|
47
|
-
server.setRequestHandler(ReadResourceRequestSchema, async (args) => {
|
|
48
|
-
return client.readResource(args.params);
|
|
49
|
-
});
|
|
50
|
-
if (serverCapabilities?.resources.subscribe) {
|
|
51
|
-
server.setNotificationHandler(
|
|
52
|
-
ResourceUpdatedNotificationSchema,
|
|
53
|
-
async (args) => {
|
|
54
|
-
return client.notification(args);
|
|
55
|
-
}
|
|
56
|
-
);
|
|
57
|
-
server.setRequestHandler(SubscribeRequestSchema, async (args) => {
|
|
58
|
-
return client.subscribeResource(args.params);
|
|
59
|
-
});
|
|
60
|
-
server.setRequestHandler(UnsubscribeRequestSchema, async (args) => {
|
|
61
|
-
return client.unsubscribeResource(args.params);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (serverCapabilities?.tools) {
|
|
66
|
-
server.setRequestHandler(CallToolRequestSchema, async (args) => {
|
|
67
|
-
return client.callTool(args.params);
|
|
68
|
-
});
|
|
69
|
-
server.setRequestHandler(ListToolsRequestSchema, async (args) => {
|
|
70
|
-
return client.listTools(args.params);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
server.setRequestHandler(CompleteRequestSchema, async (args) => {
|
|
74
|
-
return client.complete(args.params);
|
|
75
|
-
});
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// src/startSSEServer.ts
|
|
79
|
-
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
80
|
-
import http from "http";
|
|
81
|
-
var startSSEServer = async ({
|
|
82
|
-
port,
|
|
83
|
-
createServer,
|
|
84
|
-
endpoint,
|
|
85
|
-
onConnect,
|
|
86
|
-
onClose,
|
|
87
|
-
onUnhandledRequest
|
|
88
|
-
}) => {
|
|
89
|
-
const activeTransports = {};
|
|
90
|
-
const httpServer = http.createServer(async (req, res) => {
|
|
91
|
-
if (req.headers.origin) {
|
|
92
|
-
try {
|
|
93
|
-
const origin = new URL(req.headers.origin);
|
|
94
|
-
res.setHeader("Access-Control-Allow-Origin", origin.origin);
|
|
95
|
-
res.setHeader("Access-Control-Allow-Credentials", "true");
|
|
96
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
97
|
-
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error("Error parsing origin:", error);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (req.method === "OPTIONS") {
|
|
103
|
-
res.writeHead(204);
|
|
104
|
-
res.end();
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (req.method === "GET" && req.url === `/ping`) {
|
|
108
|
-
res.writeHead(200).end("pong");
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (req.method === "GET" && new URL(req.url, "http://localhost").pathname === endpoint) {
|
|
112
|
-
const transport = new SSEServerTransport("/messages", res);
|
|
113
|
-
let server;
|
|
114
|
-
try {
|
|
115
|
-
server = await createServer(req);
|
|
116
|
-
} catch (error) {
|
|
117
|
-
if (error instanceof Response) {
|
|
118
|
-
res.writeHead(error.status).end(error.statusText);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
res.writeHead(500).end("Error creating server");
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
activeTransports[transport.sessionId] = transport;
|
|
125
|
-
let closed = false;
|
|
126
|
-
res.on("close", async () => {
|
|
127
|
-
closed = true;
|
|
128
|
-
try {
|
|
129
|
-
await server.close();
|
|
130
|
-
} catch (error) {
|
|
131
|
-
console.error("Error closing server:", error);
|
|
132
|
-
}
|
|
133
|
-
delete activeTransports[transport.sessionId];
|
|
134
|
-
onClose?.(server);
|
|
135
|
-
});
|
|
136
|
-
try {
|
|
137
|
-
await server.connect(transport);
|
|
138
|
-
await transport.send({
|
|
139
|
-
jsonrpc: "2.0",
|
|
140
|
-
method: "sse/connection",
|
|
141
|
-
params: { message: "SSE Connection established" }
|
|
142
|
-
});
|
|
143
|
-
onConnect?.(server);
|
|
144
|
-
} catch (error) {
|
|
145
|
-
if (!closed) {
|
|
146
|
-
console.error("Error connecting to server:", error);
|
|
147
|
-
res.writeHead(500).end("Error connecting to server");
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
if (req.method === "POST" && req.url?.startsWith("/messages")) {
|
|
153
|
-
const sessionId = new URL(
|
|
154
|
-
req.url,
|
|
155
|
-
"https://example.com"
|
|
156
|
-
).searchParams.get("sessionId");
|
|
157
|
-
if (!sessionId) {
|
|
158
|
-
res.writeHead(400).end("No sessionId");
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
const activeTransport = activeTransports[sessionId];
|
|
162
|
-
if (!activeTransport) {
|
|
163
|
-
res.writeHead(400).end("No active transport");
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
await activeTransport.handlePostMessage(req, res);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
if (onUnhandledRequest) {
|
|
170
|
-
await onUnhandledRequest(req, res);
|
|
171
|
-
} else {
|
|
172
|
-
res.writeHead(404).end();
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
await new Promise((resolve) => {
|
|
176
|
-
httpServer.listen(port, "::", () => {
|
|
177
|
-
resolve(void 0);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
return {
|
|
181
|
-
close: async () => {
|
|
182
|
-
for (const transport of Object.values(activeTransports)) {
|
|
183
|
-
await transport.close();
|
|
184
|
-
}
|
|
185
|
-
return new Promise((resolve, reject) => {
|
|
186
|
-
httpServer.close((error) => {
|
|
187
|
-
if (error) {
|
|
188
|
-
reject(error);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
resolve();
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
export {
|
|
199
|
-
proxyServer,
|
|
200
|
-
startSSEServer
|
|
201
|
-
};
|
|
202
|
-
//# sourceMappingURL=chunk-HTMOSQ6H.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/proxyServer.ts","../src/startSSEServer.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport {\n CallToolRequestSchema,\n CompleteRequestSchema,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListResourceTemplatesRequestSchema,\n ListToolsRequestSchema,\n LoggingMessageNotificationSchema,\n ReadResourceRequestSchema,\n SubscribeRequestSchema,\n UnsubscribeRequestSchema,\n ResourceUpdatedNotificationSchema,\n ServerCapabilities,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\n\nexport const proxyServer = async ({\n server,\n client,\n serverCapabilities,\n}: {\n server: Server;\n client: Client;\n serverCapabilities: ServerCapabilities;\n}) => {\n if (serverCapabilities?.logging) {\n server.setNotificationHandler(\n LoggingMessageNotificationSchema,\n async (args) => {\n return client.notification(args);\n },\n );\n }\n\n if (serverCapabilities?.prompts) {\n server.setRequestHandler(GetPromptRequestSchema, async (args) => {\n return client.getPrompt(args.params);\n });\n\n server.setRequestHandler(ListPromptsRequestSchema, async (args) => {\n return client.listPrompts(args.params);\n });\n }\n\n if (serverCapabilities?.resources) {\n server.setRequestHandler(ListResourcesRequestSchema, async (args) => {\n return client.listResources(args.params);\n });\n\n server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n async (args) => {\n return client.listResourceTemplates(args.params);\n },\n );\n\n server.setRequestHandler(ReadResourceRequestSchema, async (args) => {\n return client.readResource(args.params);\n });\n\n if (serverCapabilities?.resources.subscribe) {\n server.setNotificationHandler(\n ResourceUpdatedNotificationSchema,\n async (args) => {\n return client.notification(args);\n },\n );\n\n server.setRequestHandler(SubscribeRequestSchema, async (args) => {\n return client.subscribeResource(args.params);\n });\n\n server.setRequestHandler(UnsubscribeRequestSchema, async (args) => {\n return client.unsubscribeResource(args.params);\n });\n }\n }\n\n if (serverCapabilities?.tools) {\n server.setRequestHandler(CallToolRequestSchema, async (args) => {\n return client.callTool(args.params);\n });\n\n server.setRequestHandler(ListToolsRequestSchema, async (args) => {\n return client.listTools(args.params);\n });\n }\n\n server.setRequestHandler(CompleteRequestSchema, async (args) => {\n return client.complete(args.params);\n });\n};\n","import { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport http from \"http\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\n\nexport type SSEServer = {\n close: () => Promise<void>;\n};\n\ntype ServerLike = {\n connect: Server[\"connect\"];\n close: Server[\"close\"];\n};\n\nexport const startSSEServer = async <T extends ServerLike>({\n port,\n createServer,\n endpoint,\n onConnect,\n onClose,\n onUnhandledRequest,\n}: {\n port: number;\n endpoint: string;\n createServer: (request: http.IncomingMessage) => Promise<T>;\n onConnect?: (server: T) => void;\n onClose?: (server: T) => void;\n onUnhandledRequest?: (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ) => Promise<void>;\n}): Promise<SSEServer> => {\n const activeTransports: Record<string, SSEServerTransport> = {};\n\n /**\n * @author https://dev.classmethod.jp/articles/mcp-sse/\n */\n const httpServer = http.createServer(async (req, res) => {\n if (req.headers.origin) {\n try {\n const origin = new URL(req.headers.origin);\n\n res.setHeader(\"Access-Control-Allow-Origin\", origin.origin);\n res.setHeader(\"Access-Control-Allow-Credentials\", \"true\");\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"*\");\n } catch (error) {\n console.error(\"Error parsing origin:\", error);\n }\n }\n\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method === \"GET\" && req.url === `/ping`) {\n res.writeHead(200).end(\"pong\");\n\n return;\n }\n\n if (req.method === \"GET\" && new URL(req.url!, \"http://localhost\").pathname === endpoint) {\n const transport = new SSEServerTransport(\"/messages\", res);\n\n let server: T;\n\n try {\n server = await createServer(req);\n } catch (error) {\n if (error instanceof Response) {\n res.writeHead(error.status).end(error.statusText);\n\n return;\n }\n\n res.writeHead(500).end(\"Error creating server\");\n\n return;\n }\n\n activeTransports[transport.sessionId] = transport;\n\n let closed = false;\n\n res.on(\"close\", async () => {\n closed = true;\n\n try {\n await server.close();\n } catch (error) {\n console.error(\"Error closing server:\", error);\n }\n\n delete activeTransports[transport.sessionId];\n\n onClose?.(server);\n });\n\n try {\n await server.connect(transport);\n\n await transport.send({\n jsonrpc: \"2.0\",\n method: \"sse/connection\",\n params: { message: \"SSE Connection established\" },\n });\n\n onConnect?.(server);\n } catch (error) {\n if (!closed) {\n console.error(\"Error connecting to server:\", error);\n\n res.writeHead(500).end(\"Error connecting to server\");\n }\n }\n\n return;\n }\n\n if (req.method === \"POST\" && req.url?.startsWith(\"/messages\")) {\n const sessionId = new URL(\n req.url,\n \"https://example.com\",\n ).searchParams.get(\"sessionId\");\n\n if (!sessionId) {\n res.writeHead(400).end(\"No sessionId\");\n\n return;\n }\n\n const activeTransport: SSEServerTransport | undefined =\n activeTransports[sessionId];\n\n if (!activeTransport) {\n res.writeHead(400).end(\"No active transport\");\n\n return;\n }\n\n await activeTransport.handlePostMessage(req, res);\n\n return;\n }\n\n if (onUnhandledRequest) {\n await onUnhandledRequest(req, res);\n } else {\n res.writeHead(404).end();\n }\n });\n\n await new Promise((resolve) => {\n httpServer.listen(port, \"::\", () => {\n resolve(undefined);\n });\n });\n\n return {\n close: async () => {\n for (const transport of Object.values(activeTransports)) {\n await transport.close();\n }\n\n return new Promise((resolve, reject) => {\n httpServer.close((error) => {\n if (error) {\n reject(error);\n\n return;\n }\n\n resolve();\n });\n });\n },\n };\n};\n"],"mappings":"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","names":[]}
|