@azure-devops/mcp 2.5.0-nightly.20260412 → 2.5.0-nightly.20260414
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/repositories.js +46 -12
- package/dist/tools/search.js +4 -1
- package/dist/tools/work-items.js +210 -62
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -811,42 +811,76 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
811
811
|
project: z.string().optional().describe("Project ID or project name. Required when repositoryId is a repository name instead of a GUID."),
|
|
812
812
|
includeWorkItemRefs: z.boolean().optional().default(false).describe("Whether to reference work items associated with the pull request."),
|
|
813
813
|
includeLabels: z.boolean().optional().default(false).describe("Whether to include a summary of labels in the response."),
|
|
814
|
-
|
|
814
|
+
includeChangedFiles: z.boolean().optional().default(false).describe("Whether to include the list of files changed in the pull request."),
|
|
815
|
+
}, async ({ repositoryId, pullRequestId, project, includeWorkItemRefs, includeLabels, includeChangedFiles }) => {
|
|
815
816
|
try {
|
|
816
817
|
const connection = await connectionProvider();
|
|
817
818
|
const gitApi = await connection.getGitApi();
|
|
818
819
|
const pullRequest = await gitApi.getPullRequest(repositoryId, pullRequestId, project, undefined, undefined, undefined, undefined, includeWorkItemRefs);
|
|
820
|
+
let enhancedResponse = { ...pullRequest };
|
|
819
821
|
if (includeLabels) {
|
|
820
822
|
try {
|
|
821
823
|
const projectId = pullRequest.repository?.project?.id;
|
|
822
824
|
const projectName = pullRequest.repository?.project?.name;
|
|
823
825
|
const labels = await gitApi.getPullRequestLabels(repositoryId, pullRequestId, projectName, projectId);
|
|
824
826
|
const labelNames = labels.map((label) => label.name).filter((name) => name !== undefined);
|
|
825
|
-
|
|
826
|
-
...
|
|
827
|
+
enhancedResponse = {
|
|
828
|
+
...enhancedResponse,
|
|
827
829
|
labelSummary: {
|
|
828
830
|
labels: labelNames,
|
|
829
831
|
labelCount: labelNames.length,
|
|
830
832
|
},
|
|
831
833
|
};
|
|
832
|
-
return {
|
|
833
|
-
content: [{ type: "text", text: JSON.stringify(enhancedResponse, null, 2) }],
|
|
834
|
-
};
|
|
835
834
|
}
|
|
836
835
|
catch (error) {
|
|
837
836
|
console.warn(`Error fetching PR labels: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
...pullRequest,
|
|
837
|
+
enhancedResponse = {
|
|
838
|
+
...enhancedResponse,
|
|
841
839
|
labelSummary: {},
|
|
842
840
|
};
|
|
843
|
-
|
|
844
|
-
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
if (includeChangedFiles) {
|
|
844
|
+
try {
|
|
845
|
+
const iterations = await gitApi.getPullRequestIterations(repositoryId, pullRequestId, project);
|
|
846
|
+
if (iterations?.length) {
|
|
847
|
+
const latestIteration = iterations[iterations.length - 1];
|
|
848
|
+
if (latestIteration.id != null) {
|
|
849
|
+
const changes = await gitApi.getPullRequestIterationChanges(repositoryId, pullRequestId, latestIteration.id, project);
|
|
850
|
+
enhancedResponse = {
|
|
851
|
+
...enhancedResponse,
|
|
852
|
+
changedFilesSummary: {
|
|
853
|
+
changeEntries: changes?.changeEntries ?? [],
|
|
854
|
+
fileCount: changes?.changeEntries?.length ?? 0,
|
|
855
|
+
nextSkip: changes?.nextSkip,
|
|
856
|
+
nextTop: changes?.nextTop,
|
|
857
|
+
},
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
else {
|
|
861
|
+
enhancedResponse = {
|
|
862
|
+
...enhancedResponse,
|
|
863
|
+
changedFilesSummary: { changeEntries: [], fileCount: 0 },
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
enhancedResponse = {
|
|
869
|
+
...enhancedResponse,
|
|
870
|
+
changedFilesSummary: { changeEntries: [], fileCount: 0 },
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
catch (error) {
|
|
875
|
+
console.warn(`Error fetching PR changed files: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
876
|
+
enhancedResponse = {
|
|
877
|
+
...enhancedResponse,
|
|
878
|
+
changedFilesSummary: {},
|
|
845
879
|
};
|
|
846
880
|
}
|
|
847
881
|
}
|
|
848
882
|
return {
|
|
849
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
883
|
+
content: [{ type: "text", text: JSON.stringify(enhancedResponse, null, 2) }],
|
|
850
884
|
};
|
|
851
885
|
}
|
|
852
886
|
catch (error) {
|
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,21 +219,36 @@ 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."),
|
|
183
|
-
fields: z
|
|
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."),
|
|
225
|
+
fields: z
|
|
226
|
+
.array(z.string())
|
|
227
|
+
.optional()
|
|
228
|
+
.describe("Optional list of fields to include in the response. If not provided, all fields will be returned. Cannot be used together with the expand parameter."),
|
|
184
229
|
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
230
|
expand: z
|
|
186
231
|
.enum(["all", "fields", "links", "none", "relations"])
|
|
187
|
-
.describe("Optional expand parameter to include additional details in the response.")
|
|
232
|
+
.describe("Optional expand parameter to include additional details in the response. Cannot be used together with the fields parameter.")
|
|
188
233
|
.optional()
|
|
189
|
-
.describe("Expand options include '
|
|
234
|
+
.describe("Expand options include 'All', 'Fields', 'Links', 'None', and 'Relations'. Relations can be used to get child workitems. Defaults to 'None'. Cannot be used together with the fields parameter."),
|
|
190
235
|
}, async ({ id, project, fields, asOf, expand }) => {
|
|
191
236
|
try {
|
|
192
237
|
const connection = await connectionProvider();
|
|
238
|
+
let resolvedProject = project;
|
|
239
|
+
if (!resolvedProject) {
|
|
240
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the work item from.");
|
|
241
|
+
if ("response" in result)
|
|
242
|
+
return result.response;
|
|
243
|
+
resolvedProject = result.resolved;
|
|
244
|
+
}
|
|
245
|
+
// The Azure DevOps API does not support using expand and fields together.
|
|
246
|
+
// When both are provided, prefer fields as it is the more specific selection.
|
|
247
|
+
if (fields && fields.length > 0 && expand != null) {
|
|
248
|
+
expand = "none";
|
|
249
|
+
}
|
|
193
250
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
194
|
-
const workItem = await workItemApi.getWorkItem(id, fields, asOf, expand,
|
|
251
|
+
const workItem = await workItemApi.getWorkItem(id, fields, asOf, expand, resolvedProject);
|
|
195
252
|
return {
|
|
196
253
|
content: [{ type: "text", text: JSON.stringify(workItem, null, 2) }],
|
|
197
254
|
};
|
|
@@ -204,15 +261,22 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
204
261
|
};
|
|
205
262
|
}
|
|
206
263
|
});
|
|
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."),
|
|
264
|
+
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.", {
|
|
265
|
+
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
266
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item to retrieve comments for."),
|
|
210
267
|
top: z.coerce.number().default(50).describe("Optional number of comments to retrieve. Defaults to all comments."),
|
|
211
268
|
}, async ({ project, workItemId, top }) => {
|
|
212
269
|
try {
|
|
213
270
|
const connection = await connectionProvider();
|
|
271
|
+
let resolvedProject = project;
|
|
272
|
+
if (!resolvedProject) {
|
|
273
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list work item comments for.");
|
|
274
|
+
if ("response" in result)
|
|
275
|
+
return result.response;
|
|
276
|
+
resolvedProject = result.resolved;
|
|
277
|
+
}
|
|
214
278
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
215
|
-
const comments = await workItemApi.getComments(
|
|
279
|
+
const comments = await workItemApi.getComments(resolvedProject, workItemId, top);
|
|
216
280
|
return {
|
|
217
281
|
content: [{ type: "text", text: JSON.stringify(comments, null, 2) }],
|
|
218
282
|
};
|
|
@@ -225,21 +289,28 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
225
289
|
};
|
|
226
290
|
}
|
|
227
291
|
});
|
|
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."),
|
|
292
|
+
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.", {
|
|
293
|
+
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
294
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item to add a comment to."),
|
|
231
295
|
comment: z.string().describe("The text of the comment to add to the work item."),
|
|
232
296
|
format: z.enum(["markdown", "html"]).optional().default("html"),
|
|
233
297
|
}, async ({ project, workItemId, comment, format }) => {
|
|
234
298
|
try {
|
|
235
299
|
const connection = await connectionProvider();
|
|
300
|
+
let resolvedProject = project;
|
|
301
|
+
if (!resolvedProject) {
|
|
302
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to add a work item comment in.");
|
|
303
|
+
if ("response" in result)
|
|
304
|
+
return result.response;
|
|
305
|
+
resolvedProject = result.resolved;
|
|
306
|
+
}
|
|
236
307
|
const orgUrl = connection.serverUrl;
|
|
237
308
|
const accessToken = await tokenProvider();
|
|
238
309
|
const body = {
|
|
239
310
|
text: comment,
|
|
240
311
|
};
|
|
241
312
|
const formatParameter = format === "markdown" ? 0 : 1;
|
|
242
|
-
const response = await fetch(`${orgUrl}/${encodeURIComponent(
|
|
313
|
+
const response = await fetch(`${orgUrl}/${encodeURIComponent(resolvedProject)}/_apis/wit/workItems/${workItemId}/comments?format=${formatParameter}&api-version=${markdownCommentsApiVersion}`, {
|
|
243
314
|
method: "POST",
|
|
244
315
|
headers: {
|
|
245
316
|
"Authorization": `Bearer ${accessToken}`,
|
|
@@ -264,8 +335,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
264
335
|
};
|
|
265
336
|
}
|
|
266
337
|
});
|
|
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."),
|
|
338
|
+
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.", {
|
|
339
|
+
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
340
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item."),
|
|
270
341
|
commentId: z.coerce.number().min(1).describe("The ID of the comment to update."),
|
|
271
342
|
text: z.string().describe("The updated comment text."),
|
|
@@ -273,11 +344,18 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
273
344
|
}, async ({ project, workItemId, commentId, text, format }) => {
|
|
274
345
|
try {
|
|
275
346
|
const connection = await connectionProvider();
|
|
347
|
+
let resolvedProject = project;
|
|
348
|
+
if (!resolvedProject) {
|
|
349
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to update the work item comment in.");
|
|
350
|
+
if ("response" in result)
|
|
351
|
+
return result.response;
|
|
352
|
+
resolvedProject = result.resolved;
|
|
353
|
+
}
|
|
276
354
|
const orgUrl = connection.serverUrl;
|
|
277
355
|
const accessToken = await tokenProvider();
|
|
278
356
|
const body = { text };
|
|
279
357
|
const formatParameter = format === "markdown" ? 0 : 1;
|
|
280
|
-
const response = await fetch(`${orgUrl}/${encodeURIComponent(
|
|
358
|
+
const response = await fetch(`${orgUrl}/${encodeURIComponent(resolvedProject)}/_apis/wit/workItems/${workItemId}/comments/${commentId}?format=${formatParameter}&api-version=${markdownCommentsApiVersion}`, {
|
|
281
359
|
method: "PATCH",
|
|
282
360
|
headers: {
|
|
283
361
|
"Authorization": `Bearer ${accessToken}`,
|
|
@@ -302,8 +380,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
302
380
|
};
|
|
303
381
|
}
|
|
304
382
|
});
|
|
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."),
|
|
383
|
+
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.", {
|
|
384
|
+
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
385
|
workItemId: z.coerce.number().min(1).describe("The ID of the work item to retrieve revisions for."),
|
|
308
386
|
top: z.coerce.number().default(50).describe("Optional number of revisions to retrieve. If not provided, all revisions will be returned."),
|
|
309
387
|
skip: z.coerce.number().optional().describe("Optional number of revisions to skip for pagination. Defaults to 0."),
|
|
@@ -315,8 +393,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
315
393
|
}, async ({ project, workItemId, top, skip, expand }) => {
|
|
316
394
|
try {
|
|
317
395
|
const connection = await connectionProvider();
|
|
396
|
+
let resolvedProject = project;
|
|
397
|
+
if (!resolvedProject) {
|
|
398
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list work item revisions for.");
|
|
399
|
+
if ("response" in result)
|
|
400
|
+
return result.response;
|
|
401
|
+
resolvedProject = result.resolved;
|
|
402
|
+
}
|
|
318
403
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
319
|
-
const revisions = await workItemApi.getRevisions(workItemId, top, skip, safeEnumConvert(WorkItemExpand, expand),
|
|
404
|
+
const revisions = await workItemApi.getRevisions(workItemId, top, skip, safeEnumConvert(WorkItemExpand, expand), resolvedProject);
|
|
320
405
|
// Dynamically clean up identity objects in revision fields
|
|
321
406
|
// Identity objects typically have properties like displayName, url, _links, id, uniqueName, imageUrl, descriptor
|
|
322
407
|
if (revisions && Array.isArray(revisions)) {
|
|
@@ -354,9 +439,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
354
439
|
};
|
|
355
440
|
}
|
|
356
441
|
});
|
|
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.", {
|
|
442
|
+
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
443
|
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."),
|
|
444
|
+
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
445
|
workItemType: z.string().describe("The type of the child work item to create."),
|
|
361
446
|
items: z.array(z.object({
|
|
362
447
|
title: z.string().describe("The title of the child work item."),
|
|
@@ -368,6 +453,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
368
453
|
}, async ({ parentId, project, workItemType, items }) => {
|
|
369
454
|
try {
|
|
370
455
|
const connection = await connectionProvider();
|
|
456
|
+
let resolvedProject = project;
|
|
457
|
+
if (!resolvedProject) {
|
|
458
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to create child work items in.");
|
|
459
|
+
if ("response" in result)
|
|
460
|
+
return result.response;
|
|
461
|
+
resolvedProject = result.resolved;
|
|
462
|
+
}
|
|
371
463
|
const orgUrl = connection.serverUrl;
|
|
372
464
|
const accessToken = await tokenProvider();
|
|
373
465
|
if (items.length > 50) {
|
|
@@ -404,7 +496,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
404
496
|
path: "/relations/-",
|
|
405
497
|
value: {
|
|
406
498
|
rel: "System.LinkTypes.Hierarchy-Reverse",
|
|
407
|
-
url: `${connection.serverUrl}/${
|
|
499
|
+
url: `${connection.serverUrl}/${resolvedProject}/_apis/wit/workItems/${parentId}`,
|
|
408
500
|
},
|
|
409
501
|
},
|
|
410
502
|
];
|
|
@@ -436,7 +528,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
436
528
|
}
|
|
437
529
|
return {
|
|
438
530
|
method: "PATCH",
|
|
439
|
-
uri: `/${encodeURIComponent(
|
|
531
|
+
uri: `/${encodeURIComponent(resolvedProject)}/_apis/wit/workitems/$${encodeURIComponent(workItemType)}?api-version=${batchApiVersion}`,
|
|
440
532
|
headers: {
|
|
441
533
|
"Content-Type": "application/json-patch+json",
|
|
442
534
|
},
|
|
@@ -523,16 +615,23 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
523
615
|
};
|
|
524
616
|
}
|
|
525
617
|
});
|
|
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."),
|
|
618
|
+
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.", {
|
|
619
|
+
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
620
|
team: z.string().optional().describe("The name or ID of the Azure DevOps team. If not provided, the default team will be used."),
|
|
529
621
|
iterationId: z.string().describe("The ID of the iteration to retrieve work items for."),
|
|
530
622
|
}, async ({ project, team, iterationId }) => {
|
|
531
623
|
try {
|
|
532
624
|
const connection = await connectionProvider();
|
|
625
|
+
let resolvedProject = project;
|
|
626
|
+
if (!resolvedProject) {
|
|
627
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve work items for iteration.");
|
|
628
|
+
if ("response" in result)
|
|
629
|
+
return result.response;
|
|
630
|
+
resolvedProject = result.resolved;
|
|
631
|
+
}
|
|
533
632
|
const workApi = await connection.getWorkApi();
|
|
534
633
|
//get the work items for the current iteration
|
|
535
|
-
const workItems = await workApi.getIterationWorkItems({ project, team }, iterationId);
|
|
634
|
+
const workItems = await workApi.getIterationWorkItems({ project: resolvedProject, team }, iterationId);
|
|
536
635
|
return {
|
|
537
636
|
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
538
637
|
};
|
|
@@ -581,14 +680,21 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
581
680
|
};
|
|
582
681
|
}
|
|
583
682
|
});
|
|
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."),
|
|
683
|
+
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.", {
|
|
684
|
+
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
685
|
workItemType: z.string().describe("The name of the work item type to retrieve."),
|
|
587
686
|
}, async ({ project, workItemType }) => {
|
|
588
687
|
try {
|
|
589
688
|
const connection = await connectionProvider();
|
|
689
|
+
let resolvedProject = project;
|
|
690
|
+
if (!resolvedProject) {
|
|
691
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the work item type from.");
|
|
692
|
+
if ("response" in result)
|
|
693
|
+
return result.response;
|
|
694
|
+
resolvedProject = result.resolved;
|
|
695
|
+
}
|
|
590
696
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
591
|
-
const workItemTypeInfo = await workItemApi.getWorkItemType(
|
|
697
|
+
const workItemTypeInfo = await workItemApi.getWorkItemType(resolvedProject, workItemType);
|
|
592
698
|
return {
|
|
593
699
|
content: [{ type: "text", text: JSON.stringify(workItemTypeInfo, null, 2) }],
|
|
594
700
|
};
|
|
@@ -601,8 +707,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
601
707
|
};
|
|
602
708
|
}
|
|
603
709
|
});
|
|
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."),
|
|
710
|
+
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.", {
|
|
711
|
+
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
712
|
workItemType: z.string().describe("The type of work item to create, e.g., 'Task', 'Bug', etc."),
|
|
607
713
|
fields: z
|
|
608
714
|
.array(z.object({
|
|
@@ -614,6 +720,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
614
720
|
}, async ({ project, workItemType, fields }) => {
|
|
615
721
|
try {
|
|
616
722
|
const connection = await connectionProvider();
|
|
723
|
+
let resolvedProject = project;
|
|
724
|
+
if (!resolvedProject) {
|
|
725
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to create the work item in.");
|
|
726
|
+
if ("response" in result)
|
|
727
|
+
return result.response;
|
|
728
|
+
resolvedProject = result.resolved;
|
|
729
|
+
}
|
|
617
730
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
618
731
|
const document = fields.map(({ name, value, format }) => ({
|
|
619
732
|
op: "add",
|
|
@@ -632,7 +745,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
632
745
|
});
|
|
633
746
|
}
|
|
634
747
|
});
|
|
635
|
-
const newWorkItem = await workItemApi.createWorkItem(null, document,
|
|
748
|
+
const newWorkItem = await workItemApi.createWorkItem(null, document, resolvedProject, workItemType);
|
|
636
749
|
if (!newWorkItem) {
|
|
637
750
|
return { content: [{ type: "text", text: "Work item was not created" }], isError: true };
|
|
638
751
|
}
|
|
@@ -648,8 +761,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
648
761
|
};
|
|
649
762
|
}
|
|
650
763
|
});
|
|
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."),
|
|
764
|
+
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.", {
|
|
765
|
+
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
766
|
query: z.string().describe("The ID or path of the query to retrieve."),
|
|
654
767
|
expand: z
|
|
655
768
|
.enum(getEnumKeys(QueryExpand))
|
|
@@ -661,8 +774,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
661
774
|
}, async ({ project, query, expand, depth, includeDeleted, useIsoDateFormat }) => {
|
|
662
775
|
try {
|
|
663
776
|
const connection = await connectionProvider();
|
|
777
|
+
let resolvedProject = project;
|
|
778
|
+
if (!resolvedProject) {
|
|
779
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the query from.");
|
|
780
|
+
if ("response" in result)
|
|
781
|
+
return result.response;
|
|
782
|
+
resolvedProject = result.resolved;
|
|
783
|
+
}
|
|
664
784
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
665
|
-
const queryDetails = await workItemApi.getQuery(
|
|
785
|
+
const queryDetails = await workItemApi.getQuery(resolvedProject, query, safeEnumConvert(QueryExpand, expand), depth, includeDeleted, useIsoDateFormat);
|
|
666
786
|
return {
|
|
667
787
|
content: [{ type: "text", text: JSON.stringify(queryDetails, null, 2) }],
|
|
668
788
|
};
|
|
@@ -776,8 +896,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
776
896
|
};
|
|
777
897
|
}
|
|
778
898
|
});
|
|
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."),
|
|
899
|
+
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.", {
|
|
900
|
+
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
901
|
updates: z
|
|
782
902
|
.array(z.object({
|
|
783
903
|
id: z.coerce.number().min(1).describe("The ID of the work item to update."),
|
|
@@ -792,6 +912,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
792
912
|
}, async ({ project, updates }) => {
|
|
793
913
|
try {
|
|
794
914
|
const connection = await connectionProvider();
|
|
915
|
+
let resolvedProject = project;
|
|
916
|
+
if (!resolvedProject) {
|
|
917
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to link work items in.");
|
|
918
|
+
if ("response" in result)
|
|
919
|
+
return result.response;
|
|
920
|
+
resolvedProject = result.resolved;
|
|
921
|
+
}
|
|
795
922
|
const orgUrl = connection.serverUrl;
|
|
796
923
|
const accessToken = await tokenProvider();
|
|
797
924
|
// Extract unique IDs from the updates array
|
|
@@ -809,7 +936,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
809
936
|
path: "/relations/-",
|
|
810
937
|
value: {
|
|
811
938
|
rel: `${getLinkTypeFromName(type)}`,
|
|
812
|
-
url: `${orgUrl}/${
|
|
939
|
+
url: `${orgUrl}/${resolvedProject}/_apis/wit/workItems/${linkToId}`,
|
|
813
940
|
attributes: {
|
|
814
941
|
comment: comment || "",
|
|
815
942
|
},
|
|
@@ -841,8 +968,8 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
841
968
|
};
|
|
842
969
|
}
|
|
843
970
|
});
|
|
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."),
|
|
971
|
+
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.", {
|
|
972
|
+
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
973
|
id: z.coerce.number().min(1).describe("The ID of the work item to remove the links from."),
|
|
847
974
|
type: z
|
|
848
975
|
.enum(["parent", "child", "duplicate", "duplicate of", "related", "successor", "predecessor", "tested by", "tests", "affects", "affected by", "artifact"])
|
|
@@ -852,8 +979,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
852
979
|
}, async ({ project, id, type, url }) => {
|
|
853
980
|
try {
|
|
854
981
|
const connection = await connectionProvider();
|
|
982
|
+
let resolvedProject = project;
|
|
983
|
+
if (!resolvedProject) {
|
|
984
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to unlink work items in.");
|
|
985
|
+
if ("response" in result)
|
|
986
|
+
return result.response;
|
|
987
|
+
resolvedProject = result.resolved;
|
|
988
|
+
}
|
|
855
989
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
856
|
-
const workItem = await workItemApi.getWorkItem(id, undefined, undefined, WorkItemExpand.Relations,
|
|
990
|
+
const workItem = await workItemApi.getWorkItem(id, undefined, undefined, WorkItemExpand.Relations, resolvedProject);
|
|
857
991
|
const relations = workItem.relations ?? [];
|
|
858
992
|
const linkType = getLinkTypeFromName(type);
|
|
859
993
|
let relationIndexes = [];
|
|
@@ -879,7 +1013,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
879
1013
|
op: "remove",
|
|
880
1014
|
path: `/relations/${idx}`,
|
|
881
1015
|
}));
|
|
882
|
-
const updatedWorkItem = await workItemApi.updateWorkItem(null, apiUpdates, id,
|
|
1016
|
+
const updatedWorkItem = await workItemApi.updateWorkItem(null, apiUpdates, id, resolvedProject);
|
|
883
1017
|
return {
|
|
884
1018
|
content: [
|
|
885
1019
|
{
|
|
@@ -905,9 +1039,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
905
1039
|
};
|
|
906
1040
|
}
|
|
907
1041
|
});
|
|
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.", {
|
|
1042
|
+
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
1043
|
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."),
|
|
1044
|
+
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
1045
|
// Option 1: Provide full URI directly
|
|
912
1046
|
artifactUri: z.string().optional().describe("The complete VSTFS URI of the artifact to link. If provided, individual component parameters are ignored."),
|
|
913
1047
|
// Option 2: Provide individual components to build URI automatically based on linkType
|
|
@@ -940,6 +1074,13 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
940
1074
|
}, async ({ workItemId, project, artifactUri, projectId, repositoryId, branchName, commitId, pullRequestId, buildId, linkType, comment }) => {
|
|
941
1075
|
try {
|
|
942
1076
|
const connection = await connectionProvider();
|
|
1077
|
+
let resolvedProject = project;
|
|
1078
|
+
if (!resolvedProject) {
|
|
1079
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to add the artifact link in.");
|
|
1080
|
+
if ("response" in result)
|
|
1081
|
+
return result.response;
|
|
1082
|
+
resolvedProject = result.resolved;
|
|
1083
|
+
}
|
|
943
1084
|
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
944
1085
|
let finalArtifactUri;
|
|
945
1086
|
if (artifactUri) {
|
|
@@ -1010,7 +1151,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
1010
1151
|
},
|
|
1011
1152
|
];
|
|
1012
1153
|
// Use the WorkItem API to update the work item with the new relation
|
|
1013
|
-
const workItem = await workItemTrackingApi.updateWorkItem({}, patchDocument, workItemId,
|
|
1154
|
+
const workItem = await workItemTrackingApi.updateWorkItem({}, patchDocument, workItemId, resolvedProject);
|
|
1014
1155
|
if (!workItem) {
|
|
1015
1156
|
return { content: [{ type: "text", text: "Work item update failed" }], isError: true };
|
|
1016
1157
|
}
|
|
@@ -1066,15 +1207,22 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
1066
1207
|
};
|
|
1067
1208
|
}
|
|
1068
1209
|
});
|
|
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."),
|
|
1210
|
+
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.", {
|
|
1211
|
+
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
1212
|
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
1213
|
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
1214
|
}, async ({ project, attachmentId, fileName }) => {
|
|
1074
1215
|
try {
|
|
1075
1216
|
const connection = await connectionProvider();
|
|
1217
|
+
let resolvedProject = project;
|
|
1218
|
+
if (!resolvedProject) {
|
|
1219
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to retrieve the work item attachment from.");
|
|
1220
|
+
if ("response" in result)
|
|
1221
|
+
return result.response;
|
|
1222
|
+
resolvedProject = result.resolved;
|
|
1223
|
+
}
|
|
1076
1224
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
1077
|
-
const stream = await workItemApi.getAttachmentContent(attachmentId, fileName,
|
|
1225
|
+
const stream = await workItemApi.getAttachmentContent(attachmentId, fileName, resolvedProject);
|
|
1078
1226
|
const chunks = [];
|
|
1079
1227
|
await new Promise((resolve, reject) => {
|
|
1080
1228
|
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.20260414";
|