@@ -73,15 +73,21 @@ describe("MCP callback server", () => {
7373 }
7474 } ) ;
7575
76- test ( "binds an ephemeral OS-assigned port on 127.0.0.1" , async ( ) => {
77- const server = await startCallbackServer ( ) ;
76+ test ( "binds concurrent servers to distinct loopback ports" , async ( ) => {
77+ const first = await startCallbackServer ( ) ;
78+ const second = await startCallbackServer ( ) ;
7879 try {
79- const url = new URL ( server . redirectUrl ) ;
80-
81- expect ( url . hostname ) . toBe ( "127.0.0.1" ) ;
82- expect ( Number ( url . port ) ) . toBeGreaterThan ( 0 ) ;
80+ const firstUrl = new URL ( first . redirectUrl ) ;
81+ const secondUrl = new URL ( second . redirectUrl ) ;
82+
83+ expect ( firstUrl . hostname ) . toBe ( "127.0.0.1" ) ;
84+ expect ( secondUrl . hostname ) . toBe ( "127.0.0.1" ) ;
85+ expect ( firstUrl . port ) . not . toBe ( secondUrl . port ) ;
86+ expect ( Number ( firstUrl . port ) ) . toBeGreaterThan ( 0 ) ;
87+ expect ( Number ( secondUrl . port ) ) . toBeGreaterThan ( 0 ) ;
8388 } finally {
84- server . close ( ) ;
89+ first . close ( ) ;
90+ second . close ( ) ;
8591 }
8692 } ) ;
8793
@@ -100,7 +106,24 @@ describe("MCP callback server", () => {
100106 ) ;
101107 } ) ;
102108
103- test ( "reports a clear actionable error when the server fails to bind" , async ( ) => {
109+ test ( "close is idempotent and does not leak a waiter after a late callback" , async ( ) => {
110+ const server = await startCallbackServer ( ) ;
111+ authorize ( server , "expected" ) ;
112+ server . close ( ) ;
113+ server . close ( ) ;
114+
115+ await expect (
116+ fetch ( `${ server . redirectUrl } ?code=abc&state=expected` ) . then (
117+ ( ) => "fetched" ,
118+ ( err : unknown ) => err ,
119+ ) ,
120+ ) . resolves . toBeInstanceOf ( Error ) ;
121+ await expect ( server . waitForCode ( new AbortController ( ) . signal ) ) . rejects . toThrow (
122+ "closed before authorization completed" ,
123+ ) ;
124+ } ) ;
125+
126+ test ( "rejects start when listen fails without rewriting the OS error as a retry" , async ( ) => {
104127 await withMockedModuleDuring (
105128 import . meta. resolve ( "node:http" ) ,
106129 ( real : typeof import ( "node:http" ) ) => ( {
@@ -113,23 +136,17 @@ describe("MCP callback server", () => {
113136 return fake ;
114137 } ,
115138 listen : ( ) => {
116- listeners . error ?.( new Error ( "listen EADDRINUSE: address already in use " ) ) ;
139+ listeners . error ?.( new Error ( "listen EACCES: permission denied " ) ) ;
117140 } ,
118141 address : ( ) : undefined => undefined ,
119142 } ;
120143 return fake as unknown as Server ;
121144 } ) as typeof real . createServer ,
122145 } ) ,
123146 async ( ) => {
124- const err : unknown = await startCallbackServer ( ) . then (
125- ( ) => undefined ,
126- ( failure : unknown ) => failure ,
147+ await expect ( startCallbackServer ( ) ) . rejects . toThrow (
148+ "Could not start the OAuth callback server: listen EACCES: permission denied" ,
127149 ) ;
128- const error = err as Error ;
129-
130- expect ( error . message ) . toContain ( "OAuth callback server" ) ;
131- expect ( error . message ) . toContain ( "EADDRINUSE" ) ;
132- expect ( error . message ) . toContain ( "retry" ) ;
133150 } ,
134151 ) ;
135152 } ) ;
0 commit comments