@@ -808,41 +808,45 @@ export const layer = Layer.effect(
808808 return yield * storeClient ( s , mcpName , client , listed , mcpConfig . timeout )
809809 }
810810
811- log . info ( "opening browser for oauth" , { mcpName, url : result . authorizationUrl , state : result . oauthState } )
811+ return yield * Effect . gen ( function * ( ) {
812+ log . info ( "opening browser for oauth" , { mcpName, url : result . authorizationUrl , state : result . oauthState } )
812813
813- const callbackPromise = McpOAuthCallback . waitForCallback ( result . oauthState , mcpName )
814+ const callbackPromise = McpOAuthCallback . waitForCallback ( result . oauthState , mcpName )
814815
815- yield * Effect . tryPromise ( ( ) => open ( result . authorizationUrl ) ) . pipe (
816- Effect . flatMap ( ( subprocess ) =>
817- Effect . callback < void , Error > ( ( resume ) => {
818- const timer = setTimeout ( ( ) => resume ( Effect . void ) , 500 )
819- subprocess . on ( "error" , ( err ) => {
820- clearTimeout ( timer )
821- resume ( Effect . fail ( err ) )
822- } )
823- subprocess . on ( "exit" , ( code ) => {
824- if ( code !== null && code !== 0 ) {
816+ yield * Effect . tryPromise ( ( ) => open ( result . authorizationUrl ) ) . pipe (
817+ Effect . flatMap ( ( subprocess ) =>
818+ Effect . callback < void , Error > ( ( resume ) => {
819+ const timer = setTimeout ( ( ) => resume ( Effect . void ) , 500 )
820+ subprocess . on ( "error" , ( err ) => {
825821 clearTimeout ( timer )
826- resume ( Effect . fail ( new Error ( `Browser open failed with exit code ${ code } ` ) ) )
827- }
828- } )
822+ resume ( Effect . fail ( err ) )
823+ } )
824+ subprocess . on ( "exit" , ( code ) => {
825+ if ( code !== null && code !== 0 ) {
826+ clearTimeout ( timer )
827+ resume ( Effect . fail ( new Error ( `Browser open failed with exit code ${ code } ` ) ) )
828+ }
829+ } )
830+ } ) ,
831+ ) ,
832+ Effect . catch ( ( ) => {
833+ log . warn ( "failed to open browser, user must open URL manually" , { mcpName } )
834+ return bus . publish ( BrowserOpenFailed , { mcpName, url : result . authorizationUrl } ) . pipe ( Effect . ignore )
829835 } ) ,
830- ) ,
831- Effect . catch ( ( ) => {
832- log . warn ( "failed to open browser, user must open URL manually" , { mcpName } )
833- return bus . publish ( BrowserOpenFailed , { mcpName, url : result . authorizationUrl } ) . pipe ( Effect . ignore )
834- } ) ,
835- )
836+ )
836837
837- const code = yield * Effect . promise ( ( ) => callbackPromise )
838+ const code = yield * Effect . promise ( ( ) => callbackPromise )
838839
839- const storedState = yield * auth . getOAuthState ( mcpName )
840- if ( storedState !== result . oauthState ) {
840+ const storedState = yield * auth . getOAuthState ( mcpName )
841+ if ( storedState !== result . oauthState ) {
842+ yield * auth . clearOAuthState ( mcpName )
843+ throw new Error ( "OAuth state mismatch - potential CSRF attack" )
844+ }
841845 yield * auth . clearOAuthState ( mcpName )
842- throw new Error ( "OAuth state mismatch - potential CSRF attack" )
843- }
844- yield * auth . clearOAuthState ( mcpName )
845- return yield * finishAuth ( mcpName , code )
846+ return yield * finishAuth ( mcpName , code )
847+ } ) . pipe (
848+ Effect . ensuring ( Effect . tryPromise ( ( ) => McpOAuthCallback . stopIfIdle ( ) ) . pipe ( Effect . ignore ) ) ,
849+ )
846850 } )
847851
848852 const finishAuth = Effect . fn ( "MCP.finishAuth" ) ( function * ( mcpName : string , authorizationCode : string ) {
0 commit comments