@azure-devops/mcp 2.2.2 → 2.3.0-nightly.20251203
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -105
- package/dist/auth.js +27 -1
- package/dist/index.js +12 -1
- package/dist/logger.js +34 -0
- package/dist/org-tenants.js +4 -3
- package/dist/prompts.js +0 -0
- package/dist/shared/domains.js +2 -1
- package/dist/tools/pipelines.js +52 -4
- package/dist/tools/repositories.js +621 -360
- package/dist/tools/test-plans.js +256 -123
- package/dist/tools/work-items.js +398 -210
- package/dist/tools/work.js +22 -3
- package/dist/tools.js +0 -0
- package/dist/useragent.js +0 -0
- package/dist/utils.js +0 -0
- package/dist/version.js +1 -1
- package/package.json +16 -7
- package/dist/domains.js +0 -1
- package/dist/http.js +0 -52
- package/dist/orgtenants.js +0 -73
- package/dist/server.js +0 -36
- package/dist/tenant.js +0 -73
- package/dist/tools/advsec.js +0 -108
- package/dist/tools/builds.js +0 -271
- package/dist/tools/releases.js +0 -97
- package/dist/tools/repos.js +0 -666
- package/dist/tools/testplans.js +0 -213
- package/dist/tools/workitems.js +0 -809
package/dist/tools/work-items.js
CHANGED
|
@@ -13,6 +13,7 @@ const WORKITEM_TOOLS = {
|
|
|
13
13
|
update_work_item: "wit_update_work_item",
|
|
14
14
|
create_work_item: "wit_create_work_item",
|
|
15
15
|
list_work_item_comments: "wit_list_work_item_comments",
|
|
16
|
+
list_work_item_revisions: "wit_list_work_item_revisions",
|
|
16
17
|
get_work_items_for_iteration: "wit_get_work_items_for_iteration",
|
|
17
18
|
add_work_item_comment: "wit_add_work_item_comment",
|
|
18
19
|
add_child_work_items: "wit_add_child_work_items",
|
|
@@ -60,26 +61,44 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
60
61
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
61
62
|
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
62
63
|
}, async ({ project, team }) => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
try {
|
|
65
|
+
const connection = await connectionProvider();
|
|
66
|
+
const workApi = await connection.getWorkApi();
|
|
67
|
+
const teamContext = { project, team };
|
|
68
|
+
const backlogs = await workApi.getBacklogs(teamContext);
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: "text", text: JSON.stringify(backlogs, null, 2) }],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: "text", text: `Error listing backlogs: ${errorMessage}` }],
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
70
80
|
});
|
|
71
81
|
server.tool(WORKITEM_TOOLS.list_backlog_work_items, "Retrieve a list of backlogs of for a given project, team, and backlog category", {
|
|
72
82
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
73
83
|
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
74
84
|
backlogId: z.string().describe("The ID of the backlog category to retrieve work items from."),
|
|
75
85
|
}, async ({ project, team, backlogId }) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
try {
|
|
87
|
+
const connection = await connectionProvider();
|
|
88
|
+
const workApi = await connection.getWorkApi();
|
|
89
|
+
const teamContext = { project, team };
|
|
90
|
+
const workItems = await workApi.getBacklogLevelWorkItems(teamContext, backlogId);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: `Error listing backlog work items: ${errorMessage}` }],
|
|
99
|
+
isError: true,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
83
102
|
});
|
|
84
103
|
server.tool(WORKITEM_TOOLS.my_work_items, "Retrieve a list of work items relevent to the authenticated user.", {
|
|
85
104
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -87,53 +106,71 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
87
106
|
top: z.number().default(50).describe("The maximum number of work items to return. Defaults to 50."),
|
|
88
107
|
includeCompleted: z.boolean().default(false).describe("Whether to include completed work items. Defaults to false."),
|
|
89
108
|
}, async ({ project, type, top, includeCompleted }) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
109
|
+
try {
|
|
110
|
+
const connection = await connectionProvider();
|
|
111
|
+
const workApi = await connection.getWorkApi();
|
|
112
|
+
const workItems = await workApi.getPredefinedQueryResults(project, type, top, includeCompleted);
|
|
113
|
+
return {
|
|
114
|
+
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text", text: `Error retrieving work items: ${errorMessage}` }],
|
|
121
|
+
isError: true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
96
124
|
});
|
|
97
125
|
server.tool(WORKITEM_TOOLS.get_work_items_batch_by_ids, "Retrieve list of work items by IDs in batch.", {
|
|
98
126
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
99
127
|
ids: z.array(z.number()).describe("The IDs of the work items to retrieve."),
|
|
100
128
|
fields: z.array(z.string()).optional().describe("Optional list of fields to include in the response. If not provided, a hardcoded default set of fields will be used."),
|
|
101
129
|
}, async ({ project, ids, fields }) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
workitems.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
try {
|
|
131
|
+
const connection = await connectionProvider();
|
|
132
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
133
|
+
const defaultFields = ["System.Id", "System.WorkItemType", "System.Title", "System.State", "System.Parent", "System.Tags", "Microsoft.VSTS.Common.StackRank", "System.AssignedTo"];
|
|
134
|
+
// If no fields are provided, use the default set of fields
|
|
135
|
+
const fieldsToUse = !fields || fields.length === 0 ? defaultFields : fields;
|
|
136
|
+
const workitems = await workItemApi.getWorkItemsBatch({ ids, fields: fieldsToUse }, project);
|
|
137
|
+
// List of identity fields that need to be transformed from objects to formatted strings
|
|
138
|
+
const identityFields = [
|
|
139
|
+
"System.AssignedTo",
|
|
140
|
+
"System.CreatedBy",
|
|
141
|
+
"System.ChangedBy",
|
|
142
|
+
"System.AuthorizedAs",
|
|
143
|
+
"Microsoft.VSTS.Common.ActivatedBy",
|
|
144
|
+
"Microsoft.VSTS.Common.ResolvedBy",
|
|
145
|
+
"Microsoft.VSTS.Common.ClosedBy",
|
|
146
|
+
];
|
|
147
|
+
// Format identity fields to include displayName and uniqueName
|
|
148
|
+
// Removing the identity object as the response. It's too much and not needed
|
|
149
|
+
if (workitems && Array.isArray(workitems)) {
|
|
150
|
+
workitems.forEach((item) => {
|
|
151
|
+
if (item.fields) {
|
|
152
|
+
identityFields.forEach((fieldName) => {
|
|
153
|
+
if (item.fields && item.fields[fieldName] && typeof item.fields[fieldName] === "object") {
|
|
154
|
+
const identityField = item.fields[fieldName];
|
|
155
|
+
const name = identityField.displayName || "";
|
|
156
|
+
const email = identityField.uniqueName || "";
|
|
157
|
+
item.fields[fieldName] = `${name} <${email}>`.trim();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
content: [{ type: "text", text: JSON.stringify(workitems, null, 2) }],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
169
|
+
return {
|
|
170
|
+
content: [{ type: "text", text: `Error retrieving work items batch: ${errorMessage}` }],
|
|
171
|
+
isError: true,
|
|
172
|
+
};
|
|
133
173
|
}
|
|
134
|
-
return {
|
|
135
|
-
content: [{ type: "text", text: JSON.stringify(workitems, null, 2) }],
|
|
136
|
-
};
|
|
137
174
|
});
|
|
138
175
|
server.tool(WORKITEM_TOOLS.get_work_item, "Get a single work item by ID.", {
|
|
139
176
|
id: z.number().describe("The ID of the work item to retrieve."),
|
|
@@ -146,24 +183,42 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
146
183
|
.optional()
|
|
147
184
|
.describe("Expand options include 'all', 'fields', 'links', 'none', and 'relations'. Relations can be used to get child workitems. Defaults to 'none'."),
|
|
148
185
|
}, async ({ id, project, fields, asOf, expand }) => {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
186
|
+
try {
|
|
187
|
+
const connection = await connectionProvider();
|
|
188
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
189
|
+
const workItem = await workItemApi.getWorkItem(id, fields, asOf, expand, project);
|
|
190
|
+
return {
|
|
191
|
+
content: [{ type: "text", text: JSON.stringify(workItem, null, 2) }],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
196
|
+
return {
|
|
197
|
+
content: [{ type: "text", text: `Error retrieving work item: ${errorMessage}` }],
|
|
198
|
+
isError: true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
155
201
|
});
|
|
156
202
|
server.tool(WORKITEM_TOOLS.list_work_item_comments, "Retrieve list of comments for a work item by ID.", {
|
|
157
203
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
158
204
|
workItemId: z.number().describe("The ID of the work item to retrieve comments for."),
|
|
159
205
|
top: z.number().default(50).describe("Optional number of comments to retrieve. Defaults to all comments."),
|
|
160
206
|
}, async ({ project, workItemId, top }) => {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
207
|
+
try {
|
|
208
|
+
const connection = await connectionProvider();
|
|
209
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
210
|
+
const comments = await workItemApi.getComments(project, workItemId, top);
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: "text", text: JSON.stringify(comments, null, 2) }],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
217
|
+
return {
|
|
218
|
+
content: [{ type: "text", text: `Error listing work item comments: ${errorMessage}` }],
|
|
219
|
+
isError: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
167
222
|
});
|
|
168
223
|
server.tool(WORKITEM_TOOLS.add_work_item_comment, "Add comment to a work item by ID.", {
|
|
169
224
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -171,29 +226,90 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
171
226
|
comment: z.string().describe("The text of the comment to add to the work item."),
|
|
172
227
|
format: z.enum(["markdown", "html"]).optional().default("html"),
|
|
173
228
|
}, async ({ project, workItemId, comment, format }) => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
229
|
+
try {
|
|
230
|
+
const connection = await connectionProvider();
|
|
231
|
+
const orgUrl = connection.serverUrl;
|
|
232
|
+
const accessToken = await tokenProvider();
|
|
233
|
+
const body = {
|
|
234
|
+
text: comment,
|
|
235
|
+
};
|
|
236
|
+
const formatParameter = format === "markdown" ? 0 : 1;
|
|
237
|
+
const response = await fetch(`${orgUrl}/${project}/_apis/wit/workItems/${workItemId}/comments?format=${formatParameter}&api-version=${markdownCommentsApiVersion}`, {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: {
|
|
240
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
241
|
+
"Content-Type": "application/json",
|
|
242
|
+
"User-Agent": userAgentProvider(),
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify(body),
|
|
245
|
+
});
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
throw new Error(`Failed to add a work item comment: ${response.statusText}}`);
|
|
248
|
+
}
|
|
249
|
+
const comments = await response.text();
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text", text: comments }],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
256
|
+
return {
|
|
257
|
+
content: [{ type: "text", text: `Error adding work item comment: ${errorMessage}` }],
|
|
258
|
+
isError: true,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
server.tool(WORKITEM_TOOLS.list_work_item_revisions, "Retrieve list of revisions for a work item by ID.", {
|
|
263
|
+
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
264
|
+
workItemId: z.number().describe("The ID of the work item to retrieve revisions for."),
|
|
265
|
+
top: z.number().default(50).describe("Optional number of revisions to retrieve. If not provided, all revisions will be returned."),
|
|
266
|
+
skip: z.number().optional().describe("Optional number of revisions to skip for pagination. Defaults to 0."),
|
|
267
|
+
expand: z
|
|
268
|
+
.enum(getEnumKeys(WorkItemExpand))
|
|
269
|
+
.default("None")
|
|
270
|
+
.optional()
|
|
271
|
+
.describe("Optional expand parameter to include additional details. Defaults to 'None'."),
|
|
272
|
+
}, async ({ project, workItemId, top, skip, expand }) => {
|
|
273
|
+
try {
|
|
274
|
+
const connection = await connectionProvider();
|
|
275
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
276
|
+
const revisions = await workItemApi.getRevisions(workItemId, top, skip, safeEnumConvert(WorkItemExpand, expand), project);
|
|
277
|
+
// Dynamically clean up identity objects in revision fields
|
|
278
|
+
// Identity objects typically have properties like displayName, url, _links, id, uniqueName, imageUrl, descriptor
|
|
279
|
+
if (revisions && Array.isArray(revisions)) {
|
|
280
|
+
revisions.forEach((revision) => {
|
|
281
|
+
if (revision.fields) {
|
|
282
|
+
Object.keys(revision.fields).forEach((fieldName) => {
|
|
283
|
+
const fieldValue = revision.fields ? revision.fields[fieldName] : undefined;
|
|
284
|
+
// Check if this is an identity object by looking for common identity properties
|
|
285
|
+
if (fieldValue &&
|
|
286
|
+
typeof fieldValue === "object" &&
|
|
287
|
+
!Array.isArray(fieldValue) &&
|
|
288
|
+
"displayName" in fieldValue &&
|
|
289
|
+
("url" in fieldValue || "_links" in fieldValue || "uniqueName" in fieldValue)) {
|
|
290
|
+
// Remove unwanted properties from identity objects
|
|
291
|
+
delete fieldValue.url;
|
|
292
|
+
delete fieldValue._links;
|
|
293
|
+
delete fieldValue.id;
|
|
294
|
+
delete fieldValue.uniqueName;
|
|
295
|
+
delete fieldValue.imageUrl;
|
|
296
|
+
delete fieldValue.descriptor;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
content: [{ type: "text", text: JSON.stringify(revisions, null, 2) }],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
308
|
+
return {
|
|
309
|
+
content: [{ type: "text", text: `Error listing work item revisions: ${errorMessage}` }],
|
|
310
|
+
isError: true,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
197
313
|
});
|
|
198
314
|
server.tool(WORKITEM_TOOLS.add_child_work_items, "Create one or many child work items from a parent by work item type and parent id.", {
|
|
199
315
|
parentId: z.number().describe("The ID of the parent work item to create a child work item under."),
|
|
@@ -369,13 +485,22 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
369
485
|
team: z.string().optional().describe("The name or ID of the Azure DevOps team. If not provided, the default team will be used."),
|
|
370
486
|
iterationId: z.string().describe("The ID of the iteration to retrieve work items for."),
|
|
371
487
|
}, async ({ project, team, iterationId }) => {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
488
|
+
try {
|
|
489
|
+
const connection = await connectionProvider();
|
|
490
|
+
const workApi = await connection.getWorkApi();
|
|
491
|
+
//get the work items for the current iteration
|
|
492
|
+
const workItems = await workApi.getIterationWorkItems({ project, team }, iterationId);
|
|
493
|
+
return {
|
|
494
|
+
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
499
|
+
return {
|
|
500
|
+
content: [{ type: "text", text: `Error retrieving work items for iteration: ${errorMessage}` }],
|
|
501
|
+
isError: true,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
379
504
|
});
|
|
380
505
|
server.tool(WORKITEM_TOOLS.update_work_item, "Update a work item by ID with specified fields.", {
|
|
381
506
|
id: z.number().describe("The ID of the work item to update."),
|
|
@@ -392,28 +517,46 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
392
517
|
}))
|
|
393
518
|
.describe("An array of field updates to apply to the work item."),
|
|
394
519
|
}, async ({ id, updates }) => {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
520
|
+
try {
|
|
521
|
+
const connection = await connectionProvider();
|
|
522
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
523
|
+
// Convert operation names to lowercase for API
|
|
524
|
+
const apiUpdates = updates.map((update) => ({
|
|
525
|
+
...update,
|
|
526
|
+
op: update.op,
|
|
527
|
+
}));
|
|
528
|
+
const updatedWorkItem = await workItemApi.updateWorkItem(null, apiUpdates, id);
|
|
529
|
+
return {
|
|
530
|
+
content: [{ type: "text", text: JSON.stringify(updatedWorkItem, null, 2) }],
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
535
|
+
return {
|
|
536
|
+
content: [{ type: "text", text: `Error updating work item: ${errorMessage}` }],
|
|
537
|
+
isError: true,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
406
540
|
});
|
|
407
541
|
server.tool(WORKITEM_TOOLS.get_work_item_type, "Get a specific work item type.", {
|
|
408
542
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
409
543
|
workItemType: z.string().describe("The name of the work item type to retrieve."),
|
|
410
544
|
}, async ({ project, workItemType }) => {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
545
|
+
try {
|
|
546
|
+
const connection = await connectionProvider();
|
|
547
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
548
|
+
const workItemTypeInfo = await workItemApi.getWorkItemType(project, workItemType);
|
|
549
|
+
return {
|
|
550
|
+
content: [{ type: "text", text: JSON.stringify(workItemTypeInfo, null, 2) }],
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
555
|
+
return {
|
|
556
|
+
content: [{ type: "text", text: `Error retrieving work item type: ${errorMessage}` }],
|
|
557
|
+
isError: true,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
417
560
|
});
|
|
418
561
|
server.tool(WORKITEM_TOOLS.create_work_item, "Create a new work item in a specified project and work item type.", {
|
|
419
562
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -473,27 +616,54 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
473
616
|
includeDeleted: z.boolean().default(false).describe("Whether to include deleted items in the query results. Defaults to false."),
|
|
474
617
|
useIsoDateFormat: z.boolean().default(false).describe("Whether to use ISO date format in the response. Defaults to false."),
|
|
475
618
|
}, async ({ project, query, expand, depth, includeDeleted, useIsoDateFormat }) => {
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
619
|
+
try {
|
|
620
|
+
const connection = await connectionProvider();
|
|
621
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
622
|
+
const queryDetails = await workItemApi.getQuery(project, query, safeEnumConvert(QueryExpand, expand), depth, includeDeleted, useIsoDateFormat);
|
|
623
|
+
return {
|
|
624
|
+
content: [{ type: "text", text: JSON.stringify(queryDetails, null, 2) }],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
629
|
+
return {
|
|
630
|
+
content: [{ type: "text", text: `Error retrieving query: ${errorMessage}` }],
|
|
631
|
+
isError: true,
|
|
632
|
+
};
|
|
633
|
+
}
|
|
482
634
|
});
|
|
483
|
-
server.tool(WORKITEM_TOOLS.get_query_results_by_id, "Retrieve the results of a work item query given the query ID.", {
|
|
635
|
+
server.tool(WORKITEM_TOOLS.get_query_results_by_id, "Retrieve the results of a work item query given the query ID. Supports full or IDs-only response types.", {
|
|
484
636
|
id: z.string().describe("The ID of the query to retrieve results for."),
|
|
485
637
|
project: z.string().optional().describe("The name or ID of the Azure DevOps project. If not provided, the default project will be used."),
|
|
486
638
|
team: z.string().optional().describe("The name or ID of the Azure DevOps team. If not provided, the default team will be used."),
|
|
487
639
|
timePrecision: z.boolean().optional().describe("Whether to include time precision in the results. Defaults to false."),
|
|
488
640
|
top: z.number().default(50).describe("The maximum number of results to return. Defaults to 50."),
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
641
|
+
responseType: z.enum(["full", "ids"]).default("full").describe("Response type: 'full' returns complete query results (default), 'ids' returns only work item IDs for reduced payload size."),
|
|
642
|
+
}, async ({ id, project, team, timePrecision, top, responseType }) => {
|
|
643
|
+
try {
|
|
644
|
+
const connection = await connectionProvider();
|
|
645
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
646
|
+
const teamContext = { project, team };
|
|
647
|
+
const queryResult = await workItemApi.queryById(id, teamContext, timePrecision, top);
|
|
648
|
+
// If ids mode, extract and return only the IDs
|
|
649
|
+
if (responseType === "ids") {
|
|
650
|
+
const ids = queryResult.workItems?.map((workItem) => workItem.id).filter((id) => id !== undefined) || [];
|
|
651
|
+
return {
|
|
652
|
+
content: [{ type: "text", text: JSON.stringify({ ids, count: ids.length }, null, 2) }],
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
// Default: return full query results
|
|
656
|
+
return {
|
|
657
|
+
content: [{ type: "text", text: JSON.stringify(queryResult, null, 2) }],
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
662
|
+
return {
|
|
663
|
+
content: [{ type: "text", text: `Error retrieving query results: ${errorMessage}` }],
|
|
664
|
+
isError: true,
|
|
665
|
+
};
|
|
666
|
+
}
|
|
497
667
|
});
|
|
498
668
|
server.tool(WORKITEM_TOOLS.update_work_items_batch, "Update work items in batch", {
|
|
499
669
|
updates: z
|
|
@@ -506,53 +676,62 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
506
676
|
}))
|
|
507
677
|
.describe("An array of updates to apply to work items. Each update should include the operation (op), work item ID (id), field path (path), and new value (value)."),
|
|
508
678
|
}, async ({ updates }) => {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
679
|
+
try {
|
|
680
|
+
const connection = await connectionProvider();
|
|
681
|
+
const orgUrl = connection.serverUrl;
|
|
682
|
+
const accessToken = await tokenProvider();
|
|
683
|
+
// Extract unique IDs from the updates array
|
|
684
|
+
const uniqueIds = Array.from(new Set(updates.map((update) => update.id)));
|
|
685
|
+
const body = uniqueIds.map((id) => {
|
|
686
|
+
const workItemUpdates = updates.filter((update) => update.id === id);
|
|
687
|
+
const operations = workItemUpdates.map(({ op, path, value, format }) => ({
|
|
688
|
+
op: op,
|
|
689
|
+
path: path,
|
|
690
|
+
value: encodeFormattedValue(value, format),
|
|
691
|
+
}));
|
|
692
|
+
// Add format operations for Markdown fields
|
|
693
|
+
workItemUpdates.forEach(({ path, value, format }) => {
|
|
694
|
+
if (format === "Markdown" && value && value.length > 50) {
|
|
695
|
+
operations.push({
|
|
696
|
+
op: "Add",
|
|
697
|
+
path: `/multilineFieldsFormat${path.replace("/fields", "")}`,
|
|
698
|
+
value: "Markdown",
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
return {
|
|
703
|
+
method: "PATCH",
|
|
704
|
+
uri: `/_apis/wit/workitems/${id}?api-version=${batchApiVersion}`,
|
|
705
|
+
headers: {
|
|
706
|
+
"Content-Type": "application/json-patch+json",
|
|
707
|
+
},
|
|
708
|
+
body: operations,
|
|
709
|
+
};
|
|
530
710
|
});
|
|
531
|
-
|
|
711
|
+
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
532
712
|
method: "PATCH",
|
|
533
|
-
uri: `/_apis/wit/workitems/${id}?api-version=${batchApiVersion}`,
|
|
534
713
|
headers: {
|
|
535
|
-
"
|
|
714
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
715
|
+
"Content-Type": "application/json",
|
|
716
|
+
"User-Agent": userAgentProvider(),
|
|
536
717
|
},
|
|
537
|
-
body:
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
555
|
-
};
|
|
718
|
+
body: JSON.stringify(body),
|
|
719
|
+
});
|
|
720
|
+
if (!response.ok) {
|
|
721
|
+
throw new Error(`Failed to update work items in batch: ${response.statusText}`);
|
|
722
|
+
}
|
|
723
|
+
const result = await response.json();
|
|
724
|
+
return {
|
|
725
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
catch (error) {
|
|
729
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
730
|
+
return {
|
|
731
|
+
content: [{ type: "text", text: `Error updating work items in batch: ${errorMessage}` }],
|
|
732
|
+
isError: true,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
556
735
|
});
|
|
557
736
|
server.tool(WORKITEM_TOOLS.work_items_link, "Link work items together in batch.", {
|
|
558
737
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -568,47 +747,56 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
568
747
|
}))
|
|
569
748
|
.describe(""),
|
|
570
749
|
}, async ({ project, updates }) => {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
750
|
+
try {
|
|
751
|
+
const connection = await connectionProvider();
|
|
752
|
+
const orgUrl = connection.serverUrl;
|
|
753
|
+
const accessToken = await tokenProvider();
|
|
754
|
+
// Extract unique IDs from the updates array
|
|
755
|
+
const uniqueIds = Array.from(new Set(updates.map((update) => update.id)));
|
|
756
|
+
const body = uniqueIds.map((id) => ({
|
|
757
|
+
method: "PATCH",
|
|
758
|
+
uri: `/_apis/wit/workitems/${id}?api-version=${batchApiVersion}`,
|
|
759
|
+
headers: {
|
|
760
|
+
"Content-Type": "application/json-patch+json",
|
|
761
|
+
},
|
|
762
|
+
body: updates
|
|
763
|
+
.filter((update) => update.id === id)
|
|
764
|
+
.map(({ linkToId, type, comment }) => ({
|
|
765
|
+
op: "add",
|
|
766
|
+
path: "/relations/-",
|
|
767
|
+
value: {
|
|
768
|
+
rel: `${getLinkTypeFromName(type)}`,
|
|
769
|
+
url: `${orgUrl}/${project}/_apis/wit/workItems/${linkToId}`,
|
|
770
|
+
attributes: {
|
|
771
|
+
comment: comment || "",
|
|
772
|
+
},
|
|
592
773
|
},
|
|
774
|
+
})),
|
|
775
|
+
}));
|
|
776
|
+
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
777
|
+
method: "PATCH",
|
|
778
|
+
headers: {
|
|
779
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
780
|
+
"Content-Type": "application/json",
|
|
781
|
+
"User-Agent": userAgentProvider(),
|
|
593
782
|
},
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
"
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
};
|
|
783
|
+
body: JSON.stringify(body),
|
|
784
|
+
});
|
|
785
|
+
if (!response.ok) {
|
|
786
|
+
throw new Error(`Failed to update work items in batch: ${response.statusText}`);
|
|
787
|
+
}
|
|
788
|
+
const result = await response.json();
|
|
789
|
+
return {
|
|
790
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
795
|
+
return {
|
|
796
|
+
content: [{ type: "text", text: `Error linking work items: ${errorMessage}` }],
|
|
797
|
+
isError: true,
|
|
798
|
+
};
|
|
799
|
+
}
|
|
612
800
|
});
|
|
613
801
|
server.tool(WORKITEM_TOOLS.work_item_unlink, "Remove one or many links from a single work item", {
|
|
614
802
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|