@azure-devops/mcp 2.4.0-nightly.20260222 → 2.4.0-nightly.20260224
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools/repositories.js +113 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, CommentThreadStatus, GitPullRequestMergeStrategy, } from "azure-devops-node-api/interfaces/GitInterfaces.js";
|
|
3
|
+
import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, CommentThreadStatus, GitPullRequestMergeStrategy, VersionControlRecursionType, } from "azure-devops-node-api/interfaces/GitInterfaces.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { getCurrentUserDetails, getUserIdFromEmail } from "./auth.js";
|
|
6
6
|
import { getEnumKeys } from "../utils.js";
|
|
@@ -23,6 +23,8 @@ const REPO_TOOLS = {
|
|
|
23
23
|
update_pull_request_thread: "repo_update_pull_request_thread",
|
|
24
24
|
search_commits: "repo_search_commits",
|
|
25
25
|
list_pull_requests_by_commits: "repo_list_pull_requests_by_commits",
|
|
26
|
+
vote_pull_request: "repo_vote_pull_request",
|
|
27
|
+
list_directory: "repo_list_directory",
|
|
26
28
|
};
|
|
27
29
|
function branchesFilterOutIrrelevantProperties(branches, top) {
|
|
28
30
|
return branches
|
|
@@ -103,6 +105,21 @@ function trimPullRequest(pr, includeDescription = false) {
|
|
|
103
105
|
project: pr.repository?.project?.name,
|
|
104
106
|
};
|
|
105
107
|
}
|
|
108
|
+
// Helper function to build a version descriptor from branch or commit
|
|
109
|
+
function buildVersionDescriptor(version, versionType) {
|
|
110
|
+
if (!version) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const versionTypeMap = {
|
|
114
|
+
Branch: GitVersionType.Branch,
|
|
115
|
+
Commit: GitVersionType.Commit,
|
|
116
|
+
Tag: GitVersionType.Tag,
|
|
117
|
+
};
|
|
118
|
+
return {
|
|
119
|
+
version: version,
|
|
120
|
+
versionType: versionTypeMap[versionType || "Branch"] ?? GitVersionType.Branch,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
106
123
|
function configureRepoTools(server, tokenProvider, connectionProvider, userAgentProvider) {
|
|
107
124
|
server.tool(REPO_TOOLS.create_pull_request, "Create a new pull request.", {
|
|
108
125
|
repositoryId: z.string().describe("The ID of the repository where the pull request will be created."),
|
|
@@ -1150,5 +1167,100 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
1150
1167
|
};
|
|
1151
1168
|
}
|
|
1152
1169
|
});
|
|
1170
|
+
server.tool(REPO_TOOLS.vote_pull_request, "Cast a vote on a pull request. Automatically adds the current user as a reviewer if they are not already one.", {
|
|
1171
|
+
repositoryId: z.string().describe("The ID of the repository."),
|
|
1172
|
+
pullRequestId: z.number().describe("The ID of the pull request."),
|
|
1173
|
+
vote: z.enum(["Approved", "ApprovedWithSuggestions", "NoVote", "WaitingForAuthor", "Rejected"]).describe("The vote to cast: Approved(10), Suggestions(5), None(0), Waiting(-5), Rejected(-10)."),
|
|
1174
|
+
}, async ({ repositoryId, pullRequestId, vote }) => {
|
|
1175
|
+
const connection = await connectionProvider();
|
|
1176
|
+
const gitApi = await connection.getGitApi();
|
|
1177
|
+
const userDetails = await getCurrentUserDetails(tokenProvider, connectionProvider, userAgentProvider);
|
|
1178
|
+
const userId = userDetails.authenticatedUser.id;
|
|
1179
|
+
if (!userId) {
|
|
1180
|
+
throw new Error("Could not determine authenticated user ID.");
|
|
1181
|
+
}
|
|
1182
|
+
const voteMap = {
|
|
1183
|
+
Approved: 10,
|
|
1184
|
+
ApprovedWithSuggestions: 5,
|
|
1185
|
+
NoVote: 0,
|
|
1186
|
+
WaitingForAuthor: -5,
|
|
1187
|
+
Rejected: -10,
|
|
1188
|
+
};
|
|
1189
|
+
await gitApi.createPullRequestReviewer({ vote: voteMap[vote], id: userId }, repositoryId, pullRequestId, userId);
|
|
1190
|
+
return {
|
|
1191
|
+
content: [
|
|
1192
|
+
{
|
|
1193
|
+
type: "text",
|
|
1194
|
+
text: `Successfully cast vote '${vote}' on PR #${pullRequestId}.`,
|
|
1195
|
+
},
|
|
1196
|
+
],
|
|
1197
|
+
};
|
|
1198
|
+
});
|
|
1199
|
+
server.tool(REPO_TOOLS.list_directory, "List files and folders in a directory within a repository. Useful for exploring the structure of a codebase or finding related files.", {
|
|
1200
|
+
repositoryId: z.string().describe("The ID or name of the repository."),
|
|
1201
|
+
path: z.string().optional().default("/").describe("The directory path to list (e.g., '/src' or '/src/components'). Defaults to repository root."),
|
|
1202
|
+
project: z.string().optional().describe("Project ID or name. Required if repositoryId is a name rather than a GUID."),
|
|
1203
|
+
version: z.string().optional().describe("The version identifier - branch name (e.g., 'main'), tag name, or commit SHA. Defaults to the repository's default branch."),
|
|
1204
|
+
versionType: z.enum(["Branch", "Commit", "Tag"]).optional().default("Branch").describe("The type of version identifier: 'Branch', 'Commit', or 'Tag'. Defaults to 'Branch'."),
|
|
1205
|
+
recursive: z.boolean().optional().default(false).describe("Whether to list items recursively. Defaults to false."),
|
|
1206
|
+
recursionDepth: z.number().optional().default(1).describe("Maximum depth for recursive listing (1-10). Only applies when recursive is true. Defaults to 1."),
|
|
1207
|
+
}, async ({ repositoryId, path, project, version, versionType, recursive, recursionDepth }) => {
|
|
1208
|
+
try {
|
|
1209
|
+
const connection = await connectionProvider();
|
|
1210
|
+
const gitApi = await connection.getGitApi();
|
|
1211
|
+
const versionDescriptor = buildVersionDescriptor(version, versionType);
|
|
1212
|
+
const clampedDepth = Math.min(Math.max(recursionDepth || 1, 1), 10);
|
|
1213
|
+
let recursionType = VersionControlRecursionType.OneLevel;
|
|
1214
|
+
if (recursive) {
|
|
1215
|
+
recursionType = VersionControlRecursionType.Full;
|
|
1216
|
+
}
|
|
1217
|
+
const items = await gitApi.getItems(repositoryId, project, path, recursionType, true, false, false, false, versionDescriptor);
|
|
1218
|
+
if (!items || items.length === 0) {
|
|
1219
|
+
return {
|
|
1220
|
+
content: [{ type: "text", text: `No items found at path: ${path}` }],
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
let filteredItems = items;
|
|
1224
|
+
if (recursive && clampedDepth < 10) {
|
|
1225
|
+
const basePath = path === "/" ? "" : path;
|
|
1226
|
+
const baseDepth = basePath.split("/").filter((p) => p).length;
|
|
1227
|
+
filteredItems = items.filter((item) => {
|
|
1228
|
+
if (!item.path)
|
|
1229
|
+
return false;
|
|
1230
|
+
const itemDepth = item.path.split("/").filter((p) => p).length;
|
|
1231
|
+
return itemDepth <= baseDepth + clampedDepth;
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
const formattedItems = filteredItems.map((item) => ({
|
|
1235
|
+
path: item.path,
|
|
1236
|
+
isFolder: item.isFolder,
|
|
1237
|
+
gitObjectType: item.gitObjectType,
|
|
1238
|
+
commitId: item.commitId,
|
|
1239
|
+
contentMetadata: item.contentMetadata
|
|
1240
|
+
? {
|
|
1241
|
+
contentType: item.contentMetadata.contentType,
|
|
1242
|
+
fileName: item.contentMetadata.fileName,
|
|
1243
|
+
}
|
|
1244
|
+
: undefined,
|
|
1245
|
+
}));
|
|
1246
|
+
const response = {
|
|
1247
|
+
count: formattedItems.length,
|
|
1248
|
+
path: path,
|
|
1249
|
+
recursive: recursive,
|
|
1250
|
+
recursionDepth: recursive ? clampedDepth : undefined,
|
|
1251
|
+
items: formattedItems,
|
|
1252
|
+
};
|
|
1253
|
+
return {
|
|
1254
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
catch (error) {
|
|
1258
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
1259
|
+
return {
|
|
1260
|
+
content: [{ type: "text", text: `Error listing directory: ${errorMessage}` }],
|
|
1261
|
+
isError: true,
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1153
1265
|
}
|
|
1154
1266
|
export { REPO_TOOLS, configureRepoTools };
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "2.4.0-nightly.
|
|
1
|
+
export const packageVersion = "2.4.0-nightly.20260224";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure-devops/mcp",
|
|
3
|
-
"version": "2.4.0-nightly.
|
|
3
|
+
"version": "2.4.0-nightly.20260224",
|
|
4
4
|
"mcpName": "microsoft.com/azure-devops",
|
|
5
5
|
"description": "MCP server for interacting with Azure DevOps",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@azure/identity": "^4.10.0",
|
|
42
|
-
"@azure/msal-node": "^
|
|
43
|
-
"@modelcontextprotocol/sdk": "1.
|
|
42
|
+
"@azure/msal-node": "^5.0.4",
|
|
43
|
+
"@modelcontextprotocol/sdk": "1.27.0",
|
|
44
44
|
"azure-devops-extension-api": "^4.264.0",
|
|
45
45
|
"azure-devops-extension-sdk": "^4.0.2",
|
|
46
46
|
"azure-devops-node-api": "^15.1.2",
|