mcp-proxy 5.10.0 → 5.11.1
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/dist/bin/mcp-proxy.js +1 -1
- package/dist/index.d.ts +15 -2
- package/dist/index.js +1 -1
- package/dist/{stdio-DF5lH8jj.js → stdio-DLSsHME0.js} +77 -21
- package/dist/stdio-DLSsHME0.js.map +1 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/InMemoryEventStore.test.ts +72 -0
- package/src/InMemoryEventStore.ts +19 -2
- package/src/authentication.test.ts +145 -7
- package/src/authentication.ts +51 -5
- package/src/proxyServer.ts +8 -6
- package/src/startHTTPServer.test.ts +144 -0
- package/src/startHTTPServer.ts +106 -22
- package/dist/stdio-DF5lH8jj.js.map +0 -1
package/src/startHTTPServer.ts
CHANGED
|
@@ -61,22 +61,82 @@ const createJsonRpcErrorResponse = (code: number, message: string) => {
|
|
|
61
61
|
// Helper function to get WWW-Authenticate header value
|
|
62
62
|
const getWWWAuthenticateHeader = (
|
|
63
63
|
oauth?: AuthConfig["oauth"],
|
|
64
|
+
options?: {
|
|
65
|
+
error?: string;
|
|
66
|
+
error_description?: string;
|
|
67
|
+
error_uri?: string;
|
|
68
|
+
scope?: string;
|
|
69
|
+
},
|
|
64
70
|
): string | undefined => {
|
|
65
|
-
if (!oauth
|
|
71
|
+
if (!oauth) {
|
|
66
72
|
return undefined;
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
const params: string[] = [];
|
|
76
|
+
|
|
77
|
+
// Add realm if configured
|
|
78
|
+
if (oauth.realm) {
|
|
79
|
+
params.push(`realm="${oauth.realm}"`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Add resource_metadata if configured
|
|
83
|
+
if (oauth.protectedResource?.resource) {
|
|
84
|
+
params.push(`resource_metadata="${oauth.protectedResource.resource}/.well-known/oauth-protected-resource"`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Add error from options or config (options takes precedence)
|
|
88
|
+
const error = options?.error || oauth.error;
|
|
89
|
+
if (error) {
|
|
90
|
+
params.push(`error="${error}"`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Add error_description from options or config (options takes precedence)
|
|
94
|
+
const error_description = options?.error_description || oauth.error_description;
|
|
95
|
+
if (error_description) {
|
|
96
|
+
// Escape quotes in error description
|
|
97
|
+
const escaped = error_description.replace(/"/g, '\\"');
|
|
98
|
+
params.push(`error_description="${escaped}"`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Add error_uri from options or config (options takes precedence)
|
|
102
|
+
const error_uri = options?.error_uri || oauth.error_uri;
|
|
103
|
+
if (error_uri) {
|
|
104
|
+
params.push(`error_uri="${error_uri}"`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Add scope from options or config (options takes precedence)
|
|
108
|
+
const scope = options?.scope || oauth.scope;
|
|
109
|
+
if (scope) {
|
|
110
|
+
params.push(`scope="${scope}"`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Return undefined if no parameters were added
|
|
114
|
+
if (params.length === 0) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return `Bearer ${params.join(", ")}`;
|
|
70
119
|
};
|
|
71
120
|
|
|
72
121
|
// Helper function to handle Response errors and send appropriate HTTP response
|
|
73
|
-
const handleResponseError = (
|
|
122
|
+
const handleResponseError = async (
|
|
74
123
|
error: unknown,
|
|
75
124
|
res: http.ServerResponse,
|
|
76
|
-
): boolean => {
|
|
77
|
-
if
|
|
125
|
+
): Promise<boolean> => {
|
|
126
|
+
// Check if it's a Response-like object (duck typing)
|
|
127
|
+
// The instanceof check may fail due to different Response implementations across module boundaries
|
|
128
|
+
const isResponseLike = error &&
|
|
129
|
+
typeof error === 'object' &&
|
|
130
|
+
'status' in error &&
|
|
131
|
+
'headers' in error &&
|
|
132
|
+
'statusText' in error;
|
|
133
|
+
|
|
134
|
+
if (isResponseLike || error instanceof Response) {
|
|
135
|
+
const responseError = error as Response;
|
|
136
|
+
|
|
137
|
+
// Convert Headers to http.OutgoingHttpHeaders format
|
|
78
138
|
const fixedHeaders: http.OutgoingHttpHeaders = {};
|
|
79
|
-
|
|
139
|
+
responseError.headers.forEach((value, key) => {
|
|
80
140
|
if (fixedHeaders[key]) {
|
|
81
141
|
if (Array.isArray(fixedHeaders[key])) {
|
|
82
142
|
(fixedHeaders[key] as string[]).push(value);
|
|
@@ -87,11 +147,16 @@ const handleResponseError = (
|
|
|
87
147
|
fixedHeaders[key] = value;
|
|
88
148
|
}
|
|
89
149
|
});
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
150
|
+
|
|
151
|
+
// Read the body from the Response object
|
|
152
|
+
const body = await responseError.text();
|
|
153
|
+
|
|
154
|
+
res.writeHead(responseError.status, responseError.statusText, fixedHeaders);
|
|
155
|
+
res.end(body);
|
|
156
|
+
|
|
93
157
|
return true;
|
|
94
158
|
}
|
|
159
|
+
|
|
95
160
|
return false;
|
|
96
161
|
};
|
|
97
162
|
|
|
@@ -260,7 +325,10 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
260
325
|
res.setHeader("Content-Type", "application/json");
|
|
261
326
|
|
|
262
327
|
// Add WWW-Authenticate header if OAuth config is available
|
|
263
|
-
const wwwAuthHeader = getWWWAuthenticateHeader(oauth
|
|
328
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth, {
|
|
329
|
+
error: "invalid_token",
|
|
330
|
+
error_description: errorMessage,
|
|
331
|
+
});
|
|
264
332
|
if (wwwAuthHeader) {
|
|
265
333
|
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
266
334
|
}
|
|
@@ -278,13 +346,21 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
278
346
|
return true;
|
|
279
347
|
}
|
|
280
348
|
} catch (error) {
|
|
349
|
+
// Check if error is a Response object with headers already set
|
|
350
|
+
if (await handleResponseError(error, res)) {
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
|
|
281
354
|
// Extract error details from thrown errors
|
|
282
355
|
const errorMessage = error instanceof Error ? error.message : "Unauthorized: Authentication error";
|
|
283
356
|
console.error("Authentication error:", error);
|
|
284
357
|
res.setHeader("Content-Type", "application/json");
|
|
285
358
|
|
|
286
359
|
// Add WWW-Authenticate header if OAuth config is available
|
|
287
|
-
const wwwAuthHeader = getWWWAuthenticateHeader(oauth
|
|
360
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth, {
|
|
361
|
+
error: "invalid_token",
|
|
362
|
+
error_description: errorMessage,
|
|
363
|
+
});
|
|
288
364
|
if (wwwAuthHeader) {
|
|
289
365
|
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
290
366
|
}
|
|
@@ -357,6 +433,11 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
357
433
|
try {
|
|
358
434
|
server = await createServer(req);
|
|
359
435
|
} catch (error) {
|
|
436
|
+
// Check if error is a Response object with headers already set
|
|
437
|
+
if (await handleResponseError(error, res)) {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
|
|
360
441
|
// Detect authentication errors and return HTTP 401
|
|
361
442
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
362
443
|
const isAuthError = errorMessage.includes('Authentication') ||
|
|
@@ -368,7 +449,10 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
368
449
|
res.setHeader("Content-Type", "application/json");
|
|
369
450
|
|
|
370
451
|
// Add WWW-Authenticate header if OAuth config is available
|
|
371
|
-
const wwwAuthHeader = getWWWAuthenticateHeader(oauth
|
|
452
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth, {
|
|
453
|
+
error: "invalid_token",
|
|
454
|
+
error_description: errorMessage,
|
|
455
|
+
});
|
|
372
456
|
if (wwwAuthHeader) {
|
|
373
457
|
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
374
458
|
}
|
|
@@ -384,10 +468,6 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
384
468
|
return true;
|
|
385
469
|
}
|
|
386
470
|
|
|
387
|
-
if (handleResponseError(error, res)) {
|
|
388
|
-
return true;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
471
|
res.writeHead(500).end("Error creating server");
|
|
392
472
|
|
|
393
473
|
return true;
|
|
@@ -416,6 +496,11 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
416
496
|
try {
|
|
417
497
|
server = await createServer(req);
|
|
418
498
|
} catch (error) {
|
|
499
|
+
// Check if error is a Response object with headers already set
|
|
500
|
+
if (await handleResponseError(error, res)) {
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
|
|
419
504
|
// Detect authentication errors and return HTTP 401
|
|
420
505
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
421
506
|
const isAuthError = errorMessage.includes('Authentication') ||
|
|
@@ -427,7 +512,10 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
427
512
|
res.setHeader("Content-Type", "application/json");
|
|
428
513
|
|
|
429
514
|
// Add WWW-Authenticate header if OAuth config is available
|
|
430
|
-
const wwwAuthHeader = getWWWAuthenticateHeader(oauth
|
|
515
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth, {
|
|
516
|
+
error: "invalid_token",
|
|
517
|
+
error_description: errorMessage,
|
|
518
|
+
});
|
|
431
519
|
if (wwwAuthHeader) {
|
|
432
520
|
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
433
521
|
}
|
|
@@ -443,10 +531,6 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
443
531
|
return true;
|
|
444
532
|
}
|
|
445
533
|
|
|
446
|
-
if (handleResponseError(error, res)) {
|
|
447
|
-
return true;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
534
|
res.writeHead(500).end("Error creating server");
|
|
451
535
|
|
|
452
536
|
return true;
|
|
@@ -601,7 +685,7 @@ const handleSSERequest = async <T extends ServerLike>({
|
|
|
601
685
|
try {
|
|
602
686
|
server = await createServer(req);
|
|
603
687
|
} catch (error) {
|
|
604
|
-
if (handleResponseError(error, res)) {
|
|
688
|
+
if (await handleResponseError(error, res)) {
|
|
605
689
|
return true;
|
|
606
690
|
}
|
|
607
691
|
|