@@ -163,6 +163,78 @@ describe("normalizeToolDefinitionsForProvider", () => {
163163 }
164164 } ) ;
165165
166+ describe ( "optional MCP properties" , ( ) => {
167+ const listIssues : ToolDefinition = {
168+ name : "mcp__linear__list_issues" ,
169+ description : "[linear] list issues" ,
170+ inputSchema : {
171+ type : "object" ,
172+ properties : {
173+ release : { type : "string" } ,
174+ priority : { type : "number" } ,
175+ } ,
176+ required : [ "release" ] ,
177+ } ,
178+ } ;
179+ const builtin : ToolDefinition = {
180+ ...listIssues ,
181+ name : "list_issues" ,
182+ } ;
183+
184+ test . each ( [
185+ { providerName : "codex/plus" , model : "gpt-5.6-terra" } ,
186+ { providerName : "opencode-go" , model : "gpt-5.1" } ,
187+ { providerName : "codex" , model : "codex-mini-latest" } ,
188+ ] ) ( "$providerName $model advertises null on MCP tools only" , ( ctx ) => {
189+ const [ mcp , own ] = normalizeToolDefinitionsForProvider (
190+ [ listIssues , builtin ] ,
191+ ctx ,
192+ ) ;
193+ expect ( mcp ?. inputSchema ) . toEqual ( {
194+ type : "object" ,
195+ properties : {
196+ release : { type : "string" } ,
197+ priority : { type : [ "number" , "null" ] } ,
198+ } ,
199+ required : [ "release" ] ,
200+ } ) ;
201+ expect ( own ) . toBe ( builtin ) ;
202+ } ) ;
203+
204+ test . each ( [
205+ { providerName : "anthropic" , model : "claude-sonnet-5-5" } ,
206+ { providerName : "moonshot" , model : "kimi-k2" } ,
207+ ] ) ( "$providerName leaves MCP tools as they are" , ( ctx ) => {
208+ const defs = [ listIssues ] ;
209+ expect ( normalizeToolDefinitionsForProvider ( defs , ctx ) ) . toBe ( defs ) ;
210+ } ) ;
211+
212+ test ( "grok sanitizes MCP tools without advertising null" , ( ) => {
213+ const [ out ] = normalizeToolDefinitionsForProvider ( [ listIssues ] , {
214+ providerName : "xai/default-2" ,
215+ model : "grok-4.6" ,
216+ } ) ;
217+ expect ( out ?. inputSchema ) . toEqual ( listIssues . inputSchema ) ;
218+ } ) ;
219+
220+ test ( "returns the same array when no MCP property changes" , ( ) => {
221+ const nullable : ToolDefinition = {
222+ ...listIssues ,
223+ inputSchema : {
224+ type : "object" ,
225+ properties : { assignee : { type : [ "string" , "null" ] } } ,
226+ } ,
227+ } ;
228+ const defs = [ nullable , builtin ] ;
229+ expect (
230+ normalizeToolDefinitionsForProvider ( defs , {
231+ providerName : "openai" ,
232+ model : "gpt-5.6" ,
233+ } ) ,
234+ ) . toBe ( defs ) ;
235+ } ) ;
236+ } ) ;
237+
166238 test ( "grok sanitizes untyped properties and strips $schema" , ( ) => {
167239 const untyped : ToolDefinition = {
168240 name : "submit_result" ,
0 commit comments