@@ -107,6 +107,8 @@ const serveMetadataServer = (config: {
107107 | "error"
108108 | null ;
109109 readonly authServerScopes ?: readonly string [ ] ;
110+ readonly authServerAuthorizationPath ?: string ;
111+ readonly authServerTokenPath ?: string ;
110112} ) =>
111113 Effect . gen ( function * ( ) {
112114 const baseUrlRef = { value : "" } ;
@@ -133,8 +135,8 @@ const serveMetadataServer = (config: {
133135 ) {
134136 return HttpServerResponse . jsonUnsafe ( {
135137 issuer : base ,
136- authorization_endpoint : `${ base } /authorize` ,
137- token_endpoint : `${ base } /token` ,
138+ authorization_endpoint : `${ base } ${ config . authServerAuthorizationPath ?? " /authorize" } ` ,
139+ token_endpoint : `${ base } ${ config . authServerTokenPath ?? " /token" } ` ,
138140 response_types_supported : [ "code" ] ,
139141 code_challenge_methods_supported : [ "S256" ] ,
140142 scopes_supported : config . authServerScopes ,
@@ -325,6 +327,49 @@ describe("oauth.start integration-driven scopes", () => {
325327 ) ,
326328 ) ;
327329
330+ it . effect ( "keeps declared scopes when same-origin metadata describes another OAuth surface" , ( ) =>
331+ Effect . scoped (
332+ Effect . gen ( function * ( ) {
333+ const server = yield * serveMetadataServer ( {
334+ authServerScopes : [ "mcp:connect" ] ,
335+ authServerAuthorizationPath : "/oauth/mcp" ,
336+ } ) ;
337+ const plugins = [
338+ memoryCredentialsPlugin ( ) ,
339+ makeScopePlugin ( { scopes : [ "file_content:read" , "file_comments:write" ] } ) ,
340+ ] as const ;
341+ const { executor } = yield * makeTestWorkspaceHarness ( { plugins } ) ;
342+ yield * executor . acme . seed ( ) ;
343+
344+ yield * executor . oauth . createClient ( {
345+ owner : "org" ,
346+ slug : CLIENT ,
347+ authorizationUrl : server . authorizationEndpoint ,
348+ tokenUrl : server . tokenEndpoint ,
349+ grant : "authorization_code" ,
350+ clientId : "test-client" ,
351+ clientSecret : "test-secret" ,
352+ } ) ;
353+
354+ const started = yield * executor . oauth . start ( {
355+ owner : "org" ,
356+ client : CLIENT ,
357+ clientOwner : "org" ,
358+ name : ConnectionName . make ( "main" ) ,
359+ integration : INTEG ,
360+ template : TEMPLATE ,
361+ } ) ;
362+ expect ( started . status ) . toBe ( "redirect" ) ;
363+ if ( started . status !== "redirect" ) return ;
364+
365+ expect ( scopesFromAuthorizeUrl ( started . authorizationUrl ) ) . toEqual ( [
366+ "file_content:read" ,
367+ "file_comments:write" ,
368+ ] ) ;
369+ } ) ,
370+ ) ,
371+ ) ;
372+
328373 it . effect (
329374 "(d) for MCP, the resource's own scopes_supported wins over a divergent authorization server" ,
330375 ( ) =>
0 commit comments