mcp-proxy 5.8.0 → 5.9.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/dist/bin/mcp-proxy.js +1 -1
- package/dist/index.d.ts +23 -3
- package/dist/index.js +2 -2
- package/dist/{stdio-so1-I7Pn.js → stdio-CsjPjeWC.js} +82 -35
- package/dist/stdio-CsjPjeWC.js.map +1 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/authentication.test.ts +77 -2
- package/src/authentication.ts +16 -4
- package/src/index.ts +2 -0
- package/src/startHTTPServer.test.ts +494 -0
- package/src/startHTTPServer.ts +99 -5
- package/dist/stdio-so1-I7Pn.js.map +0 -1
package/src/startHTTPServer.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
|
8
8
|
import http from "http";
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
10
|
|
|
11
|
-
import { AuthenticationMiddleware } from "./authentication.js";
|
|
11
|
+
import { AuthConfig, AuthenticationMiddleware } from "./authentication.js";
|
|
12
12
|
import { InMemoryEventStore } from "./InMemoryEventStore.js";
|
|
13
13
|
|
|
14
14
|
export type SSEServer = {
|
|
@@ -49,6 +49,17 @@ const createJsonRpcErrorResponse = (code: number, message: string) => {
|
|
|
49
49
|
});
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
+
// Helper function to get WWW-Authenticate header value
|
|
53
|
+
const getWWWAuthenticateHeader = (
|
|
54
|
+
oauth?: AuthConfig["oauth"],
|
|
55
|
+
): string | undefined => {
|
|
56
|
+
if (!oauth?.protectedResource?.resource) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return `Bearer resource_metadata="${oauth.protectedResource.resource}/.well-known/oauth-protected-resource"`;
|
|
61
|
+
};
|
|
62
|
+
|
|
52
63
|
// Helper function to handle Response errors and send appropriate HTTP response
|
|
53
64
|
const handleResponseError = (
|
|
54
65
|
error: unknown,
|
|
@@ -98,6 +109,7 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
98
109
|
enableJsonResponse,
|
|
99
110
|
endpoint,
|
|
100
111
|
eventStore,
|
|
112
|
+
oauth,
|
|
101
113
|
onClose,
|
|
102
114
|
onConnect,
|
|
103
115
|
req,
|
|
@@ -113,6 +125,7 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
113
125
|
enableJsonResponse?: boolean;
|
|
114
126
|
endpoint: string;
|
|
115
127
|
eventStore?: EventStore;
|
|
128
|
+
oauth?: AuthConfig["oauth"];
|
|
116
129
|
onClose?: (server: T) => Promise<void>;
|
|
117
130
|
onConnect?: (server: T) => Promise<void>;
|
|
118
131
|
req: http.IncomingMessage;
|
|
@@ -138,13 +151,28 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
138
151
|
if (stateless && authenticate) {
|
|
139
152
|
try {
|
|
140
153
|
const authResult = await authenticate(req);
|
|
141
|
-
|
|
154
|
+
|
|
155
|
+
// Check for both falsy AND { authenticated: false } pattern
|
|
156
|
+
if (!authResult || (typeof authResult === 'object' && 'authenticated' in authResult && !authResult.authenticated)) {
|
|
157
|
+
// Extract error message if available
|
|
158
|
+
const errorMessage =
|
|
159
|
+
authResult && typeof authResult === 'object' && 'error' in authResult && typeof authResult.error === 'string'
|
|
160
|
+
? authResult.error
|
|
161
|
+
: "Unauthorized: Authentication failed";
|
|
162
|
+
|
|
142
163
|
res.setHeader("Content-Type", "application/json");
|
|
164
|
+
|
|
165
|
+
// Add WWW-Authenticate header if OAuth config is available
|
|
166
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth);
|
|
167
|
+
if (wwwAuthHeader) {
|
|
168
|
+
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
169
|
+
}
|
|
170
|
+
|
|
143
171
|
res.writeHead(401).end(
|
|
144
172
|
JSON.stringify({
|
|
145
173
|
error: {
|
|
146
174
|
code: -32000,
|
|
147
|
-
message:
|
|
175
|
+
message: errorMessage
|
|
148
176
|
},
|
|
149
177
|
id: (body as { id?: unknown })?.id ?? null,
|
|
150
178
|
jsonrpc: "2.0"
|
|
@@ -153,13 +181,22 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
153
181
|
return true;
|
|
154
182
|
}
|
|
155
183
|
} catch (error) {
|
|
184
|
+
// Extract error details from thrown errors
|
|
185
|
+
const errorMessage = error instanceof Error ? error.message : "Unauthorized: Authentication error";
|
|
156
186
|
console.error("Authentication error:", error);
|
|
157
187
|
res.setHeader("Content-Type", "application/json");
|
|
188
|
+
|
|
189
|
+
// Add WWW-Authenticate header if OAuth config is available
|
|
190
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth);
|
|
191
|
+
if (wwwAuthHeader) {
|
|
192
|
+
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
193
|
+
}
|
|
194
|
+
|
|
158
195
|
res.writeHead(401).end(
|
|
159
196
|
JSON.stringify({
|
|
160
197
|
error: {
|
|
161
198
|
code: -32000,
|
|
162
|
-
message:
|
|
199
|
+
message: errorMessage
|
|
163
200
|
},
|
|
164
201
|
id: (body as { id?: unknown })?.id ?? null,
|
|
165
202
|
jsonrpc: "2.0"
|
|
@@ -223,6 +260,33 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
223
260
|
try {
|
|
224
261
|
server = await createServer(req);
|
|
225
262
|
} catch (error) {
|
|
263
|
+
// Detect authentication errors and return HTTP 401
|
|
264
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
265
|
+
const isAuthError = errorMessage.includes('Authentication') ||
|
|
266
|
+
errorMessage.includes('Invalid JWT') ||
|
|
267
|
+
errorMessage.includes('Token') ||
|
|
268
|
+
errorMessage.includes('Unauthorized');
|
|
269
|
+
|
|
270
|
+
if (isAuthError) {
|
|
271
|
+
res.setHeader("Content-Type", "application/json");
|
|
272
|
+
|
|
273
|
+
// Add WWW-Authenticate header if OAuth config is available
|
|
274
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth);
|
|
275
|
+
if (wwwAuthHeader) {
|
|
276
|
+
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
res.writeHead(401).end(JSON.stringify({
|
|
280
|
+
error: {
|
|
281
|
+
code: -32000,
|
|
282
|
+
message: errorMessage
|
|
283
|
+
},
|
|
284
|
+
id: (body as { id?: unknown })?.id ?? null,
|
|
285
|
+
jsonrpc: "2.0"
|
|
286
|
+
}));
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
|
|
226
290
|
if (handleResponseError(error, res)) {
|
|
227
291
|
return true;
|
|
228
292
|
}
|
|
@@ -255,6 +319,33 @@ const handleStreamRequest = async <T extends ServerLike>({
|
|
|
255
319
|
try {
|
|
256
320
|
server = await createServer(req);
|
|
257
321
|
} catch (error) {
|
|
322
|
+
// Detect authentication errors and return HTTP 401
|
|
323
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
324
|
+
const isAuthError = errorMessage.includes('Authentication') ||
|
|
325
|
+
errorMessage.includes('Invalid JWT') ||
|
|
326
|
+
errorMessage.includes('Token') ||
|
|
327
|
+
errorMessage.includes('Unauthorized');
|
|
328
|
+
|
|
329
|
+
if (isAuthError) {
|
|
330
|
+
res.setHeader("Content-Type", "application/json");
|
|
331
|
+
|
|
332
|
+
// Add WWW-Authenticate header if OAuth config is available
|
|
333
|
+
const wwwAuthHeader = getWWWAuthenticateHeader(oauth);
|
|
334
|
+
if (wwwAuthHeader) {
|
|
335
|
+
res.setHeader("WWW-Authenticate", wwwAuthHeader);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
res.writeHead(401).end(JSON.stringify({
|
|
339
|
+
error: {
|
|
340
|
+
code: -32000,
|
|
341
|
+
message: errorMessage
|
|
342
|
+
},
|
|
343
|
+
id: (body as { id?: unknown })?.id ?? null,
|
|
344
|
+
jsonrpc: "2.0"
|
|
345
|
+
}));
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
|
|
258
349
|
if (handleResponseError(error, res)) {
|
|
259
350
|
return true;
|
|
260
351
|
}
|
|
@@ -499,6 +590,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
499
590
|
enableJsonResponse,
|
|
500
591
|
eventStore,
|
|
501
592
|
host = "::",
|
|
593
|
+
oauth,
|
|
502
594
|
onClose,
|
|
503
595
|
onConnect,
|
|
504
596
|
onUnhandledRequest,
|
|
@@ -513,6 +605,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
513
605
|
enableJsonResponse?: boolean;
|
|
514
606
|
eventStore?: EventStore;
|
|
515
607
|
host?: string;
|
|
608
|
+
oauth?: AuthConfig["oauth"];
|
|
516
609
|
onClose?: (server: T) => Promise<void>;
|
|
517
610
|
onConnect?: (server: T) => Promise<void>;
|
|
518
611
|
onUnhandledRequest?: (
|
|
@@ -534,7 +627,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
534
627
|
}
|
|
535
628
|
> = {};
|
|
536
629
|
|
|
537
|
-
const authMiddleware = new AuthenticationMiddleware({ apiKey });
|
|
630
|
+
const authMiddleware = new AuthenticationMiddleware({ apiKey, oauth });
|
|
538
631
|
|
|
539
632
|
/**
|
|
540
633
|
* @author https://dev.classmethod.jp/articles/mcp-sse/
|
|
@@ -597,6 +690,7 @@ export const startHTTPServer = async <T extends ServerLike>({
|
|
|
597
690
|
enableJsonResponse,
|
|
598
691
|
endpoint: streamEndpoint,
|
|
599
692
|
eventStore,
|
|
693
|
+
oauth,
|
|
600
694
|
onClose,
|
|
601
695
|
onConnect,
|
|
602
696
|
req,
|