@@ -12,7 +12,7 @@ export const getWorkItemsBatchTool: ToolConfig<GetWorkItemsBatchParams, GetWorkI
1212 id : 'azure_devops_get_work_items_batch' ,
1313 name : 'Azure DevOps Get Work Items Batch' ,
1414 description :
15- 'Fetch full details for multiple work items by ID from Azure DevOps in a single call . Pass comma-separated IDs (e.g. "123,456,789"). Maximum 200 IDs per request .' ,
15+ 'Fetch full details for multiple work items by ID from Azure DevOps. Pass comma-separated IDs (e.g. "123,456,789"). Requests with more than 200 IDs are automatically split into chunks .' ,
1616 version : '1.0.0' ,
1717
1818 params : {
@@ -33,7 +33,7 @@ export const getWorkItemsBatchTool: ToolConfig<GetWorkItemsBatchParams, GetWorkI
3333 required : true ,
3434 visibility : 'user-or-llm' ,
3535 description :
36- 'Comma-separated work item IDs to fetch (e.g. "123,456,789"). Maximum 200 IDs.' ,
36+ 'Comma-separated work item IDs to fetch (e.g. "123,456,789"). Lists longer than 200 IDs are chunked automatically .' ,
3737 } ,
3838 accessToken : {
3939 type : 'string' ,
@@ -45,10 +45,15 @@ export const getWorkItemsBatchTool: ToolConfig<GetWorkItemsBatchParams, GetWorkI
4545
4646 request : {
4747 url : ( params ) => {
48+ const allIds = params . ids
49+ . split ( ',' )
50+ . map ( ( id ) => id . trim ( ) )
51+ . filter ( Boolean )
52+ const firstChunk = allIds . slice ( 0 , 200 )
4853 const url = new URL (
4954 `https://dev.azure.com/${ params . organization . trim ( ) } /${ params . project . trim ( ) } /_apis/wit/workitems`
5055 )
51- url . searchParams . set ( 'ids' , params . ids )
56+ url . searchParams . set ( 'ids' , firstChunk . join ( ',' ) )
5257 url . searchParams . set ( '$expand' , 'all' )
5358 url . searchParams . set ( 'api-version' , '7.2-preview.3' )
5459 return url . toString ( )
@@ -60,22 +65,61 @@ export const getWorkItemsBatchTool: ToolConfig<GetWorkItemsBatchParams, GetWorkI
6065 } ) ,
6166 } ,
6267
63- transformResponse : async ( response ) => {
64- const data = await response . json ( )
65- const workItems : AzureDevOpsWorkItem [ ] = ( data . value ?? [ ] ) . map (
68+ transformResponse : async ( response , params ) => {
69+ const firstData = await response . json ( )
70+ const workItems : AzureDevOpsWorkItem [ ] = ( firstData . value ?? [ ] ) . map (
6671 ( raw : AzureDevOpsRawWorkItem ) => mapWorkItem ( raw )
6772 )
6873
74+ const allIds = params ! . ids
75+ . split ( ',' )
76+ . map ( ( id ) => id . trim ( ) )
77+ . filter ( Boolean )
78+
79+ if ( allIds . length > 200 ) {
80+ const BATCH_SIZE = 200
81+ const organization = params ! . organization . trim ( )
82+ const project = params ! . project . trim ( )
83+ const authHeader = `Basic ${ btoa ( `:${ params ! . accessToken } ` ) } `
84+
85+ for ( let i = BATCH_SIZE ; i < allIds . length ; i += BATCH_SIZE ) {
86+ const chunk = allIds . slice ( i , i + BATCH_SIZE )
87+ const detailsUrl = new URL (
88+ `https://dev.azure.com/${ organization } /${ project } /_apis/wit/workitems`
89+ )
90+ detailsUrl . searchParams . set ( 'ids' , chunk . join ( ',' ) )
91+ detailsUrl . searchParams . set ( '$expand' , 'all' )
92+ detailsUrl . searchParams . set ( 'api-version' , '7.2-preview.3' )
93+
94+ const chunkResponse = await fetch ( detailsUrl . toString ( ) , {
95+ method : 'GET' ,
96+ headers : { 'Content-Type' : 'application/json' , Authorization : authHeader } ,
97+ } )
98+
99+ if ( ! chunkResponse . ok ) {
100+ const errorBody = await chunkResponse . text ( ) . catch ( ( ) => '' )
101+ throw new Error (
102+ `Failed to fetch work item batch chunk (${ chunkResponse . status } ): ${ errorBody || chunkResponse . statusText } `
103+ )
104+ }
105+
106+ const chunkData = await chunkResponse . json ( )
107+ for ( const raw of chunkData . value ?? [ ] ) {
108+ workItems . push ( mapWorkItem ( raw as AzureDevOpsRawWorkItem ) )
109+ }
110+ }
111+ }
112+
69113 const content =
70114 workItems . length === 0
71115 ? 'No work items found for the provided IDs.'
72- : `Found ${ workItems . length } work item(s):\n\n${ workItems . map ( formatWorkItem ) . join ( '\n\n' ) } `
116+ : `Found ${ workItems . length } work item(s) (of ${ allIds . length } requested) :\n\n${ workItems . map ( formatWorkItem ) . join ( '\n\n' ) } `
73117
74118 return {
75119 success : true ,
76120 output : {
77121 content,
78- metadata : { count : workItems . length , workItems } ,
122+ metadata : { count : workItems . length , totalRequested : allIds . length , workItems } ,
79123 } ,
80124 }
81125 } ,
@@ -90,6 +134,11 @@ export const getWorkItemsBatchTool: ToolConfig<GetWorkItemsBatchParams, GetWorkI
90134 description : 'Work items metadata' ,
91135 properties : {
92136 count : { type : 'number' , description : 'Number of work items returned' } ,
137+ totalRequested : {
138+ type : 'number' ,
139+ description : 'Total number of IDs requested (across all chunks)' ,
140+ optional : true ,
141+ } ,
93142 workItems : {
94143 type : 'array' ,
95144 description : 'Array of work item details' ,
0 commit comments