@azure-devops/mcp 2.8.0-nightly.20260630 → 2.8.0-nightly.20260702

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.
@@ -3,7 +3,8 @@
3
3
  import { PullRequestStatus, GitVersionType, GitPullRequestQueryType, CommentThreadStatus, GitPullRequestMergeStrategy, 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
- import { extractAdoStreamError, getEnumKeys, streamToString } from "../utils.js";
6
+ import { extractAdoStreamError, getEnumKeys, streamToString, apiVersion } from "../utils.js";
7
+ import { orgName } from "../index.js";
7
8
  const REPO_TOOLS = {
8
9
  list_repos_by_project: "repo_list_repos_by_project",
9
10
  list_pull_requests_by_repo_or_project: "repo_list_pull_requests_by_repo_or_project",
@@ -1431,138 +1432,63 @@ function configureRepoTools(server, tokenProvider, connectionProvider, userAgent
1431
1432
  };
1432
1433
  }
1433
1434
  });
1434
- const gitVersionTypeStrings = Object.values(GitVersionType).filter((value) => typeof value === "string");
1435
- 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.", {
1436
- project: z.string().describe("Project name or ID"),
1437
- repository: z.string().describe("Repository name or ID"),
1438
- // Existing parameters
1439
- fromCommit: z.string().optional().describe("Starting commit ID"),
1440
- toCommit: z.string().optional().describe("Ending commit ID"),
1441
- version: z.string().optional().describe("The name of the branch, tag or commit to filter commits by"),
1442
- versionType: z
1443
- .enum(gitVersionTypeStrings)
1435
+ 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 and more.", {
1436
+ searchText: z.string().describe("Keywords to search for in commit messages"),
1437
+ project: z
1438
+ .union([z.string().transform((value) => [value]), z.array(z.string())])
1444
1439
  .optional()
1445
- .default(GitVersionType[GitVersionType.Branch])
1446
- .describe("The meaning of the version parameter, e.g., branch, tag or commit"),
1447
- skip: z.coerce.number().optional().default(0).describe("Number of commits to skip"),
1448
- top: z.coerce.number().optional().default(10).describe("Maximum number of commits to return"),
1449
- includeLinks: z.boolean().optional().default(false).describe("Include commit links"),
1450
- includeWorkItems: z.boolean().optional().default(false).describe("Include associated work items"),
1451
- // Enhanced search parameters
1452
- searchText: z.string().optional().describe("Search text to filter commits by description/comment. Supports partial matching."),
1453
- author: z.string().optional().describe("Filter commits by author email or display name"),
1454
- authorEmail: z.string().optional().describe("Filter commits by exact author email address"),
1455
- committer: z.string().optional().describe("Filter commits by committer email or display name"),
1456
- committerEmail: z.string().optional().describe("Filter commits by exact committer email address"),
1457
- fromDate: z.string().optional().describe("Filter commits from this date (ISO 8601 format, e.g., '2024-01-01T00:00:00Z')"),
1458
- toDate: z.string().optional().describe("Filter commits to this date (ISO 8601 format, e.g., '2024-12-31T23:59:59Z')"),
1459
- commitIds: z.array(z.string()).optional().describe("Array of specific commit IDs to retrieve. When provided, other filters are ignored except top/skip."),
1460
- historySimplificationMode: z.enum(["FirstParent", "SimplifyMerges", "FullHistory", "FullHistorySimplifyMerges"]).optional().describe("How to simplify the commit history"),
1461
- }, async ({ project, repository, fromCommit, toCommit, version, versionType, skip, top, includeLinks, includeWorkItems, searchText, author, authorEmail, committer, committerEmail, fromDate, toDate, commitIds, historySimplificationMode, }) => {
1462
- try {
1463
- const connection = await connectionProvider();
1464
- const gitApi = await connection.getGitApi();
1465
- // If specific commit IDs are provided, use getCommits with commit ID filtering
1466
- if (commitIds && commitIds.length > 0) {
1467
- const commits = [];
1468
- const batchSize = Math.min(top || 10, commitIds.length);
1469
- const startIndex = skip || 0;
1470
- const endIndex = Math.min(startIndex + batchSize, commitIds.length);
1471
- // Process commits in the requested range
1472
- const requestedCommitIds = commitIds.slice(startIndex, endIndex);
1473
- // Use getCommits for each commit ID to maintain consistency
1474
- for (const commitId of requestedCommitIds) {
1475
- try {
1476
- const searchCriteria = {
1477
- includeLinks: includeLinks,
1478
- includeWorkItems: includeWorkItems,
1479
- fromCommitId: commitId,
1480
- toCommitId: commitId,
1481
- };
1482
- const commitResults = await gitApi.getCommits(repository, searchCriteria, project, 0, 1);
1483
- if (commitResults && commitResults.length > 0) {
1484
- commits.push(commitResults[0]);
1485
- }
1486
- }
1487
- catch (error) {
1488
- // Log error but continue with other commits
1489
- console.warn(`Failed to retrieve commit ${commitId}: ${error instanceof Error ? error.message : String(error)}`);
1490
- // Add error information to result instead of failing completely
1491
- commits.push({
1492
- commitId: commitId,
1493
- error: `Failed to retrieve: ${error instanceof Error ? error.message : String(error)}`,
1494
- });
1495
- }
1496
- }
1497
- return {
1498
- content: [{ type: "text", text: JSON.stringify(commits, null, 2) }],
1499
- };
1500
- }
1501
- const searchCriteria = {
1502
- fromCommitId: fromCommit,
1503
- toCommitId: toCommit,
1504
- includeLinks: includeLinks,
1505
- includeWorkItems: includeWorkItems,
1506
- };
1507
- // Add author filter
1508
- if (author) {
1509
- searchCriteria.author = author;
1510
- }
1511
- // Add date range filters (ADO API expects ISO string format)
1512
- if (fromDate) {
1513
- searchCriteria.fromDate = fromDate;
1514
- }
1515
- if (toDate) {
1516
- searchCriteria.toDate = toDate;
1517
- }
1518
- // Add history simplification if specified
1519
- if (historySimplificationMode) {
1520
- // Note: This parameter might not be directly supported by all ADO API versions
1521
- // but we'll include it in the criteria for forward compatibility
1522
- const extendedCriteria = searchCriteria;
1523
- extendedCriteria.historySimplificationMode = historySimplificationMode;
1524
- }
1525
- if (version) {
1526
- const itemVersion = {
1527
- version: version,
1528
- versionType: GitVersionType[versionType],
1529
- };
1530
- searchCriteria.itemVersion = itemVersion;
1531
- }
1532
- const commits = await gitApi.getCommits(repository, searchCriteria, project, skip, top);
1533
- // Additional client-side filtering for enhanced search capabilities
1534
- let filteredCommits = commits;
1535
- // Filter by search text in commit message if not handled by API
1536
- if (searchText && filteredCommits) {
1537
- filteredCommits = filteredCommits.filter((commit) => commit.comment?.toLowerCase().includes(searchText.toLowerCase()));
1538
- }
1539
- // Filter by author email if specified
1540
- if (authorEmail && filteredCommits) {
1541
- filteredCommits = filteredCommits.filter((commit) => commit.author?.email?.toLowerCase() === authorEmail.toLowerCase());
1542
- }
1543
- // Filter by committer if specified
1544
- if (committer && filteredCommits) {
1545
- filteredCommits = filteredCommits.filter((commit) => commit.committer?.name?.toLowerCase().includes(committer.toLowerCase()) || commit.committer?.email?.toLowerCase().includes(committer.toLowerCase()));
1546
- }
1547
- // Filter by committer email if specified
1548
- if (committerEmail && filteredCommits) {
1549
- filteredCommits = filteredCommits.filter((commit) => commit.committer?.email?.toLowerCase() === committerEmail.toLowerCase());
1550
- }
1551
- return {
1552
- content: [{ type: "text", text: JSON.stringify(filteredCommits, null, 2) }],
1553
- };
1440
+ .describe("The names of the projects to search within. If omitted, searches across all projects in the organization."),
1441
+ repository: z.array(z.string()).optional().describe("The names of the repositories to search within. If omitted, searches across all repositories in the specified projects."),
1442
+ branch: z.array(z.string()).optional().describe("The names of the repository branches to search within. If omitted, searches across all branches in the specified repositories."),
1443
+ author: z.array(z.string()).optional().describe("The names of the commit authors to search for. Only full display names are supported."),
1444
+ commitStartDate: z.string().optional().describe("Filter commits from this date (format: 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS')"),
1445
+ commitEndDate: z.string().optional().describe("Filter commits up to this date (format: 'YYYY-MM-DD' or 'YYYY-MM-DDTHH:MM:SS', e.g. '2025-06-19T23:59:59' for full day)"),
1446
+ orderBy: z.enum(["ASC", "DESC"]).optional().describe("Sort commits by date: 'ASC' for oldest-first, 'DESC' for newest-first. Defaults to relevance if omitted."),
1447
+ includeFacets: z.boolean().default(false).describe("Include facets in the search results"),
1448
+ skip: z.coerce.number().default(0).describe("Number of results to skip"),
1449
+ top: z.coerce.number().default(10).describe("Maximum number of results to return"),
1450
+ }, async ({ searchText, project, repository, branch, author, commitStartDate, commitEndDate, orderBy, includeFacets, skip, top }) => {
1451
+ const accessToken = await tokenProvider();
1452
+ const url = `https://almsearch.dev.azure.com/${orgName}/_apis/search/commitSearchResults?api-version=${apiVersion}`;
1453
+ const requestBody = {
1454
+ searchText,
1455
+ includeFacets,
1456
+ $skip: skip,
1457
+ $top: top,
1458
+ };
1459
+ const filters = {};
1460
+ if (project && project.length > 0)
1461
+ filters.projectName = project;
1462
+ if (repository && repository.length > 0)
1463
+ filters.repositoryName = repository;
1464
+ if (branch && branch.length > 0)
1465
+ filters.branchName = branch;
1466
+ if (author && author.length > 0)
1467
+ filters.authorName = author;
1468
+ if (commitStartDate)
1469
+ filters.commitStartDate = [commitStartDate];
1470
+ if (commitEndDate)
1471
+ filters.commitEndDate = [commitEndDate];
1472
+ requestBody.filters = filters;
1473
+ if (orderBy) {
1474
+ requestBody.$orderBy = [{ field: "commitDate", sortOrder: orderBy }];
1554
1475
  }
1555
- catch (error) {
1556
- return {
1557
- content: [
1558
- {
1559
- type: "text",
1560
- text: `Error searching commits: ${error instanceof Error ? error.message : String(error)}`,
1561
- },
1562
- ],
1563
- isError: true,
1564
- };
1476
+ const response = await fetch(url, {
1477
+ method: "POST",
1478
+ headers: {
1479
+ "Content-Type": "application/json",
1480
+ "Authorization": `Bearer ${accessToken}`,
1481
+ "User-Agent": userAgentProvider(),
1482
+ },
1483
+ body: JSON.stringify(requestBody),
1484
+ });
1485
+ if (!response.ok) {
1486
+ throw new Error(`Azure DevOps Commit Search API error: ${response.status} ${response.statusText}`);
1565
1487
  }
1488
+ const result = await response.text();
1489
+ return {
1490
+ content: [{ type: "text", text: result }],
1491
+ };
1566
1492
  });
1567
1493
  const pullRequestQueryTypesStrings = Object.values(GitPullRequestQueryType).filter((value) => typeof value === "string");
1568
1494
  server.tool(REPO_TOOLS.list_pull_requests_by_commits, "Lists pull requests by commit IDs to find which pull requests contain specific commits", {
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const packageVersion = "2.8.0-nightly.20260630";
1
+ export const packageVersion = "2.8.0-nightly.20260702";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-devops/mcp",
3
- "version": "2.8.0-nightly.20260630",
3
+ "version": "2.8.0-nightly.20260702",
4
4
  "mcpName": "microsoft.com/azure-devops",
5
5
  "description": "MCP server for interacting with Azure DevOps",
6
6
  "license": "MIT",