@azure-devops/mcp 1.0.0 → 1.1.0
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/LICENSE.md +21 -21
- package/README.md +314 -293
- package/dist/index.js +17 -12
- package/dist/prompts.js +35 -10
- package/dist/tools/auth.js +1 -1
- package/dist/tools/builds.js +63 -9
- package/dist/tools/core.js +12 -6
- package/dist/tools/releases.js +13 -7
- package/dist/tools/repos.js +161 -31
- package/dist/tools/search.js +53 -47
- package/dist/tools/testplans.js +12 -18
- package/dist/tools/wiki.js +79 -31
- package/dist/tools/work.js +16 -12
- package/dist/tools/workitems.js +158 -89
- package/dist/tools.js +3 -3
- package/dist/useragent.js +18 -0
- package/dist/utils.js +0 -2
- package/dist/version.js +1 -1
- package/package.json +62 -54
package/dist/tools/workitems.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { batchApiVersion
|
|
4
|
+
import { batchApiVersion } from "../utils.js";
|
|
5
5
|
const WORKITEM_TOOLS = {
|
|
6
6
|
my_work_items: "wit_my_work_items",
|
|
7
7
|
list_backlogs: "wit_list_backlogs",
|
|
@@ -13,14 +13,14 @@ const WORKITEM_TOOLS = {
|
|
|
13
13
|
list_work_item_comments: "wit_list_work_item_comments",
|
|
14
14
|
get_work_items_for_iteration: "wit_get_work_items_for_iteration",
|
|
15
15
|
add_work_item_comment: "wit_add_work_item_comment",
|
|
16
|
-
|
|
16
|
+
add_child_work_items: "wit_add_child_work_items",
|
|
17
17
|
link_work_item_to_pull_request: "wit_link_work_item_to_pull_request",
|
|
18
18
|
get_work_item_type: "wit_get_work_item_type",
|
|
19
19
|
get_query: "wit_get_query",
|
|
20
20
|
get_query_results_by_id: "wit_get_query_results_by_id",
|
|
21
21
|
update_work_items_batch: "wit_update_work_items_batch",
|
|
22
22
|
close_and_link_workitem_duplicates: "wit_close_and_link_workitem_duplicates",
|
|
23
|
-
work_items_link: "wit_work_items_link"
|
|
23
|
+
work_items_link: "wit_work_items_link",
|
|
24
24
|
};
|
|
25
25
|
function getLinkTypeFromName(name) {
|
|
26
26
|
switch (name.toLowerCase()) {
|
|
@@ -46,10 +46,10 @@ function getLinkTypeFromName(name) {
|
|
|
46
46
|
throw new Error(`Unknown link type: ${name}`);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
49
|
+
function configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
50
50
|
server.tool(WORKITEM_TOOLS.list_backlogs, "Revieve a list of backlogs for a given project and team.", {
|
|
51
51
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
52
|
-
team: z.string().describe("The name or ID of the Azure DevOps team.")
|
|
52
|
+
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
53
53
|
}, async ({ project, team }) => {
|
|
54
54
|
const connection = await connectionProvider();
|
|
55
55
|
const workApi = await connection.getWorkApi();
|
|
@@ -62,7 +62,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
62
62
|
server.tool(WORKITEM_TOOLS.list_backlog_work_items, "Retrieve a list of backlogs of for a given project, team, and backlog category", {
|
|
63
63
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
64
64
|
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
65
|
-
backlogId: z.string().describe("The ID of the backlog category to retrieve work items from.")
|
|
65
|
+
backlogId: z.string().describe("The ID of the backlog category to retrieve work items from."),
|
|
66
66
|
}, async ({ project, team, backlogId }) => {
|
|
67
67
|
const connection = await connectionProvider();
|
|
68
68
|
const workApi = await connection.getWorkApi();
|
|
@@ -87,7 +87,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
87
87
|
});
|
|
88
88
|
server.tool(WORKITEM_TOOLS.get_work_items_batch_by_ids, "Retrieve list of work items by IDs in batch.", {
|
|
89
89
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
90
|
-
ids: z.array(z.number()).describe("The IDs of the work items to retrieve.")
|
|
90
|
+
ids: z.array(z.number()).describe("The IDs of the work items to retrieve."),
|
|
91
91
|
}, async ({ project, ids }) => {
|
|
92
92
|
const connection = await connectionProvider();
|
|
93
93
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
@@ -101,11 +101,12 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
101
101
|
id: z.number().describe("The ID of the work item to retrieve."),
|
|
102
102
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
103
103
|
fields: z.array(z.string()).optional().describe("Optional list of fields to include in the response. If not provided, all fields will be returned."),
|
|
104
|
-
asOf: z.date().optional().describe("Optional date to retrieve the work item as of a specific time. If not provided, the current state will be returned."),
|
|
104
|
+
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."),
|
|
105
105
|
expand: z
|
|
106
106
|
.enum(["all", "fields", "links", "none", "relations"])
|
|
107
107
|
.describe("Optional expand parameter to include additional details in the response.")
|
|
108
|
-
.optional()
|
|
108
|
+
.optional()
|
|
109
|
+
.describe("Expand options include 'all', 'fields', 'links', 'none', and 'relations'. Defaults to 'none'."),
|
|
109
110
|
}, async ({ id, project, fields, asOf, expand }) => {
|
|
110
111
|
const connection = await connectionProvider();
|
|
111
112
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
@@ -117,7 +118,7 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
117
118
|
server.tool(WORKITEM_TOOLS.list_work_item_comments, "Retrieve list of comments for a work item by ID.", {
|
|
118
119
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
119
120
|
workItemId: z.number().describe("The ID of the work item to retrieve comments for."),
|
|
120
|
-
top: z.number().default(50).describe("Optional number of comments to retrieve. Defaults to all comments.")
|
|
121
|
+
top: z.number().default(50).describe("Optional number of comments to retrieve. Defaults to all comments."),
|
|
121
122
|
}, async ({ project, workItemId, top }) => {
|
|
122
123
|
const connection = await connectionProvider();
|
|
123
124
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
@@ -129,69 +130,128 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
129
130
|
server.tool(WORKITEM_TOOLS.add_work_item_comment, "Add comment to a work item by ID.", {
|
|
130
131
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
131
132
|
workItemId: z.number().describe("The ID of the work item to add a comment to."),
|
|
132
|
-
comment: z.string().describe("The text of the comment to add to the work item.")
|
|
133
|
+
comment: z.string().describe("The text of the comment to add to the work item."),
|
|
133
134
|
}, async ({ project, workItemId, comment }) => {
|
|
134
135
|
const connection = await connectionProvider();
|
|
135
136
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
136
137
|
const commentCreate = { text: comment };
|
|
137
138
|
const commentResponse = await workItemApi.addComment(commentCreate, project, workItemId);
|
|
138
139
|
return {
|
|
139
|
-
content: [
|
|
140
|
-
{ type: "text", text: JSON.stringify(commentResponse, null, 2) },
|
|
141
|
-
],
|
|
140
|
+
content: [{ type: "text", text: JSON.stringify(commentResponse, null, 2) }],
|
|
142
141
|
};
|
|
143
142
|
});
|
|
144
|
-
server.tool(WORKITEM_TOOLS.
|
|
143
|
+
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.", {
|
|
145
144
|
parentId: z.number().describe("The ID of the parent work item to create a child work item under."),
|
|
146
145
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
147
146
|
workItemType: z.string().describe("The type of the child work item to create."),
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
{
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
147
|
+
items: z.array(z.object({
|
|
148
|
+
title: z.string().describe("The title of the child work item."),
|
|
149
|
+
description: z.string().describe("The description of the child work item."),
|
|
150
|
+
format: z.enum(["Markdown", "Html"]).default("Html").describe("Format for the description on the child work item, e.g., 'Markdown', 'Html'. Defaults to 'Html'."),
|
|
151
|
+
areaPath: z.string().optional().describe("Optional area path for the child work item."),
|
|
152
|
+
iterationPath: z.string().optional().describe("Optional iteration path for the child work item."),
|
|
153
|
+
})),
|
|
154
|
+
}, async ({ parentId, project, workItemType, items }) => {
|
|
155
|
+
try {
|
|
156
|
+
const connection = await connectionProvider();
|
|
157
|
+
const orgUrl = connection.serverUrl;
|
|
158
|
+
const accessToken = await tokenProvider();
|
|
159
|
+
if (items.length > 50) {
|
|
160
|
+
return {
|
|
161
|
+
content: [{ type: "text", text: `A maximum of 50 child work items can be created in a single call.` }],
|
|
162
|
+
isError: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
const body = items.map((item, x) => {
|
|
166
|
+
const ops = [
|
|
167
|
+
{
|
|
168
|
+
op: "add",
|
|
169
|
+
path: "/id",
|
|
170
|
+
value: `-${x + 1}`,
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
op: "add",
|
|
174
|
+
path: "/fields/System.Title",
|
|
175
|
+
value: item.title,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
op: "add",
|
|
179
|
+
path: "/fields/System.Description",
|
|
180
|
+
value: item.description,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
op: "add",
|
|
184
|
+
path: "/fields/Microsoft.VSTS.TCM.ReproSteps",
|
|
185
|
+
value: item.description,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
op: "add",
|
|
189
|
+
path: "/relations/-",
|
|
190
|
+
value: {
|
|
191
|
+
rel: "System.LinkTypes.Hierarchy-Reverse",
|
|
192
|
+
url: `${connection.serverUrl}/${project}/_apis/wit/workItems/${parentId}`,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
];
|
|
196
|
+
if (item.areaPath && item.areaPath.trim().length > 0) {
|
|
197
|
+
ops.push({
|
|
198
|
+
op: "add",
|
|
199
|
+
path: "/fields/System.AreaPath",
|
|
200
|
+
value: item.areaPath,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
if (item.format && item.format === "Markdown") {
|
|
204
|
+
ops.push({
|
|
205
|
+
op: "add",
|
|
206
|
+
path: "/multilineFieldsFormat/System.Description",
|
|
207
|
+
value: item.format,
|
|
208
|
+
});
|
|
209
|
+
ops.push({
|
|
210
|
+
op: "add",
|
|
211
|
+
path: "/multilineFieldsFormat/Microsoft.VSTS.TCM.ReproSteps",
|
|
212
|
+
value: item.format,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
if (item.iterationPath && item.iterationPath.trim().length > 0) {
|
|
216
|
+
ops.push({
|
|
217
|
+
op: "add",
|
|
218
|
+
path: "/fields/System.IterationPath",
|
|
219
|
+
value: item.iterationPath,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
method: "PATCH",
|
|
224
|
+
uri: `/${project}/_apis/wit/workitems/$${workItemType}?api-version=${batchApiVersion}`,
|
|
225
|
+
headers: {
|
|
226
|
+
"Content-Type": "application/json-patch+json",
|
|
227
|
+
},
|
|
228
|
+
body: ops,
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
232
|
+
method: "PATCH",
|
|
233
|
+
headers: {
|
|
234
|
+
"Authorization": `Bearer ${accessToken.token}`,
|
|
235
|
+
"Content-Type": "application/json",
|
|
236
|
+
"User-Agent": userAgentProvider(),
|
|
172
237
|
},
|
|
173
|
-
|
|
174
|
-
];
|
|
175
|
-
if (areaPath && areaPath.trim().length > 0) {
|
|
176
|
-
document.push({
|
|
177
|
-
op: "add",
|
|
178
|
-
path: "/fields/System.AreaPath",
|
|
179
|
-
value: areaPath,
|
|
238
|
+
body: JSON.stringify(body),
|
|
180
239
|
});
|
|
240
|
+
if (!response.ok) {
|
|
241
|
+
throw new Error(`Failed to update work items in batch: ${response.statusText}`);
|
|
242
|
+
}
|
|
243
|
+
const result = await response.json();
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
246
|
+
};
|
|
181
247
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text", text: `Error creating child work items: ${errorMessage}` }],
|
|
252
|
+
isError: true,
|
|
253
|
+
};
|
|
188
254
|
}
|
|
189
|
-
const childWorkItem = await workItemApi.createWorkItem(null, document, project, workItemType);
|
|
190
|
-
return {
|
|
191
|
-
content: [
|
|
192
|
-
{ type: "text", text: JSON.stringify(childWorkItem, null, 2) },
|
|
193
|
-
],
|
|
194
|
-
};
|
|
195
255
|
});
|
|
196
256
|
server.tool(WORKITEM_TOOLS.link_work_item_to_pull_request, "Link a single work item to an existing pull request.", {
|
|
197
257
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -239,10 +299,10 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
239
299
|
};
|
|
240
300
|
}
|
|
241
301
|
catch (error) {
|
|
242
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
302
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
243
303
|
return {
|
|
244
304
|
content: [{ type: "text", text: `Error linking work item to pull request: ${errorMessage}` }],
|
|
245
|
-
isError: true
|
|
305
|
+
isError: true,
|
|
246
306
|
};
|
|
247
307
|
}
|
|
248
308
|
});
|
|
@@ -261,19 +321,19 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
261
321
|
});
|
|
262
322
|
server.tool(WORKITEM_TOOLS.update_work_item, "Update a work item by ID with specified fields.", {
|
|
263
323
|
id: z.number().describe("The ID of the work item to update."),
|
|
264
|
-
updates: z
|
|
324
|
+
updates: z
|
|
325
|
+
.array(z.object({
|
|
265
326
|
op: z.enum(["add", "replace", "remove"]).default("add").describe("The operation to perform on the field."),
|
|
266
327
|
path: z.string().describe("The path of the field to update, e.g., '/fields/System.Title'."),
|
|
267
328
|
value: z.string().describe("The new value for the field. This is required for 'add' and 'replace' operations, and should be omitted for 'remove' operations."),
|
|
268
|
-
}))
|
|
329
|
+
}))
|
|
330
|
+
.describe("An array of field updates to apply to the work item."),
|
|
269
331
|
}, async ({ id, updates }) => {
|
|
270
332
|
const connection = await connectionProvider();
|
|
271
333
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
272
334
|
const updatedWorkItem = await workItemApi.updateWorkItem(null, updates, id);
|
|
273
335
|
return {
|
|
274
|
-
content: [
|
|
275
|
-
{ type: "text", text: JSON.stringify(updatedWorkItem, null, 2) },
|
|
276
|
-
],
|
|
336
|
+
content: [{ type: "text", text: JSON.stringify(updatedWorkItem, null, 2) }],
|
|
277
337
|
};
|
|
278
338
|
});
|
|
279
339
|
server.tool(WORKITEM_TOOLS.get_work_item_type, "Get a specific work item type.", {
|
|
@@ -284,15 +344,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
284
344
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
285
345
|
const workItemTypeInfo = await workItemApi.getWorkItemType(project, workItemType);
|
|
286
346
|
return {
|
|
287
|
-
content: [
|
|
288
|
-
{ type: "text", text: JSON.stringify(workItemTypeInfo, null, 2) },
|
|
289
|
-
],
|
|
347
|
+
content: [{ type: "text", text: JSON.stringify(workItemTypeInfo, null, 2) }],
|
|
290
348
|
};
|
|
291
349
|
});
|
|
292
350
|
server.tool(WORKITEM_TOOLS.create_work_item, "Create a new work item in a specified project and work item type.", {
|
|
293
351
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
294
352
|
workItemType: z.string().describe("The type of work item to create, e.g., 'Task', 'Bug', etc."),
|
|
295
|
-
fields: z
|
|
353
|
+
fields: z
|
|
354
|
+
.record(z.string(), z.string())
|
|
355
|
+
.describe("A record of field names and values to set on the new work item. Each key is a field name, and each value is the corresponding value to set for that field."),
|
|
296
356
|
}, async ({ project, workItemType, fields }) => {
|
|
297
357
|
try {
|
|
298
358
|
const connection = await connectionProvider();
|
|
@@ -311,10 +371,10 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
311
371
|
};
|
|
312
372
|
}
|
|
313
373
|
catch (error) {
|
|
314
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
374
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
315
375
|
return {
|
|
316
376
|
content: [{ type: "text", text: `Error creating work item: ${errorMessage}` }],
|
|
317
|
-
isError: true
|
|
377
|
+
isError: true,
|
|
318
378
|
};
|
|
319
379
|
}
|
|
320
380
|
});
|
|
@@ -325,14 +385,12 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
325
385
|
depth: z.number().default(0).describe("Optional depth parameter to specify how deep to expand the query. Defaults to 0."),
|
|
326
386
|
includeDeleted: z.boolean().default(false).describe("Whether to include deleted items in the query results. Defaults to false."),
|
|
327
387
|
useIsoDateFormat: z.boolean().default(false).describe("Whether to use ISO date format in the response. Defaults to false."),
|
|
328
|
-
}, async ({ project, query, expand, depth, includeDeleted, useIsoDateFormat
|
|
388
|
+
}, async ({ project, query, expand, depth, includeDeleted, useIsoDateFormat }) => {
|
|
329
389
|
const connection = await connectionProvider();
|
|
330
390
|
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
331
391
|
const queryDetails = await workItemApi.getQuery(project, query, expand, depth, includeDeleted, useIsoDateFormat);
|
|
332
392
|
return {
|
|
333
|
-
content: [
|
|
334
|
-
{ type: "text", text: JSON.stringify(queryDetails, null, 2) },
|
|
335
|
-
],
|
|
393
|
+
content: [{ type: "text", text: JSON.stringify(queryDetails, null, 2) }],
|
|
336
394
|
};
|
|
337
395
|
});
|
|
338
396
|
server.tool(WORKITEM_TOOLS.get_query_results_by_id, "Retrieve the results of a work item query given the query ID.", {
|
|
@@ -351,12 +409,14 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
351
409
|
};
|
|
352
410
|
});
|
|
353
411
|
server.tool(WORKITEM_TOOLS.update_work_items_batch, "Update work items in batch", {
|
|
354
|
-
updates: z
|
|
412
|
+
updates: z
|
|
413
|
+
.array(z.object({
|
|
355
414
|
op: z.enum(["add", "replace", "remove"]).default("add").describe("The operation to perform on the field."),
|
|
356
415
|
id: z.number().describe("The ID of the work item to update."),
|
|
357
416
|
path: z.string().describe("The path of the field to update, e.g., '/fields/System.Title'."),
|
|
358
417
|
value: z.string().describe("The new value for the field. This is required for 'add' and 'replace' operations, and should be omitted for 'remove' operations."),
|
|
359
|
-
}))
|
|
418
|
+
}))
|
|
419
|
+
.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)."),
|
|
360
420
|
}, async ({ updates }) => {
|
|
361
421
|
const connection = await connectionProvider();
|
|
362
422
|
const orgUrl = connection.serverUrl;
|
|
@@ -369,7 +429,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
369
429
|
headers: {
|
|
370
430
|
"Content-Type": "application/json-patch+json",
|
|
371
431
|
},
|
|
372
|
-
body: updates
|
|
432
|
+
body: updates
|
|
433
|
+
.filter((update) => update.id === id)
|
|
434
|
+
.map(({ op, path, value }) => ({
|
|
373
435
|
op: op,
|
|
374
436
|
path: path,
|
|
375
437
|
value: value,
|
|
@@ -378,9 +440,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
378
440
|
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
379
441
|
method: "PATCH",
|
|
380
442
|
headers: {
|
|
381
|
-
Authorization: `Bearer ${accessToken.token}`,
|
|
443
|
+
"Authorization": `Bearer ${accessToken.token}`,
|
|
382
444
|
"Content-Type": "application/json",
|
|
383
|
-
"User-Agent":
|
|
445
|
+
"User-Agent": userAgentProvider(),
|
|
384
446
|
},
|
|
385
447
|
body: JSON.stringify(body),
|
|
386
448
|
});
|
|
@@ -394,12 +456,17 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
394
456
|
});
|
|
395
457
|
server.tool(WORKITEM_TOOLS.work_items_link, "Link work items together in batch.", {
|
|
396
458
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
397
|
-
updates: z
|
|
459
|
+
updates: z
|
|
460
|
+
.array(z.object({
|
|
398
461
|
id: z.number().describe("The ID of the work item to update."),
|
|
399
462
|
linkToId: z.number().describe("The ID of the work item to link to."),
|
|
400
|
-
type: z
|
|
463
|
+
type: z
|
|
464
|
+
.enum(["parent", "child", "duplicate", "duplicate of", "related", "successor", "predecessor", "tested by", "tests"])
|
|
465
|
+
.default("related")
|
|
466
|
+
.describe("Type of link to create between the work items. Options include 'parent', 'child', 'duplicate', 'duplicate of', 'related', 'successor', 'predecessor', 'tested by', 'tests', 'referenced by', and 'references'. Defaults to 'related'."),
|
|
401
467
|
comment: z.string().optional().describe("Optional comment to include with the link. This can be used to provide additional context for the link being created."),
|
|
402
|
-
}))
|
|
468
|
+
}))
|
|
469
|
+
.describe(""),
|
|
403
470
|
}, async ({ project, updates }) => {
|
|
404
471
|
const connection = await connectionProvider();
|
|
405
472
|
const orgUrl = connection.serverUrl;
|
|
@@ -412,7 +479,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
412
479
|
headers: {
|
|
413
480
|
"Content-Type": "application/json-patch+json",
|
|
414
481
|
},
|
|
415
|
-
body: updates
|
|
482
|
+
body: updates
|
|
483
|
+
.filter((update) => update.id === id)
|
|
484
|
+
.map(({ linkToId, type, comment }) => ({
|
|
416
485
|
op: "add",
|
|
417
486
|
path: "/relations/-",
|
|
418
487
|
value: {
|
|
@@ -421,15 +490,15 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
421
490
|
attributes: {
|
|
422
491
|
comment: comment || "",
|
|
423
492
|
},
|
|
424
|
-
}
|
|
493
|
+
},
|
|
425
494
|
})),
|
|
426
495
|
}));
|
|
427
496
|
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
428
497
|
method: "PATCH",
|
|
429
498
|
headers: {
|
|
430
|
-
Authorization: `Bearer ${accessToken.token}`,
|
|
499
|
+
"Authorization": `Bearer ${accessToken.token}`,
|
|
431
500
|
"Content-Type": "application/json",
|
|
432
|
-
"User-Agent":
|
|
501
|
+
"User-Agent": userAgentProvider(),
|
|
433
502
|
},
|
|
434
503
|
body: JSON.stringify(body),
|
|
435
504
|
});
|
|
@@ -474,9 +543,9 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider) {
|
|
|
474
543
|
const response = await fetch(`${connection.serverUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
475
544
|
method: "PATCH",
|
|
476
545
|
headers: {
|
|
477
|
-
Authorization: `Bearer ${accessToken.token}`,
|
|
546
|
+
"Authorization": `Bearer ${accessToken.token}`,
|
|
478
547
|
"Content-Type": "application/json",
|
|
479
|
-
"User-Agent":
|
|
548
|
+
"User-Agent": userAgentProvider(),
|
|
480
549
|
},
|
|
481
550
|
body: JSON.stringify(body),
|
|
482
551
|
});
|
package/dist/tools.js
CHANGED
|
@@ -9,15 +9,15 @@ import { configureReleaseTools } from "./tools/releases.js";
|
|
|
9
9
|
import { configureWikiTools } from "./tools/wiki.js";
|
|
10
10
|
import { configureTestPlanTools } from "./tools/testplans.js";
|
|
11
11
|
import { configureSearchTools } from "./tools/search.js";
|
|
12
|
-
function configureAllTools(server, tokenProvider, connectionProvider) {
|
|
12
|
+
function configureAllTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
13
13
|
configureCoreTools(server, tokenProvider, connectionProvider);
|
|
14
14
|
configureWorkTools(server, tokenProvider, connectionProvider);
|
|
15
15
|
configureBuildTools(server, tokenProvider, connectionProvider);
|
|
16
16
|
configureRepoTools(server, tokenProvider, connectionProvider);
|
|
17
|
-
configureWorkItemTools(server, tokenProvider, connectionProvider);
|
|
17
|
+
configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider);
|
|
18
18
|
configureReleaseTools(server, tokenProvider, connectionProvider);
|
|
19
19
|
configureWikiTools(server, tokenProvider, connectionProvider);
|
|
20
20
|
configureTestPlanTools(server, tokenProvider, connectionProvider);
|
|
21
|
-
configureSearchTools(server, tokenProvider, connectionProvider);
|
|
21
|
+
configureSearchTools(server, tokenProvider, connectionProvider, userAgentProvider);
|
|
22
22
|
}
|
|
23
23
|
export { configureAllTools };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class UserAgentComposer {
|
|
2
|
+
_userAgent;
|
|
3
|
+
_mcpClientInfoAppended;
|
|
4
|
+
constructor(packageVersion) {
|
|
5
|
+
this._userAgent = `AzureDevOps.MCP/${packageVersion} (local)`;
|
|
6
|
+
this._mcpClientInfoAppended = false;
|
|
7
|
+
}
|
|
8
|
+
get userAgent() {
|
|
9
|
+
return this._userAgent;
|
|
10
|
+
}
|
|
11
|
+
appendMcpClientInfo(info) {
|
|
12
|
+
if (!this._mcpClientInfoAppended && info && info.name && info.version) {
|
|
13
|
+
this._userAgent += ` ${info.name}/${info.version}`;
|
|
14
|
+
this._mcpClientInfoAppended = true;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export { UserAgentComposer };
|
package/dist/utils.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import { packageVersion } from "./version.js";
|
|
4
3
|
export const apiVersion = "7.2-preview.1";
|
|
5
4
|
export const batchApiVersion = "5.0";
|
|
6
|
-
export const userAgent = `AzureDevOps.MCP/${packageVersion} (local)`;
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "1.
|
|
1
|
+
export const packageVersion = "1.1.0";
|
package/package.json
CHANGED
|
@@ -1,54 +1,62 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@azure-devops/mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MCP server for interacting with Azure DevOps",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Microsoft Corporation",
|
|
7
|
-
"homepage": "
|
|
8
|
-
"bugs": "
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
},
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@azure-devops/mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP server for interacting with Azure DevOps",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Microsoft Corporation",
|
|
7
|
+
"homepage": "https://github.com/microsoft/azure-devops-mcp",
|
|
8
|
+
"bugs": "https://github.com/microsoft/azure-devops-mcp/issues",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/microsoft/azure-devops-mcp.git"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"mcp-server-azuredevops": "dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"preinstall": "npm config set registry https://registry.npmjs.org/",
|
|
25
|
+
"prebuild": "node -p \"'export const packageVersion = ' + JSON.stringify(require('./package.json').version) + ';\\n'\" > src/version.ts && prettier --write src/version.ts",
|
|
26
|
+
"build": "tsc && shx chmod +x dist/*.js",
|
|
27
|
+
"prepare": "npm run build",
|
|
28
|
+
"watch": "tsc --watch",
|
|
29
|
+
"inspect": "npx @modelcontextprotocol/inspector node dist/index.js",
|
|
30
|
+
"start": "node -r tsconfig-paths/register dist/index.js",
|
|
31
|
+
"eslint": "eslint",
|
|
32
|
+
"eslint-fix": "eslint --fix",
|
|
33
|
+
"format": "prettier --write .",
|
|
34
|
+
"format-check": "prettier --check .",
|
|
35
|
+
"clean": "shx rm -rf dist",
|
|
36
|
+
"test": "jest"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@azure/identity": "^4.10.0",
|
|
40
|
+
"@modelcontextprotocol/sdk": "1.13.2",
|
|
41
|
+
"azure-devops-extension-api": "^4.252.0",
|
|
42
|
+
"azure-devops-extension-sdk": "^4.0.2",
|
|
43
|
+
"azure-devops-node-api": "^15.1.0",
|
|
44
|
+
"zod": "^3.25.63",
|
|
45
|
+
"zod-to-json-schema": "^3.24.5"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@modelcontextprotocol/inspector": "^0.15.0",
|
|
49
|
+
"@types/jest": "^30.0.0",
|
|
50
|
+
"@types/node": "^22",
|
|
51
|
+
"eslint-config-prettier": "10.1.5",
|
|
52
|
+
"eslint-plugin-header": "^3.1.1",
|
|
53
|
+
"jest": "^30.0.2",
|
|
54
|
+
"jest-extended": "^6.0.0",
|
|
55
|
+
"prettier": "3.6.2",
|
|
56
|
+
"shx": "^0.4.0",
|
|
57
|
+
"ts-jest": "^29.4.0",
|
|
58
|
+
"tsconfig-paths": "^4.2.0",
|
|
59
|
+
"typescript": "^5.8.3",
|
|
60
|
+
"typescript-eslint": "^8.32.1"
|
|
61
|
+
}
|
|
62
|
+
}
|