mcp-proxy 5.12.0 → 5.12.2

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/jsr.json CHANGED
@@ -3,5 +3,5 @@
3
3
  "include": ["src/index.ts", "src/bin/mcp-proxy.ts"],
4
4
  "license": "MIT",
5
5
  "name": "@punkpeye/mcp-proxy",
6
- "version": "5.12.0"
6
+ "version": "5.12.2"
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-proxy",
3
- "version": "5.12.0",
3
+ "version": "5.12.2",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "build": "tsdown",
@@ -38,28 +38,28 @@
38
38
  ]
39
39
  },
40
40
  "devDependencies": {
41
- "@eslint/js": "^9.36.0",
42
- "@modelcontextprotocol/sdk": "^1.18.1",
43
- "@sebbo2002/semantic-release-jsr": "^3.0.1",
44
- "@tsconfig/node22": "^22.0.2",
45
- "@types/express": "^5.0.3",
46
- "@types/node": "^24.5.2",
47
- "@types/yargs": "^17.0.33",
48
- "eslint": "^9.36.0",
41
+ "@eslint/js": "^9.39.1",
42
+ "@modelcontextprotocol/sdk": "^1.24.3",
43
+ "@sebbo2002/semantic-release-jsr": "^3.1.0",
44
+ "@tsconfig/node22": "^22.0.5",
45
+ "@types/express": "^5.0.6",
46
+ "@types/node": "^24.10.1",
47
+ "@types/yargs": "^17.0.35",
48
+ "eslint": "^9.39.1",
49
49
  "eslint-config-prettier": "^10.1.8",
50
- "eslint-plugin-perfectionist": "^4.15.0",
51
- "eventsource": "^4.0.0",
52
- "express": "^5.0.1",
50
+ "eslint-plugin-perfectionist": "^4.15.1",
51
+ "eventsource": "^4.1.0",
52
+ "express": "^5.2.1",
53
53
  "get-port-please": "^3.2.0",
54
- "jiti": "^2.5.1",
54
+ "jiti": "^2.6.1",
55
55
  "jsr": "^0.13.5",
56
- "prettier": "^3.6.2",
57
- "semantic-release": "^24.2.8",
58
- "tsdown": "^0.15.2",
59
- "tsx": "^4.20.5",
60
- "typescript": "^5.9.2",
61
- "typescript-eslint": "^8.44.0",
62
- "vitest": "^3.2.4",
56
+ "prettier": "^3.7.4",
57
+ "semantic-release": "^24.2.9",
58
+ "tsdown": "^0.17.0",
59
+ "tsx": "^4.21.0",
60
+ "typescript": "^5.9.3",
61
+ "typescript-eslint": "^8.48.1",
62
+ "vitest": "^4.0.15",
63
63
  "yargs": "^18.0.0"
64
64
  },
