@@ -136,7 +136,7 @@ describe("integration — late MCP dispatch", () => {
136136 ) ;
137137
138138 test . serial (
139- "unadvertised MCP tool dispatch fails loudly instead of altering results" ,
139+ "unknown MCP tool dispatch fails loudly instead of altering results" ,
140140 async ( ) => {
141141 const session = await openIntegrationSession ( {
142142 permissionGate : permissionGate ( ) ,
@@ -288,6 +288,69 @@ describe("integration — late MCP dispatch", () => {
288288 } ,
289289 ) ;
290290
291+ test . serial (
292+ "tool_search promotion does not inject omitted optional MCP arguments" ,
293+ async ( ) => {
294+ const session = await openIntegrationSession ( {
295+ permissionGate : permissionGate ( ) ,
296+ createAgentFn : createAgentWithLiveToolDispatch ,
297+ } ) ;
298+ let receivedArgs : Record < string , unknown > | undefined ;
299+
300+ try {
301+ const tools = lateMcpTools ( ( toolName , args ) => {
302+ if ( toolName === "list_issues" ) {
303+ receivedArgs = args ;
304+ }
305+ } ) ;
306+ expect ( tools ) . toHaveLength ( 1 ) ;
307+ expect ( tools [ 0 ] ?. kind ) . toBe ( "full" ) ;
308+ const expectedSchema = structuredClone ( LATE_MCP_SCHEMA ) ;
309+ session . toolset . dynamicRunner . addTools ( tools ) ;
310+ session . toolset . setToolPromoter ( ( ) => {
311+ session . updateToolDefinitions (
312+ session . toolset . dynamicRunner . currentDefinitions ( ) ,
313+ ) ;
314+ } ) ;
315+ session . harness . scenario . replyOnce ( "anthropic" , {
316+ toolCalls : [
317+ { name : "tool_search" , args : { query : "linear list issues" } } ,
318+ ] ,
319+ } ) ;
320+ session . harness . scenario . replyOnce ( "anthropic" , {
321+ toolCalls : [ { name : LATE_MCP , args : { limit : 1 } } ] ,
322+ } ) ;
323+ session . harness . scenario . replyOnce ( "anthropic" , { text : "listed" } ) ;
324+
325+ const { events } = await runUntilDone ( session , "list one linear issue" ) ;
326+ const bodies = await Promise . all (
327+ session . harness . scenario
328+ . matchedRequests ( )
329+ . map (
330+ async ( request ) =>
331+ JSON . parse (
332+ await ( request . clone ( ) as unknown as Request ) . text ( ) ,
333+ ) as AnthropicRequestBody ,
334+ ) ,
335+ ) ;
336+ const publishedTool = bodies
337+ . flatMap ( ( body ) => body . tools ?? [ ] )
338+ . find ( ( tool ) => tool . name === LATE_MCP ) ;
339+
340+ expect ( publishedTool ?. input_schema . properties ) . toEqual (
341+ expectedSchema . properties ,
342+ ) ;
343+ expect (
344+ Object . hasOwn ( publishedTool ?. input_schema ?? { } , "required" ) ,
345+ ) . toBe ( false ) ;
346+ expect ( receivedArgs ) . toEqual ( { limit : 1 } ) ;
347+ expect ( toolDoneContents ( events ) ) . toContain ( "ISSUE-1" ) ;
348+ } finally {
349+ await closeIntegrationSession ( session ) ;
350+ }
351+ } ,
352+ ) ;
353+
291354 test . serial (
292355 "tool_search promotion preserves required and optional MCP arguments" ,
293356 async ( ) => {
0 commit comments