@azure-devops/mcp 2.2.2-nightly.20251112 → 2.2.2-nightly.20251113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/tools/pipelines.js +52 -4
- package/dist/tools/work-items.js +378 -243
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,6 +114,7 @@ Interact with these Azure DevOps services:
|
|
|
114
114
|
- **pipelines_get_build_changes**: Get the changes associated with a specific build.
|
|
115
115
|
- **pipelines_get_build_status**: Fetch the status of a specific build.
|
|
116
116
|
- **pipelines_update_build_stage**: Update the stage of a specific build.
|
|
117
|
+
- **pipelines_create_pipeline**: Creates a pipeline definition with YAML configuration for a given project.
|
|
117
118
|
- **pipelines_get_run**: Gets a run for a particular pipeline.
|
|
118
119
|
- **pipelines_list_runs**: Gets top 10000 runs for a particular pipeline.
|
|
119
120
|
- **pipelines_run_pipeline**: Starts a new run of a pipeline.
|
package/dist/tools/pipelines.js
CHANGED
|
@@ -4,6 +4,7 @@ import { apiVersion, getEnumKeys, safeEnumConvert } from "../utils.js";
|
|
|
4
4
|
import { BuildQueryOrder, DefinitionQueryOrder } from "azure-devops-node-api/interfaces/BuildInterfaces.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { StageUpdateType } from "azure-devops-node-api/interfaces/BuildInterfaces.js";
|
|
7
|
+
import { ConfigurationType, RepositoryType } from "azure-devops-node-api/interfaces/PipelinesInterfaces.js";
|
|
7
8
|
const PIPELINE_TOOLS = {
|
|
8
9
|
pipelines_get_builds: "pipelines_get_builds",
|
|
9
10
|
pipelines_get_build_changes: "pipelines_get_build_changes",
|
|
@@ -13,6 +14,7 @@ const PIPELINE_TOOLS = {
|
|
|
13
14
|
pipelines_get_build_log_by_id: "pipelines_get_build_log_by_id",
|
|
14
15
|
pipelines_get_build_status: "pipelines_get_build_status",
|
|
15
16
|
pipelines_update_build_stage: "pipelines_update_build_stage",
|
|
17
|
+
pipelines_create_pipeline: "pipelines_create_pipeline",
|
|
16
18
|
pipelines_get_run: "pipelines_get_run",
|
|
17
19
|
pipelines_list_runs: "pipelines_list_runs",
|
|
18
20
|
pipelines_run_pipeline: "pipelines_run_pipeline",
|
|
@@ -47,6 +49,56 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
47
49
|
content: [{ type: "text", text: JSON.stringify(buildDefinitions, null, 2) }],
|
|
48
50
|
};
|
|
49
51
|
});
|
|
52
|
+
const variableSchema = z.object({
|
|
53
|
+
value: z.string().optional(),
|
|
54
|
+
isSecret: z.boolean().optional(),
|
|
55
|
+
});
|
|
56
|
+
server.tool(PIPELINE_TOOLS.pipelines_create_pipeline, "Creates a pipeline definition with YAML configuration for a given project.", {
|
|
57
|
+
project: z.string().describe("Project ID or name to run the build in."),
|
|
58
|
+
name: z.string().describe("Name of the new pipeline."),
|
|
59
|
+
folder: z.string().optional().describe("Folder path for the new pipeline. Defaults to '\\' if not specified."),
|
|
60
|
+
yamlPath: z.string().describe("The path to the pipeline's YAML file in the repository"),
|
|
61
|
+
repositoryType: z.enum(getEnumKeys(RepositoryType)).describe("The type of repository where the pipeline's YAML file is located."),
|
|
62
|
+
repositoryName: z.string().describe("The name of the repository. In case of GitHub repository, this is the full name (:owner/:repo) - e.g. octocat/Hello-World."),
|
|
63
|
+
repositoryId: z.string().optional().describe("The ID of the repository."),
|
|
64
|
+
repositoryConnectionId: z.string().optional().describe("The service connection ID for GitHub repositories. Not required for Azure Repos Git."),
|
|
65
|
+
}, async ({ project, name, folder, yamlPath, repositoryType, repositoryName, repositoryId, repositoryConnectionId }) => {
|
|
66
|
+
const connection = await connectionProvider();
|
|
67
|
+
const pipelinesApi = await connection.getPipelinesApi();
|
|
68
|
+
let repositoryTypeEnumValue = safeEnumConvert(RepositoryType, repositoryType);
|
|
69
|
+
let repositoryPayload = {
|
|
70
|
+
type: repositoryType,
|
|
71
|
+
};
|
|
72
|
+
if (repositoryTypeEnumValue === RepositoryType.AzureReposGit) {
|
|
73
|
+
repositoryPayload.id = repositoryId;
|
|
74
|
+
repositoryPayload.name = repositoryName;
|
|
75
|
+
}
|
|
76
|
+
else if (repositoryTypeEnumValue === RepositoryType.GitHub) {
|
|
77
|
+
if (!repositoryConnectionId) {
|
|
78
|
+
throw new Error("Parameter 'repositoryConnectionId' is required for GitHub repositories.");
|
|
79
|
+
}
|
|
80
|
+
repositoryPayload.connection = { id: repositoryConnectionId };
|
|
81
|
+
repositoryPayload.fullname = repositoryName;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
throw new Error("Unsupported repository type");
|
|
85
|
+
}
|
|
86
|
+
const yamlConfigurationType = getEnumKeys(ConfigurationType).find((k) => ConfigurationType[k] === ConfigurationType.Yaml);
|
|
87
|
+
const createPipelineParams = {
|
|
88
|
+
name: name,
|
|
89
|
+
folder: folder || "\\",
|
|
90
|
+
configuration: {
|
|
91
|
+
type: yamlConfigurationType,
|
|
92
|
+
path: yamlPath,
|
|
93
|
+
repository: repositoryPayload,
|
|
94
|
+
variables: undefined,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
const newPipeline = await pipelinesApi.createPipeline(createPipelineParams, project);
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: "text", text: JSON.stringify(newPipeline, null, 2) }],
|
|
100
|
+
};
|
|
101
|
+
});
|
|
50
102
|
server.tool(PIPELINE_TOOLS.pipelines_get_build_definition_revisions, "Retrieves a list of revisions for a specific build definition.", {
|
|
51
103
|
project: z.string().describe("Project ID or name to get the build definition revisions for"),
|
|
52
104
|
definitionId: z.number().describe("ID of the build definition to get revisions for"),
|
|
@@ -154,10 +206,6 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
154
206
|
content: [{ type: "text", text: JSON.stringify(pipelineRuns, null, 2) }],
|
|
155
207
|
};
|
|
156
208
|
});
|
|
157
|
-
const variableSchema = z.object({
|
|
158
|
-
value: z.string().optional(),
|
|
159
|
-
isSecret: z.boolean().optional(),
|
|
160
|
-
});
|
|
161
209
|
const resourcesSchema = z.object({
|
|
162
210
|
builds: z
|
|
163
211
|
.record(z.string().describe("Name of the build resource."), z.object({
|
package/dist/tools/work-items.js
CHANGED
|
@@ -61,26 +61,44 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
61
61
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
62
62
|
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
63
63
|
}, async ({ project, team }) => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
try {
|
|
65
|
+
const connection = await connectionProvider();
|
|
66
|
+
const workApi = await connection.getWorkApi();
|
|
67
|
+
const teamContext = { project, team };
|
|
68
|
+
const backlogs = await workApi.getBacklogs(teamContext);
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: "text", text: JSON.stringify(backlogs, null, 2) }],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: "text", text: `Error listing backlogs: ${errorMessage}` }],
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
71
80
|
});
|
|
72
81
|
server.tool(WORKITEM_TOOLS.list_backlog_work_items, "Retrieve a list of backlogs of for a given project, team, and backlog category", {
|
|
73
82
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
74
83
|
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
75
84
|
backlogId: z.string().describe("The ID of the backlog category to retrieve work items from."),
|
|
76
85
|
}, async ({ project, team, backlogId }) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
try {
|
|
87
|
+
const connection = await connectionProvider();
|
|
88
|
+
const workApi = await connection.getWorkApi();
|
|
89
|
+
const teamContext = { project, team };
|
|
90
|
+
const workItems = await workApi.getBacklogLevelWorkItems(teamContext, backlogId);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: `Error listing backlog work items: ${errorMessage}` }],
|
|
99
|
+
isError: true,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
84
102
|
});
|
|
85
103
|
server.tool(WORKITEM_TOOLS.my_work_items, "Retrieve a list of work items relevent to the authenticated user.", {
|
|
86
104
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -88,53 +106,71 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
88
106
|
top: z.number().default(50).describe("The maximum number of work items to return. Defaults to 50."),
|
|
89
107
|
includeCompleted: z.boolean().default(false).describe("Whether to include completed work items. Defaults to false."),
|
|
90
108
|
}, async ({ project, type, top, includeCompleted }) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
109
|
+
try {
|
|
110
|
+
const connection = await connectionProvider();
|
|
111
|
+
const workApi = await connection.getWorkApi();
|
|
112
|
+
const workItems = await workApi.getPredefinedQueryResults(project, type, top, includeCompleted);
|
|
113
|
+
return {
|
|
114
|
+
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text", text: `Error retrieving work items: ${errorMessage}` }],
|
|
121
|
+
isError: true,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
97
124
|
});
|
|
98
125
|
server.tool(WORKITEM_TOOLS.get_work_items_batch_by_ids, "Retrieve list of work items by IDs in batch.", {
|
|
99
126
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
100
127
|
ids: z.array(z.number()).describe("The IDs of the work items to retrieve."),
|
|
101
128
|
fields: z.array(z.string()).optional().describe("Optional list of fields to include in the response. If not provided, a hardcoded default set of fields will be used."),
|
|
102
129
|
}, async ({ project, ids, fields }) => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
workitems.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
130
|
+
try {
|
|
131
|
+
const connection = await connectionProvider();
|
|
132
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
133
|
+
const defaultFields = ["System.Id", "System.WorkItemType", "System.Title", "System.State", "System.Parent", "System.Tags", "Microsoft.VSTS.Common.StackRank", "System.AssignedTo"];
|
|
134
|
+
// If no fields are provided, use the default set of fields
|
|
135
|
+
const fieldsToUse = !fields || fields.length === 0 ? defaultFields : fields;
|
|
136
|
+
const workitems = await workItemApi.getWorkItemsBatch({ ids, fields: fieldsToUse }, project);
|
|
137
|
+
// List of identity fields that need to be transformed from objects to formatted strings
|
|
138
|
+
const identityFields = [
|
|
139
|
+
"System.AssignedTo",
|
|
140
|
+
"System.CreatedBy",
|
|
141
|
+
"System.ChangedBy",
|
|
142
|
+
"System.AuthorizedAs",
|
|
143
|
+
"Microsoft.VSTS.Common.ActivatedBy",
|
|
144
|
+
"Microsoft.VSTS.Common.ResolvedBy",
|
|
145
|
+
"Microsoft.VSTS.Common.ClosedBy",
|
|
146
|
+
];
|
|
147
|
+
// Format identity fields to include displayName and uniqueName
|
|
148
|
+
// Removing the identity object as the response. It's too much and not needed
|
|
149
|
+
if (workitems && Array.isArray(workitems)) {
|
|
150
|
+
workitems.forEach((item) => {
|
|
151
|
+
if (item.fields) {
|
|
152
|
+
identityFields.forEach((fieldName) => {
|
|
153
|
+
if (item.fields && item.fields[fieldName] && typeof item.fields[fieldName] === "object") {
|
|
154
|
+
const identityField = item.fields[fieldName];
|
|
155
|
+
const name = identityField.displayName || "";
|
|
156
|
+
const email = identityField.uniqueName || "";
|
|
157
|
+
item.fields[fieldName] = `${name} <${email}>`.trim();
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
content: [{ type: "text", text: JSON.stringify(workitems, null, 2) }],
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
169
|
+
return {
|
|
170
|
+
content: [{ type: "text", text: `Error retrieving work items batch: ${errorMessage}` }],
|
|
171
|
+
isError: true,
|
|
172
|
+
};
|
|
134
173
|
}
|
|
135
|
-
return {
|
|
136
|
-
content: [{ type: "text", text: JSON.stringify(workitems, null, 2) }],
|
|
137
|
-
};
|
|
138
174
|
});
|
|
139
175
|
server.tool(WORKITEM_TOOLS.get_work_item, "Get a single work item by ID.", {
|
|
140
176
|
id: z.number().describe("The ID of the work item to retrieve."),
|
|
@@ -147,24 +183,42 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
147
183
|
.optional()
|
|
148
184
|
.describe("Expand options include 'all', 'fields', 'links', 'none', and 'relations'. Relations can be used to get child workitems. Defaults to 'none'."),
|
|
149
185
|
}, async ({ id, project, fields, asOf, expand }) => {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
186
|
+
try {
|
|
187
|
+
const connection = await connectionProvider();
|
|
188
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
189
|
+
const workItem = await workItemApi.getWorkItem(id, fields, asOf, expand, project);
|
|
190
|
+
return {
|
|
191
|
+
content: [{ type: "text", text: JSON.stringify(workItem, null, 2) }],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
196
|
+
return {
|
|
197
|
+
content: [{ type: "text", text: `Error retrieving work item: ${errorMessage}` }],
|
|
198
|
+
isError: true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
156
201
|
});
|
|
157
202
|
server.tool(WORKITEM_TOOLS.list_work_item_comments, "Retrieve list of comments for a work item by ID.", {
|
|
158
203
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
159
204
|
workItemId: z.number().describe("The ID of the work item to retrieve comments for."),
|
|
160
205
|
top: z.number().default(50).describe("Optional number of comments to retrieve. Defaults to all comments."),
|
|
161
206
|
}, async ({ project, workItemId, top }) => {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
207
|
+
try {
|
|
208
|
+
const connection = await connectionProvider();
|
|
209
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
210
|
+
const comments = await workItemApi.getComments(project, workItemId, top);
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: "text", text: JSON.stringify(comments, null, 2) }],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
217
|
+
return {
|
|
218
|
+
content: [{ type: "text", text: `Error listing work item comments: ${errorMessage}` }],
|
|
219
|
+
isError: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
168
222
|
});
|
|
169
223
|
server.tool(WORKITEM_TOOLS.add_work_item_comment, "Add comment to a work item by ID.", {
|
|
170
224
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -172,29 +226,38 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
172
226
|
comment: z.string().describe("The text of the comment to add to the work item."),
|
|
173
227
|
format: z.enum(["markdown", "html"]).optional().default("html"),
|
|
174
228
|
}, async ({ project, workItemId, comment, format }) => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
229
|
+
try {
|
|
230
|
+
const connection = await connectionProvider();
|
|
231
|
+
const orgUrl = connection.serverUrl;
|
|
232
|
+
const accessToken = await tokenProvider();
|
|
233
|
+
const body = {
|
|
234
|
+
text: comment,
|
|
235
|
+
};
|
|
236
|
+
const formatParameter = format === "markdown" ? 0 : 1;
|
|
237
|
+
const response = await fetch(`${orgUrl}/${project}/_apis/wit/workItems/${workItemId}/comments?format=${formatParameter}&api-version=${markdownCommentsApiVersion}`, {
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers: {
|
|
240
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
241
|
+
"Content-Type": "application/json",
|
|
242
|
+
"User-Agent": userAgentProvider(),
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify(body),
|
|
245
|
+
});
|
|
246
|
+
if (!response.ok) {
|
|
247
|
+
throw new Error(`Failed to add a work item comment: ${response.statusText}}`);
|
|
248
|
+
}
|
|
249
|
+
const comments = await response.text();
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text", text: comments }],
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
256
|
+
return {
|
|
257
|
+
content: [{ type: "text", text: `Error adding work item comment: ${errorMessage}` }],
|
|
258
|
+
isError: true,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
198
261
|
});
|
|
199
262
|
server.tool(WORKITEM_TOOLS.list_work_item_revisions, "Retrieve list of revisions for a work item by ID.", {
|
|
200
263
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -207,37 +270,46 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
207
270
|
.optional()
|
|
208
271
|
.describe("Optional expand parameter to include additional details. Defaults to 'None'."),
|
|
209
272
|
}, async ({ project, workItemId, top, skip, expand }) => {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
revisions.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
273
|
+
try {
|
|
274
|
+
const connection = await connectionProvider();
|
|
275
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
276
|
+
const revisions = await workItemApi.getRevisions(workItemId, top, skip, safeEnumConvert(WorkItemExpand, expand), project);
|
|
277
|
+
// Dynamically clean up identity objects in revision fields
|
|
278
|
+
// Identity objects typically have properties like displayName, url, _links, id, uniqueName, imageUrl, descriptor
|
|
279
|
+
if (revisions && Array.isArray(revisions)) {
|
|
280
|
+
revisions.forEach((revision) => {
|
|
281
|
+
if (revision.fields) {
|
|
282
|
+
Object.keys(revision.fields).forEach((fieldName) => {
|
|
283
|
+
const fieldValue = revision.fields ? revision.fields[fieldName] : undefined;
|
|
284
|
+
// Check if this is an identity object by looking for common identity properties
|
|
285
|
+
if (fieldValue &&
|
|
286
|
+
typeof fieldValue === "object" &&
|
|
287
|
+
!Array.isArray(fieldValue) &&
|
|
288
|
+
"displayName" in fieldValue &&
|
|
289
|
+
("url" in fieldValue || "_links" in fieldValue || "uniqueName" in fieldValue)) {
|
|
290
|
+
// Remove unwanted properties from identity objects
|
|
291
|
+
delete fieldValue.url;
|
|
292
|
+
delete fieldValue._links;
|
|
293
|
+
delete fieldValue.id;
|
|
294
|
+
delete fieldValue.uniqueName;
|
|
295
|
+
delete fieldValue.imageUrl;
|
|
296
|
+
delete fieldValue.descriptor;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
content: [{ type: "text", text: JSON.stringify(revisions, null, 2) }],
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
308
|
+
return {
|
|
309
|
+
content: [{ type: "text", text: `Error listing work item revisions: ${errorMessage}` }],
|
|
310
|
+
isError: true,
|
|
311
|
+
};
|
|
237
312
|
}
|
|
238
|
-
return {
|
|
239
|
-
content: [{ type: "text", text: JSON.stringify(revisions, null, 2) }],
|
|
240
|
-
};
|
|
241
313
|
});
|
|
242
314
|
server.tool(WORKITEM_TOOLS.add_child_work_items, "Create one or many child work items from a parent by work item type and parent id.", {
|
|
243
315
|
parentId: z.number().describe("The ID of the parent work item to create a child work item under."),
|
|
@@ -413,13 +485,22 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
413
485
|
team: z.string().optional().describe("The name or ID of the Azure DevOps team. If not provided, the default team will be used."),
|
|
414
486
|
iterationId: z.string().describe("The ID of the iteration to retrieve work items for."),
|
|
415
487
|
}, async ({ project, team, iterationId }) => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
488
|
+
try {
|
|
489
|
+
const connection = await connectionProvider();
|
|
490
|
+
const workApi = await connection.getWorkApi();
|
|
491
|
+
//get the work items for the current iteration
|
|
492
|
+
const workItems = await workApi.getIterationWorkItems({ project, team }, iterationId);
|
|
493
|
+
return {
|
|
494
|
+
content: [{ type: "text", text: JSON.stringify(workItems, null, 2) }],
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
catch (error) {
|
|
498
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
499
|
+
return {
|
|
500
|
+
content: [{ type: "text", text: `Error retrieving work items for iteration: ${errorMessage}` }],
|
|
501
|
+
isError: true,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
423
504
|
});
|
|
424
505
|
server.tool(WORKITEM_TOOLS.update_work_item, "Update a work item by ID with specified fields.", {
|
|
425
506
|
id: z.number().describe("The ID of the work item to update."),
|
|
@@ -436,28 +517,46 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
436
517
|
}))
|
|
437
518
|
.describe("An array of field updates to apply to the work item."),
|
|
438
519
|
}, async ({ id, updates }) => {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
520
|
+
try {
|
|
521
|
+
const connection = await connectionProvider();
|
|
522
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
523
|
+
// Convert operation names to lowercase for API
|
|
524
|
+
const apiUpdates = updates.map((update) => ({
|
|
525
|
+
...update,
|
|
526
|
+
op: update.op,
|
|
527
|
+
}));
|
|
528
|
+
const updatedWorkItem = await workItemApi.updateWorkItem(null, apiUpdates, id);
|
|
529
|
+
return {
|
|
530
|
+
content: [{ type: "text", text: JSON.stringify(updatedWorkItem, null, 2) }],
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
535
|
+
return {
|
|
536
|
+
content: [{ type: "text", text: `Error updating work item: ${errorMessage}` }],
|
|
537
|
+
isError: true,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
450
540
|
});
|
|
451
541
|
server.tool(WORKITEM_TOOLS.get_work_item_type, "Get a specific work item type.", {
|
|
452
542
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
453
543
|
workItemType: z.string().describe("The name of the work item type to retrieve."),
|
|
454
544
|
}, async ({ project, workItemType }) => {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
545
|
+
try {
|
|
546
|
+
const connection = await connectionProvider();
|
|
547
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
548
|
+
const workItemTypeInfo = await workItemApi.getWorkItemType(project, workItemType);
|
|
549
|
+
return {
|
|
550
|
+
content: [{ type: "text", text: JSON.stringify(workItemTypeInfo, null, 2) }],
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
catch (error) {
|
|
554
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
555
|
+
return {
|
|
556
|
+
content: [{ type: "text", text: `Error retrieving work item type: ${errorMessage}` }],
|
|
557
|
+
isError: true,
|
|
558
|
+
};
|
|
559
|
+
}
|
|
461
560
|
});
|
|
462
561
|
server.tool(WORKITEM_TOOLS.create_work_item, "Create a new work item in a specified project and work item type.", {
|
|
463
562
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -517,12 +616,21 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
517
616
|
includeDeleted: z.boolean().default(false).describe("Whether to include deleted items in the query results. Defaults to false."),
|
|
518
617
|
useIsoDateFormat: z.boolean().default(false).describe("Whether to use ISO date format in the response. Defaults to false."),
|
|
519
618
|
}, async ({ project, query, expand, depth, includeDeleted, useIsoDateFormat }) => {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
619
|
+
try {
|
|
620
|
+
const connection = await connectionProvider();
|
|
621
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
622
|
+
const queryDetails = await workItemApi.getQuery(project, query, safeEnumConvert(QueryExpand, expand), depth, includeDeleted, useIsoDateFormat);
|
|
623
|
+
return {
|
|
624
|
+
content: [{ type: "text", text: JSON.stringify(queryDetails, null, 2) }],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
629
|
+
return {
|
|
630
|
+
content: [{ type: "text", text: `Error retrieving query: ${errorMessage}` }],
|
|
631
|
+
isError: true,
|
|
632
|
+
};
|
|
633
|
+
}
|
|
526
634
|
});
|
|
527
635
|
server.tool(WORKITEM_TOOLS.get_query_results_by_id, "Retrieve the results of a work item query given the query ID. Supports full or IDs-only response types.", {
|
|
528
636
|
id: z.string().describe("The ID of the query to retrieve results for."),
|
|
@@ -532,21 +640,30 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
532
640
|
top: z.number().default(50).describe("The maximum number of results to return. Defaults to 50."),
|
|
533
641
|
responseType: z.enum(["full", "ids"]).default("full").describe("Response type: 'full' returns complete query results (default), 'ids' returns only work item IDs for reduced payload size."),
|
|
534
642
|
}, async ({ id, project, team, timePrecision, top, responseType }) => {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
643
|
+
try {
|
|
644
|
+
const connection = await connectionProvider();
|
|
645
|
+
const workItemApi = await connection.getWorkItemTrackingApi();
|
|
646
|
+
const teamContext = { project, team };
|
|
647
|
+
const queryResult = await workItemApi.queryById(id, teamContext, timePrecision, top);
|
|
648
|
+
// If ids mode, extract and return only the IDs
|
|
649
|
+
if (responseType === "ids") {
|
|
650
|
+
const ids = queryResult.workItems?.map((workItem) => workItem.id).filter((id) => id !== undefined) || [];
|
|
651
|
+
return {
|
|
652
|
+
content: [{ type: "text", text: JSON.stringify({ ids, count: ids.length }, null, 2) }],
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
// Default: return full query results
|
|
542
656
|
return {
|
|
543
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
657
|
+
content: [{ type: "text", text: JSON.stringify(queryResult, null, 2) }],
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
662
|
+
return {
|
|
663
|
+
content: [{ type: "text", text: `Error retrieving query results: ${errorMessage}` }],
|
|
664
|
+
isError: true,
|
|
544
665
|
};
|
|
545
666
|
}
|
|
546
|
-
// Default: return full query results
|
|
547
|
-
return {
|
|
548
|
-
content: [{ type: "text", text: JSON.stringify(queryResult, null, 2) }],
|
|
549
|
-
};
|
|
550
667
|
});
|
|
551
668
|
server.tool(WORKITEM_TOOLS.update_work_items_batch, "Update work items in batch", {
|
|
552
669
|
updates: z
|
|
@@ -559,53 +676,62 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
559
676
|
}))
|
|
560
677
|
.describe("An array of updates to apply to work items. Each update should include the operation (op), work item ID (id), field path (path), and new value (value)."),
|
|
561
678
|
}, async ({ updates }) => {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
679
|
+
try {
|
|
680
|
+
const connection = await connectionProvider();
|
|
681
|
+
const orgUrl = connection.serverUrl;
|
|
682
|
+
const accessToken = await tokenProvider();
|
|
683
|
+
// Extract unique IDs from the updates array
|
|
684
|
+
const uniqueIds = Array.from(new Set(updates.map((update) => update.id)));
|
|
685
|
+
const body = uniqueIds.map((id) => {
|
|
686
|
+
const workItemUpdates = updates.filter((update) => update.id === id);
|
|
687
|
+
const operations = workItemUpdates.map(({ op, path, value, format }) => ({
|
|
688
|
+
op: op,
|
|
689
|
+
path: path,
|
|
690
|
+
value: encodeFormattedValue(value, format),
|
|
691
|
+
}));
|
|
692
|
+
// Add format operations for Markdown fields
|
|
693
|
+
workItemUpdates.forEach(({ path, value, format }) => {
|
|
694
|
+
if (format === "Markdown" && value && value.length > 50) {
|
|
695
|
+
operations.push({
|
|
696
|
+
op: "Add",
|
|
697
|
+
path: `/multilineFieldsFormat${path.replace("/fields", "")}`,
|
|
698
|
+
value: "Markdown",
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
return {
|
|
703
|
+
method: "PATCH",
|
|
704
|
+
uri: `/_apis/wit/workitems/${id}?api-version=${batchApiVersion}`,
|
|
705
|
+
headers: {
|
|
706
|
+
"Content-Type": "application/json-patch+json",
|
|
707
|
+
},
|
|
708
|
+
body: operations,
|
|
709
|
+
};
|
|
583
710
|
});
|
|
584
|
-
|
|
711
|
+
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
585
712
|
method: "PATCH",
|
|
586
|
-
uri: `/_apis/wit/workitems/${id}?api-version=${batchApiVersion}`,
|
|
587
713
|
headers: {
|
|
588
|
-
"
|
|
714
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
715
|
+
"Content-Type": "application/json",
|
|
716
|
+
"User-Agent": userAgentProvider(),
|
|
589
717
|
},
|
|
590
|
-
body:
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
608
|
-
};
|
|
718
|
+
body: JSON.stringify(body),
|
|
719
|
+
});
|
|
720
|
+
if (!response.ok) {
|
|
721
|
+
throw new Error(`Failed to update work items in batch: ${response.statusText}`);
|
|
722
|
+
}
|
|
723
|
+
const result = await response.json();
|
|
724
|
+
return {
|
|
725
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
catch (error) {
|
|
729
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
730
|
+
return {
|
|
731
|
+
content: [{ type: "text", text: `Error updating work items in batch: ${errorMessage}` }],
|
|
732
|
+
isError: true,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
609
735
|
});
|
|
610
736
|
server.tool(WORKITEM_TOOLS.work_items_link, "Link work items together in batch.", {
|
|
611
737
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
@@ -621,47 +747,56 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
|
|
|
621
747
|
}))
|
|
622
748
|
.describe(""),
|
|
623
749
|
}, async ({ project, updates }) => {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
750
|
+
try {
|
|
751
|
+
const connection = await connectionProvider();
|
|
752
|
+
const orgUrl = connection.serverUrl;
|
|
753
|
+
const accessToken = await tokenProvider();
|
|
754
|
+
// Extract unique IDs from the updates array
|
|
755
|
+
const uniqueIds = Array.from(new Set(updates.map((update) => update.id)));
|
|
756
|
+
const body = uniqueIds.map((id) => ({
|
|
757
|
+
method: "PATCH",
|
|
758
|
+
uri: `/_apis/wit/workitems/${id}?api-version=${batchApiVersion}`,
|
|
759
|
+
headers: {
|
|
760
|
+
"Content-Type": "application/json-patch+json",
|
|
761
|
+
},
|
|
762
|
+
body: updates
|
|
763
|
+
.filter((update) => update.id === id)
|
|
764
|
+
.map(({ linkToId, type, comment }) => ({
|
|
765
|
+
op: "add",
|
|
766
|
+
path: "/relations/-",
|
|
767
|
+
value: {
|
|
768
|
+
rel: `${getLinkTypeFromName(type)}`,
|
|
769
|
+
url: `${orgUrl}/${project}/_apis/wit/workItems/${linkToId}`,
|
|
770
|
+
attributes: {
|
|
771
|
+
comment: comment || "",
|
|
772
|
+
},
|
|
645
773
|
},
|
|
774
|
+
})),
|
|
775
|
+
}));
|
|
776
|
+
const response = await fetch(`${orgUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
|
|
777
|
+
method: "PATCH",
|
|
778
|
+
headers: {
|
|
779
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
780
|
+
"Content-Type": "application/json",
|
|
781
|
+
"User-Agent": userAgentProvider(),
|
|
646
782
|
},
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
"
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
};
|
|
783
|
+
body: JSON.stringify(body),
|
|
784
|
+
});
|
|
785
|
+
if (!response.ok) {
|
|
786
|
+
throw new Error(`Failed to update work items in batch: ${response.statusText}`);
|
|
787
|
+
}
|
|
788
|
+
const result = await response.json();
|
|
789
|
+
return {
|
|
790
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
catch (error) {
|
|
794
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
795
|
+
return {
|
|
796
|
+
content: [{ type: "text", text: `Error linking work items: ${errorMessage}` }],
|
|
797
|
+
isError: true,
|
|
798
|
+
};
|
|
799
|
+
}
|
|
665
800
|
});
|
|
666
801
|
server.tool(WORKITEM_TOOLS.work_item_unlink, "Remove one or many links from a single work item", {
|
|
667
802
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "2.2.2-nightly.
|
|
1
|
+
export const packageVersion = "2.2.2-nightly.20251113";
|