@kya-os/mcp-i-core 1.3.0 → 1.3.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.
@@ -57,12 +57,14 @@ describe("ProviderResolver - Edge Cases", () => {
57
57
  const getProviderNamesMock = vi.fn().mockReturnValue([]);
58
58
  const loadFromAgentShieldMock = vi.fn().mockResolvedValue(undefined);
59
59
  const hasProviderMock = vi.fn().mockReturnValue(false);
60
+ const getConfiguredProviderMock = vi.fn().mockReturnValue(null);
60
61
 
61
62
  mockRegistry = {
62
63
  hasProvider: hasProviderMock,
63
64
  getAllProviders: vi.fn().mockReturnValue([]),
64
65
  getProviderNames: getProviderNamesMock,
65
66
  loadFromAgentShield: loadFromAgentShieldMock,
67
+ getConfiguredProvider: getConfiguredProviderMock,
66
68
  } as any;
67
69
 
68
70
  resolver = new ProviderResolver(mockRegistry, mockConfigService);
@@ -154,19 +156,16 @@ describe("ProviderResolver - Edge Cases", () => {
154
156
  requiredScopes: [],
155
157
  };
156
158
 
157
- // Should fall through to Priority 3
158
- (mockRegistry.hasProvider as any).mockReturnValue(false);
159
- (mockRegistry.getAllProviders as any).mockReturnValue([
160
- { clientId: "github_client_id" },
161
- ]);
162
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
159
+ // Should fall through to Priority 3 (configuredProvider)
160
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
161
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
163
162
 
164
163
  const provider = await resolver.resolveProvider(
165
164
  toolProtection,
166
165
  "test-project"
167
166
  );
168
167
 
169
- // Should use first configured provider (Priority 3)
168
+ // Should use configured provider (Priority 3)
170
169
  expect(provider).toBe("github");
171
170
  });
172
171
 
@@ -176,19 +175,16 @@ describe("ProviderResolver - Edge Cases", () => {
176
175
  requiredScopes: ["github:repo:read", "google:calendar:read"],
177
176
  };
178
177
 
179
- // Ambiguous scopes should fall through to Priority 3
180
- (mockRegistry.hasProvider as any).mockReturnValue(false);
181
- (mockRegistry.getAllProviders as any).mockReturnValue([
182
- { clientId: "github_client_id" },
183
- ]);
184
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
178
+ // Ambiguous scopes should fall through to Priority 3 (configuredProvider)
179
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
180
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
185
181
 
186
182
  const provider = await resolver.resolveProvider(
187
183
  toolProtection,
188
184
  "test-project"
189
185
  );
190
186
 
191
- // Should use first configured provider (Priority 3)
187
+ // Should use configured provider (Priority 3)
192
188
  expect(provider).toBe("github");
193
189
  });
194
190
 
@@ -198,19 +194,16 @@ describe("ProviderResolver - Edge Cases", () => {
198
194
  requiredScopes: ["custom:unknown:scope"],
199
195
  };
200
196
 
201
- // Unknown prefix should fall through to Priority 3
202
- (mockRegistry.hasProvider as any).mockReturnValue(false);
203
- (mockRegistry.getAllProviders as any).mockReturnValue([
204
- { clientId: "github_client_id" },
205
- ]);
206
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
197
+ // Unknown prefix should fall through to Priority 3 (configuredProvider)
198
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
199
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
207
200
 
208
201
  const provider = await resolver.resolveProvider(
209
202
  toolProtection,
210
203
  "test-project"
211
204
  );
212
205
 
213
- // Should use first configured provider (Priority 3)
206
+ // Should use configured provider (Priority 3)
214
207
  expect(provider).toBe("github");
215
208
  });
216
209
 
@@ -277,19 +270,16 @@ describe("ProviderResolver - Edge Cases", () => {
277
270
  requiredScopes: ["read", "write"], // No colons
278
271
  };
279
272
 
280
- // Should fall through to Priority 3
281
- (mockRegistry.hasProvider as any).mockReturnValue(false);
282
- (mockRegistry.getAllProviders as any).mockReturnValue([
283
- { clientId: "github_client_id" },
284
- ]);
285
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
273
+ // Should fall through to Priority 3 (configuredProvider)
274
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
275
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
286
276
 
287
277
  const provider = await resolver.resolveProvider(
288
278
  toolProtection,
289
279
  "test-project"
290
280
  );
291
281
 
292
- // Should use first configured provider (Priority 3)
282
+ // Should use configured provider (Priority 3)
293
283
  expect(provider).toBe("github");
294
284
  });
295
285
 
@@ -299,90 +289,102 @@ describe("ProviderResolver - Edge Cases", () => {
299
289
  requiredScopes: ["github:repo:read"],
300
290
  };
301
291
 
302
- // Inferred provider exists but not in registry
303
- (mockRegistry.getProviderNames as any).mockReturnValue(["google"]); // Only google configured
304
- (mockRegistry.hasProvider as any).mockReturnValue(false); // github not in registry
305
-
306
- // Should fall through to Priority 3
307
- (mockRegistry.getAllProviders as any).mockReturnValue([
308
- { clientId: "google_client_id" },
309
- ]);
292
+ // Inferred provider (github) exists but not in registry
293
+ // configuredProvider is google
310
294
  (mockRegistry.getProviderNames as any).mockReturnValue(["google"]);
295
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "google");
296
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("google");
311
297
 
312
298
  const provider = await resolver.resolveProvider(
313
299
  toolProtection,
314
300
  "test-project"
315
301
  );
316
302
 
317
- // Should use first configured provider (Priority 3)
303
+ // Should use configured provider (Priority 3)
318
304
  expect(provider).toBe("google");
319
305
  });
320
306
  });
321
307
 
322
- describe("Fallback behavior (Priority 3)", () => {
323
- it("should log deprecation warning for Phase 1 fallback", async () => {
308
+ describe("Fallback behavior (Priority 3 - configuredProvider)", () => {
309
+ it("should log warning when using configuredProvider fallback", async () => {
324
310
  const toolProtection: ToolProtection = {
325
311
  requiresDelegation: true,
326
312
  requiredScopes: ["custom:scope"],
327
313
  };
328
314
 
329
- (mockRegistry.hasProvider as any).mockReturnValue(false);
330
- (mockRegistry.getAllProviders as any).mockReturnValue([
331
- { clientId: "github_client_id" },
332
- ]);
333
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
315
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
316
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
334
317
 
335
318
  const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
336
319
 
337
320
  await resolver.resolveProvider(toolProtection, "test-project");
338
321
 
339
322
  expect(consoleSpy).toHaveBeenCalledWith(
340
- expect.stringContaining("deprecated")
323
+ expect.stringContaining("project-configured provider")
341
324
  );
342
325
  expect(consoleSpy).toHaveBeenCalledWith(
343
- expect.stringContaining("first configured provider")
326
+ expect.stringContaining("github")
344
327
  );
345
328
 
346
329
  consoleSpy.mockRestore();
347
330
  });
348
331
 
349
- it("should use first configured provider when oauthProvider not specified", async () => {
332
+ it("should use configuredProvider when oauthProvider not specified", async () => {
350
333
  const toolProtection: ToolProtection = {
351
334
  requiresDelegation: true,
352
335
  requiredScopes: ["custom:scope"],
353
336
  };
354
337
 
338
+ // Multiple providers available, but configuredProvider is google
339
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) =>
340
+ name === "github" || name === "google"
341
+ );
342
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("google");
343
+
344
+ const provider = await resolver.resolveProvider(
345
+ toolProtection,
346
+ "test-project"
347
+ );
348
+
349
+ // Should use configuredProvider, not first alphabetically
350
+ expect(provider).toBe("google");
351
+ });
352
+
353
+ it("should NOT fall back to first provider alphabetically", async () => {
354
+ const toolProtection: ToolProtection = {
355
+ requiresDelegation: true,
356
+ requiredScopes: ["custom:scope"],
357
+ };
358
+
359
+ // Multiple providers in registry, but NO configuredProvider
355
360
  (mockRegistry.hasProvider as any).mockReturnValue(false);
361
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue(null);
356
362
  (mockRegistry.getAllProviders as any).mockReturnValue([
357
363
  { clientId: "github_client_id" },
358
364
  { clientId: "google_client_id" },
359
365
  ]);
360
366
  (mockRegistry.getProviderNames as any).mockReturnValue(["github", "google"]);
361
367
 
362
- const provider = await resolver.resolveProvider(
363
- toolProtection,
364
- "test-project"
365
- );
366
-
367
- // Should use first provider
368
- expect(provider).toBe("github");
368
+ // Should throw error, NOT pick first provider
369
+ await expect(
370
+ resolver.resolveProvider(toolProtection, "test-project")
371
+ ).rejects.toThrow(/no provider is configured/);
369
372
  });
370
373
  });
371
374
 
372
375
  describe("Error scenarios (Priority 4)", () => {
373
- it("should throw error if no providers configured", async () => {
376
+ it("should throw error if no provider is configured", async () => {
374
377
  const toolProtection: ToolProtection = {
375
378
  requiresDelegation: true,
376
379
  requiredScopes: [],
377
380
  };
378
381
 
379
382
  (mockRegistry.hasProvider as any).mockReturnValue(false);
380
- (mockRegistry.getAllProviders as any).mockReturnValue([]);
381
- (mockRegistry.getProviderNames as any).mockReturnValue([]);
383
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue(null);
382
384
 
383
385
  await expect(
384
386
  resolver.resolveProvider(toolProtection, "test-project")
385
- ).rejects.toThrow(/no provider could be resolved/);
387
+ ).rejects.toThrow(/no provider is configured/);
386
388
  });
387
389
 
388
390
  it("should include project ID in error message", async () => {
@@ -392,8 +394,7 @@ describe("ProviderResolver - Edge Cases", () => {
392
394
  };
393
395
 
394
396
  (mockRegistry.hasProvider as any).mockReturnValue(false);
395
- (mockRegistry.getAllProviders as any).mockReturnValue([]);
396
- (mockRegistry.getProviderNames as any).mockReturnValue([]);
397
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue(null);
397
398
 
398
399
  try {
399
400
  await resolver.resolveProvider(toolProtection, "test-project-123");
@@ -409,15 +410,14 @@ describe("ProviderResolver - Edge Cases", () => {
409
410
  };
410
411
 
411
412
  (mockRegistry.hasProvider as any).mockReturnValue(false);
412
- (mockRegistry.getAllProviders as any).mockReturnValue([]);
413
- (mockRegistry.getProviderNames as any).mockReturnValue([]);
413
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue(null);
414
414
 
415
415
  try {
416
416
  await resolver.resolveProvider(toolProtection, "test-project");
417
417
  } catch (error) {
418
418
  const message = (error as Error).message;
419
- expect(message).toContain("oauthProvider");
420
- expect(message).toContain("configure");
419
+ expect(message).toContain("AgentShield dashboard");
420
+ expect(message).toContain("Configure");
421
421
  }
422
422
  });
423
423
  });
@@ -483,5 +483,109 @@ describe("ProviderResolver - Edge Cases", () => {
483
483
  expect(provider).toBe("github");
484
484
  });
485
485
  });
486
+
487
+ describe("configuredProvider behavior (Priority 3)", () => {
488
+ it("should use configuredProvider when tool has no oauthProvider", async () => {
489
+ const toolProtection: ToolProtection = {
490
+ requiresDelegation: true,
491
+ requiredScopes: ["greet:execute"], // No scope inference match
492
+ };
493
+
494
+ // configuredProvider is github
495
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
496
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
497
+
498
+ const provider = await resolver.resolveProvider(
499
+ toolProtection,
500
+ "test-project"
501
+ );
502
+
503
+ expect(provider).toBe("github");
504
+ expect(mockRegistry.getConfiguredProvider).toHaveBeenCalled();
505
+ });
506
+
507
+ it("should throw when configuredProvider is null and tool needs OAuth", async () => {
508
+ const toolProtection: ToolProtection = {
509
+ requiresDelegation: true,
510
+ requiredScopes: ["greet:execute"],
511
+ };
512
+
513
+ // No configuredProvider set
514
+ (mockRegistry.hasProvider as any).mockReturnValue(false);
515
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue(null);
516
+
517
+ await expect(
518
+ resolver.resolveProvider(toolProtection, "test-project")
519
+ ).rejects.toThrow(/no provider is configured/);
520
+ });
521
+
522
+ it("should NOT use unconfigured providers as fallback", async () => {
523
+ const toolProtection: ToolProtection = {
524
+ requiresDelegation: true,
525
+ requiredScopes: ["greet:execute"],
526
+ };
527
+
528
+ // Registry has providers but configuredProvider is null
529
+ // This simulates AgentShield returning all providers but none configured
530
+ (mockRegistry.hasProvider as any).mockReturnValue(false);
531
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue(null);
532
+ (mockRegistry.getAllProviders as any).mockReturnValue([
533
+ { clientId: "github_client_id" },
534
+ { clientId: "google_client_id" },
535
+ { clientId: "microsoft_client_id" },
536
+ ]);
537
+ (mockRegistry.getProviderNames as any).mockReturnValue(["github", "google", "microsoft"]);
538
+
539
+ // Should NOT pick "github" alphabetically - should throw
540
+ await expect(
541
+ resolver.resolveProvider(toolProtection, "test-project")
542
+ ).rejects.toThrow(/no provider is configured/);
543
+ });
544
+
545
+ it("should prefer tool-specific oauthProvider over configuredProvider", async () => {
546
+ const toolProtection: ToolProtection = {
547
+ requiresDelegation: true,
548
+ requiredScopes: [],
549
+ oauthProvider: "google", // Tool specifies google
550
+ };
551
+
552
+ // configuredProvider is github, but tool wants google
553
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) =>
554
+ name === "github" || name === "google"
555
+ );
556
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
557
+ (mockRegistry.getProviderNames as any).mockReturnValue(["github", "google"]);
558
+
559
+ const provider = await resolver.resolveProvider(
560
+ toolProtection,
561
+ "test-project"
562
+ );
563
+
564
+ // Priority 1 (tool-specific) should win over Priority 3 (configuredProvider)
565
+ expect(provider).toBe("google");
566
+ });
567
+
568
+ it("should prefer scope-inferred provider over configuredProvider", async () => {
569
+ const toolProtection: ToolProtection = {
570
+ requiresDelegation: true,
571
+ requiredScopes: ["google:calendar:read"], // Infers google
572
+ };
573
+
574
+ // configuredProvider is github, but scopes infer google
575
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) =>
576
+ name === "github" || name === "google"
577
+ );
578
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
579
+ (mockRegistry.getProviderNames as any).mockReturnValue(["github", "google"]);
580
+
581
+ const provider = await resolver.resolveProvider(
582
+ toolProtection,
583
+ "test-project"
584
+ );
585
+
586
+ // Priority 2 (scope inference) should win over Priority 3 (configuredProvider)
587
+ expect(provider).toBe("google");
588
+ });
589
+ });
486
590
  });
487
591
 
@@ -37,6 +37,8 @@ describe("Provider Resolution Integration", () => {
37
37
  requiresClientSecret: false,
38
38
  },
39
39
  },
40
+ // Explicitly configured provider (Priority 3 fallback)
41
+ configuredProvider: "github",
40
42
  };
41
43
 
42
44
  beforeEach(() => {
@@ -95,7 +97,7 @@ describe("Provider Resolution Integration", () => {
95
97
  consoleSpy.mockRestore();
96
98
  });
97
99
 
98
- it("should fall back to first configured provider for Phase 1 compatibility", async () => {
100
+ it("should fall back to configuredProvider for Phase 1 compatibility", async () => {
99
101
  await providerRegistry.loadFromAgentShield("test-project");
100
102
 
101
103
  const toolProtection: ToolProtection = {
@@ -111,10 +113,10 @@ describe("Provider Resolution Integration", () => {
111
113
  "test-project"
112
114
  );
113
115
 
114
- // Should use first configured provider (github)
116
+ // Should use configuredProvider (github)
115
117
  expect(provider).toBe("github");
116
118
  expect(consoleSpy).toHaveBeenCalledWith(
117
- expect.stringContaining("deprecated")
119
+ expect.stringContaining("project-configured provider")
118
120
  );
119
121
 
120
122
  consoleSpy.mockRestore();
@@ -157,9 +159,11 @@ describe("Provider Resolution Integration", () => {
157
159
  "test-project"
158
160
  );
159
161
 
160
- // Should fall back to first provider
162
+ // Should fall back to configuredProvider
161
163
  expect(provider).toBe("github");
162
- expect(consoleSpy).toHaveBeenCalled();
164
+ expect(consoleSpy).toHaveBeenCalledWith(
165
+ expect.stringContaining("project-configured provider")
166
+ );
163
167
 
164
168
  consoleSpy.mockRestore();
165
169
  });
@@ -45,6 +45,7 @@ describe("ProviderResolver", () => {
45
45
  getAllProviders: vi.fn().mockReturnValue([]),
46
46
  getProviderNames: vi.fn().mockReturnValue([]),
47
47
  loadFromAgentShield: vi.fn().mockResolvedValue(undefined),
48
+ getConfiguredProvider: vi.fn().mockReturnValue(null), // New method for configuredProvider
48
49
  } as any;
49
50
 
50
51
  resolver = new ProviderResolver(mockRegistry, mockConfigService);
@@ -121,42 +122,37 @@ describe("ProviderResolver", () => {
121
122
  expect(provider).toBe("google");
122
123
  });
123
124
 
124
- it("should return null for ambiguous scopes", async () => {
125
+ it("should fall back to configuredProvider for ambiguous scopes", async () => {
125
126
  const toolProtection: ToolProtection = {
126
127
  requiresDelegation: true,
127
128
  requiredScopes: ["github:read", "google:read"],
128
129
  };
129
130
 
130
- (mockRegistry.hasProvider as any).mockReturnValue(true);
131
-
132
- // Should fall through to Priority 3
133
- (mockRegistry.getAllProviders as any).mockReturnValue([
134
- { clientId: "github_client_id" },
135
- ]);
136
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
131
+ // Ambiguous scopes - both infer different providers
132
+ // Should fall through to Priority 3 (configuredProvider)
133
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
134
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
137
135
 
138
136
  const provider = await resolver.resolveProvider(
139
137
  toolProtection,
140
138
  "test-project"
141
139
  );
142
140
 
143
- // Falls back to first configured provider
141
+ // Falls back to configuredProvider
144
142
  expect(provider).toBe("github");
145
143
  });
146
144
  });
147
145
 
148
- describe("resolveProvider - Priority 3: First configured provider", () => {
149
- it("should use first configured provider as fallback", async () => {
146
+ describe("resolveProvider - Priority 3: Project-configured provider", () => {
147
+ it("should use configuredProvider as fallback", async () => {
150
148
  const toolProtection: ToolProtection = {
151
149
  requiresDelegation: true,
152
150
  requiredScopes: ["custom:scope"],
153
151
  };
154
152
 
155
- (mockRegistry.hasProvider as any).mockReturnValue(false);
156
- (mockRegistry.getAllProviders as any).mockReturnValue([
157
- { clientId: "github_client_id" },
158
- ]);
159
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
153
+ // configuredProvider is github
154
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
155
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
160
156
 
161
157
  const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
162
158
 
@@ -167,30 +163,28 @@ describe("ProviderResolver", () => {
167
163
 
168
164
  expect(provider).toBe("github");
169
165
  expect(consoleSpy).toHaveBeenCalledWith(
170
- expect.stringContaining("deprecated")
166
+ expect.stringContaining("project-configured provider")
171
167
  );
172
168
 
173
169
  consoleSpy.mockRestore();
174
170
  });
175
171
 
176
- it("should log deprecation warning when using fallback", async () => {
172
+ it("should log warning when using configuredProvider fallback", async () => {
177
173
  const toolProtection: ToolProtection = {
178
174
  requiresDelegation: true,
179
175
  requiredScopes: [],
180
176
  };
181
177
 
182
- (mockRegistry.hasProvider as any).mockReturnValue(false);
183
- (mockRegistry.getAllProviders as any).mockReturnValue([
184
- { clientId: "github_client_id" },
185
- ]);
186
- (mockRegistry.getProviderNames as any).mockReturnValue(["github"]);
178
+ // configuredProvider is github
179
+ (mockRegistry.hasProvider as any).mockImplementation((name: string) => name === "github");
180
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue("github");
187
181
 
188
182
  const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
189
183
 
190
184
  await resolver.resolveProvider(toolProtection, "test-project");
191
185
 
192
186
  expect(consoleSpy).toHaveBeenCalledWith(
193
- expect.stringContaining("deprecated")
187
+ expect.stringContaining("Consider explicitly setting oauthProvider")
194
188
  );
195
189
 
196
190
  consoleSpy.mockRestore();
@@ -198,19 +192,21 @@ describe("ProviderResolver", () => {
198
192
  });
199
193
 
200
194
  describe("resolveProvider - Priority 4: Error if no provider", () => {
201
- it("should throw error if no provider can be resolved", async () => {
195
+ it("should throw error if no provider is configured", async () => {
202
196
  const toolProtection: ToolProtection = {
203
197
  requiresDelegation: true,
204
198
  requiredScopes: [],
205
199
  };
206
200
 
201
+ // No configuredProvider set
207
202
  (mockRegistry.hasProvider as any).mockReturnValue(false);
203
+ (mockRegistry.getConfiguredProvider as any).mockReturnValue(null);
208
204
  (mockRegistry.getAllProviders as any).mockReturnValue([]);
209
205
  (mockRegistry.getProviderNames as any).mockReturnValue([]);
210
206
 
211
207
  await expect(
212
208
  resolver.resolveProvider(toolProtection, "test-project")
213
- ).rejects.toThrow(/no provider could be resolved/);
209
+ ).rejects.toThrow(/no provider is configured/);
214
210
  });
215
211
  });
216
212
  });
@@ -99,6 +99,7 @@ export class OAuthConfigService {
99
99
  success: boolean;
100
100
  data?: {
101
101
  providers?: Record<string, unknown>;
102
+ configuredProvider?: string | null;
102
103
  };
103
104
  };
104
105
 
@@ -116,11 +117,12 @@ export class OAuthConfigService {
116
117
  }
117
118
 
118
119
  // Build OAuthConfig object
119
- // Note: API does NOT return defaultProvider field (Phase 1 architecture)
120
- // Phase 1 uses configured provider as temporary fallback
121
- // Phase 2+ requires tools to explicitly specify oauthProvider
120
+ // Extract configuredProvider from API response - this indicates which provider
121
+ // the user has actually configured in AgentShield dashboard
122
+ const configuredProvider = result.data.configuredProvider || null;
122
123
  const config: OAuthConfig = {
123
124
  providers: providers as Record<string, OAuthProvider>,
125
+ configuredProvider,
124
126
  };
125
127
 
126
128
  // Cache config
@@ -130,6 +132,7 @@ export class OAuthConfigService {
130
132
  projectId,
131
133
  providerCount: Object.keys(providers).length,
132
134
  providers: Object.keys(providers),
135
+ configuredProvider,
133
136
  expiresAt: new Date(
134
137
  Date.now() + this.config.cacheTtl
135
138
  ).toISOString(),
@@ -19,6 +19,7 @@ import type { ProviderValidator } from "./provider-validator.js";
19
19
  */
20
20
  export class OAuthProviderRegistry {
21
21
  private providers: Map<string, OAuthProvider> = new Map();
22
+ private _configuredProvider: string | null = null;
22
23
 
23
24
  constructor(
24
25
  private configService: OAuthConfigService,
@@ -30,6 +31,7 @@ export class OAuthProviderRegistry {
30
31
  *
31
32
  * Fetches OAuth configuration and caches providers in memory.
32
33
  * Clears existing providers before loading new ones.
34
+ * Also stores the configured provider from the API response.
33
35
  *
34
36
  * @param projectId - Project ID to load providers for
35
37
  */
@@ -39,12 +41,28 @@ export class OAuthProviderRegistry {
39
41
  // Clear existing providers
40
42
  this.providers.clear();
41
43
 
44
+ // Store the configured provider from API response
45
+ // This is the provider the user has explicitly configured in AgentShield dashboard
46
+ this._configuredProvider = config.configuredProvider || null;
47
+
42
48
  // Register all providers from config
43
49
  for (const [name, providerConfig] of Object.entries(config.providers)) {
44
50
  this.providers.set(name, providerConfig);
45
51
  }
46
52
  }
47
53
 
54
+ /**
55
+ * Get the explicitly configured provider for this project
56
+ *
57
+ * Returns the provider that the user has configured in AgentShield dashboard.
58
+ * Used by ProviderResolver as fallback when tool doesn't specify oauthProvider.
59
+ *
60
+ * @returns Configured provider name, or null if no provider is configured
61
+ */
62
+ getConfiguredProvider(): string | null {
63
+ return this._configuredProvider;
64
+ }
65
+
48
66
  /**
49
67
  * Get provider by name
50
68
  *