Skip to content

Add tool mcp_ado_repo_get_pull_request_changes#801

Merged
nikolapeja6 merged 8 commits intomicrosoft:mainfrom
posidron:user/chdieh/mcp_ado_repo_get_pull_request_changes
Mar 23, 2026
Merged

Add tool mcp_ado_repo_get_pull_request_changes#801
nikolapeja6 merged 8 commits intomicrosoft:mainfrom
posidron:user/chdieh/mcp_ado_repo_get_pull_request_changes

Conversation

@posidron
Copy link
Copy Markdown
Contributor

Replace by description of the work done

GitHub issue number

Associated Risks

Replace by possible risks this pull request can bring you might have thought of

PR Checklist

  • I have read the contribution guidelines
  • I have read the code of conduct guidelines
  • Title of the pull request is clear and informative.
  • 👌 Code hygiene
  • 🔭 Telemetry added, updated, or N/A
  • 📄 Documentation added, updated, or N/A
  • 🛡️ Automated tests added, or N/A

🧪 How did you test it?

Replace with use cases tested and models used

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Dec 19, 2025

Codecov Report

❌ Patch coverage is 62.67606% with 53 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@a60f88d). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/tools/repositories.ts 60.44% 30 Missing and 23 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #801   +/-   ##
=======================================
  Coverage        ?   91.27%           
=======================================
  Files           ?       17           
  Lines           ?     1834           
  Branches        ?      413           
=======================================
  Hits            ?     1674           
  Misses          ?       56           
  Partials        ?      104           
Flag Coverage Δ
unittests 91.27% <62.67%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@danhellem danhellem added Do Not Merge ❌ do not merge this pull request Needs Review 👓 needs review by the product team labels Dec 22, 2025
@danhellem
Copy link
Copy Markdown
Collaborator

@posidron this PR has been in draft for over 3 weeks. If we do not see any progress by 1/13, we will go ahead and close the PR. Hope to hear from you soon.

Also please follow guidance as documented in Contributions file. We request an Issue be created first for approval before a PR will be considered.

@danhellem
Copy link
Copy Markdown
Collaborator

Closing this draft due to no response from author

@danhellem danhellem closed this Jan 13, 2026
@posidron
Copy link
Copy Markdown
Contributor Author

@danhellem I created the issue here: #868

@danhellem danhellem reopened this Jan 29, 2026
@posidron posidron marked this pull request as ready for review January 29, 2026 22:33
@posidron posidron requested a review from a team as a code owner January 29, 2026 22:33
@danhellem
Copy link
Copy Markdown
Collaborator

@posidron I think we agree that this is a good idea. @nikolapeja6 on our team is going to spend some time on this reviewing your PR and testing to make sure the tool works at scale with large PRs. It will be a week or two before have any results.

Stand by

Comment thread docs/TOOLSET.md

**Notes**:

- If `iterationId` is not specified, returns changes for the latest iteration
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to get all changes from one PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way how I use it is in combination with https://dev.azure.com/microsoft/Edge/_git/chromium.src/pullrequest/14659824 - in VSCode you would then write /create-security-review <ADO_PR_URL>.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new repositories tool for retrieving pull request iteration changes, including optional enriched diff metadata and line-level content, and documents/tests the new capability.

Changes:

  • Introduces repo_get_pull_request_changes in src/tools/repositories.ts, with options for iteration selection, pagination, diff enrichment, and line-content enrichment.
  • Adds extensive Jest coverage for the new tool’s behavior (iteration resolution, pagination, diff/line-content paths, and error handling).
  • Updates docs/TOOLSET.md to list and describe the new tool.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 6 comments.

File Description
src/tools/repositories.ts Adds the new PR changes tool and supporting stream-to-string + diff/line-content enrichment logic.
test/src/tools/repositories.test.ts Adds tests validating registration and behavior of the new tool, including diff and line-content enrichment scenarios.
docs/TOOLSET.md Documents the new tool, parameters, and behavioral notes.
Comments suppressed due to low confidence (2)

