@azure-devops/mcp 2.2.1-nightly.20251104 → 2.2.1-nightly.20251105

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.
@@ -39,6 +39,7 @@ function trimPullRequestThread(thread) {
39
39
  lastUpdatedDate: thread.lastUpdatedDate,
40
40
  status: thread.status,
41
41
  comments: trimComments(thread.comments),
42
+ threadContext: thread.threadContext,
42
43
  };
43
44
  }
44
45
  /**
@@ -106,7 +107,7 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
106
107
  sourceRefName: z.string().describe("The source branch name for the pull request, e.g., 'refs/heads/feature-branch'."),
107
108
  targetRefName: z.string().describe("The target branch name for the pull request, e.g., 'refs/heads/main'."),
108
109
  title: z.string().describe("The title of the pull request."),
109
- description: z.string().optional().describe("The description of the pull request. Optional."),
110
+ description: z.string().max(4000).optional().describe("The description of the pull request. Must not be longer than 4000 characters. Optional."),
110
111
  isDraft: z.boolean().optional().default(false).describe("Indicates whether the pull request is a draft. Defaults to false."),
111
112
  workItems: z.string().optional().describe("Work item IDs to associate with the pull request, space-separated."),
112
113
  forkSourceRepositoryId: z.string().optional().describe("The ID of the fork repository that the pull request originates from. Optional, used when creating a pull request from a fork."),
@@ -224,7 +225,7 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
224
225
  repositoryId: z.string().describe("The ID of the repository where the pull request exists."),
225
226
  pullRequestId: z.number().describe("The ID of the pull request to update."),
226
227
  title: z.string().optional().describe("The new title for the pull request."),
227
- description: z.string().optional().describe("The new description for the pull request."),
228
+ description: z.string().max(4000).optional().describe("The new description for the pull request. Must not be longer than 4000 characters."),
228
229
  isDraft: z.boolean().optional().describe("Whether the pull request should be a draft."),
229
230
  targetRefName: z.string().optional().describe("The new target branch name (e.g., 'refs/heads/main')."),
230
231
  status: z.enum(["Active", "Abandoned"]).optional().describe("The new status of the pull request. Can be 'Active' or 'Abandoned'."),
@@ -704,9 +705,10 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
704
705
  };
705
706
  });
706
707
  const gitVersionTypeStrings = Object.values(GitVersionType).filter((value) => typeof value === "string");
707
- server.tool(REPO_TOOLS.search_commits, "Searches for commits in a repository", {
708
+ server.tool(REPO_TOOLS.search_commits, "Search for commits in a repository with comprehensive filtering capabilities. Supports searching by description/comment text, time range, author, committer, specific commit IDs, and more. This is the unified tool for all commit search operations.", {
708
709
  project: z.string().describe("Project name or ID"),
709
710
  repository: z.string().describe("Repository name or ID"),
711
+ // Existing parameters
710
712
  fromCommit: z.string().optional().describe("Starting commit ID"),
711
713
  toCommit: z.string().optional().describe("Ending commit ID"),
712
714
  version: z.string().optional().describe("The name of the branch, tag or commit to filter commits by"),
@@ -719,16 +721,79 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
719
721
  top: z.number().optional().default(10).describe("Maximum number of commits to return"),
720
722
  includeLinks: z.boolean().optional().default(false).describe("Include commit links"),
721
723
  includeWorkItems: z.boolean().optional().default(false).describe("Include associated work items"),
722
- }, async ({ project, repository, fromCommit, toCommit, version, versionType, skip, top, includeLinks, includeWorkItems }) => {
724
+ // Enhanced search parameters
725
+ searchText: z.string().optional().describe("Search text to filter commits by description/comment. Supports partial matching."),
726
+ author: z.string().optional().describe("Filter commits by author email or display name"),
727
+ authorEmail: z.string().optional().describe("Filter commits by exact author email address"),
728
+ committer: z.string().optional().describe("Filter commits by committer email or display name"),
729
+ committerEmail: z.string().optional().describe("Filter commits by exact committer email address"),
730
+ fromDate: z.string().optional().describe("Filter commits from this date (ISO 8601 format, e.g., '2024-01-01T00:00:00Z')"),
731
+ toDate: z.string().optional().describe("Filter commits to this date (ISO 8601 format, e.g., '2024-12-31T23:59:59Z')"),
732
+ commitIds: z.array(z.string()).optional().describe("Array of specific commit IDs to retrieve. When provided, other filters are ignored except top/skip."),
733
+ historySimplificationMode: z.enum(["FirstParent", "SimplifyMerges", "FullHistory", "FullHistorySimplifyMerges"]).optional().describe("How to simplify the commit history"),
734
+ }, async ({ project, repository, fromCommit, toCommit, version, versionType, skip, top, includeLinks, includeWorkItems, searchText, author, authorEmail, committer, committerEmail, fromDate, toDate, commitIds, historySimplificationMode, }) => {
723
735
  try {
724
736
  const connection = await connectionProvider();
725
737
  const gitApi = await connection.getGitApi();
738
+ // If specific commit IDs are provided, use getCommits with commit ID filtering
739
+ if (commitIds && commitIds.length > 0) {
740
+ const commits = [];
741
+ const batchSize = Math.min(top || 10, commitIds.length);
742
+ const startIndex = skip || 0;
743
+ const endIndex = Math.min(startIndex + batchSize, commitIds.length);
744
+ // Process commits in the requested range
745
+ const requestedCommitIds = commitIds.slice(startIndex, endIndex);
746
+ // Use getCommits for each commit ID to maintain consistency
747
+ for (const commitId of requestedCommitIds) {
748
+ try {
749
+ const searchCriteria = {
750
+ includeLinks: includeLinks,
751
+ includeWorkItems: includeWorkItems,
752
+ fromCommitId: commitId,
753
+ toCommitId: commitId,
754
+ };
755
+ const commitResults = await gitApi.getCommits(repository, searchCriteria, project, 0, 1);
756
+ if (commitResults && commitResults.length > 0) {
757
+ commits.push(commitResults[0]);
758
+ }
759
+ }
760
+ catch (error) {
761
+ // Log error but continue with other commits
762
+ console.warn(`Failed to retrieve commit ${commitId}: ${error instanceof Error ? error.message : String(error)}`);
763
+ // Add error information to result instead of failing completely
764
+ commits.push({
765
+ commitId: commitId,
766
+ error: `Failed to retrieve: ${error instanceof Error ? error.message : String(error)}`,
767
+ });
768
+ }
769
+ }
770
+ return {
771
+ content: [{ type: "text", text: JSON.stringify(commits, null, 2) }],
772
+ };
773
+ }
726
774
  const searchCriteria = {
727
775
  fromCommitId: fromCommit,
728
776
  toCommitId: toCommit,
729
777
  includeLinks: includeLinks,
730
778
  includeWorkItems: includeWorkItems,
731
779
  };
780
+ // Add author filter
781
+ if (author) {
782
+ searchCriteria.author = author;
783
+ }
784
+ // Add date range filters (ADO API expects ISO string format)
785
+ if (fromDate) {
786
+ searchCriteria.fromDate = fromDate;
787
+ }
788
+ if (toDate) {
789
+ searchCriteria.toDate = toDate;
790
+ }
791
+ // Add history simplification if specified
792
+ if (historySimplificationMode) {
793
+ // Note: This parameter might not be directly supported by all ADO API versions
794
+ // but we'll include it in the criteria for forward compatibility
795
+ searchCriteria.historySimplificationMode = historySimplificationMode;
796
+ }
732
797
  if (version) {
733
798
  const itemVersion = {
734
799
  version: version,
@@ -736,10 +801,27 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
736
801
  };
737
802
  searchCriteria.itemVersion = itemVersion;
738
803
  }
739
- const commits = await gitApi.getCommits(repository, searchCriteria, project, skip, // skip
740
- top);
804
+ const commits = await gitApi.getCommits(repository, searchCriteria, project, skip, top);
805
+ // Additional client-side filtering for enhanced search capabilities
806
+ let filteredCommits = commits;
807
+ // Filter by search text in commit message if not handled by API
808
+ if (searchText && filteredCommits) {
809
+ filteredCommits = filteredCommits.filter((commit) => commit.comment?.toLowerCase().includes(searchText.toLowerCase()));
810
+ }
811
+ // Filter by author email if specified
812
+ if (authorEmail && filteredCommits) {
813
+ filteredCommits = filteredCommits.filter((commit) => commit.author?.email?.toLowerCase() === authorEmail.toLowerCase());
814
+ }
815
+ // Filter by committer if specified
816
+ if (committer && filteredCommits) {
817
+ filteredCommits = filteredCommits.filter((commit) => commit.committer?.name?.toLowerCase().includes(committer.toLowerCase()) || commit.committer?.email?.toLowerCase().includes(committer.toLowerCase()));
818
+ }
819
+ // Filter by committer email if specified
820
+ if (committerEmail && filteredCommits) {
821
+ filteredCommits = filteredCommits.filter((commit) => commit.committer?.email?.toLowerCase() === committerEmail.toLowerCase());
822
+ }
741
823
  return {
742
- content: [{ type: "text", text: JSON.stringify(commits, null, 2) }],
824
+ content: [{ type: "text", text: JSON.stringify(filteredCommits, null, 2) }],
743
825
  };
744
826
  }
745
827
  catch (error) {
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const packageVersion = "2.2.1-nightly.20251104";
1
+ export const packageVersion = "2.2.1-nightly.20251105";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-devops/mcp",
3
- "version": "2.2.1-nightly.20251104",
3
+ "version": "2.2.1-nightly.20251105",
4
4
  "description": "MCP server for interacting with Azure DevOps",
5
5
  "license": "MIT",
6
6
  "author": "Microsoft Corporation",
@@ -38,6 +38,7 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@azure/identity": "^4.10.0",
41
+ "@azure/msal-node": "^3.6.0",
41
42
  "@modelcontextprotocol/sdk": "1.20.2",
42
43
  "azure-devops-extension-api": "^4.252.0",
43
44
  "azure-devops-extension-sdk": "^4.0.2",