@azure-devops/mcp 2.10.0-nightly.20260922 → 2.10.0-nightly.20260924
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.
|
@@ -32,6 +32,7 @@ const TOOL_ANNOTATIONS = {
|
|
|
32
32
|
repo_branch: READ_ONLY_ANNOTATIONS,
|
|
33
33
|
repo_create_branch: ADDITIVE_ANNOTATIONS,
|
|
34
34
|
repo_file: READ_ONLY_ANNOTATIONS,
|
|
35
|
+
repo_file_write: MUTATING_ANNOTATIONS,
|
|
35
36
|
repo_pull_request: READ_ONLY_ANNOTATIONS,
|
|
36
37
|
repo_pull_request_org: READ_ONLY_ANNOTATIONS,
|
|
37
38
|
repo_pull_request_thread: READ_ONLY_ANNOTATIONS,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Microsoft Corporation.
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
|
-
import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, CommentThreadStatus, GitPullRequestMergeStrategy, VersionControlRecursionType, } from "azure-devops-node-api/interfaces/GitInterfaces.js";
|
|
3
|
+
import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, CommentThreadStatus, GitPullRequestMergeStrategy, ItemContentType, VersionControlChangeType, 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 { extractAdoStreamError, getEnumKeys, streamToString, apiVersion } from "../utils.js";
|
|
@@ -13,6 +13,7 @@ const REPO_TOOLS = {
|
|
|
13
13
|
repo_pull_request_thread: "repo_pull_request_thread",
|
|
14
14
|
repo_branch: "repo_branch",
|
|
15
15
|
repo_file: "repo_file",
|
|
16
|
+
repo_file_write: "repo_file_write",
|
|
16
17
|
repo_search_commits: "repo_search_commits",
|
|
17
18
|
repo_pull_request_write: "repo_pull_request_write",
|
|
18
19
|
repo_pull_request_thread_write: "repo_pull_request_thread_write",
|
|
@@ -199,9 +200,13 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
199
200
|
project: z.string().optional().describe("Project ID or project name. Required for list_by_commits. Optional for get and list."),
|
|
200
201
|
includeWorkItemRefs: z.boolean().optional().default(false).describe("Whether to include work item references. Used for get."),
|
|
201
202
|
includeLabels: z.boolean().optional().default(false).describe("Whether to include labels. Used for get."),
|
|
202
|
-
includeChangedFiles: z
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
includeChangedFiles: z
|
|
204
|
+
.boolean()
|
|
205
|
+
.optional()
|
|
206
|
+
.default(false)
|
|
207
|
+
.describe("Whether to include the list of changed files. When changedFilesSummary returns a nextSkip greater than 0, pass nextSkip and nextTop back as skip and top for the next page. Used for get."),
|
|
208
|
+
top: z.coerce.number().default(100).describe("The maximum number of pull requests to return (list), or of changed files to return (get with includeChangedFiles). Defaults to 100."),
|
|
209
|
+
skip: z.coerce.number().default(0).describe("The number of pull requests to skip (list), or of changed files to skip (get with includeChangedFiles). Defaults to 0."),
|
|
205
210
|
created_by_me: z.boolean().default(false).describe("Filter pull requests created by the current user. Used for list."),
|
|
206
211
|
created_by_user: z.string().optional().describe("Filter pull requests created by a specific user email. Used for list."),
|
|
207
212
|
i_am_reviewer: z.boolean().default(false).describe("Filter pull requests where the current user is a reviewer. Used for list."),
|
|
@@ -249,7 +254,7 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
249
254
|
if (iterations?.length) {
|
|
250
255
|
const latestIteration = iterations[iterations.length - 1];
|
|
251
256
|
if (latestIteration.id != null) {
|
|
252
|
-
const changes = await gitApi.getPullRequestIterationChanges(repositoryId, pullRequestId, latestIteration.id, project);
|
|
257
|
+
const changes = await gitApi.getPullRequestIterationChanges(repositoryId, pullRequestId, latestIteration.id, project, top, skip);
|
|
253
258
|
enhancedResponse = {
|
|
254
259
|
...enhancedResponse,
|
|
255
260
|
changedFilesSummary: {
|
|
@@ -506,8 +511,7 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
506
511
|
versionType: z
|
|
507
512
|
.enum(fileVersionTypeStrings)
|
|
508
513
|
.optional()
|
|
509
|
-
.
|
|
510
|
-
.describe("How to interpret the version parameter. Used for get_content. Defaults to 'Commit'."),
|
|
514
|
+
.describe("How to interpret the version parameter. Defaults to 'Commit' for get_content and 'Branch' for list_directory."),
|
|
511
515
|
recursive: z.boolean().optional().default(false).describe("Whether to list items recursively. Used for list_directory. Defaults to false."),
|
|
512
516
|
recursionDepth: z.coerce.number().min(1).optional().default(1).describe("Maximum depth for recursive listing. Used for list_directory when recursive is true. Defaults to 1."),
|
|
513
517
|
}, async ({ action, repositoryId, path, project, version, versionType, recursive, recursionDepth }) => {
|
|
@@ -517,7 +521,7 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
517
521
|
if (action === "get_content") {
|
|
518
522
|
if (!path)
|
|
519
523
|
return { content: [{ type: "text", text: "path is required for get_content" }], isError: true };
|
|
520
|
-
const versionDescriptor = version
|
|
524
|
+
const versionDescriptor = buildVersionDescriptor(version, versionType ?? "Commit");
|
|
521
525
|
const stream = await gitApi.getItemText(repositoryId, path, project, undefined, undefined, undefined, undefined, false, versionDescriptor, true);
|
|
522
526
|
const content = await streamToString(stream);
|
|
523
527
|
const streamError = extractAdoStreamError(content);
|
|
@@ -527,7 +531,7 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
527
531
|
return createExternalContentResponse(content, "repository file");
|
|
528
532
|
}
|
|
529
533
|
if (action === "list_directory") {
|
|
530
|
-
const versionDescriptor = buildVersionDescriptor(version, versionType
|
|
534
|
+
const versionDescriptor = buildVersionDescriptor(version, versionType);
|
|
531
535
|
const clampedDepth = Math.min(Math.max(recursionDepth || 1, 1), 10);
|
|
532
536
|
const recursionType = recursive ? VersionControlRecursionType.Full : VersionControlRecursionType.OneLevel;
|
|
533
537
|
const items = await gitApi.getItems(repositoryId, project, path, recursionType, true, false, false, false, versionDescriptor);
|
|
@@ -568,6 +572,63 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
568
572
|
return { content: [{ type: "text", text: `Error with file operation: ${errorMessage}` }], isError: true };
|
|
569
573
|
}
|
|
570
574
|
});
|
|
575
|
+
// --- repo_file_write -------------------------------------------------------
|
|
576
|
+
server.tool(REPO_TOOLS.repo_file_write, "Create or update a text file in an existing repository branch and commit the change.", {
|
|
577
|
+
action: z.enum(["create", "update"]).describe("Whether to create a new file or update an existing file."),
|
|
578
|
+
repositoryId: z.string().describe("The ID or name of the repository."),
|
|
579
|
+
project: z.string().optional().describe("Project ID or project name. Required when repositoryId is a name."),
|
|
580
|
+
branchName: z.string().min(1).describe("The name of the existing branch to update, without the 'refs/heads/' prefix."),
|
|
581
|
+
path: z.string().min(1).describe("The absolute repository path of the file, for example '/src/example.ts'."),
|
|
582
|
+
content: z.string().describe("The complete UTF-8 text content to write to the file."),
|
|
583
|
+
commitMessage: z.string().min(1).describe("The commit message for the file change."),
|
|
584
|
+
expectedOldObjectId: z
|
|
585
|
+
.string()
|
|
586
|
+
.optional()
|
|
587
|
+
.describe("The expected current commit ID of the branch. When provided, the push fails if the branch has moved. If omitted, the current branch head is retrieved before pushing."),
|
|
588
|
+
}, async ({ action, repositoryId, project, branchName, path, content, commitMessage, expectedOldObjectId }) => {
|
|
589
|
+
try {
|
|
590
|
+
const connection = await connectionProvider();
|
|
591
|
+
const gitApi = await connection.getGitApi();
|
|
592
|
+
const refName = `refs/heads/${branchName}`;
|
|
593
|
+
const refs = await gitApi.getRefs(repositoryId, project, "heads/", false, false, undefined, false, undefined, branchName);
|
|
594
|
+
const branch = refs.find((ref) => ref.name === refName);
|
|
595
|
+
if (!branch?.objectId) {
|
|
596
|
+
return { content: [{ type: "text", text: `Error: Branch '${branchName}' not found in repository ${repositoryId}` }], isError: true };
|
|
597
|
+
}
|
|
598
|
+
if (expectedOldObjectId && expectedOldObjectId !== branch.objectId) {
|
|
599
|
+
return { content: [{ type: "text", text: `Error: Branch '${branchName}' has moved from expected commit ${expectedOldObjectId}` }], isError: true };
|
|
600
|
+
}
|
|
601
|
+
const oldObjectId = branch.objectId;
|
|
602
|
+
const filePath = path.startsWith("/") ? path : `/${path}`;
|
|
603
|
+
const push = await gitApi.createPush({
|
|
604
|
+
refUpdates: [{ name: refName, oldObjectId }],
|
|
605
|
+
commits: [
|
|
606
|
+
{
|
|
607
|
+
comment: commitMessage,
|
|
608
|
+
changes: [
|
|
609
|
+
{
|
|
610
|
+
changeType: action === "create" ? VersionControlChangeType.Add : VersionControlChangeType.Edit,
|
|
611
|
+
item: { path: filePath },
|
|
612
|
+
newContent: { content, contentType: ItemContentType.RawText },
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
}, repositoryId, project);
|
|
618
|
+
return {
|
|
619
|
+
content: [
|
|
620
|
+
{
|
|
621
|
+
type: "text",
|
|
622
|
+
text: JSON.stringify({ pushId: push.pushId, commitId: push.commits?.[0]?.commitId, branchName, path: filePath }, null, 2),
|
|
623
|
+
},
|
|
624
|
+
],
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
629
|
+
return { content: [{ type: "text", text: `Error writing repository file: ${errorMessage}` }], isError: true };
|
|
630
|
+
}
|
|
631
|
+
});
|
|
571
632
|
// --- repo_search_commits ---------------------------------------------------
|
|
572
633
|
server.tool(REPO_TOOLS.repo_search_commits, "Search commits with filtering by text, author, date range, and more.", {
|
|
573
634
|
searchText: z.string().describe("Keywords to search for in commit messages"),
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = "2.10.0-nightly.
|
|
1
|
+
export const packageVersion = "2.10.0-nightly.20260924";
|
package/package.json
CHANGED