src/tools/repositories.ts:1148

  • includeLineContent enrichment reads lineDiffBlocks fields named originalLineNumberStart/originalLinesCount and modifiedLineNumberStart/modifiedLinesCount, but other added tests in this PR use originalLineStart/originalLineCount and modifiedLineStart/modifiedLineCount. If the actual getFileDiffs response uses the latter shape, this code will never attach originalLines/modifiedLines. Consider supporting both field name variants (or switch to the correct one per the SDK type) and make the tests consistent.
                            // Add original (base) lines if they exist
                            if (block.originalLineNumberStart && block.originalLinesCount) {
                              const startIdx = block.originalLineNumberStart - 1;
                              const endIdx = startIdx + block.originalLinesCount;
                              enrichedBlock.originalLines = baseLines.slice(startIdx, endIdx);
                            }

                            // Add modified (target) lines if they exist
                            if (block.modifiedLineNumberStart && block.modifiedLinesCount) {
                              const startIdx = block.modifiedLineNumberStart - 1;
                              const endIdx = startIdx + block.modifiedLinesCount;
                              enrichedBlock.modifiedLines = targetLines.slice(startIdx, endIdx);

test/src/tools/repositories.test.ts:47

  • The mockGitApi type definition is missing methods that are added to the mock object (getPullRequestIteration, getFileDiffs, getItemText). With TypeScript's excess property checks, assigning the object literal with these extra keys will fail compilation. Extend the mockGitApi type to include these methods (or loosen the typing, e.g., with a mapped type/Partial, or cast the object literal).
  let mockGitApi: {
    updatePullRequest: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    createPullRequest: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    createPullRequestReviewers: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    deletePullRequestReviewer: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getRepositories: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getPullRequests: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getPullRequestsByProject: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getThreads: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getComments: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getRefs: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getPullRequest: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getPullRequestLabels: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    createPullRequestLabel: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    deletePullRequestLabels: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    createComment: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    createThread: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    updateThread: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getCommits: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getPullRequestQuery: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    updateRefs: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getPullRequestIterationChanges: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
    getPullRequestIterations: jest.MockedFunction<(...args: unknown[]) => Promise<unknown>>;
  };

Comment thread src/tools/repositories.ts Outdated
// If no iteration ID provided, get the latest iteration
let targetIterationId = iterationId;
let targetIteration;
if (!targetIterationId) {
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iterationId is treated as “not provided” via a falsy check (if (!targetIterationId)), which will also treat 0 as missing and fetch the latest iteration instead. Use a null/undefined check (e.g., iterationId == null) to distinguish an omitted value from 0.

Suggested change
if (!targetIterationId) {
if (targetIterationId == null) {

Copilot uses AI. Check for mistakes.
Comment thread src/tools/repositories.ts Outdated
Comment on lines +1049 to +1051
// Exclude added (1) and deleted (16) files as they don't have both versions to diff
const fileDiffParams = changes.changeEntries
.filter((entry) => entry.item?.path && entry.changeType !== 1 && entry.changeType !== 16) // Only modified files
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change-type filtering for fileDiffParams assumes numeric changeType values (excluding 1 and 16), but the added tests in this PR use string values like "add"/"edit" for changeType. If the API (or mocks) provide strings, added/deleted files won’t be excluded and the behavior won’t match the comment. Normalize changeType to the expected enum representation (or handle both string and numeric forms) and align tests/mocks accordingly.

This issue also appears on line 1137 of the same file.

Suggested change
// Exclude added (1) and deleted (16) files as they don't have both versions to diff
const fileDiffParams = changes.changeEntries
.filter((entry) => entry.item?.path && entry.changeType !== 1 && entry.changeType !== 16) // Only modified files
// Exclude added (1 / "add") and deleted (16 / "delete") files as they don't have both versions to diff
const fileDiffParams = changes.changeEntries
.filter((entry) => {
if (!entry.item?.path) {
return false;
}
const changeType = entry.changeType as unknown;
if (typeof changeType === "number") {
// Numeric enum values: 1 = add, 16 = delete
return changeType !== 1 && changeType !== 16;
}
if (typeof changeType === "string") {
const normalized = changeType.toLowerCase();
// String representations: exclude adds/deletes, allow edits/other modifications
return normalized !== "add" && normalized !== "delete";
}
// If changeType is an unexpected type, be conservative and include it
return true;
}) // Only modified files

Copilot uses AI. Check for mistakes.
Comment thread src/tools/repositories.ts
Comment thread src/tools/repositories.ts Outdated
Comment on lines +1119 to +1125
// Check if response is an error (Azure DevOps returns JSON error in stream)
if (baseText.startsWith("{") && baseText.includes("innerException")) {
throw new Error(`Failed to fetch base file content: ${baseText}`);
}
if (targetText.startsWith("{") && targetText.includes("innerException")) {
throw new Error(`Failed to fetch target file content: ${targetText}`);
}
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The “Azure DevOps error JSON” detection treats any file content that starts with { and contains innerException as an error. This can misclassify legitimate JSON/code files containing that substring. A more robust approach would be to JSON.parse and check for expected error keys (e.g., message plus a known schema), or rely on HTTP status/SDK errors instead of content heuristics.

Copilot uses AI. Check for mistakes.
Comment thread src/tools/repositories.ts Outdated
Comment on lines +34 to +38
const chunks: Buffer[] = [];
return new Promise((resolve, reject) => {
stream.on("data", (chunk: Buffer) => chunks.push(chunk));
stream.on("error", reject);
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

streamToString is now duplicated (there is also a streamToString helper in src/tools/wiki.ts). Consider extracting a shared utility (e.g., in src/utils.ts) to avoid divergent implementations (buffer-based vs setEncoding) and make stream handling consistent across tools.

Suggested change
const chunks: Buffer[] = [];
return new Promise((resolve, reject) => {
stream.on("data", (chunk: Buffer) => chunks.push(chunk));
stream.on("error", reject);
stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
return new Promise((resolve, reject) => {
let data = "";
stream.setEncoding("utf8");
stream.on("data", (chunk: string) => {
data += chunk;
});
stream.on("error", reject);
stream.on("end", () => resolve(data));

Copilot uses AI. Check for mistakes.
Comment thread test/src/tools/repositories.test.ts
nikolapeja6
nikolapeja6 previously approved these changes Feb 24, 2026
Copy link
Copy Markdown
Member

@nikolapeja6 nikolapeja6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Copy Markdown

@quanghuynh10111-png quanghuynh10111-png left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • [ ]

@danhellem
Copy link
Copy Markdown
Collaborator

Lets please get the conflicts resolved and comments addressed. If not activity before 3/23, we will close this PR

@posidron
Copy link
Copy Markdown
Contributor Author

@danhellem I got side tracked, I can look at the merge conflicts tomorrow. Can you please not close it.

…epo_get_pull_request_changes

# Conflicts:
#	src/tools/repositories.ts
#	test/src/tools/repositories.test.ts
@nikolapeja6 nikolapeja6 merged commit 5af70f5 into microsoft:main Mar 23, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Review 👓 needs review by the product team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants