Skip to content

Commit 61b4f0d

Browse files
committed
Align resolveTokenScopeForServer api:// guard with Python implementation
Replace broad startsWith('api://') check with an exact match against ATG_APP_ID_URI ('api://ea9ffc3e-...'), mirroring the Python utility.py logic. This allows V2 servers that use api:// audience URIs to get their own per-server token instead of being silently routed to the shared ATG scope.
1 parent 364aa71 commit 61b4f0d

2 files changed

Lines changed: 11 additions & 4 deletions

File tree

packages/agents-a365-tooling/src/configuration/ToolingConfiguration.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ import { MCPServerConfig } from '../contracts';
99
const MCP_PLATFORM_PROD_BASE_URL = 'https://agent365.svc.cloud.microsoft';
1010
const PROD_MCP_PLATFORM_AUTHENTICATION_SCOPE = 'ea9ffc3e-8a23-4a7d-836d-234d7c7565c1/.default';
1111
const ATG_APP_ID = 'ea9ffc3e-8a23-4a7d-836d-234d7c7565c1';
12+
const ATG_APP_ID_URI = `api://${ATG_APP_ID}`; // "api://ea9ffc3e-8a23-4a7d-836d-234d7c7565c1"
1213

1314
/**
1415
* Resolve the OAuth scope to request for a given MCP server.
1516
* V2 servers carry their own audience GUID in the `audience` field; V1 servers (no audience,
16-
* or audience matching the shared ATG AppId) fall back to the shared ATG scope.
17+
* or audience matching the shared ATG AppId or its api:// URI form) fall back to the shared ATG scope.
1718
*/
1819
export function resolveTokenScopeForServer(server: MCPServerConfig): string {
1920
if (server.audience &&
2021
server.audience !== ATG_APP_ID &&
21-
!server.audience.startsWith('api://')) {
22+
server.audience !== ATG_APP_ID_URI) {
2223
// V2 server: use explicit scope if provided, otherwise fall back to /.default
2324
if (server.scope) {
2425
return `${server.audience}/${server.scope}`;

tests/tooling/configuration/ToolingConfiguration.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,14 @@ describe('resolveTokenScopeForServer', () => {
266266
expect(resolveTokenScopeForServer({ mcpServerName: 'mail', url: 'https://mail.example.com', audience: ATG_APP_ID })).toBe(ATG_SCOPE);
267267
});
268268

269-
it('should return ATG scope when audience starts with api:// (not a plain GUID)', () => {
270-
expect(resolveTokenScopeForServer({ mcpServerName: 'mail', url: 'https://mail.example.com', audience: 'api://custom-app-id' })).toBe(ATG_SCOPE);
269+
it('should return ATG scope when audience is the ATG api:// URI form', () => {
270+
const atgAppIdUri = `api://${ATG_APP_ID}`;
271+
expect(resolveTokenScopeForServer({ mcpServerName: 'mail', url: 'https://mail.example.com', audience: atgAppIdUri })).toBe(ATG_SCOPE);
272+
});
273+
274+
it('should return per-server scope when audience is a non-ATG api:// URI (V2 server)', () => {
275+
const v2AppIdUri = 'api://custom-app-id';
276+
expect(resolveTokenScopeForServer({ mcpServerName: 'mail', url: 'https://mail.example.com', audience: v2AppIdUri })).toBe(`${v2AppIdUri}/.default`);
271277
});
272278

273279
it('should return per-server scope for a V2 GUID audience', () => {

0 commit comments

Comments
 (0)