@toolsdk.ai/registry 1.0.133 → 1.0.134

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.
@@ -0,0 +1,598 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ // Mock dependencies before importing the module under test
3
+ vi.mock("../../package/package-handler", () => ({
4
+ repository: {
5
+ getPackageConfig: vi.fn(),
6
+ },
7
+ }));
8
+ vi.mock("../oauth-session", () => ({
9
+ oauthSessionStore: {
10
+ set: vi.fn(),
11
+ get: vi.fn(),
12
+ getByState: vi.fn(),
13
+ delete: vi.fn(),
14
+ },
15
+ }));
16
+ vi.mock("../oauth-utils", () => ({
17
+ discoverProtectedResourceMetadata: vi.fn(),
18
+ discoverAuthServerMetadata: vi.fn(),
19
+ verifyPKCESupport: vi.fn(),
20
+ registerClient: vi.fn(),
21
+ generatePKCE: vi.fn(),
22
+ generateState: vi.fn(),
23
+ generateSessionId: vi.fn(),
24
+ buildAuthorizationUrl: vi.fn(),
25
+ getCanonicalResourceUri: vi.fn(),
26
+ exchangeCodeForTokens: vi.fn(),
27
+ refreshAccessToken: vi.fn(),
28
+ }));
29
+ vi.mock("../../shared/config/environment", () => ({
30
+ getServerPort: vi.fn(() => 3000),
31
+ }));
32
+ // Import after mocking
33
+ import { repository } from "../../package/package-handler";
34
+ import { handleCallback, handleRefresh, prepareOAuth } from "../oauth-handler";
35
+ import { oauthSessionStore } from "../oauth-session";
36
+ import { buildAuthorizationUrl, discoverAuthServerMetadata, discoverProtectedResourceMetadata, exchangeCodeForTokens, generatePKCE, generateSessionId, generateState, getCanonicalResourceUri, refreshAccessToken, registerClient, verifyPKCESupport, } from "../oauth-utils";
37
+ // Helper functions to create mock data
38
+ function createMockResourceMetadata(overrides = {}) {
39
+ return Object.assign({ resource: "https://mcp.example.com/server", authorization_servers: ["https://auth.example.com"], scopes_supported: ["read", "write"] }, overrides);
40
+ }
41
+ function createMockOAuthMetadata(overrides = {}) {
42
+ return Object.assign({ issuer: "https://auth.example.com", authorization_endpoint: "https://auth.example.com/authorize", token_endpoint: "https://auth.example.com/token", registration_endpoint: "https://auth.example.com/register", code_challenge_methods_supported: ["S256"], scopes_supported: ["read", "write"] }, overrides);
43
+ }
44
+ function createMockSession(overrides = {}) {
45
+ return Object.assign({ sessionId: "test-session-id", state: "test-state", codeVerifier: "test-code-verifier", codeChallenge: "test-code-challenge", clientInfo: {
46
+ client_id: "test-client-id",
47
+ client_secret: "test-client-secret",
48
+ }, callbackBaseUrl: "http://localhost:3003/callback", mcpServerUrl: "https://mcp.example.com/server", packageName: "test-package", oauthMetadata: createMockOAuthMetadata(), createdAt: Date.now() }, overrides);
49
+ }
50
+ describe("oauth-handler", () => {
51
+ beforeEach(() => {
52
+ vi.clearAllMocks();
53
+ // Setup default mock implementations
54
+ vi.mocked(generatePKCE).mockReturnValue({
55
+ codeVerifier: "mock-code-verifier",
56
+ codeChallenge: "mock-code-challenge",
57
+ codeChallengeMethod: "S256",
58
+ });
59
+ vi.mocked(generateState).mockReturnValue("mock-state");
60
+ vi.mocked(generateSessionId).mockReturnValue("mock-session-id");
61
+ vi.mocked(getCanonicalResourceUri).mockReturnValue("https://mcp.example.com/server");
62
+ vi.mocked(buildAuthorizationUrl).mockReturnValue("https://auth.example.com/authorize?...");
63
+ });
64
+ afterEach(() => {
65
+ vi.restoreAllMocks();
66
+ });
67
+ describe("prepareOAuth", () => {
68
+ describe("successful OAuth flow preparation", () => {
69
+ it("should successfully prepare OAuth flow with direct mcpServerUrl", async () => {
70
+ // Arrange
71
+ const resourceMetadata = createMockResourceMetadata();
72
+ const oauthMetadata = createMockOAuthMetadata();
73
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
74
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
75
+ vi.mocked(verifyPKCESupport).mockReturnValue({ supported: true, advertised: true });
76
+ vi.mocked(registerClient).mockResolvedValue({
77
+ client_id: "registered-client-id",
78
+ client_secret: "registered-secret",
79
+ });
80
+ // Act
81
+ const result = await prepareOAuth({
82
+ packageName: "test-package",
83
+ callbackBaseUrl: "http://localhost:3003/callback",
84
+ mcpServerUrl: "https://mcp.example.com/server",
85
+ });
86
+ // Assert
87
+ expect(result.success).toBe(true);
88
+ expect(result.code).toBe(200);
89
+ expect(result.data).toHaveProperty("authUrl");
90
+ expect(result.data).toHaveProperty("sessionId");
91
+ expect(result.data).toHaveProperty("mcpServerUrl");
92
+ expect(oauthSessionStore.set).toHaveBeenCalled();
93
+ });
94
+ it("should successfully prepare OAuth flow using package config", async () => {
95
+ // Arrange
96
+ vi.mocked(repository.getPackageConfig).mockReturnValue({
97
+ name: "test-package",
98
+ remotes: [{ type: "streamable-http", url: "https://mcp.example.com/server" }],
99
+ });
100
+ const resourceMetadata = createMockResourceMetadata();
101
+ const oauthMetadata = createMockOAuthMetadata();
102
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
103
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
104
+ vi.mocked(verifyPKCESupport).mockReturnValue({ supported: true, advertised: true });
105
+ vi.mocked(registerClient).mockResolvedValue({
106
+ client_id: "registered-client-id",
107
+ });
108
+ // Act
109
+ const result = await prepareOAuth({
110
+ packageName: "test-package",
111
+ callbackBaseUrl: "http://localhost:3003/callback",
112
+ });
113
+ // Assert
114
+ expect(result.success).toBe(true);
115
+ expect(result.code).toBe(200);
116
+ });
117
+ it("should use default client when no registration endpoint", async () => {
118
+ // Arrange
119
+ const resourceMetadata = createMockResourceMetadata();
120
+ const oauthMetadata = createMockOAuthMetadata({ registration_endpoint: undefined });
121
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
122
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
123
+ vi.mocked(verifyPKCESupport).mockReturnValue({ supported: true, advertised: true });
124
+ // Act
125
+ const result = await prepareOAuth({
126
+ packageName: "test-package",
127
+ callbackBaseUrl: "http://localhost:3003/callback",
128
+ mcpServerUrl: "https://mcp.example.com/server",
129
+ });
130
+ // Assert
131
+ expect(result.success).toBe(true);
132
+ expect(registerClient).not.toHaveBeenCalled();
133
+ });
134
+ it("should log warning but proceed when PKCE is not advertised", async () => {
135
+ // Arrange
136
+ const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => { });
137
+ const resourceMetadata = createMockResourceMetadata();
138
+ const oauthMetadata = createMockOAuthMetadata();
139
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
140
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
141
+ vi.mocked(verifyPKCESupport).mockReturnValue({ supported: true, advertised: false });
142
+ vi.mocked(registerClient).mockResolvedValue({ client_id: "test-client" });
143
+ // Act
144
+ const result = await prepareOAuth({
145
+ packageName: "test-package",
146
+ callbackBaseUrl: "http://localhost:3003/callback",
147
+ mcpServerUrl: "https://mcp.example.com/server",
148
+ });
149
+ // Assert
150
+ expect(result.success).toBe(true);
151
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("does not advertise code_challenge_methods_supported"));
152
+ consoleSpy.mockRestore();
153
+ });
154
+ });
155
+ describe("error handling during discovery/registration", () => {
156
+ it("should return error when package does not support OAuth", async () => {
157
+ // Arrange
158
+ vi.mocked(repository.getPackageConfig).mockReturnValue({
159
+ packageName: "test-package",
160
+ type: "mcp-server",
161
+ runtime: "node",
162
+ remotes: [],
163
+ });
164
+ // Act
165
+ const result = await prepareOAuth({
166
+ packageName: "test-package",
167
+ callbackBaseUrl: "http://localhost:3003/callback",
168
+ });
169
+ // Assert
170
+ expect(result.success).toBe(false);
171
+ expect(result.code).toBe(400);
172
+ expect(result.message).toContain("does not support OAuth");
173
+ });
174
+ it("should return error when no authorization servers found", async () => {
175
+ // Arrange
176
+ const resourceMetadata = createMockResourceMetadata({ authorization_servers: [] });
177
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
178
+ // Act
179
+ const result = await prepareOAuth({
180
+ packageName: "test-package",
181
+ callbackBaseUrl: "http://localhost:3003/callback",
182
+ mcpServerUrl: "https://mcp.example.com/server",
183
+ });
184
+ // Assert
185
+ expect(result.success).toBe(false);
186
+ expect(result.code).toBe(400);
187
+ expect(result.message).toContain("No authorization servers found");
188
+ });
189
+ it("should return error when PKCE is explicitly not supported", async () => {
190
+ // Arrange
191
+ const resourceMetadata = createMockResourceMetadata();
192
+ const oauthMetadata = createMockOAuthMetadata();
193
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
194
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
195
+ vi.mocked(verifyPKCESupport).mockReturnValue({ supported: false, advertised: true });
196
+ // Act
197
+ const result = await prepareOAuth({
198
+ packageName: "test-package",
199
+ callbackBaseUrl: "http://localhost:3003/callback",
200
+ mcpServerUrl: "https://mcp.example.com/server",
201
+ });
202
+ // Assert
203
+ expect(result.success).toBe(false);
204
+ expect(result.code).toBe(400);
205
+ expect(result.message).toContain("does not support PKCE");
206
+ });
207
+ it("should return error when resource metadata discovery fails", async () => {
208
+ // Arrange
209
+ vi.mocked(discoverProtectedResourceMetadata).mockRejectedValue(new Error("Discovery failed"));
210
+ // Act
211
+ const result = await prepareOAuth({
212
+ packageName: "test-package",
213
+ callbackBaseUrl: "http://localhost:3003/callback",
214
+ mcpServerUrl: "https://mcp.example.com/server",
215
+ });
216
+ // Assert
217
+ expect(result.success).toBe(false);
218
+ expect(result.code).toBe(500);
219
+ expect(result.message).toBe("Discovery failed");
220
+ });
221
+ it("should return error when auth server metadata discovery fails", async () => {
222
+ // Arrange
223
+ const resourceMetadata = createMockResourceMetadata();
224
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
225
+ vi.mocked(discoverAuthServerMetadata).mockRejectedValue(new Error("Auth server discovery failed"));
226
+ // Act
227
+ const result = await prepareOAuth({
228
+ packageName: "test-package",
229
+ callbackBaseUrl: "http://localhost:3003/callback",
230
+ mcpServerUrl: "https://mcp.example.com/server",
231
+ });
232
+ // Assert
233
+ expect(result.success).toBe(false);
234
+ expect(result.code).toBe(500);
235
+ expect(result.message).toBe("Auth server discovery failed");
236
+ });
237
+ it("should return error when client registration fails", async () => {
238
+ // Arrange
239
+ const resourceMetadata = createMockResourceMetadata();
240
+ const oauthMetadata = createMockOAuthMetadata();
241
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
242
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
243
+ vi.mocked(verifyPKCESupport).mockReturnValue({ supported: true, advertised: true });
244
+ vi.mocked(registerClient).mockRejectedValue(new Error("Registration failed"));
245
+ // Act
246
+ const result = await prepareOAuth({
247
+ packageName: "test-package",
248
+ callbackBaseUrl: "http://localhost:3003/callback",
249
+ mcpServerUrl: "https://mcp.example.com/server",
250
+ });
251
+ // Assert
252
+ expect(result.success).toBe(false);
253
+ expect(result.code).toBe(500);
254
+ expect(result.message).toBe("Registration failed");
255
+ });
256
+ it("should handle non-Error exceptions gracefully", async () => {
257
+ // Arrange
258
+ vi.mocked(discoverProtectedResourceMetadata).mockRejectedValue("Unknown error");
259
+ // Act
260
+ const result = await prepareOAuth({
261
+ packageName: "test-package",
262
+ callbackBaseUrl: "http://localhost:3003/callback",
263
+ mcpServerUrl: "https://mcp.example.com/server",
264
+ });
265
+ // Assert
266
+ expect(result.success).toBe(false);
267
+ expect(result.code).toBe(500);
268
+ expect(result.message).toBe("Unknown error during OAuth preparation");
269
+ });
270
+ });
271
+ });
272
+ describe("handleCallback", () => {
273
+ describe("callback handling with valid/invalid state", () => {
274
+ it("should return error when OAuth error is present in params", async () => {
275
+ // Act
276
+ const result = await handleCallback({
277
+ error: "access_denied",
278
+ error_description: "User denied access",
279
+ });
280
+ // Assert
281
+ expect(result.success).toBe(false);
282
+ expect(result.error).toBe("access_denied");
283
+ expect(result.error_description).toBe("User denied access");
284
+ expect(result.html).toContain("Authorization Failed");
285
+ });
286
+ it("should return error when code is missing", async () => {
287
+ // Act
288
+ const result = await handleCallback({
289
+ state: "test-state",
290
+ });
291
+ // Assert
292
+ expect(result.success).toBe(false);
293
+ expect(result.error).toBe("invalid_request");
294
+ expect(result.error_description).toBe("Missing code or state parameter");
295
+ });
296
+ it("should return error when state is missing", async () => {
297
+ // Act
298
+ const result = await handleCallback({
299
+ code: "test-code",
300
+ });
301
+ // Assert
302
+ expect(result.success).toBe(false);
303
+ expect(result.error).toBe("invalid_request");
304
+ expect(result.error_description).toBe("Missing code or state parameter");
305
+ });
306
+ it("should return error when session is not found by state", async () => {
307
+ // Arrange
308
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(undefined);
309
+ // Act
310
+ const result = await handleCallback({
311
+ code: "test-code",
312
+ state: "invalid-state",
313
+ });
314
+ // Assert
315
+ expect(result.success).toBe(false);
316
+ expect(result.error).toBe("invalid_state");
317
+ expect(result.error_description).toBe("Invalid or expired state parameter");
318
+ expect(result.html).toContain("Invalid or expired authorization session");
319
+ });
320
+ });
321
+ describe("token exchange success and failure scenarios", () => {
322
+ it("should successfully exchange code for tokens", async () => {
323
+ // Arrange
324
+ const mockSession = createMockSession();
325
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(mockSession);
326
+ vi.mocked(exchangeCodeForTokens).mockResolvedValue({
327
+ access_token: "test-access-token",
328
+ token_type: "Bearer",
329
+ expires_in: 3600,
330
+ refresh_token: "test-refresh-token",
331
+ });
332
+ // Mock fetch for callback POST
333
+ const mockFetch = vi.fn().mockResolvedValue({ ok: true });
334
+ global.fetch = mockFetch;
335
+ // Act
336
+ const result = await handleCallback({
337
+ code: "test-code",
338
+ state: "test-state",
339
+ });
340
+ // Assert
341
+ expect(result.success).toBe(true);
342
+ expect(result.sessionId).toBe(mockSession.sessionId);
343
+ expect(result.html).toContain("Authorization Successful");
344
+ expect(oauthSessionStore.delete).toHaveBeenCalledWith(mockSession.sessionId);
345
+ });
346
+ it("should POST callback data to callbackBaseUrl", async () => {
347
+ // Arrange
348
+ const mockSession = createMockSession();
349
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(mockSession);
350
+ vi.mocked(exchangeCodeForTokens).mockResolvedValue({
351
+ access_token: "test-access-token",
352
+ token_type: "Bearer",
353
+ });
354
+ const mockFetch = vi.fn().mockResolvedValue({ ok: true });
355
+ global.fetch = mockFetch;
356
+ // Act
357
+ await handleCallback({
358
+ code: "test-code",
359
+ state: "test-state",
360
+ });
361
+ // Assert
362
+ expect(mockFetch).toHaveBeenCalledWith(mockSession.callbackBaseUrl, expect.objectContaining({
363
+ method: "POST",
364
+ headers: { "Content-Type": "application/json" },
365
+ body: expect.stringContaining("test-access-token"),
366
+ }));
367
+ });
368
+ it("should succeed even if callback POST fails", async () => {
369
+ // Arrange
370
+ const mockSession = createMockSession();
371
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(mockSession);
372
+ vi.mocked(exchangeCodeForTokens).mockResolvedValue({
373
+ access_token: "test-access-token",
374
+ token_type: "Bearer",
375
+ });
376
+ // Mock fetch to fail
377
+ const mockFetch = vi.fn().mockRejectedValue(new Error("Network error"));
378
+ global.fetch = mockFetch;
379
+ // Act
380
+ const result = await handleCallback({
381
+ code: "test-code",
382
+ state: "test-state",
383
+ });
384
+ // Assert - should still succeed
385
+ expect(result.success).toBe(true);
386
+ });
387
+ it("should log warning when callback POST returns non-ok status", async () => {
388
+ // Arrange
389
+ const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
390
+ const mockSession = createMockSession();
391
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(mockSession);
392
+ vi.mocked(exchangeCodeForTokens).mockResolvedValue({
393
+ access_token: "test-access-token",
394
+ token_type: "Bearer",
395
+ });
396
+ const mockFetch = vi.fn().mockResolvedValue({ ok: false, status: 500 });
397
+ global.fetch = mockFetch;
398
+ // Act
399
+ const result = await handleCallback({
400
+ code: "test-code",
401
+ state: "test-state",
402
+ });
403
+ // Assert
404
+ expect(result.success).toBe(true);
405
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Callback POST returned"));
406
+ consoleSpy.mockRestore();
407
+ });
408
+ it("should return error when token exchange fails", async () => {
409
+ // Arrange
410
+ const mockSession = createMockSession();
411
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(mockSession);
412
+ vi.mocked(exchangeCodeForTokens).mockRejectedValue(new Error("Invalid grant"));
413
+ // Act
414
+ const result = await handleCallback({
415
+ code: "test-code",
416
+ state: "test-state",
417
+ });
418
+ // Assert
419
+ expect(result.success).toBe(false);
420
+ expect(result.error).toBe("token_exchange_failed");
421
+ expect(result.error_description).toBe("Invalid grant");
422
+ expect(oauthSessionStore.delete).toHaveBeenCalledWith(mockSession.sessionId);
423
+ });
424
+ it("should handle non-Error exceptions during token exchange", async () => {
425
+ // Arrange
426
+ const mockSession = createMockSession();
427
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(mockSession);
428
+ vi.mocked(exchangeCodeForTokens).mockRejectedValue("Unknown error");
429
+ // Act
430
+ const result = await handleCallback({
431
+ code: "test-code",
432
+ state: "test-state",
433
+ });
434
+ // Assert
435
+ expect(result.success).toBe(false);
436
+ expect(result.error_description).toBe("Token exchange failed");
437
+ });
438
+ });
439
+ describe("HTML response generation", () => {
440
+ it("should generate success HTML with postMessage script", async () => {
441
+ // Arrange
442
+ const mockSession = createMockSession();
443
+ vi.mocked(oauthSessionStore.getByState).mockReturnValue(mockSession);
444
+ vi.mocked(exchangeCodeForTokens).mockResolvedValue({
445
+ access_token: "test-access-token",
446
+ token_type: "Bearer",
447
+ });
448
+ global.fetch = vi.fn().mockResolvedValue({ ok: true });
449
+ // Act
450
+ const result = await handleCallback({
451
+ code: "test-code",
452
+ state: "test-state",
453
+ });
454
+ // Assert
455
+ expect(result.html).toContain("oauth-callback");
456
+ expect(result.html).toContain("postMessage");
457
+ expect(result.html).toContain("window.opener");
458
+ });
459
+ it("should generate error HTML with error message", async () => {
460
+ // Act
461
+ const result = await handleCallback({
462
+ error: "access_denied",
463
+ error_description: "User denied access",
464
+ });
465
+ // Assert
466
+ expect(result.html).toContain("User denied access");
467
+ expect(result.html).toContain("Authorization Failed");
468
+ });
469
+ it("should use error code when error_description is not provided", async () => {
470
+ // Act
471
+ const result = await handleCallback({
472
+ error: "server_error",
473
+ });
474
+ // Assert
475
+ expect(result.html).toContain("server_error");
476
+ });
477
+ });
478
+ });
479
+ describe("handleRefresh", () => {
480
+ describe("refresh token functionality", () => {
481
+ it("should successfully refresh access token", async () => {
482
+ // Arrange
483
+ const resourceMetadata = createMockResourceMetadata();
484
+ const oauthMetadata = createMockOAuthMetadata();
485
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
486
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
487
+ vi.mocked(refreshAccessToken).mockResolvedValue({
488
+ access_token: "new-access-token",
489
+ token_type: "Bearer",
490
+ expires_in: 3600,
491
+ refresh_token: "new-refresh-token",
492
+ });
493
+ // Act
494
+ const result = await handleRefresh({
495
+ mcpServerUrl: "https://mcp.example.com/server",
496
+ refreshToken: "old-refresh-token",
497
+ clientId: "test-client-id",
498
+ clientSecret: "test-client-secret",
499
+ });
500
+ // Assert
501
+ expect(result.success).toBe(true);
502
+ expect(result.code).toBe(200);
503
+ expect(result.data).toEqual({
504
+ access_token: "new-access-token",
505
+ token_type: "Bearer",
506
+ expires_in: 3600,
507
+ refresh_token: "new-refresh-token",
508
+ });
509
+ });
510
+ it("should return error when no authorization servers found", async () => {
511
+ // Arrange
512
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue({
513
+ resource: "https://mcp.example.com/server",
514
+ authorization_servers: [],
515
+ });
516
+ // Act
517
+ const result = await handleRefresh({
518
+ mcpServerUrl: "https://mcp.example.com/server",
519
+ refreshToken: "old-refresh-token",
520
+ clientId: "test-client-id",
521
+ });
522
+ // Assert
523
+ expect(result.success).toBe(false);
524
+ expect(result.code).toBe(400);
525
+ expect(result.message).toContain("No authorization servers found");
526
+ });
527
+ it("should return error when resource metadata discovery fails", async () => {
528
+ // Arrange
529
+ vi.mocked(discoverProtectedResourceMetadata).mockRejectedValue(new Error("Discovery failed"));
530
+ // Act
531
+ const result = await handleRefresh({
532
+ mcpServerUrl: "https://mcp.example.com/server",
533
+ refreshToken: "old-refresh-token",
534
+ clientId: "test-client-id",
535
+ });
536
+ // Assert
537
+ expect(result.success).toBe(false);
538
+ expect(result.code).toBe(500);
539
+ expect(result.message).toBe("Discovery failed");
540
+ });
541
+ it("should return error when refresh token request fails", async () => {
542
+ // Arrange
543
+ const resourceMetadata = createMockResourceMetadata();
544
+ const oauthMetadata = createMockOAuthMetadata();
545
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
546
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
547
+ vi.mocked(refreshAccessToken).mockRejectedValue(new Error("Invalid refresh token"));
548
+ // Act
549
+ const result = await handleRefresh({
550
+ mcpServerUrl: "https://mcp.example.com/server",
551
+ refreshToken: "invalid-refresh-token",
552
+ clientId: "test-client-id",
553
+ });
554
+ // Assert
555
+ expect(result.success).toBe(false);
556
+ expect(result.code).toBe(500);
557
+ expect(result.message).toBe("Invalid refresh token");
558
+ });
559
+ it("should handle non-Error exceptions during refresh", async () => {
560
+ // Arrange
561
+ vi.mocked(discoverProtectedResourceMetadata).mockRejectedValue("Unknown error");
562
+ // Act
563
+ const result = await handleRefresh({
564
+ mcpServerUrl: "https://mcp.example.com/server",
565
+ refreshToken: "old-refresh-token",
566
+ clientId: "test-client-id",
567
+ });
568
+ // Assert
569
+ expect(result.success).toBe(false);
570
+ expect(result.message).toBe("Token refresh failed");
571
+ });
572
+ it("should work without client secret", async () => {
573
+ // Arrange
574
+ const resourceMetadata = createMockResourceMetadata();
575
+ const oauthMetadata = createMockOAuthMetadata();
576
+ vi.mocked(discoverProtectedResourceMetadata).mockResolvedValue(resourceMetadata);
577
+ vi.mocked(discoverAuthServerMetadata).mockResolvedValue(oauthMetadata);
578
+ vi.mocked(refreshAccessToken).mockResolvedValue({
579
+ access_token: "new-access-token",
580
+ token_type: "Bearer",
581
+ });
582
+ // Act
583
+ const result = await handleRefresh({
584
+ mcpServerUrl: "https://mcp.example.com/server",
585
+ refreshToken: "old-refresh-token",
586
+ clientId: "test-client-id",
587
+ // No clientSecret
588
+ });
589
+ // Assert
590
+ expect(result.success).toBe(true);
591
+ expect(refreshAccessToken).toHaveBeenCalledWith(expect.objectContaining({
592
+ clientId: "test-client-id",
593
+ clientSecret: undefined,
594
+ }));
595
+ });
596
+ });
597
+ });
598
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolsdk.ai/registry",
3
- "version": "1.0.133",
3
+ "version": "1.0.134",
4
4
  "description": "An Open, Structured, and Standard Registry for MCP Servers and Packages.",
5
5
  "keywords": [
6
6
  "mcp",