@azure-devops/mcp 2.2.1-nightly.20251028 → 2.2.1-nightly.20251030
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 +3 -0
- package/dist/auth.js +1 -1
- package/dist/index.js +3 -3
- package/dist/org-tenants.js +7 -7
- package/dist/tools/work.js +132 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,9 @@ Interact with these Azure DevOps services:
|
|
|
55
55
|
- **work_list_team_iterations**: Retrieve a list of iterations for a specific team in a project.
|
|
56
56
|
- **work_create_iterations**: Create new iterations in a specified Azure DevOps project.
|
|
57
57
|
- **work_assign_iterations**: Assign existing iterations to a specific team in a project.
|
|
58
|
+
- **work_get_team_capacity**: Get the team capacity of a specific team and iteration in a project.
|
|
59
|
+
- **work_update_team_capacity**: Update the team capacity of a team member for a specific iteration in a project.
|
|
60
|
+
- **work_get_iteration_capacities**: Get an iteration's capacity for all teams in iteration and project.
|
|
58
61
|
|
|
59
62
|
### 📅 Work Items
|
|
60
63
|
|
package/dist/auth.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Licensed under the MIT License.
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import
|
|
6
|
+
import { getBearerHandler, WebApi } from "azure-devops-node-api";
|
|
7
7
|
import yargs from "yargs";
|
|
8
8
|
import { hideBin } from "yargs/helpers";
|
|
9
9
|
import { createAuthenticator } from "./auth.js";
|
|
@@ -57,8 +57,8 @@ export const enabledDomains = domainsManager.getEnabledDomains();
|
|
|
57
57
|
function getAzureDevOpsClient(getAzureDevOpsToken, userAgentComposer) {
|
|
58
58
|
return async () => {
|
|
59
59
|
const accessToken = await getAzureDevOpsToken();
|
|
60
|
-
const authHandler =
|
|
61
|
-
const connection = new
|
|
60
|
+
const authHandler = getBearerHandler(accessToken);
|
|
61
|
+
const connection = new WebApi(orgUrl, authHandler, undefined, {
|
|
62
62
|
productName: "AzureDevOps.MCP",
|
|
63
63
|
productVersion: packageVersion,
|
|
64
64
|
userAgent: userAgentComposer.userAgent,
|
package/dist/org-tenants.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
const CACHE_FILE =
|
|
3
|
+
import { readFile, writeFile } from "fs/promises";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
const CACHE_FILE = join(homedir(), ".ado_orgs.cache");
|
|
7
7
|
const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 1 week in milliseconds
|
|
8
8
|
async function loadCache() {
|
|
9
9
|
try {
|
|
10
|
-
const cacheData = await
|
|
10
|
+
const cacheData = await readFile(CACHE_FILE, "utf-8");
|
|
11
11
|
return JSON.parse(cacheData);
|
|
12
12
|
}
|
|
13
|
-
catch
|
|
13
|
+
catch {
|
|
14
14
|
// Cache file doesn't exist or is invalid, return empty cache
|
|
15
15
|
return {};
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
async function trySavingCache(cache) {
|
|
19
19
|
try {
|
|
20
|
-
await
|
|
20
|
+
await writeFile(CACHE_FILE, JSON.stringify(cache, null, 2), "utf-8");
|
|
21
21
|
}
|
|
22
22
|
catch (error) {
|
|
23
23
|
console.error("Failed to save org tenants cache:", error);
|
package/dist/tools/work.js
CHANGED
|
@@ -6,6 +6,9 @@ const WORK_TOOLS = {
|
|
|
6
6
|
list_team_iterations: "work_list_team_iterations",
|
|
7
7
|
create_iterations: "work_create_iterations",
|
|
8
8
|
assign_iterations: "work_assign_iterations",
|
|
9
|
+
get_team_capacity: "work_get_team_capacity",
|
|
10
|
+
update_team_capacity: "work_update_team_capacity",
|
|
11
|
+
get_iteration_capacities: "work_get_iteration_capacities",
|
|
9
12
|
};
|
|
10
13
|
function configureWorkTools(server, _, connectionProvider) {
|
|
11
14
|
server.tool(WORK_TOOLS.list_team_iterations, "Retrieve a list of iterations for a specific team in a project.", {
|
|
@@ -110,5 +113,134 @@ function configureWorkTools(server, _, connectionProvider) {
|
|
|
110
113
|
};
|
|
111
114
|
}
|
|
112
115
|
});
|
|
116
|
+
server.tool(WORK_TOOLS.get_team_capacity, "Get the team capacity of a specific team and iteration in a project.", {
|
|
117
|
+
project: z.string().describe("The name or Id of the Azure DevOps project."),
|
|
118
|
+
team: z.string().describe("The name or Id of the Azure DevOps team."),
|
|
119
|
+
iterationId: z.string().describe("The Iteration Id to get capacity for."),
|
|
120
|
+
}, async ({ project, team, iterationId }) => {
|
|
121
|
+
try {
|
|
122
|
+
const connection = await connectionProvider();
|
|
123
|
+
const workApi = await connection.getWorkApi();
|
|
124
|
+
const teamContext = { project, team };
|
|
125
|
+
const rawResults = await workApi.getCapacitiesWithIdentityRefAndTotals(teamContext, iterationId);
|
|
126
|
+
if (!rawResults || rawResults.teamMembers?.length === 0) {
|
|
127
|
+
return { content: [{ type: "text", text: "No team capacity assigned to the team" }], isError: true };
|
|
128
|
+
}
|
|
129
|
+
// Remove unwanted fields from teamMember and url
|
|
130
|
+
const simplifiedResults = {
|
|
131
|
+
...rawResults,
|
|
132
|
+
teamMembers: (rawResults.teamMembers || []).map((member) => {
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
134
|
+
const { url, ...rest } = member;
|
|
135
|
+
return {
|
|
136
|
+
...rest,
|
|
137
|
+
teamMember: member.teamMember
|
|
138
|
+
? {
|
|
139
|
+
displayName: member.teamMember.displayName,
|
|
140
|
+
id: member.teamMember.id,
|
|
141
|
+
uniqueName: member.teamMember.uniqueName,
|
|
142
|
+
}
|
|
143
|
+
: undefined,
|
|
144
|
+
};
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
content: [{ type: "text", text: JSON.stringify(simplifiedResults, null, 2) }],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: "text", text: `Error getting team capacity: ${errorMessage}` }],
|
|
155
|
+
isError: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
server.tool(WORK_TOOLS.update_team_capacity, "Update the team capacity of a team member for a specific iteration in a project.", {
|
|
160
|
+
project: z.string().describe("The name or Id of the Azure DevOps project."),
|
|
161
|
+
team: z.string().describe("The name or Id of the Azure DevOps team."),
|
|
162
|
+
teamMemberId: z.string().describe("The team member Id for the specific team member."),
|
|
163
|
+
iterationId: z.string().describe("The Iteration Id to update the capacity for."),
|
|
164
|
+
activities: z
|
|
165
|
+
.array(z.object({
|
|
166
|
+
name: z.string().describe("The name of the activity (e.g., 'Development')."),
|
|
167
|
+
capacityPerDay: z.number().describe("The capacity per day for this activity."),
|
|
168
|
+
}))
|
|
169
|
+
.describe("Array of activities and their daily capacities for the team member."),
|
|
170
|
+
daysOff: z
|
|
171
|
+
.array(z.object({
|
|
172
|
+
start: z.string().describe("Start date of the day off in ISO format."),
|
|
173
|
+
end: z.string().describe("End date of the day off in ISO format."),
|
|
174
|
+
}))
|
|
175
|
+
.optional()
|
|
176
|
+
.describe("Array of days off for the team member, each with a start and end date in ISO format."),
|
|
177
|
+
}, async ({ project, team, teamMemberId, iterationId, activities, daysOff }) => {
|
|
178
|
+
try {
|
|
179
|
+
const connection = await connectionProvider();
|
|
180
|
+
const workApi = await connection.getWorkApi();
|
|
181
|
+
const teamContext = { project, team };
|
|
182
|
+
// Prepare the capacity update object
|
|
183
|
+
const capacityPatch = {
|
|
184
|
+
activities: activities.map((a) => ({
|
|
185
|
+
name: a.name,
|
|
186
|
+
capacityPerDay: a.capacityPerDay,
|
|
187
|
+
})),
|
|
188
|
+
daysOff: (daysOff || []).map((d) => ({
|
|
189
|
+
start: new Date(d.start),
|
|
190
|
+
end: new Date(d.end),
|
|
191
|
+
})),
|
|
192
|
+
};
|
|
193
|
+
// Update the team member's capacity
|
|
194
|
+
const updatedCapacity = await workApi.updateCapacityWithIdentityRef(capacityPatch, teamContext, iterationId, teamMemberId);
|
|
195
|
+
if (!updatedCapacity) {
|
|
196
|
+
return { content: [{ type: "text", text: "Failed to update team member capacity" }], isError: true };
|
|
197
|
+
}
|
|
198
|
+
// Simplify output
|
|
199
|
+
const simplifiedResult = {
|
|
200
|
+
teamMember: updatedCapacity.teamMember
|
|
201
|
+
? {
|
|
202
|
+
displayName: updatedCapacity.teamMember.displayName,
|
|
203
|
+
id: updatedCapacity.teamMember.id,
|
|
204
|
+
uniqueName: updatedCapacity.teamMember.uniqueName,
|
|
205
|
+
}
|
|
206
|
+
: undefined,
|
|
207
|
+
activities: updatedCapacity.activities,
|
|
208
|
+
daysOff: updatedCapacity.daysOff,
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
content: [{ type: "text", text: JSON.stringify(simplifiedResult, null, 2) }],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
216
|
+
return {
|
|
217
|
+
content: [{ type: "text", text: `Error updating team capacity: ${errorMessage}` }],
|
|
218
|
+
isError: true,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
server.tool(WORK_TOOLS.get_iteration_capacities, "Get an iteration's capacity for all teams in iteration and project.", {
|
|
223
|
+
project: z.string().describe("The name or Id of the Azure DevOps project."),
|
|
224
|
+
iterationId: z.string().describe("The Iteration Id to get capacity for."),
|
|
225
|
+
}, async ({ project, iterationId }) => {
|
|
226
|
+
try {
|
|
227
|
+
const connection = await connectionProvider();
|
|
228
|
+
const workApi = await connection.getWorkApi();
|
|
229
|
+
const rawResults = await workApi.getTotalIterationCapacities(project, iterationId);
|
|
230
|
+
if (!rawResults || !rawResults.teams || rawResults.teams.length === 0) {
|
|
231
|
+
return { content: [{ type: "text", text: "No iteration capacity assigned to the teams" }], isError: true };
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
content: [{ type: "text", text: JSON.stringify(rawResults, null, 2) }],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
239
|
+
return {
|
|
240
|
+
content: [{ type: "text", text: `Error getting iteration capacities: ${errorMessage}` }],
|
|
241
|
+
isError: true,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
});
|
|
113
245
|
}
|
|
114
246
|
export { WORK_TOOLS, configureWorkTools };
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "2.2.1-nightly.
|
|
1
|
+
export const packageVersion = "2.2.1-nightly.20251030";
|