@azure-devops/mcp 2.8.1 → 2.9.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/README.md +2 -7
- package/dist/shared/command.js +34 -0
- package/dist/tools/advanced-security.js +31 -11
- package/dist/tools/pipelines.dto.js +95 -0
- package/dist/tools/pipelines.js +350 -373
- package/dist/tools/repositories.js +695 -1485
- package/dist/tools/test-plans.js +297 -396
- package/dist/tools/wiki.js +242 -273
- package/dist/tools/work-items.js +851 -1174
- package/dist/tools/work.js +239 -285
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/tools/work.js
CHANGED
|
@@ -4,244 +4,284 @@ import { z } from "zod";
|
|
|
4
4
|
import { TreeStructureGroup, TreeNodeStructureType } from "azure-devops-node-api/interfaces/WorkItemTrackingInterfaces.js";
|
|
5
5
|
import { elicitProject, elicitTeam } from "../shared/elicitations.js";
|
|
6
6
|
const WORK_TOOLS = {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
assign_iterations: "work_assign_iterations",
|
|
11
|
-
get_team_capacity: "work_get_team_capacity",
|
|
12
|
-
update_team_capacity: "work_update_team_capacity",
|
|
13
|
-
get_iteration_capacities: "work_get_iteration_capacities",
|
|
14
|
-
get_team_settings: "work_get_team_settings",
|
|
7
|
+
work: "work",
|
|
8
|
+
work_iteration_write: "work_iteration_write",
|
|
9
|
+
work_capacity_write: "work_capacity_write",
|
|
15
10
|
};
|
|
16
11
|
function configureWorkTools(server, _, connectionProvider) {
|
|
17
|
-
server.tool(WORK_TOOLS.
|
|
12
|
+
server.tool(WORK_TOOLS.work, "Retrieve work-related data for a project or team. Use the action parameter to specify the operation.", {
|
|
13
|
+
action: z
|
|
14
|
+
.enum(["list_iterations", "list_team_iterations", "get_team_settings", "get_team_capacity", "get_iteration_capacities"])
|
|
15
|
+
.describe("The action to perform. Options: list_iterations (list all iterations in a project), list_team_iterations (list iterations assigned to a team), get_team_settings (get team settings including default iteration and area path), get_team_capacity (get team capacity for an iteration), get_iteration_capacities (get capacity for all teams in an iteration)."),
|
|
18
16
|
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."),
|
|
19
|
-
team: z
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
team: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("The name or ID of the Azure DevOps team. Required for list_team_iterations, get_team_settings, and get_team_capacity. Reuse from prior context if already known."),
|
|
21
|
+
iterationId: z.string().optional().describe("The Iteration ID. Required for get_team_capacity and get_iteration_capacities."),
|
|
22
|
+
timeframe: z.enum(["current"]).optional().describe("The timeframe for list_team_iterations. Only 'current' is supported."),
|
|
23
|
+
depth: z.coerce.number().default(2).describe("Depth of children to fetch. Used for list_iterations. Defaults to 2."),
|
|
24
|
+
excludedIds: z.array(z.coerce.number().min(1)).optional().describe("An optional array of iteration IDs, and their children, to exclude from results. Used for list_iterations."),
|
|
25
|
+
}, async ({ action, project, team, iterationId, timeframe, depth, excludedIds }) => {
|
|
22
26
|
try {
|
|
23
27
|
const connection = await connectionProvider();
|
|
24
28
|
let resolvedProject = project;
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
let resolvedTeam = team;
|
|
32
|
-
if (!resolvedTeam) {
|
|
33
|
-
const result = await elicitTeam(server, connection, resolvedProject, "Select the Azure DevOps team to list iterations for.");
|
|
34
|
-
if ("response" in result)
|
|
35
|
-
return result.response;
|
|
36
|
-
resolvedTeam = result.resolved;
|
|
37
|
-
}
|
|
38
|
-
const workApi = await connection.getWorkApi();
|
|
39
|
-
const iterations = await workApi.getTeamIterations({ project: resolvedProject, team: resolvedTeam }, timeframe);
|
|
40
|
-
if (!iterations) {
|
|
41
|
-
return { content: [{ type: "text", text: "No iterations found" }], isError: true };
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
content: [
|
|
45
|
-
{ type: "text", text: `Project: ${resolvedProject}, Team: ${resolvedTeam}` },
|
|
46
|
-
{ type: "text", text: JSON.stringify(iterations, null, 2) },
|
|
47
|
-
],
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
52
|
-
return {
|
|
53
|
-
content: [{ type: "text", text: `Error fetching team iterations: ${errorMessage}` }],
|
|
54
|
-
isError: true,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
server.tool(WORK_TOOLS.create_iterations, "Create new iterations in a specified Azure DevOps project.", {
|
|
59
|
-
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
60
|
-
iterations: z
|
|
61
|
-
.array(z.object({
|
|
62
|
-
iterationName: z.string().describe("The name of the iteration to create."),
|
|
63
|
-
startDate: z.string().optional().describe("The start date of the iteration in ISO format (e.g., '2023-01-01T00:00:00Z'). Optional."),
|
|
64
|
-
finishDate: z.string().optional().describe("The finish date of the iteration in ISO format (e.g., '2023-01-31T23:59:59Z'). Optional."),
|
|
65
|
-
}))
|
|
66
|
-
.describe("An array of iterations to create. Each iteration must have a name and can optionally have start and finish dates in ISO format."),
|
|
67
|
-
}, async ({ project, iterations }) => {
|
|
68
|
-
try {
|
|
69
|
-
const connection = await connectionProvider();
|
|
70
|
-
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
71
|
-
const results = [];
|
|
72
|
-
for (const { iterationName, startDate, finishDate } of iterations) {
|
|
73
|
-
// Step 1: Create the iteration
|
|
74
|
-
const iteration = await workItemTrackingApi.createOrUpdateClassificationNode({
|
|
75
|
-
name: iterationName,
|
|
76
|
-
attributes: {
|
|
77
|
-
startDate: startDate ? new Date(startDate) : undefined,
|
|
78
|
-
finishDate: finishDate ? new Date(finishDate) : undefined,
|
|
79
|
-
},
|
|
80
|
-
}, project, TreeStructureGroup.Iterations);
|
|
81
|
-
if (iteration) {
|
|
82
|
-
results.push(iteration);
|
|
29
|
+
if (action === "list_team_iterations") {
|
|
30
|
+
if (!resolvedProject) {
|
|
31
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list team iterations for.");
|
|
32
|
+
if ("response" in result)
|
|
33
|
+
return result.response;
|
|
34
|
+
resolvedProject = result.resolved;
|
|
83
35
|
}
|
|
36
|
+
let resolvedTeam = team;
|
|
37
|
+
if (!resolvedTeam) {
|
|
38
|
+
const result = await elicitTeam(server, connection, resolvedProject, "Select the Azure DevOps team to list iterations for.");
|
|
39
|
+
if ("response" in result)
|
|
40
|
+
return result.response;
|
|
41
|
+
resolvedTeam = result.resolved;
|
|
42
|
+
}
|
|
43
|
+
const workApi = await connection.getWorkApi();
|
|
44
|
+
const iterations = await workApi.getTeamIterations({ project: resolvedProject, team: resolvedTeam }, timeframe);
|
|
45
|
+
if (!iterations) {
|
|
46
|
+
return { content: [{ type: "text", text: "No iterations found" }], isError: true };
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{ type: "text", text: `Project: ${resolvedProject}, Team: ${resolvedTeam}` },
|
|
51
|
+
{ type: "text", text: JSON.stringify(iterations, null, 2) },
|
|
52
|
+
],
|
|
53
|
+
};
|
|
84
54
|
}
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
55
|
+
if (action === "list_iterations") {
|
|
56
|
+
if (!resolvedProject) {
|
|
57
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to list iterations for.");
|
|
58
|
+
if ("response" in result)
|
|
59
|
+
return result.response;
|
|
60
|
+
resolvedProject = result.resolved;
|
|
61
|
+
}
|
|
62
|
+
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
63
|
+
const effectiveDepth = depth ?? 1;
|
|
64
|
+
const results = await workItemTrackingApi.getClassificationNodes(resolvedProject, [], effectiveDepth);
|
|
65
|
+
if (!results) {
|
|
66
|
+
return { content: [{ type: "text", text: "No iterations were found" }], isError: true };
|
|
67
|
+
}
|
|
68
|
+
let filteredResults = results.filter((node) => node.structureType === TreeNodeStructureType.Iteration);
|
|
69
|
+
if (excludedIds && excludedIds.length > 0) {
|
|
70
|
+
const filterOutIds = (nodes) => {
|
|
71
|
+
return nodes
|
|
72
|
+
.filter((node) => !node.id || !excludedIds.includes(node.id))
|
|
73
|
+
.map((node) => {
|
|
74
|
+
if (node.children && node.children.length > 0) {
|
|
75
|
+
return {
|
|
76
|
+
...node,
|
|
77
|
+
children: filterOutIds(node.children),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return node;
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
filteredResults = filterOutIds(filteredResults);
|
|
84
|
+
}
|
|
85
|
+
if (filteredResults.length === 0) {
|
|
86
|
+
return { content: [{ type: "text", text: "No iterations were found" }], isError: true };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
content: [{ type: "text", text: JSON.stringify(filteredResults, null, 2) }],
|
|
90
|
+
};
|
|
118
91
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
92
|
+
if (action === "get_team_settings") {
|
|
93
|
+
if (!resolvedProject) {
|
|
94
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to get team settings for.");
|
|
95
|
+
if ("response" in result)
|
|
96
|
+
return result.response;
|
|
97
|
+
resolvedProject = result.resolved;
|
|
98
|
+
}
|
|
99
|
+
let resolvedTeam = team;
|
|
100
|
+
if (!resolvedTeam) {
|
|
101
|
+
const result = await elicitTeam(server, connection, resolvedProject, "Select the Azure DevOps team to get settings for.");
|
|
102
|
+
if ("response" in result)
|
|
103
|
+
return result.response;
|
|
104
|
+
resolvedTeam = result.resolved;
|
|
105
|
+
}
|
|
106
|
+
const workApi = await connection.getWorkApi();
|
|
107
|
+
const teamContext = { project: resolvedProject, team: resolvedTeam };
|
|
108
|
+
const teamSettings = await workApi.getTeamSettings(teamContext);
|
|
109
|
+
if (!teamSettings) {
|
|
110
|
+
return { content: [{ type: "text", text: "No team settings found" }], isError: true };
|
|
111
|
+
}
|
|
112
|
+
const teamFieldValues = await workApi.getTeamFieldValues(teamContext);
|
|
113
|
+
const settingsResult = {
|
|
114
|
+
backlogIteration: teamSettings.backlogIteration,
|
|
115
|
+
defaultIteration: teamSettings.defaultIteration,
|
|
116
|
+
defaultIterationMacro: teamSettings.defaultIterationMacro,
|
|
117
|
+
backlogVisibilities: teamSettings.backlogVisibilities,
|
|
118
|
+
bugsBehavior: teamSettings.bugsBehavior,
|
|
119
|
+
workingDays: teamSettings.workingDays,
|
|
120
|
+
defaultAreaPath: teamFieldValues?.defaultValue,
|
|
121
|
+
areaPathField: teamFieldValues?.field,
|
|
122
|
+
areaPaths: teamFieldValues?.values,
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
content: [
|
|
126
|
+
{ type: "text", text: `Project: ${resolvedProject}, Team: ${resolvedTeam}` },
|
|
127
|
+
{ type: "text", text: JSON.stringify(settingsResult, null, 2) },
|
|
128
|
+
],
|
|
129
|
+
};
|
|
123
130
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
if (action === "get_team_capacity") {
|
|
132
|
+
if (!resolvedProject) {
|
|
133
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to get team capacity for.");
|
|
134
|
+
if ("response" in result)
|
|
135
|
+
return result.response;
|
|
136
|
+
resolvedProject = result.resolved;
|
|
137
|
+
}
|
|
138
|
+
if (!team) {
|
|
139
|
+
return { content: [{ type: "text", text: "Team is required for get_team_capacity" }], isError: true };
|
|
140
|
+
}
|
|
141
|
+
if (!iterationId) {
|
|
142
|
+
return { content: [{ type: "text", text: "iterationId is required for get_team_capacity" }], isError: true };
|
|
143
|
+
}
|
|
144
|
+
const workApi = await connection.getWorkApi();
|
|
145
|
+
const teamContext = { project: resolvedProject, team };
|
|
146
|
+
const rawResults = await workApi.getCapacitiesWithIdentityRefAndTotals(teamContext, iterationId);
|
|
147
|
+
if (!rawResults || rawResults.teamMembers?.length === 0) {
|
|
148
|
+
return { content: [{ type: "text", text: "No team capacity assigned to the team" }], isError: true };
|
|
149
|
+
}
|
|
150
|
+
const simplifiedResults = {
|
|
151
|
+
...rawResults,
|
|
152
|
+
teamMembers: (rawResults.teamMembers || []).map((member) => {
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
154
|
+
const { url, ...rest } = member;
|
|
155
|
+
return {
|
|
156
|
+
...rest,
|
|
157
|
+
teamMember: member.teamMember
|
|
158
|
+
? {
|
|
159
|
+
displayName: member.teamMember.displayName,
|
|
160
|
+
id: member.teamMember.id,
|
|
161
|
+
uniqueName: member.teamMember.uniqueName,
|
|
162
|
+
}
|
|
163
|
+
: undefined,
|
|
164
|
+
};
|
|
165
|
+
}),
|
|
166
|
+
};
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text", text: JSON.stringify(simplifiedResults, null, 2) }],
|
|
140
169
|
};
|
|
141
|
-
filteredResults = filterOutIds(filteredResults);
|
|
142
170
|
}
|
|
143
|
-
if (
|
|
144
|
-
|
|
171
|
+
if (action === "get_iteration_capacities") {
|
|
172
|
+
if (!resolvedProject) {
|
|
173
|
+
const result = await elicitProject(server, connection, "Select the Azure DevOps project to get iteration capacities for.");
|
|
174
|
+
if ("response" in result)
|
|
175
|
+
return result.response;
|
|
176
|
+
resolvedProject = result.resolved;
|
|
177
|
+
}
|
|
178
|
+
if (!iterationId) {
|
|
179
|
+
return { content: [{ type: "text", text: "iterationId is required for get_iteration_capacities" }], isError: true };
|
|
180
|
+
}
|
|
181
|
+
const workApi = await connection.getWorkApi();
|
|
182
|
+
const rawResults = await workApi.getTotalIterationCapacities(resolvedProject, iterationId);
|
|
183
|
+
if (!rawResults || !rawResults.teams || rawResults.teams.length === 0) {
|
|
184
|
+
return { content: [{ type: "text", text: "No iteration capacity assigned to the teams" }], isError: true };
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: "text", text: JSON.stringify(rawResults, null, 2) }],
|
|
188
|
+
};
|
|
145
189
|
}
|
|
146
|
-
return {
|
|
147
|
-
content: [{ type: "text", text: JSON.stringify(filteredResults, null, 2) }],
|
|
148
|
-
};
|
|
190
|
+
return { content: [{ type: "text", text: `Unknown action: ${action}` }], isError: true };
|
|
149
191
|
}
|
|
150
192
|
catch (error) {
|
|
151
193
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
194
|
+
const actionErrorMessages = {
|
|
195
|
+
list_team_iterations: `Error fetching team iterations: ${errorMessage}`,
|
|
196
|
+
list_iterations: `Error fetching iterations: ${errorMessage}`,
|
|
197
|
+
get_team_settings: `Error fetching team settings: ${errorMessage}`,
|
|
198
|
+
get_team_capacity: `Error getting team capacity: ${errorMessage}`,
|
|
199
|
+
get_iteration_capacities: `Error getting iteration capacities: ${errorMessage}`,
|
|
200
|
+
};
|
|
152
201
|
return {
|
|
153
|
-
content: [{ type: "text", text: `Error
|
|
202
|
+
content: [{ type: "text", text: actionErrorMessages[action] ?? `Error: ${errorMessage}` }],
|
|
154
203
|
isError: true,
|
|
155
204
|
};
|
|
156
205
|
}
|
|
157
206
|
});
|
|
158
|
-
server.tool(WORK_TOOLS.
|
|
207
|
+
server.tool(WORK_TOOLS.work_iteration_write, "Create or assign iterations in an Azure DevOps project. Use the action parameter to specify the operation.", {
|
|
208
|
+
action: z.enum(["create", "assign"]).describe("The action to perform. 'create' creates new iterations in the project; 'assign' assigns existing iterations to a team."),
|
|
159
209
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
160
|
-
team: z.string().describe("The name or ID of the Azure DevOps team."),
|
|
210
|
+
team: z.string().optional().describe("The name or ID of the Azure DevOps team. Required for assign."),
|
|
161
211
|
iterations: z
|
|
162
212
|
.array(z.object({
|
|
163
|
-
|
|
164
|
-
|
|
213
|
+
iterationName: z.string().optional().describe("The name of the iteration to create. Used for create."),
|
|
214
|
+
startDate: z.string().optional().describe("The start date of the iteration in ISO format (e.g., '2023-01-01T00:00:00Z'). Used for create."),
|
|
215
|
+
finishDate: z.string().optional().describe("The finish date of the iteration in ISO format (e.g., '2023-01-31T23:59:59Z'). Used for create."),
|
|
216
|
+
identifier: z.string().optional().describe("The identifier of the iteration to assign. Used for assign."),
|
|
217
|
+
path: z.string().optional().describe("The path of the iteration to assign, e.g., 'Project/Iteration'. Used for assign."),
|
|
165
218
|
}))
|
|
166
|
-
.describe("An array of iterations to
|
|
167
|
-
}, async ({ project, team, iterations }) => {
|
|
219
|
+
.describe("An array of iterations to process. For create: provide iterationName and optional dates. For assign: provide identifier and path."),
|
|
220
|
+
}, async ({ action, project, team, iterations }) => {
|
|
168
221
|
try {
|
|
169
222
|
const connection = await connectionProvider();
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
223
|
+
if (action === "create") {
|
|
224
|
+
const workItemTrackingApi = await connection.getWorkItemTrackingApi();
|
|
225
|
+
const results = [];
|
|
226
|
+
for (const { iterationName, startDate, finishDate } of iterations) {
|
|
227
|
+
if (!iterationName)
|
|
228
|
+
continue;
|
|
229
|
+
const iteration = await workItemTrackingApi.createOrUpdateClassificationNode({
|
|
230
|
+
name: iterationName,
|
|
231
|
+
attributes: {
|
|
232
|
+
startDate: startDate ? new Date(startDate) : undefined,
|
|
233
|
+
finishDate: finishDate ? new Date(finishDate) : undefined,
|
|
234
|
+
},
|
|
235
|
+
}, project, TreeStructureGroup.Iterations);
|
|
236
|
+
if (iteration) {
|
|
237
|
+
results.push(iteration);
|
|
238
|
+
}
|
|
177
239
|
}
|
|
240
|
+
if (results.length === 0) {
|
|
241
|
+
return { content: [{ type: "text", text: "No iterations were created" }], isError: true };
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
245
|
+
};
|
|
178
246
|
}
|
|
179
|
-
if (
|
|
180
|
-
|
|
247
|
+
if (action === "assign") {
|
|
248
|
+
if (!team) {
|
|
249
|
+
return { content: [{ type: "text", text: "Team is required for assign" }], isError: true };
|
|
250
|
+
}
|
|
251
|
+
const workApi = await connection.getWorkApi();
|
|
252
|
+
const teamContext = { project, team };
|
|
253
|
+
const results = [];
|
|
254
|
+
for (const { identifier, path } of iterations) {
|
|
255
|
+
if (!identifier || !path)
|
|
256
|
+
continue;
|
|
257
|
+
const assignment = await workApi.postTeamIteration({ path: path, id: identifier }, teamContext);
|
|
258
|
+
if (assignment) {
|
|
259
|
+
results.push(assignment);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (results.length === 0) {
|
|
263
|
+
return { content: [{ type: "text", text: "No iterations were assigned to the team" }], isError: true };
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
267
|
+
};
|
|
181
268
|
}
|
|
182
|
-
return {
|
|
183
|
-
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
184
|
-
};
|
|
269
|
+
return { content: [{ type: "text", text: `Unknown action: ${action}` }], isError: true };
|
|
185
270
|
}
|
|
186
271
|
catch (error) {
|
|
187
272
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
server.tool(WORK_TOOLS.get_team_capacity, "Get the team capacity of a specific team and iteration in a project. If a project is not specified, you will be prompted to select one.", {
|
|
195
|
-
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."),
|
|
196
|
-
team: z.string().describe("The name or Id of the Azure DevOps team. Reuse from prior context if already known."),
|
|
197
|
-
iterationId: z.string().describe("The Iteration Id to get capacity for."),
|
|
198
|
-
}, async ({ project, team, iterationId }) => {
|
|
199
|
-
try {
|
|
200
|
-
const connection = await connectionProvider();
|
|
201
|
-
let resolvedProject = project;
|
|
202
|
-
if (!resolvedProject) {
|
|
203
|
-
const result = await elicitProject(server, connection, "Select the Azure DevOps project to get team capacity for.");
|
|
204
|
-
if ("response" in result)
|
|
205
|
-
return result.response;
|
|
206
|
-
resolvedProject = result.resolved;
|
|
207
|
-
}
|
|
208
|
-
const workApi = await connection.getWorkApi();
|
|
209
|
-
const teamContext = { project: resolvedProject, team };
|
|
210
|
-
const rawResults = await workApi.getCapacitiesWithIdentityRefAndTotals(teamContext, iterationId);
|
|
211
|
-
if (!rawResults || rawResults.teamMembers?.length === 0) {
|
|
212
|
-
return { content: [{ type: "text", text: "No team capacity assigned to the team" }], isError: true };
|
|
213
|
-
}
|
|
214
|
-
// Remove unwanted fields from teamMember and url
|
|
215
|
-
const simplifiedResults = {
|
|
216
|
-
...rawResults,
|
|
217
|
-
teamMembers: (rawResults.teamMembers || []).map((member) => {
|
|
218
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
219
|
-
const { url, ...rest } = member;
|
|
220
|
-
return {
|
|
221
|
-
...rest,
|
|
222
|
-
teamMember: member.teamMember
|
|
223
|
-
? {
|
|
224
|
-
displayName: member.teamMember.displayName,
|
|
225
|
-
id: member.teamMember.id,
|
|
226
|
-
uniqueName: member.teamMember.uniqueName,
|
|
227
|
-
}
|
|
228
|
-
: undefined,
|
|
229
|
-
};
|
|
230
|
-
}),
|
|
273
|
+
const actionErrorMessages = {
|
|
274
|
+
create: `Error creating iterations: ${errorMessage}`,
|
|
275
|
+
assign: `Error assigning iterations: ${errorMessage}`,
|
|
231
276
|
};
|
|
232
277
|
return {
|
|
233
|
-
content: [{ type: "text", text:
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
catch (error) {
|
|
237
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
238
|
-
return {
|
|
239
|
-
content: [{ type: "text", text: `Error getting team capacity: ${errorMessage}` }],
|
|
278
|
+
content: [{ type: "text", text: actionErrorMessages[action] ?? `Error: ${errorMessage}` }],
|
|
240
279
|
isError: true,
|
|
241
280
|
};
|
|
242
281
|
}
|
|
243
282
|
});
|
|
244
|
-
server.tool(WORK_TOOLS.
|
|
283
|
+
server.tool(WORK_TOOLS.work_capacity_write, "Update the team capacity of a team member for a specific iteration in a project.", {
|
|
284
|
+
action: z.literal("update").describe("The action to perform. Only 'update' is supported."),
|
|
245
285
|
project: z.string().describe("The name or Id of the Azure DevOps project."),
|
|
246
286
|
team: z.string().describe("The name or Id of the Azure DevOps team."),
|
|
247
287
|
teamMemberId: z.string().describe("The team member Id for the specific team member."),
|
|
@@ -264,7 +304,6 @@ function configureWorkTools(server, _, connectionProvider) {
|
|
|
264
304
|
const connection = await connectionProvider();
|
|
265
305
|
const workApi = await connection.getWorkApi();
|
|
266
306
|
const teamContext = { project, team };
|
|
267
|
-
// Prepare the capacity update object
|
|
268
307
|
const capacityPatch = {
|
|
269
308
|
activities: activities.map((a) => ({
|
|
270
309
|
name: a.name,
|
|
@@ -275,12 +314,10 @@ function configureWorkTools(server, _, connectionProvider) {
|
|
|
275
314
|
end: new Date(d.end),
|
|
276
315
|
})),
|
|
277
316
|
};
|
|
278
|
-
// Update the team member's capacity
|
|
279
317
|
const updatedCapacity = await workApi.updateCapacityWithIdentityRef(capacityPatch, teamContext, iterationId, teamMemberId);
|
|
280
318
|
if (!updatedCapacity) {
|
|
281
319
|
return { content: [{ type: "text", text: "Failed to update team member capacity" }], isError: true };
|
|
282
320
|
}
|
|
283
|
-
// Simplify output
|
|
284
321
|
const simplifiedResult = {
|
|
285
322
|
teamMember: updatedCapacity.teamMember
|
|
286
323
|
? {
|
|
@@ -304,88 +341,5 @@ function configureWorkTools(server, _, connectionProvider) {
|
|
|
304
341
|
};
|
|
305
342
|
}
|
|
306
343
|
});
|
|
307
|
-
server.tool(WORK_TOOLS.get_iteration_capacities, "Get an iteration's capacity for all teams in iteration and project. If a project is not specified, you will be prompted to select one.", {
|
|
308
|
-
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."),
|
|
309
|
-
iterationId: z.string().describe("The Iteration Id to get capacity for."),
|
|
310
|
-
}, async ({ project, iterationId }) => {
|
|
311
|
-
try {
|
|
312
|
-
const connection = await connectionProvider();
|
|
313
|
-
let resolvedProject = project;
|
|
314
|
-
if (!resolvedProject) {
|
|
315
|
-
const result = await elicitProject(server, connection, "Select the Azure DevOps project to get iteration capacities for.");
|
|
316
|
-
if ("response" in result)
|
|
317
|
-
return result.response;
|
|
318
|
-
resolvedProject = result.resolved;
|
|
319
|
-
}
|
|
320
|
-
const workApi = await connection.getWorkApi();
|
|
321
|
-
const rawResults = await workApi.getTotalIterationCapacities(resolvedProject, iterationId);
|
|
322
|
-
if (!rawResults || !rawResults.teams || rawResults.teams.length === 0) {
|
|
323
|
-
return { content: [{ type: "text", text: "No iteration capacity assigned to the teams" }], isError: true };
|
|
324
|
-
}
|
|
325
|
-
return {
|
|
326
|
-
content: [{ type: "text", text: JSON.stringify(rawResults, null, 2) }],
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
catch (error) {
|
|
330
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
331
|
-
return {
|
|
332
|
-
content: [{ type: "text", text: `Error getting iteration capacities: ${errorMessage}` }],
|
|
333
|
-
isError: true,
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
server.tool(WORK_TOOLS.get_team_settings, "Get team settings including default iteration, backlog iteration, and default area path for a team. If a project or team is not specified, you will be prompted to select one.", {
|
|
338
|
-
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."),
|
|
339
|
-
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."),
|
|
340
|
-
}, async ({ project, team }) => {
|
|
341
|
-
try {
|
|
342
|
-
const connection = await connectionProvider();
|
|
343
|
-
let resolvedProject = project;
|
|
344
|
-
if (!resolvedProject) {
|
|
345
|
-
const result = await elicitProject(server, connection, "Select the Azure DevOps project to get team settings for.");
|
|
346
|
-
if ("response" in result)
|
|
347
|
-
return result.response;
|
|
348
|
-
resolvedProject = result.resolved;
|
|
349
|
-
}
|
|
350
|
-
let resolvedTeam = team;
|
|
351
|
-
if (!resolvedTeam) {
|
|
352
|
-
const result = await elicitTeam(server, connection, resolvedProject, "Select the Azure DevOps team to get settings for.");
|
|
353
|
-
if ("response" in result)
|
|
354
|
-
return result.response;
|
|
355
|
-
resolvedTeam = result.resolved;
|
|
356
|
-
}
|
|
357
|
-
const workApi = await connection.getWorkApi();
|
|
358
|
-
const teamContext = { project: resolvedProject, team: resolvedTeam };
|
|
359
|
-
const teamSettings = await workApi.getTeamSettings(teamContext);
|
|
360
|
-
if (!teamSettings) {
|
|
361
|
-
return { content: [{ type: "text", text: "No team settings found" }], isError: true };
|
|
362
|
-
}
|
|
363
|
-
const teamFieldValues = await workApi.getTeamFieldValues(teamContext);
|
|
364
|
-
const result = {
|
|
365
|
-
backlogIteration: teamSettings.backlogIteration,
|
|
366
|
-
defaultIteration: teamSettings.defaultIteration,
|
|
367
|
-
defaultIterationMacro: teamSettings.defaultIterationMacro,
|
|
368
|
-
backlogVisibilities: teamSettings.backlogVisibilities,
|
|
369
|
-
bugsBehavior: teamSettings.bugsBehavior,
|
|
370
|
-
workingDays: teamSettings.workingDays,
|
|
371
|
-
defaultAreaPath: teamFieldValues?.defaultValue,
|
|
372
|
-
areaPathField: teamFieldValues?.field,
|
|
373
|
-
areaPaths: teamFieldValues?.values,
|
|
374
|
-
};
|
|
375
|
-
return {
|
|
376
|
-
content: [
|
|
377
|
-
{ type: "text", text: `Project: ${resolvedProject}, Team: ${resolvedTeam}` },
|
|
378
|
-
{ type: "text", text: JSON.stringify(result, null, 2) },
|
|
379
|
-
],
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
catch (error) {
|
|
383
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
384
|
-
return {
|
|
385
|
-
content: [{ type: "text", text: `Error fetching team settings: ${errorMessage}` }],
|
|
386
|
-
isError: true,
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
344
|
}
|
|
391
345
|
export { WORK_TOOLS, configureWorkTools };
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "2.
|
|
1
|
+
export const packageVersion = "2.9.0";
|