Skip to content

Commit ff94eac

Browse files
authored
Better filtering in branch tools (#440)
Adding option to filter branches by name when listing branches. When getting branches, not all branches will be fetched and then filtered on the client side, but we'll only fetch the relevant branch. ## GitHub issue number / ## **Associated Risks** ## ✅ **PR Checklist** - [x] **I have read the [contribution guidelines](https://github.com/microsoft/azure-devops-mcp/blob/main/CONTRIBUTING.md)** - [x] **I have read the [code of conduct guidelines](https://github.com/microsoft/azure-devops-mcp/blob/main/CODE_OF_CONDUCT.md)** - [x] Title of the pull request is clear and informative. - [x] 👌 Code hygiene - [x] 🔭 Telemetry added, updated, or N/A - [x] 📄 Documentation added, updated, or N/A - [x] 🛡️ Automated tests added, or N/A ## 🧪 **How did you test it?** Manually tested, and updated tests.
1 parent 1ce35e4 commit ff94eac

File tree

2 files changed

+12
-9
lines changed

2 files changed

+12
-9
lines changed

src/tools/repos.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -486,11 +486,12 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
486486
{
487487
repositoryId: z.string().describe("The ID of the repository where the branches are located."),
488488
top: z.number().default(100).describe("The maximum number of branches to return. Defaults to 100."),
489+
filterContains: z.string().optional().describe("Filter to find branches that contain this string in their name."),
489490
},
490-
async ({ repositoryId, top }) => {
491+
async ({ repositoryId, top, filterContains }) => {
491492
const connection = await connectionProvider();
492493
const gitApi = await connection.getGitApi();
493-
const branches = await gitApi.getRefs(repositoryId, undefined);
494+
const branches = await gitApi.getRefs(repositoryId, undefined, "heads/", undefined, undefined, undefined, undefined, undefined, filterContains);
494495

495496
const filteredBranches = branchesFilterOutIrrelevantProperties(branches, top);
496497

@@ -506,11 +507,12 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
506507
{
507508
repositoryId: z.string().describe("The ID of the repository where the branches are located."),
508509
top: z.number().default(100).describe("The maximum number of branches to return."),
510+
filterContains: z.string().optional().describe("Filter to find branches that contain this string in their name."),
509511
},
510-
async ({ repositoryId, top }) => {
512+
async ({ repositoryId, top, filterContains }) => {
511513
const connection = await connectionProvider();
512514
const gitApi = await connection.getGitApi();
513-
const branches = await gitApi.getRefs(repositoryId, undefined, undefined, undefined, undefined, true);
515+
const branches = await gitApi.getRefs(repositoryId, undefined, "heads/", undefined, undefined, true, undefined, undefined, filterContains);
514516

515517
const filteredBranches = branchesFilterOutIrrelevantProperties(branches, top);
516518

@@ -554,8 +556,8 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
554556
async ({ repositoryId, branchName }) => {
555557
const connection = await connectionProvider();
556558
const gitApi = await connection.getGitApi();
557-
const branches = await gitApi.getRefs(repositoryId);
558-
const branch = branches?.find((branch) => branch.name === `refs/heads/${branchName}`);
559+
const branches = await gitApi.getRefs(repositoryId, undefined, "heads/", false, false, undefined, false, undefined, branchName);
560+
const branch = branches.find((branch) => branch.name === `refs/heads/${branchName}` || branch.name === branchName);
559561
if (!branch) {
560562
return {
561563
content: [
@@ -564,6 +566,7 @@ function configureRepoTools(server: McpServer, tokenProvider: () => Promise<Acce
564566
text: `Branch ${branchName} not found in repository ${repositoryId}`,
565567
},
566568
],
569+
isError: true,
567570
};
568571
}
569572
return {

test/src/tools/repos.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ describe("repos tools", () => {
763763

764764
const result = await handler(params);
765765

766-
expect(mockGitApi.getRefs).toHaveBeenCalledWith("repo123", undefined);
766+
expect(mockGitApi.getRefs).toHaveBeenCalledWith("repo123", undefined, "heads/", undefined, undefined, undefined, undefined, undefined, undefined);
767767

768768
const expectedResult = ["main", "feature-2", "feature-1"]; // Sorted reverse alphabetically
769769
expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
@@ -788,7 +788,7 @@ describe("repos tools", () => {
788788

789789
const result = await handler(params);
790790

791-
expect(mockGitApi.getRefs).toHaveBeenCalledWith("repo123", undefined, undefined, undefined, undefined, true);
791+
expect(mockGitApi.getRefs).toHaveBeenCalledWith("repo123", undefined, "heads/", undefined, undefined, true, undefined, undefined, undefined);
792792

793793
const expectedResult = ["my-feature", "main"];
794794
expect(result.content[0].text).toBe(JSON.stringify(expectedResult, null, 2));
@@ -882,7 +882,7 @@ describe("repos tools", () => {
882882

883883
const result = await handler(params);
884884

885-
expect(mockGitApi.getRefs).toHaveBeenCalledWith("repo123");
885+
expect(mockGitApi.getRefs).toHaveBeenCalledWith("repo123", undefined, "heads/", false, false, undefined, false, undefined, "main");
886886
expect(result.content[0].text).toBe(JSON.stringify(mockBranches[0], null, 2));
887887
});
888888

0 commit comments

Comments
 (0)