@azure-devops/mcp 2.5.0-nightly.20260412 → 2.5.0-nightly.20260413
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/dist/tools/search.js +4 -1
- package/dist/tools/work-items.js +199 -59
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/tools/search.js
CHANGED
|
@@ -12,7 +12,10 @@ const SEARCH_TOOLS = {
|
|
|
12
12
|
function configureSearchTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
13
13
|
server.tool(SEARCH_TOOLS.search_code, "Search Azure DevOps Repositories for a given search text", {
|
|
14
14
|
searchText: z.string().describe("Keywords to search for in code repositories"),
|
|
15
|
-
project: z
|
|
15
|
+
project: z
|
|
16
|
+
.union([z.string().transform((value) => [value]), z.array(z.string())])
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Filter by projects"),
|
|
16
19
|
repository: z.array(z.string()).optional().describe("Filter by repositories"),
|
|
17
20
|
path: z.array(z.string()).optional().describe("Filter by paths"),
|
|
18
21
|
branch: z.array(z.string()).optional().describe("Filter by branches"),
|
package/dist/tools/work-items.js
CHANGED
|
@@ -4,7 +4,7 @@ import { WorkItemExpand } from "azure-devops-node-api/interfaces/WorkItemTrackin
|
|
|
4
4
|
import { QueryExpand } from "azure-devops-node-api/interfaces/WorkItemTrackingInterfaces.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { batchApiVersion, markdownCommentsApiVersion, getEnumKeys, safeEnumConvert, encodeFormattedValue } from "../utils.js";
|
|
7
|
-
import { elicitProject } from "../shared/elicitations.js";
|
|
7
|
+
import { elicitProject, elicitTeam } from "../shared/elicitations.js";
|
|
8
8
|
import { createExternalContentResponse } from "../shared/content-safety.js";
|
|
9
9
|
const WORKITEM_TOOLS = {
|
|
10
10
|
my_work_items: "wit_my_work_items",
|
|
@@ -62,14 +62,28 @@ function getLinkTypeFromName(name) {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
function configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
65
|
-
server.tool(WORKITEM_TOOLS.list_backlogs, "Receive a list of backlogs for a given project and team.", {
|
|
66
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
67
|
-
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
65
|
+
server.tool(WORKITEM_TOOLS.list_backlogs, "Receive a list of backlogs for a given project and team. If a project or team is not specified, you will be prompted to select one.", {
|
|
66
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
67
|
+
team: z.string().optional().describe("The name or ID of the Azure DevOps team. Reuse from prior context if already known. If not provided, a team selection prompt will be shown."),
|
|
68
68
|
}, async ({ project, team }) => {
|
|
69
69
|
try {
|
|
70
70
|
const connection = await connectionProvider();
|
|
71
|
+
let resolvedProject = project;
|
|
72
|
+
if (!resolvedProject) {
|
|
73
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list backlogs for.");
|
|
74
|
+
if ("response" in result)
|
|
75
|
+
return result.response;
|
|
76
|
+
resolvedProject = result.resolved;
|
|
77
|
+
}
|
|
78
|
+
let resolvedTeam = team;
|
|
79
|
+
if (!resolvedTeam) {
|
|
80
|
+
const result = await elicitTeam(server, connection, resolvedProject, "Select the Azure DevOps team to list backlogs for.");
|
|
81
|
+
if ("response" in result)
|
|
82
|
+
return result.response;
|
|
83
|
+
resolvedTeam = result.resolved;
|
|
84
|
+
}
|
|
71
85
|
const workApi = await connection.getWorkApi();
|
|
72
|
-
const teamContext = { project, team };
|
|
86
|
+
const teamContext = { project: resolvedProject, team: resolvedTeam };
|
|
73
87
|
const backlogs = await workApi.getBacklogs(teamContext);
|
|
74
88
|
return {
|
|
75
89
|
content: [{ type: "text", text: JSON.stringify(backlogs, null, 2) }],
|
|
@@ -83,15 +97,29 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
83
97
|
};
|
|
84
98
|
}
|
|
85
99
|
});
|
|
86
|
-
server.tool(WORKITEM_TOOLS.list_backlog_work_items, "Retrieve a list of backlogs of for a given project, team, and backlog category", {
|
|
87
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
88
|
-
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
100
|
+
server.tool(WORKITEM_TOOLS.list_backlog_work_items, "Retrieve a list of backlogs of for a given project, team, and backlog category. If a project or team is not specified, you will be prompted to select one.", {
|
|
101
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
102
|
+
team: z.string().optional().describe("The name or ID of the Azure DevOps team. Reuse from prior context if already known. If not provided, a team selection prompt will be shown."),
|
|
89
103
|
backlogId: z.string().describe("The ID of the backlog category to retrieve work items from."),
|
|
90
104
|
}, async ({ project, team, backlogId }) => {
|
|
91
105
|
try {
|
|
92
106
|
const connection = await connectionProvider();
|
|
107
|
+
let resolvedProject = project;
|
|
108
|
+
if (!resolvedProject) {
|
|
109
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list backlog work items for.");
|
|
110
|
+
if ("response" in result)
|
|
111
|
+
return result.response;
|
|
112
|
+
resolvedProject = result.resolved;
|
|
113
|
+
}
|
|
114
|
+
let resolvedTeam = team;
|
|
115
|
+
if (!resolvedTeam) {
|
|
116
|
+
const result = await elicitTeam(server, connection, resolvedProject, "Select the Azure DevOps team to list backlog work items for.");
|
|
117
|
+
if ("response" in result)
|
|
118
|
+
return result.response;
|
|
119
|
+
resolvedTeam = result.resolved;
|
|
120
|
+
}
|
|
93
121
|
const workApi = await connection.getWorkApi();
|
|
94
|
-
const teamContext = { project, team };
|
|
122
|
+
const teamContext = { project: resolvedProject, team: resolvedTeam };
|
|
95
123
|
const workItems = await workApi.getBacklogLevelWorkItems(teamContext, backlogId);
|
|
96
124
|
return {
|
|
97
125
|
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
@@ -105,16 +133,23 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
105
133
|
};
|
|
106
134
|
}
|
|
107
135
|
});
|
|
108
|
-
server.tool(WORKITEM_TOOLS.my_work_items, "Retrieve a list of work items relevent to the authenticated user.", {
|
|
109
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
136
|
+
server.tool(WORKITEM_TOOLS.my_work_items, "Retrieve a list of work items relevent to the authenticated user. If a project is not specified, you will be prompted to select one.", {
|
|
137
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
110
138
|
type: z.enum(["assignedtome", "myactivity"]).default("assignedtome").describe("The type of work items to retrieve. Defaults to 'assignedtome'."),
|
|
111
139
|
top: z.coerce.number().default(50).describe("The maximum number of work items to return. Defaults to 50."),
|
|
112
140
|
includeCompleted: z.boolean().default(false).describe("Whether to include completed work items. Defaults to false."),
|
|
113
141
|
}, async ({ project, type, top, includeCompleted }) => {
|
|
114
142
|
try {
|
|
115
143
|
const connection = await connectionProvider();
|
|
144
|
+
let resolvedProject = project;
|
|
145
|
+
if (!resolvedProject) {
|
|
146
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve work items for.");
|
|
147
|
+
if ("response" in result)
|
|
148
|
+
return result.response;
|
|
149
|
+
resolvedProject = result.resolved;
|
|
150
|
+
}
|
|
116
151
|
const workApi = await connection.getWorkApi();
|
|
117
|
-
const workItems = await workApi.getPredefinedQueryResults(
|
|
152
|
+
const workItems = await workApi.getPredefinedQueryResults(resolvedProject, type, top, includeCompleted);
|
|
118
153
|
return {
|
|
119
154
|
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
120
155
|
};
|
|
@@ -127,18 +162,25 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
127
162
|
};
|
|
128
163
|
}
|
|
129
164
|
});
|
|
130
|
-
server.tool(WORKITEM_TOOLS.get_work_items_batch_by_ids, "Retrieve list of work items by IDs in batch.", {
|
|
131
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
165
|
+
server.tool(WORKITEM_TOOLS.get_work_items_batch_by_ids, "Retrieve list of work items by IDs in batch. If a project is not specified, you will be prompted to select one.", {
|
|
166
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
132
167
|
ids: z.array(z.coerce.number().min(1)).describe("The IDs of the work items to retrieve."),
|
|
133
168
|
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."),
|
|
134
169
|
}, async ({ project, ids, fields }) => {
|
|
135
170
|
try {
|
|
136
171
|
const connection = await connectionProvider();
|
|
172
|
+
let resolvedProject = project;
|
|
173
|
+
if (!resolvedProject) {
|
|
174
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve work items for.");
|
|
175
|
+
if ("response" in result)
|
|
176
|
+
return result.response;
|
|
177
|
+
resolvedProject = result.resolved;
|
|
178
|
+
}
|
|
137
179
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
138
180
|
const defaultFields = ["System.Id", "System.WorkItemType", "System.Title", "System.State", "System.Parent", "System.Tags", "Microsoft.VSTS.Common.StackRank", "System.AssignedTo"];
|
|
139
181
|
// If no fields are provided, use the default set of fields
|
|
140
182
|
const fieldsToUse = !fields || fields.length === 0 ? defaultFields : fields;
|
|
141
|
-
const workitems = await workItemApi.getWorkItemsBatch({ ids, fields: fieldsToUse },
|
|
183
|
+
const workitems = await workItemApi.getWorkItemsBatch({ ids, fields: fieldsToUse }, resolvedProject);
|
|
142
184
|
// List of identity fields that need to be transformed from objects to formatted strings
|
|
143
185
|
const identityFields = [
|
|
144
186
|
"System.AssignedTo",
|
|
@@ -177,9 +219,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
177
219
|
};
|
|
178
220
|
}
|
|
179
221
|
});
|
|
180
|
-
server.tool(WORKITEM_TOOLS.get_work_item, "Get a single work item by ID.", {
|
|
222
|
+
server.tool(WORKITEM_TOOLS.get_work_item, "Get a single work item by ID. If a project is not specified, you will be prompted to select one.", {
|
|
181
223
|
id: z.coerce.number().min(1).describe("The ID of the work item to retrieve."),
|
|
182
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
224
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
183
225
|
fields: z.array(z.string()).optional().describe("Optional list of fields to include in the response. If not provided, all fields will be returned."),
|
|
184
226
|
asOf: z.coerce.date().optional().describe("Optional date string to retrieve the work item as of a specific time. If not provided, the current state will be returned."),
|
|
185
227
|
expand: z
|
|
@@ -190,8 +232,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
190
232
|
}, async ({ id, project, fields, asOf, expand }) => {
|
|
191
233
|
try {
|
|
192
234
|
const connection = await connectionProvider();
|
|
235
|
+
let resolvedProject = project;
|
|
236
|
+
if (!resolvedProject) {
|
|
237
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the work item from.");
|
|
238
|
+
if ("response" in result)
|
|
239
|
+
return result.response;
|
|
240
|
+
resolvedProject = result.resolved;
|
|
241
|
+
}
|
|
193
242
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
194
|
-
const workItem = await workItemApi.getWorkItem(id, fields, asOf, expand,
|
|
243
|
+
const workItem = await workItemApi.getWorkItem(id, fields, asOf, expand, resolvedProject);
|
|
195
244
|
return {
|
|
196
245
|
content: [{ type: "text", text: JSON.stringify(workItem, null, 2) }],
|
|
197
246
|
};
|
|
@@ -204,15 +253,22 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
204
253
|
};
|
|
205
254
|
}
|
|
206
255
|
});
|
|
207
|
-
server.tool(WORKITEM_TOOLS.list_work_item_comments, "Retrieve list of comments for a work item by ID.", {
|
|
208
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
256
|
+
server.tool(WORKITEM_TOOLS.list_work_item_comments, "Retrieve list of comments for a work item by ID. If a project is not specified, you will be prompted to select one.", {
|
|
257
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
209
258
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item to retrieve comments for."),
|
|
210
259
|
top: z.coerce.number().default(50).describe("Optional number of comments to retrieve. Defaults to all comments."),
|
|
211
260
|
}, async ({ project, workItemId, top }) => {
|
|
212
261
|
try {
|
|
213
262
|
const connection = await connectionProvider();
|
|
263
|
+
let resolvedProject = project;
|
|
264
|
+
if (!resolvedProject) {
|
|
265
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list work item comments for.");
|
|
266
|
+
if ("response" in result)
|
|
267
|
+
return result.response;
|
|
268
|
+
resolvedProject = result.resolved;
|
|
269
|
+
}
|
|
214
270
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
215
|
-
const comments = await workItemApi.getComments(
|
|
271
|
+
const comments = await workItemApi.getComments(resolvedProject, workItemId, top);
|
|
216
272
|
return {
|
|
217
273
|
content: [{ type: "text", text: JSON.stringify(comments, null, 2) }],
|
|
218
274
|
};
|
|
@@ -225,21 +281,28 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
225
281
|
};
|
|
226
282
|
}
|
|
227
283
|
});
|
|
228
|
-
server.tool(WORKITEM_TOOLS.add_work_item_comment, "Add comment to a work item by ID.", {
|
|
229
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
284
|
+
server.tool(WORKITEM_TOOLS.add_work_item_comment, "Add comment to a work item by ID. If a project is not specified, you will be prompted to select one.", {
|
|
285
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
230
286
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item to add a comment to."),
|
|
231
287
|
comment: z.string().describe("The text of the comment to add to the work item."),
|
|
232
288
|
format: z.enum(["markdown", "html"]).optional().default("html"),
|
|
233
289
|
}, async ({ project, workItemId, comment, format }) => {
|
|
234
290
|
try {
|
|
235
291
|
const connection = await connectionProvider();
|
|
292
|
+
let resolvedProject = project;
|
|
293
|
+
if (!resolvedProject) {
|
|
294
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to add a work item comment in.");
|
|
295
|
+
if ("response" in result)
|
|
296
|
+
return result.response;
|
|
297
|
+
resolvedProject = result.resolved;
|
|
298
|
+
}
|
|
236
299
|
const orgUrl = connection.serverUrl;
|
|
237
300
|
const accessToken = await tokenProvider();
|
|
238
301
|
const body = {
|
|
239
302
|
text: comment,
|
|
240
303
|
};
|
|
241
304
|
const formatParameter = format === "markdown" ? 0 : 1;
|
|
242
|
-
const response = await fetch(`${orgUrl}/${encodeURIComponent(
|
|
305
|
+
const response = await fetch(`${orgUrl}/${encodeURIComponent(resolvedProject)}/_apis/wit/workItems/${workItemId}/comments?format=${formatParameter}&api-version=${markdownCommentsApiVersion}`, {
|
|
243
306
|
method: "POST",
|
|
244
307
|
headers: {
|
|
245
308
|
"Authorization": `Bearer ${accessToken}`,
|
|
@@ -264,8 +327,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
264
327
|
};
|
|
265
328
|
}
|
|
266
329
|
});
|
|
267
|
-
server.tool(WORKITEM_TOOLS.update_work_item_comment, "Update an existing comment on a work item by ID.", {
|
|
268
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
330
|
+
server.tool(WORKITEM_TOOLS.update_work_item_comment, "Update an existing comment on a work item by ID. If a project is not specified, you will be prompted to select one.", {
|
|
331
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
269
332
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item."),
|
|
270
333
|
commentId: z.coerce.number().min(1).describe("The ID of the comment to update."),
|
|
271
334
|
text: z.string().describe("The updated comment text."),
|
|
@@ -273,11 +336,18 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
273
336
|
}, async ({ project, workItemId, commentId, text, format }) => {
|
|
274
337
|
try {
|
|
275
338
|
const connection = await connectionProvider();
|
|
339
|
+
let resolvedProject = project;
|
|
340
|
+
if (!resolvedProject) {
|
|
341
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to update the work item comment in.");
|
|
342
|
+
if ("response" in result)
|
|
343
|
+
return result.response;
|
|
344
|
+
resolvedProject = result.resolved;
|
|
345
|
+
}
|
|
276
346
|
const orgUrl = connection.serverUrl;
|
|
277
347
|
const accessToken = await tokenProvider();
|
|
278
348
|
const body = { text };
|
|
279
349
|
const formatParameter = format === "markdown" ? 0 : 1;
|
|
280
|
-
const response = await fetch(`${orgUrl}/${encodeURIComponent(
|
|
350
|
+
const response = await fetch(`${orgUrl}/${encodeURIComponent(resolvedProject)}/_apis/wit/workItems/${workItemId}/comments/${commentId}?format=${formatParameter}&api-version=${markdownCommentsApiVersion}`, {
|
|
281
351
|
method: "PATCH",
|
|
282
352
|
headers: {
|
|
283
353
|
"Authorization": `Bearer ${accessToken}`,
|
|
@@ -302,8 +372,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
302
372
|
};
|
|
303
373
|
}
|
|
304
374
|
});
|
|
305
|
-
server.tool(WORKITEM_TOOLS.list_work_item_revisions, "Retrieve list of revisions for a work item by ID.", {
|
|
306
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
375
|
+
server.tool(WORKITEM_TOOLS.list_work_item_revisions, "Retrieve list of revisions for a work item by ID. If a project is not specified, you will be prompted to select one.", {
|
|
376
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
307
377
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item to retrieve revisions for."),
|
|
308
378
|
top: z.coerce.number().default(50).describe("Optional number of revisions to retrieve. If not provided, all revisions will be returned."),
|
|
309
379
|
skip: z.coerce.number().optional().describe("Optional number of revisions to skip for pagination. Defaults to 0."),
|
|
@@ -315,8 +385,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
315
385
|
}, async ({ project, workItemId, top, skip, expand }) => {
|
|
316
386
|
try {
|
|
317
387
|
const connection = await connectionProvider();
|
|
388
|
+
let resolvedProject = project;
|
|
389
|
+
if (!resolvedProject) {
|
|
390
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list work item revisions for.");
|
|
391
|
+
if ("response" in result)
|
|
392
|
+
return result.response;
|
|
393
|
+
resolvedProject = result.resolved;
|
|
394
|
+
}
|
|
318
395
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
319
|
-
const revisions = await workItemApi.getRevisions(workItemId, top, skip, safeEnumConvert(WorkItemExpand, expand),
|
|
396
|
+
const revisions = await workItemApi.getRevisions(workItemId, top, skip, safeEnumConvert(WorkItemExpand, expand), resolvedProject);
|
|
320
397
|
// Dynamically clean up identity objects in revision fields
|
|
321
398
|
// Identity objects typically have properties like displayName, url, _links, id, uniqueName, imageUrl, descriptor
|
|
322
399
|
if (revisions && Array.isArray(revisions)) {
|
|
@@ -354,9 +431,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
354
431
|
};
|
|
355
432
|
}
|
|
356
433
|
});
|
|
357
|
-
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.", {
|
|
434
|
+
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. If a project is not specified, you will be prompted to select one.", {
|
|
358
435
|
parentId: z.coerce.number().min(1).describe("The ID of the parent work item to create a child work item under."),
|
|
359
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
436
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
360
437
|
workItemType: z.string().describe("The type of the child work item to create."),
|
|
361
438
|
items: z.array(z.object({
|
|
362
439
|
title: z.string().describe("The title of the child work item."),
|
|
@@ -368,6 +445,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
368
445
|
}, async ({ parentId, project, workItemType, items }) => {
|
|
369
446
|
try {
|
|
370
447
|
const connection = await connectionProvider();
|
|
448
|
+
let resolvedProject = project;
|
|
449
|
+
if (!resolvedProject) {
|
|
450
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to create child work items in.");
|
|
451
|
+
if ("response" in result)
|
|
452
|
+
return result.response;
|
|
453
|
+
resolvedProject = result.resolved;
|
|
454
|
+
}
|
|
371
455
|
const orgUrl = connection.serverUrl;
|
|
372
456
|
const accessToken = await tokenProvider();
|
|
373
457
|
if (items.length > 50) {
|
|
@@ -404,7 +488,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
404
488
|
path: "/relations/-",
|
|
405
489
|
value: {
|
|
406
490
|
rel: "System.LinkTypes.Hierarchy-Reverse",
|
|
407
|
-
url: `${connection.serverUrl}/${
|
|
491
|
+
url: `${connection.serverUrl}/${resolvedProject}/_apis/wit/workItems/${parentId}`,
|
|
408
492
|
},
|
|
409
493
|
},
|
|
410
494
|
];
|
|
@@ -436,7 +520,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
436
520
|
}
|
|
437
521
|
return {
|
|
438
522
|
method: "PATCH",
|
|
439
|
-
uri: `/${encodeURIComponent(
|
|
523
|
+
uri: `/${encodeURIComponent(resolvedProject)}/_apis/wit/workitems/$${encodeURIComponent(workItemType)}?api-version=${batchApiVersion}`,
|
|
440
524
|
headers: {
|
|
441
525
|
"Content-Type": "application/json-patch+json",
|
|
442
526
|
},
|
|
@@ -523,16 +607,23 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
523
607
|
};
|
|
524
608
|
}
|
|
525
609
|
});
|
|
526
|
-
server.tool(WORKITEM_TOOLS.get_work_items_for_iteration, "Retrieve a list of work items for a specified iteration.", {
|
|
527
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
610
|
+
server.tool(WORKITEM_TOOLS.get_work_items_for_iteration, "Retrieve a list of work items for a specified iteration. If a project is not specified, you will be prompted to select one.", {
|
|
611
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
528
612
|
team: z.string().optional().describe("The name or ID of the Azure DevOps team. If not provided, the default team will be used."),
|
|
529
613
|
iterationId: z.string().describe("The ID of the iteration to retrieve work items for."),
|
|
530
614
|
}, async ({ project, team, iterationId }) => {
|
|
531
615
|
try {
|
|
532
616
|
const connection = await connectionProvider();
|
|
617
|
+
let resolvedProject = project;
|
|
618
|
+
if (!resolvedProject) {
|
|
619
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve work items for iteration.");
|
|
620
|
+
if ("response" in result)
|
|
621
|
+
return result.response;
|
|
622
|
+
resolvedProject = result.resolved;
|
|
623
|
+
}
|
|
533
624
|
const workApi = await connection.getWorkApi();
|
|
534
625
|
//get the work items for the current iteration
|
|
535
|
-
const workItems = await workApi.getIterationWorkItems({ project, team }, iterationId);
|
|
626
|
+
const workItems = await workApi.getIterationWorkItems({ project: resolvedProject, team }, iterationId);
|
|
536
627
|
return {
|
|
537
628
|
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
538
629
|
};
|
|
@@ -581,14 +672,21 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
581
672
|
};
|
|
582
673
|
}
|
|
583
674
|
});
|
|
584
|
-
server.tool(WORKITEM_TOOLS.get_work_item_type, "Get a specific work item type.", {
|
|
585
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
675
|
+
server.tool(WORKITEM_TOOLS.get_work_item_type, "Get a specific work item type. If a project is not specified, you will be prompted to select one.", {
|
|
676
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
586
677
|
workItemType: z.string().describe("The name of the work item type to retrieve."),
|
|
587
678
|
}, async ({ project, workItemType }) => {
|
|
588
679
|
try {
|
|
589
680
|
const connection = await connectionProvider();
|
|
681
|
+
let resolvedProject = project;
|
|
682
|
+
if (!resolvedProject) {
|
|
683
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the work item type from.");
|
|
684
|
+
if ("response" in result)
|
|
685
|
+
return result.response;
|
|
686
|
+
resolvedProject = result.resolved;
|
|
687
|
+
}
|
|
590
688
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
591
|
-
const workItemTypeInfo = await workItemApi.getWorkItemType(
|
|
689
|
+
const workItemTypeInfo = await workItemApi.getWorkItemType(resolvedProject, workItemType);
|
|
592
690
|
return {
|
|
593
691
|
content: [{ type: "text", text: JSON.stringify(workItemTypeInfo, null, 2) }],
|
|
594
692
|
};
|
|
@@ -601,8 +699,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
601
699
|
};
|
|
602
700
|
}
|
|
603
701
|
});
|
|
604
|
-
server.tool(WORKITEM_TOOLS.create_work_item, "Create a new work item in a specified project and work item type.", {
|
|
605
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
702
|
+
server.tool(WORKITEM_TOOLS.create_work_item, "Create a new work item in a specified project and work item type. If a project is not specified, you will be prompted to select one.", {
|
|
703
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
606
704
|
workItemType: z.string().describe("The type of work item to create, e.g., 'Task', 'Bug', etc."),
|
|
607
705
|
fields: z
|
|
608
706
|
.array(z.object({
|
|
@@ -614,6 +712,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
614
712
|
}, async ({ project, workItemType, fields }) => {
|
|
615
713
|
try {
|
|
616
714
|
const connection = await connectionProvider();
|
|
715
|
+
let resolvedProject = project;
|
|
716
|
+
if (!resolvedProject) {
|
|
717
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to create the work item in.");
|
|
718
|
+
if ("response" in result)
|
|
719
|
+
return result.response;
|
|
720
|
+
resolvedProject = result.resolved;
|
|
721
|
+
}
|
|
617
722
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
618
723
|
const document = fields.map(({ name, value, format }) => ({
|
|
619
724
|
op: "add",
|
|
@@ -632,7 +737,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
632
737
|
});
|
|
633
738
|
}
|
|
634
739
|
});
|
|
635
|
-
const newWorkItem = await workItemApi.createWorkItem(null, document,
|
|
740
|
+
const newWorkItem = await workItemApi.createWorkItem(null, document, resolvedProject, workItemType);
|
|
636
741
|
if (!newWorkItem) {
|
|
637
742
|
return { content: [{ type: "text", text: "Work item was not created" }], isError: true };
|
|
638
743
|
}
|
|
@@ -648,8 +753,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
648
753
|
};
|
|
649
754
|
}
|
|
650
755
|
});
|
|
651
|
-
server.tool(WORKITEM_TOOLS.get_query, "Get a query by its ID or path.", {
|
|
652
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
756
|
+
server.tool(WORKITEM_TOOLS.get_query, "Get a query by its ID or path. If a project is not specified, you will be prompted to select one.", {
|
|
757
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
653
758
|
query: z.string().describe("The ID or path of the query to retrieve."),
|
|
654
759
|
expand: z
|
|
655
760
|
.enum(getEnumKeys(QueryExpand))
|
|
@@ -661,8 +766,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
661
766
|
}, async ({ project, query, expand, depth, includeDeleted, useIsoDateFormat }) => {
|
|
662
767
|
try {
|
|
663
768
|
const connection = await connectionProvider();
|
|
769
|
+
let resolvedProject = project;
|
|
770
|
+
if (!resolvedProject) {
|
|
771
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the query from.");
|
|
772
|
+
if ("response" in result)
|
|
773
|
+
return result.response;
|
|
774
|
+
resolvedProject = result.resolved;
|
|
775
|
+
}
|
|
664
776
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
665
|
-
const queryDetails = await workItemApi.getQuery(
|
|
777
|
+
const queryDetails = await workItemApi.getQuery(resolvedProject, query, safeEnumConvert(QueryExpand, expand), depth, includeDeleted, useIsoDateFormat);
|
|
666
778
|
return {
|
|
667
779
|
content: [{ type: "text", text: JSON.stringify(queryDetails, null, 2) }],
|
|
668
780
|
};
|
|
@@ -776,8 +888,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
776
888
|
};
|
|
777
889
|
}
|
|
778
890
|
});
|
|
779
|
-
server.tool(WORKITEM_TOOLS.work_items_link, "Link work items together in batch.", {
|
|
780
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
891
|
+
server.tool(WORKITEM_TOOLS.work_items_link, "Link work items together in batch. If a project is not specified, you will be prompted to select one.", {
|
|
892
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
781
893
|
updates: z
|
|
782
894
|
.array(z.object({
|
|
783
895
|
id: z.coerce.number().min(1).describe("The ID of the work item to update."),
|
|
@@ -792,6 +904,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
792
904
|
}, async ({ project, updates }) => {
|
|
793
905
|
try {
|
|
794
906
|
const connection = await connectionProvider();
|
|
907
|
+
let resolvedProject = project;
|
|
908
|
+
if (!resolvedProject) {
|
|
909
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to link work items in.");
|
|
910
|
+
if ("response" in result)
|
|
911
|
+
return result.response;
|
|
912
|
+
resolvedProject = result.resolved;
|
|
913
|
+
}
|
|
795
914
|
const orgUrl = connection.serverUrl;
|
|
796
915
|
const accessToken = await tokenProvider();
|
|
797
916
|
// Extract unique IDs from the updates array
|
|
@@ -809,7 +928,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
809
928
|
path: "/relations/-",
|
|
810
929
|
value: {
|
|
811
930
|
rel: `${getLinkTypeFromName(type)}`,
|
|
812
|
-
url: `${orgUrl}/${
|
|
931
|
+
url: `${orgUrl}/${resolvedProject}/_apis/wit/workItems/${linkToId}`,
|
|
813
932
|
attributes: {
|
|
814
933
|
comment: comment || "",
|
|
815
934
|
},
|
|
@@ -841,8 +960,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
841
960
|
};
|
|
842
961
|
}
|
|
843
962
|
});
|
|
844
|
-
server.tool(WORKITEM_TOOLS.work_item_unlink, "Remove one or many links from a single work item", {
|
|
845
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
963
|
+
server.tool(WORKITEM_TOOLS.work_item_unlink, "Remove one or many links from a single work item. If a project is not specified, you will be prompted to select one.", {
|
|
964
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
846
965
|
id: z.coerce.number().min(1).describe("The ID of the work item to remove the links from."),
|
|
847
966
|
type: z
|
|
848
967
|
.enum(["parent", "child", "duplicate", "duplicate of", "related", "successor", "predecessor", "tested by", "tests", "affects", "affected by", "artifact"])
|
|
@@ -852,8 +971,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
852
971
|
}, async ({ project, id, type, url }) => {
|
|
853
972
|
try {
|
|
854
973
|
const connection = await connectionProvider();
|
|
974
|
+
let resolvedProject = project;
|
|
975
|
+
if (!resolvedProject) {
|
|
976
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to unlink work items in.");
|
|
977
|
+
if ("response" in result)
|
|
978
|
+
return result.response;
|
|
979
|
+
resolvedProject = result.resolved;
|
|
980
|
+
}
|
|
855
981
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
856
|
-
const workItem = await workItemApi.getWorkItem(id, undefined, undefined, WorkItemExpand.Relations,
|
|
982
|
+
const workItem = await workItemApi.getWorkItem(id, undefined, undefined, WorkItemExpand.Relations, resolvedProject);
|
|
857
983
|
const relations = workItem.relations ?? [];
|
|
858
984
|
const linkType = getLinkTypeFromName(type);
|
|
859
985
|
let relationIndexes = [];
|
|
@@ -879,7 +1005,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
879
1005
|
op: "remove",
|
|
880
1006
|
path: `/relations/${idx}`,
|
|
881
1007
|
}));
|
|
882
|
-
const updatedWorkItem = await workItemApi.updateWorkItem(null, apiUpdates, id,
|
|
1008
|
+
const updatedWorkItem = await workItemApi.updateWorkItem(null, apiUpdates, id, resolvedProject);
|
|
883
1009
|
return {
|
|
884
1010
|
content: [
|
|
885
1011
|
{
|
|
@@ -905,9 +1031,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
905
1031
|
};
|
|
906
1032
|
}
|
|
907
1033
|
});
|
|
908
|
-
server.tool(WORKITEM_TOOLS.add_artifact_link, "Add artifact links (repository, branch, commit, builds) to work items. You can either provide the full vstfs URI or the individual components to build it automatically.", {
|
|
1034
|
+
server.tool(WORKITEM_TOOLS.add_artifact_link, "Add artifact links (repository, branch, commit, builds) to work items. You can either provide the full vstfs URI or the individual components to build it automatically. If a project is not specified, you will be prompted to select one.", {
|
|
909
1035
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item to add the artifact link to."),
|
|
910
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
1036
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
911
1037
|
// Option 1: Provide full URI directly
|
|
912
1038
|
artifactUri: z.string().optional().describe("The complete VSTFS URI of the artifact to link. If provided, individual component parameters are ignored."),
|
|
913
1039
|
// Option 2: Provide individual components to build URI automatically based on linkType
|
|
@@ -940,6 +1066,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
940
1066
|
}, async ({ workItemId, project, artifactUri, projectId, repositoryId, branchName, commitId, pullRequestId, buildId, linkType, comment }) => {
|
|
941
1067
|
try {
|
|
942
1068
|
const connection = await connectionProvider();
|
|
1069
|
+
let resolvedProject = project;
|
|
1070
|
+
if (!resolvedProject) {
|
|
1071
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to add the artifact link in.");
|
|
1072
|
+
if ("response" in result)
|
|
1073
|
+
return result.response;
|
|
1074
|
+
resolvedProject = result.resolved;
|
|
1075
|
+
}
|
|
943
1076
|
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
944
1077
|
let finalArtifactUri;
|
|
945
1078
|
if (artifactUri) {
|
|
@@ -1010,7 +1143,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
1010
1143
|
},
|
|
1011
1144
|
];
|
|
1012
1145
|
// Use the WorkItem API to update the work item with the new relation
|
|
1013
|
-
const workItem = await workItemTrackingApi.updateWorkItem({}, patchDocument, workItemId,
|
|
1146
|
+
const workItem = await workItemTrackingApi.updateWorkItem({}, patchDocument, workItemId, resolvedProject);
|
|
1014
1147
|
if (!workItem) {
|
|
1015
1148
|
return { content: [{ type: "text", text: "Work item update failed" }], isError: true };
|
|
1016
1149
|
}
|
|
@@ -1066,15 +1199,22 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
1066
1199
|
};
|
|
1067
1200
|
}
|
|
1068
1201
|
});
|
|
1069
|
-
server.tool(WORKITEM_TOOLS.get_work_item_attachment, "Download a work item attachment by its ID and return the content as a base64-encoded resource. Useful for viewing images (e.g. screenshots) attached to work items such as bugs.", {
|
|
1070
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
1202
|
+
server.tool(WORKITEM_TOOLS.get_work_item_attachment, "Download a work item attachment by its ID and return the content as a base64-encoded resource. Useful for viewing images (e.g. screenshots) attached to work items such as bugs. If a project is not specified, you will be prompted to select one.", {
|
|
1203
|
+
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
1071
1204
|
attachmentId: z.string().describe("The GUID of the attachment. Found in the attachment URL: https://dev.azure.com/{org}/{project}/_apis/wit/attachments/{attachmentId}"),
|
|
1072
1205
|
fileName: z.string().optional().describe("The file name of the attachment, e.g. 'screenshot.png'. Used to determine the MIME type for the returned resource."),
|
|
1073
1206
|
}, async ({ project, attachmentId, fileName }) => {
|
|
1074
1207
|
try {
|
|
1075
1208
|
const connection = await connectionProvider();
|
|
1209
|
+
let resolvedProject = project;
|
|
1210
|
+
if (!resolvedProject) {
|
|
1211
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the work item attachment from.");
|
|
1212
|
+
if ("response" in result)
|
|
1213
|
+
return result.response;
|
|
1214
|
+
resolvedProject = result.resolved;
|
|
1215
|
+
}
|
|
1076
1216
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
1077
|
-
const stream = await workItemApi.getAttachmentContent(attachmentId, fileName,
|
|
1217
|
+
const stream = await workItemApi.getAttachmentContent(attachmentId, fileName, resolvedProject);
|
|
1078
1218
|
const chunks = [];
|
|
1079
1219
|
await new Promise((resolve, reject) => {
|
|
1080
1220
|
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "2.5.0-nightly.
|
|
1
|
+
export const packageVersion = "2.5.0-nightly.20260413";
|