@@ -564,7 +564,7 @@ describe("Connection", () => {
564564 expect ( response . authMethods ?. [ 0 ] . id ) . toBe ( "oauth" ) ;
565565 } ) ;
566566
567- it ( "preserves unknown properties on known incoming params" , async ( ) => {
567+ it ( "strips unknown properties on known incoming params" , async ( ) => {
568568 let receivedInitializeParams : Record < string , unknown > | undefined ;
569569 let receivedSessionUpdate : Record < string , unknown > | undefined ;
570570
@@ -661,26 +661,20 @@ describe("Connection", () => {
661661 } as any ) ;
662662
663663 await vi . waitFor ( ( ) => {
664- expect ( receivedInitializeParams ) . toMatchObject ( {
665- extraTopLevel : "keep me" ,
666- clientCapabilities : {
667- customCapability : {
668- enabled : true ,
669- } ,
670- fs : {
671- experimentalFs : true ,
672- } ,
673- } ,
674- } ) ;
664+ expect ( receivedInitializeParams ) . not . toHaveProperty ( "extraTopLevel" ) ;
665+ expect ( receivedInitializeParams ) . not . toHaveProperty (
666+ "clientCapabilities.customCapability" ,
667+ ) ;
668+ expect ( receivedInitializeParams ) . not . toHaveProperty (
669+ "clientCapabilities.fs.experimentalFs" ,
670+ ) ;
675671
676- expect ( receivedSessionUpdate ) . toMatchObject ( {
677- extraNotificationField : "keep this too" ,
678- update : {
679- extraUpdateField : {
680- keep : true ,
681- } ,
682- } ,
683- } ) ;
672+ expect ( receivedSessionUpdate ) . not . toHaveProperty (
673+ "extraNotificationField" ,
674+ ) ;
675+ expect ( receivedSessionUpdate ) . not . toHaveProperty (
676+ "update.extraUpdateField" ,
677+ ) ;
684678 } ) ;
685679 } ) ;
686680
@@ -2002,6 +1996,22 @@ describe("Connection", () => {
20021996 expect ( ( receivedRequest as any ) ?. sessionId ) . toBe ( "test-session" ) ;
20031997 expect ( ( receivedRequest as any ) ?. mode ) . toBe ( "form" ) ;
20041998
1999+ // Test url-mode elicitation request
2000+ receivedRequest = undefined ;
2001+ const urlResponse = await clientConnection . unstable_createElicitation ( {
2002+ sessionId : "test-session" ,
2003+ mode : "url" ,
2004+ message : "Please authenticate" ,
2005+ elicitationId : "elic-url-1" ,
2006+ url : "https://example.com/auth" ,
2007+ } ) ;
2008+
2009+ expect ( urlResponse . action ) . toBe ( "accept" ) ;
2010+ expect ( ( receivedRequest as any ) ?. message ) . toBe ( "Please authenticate" ) ;
2011+ expect ( ( receivedRequest as any ) ?. mode ) . toBe ( "url" ) ;
2012+ expect ( ( receivedRequest as any ) ?. url ) . toBe ( "https://example.com/auth" ) ;
2013+ expect ( ( receivedRequest as any ) ?. elicitationId ) . toBe ( "elic-url-1" ) ;
2014+
20052015 // Test elicitation complete notification
20062016 await clientConnection . unstable_completeElicitation ( {
20072017 elicitationId : "elic-1" ,
@@ -2012,6 +2022,58 @@ describe("Connection", () => {
20122022 } ) ;
20132023 } ) ;
20142024
2025+ it ( "silently ignores completeElicitation when client does not implement handler" , async ( ) => {
2026+ class TestClient implements Client {
2027+ async writeTextFile (
2028+ _ : WriteTextFileRequest ,
2029+ ) : Promise < WriteTextFileResponse > {
2030+ return { } ;
2031+ }
2032+ async readTextFile (
2033+ _ : ReadTextFileRequest ,
2034+ ) : Promise < ReadTextFileResponse > {
2035+ return { content : "" } ;
2036+ }
2037+ async requestPermission (
2038+ _ : RequestPermissionRequest ,
2039+ ) : Promise < RequestPermissionResponse > {
2040+ return { outcome : { outcome : "selected" , optionId : "allow" } } ;
2041+ }
2042+ async sessionUpdate ( _ : SessionNotification ) : Promise < void > { }
2043+ }
2044+
2045+ class TestAgent implements Agent {
2046+ async initialize ( _ : InitializeRequest ) : Promise < InitializeResponse > {
2047+ return {
2048+ protocolVersion : 1 ,
2049+ agentCapabilities : { loadSession : false } ,
2050+ authMethods : [ ] ,
2051+ } ;
2052+ }
2053+ async newSession ( _ : NewSessionRequest ) : Promise < NewSessionResponse > {
2054+ return { sessionId : "test-session" } ;
2055+ }
2056+ async authenticate ( _ : AuthenticateRequest ) : Promise < void > { }
2057+ async prompt ( _ : PromptRequest ) : Promise < PromptResponse > {
2058+ return { stopReason : "end_turn" } ;
2059+ }
2060+ async cancel ( _ : CancelNotification ) : Promise < void > { }
2061+ }
2062+
2063+ new ClientSideConnection (
2064+ ( ) => new TestClient ( ) ,
2065+ ndJsonStream ( clientToAgent . writable , agentToClient . readable ) ,
2066+ ) ;
2067+ const clientConnection = new AgentSideConnection (
2068+ ( ) => new TestAgent ( ) ,
2069+ ndJsonStream ( agentToClient . writable , clientToAgent . readable ) ,
2070+ ) ;
2071+
2072+ await clientConnection . unstable_completeElicitation ( {
2073+ elicitationId : "elic-1" ,
2074+ } ) ;
2075+ } ) ;
2076+
20152077 it ( "rejects elicitation request when client does not implement handler" , async ( ) => {
20162078 // Client WITHOUT unstable_createElicitation
20172079 class TestClient implements Client {
@@ -2060,8 +2122,8 @@ describe("Connection", () => {
20602122 ndJsonStream ( agentToClient . writable , clientToAgent . readable ) ,
20612123 ) ;
20622124
2063- try {
2064- await clientConnection . unstable_createElicitation ( {
2125+ await expect (
2126+ clientConnection . unstable_createElicitation ( {
20652127 sessionId : "test-session" ,
20662128 mode : "form" ,
20672129 message : "Enter your name" ,
@@ -2071,11 +2133,8 @@ describe("Connection", () => {
20712133 name : { type : "string" } ,
20722134 } ,
20732135 } ,
2074- } ) ;
2075- expect . fail ( "Should have thrown method not found error" ) ;
2076- } catch ( error : any ) {
2077- expect ( error . code ) . toBe ( - 32601 ) ; // Method not found
2078- }
2136+ } ) ,
2137+ ) . rejects . toMatchObject ( { code : - 32601 } ) ;
20792138 } ) ;
20802139} ) ;
20812140
@@ -2125,6 +2184,18 @@ describe("CreateElicitationRequest schema", () => {
21252184 expect ( result . success ) . toBe ( true ) ;
21262185 } ) ;
21272186
2187+ it ( "accepts url-mode request with optional toolCallId" , ( ) => {
2188+ const result = zCreateElicitationRequest . safeParse ( {
2189+ sessionId : "sess-1" ,
2190+ toolCallId : "tc-1" ,
2191+ mode : "url" ,
2192+ message : "Please authenticate" ,
2193+ elicitationId : "elic-1" ,
2194+ url : "https://example.com/auth" ,
2195+ } ) ;
2196+ expect ( result . success ) . toBe ( true ) ;
2197+ } ) ;
2198+
21282199 it ( "accepts url-mode request scoped to a request" , ( ) => {
21292200 const result = zCreateElicitationRequest . safeParse ( {
21302201 requestId : "req-1" ,
@@ -2163,7 +2234,7 @@ describe("CreateElicitationRequest schema", () => {
21632234 expect ( result . success ) . toBe ( false ) ;
21642235 } ) ;
21652236
2166- it ( "rejects request without scope (no sessionId or requestId)" , ( ) => {
2237+ it ( "rejects form-mode request without scope (no sessionId or requestId)" , ( ) => {
21672238 const result = zCreateElicitationRequest . safeParse ( {
21682239 mode : "form" ,
21692240 message : "Enter your name" ,
@@ -2172,14 +2243,24 @@ describe("CreateElicitationRequest schema", () => {
21722243 expect ( result . success ) . toBe ( false ) ;
21732244 } ) ;
21742245
2175- it ( "preserves unknown properties (looseObject)" , ( ) => {
2246+ it ( "rejects url-mode request without scope (no sessionId or requestId)" , ( ) => {
2247+ const result = zCreateElicitationRequest . safeParse ( {
2248+ mode : "url" ,
2249+ message : "Please authenticate" ,
2250+ elicitationId : "elic-1" ,
2251+ url : "https://example.com/auth" ,
2252+ } ) ;
2253+ expect ( result . success ) . toBe ( false ) ;
2254+ } ) ;
2255+
2256+ it ( "strips unknown properties" , ( ) => {
21762257 const result = zCreateElicitationRequest . safeParse ( {
21772258 ...formSessionRequest ,
21782259 customField : "custom-value" ,
21792260 } ) ;
21802261 expect ( result . success ) . toBe ( true ) ;
21812262 if ( result . success ) {
2182- expect ( ( result . data as any ) . customField ) . toBe ( "custom-value" ) ;
2263+ expect ( ( result . data as any ) . customField ) . toBeUndefined ( ) ;
21832264 }
21842265 } ) ;
21852266} ) ;
0 commit comments