@ejazullah/browser-mcp 0.0.59 → 0.0.60
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/lib/mcp/transport.js +64 -3
- package/package.json +1 -1
package/lib/mcp/transport.js
CHANGED
|
@@ -128,7 +128,37 @@ async function handleSSE(serverBackendFactory, req, res, url, sessions) {
|
|
|
128
128
|
res.statusCode = 405;
|
|
129
129
|
res.end('Method not allowed');
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
function cdpSessionKeyFromUrl(value) {
|
|
132
|
+
try {
|
|
133
|
+
const parsed = new URL(value);
|
|
134
|
+
const match = parsed.pathname.match(/\/devtools\/([^/]+)/i);
|
|
135
|
+
return match?.[1];
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
const match = value.match(/\/devtools\/([^/]+)/i);
|
|
139
|
+
return match?.[1];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function resolveClientCdpSessionKey(req) {
|
|
143
|
+
const explicitSessionId = req.headers['x-cdp-session-id'];
|
|
144
|
+
if (explicitSessionId)
|
|
145
|
+
return explicitSessionId;
|
|
146
|
+
const cdpUrl = req.headers['x-cdp-url']
|
|
147
|
+
?? req.headers['x-cdp-endpoint']
|
|
148
|
+
?? req.headers['x-browser-url'];
|
|
149
|
+
if (!cdpUrl)
|
|
150
|
+
return undefined;
|
|
151
|
+
return cdpSessionKeyFromUrl(cdpUrl);
|
|
152
|
+
}
|
|
153
|
+
function findSessionIdByCdpKey(sessionCdpKeys, sessions, cdpSessionKey) {
|
|
154
|
+
for (const [knownSessionId, knownCdpSessionKey] of sessionCdpKeys.entries()) {
|
|
155
|
+
if (knownCdpSessionKey === cdpSessionKey && sessions.has(knownSessionId))
|
|
156
|
+
return knownSessionId;
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
async function handleStreamable(serverBackendFactory, req, res, sessions, sessionCdpKeys) {
|
|
161
|
+
const cdpSessionKey = resolveClientCdpSessionKey(req);
|
|
132
162
|
const sessionId = req.headers['mcp-session-id'];
|
|
133
163
|
if (sessionId) {
|
|
134
164
|
const transport = sessions.get(sessionId);
|
|
@@ -142,14 +172,43 @@ async function handleStreamable(serverBackendFactory, req, res, sessions) {
|
|
|
142
172
|
testDebug(`stale http session id: ${sessionId}, creating a new session`);
|
|
143
173
|
}
|
|
144
174
|
else {
|
|
145
|
-
|
|
175
|
+
if (cdpSessionKey) {
|
|
176
|
+
const knownCdpSessionKey = sessionCdpKeys.get(sessionId);
|
|
177
|
+
if (knownCdpSessionKey && knownCdpSessionKey !== cdpSessionKey) {
|
|
178
|
+
delete req.headers['mcp-session-id'];
|
|
179
|
+
testDebug(`browser changed for mcp session ${sessionId} (${knownCdpSessionKey} -> ${cdpSessionKey}), creating a new session`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
if (!knownCdpSessionKey)
|
|
183
|
+
sessionCdpKeys.set(sessionId, cdpSessionKey);
|
|
184
|
+
return await transport.handleRequest(req, res);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
return await transport.handleRequest(req, res);
|
|
189
|
+
}
|
|
146
190
|
}
|
|
147
191
|
}
|
|
192
|
+
if (!sessionId && cdpSessionKey && req.method === 'POST') {
|
|
193
|
+
const matchedSessionId = findSessionIdByCdpKey(sessionCdpKeys, sessions, cdpSessionKey);
|
|
194
|
+
if (matchedSessionId) {
|
|
195
|
+
const matchedTransport = sessions.get(matchedSessionId);
|
|
196
|
+
testDebug(`missing mcp-session-id, reusing cdp-matched session: ${matchedSessionId} (${cdpSessionKey})`);
|
|
197
|
+
return await matchedTransport.handleRequest(req, res);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!sessionId && req.method === 'POST' && sessions.size === 1) {
|
|
201
|
+
const [singleSessionId, singleTransport] = sessions.entries().next().value;
|
|
202
|
+
testDebug(`missing mcp-session-id, reusing single active session: ${singleSessionId}`);
|
|
203
|
+
return await singleTransport.handleRequest(req, res);
|
|
204
|
+
}
|
|
148
205
|
if (req.method === 'POST') {
|
|
149
206
|
const transport = new StreamableHTTPServerTransport({
|
|
150
207
|
sessionIdGenerator: () => crypto.randomUUID(),
|
|
151
208
|
onsessioninitialized: async (sessionId) => {
|
|
152
209
|
testDebug(`create http session: ${transport.sessionId}`);
|
|
210
|
+
if (cdpSessionKey)
|
|
211
|
+
sessionCdpKeys.set(sessionId, cdpSessionKey);
|
|
153
212
|
await mcpServer.connect(serverBackendFactory, transport, true);
|
|
154
213
|
sessions.set(sessionId, transport);
|
|
155
214
|
}
|
|
@@ -158,6 +217,7 @@ async function handleStreamable(serverBackendFactory, req, res, sessions) {
|
|
|
158
217
|
if (!transport.sessionId)
|
|
159
218
|
return;
|
|
160
219
|
sessions.delete(transport.sessionId);
|
|
220
|
+
sessionCdpKeys.delete(transport.sessionId);
|
|
161
221
|
testDebug(`delete http session: ${transport.sessionId}`);
|
|
162
222
|
};
|
|
163
223
|
await transport.handleRequest(req, res);
|
|
@@ -169,6 +229,7 @@ async function handleStreamable(serverBackendFactory, req, res, sessions) {
|
|
|
169
229
|
function startHttpTransport(httpServer, serverBackendFactory, auth) {
|
|
170
230
|
const sseSessions = new Map();
|
|
171
231
|
const streamableSessions = new Map();
|
|
232
|
+
const streamableSessionCdpKeys = new Map();
|
|
172
233
|
// Configure CORS with permissive settings for MCP tools
|
|
173
234
|
const corsHandler = cors({
|
|
174
235
|
origin: true, // Allow all origins
|
|
@@ -193,7 +254,7 @@ function startHttpTransport(httpServer, serverBackendFactory, auth) {
|
|
|
193
254
|
if (url.pathname.startsWith('/sse'))
|
|
194
255
|
await handleSSE(serverBackendFactory, req, res, url, sseSessions);
|
|
195
256
|
else
|
|
196
|
-
await handleStreamable(serverBackendFactory, req, res, streamableSessions);
|
|
257
|
+
await handleStreamable(serverBackendFactory, req, res, streamableSessions, streamableSessionCdpKeys);
|
|
197
258
|
});
|
|
198
259
|
});
|
|
199
260
|
const url = httpAddressToString(httpServer.address());
|