65
65
  "tsdown": {
@@ -8,7 +8,7 @@ describe("InMemoryEventStore", () => {
8
8
  it("stores events and replays them after a specific event ID", async () => {
9
9
  const store = new InMemoryEventStore();
10
10
  const streamId = "test-stream-123";
11
-
11
+
12
12
  // Create test messages
13
13
  const messages: JSONRPCMessage[] = [
14
14
  { id: 1, jsonrpc: "2.0", method: "initialize" },
@@ -17,7 +17,7 @@ describe("InMemoryEventStore", () => {
17
17
  { id: 3, jsonrpc: "2.0", result: { success: true } },
18
18
  { id: 4, jsonrpc: "2.0", method: "shutdown" },
19
19
  ];
20
-
20
+
21
21
  // Store all events and keep track of event IDs
22
22
  // Add small delays to ensure different timestamps for proper ordering
23
23
  const eventIds: string[] = [];
@@ -26,32 +26,33 @@ describe("InMemoryEventStore", () => {
26
26
  expect(eventId).toContain(streamId);
27
27
  eventIds.push(eventId);
28
28
  // Small delay to ensure different timestamps
29
- await new Promise(resolve => setTimeout(resolve, 1));
29
+ await new Promise((resolve) => setTimeout(resolve, 1));
30
30
  }
31
-
31
+
32
32
  // Test replaying events after the second event
33
- const replayedEvents: Array<{ eventId: string; message: JSONRPCMessage }> = [];
33
+ const replayedEvents: Array<{ eventId: string; message: JSONRPCMessage }> =
34
+ [];
34
35
  const sendMock = vi.fn(async (eventId: string, message: JSONRPCMessage) => {
35
36
  replayedEvents.push({ eventId, message });
36
37
  });
37
-
38
+
38
39
  const returnedStreamId = await store.replayEventsAfter(
39
40
  eventIds[1], // Replay after the second event
40
- { send: sendMock }
41
+ { send: sendMock },
41
42
  );
42
-
43
+
43
44
  // Verify the correct stream ID was returned
44
45
  expect(returnedStreamId).toBe(streamId);
45
-
46
+
46
47
  // Verify that events 3, 4, and 5 were replayed (after event 2)
47
48
  expect(replayedEvents).toHaveLength(3);
48
49
  expect(sendMock).toHaveBeenCalledTimes(3);
49
-
50
+
50
51
  // Verify the replayed messages are correct and in order
51
52
  expect(replayedEvents[0].message).toEqual(messages[2]);
52
53
  expect(replayedEvents[1].message).toEqual(messages[3]);
53
54
  expect(replayedEvents[2].message).toEqual(messages[4]);
54
-
55
+
55
56
  // Verify event IDs are preserved
56
57
  expect(replayedEvents[0].eventId).toBe(eventIds[2]);
57
58
  expect(replayedEvents[1].eventId).toBe(eventIds[3]);
@@ -62,99 +63,106 @@ describe("InMemoryEventStore", () => {
62
63
  const store = new InMemoryEventStore();
63
64
  const streamId1 = "stream-alpha";
64
65
  const streamId2 = "stream-beta";
65
-
66
+
66
67
  // Create messages for two different streams
67
68
  const stream1Messages: JSONRPCMessage[] = [
68
69
  { id: 1, jsonrpc: "2.0", method: "stream1.init" },
69
70
  { id: 2, jsonrpc: "2.0", method: "stream1.process" },
70
71
  { id: 3, jsonrpc: "2.0", method: "stream1.complete" },
71
72
  ];
72
-
73
+
73
74
  const stream2Messages: JSONRPCMessage[] = [
74
75
  { id: 10, jsonrpc: "2.0", method: "stream2.init" },
75
76
  { id: 20, jsonrpc: "2.0", method: "stream2.process" },
76
77
  { id: 30, jsonrpc: "2.0", method: "stream2.complete" },
77
78
  ];
78
-
79
+
79
80
  // Interleave storing events from both streams with small delays
80
81
  const stream1EventIds: string[] = [];
81
82
  const stream2EventIds: string[] = [];
82
-
83
+
83
84
  // Store first event from each stream
84
85
  stream1EventIds.push(await store.storeEvent(streamId1, stream1Messages[0]));
85
- await new Promise(resolve => setTimeout(resolve, 1));
86
+ await new Promise((resolve) => setTimeout(resolve, 1));
86
87
  stream2EventIds.push(await store.storeEvent(streamId2, stream2Messages[0]));
87
- await new Promise(resolve => setTimeout(resolve, 1));
88
-
88
+ await new Promise((resolve) => setTimeout(resolve, 1));
89
+
89
90
  // Store second event from each stream
90
91
  stream1EventIds.push(await store.storeEvent(streamId1, stream1Messages[1]));
91
- await new Promise(resolve => setTimeout(resolve, 1));
92
+ await new Promise((resolve) => setTimeout(resolve, 1));
92
93
  stream2EventIds.push(await store.storeEvent(streamId2, stream2Messages[1]));
93
- await new Promise(resolve => setTimeout(resolve, 1));
94
-
94
+ await new Promise((resolve) => setTimeout(resolve, 1));
95
+
95
96
  // Store third event from each stream
96
97
  stream1EventIds.push(await store.storeEvent(streamId1, stream1Messages[2]));
97
- await new Promise(resolve => setTimeout(resolve, 1));
98
+ await new Promise((resolve) => setTimeout(resolve, 1));
98
99
  stream2EventIds.push(await store.storeEvent(streamId2, stream2Messages[2]));
99
-
100
+
100
101
  // Replay events from stream 1 after its first event
101
- const stream1ReplayedEvents: Array<{ eventId: string; message: JSONRPCMessage }> = [];
102
- const stream1SendMock = vi.fn(async (eventId: string, message: JSONRPCMessage) => {
103
- stream1ReplayedEvents.push({ eventId, message });
104
- });
105
-
102
+ const stream1ReplayedEvents: Array<{
103
+ eventId: string;
104
+ message: JSONRPCMessage;
105
+ }> = [];
106
+ const stream1SendMock = vi.fn(
107
+ async (eventId: string, message: JSONRPCMessage) => {
108
+ stream1ReplayedEvents.push({ eventId, message });
109
+ },
110
+ );
111
+
106
112
  const returnedStreamId1 = await store.replayEventsAfter(
107
113
  stream1EventIds[0],
108
- { send: stream1SendMock }
114
+ { send: stream1SendMock },
109
115
  );
110
-
116
+
111
117
  // Verify only stream 1 events were replayed
112
118
  expect(returnedStreamId1).toBe(streamId1);
113
119
  expect(stream1ReplayedEvents).toHaveLength(2);
114
120
  expect(stream1ReplayedEvents[0].message).toEqual(stream1Messages[1]);
115
121
  expect(stream1ReplayedEvents[1].message).toEqual(stream1Messages[2]);
116
-
122
+
117
123
  // Verify no stream 2 events were included
118
124
  for (const event of stream1ReplayedEvents) {
119
125
  expect(event.eventId).toContain(streamId1);
120
126
  expect(event.eventId).not.toContain(streamId2);
121
127
  }
122
-
128
+
123
129
  // Now replay events from stream 2 after its first event
124
- const stream2ReplayedEvents: Array<{ eventId: string; message: JSONRPCMessage }> = [];
125
- const stream2SendMock = vi.fn(async (eventId: string, message: JSONRPCMessage) => {
126
- stream2ReplayedEvents.push({ eventId, message });
127
- });
128
-
130
+ const stream2ReplayedEvents: Array<{
131
+ eventId: string;
132
+ message: JSONRPCMessage;
133
+ }> = [];
134
+ const stream2SendMock = vi.fn(
135
+ async (eventId: string, message: JSONRPCMessage) => {
136
+ stream2ReplayedEvents.push({ eventId, message });
137
+ },
138
+ );
139
+
129
140
  const returnedStreamId2 = await store.replayEventsAfter(
130
141
  stream2EventIds[0],
131
- { send: stream2SendMock }
142
+ { send: stream2SendMock },
132
143
  );
133
-
144
+
134
145
  // Verify only stream 2 events were replayed
135
146
  expect(returnedStreamId2).toBe(streamId2);
136
147
  expect(stream2ReplayedEvents).toHaveLength(2);
137
148
  expect(stream2ReplayedEvents[0].message).toEqual(stream2Messages[1]);
138
149
  expect(stream2ReplayedEvents[1].message).toEqual(stream2Messages[2]);
139
-
150
+
140
151
  // Verify no stream 1 events were included
141
152
  for (const event of stream2ReplayedEvents) {
142
153
  expect(event.eventId).toContain(streamId2);
143
154
  expect(event.eventId).not.toContain(streamId1);
144
155
  }
145
-
156
+
146
157
  // Test edge case: replay with non-existent event ID returns empty string
147
158
  const invalidResult = await store.replayEventsAfter(
148
159
  "non-existent-event-id",
149
- { send: vi.fn() }
160
+ { send: vi.fn() },
150
161
  );
151
162
  expect(invalidResult).toBe("");
152
-
163
+
153
164
  // Test edge case: replay with empty event ID returns empty string
154
- const emptyResult = await store.replayEventsAfter(
155
- "",
156
- { send: vi.fn() }
157
- );
165
+ const emptyResult = await store.replayEventsAfter("", { send: vi.fn() });
158
166
  expect(emptyResult).toBe("");
159
167
  });
160
168
 
@@ -188,12 +196,12 @@ describe("InMemoryEventStore", () => {
188
196
 
189
197
  const timestampParts = parts.map(([, timestamp]) => timestamp);
190
198
  expect(timestampParts).toEqual(
191
- Array(messages.length).fill(fixedTimestamp.toString())
199
+ Array(messages.length).fill(fixedTimestamp.toString()),
192
200
  );
193
201
 
194
202
  const counterSuffixes = parts.map(([, , counter]) => counter);
195
203
  expect(counterSuffixes).toEqual(
196
- messages.map((_, index) => (index).toString(36).padStart(4, "0"))
204
+ messages.map((_, index) => index.toString(36).padStart(4, "0")),
197
205
  );
198
206
 
199
207
  // Random parts should be 3 base36 characters each (due to substring(2, 5))
@@ -229,4 +237,4 @@ describe("InMemoryEventStore", () => {
229
237
  secondSpy.mockRestore();
230
238
  }
231
239
  });
232
- });
240
+ });
@@ -81,13 +81,12 @@ export class InMemoryEventStore implements EventStore {
81
81
  }
82
82
 
83
83
  /**
84
- * Generates a monotonic unique event ID in
84
+ * Generates a monotonic unique event ID in
85
85
  * `${streamId}_${timestamp}_${counter}_${random}` format.
86
86
  */
87
87
  private generateEventId(streamId: string): string {
88
-
89
88
  const now = Date.now();
90
-
89
+
91
90
  if (now === this.lastTimestamp) {
92
91
  this.lastTimestampCounter++;
93
92
  } else {
@@ -4,7 +4,9 @@ import { describe, expect, it } from "vitest";
4
4
  import { AuthenticationMiddleware } from "./authentication.js";
5
5
 
6
6
  describe("AuthenticationMiddleware", () => {
7
- const createMockRequest = (headers: Record<string, string> = {}): IncomingMessage => {
7
+ const createMockRequest = (
8
+ headers: Record<string, string> = {},
9
+ ): IncomingMessage => {
8
10
  // Simulate Node.js http module behavior which converts all header names to lowercase
9
11
  const lowercaseHeaders: Record<string, string> = {};
10
12
  for (const [key, value] of Object.entries(headers)) {
@@ -98,7 +100,9 @@ describe("AuthenticationMiddleware", () => {
98
100
 
99
101
  const body = JSON.parse(response.body);
100
102
  expect(body.error.code).toBe(401);
101
- expect(body.error.message).toBe("Unauthorized: Invalid or missing API key");
103
+ expect(body.error.message).toBe(
104
+ "Unauthorized: Invalid or missing API key",
105
+ );
102
106
  expect(body.jsonrpc).toBe("2.0");
103
107
  expect(body.id).toBe(null);
104
108
  });
@@ -207,8 +211,12 @@ describe("AuthenticationMiddleware", () => {
207
211
  });
208
212
  const response = middleware.getUnauthorizedResponse();
209
213
 
210
- expect(response.headers["WWW-Authenticate"]).toContain('realm="example-realm"');
211
- expect(response.headers["WWW-Authenticate"]).toContain('resource_metadata="https://example.com/.well-known/oauth-protected-resource"');
214
+ expect(response.headers["WWW-Authenticate"]).toContain(
215
+ 'realm="example-realm"',
216
+ );
217
+ expect(response.headers["WWW-Authenticate"]).toContain(
218
+ 'resource_metadata="https://example.com/.well-known/oauth-protected-resource"',
219
+ );
212
220
  });
213
221
 
214
222
  it("should include custom error in WWW-Authenticate header via options", () => {
@@ -225,8 +233,12 @@ describe("AuthenticationMiddleware", () => {
225
233
  error_description: "The request requires higher privileges",
226
234
  });
227
235
 
228
- expect(response.headers["WWW-Authenticate"]).toContain('error="insufficient_scope"');
229
- expect(response.headers["WWW-Authenticate"]).toContain('error_description="The request requires higher privileges"');
236
+ expect(response.headers["WWW-Authenticate"]).toContain(
237
+ 'error="insufficient_scope"',
238
+ );
239
+ expect(response.headers["WWW-Authenticate"]).toContain(
240
+ 'error_description="The request requires higher privileges"',
241
+ );
230
242
  });
231
243
 
232
244
  it("should include scope in WWW-Authenticate header", () => {
@@ -241,7 +253,9 @@ describe("AuthenticationMiddleware", () => {
241
253
  });
242
254
  const response = middleware.getUnauthorizedResponse();
243
255
 
244
- expect(response.headers["WWW-Authenticate"]).toContain('scope="read write"');
256
+ expect(response.headers["WWW-Authenticate"]).toContain(
257
+ 'scope="read write"',
258
+ );
245
259
  });
246
260
 
247
261
  it("should override config error with options error", () => {
@@ -260,10 +274,18 @@ describe("AuthenticationMiddleware", () => {
260
274
  error_description: "Options error description",
261
275
  });
262
276
 
263
- expect(response.headers["WWW-Authenticate"]).toContain('error="invalid_token"');
264
- expect(response.headers["WWW-Authenticate"]).toContain('error_description="Options error description"');
265
- expect(response.headers["WWW-Authenticate"]).not.toContain("invalid_request");
266
- expect(response.headers["WWW-Authenticate"]).not.toContain("Config error description");
277
+ expect(response.headers["WWW-Authenticate"]).toContain(
278
+ 'error="invalid_token"',
279
+ );
280
+ expect(response.headers["WWW-Authenticate"]).toContain(
281
+ 'error_description="Options error description"',
282
+ );
283
+ expect(response.headers["WWW-Authenticate"]).not.toContain(
284
+ "invalid_request",
285
+ );
286
+ expect(response.headers["WWW-Authenticate"]).not.toContain(
287
+ "Config error description",
288
+ );
267
289
  });
268
290
 
269
291
  it("should include error_uri in WWW-Authenticate header", () => {
@@ -278,7 +300,9 @@ describe("AuthenticationMiddleware", () => {
278
300
  });
279
301
  const response = middleware.getUnauthorizedResponse();
280
302
 
281
- expect(response.headers["WWW-Authenticate"]).toContain('error_uri="https://example.com/errors/auth"');
303
+ expect(response.headers["WWW-Authenticate"]).toContain(
304
+ 'error_uri="https://example.com/errors/auth"',
305
+ );
282
306
  });
283
307
 
284
308
  it("should properly escape quotes in error_description", () => {
@@ -294,7 +318,9 @@ describe("AuthenticationMiddleware", () => {
294
318
  error_description: 'Token "abc123" is invalid',
295
319
  });
296
320
 
297
- expect(response.headers["WWW-Authenticate"]).toContain('error_description="Token \\"abc123\\" is invalid"');
321
+ expect(response.headers["WWW-Authenticate"]).toContain(
322
+ 'error_description="Token \\"abc123\\" is invalid"',
323
+ );
298
324
  });
299
325
 
300
326
  it("should include all parameters in correct order", () => {
@@ -315,16 +341,24 @@ describe("AuthenticationMiddleware", () => {
315
341
 
316
342
  const header = response.headers["WWW-Authenticate"];
317
343
  expect(header).toContain('realm="my-realm"');
318
- expect(header).toContain('resource_metadata="https://example.com/.well-known/oauth-protected-resource"');
344
+ expect(header).toContain(
345
+ 'resource_metadata="https://example.com/.well-known/oauth-protected-resource"',
346
+ );
319
347
  expect(header).toContain('error="invalid_token"');
320
348
  expect(header).toContain('error_description="Token expired"');
321
349
  expect(header).toContain('error_uri="https://example.com/errors"');
322
350
  expect(header).toContain('scope="read write"');
323
351
 
324
352
  // Check order: realm, resource_metadata, error, error_description, error_uri, scope
325
- expect(header.indexOf('realm=')).toBeLessThan(header.indexOf('resource_metadata='));
326
- expect(header.indexOf('resource_metadata=')).toBeLessThan(header.indexOf('error='));
327
- expect(header.indexOf('error=')).toBeLessThan(header.indexOf('error_description='));
353
+ expect(header.indexOf("realm=")).toBeLessThan(
354
+ header.indexOf("resource_metadata="),
355
+ );
356
+ expect(header.indexOf("resource_metadata=")).toBeLessThan(
357
+ header.indexOf("error="),
358
+ );
359
+ expect(header.indexOf("error=")).toBeLessThan(
360
+ header.indexOf("error_description="),
361
+ );
328
362
  });
329
363
  });
330
364
 
@@ -352,9 +386,15 @@ describe("AuthenticationMiddleware", () => {
352
386
  });
353
387
  const response = middleware.getScopeChallengeResponse(["read", "write"]);
354
388
 
355
- expect(response.headers["WWW-Authenticate"]).toContain('error="insufficient_scope"');
356
- expect(response.headers["WWW-Authenticate"]).toContain('scope="read write"');
357
- expect(response.headers["WWW-Authenticate"]).toContain('resource_metadata="https://example.com/.well-known/oauth-protected-resource"');
389
+ expect(response.headers["WWW-Authenticate"]).toContain(
390
+ 'error="insufficient_scope"',
391
+ );
392
+ expect(response.headers["WWW-Authenticate"]).toContain(
393
+ 'scope="read write"',
394
+ );
395
+ expect(response.headers["WWW-Authenticate"]).toContain(
396
+ 'resource_metadata="https://example.com/.well-known/oauth-protected-resource"',
397
+ );
358
398
  });
359
399
 
360
400
  it("should include error_description in WWW-Authenticate header", () => {
@@ -370,7 +410,9 @@ describe("AuthenticationMiddleware", () => {
370
410
  "Admin access required",
371
411
  );
372
412
 
373
- expect(response.headers["WWW-Authenticate"]).toContain('error_description="Admin access required"');
413
+ expect(response.headers["WWW-Authenticate"]).toContain(
414
+ 'error_description="Admin access required"',
415
+ );
374
416
  });
375
417
 
376
418
  it("should escape quotes in error_description", () => {
@@ -386,7 +428,9 @@ describe("AuthenticationMiddleware", () => {
386
428
  'Requires "admin" scope',
387
429
  );
388
430
 
389
- expect(response.headers["WWW-Authenticate"]).toContain('error_description="Requires \\"admin\\" scope"');
431
+ expect(response.headers["WWW-Authenticate"]).toContain(
432
+ 'error_description="Requires \\"admin\\" scope"',
433
+ );
390
434
  });
391
435
 
392
436
  it("should include request ID in response body", () => {
@@ -397,7 +441,11 @@ describe("AuthenticationMiddleware", () => {
397
441
  },
398
442
  },
399
443
  });
400
- const response = middleware.getScopeChallengeResponse(["read"], undefined, 123);
444
+ const response = middleware.getScopeChallengeResponse(
445
+ ["read"],
446
+ undefined,
447
+ 123,
448
+ );
401
449
 
402
450
  const body = JSON.parse(response.body);
403
451
  expect(body.id).toBe(123);
@@ -460,11 +508,21 @@ describe("AuthenticationMiddleware", () => {
460
508
  },
461
509
  },
462
510
  });
463
- const response = middleware.getScopeChallengeResponse(["read", "write", "admin"]);
464
-
465
- expect(response.headers["WWW-Authenticate"]).toContain('scope="read write admin"');
511
+ const response = middleware.getScopeChallengeResponse([
512
+ "read",
513
+ "write",
514
+ "admin",
515
+ ]);
516
+
517
+ expect(response.headers["WWW-Authenticate"]).toContain(
518
+ 'scope="read write admin"',
519
+ );
466
520
  const body = JSON.parse(response.body);
467
- expect(body.error.data.required_scopes).toEqual(["read", "write", "admin"]);
521
+ expect(body.error.data.required_scopes).toEqual([
522
+ "read",
523
+ "write",
524
+ "admin",
525
+ ]);
468
526
  });
469
527
 
470
528
  it("should not include WWW-Authenticate header without OAuth config", () => {
@@ -483,7 +541,11 @@ describe("AuthenticationMiddleware", () => {
483
541
  },
484
542
  },
485
543
  });
486
- const response = middleware.getScopeChallengeResponse(["read"], "Description", "req-123");
544
+ const response = middleware.getScopeChallengeResponse(
545
+ ["read"],
546
+ "Description",
547
+ "req-123",
548
+ );
487
549
 
488
550
  const body = JSON.parse(response.body);
489
551
  expect(body.jsonrpc).toBe("2.0");
@@ -522,4 +584,4 @@ describe("AuthenticationMiddleware", () => {
522
584
  expect(body.error.data.required_scopes).toEqual([]);
523
585
  });
524
586
  });
525
- });
587
+ });
@@ -83,15 +83,21 @@ export class AuthenticationMiddleware {
83
83
 
84
84
  // Add resource_metadata if configured
85
85
  if (this.config.oauth.protectedResource?.resource) {
86
- params.push(`resource_metadata="${this.config.oauth.protectedResource.resource}/.well-known/oauth-protected-resource"`);
86
+ params.push(
87
+ `resource_metadata="${this.config.oauth.protectedResource.resource}/.well-known/oauth-protected-resource"`,
88
+ );
87
89
  }
88
90
 
89
91
  // Add error from options or config (options takes precedence)
90
- const error = options?.error || this.config.oauth.error || "invalid_token";
92
+ const error =
93
+ options?.error || this.config.oauth.error || "invalid_token";
91
94
  params.push(`error="${error}"`);
92
95
 
93
96
  // Add error_description from options or config (options takes precedence)
94
- const error_description = options?.error_description || this.config.oauth.error_description || "Unauthorized: Invalid or missing API key";
97
+ const error_description =
98
+ options?.error_description ||
99
+ this.config.oauth.error_description ||
100
+ "Unauthorized: Invalid or missing API key";
95
101
  // Escape quotes in error description
96
102
  const escaped = error_description.replace(/"/g, '\\"');
97
103
  params.push(`error_description="${escaped}"`);
@@ -117,7 +123,9 @@ export class AuthenticationMiddleware {
117
123
  body: JSON.stringify({
118
124
  error: {
119
125
  code: 401,
120
- message: options?.error_description || "Unauthorized: Invalid or missing API key",
126
+ message:
127
+ options?.error_description ||
128
+ "Unauthorized: Invalid or missing API key",
121
129
  },
122
130
  id: null,
123
131
  jsonrpc: "2.0",
@@ -143,4 +151,3 @@ export class AuthenticationMiddleware {
143
151
  return apiKey === this.config.apiKey;
144
152
  }
145
153
  }
146
-
@@ -68,7 +68,8 @@ const argv = await yargs(hideBin(process.argv))
68
68
  },
69
69
  requestTimeout: {
70
70
  default: 300000,
71
- describe: "The timeout (in milliseconds) for requests to the MCP server (default: 5 minutes)",
71
+ describe:
72
+ "The timeout (in milliseconds) for requests to the MCP server (default: 5 minutes)",
72
73
  type: "number",
73
74
  },
74
75
  server: {