@azure-devops/mcp 2.10.0-nightly.20261012 → 2.10.0-nightly.20261014

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.
@@ -1,7 +1,27 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
+ import { TestOutcome } from "azure-devops-node-api/interfaces/TestInterfaces.js";
3
4
  import { z } from "zod";
4
5
  import { apiVersion } from "../utils.js";
6
+ const TEST_RESULT_PAGE_SIZE = 1_000;
7
+ const TEST_RESULT_CONCURRENCY = 5;
8
+ const TEST_OUTCOMES = [
9
+ "Unspecified",
10
+ "None",
11
+ "Passed",
12
+ "Failed",
13
+ "Inconclusive",
14
+ "Timeout",
15
+ "Aborted",
16
+ "Blocked",
17
+ "NotExecuted",
18
+ "Warning",
19
+ "Error",
20
+ "NotApplicable",
21
+ "Paused",
22
+ "InProgress",
23
+ "NotImpacted",
24
+ ];
5
25
  const TEST_PLAN_TOOLS = {
6
26
  testplan: "testplan",
7
27
  test_results_from_build_id: "testplan_show_test_results_from_build_id",
@@ -137,33 +157,50 @@ function configureTestPlanTools(server, tokenProvider, connectionProvider, userA
137
157
  server.tool(TEST_PLAN_TOOLS.test_results_from_build_id, "Gets a list of test results for a given project and build ID. Can filter by test outcome (e.g. Failed, Passed, Aborted). Returns test case titles, error messages, stack traces, and outcomes. Efficiently handles builds with large numbers of test runs.", {
138
158
  project: z.string().describe("The unique identifier (ID or name) of the Azure DevOps project."),
139
159
  buildid: z.coerce.number().min(1).describe("The ID of the build."),
140
- outcomes: z.array(z.string()).optional().describe("Filter results by test outcome, e.g. ['Failed', 'Passed', 'Aborted']."),
160
+ outcomes: z.array(z.enum(TEST_OUTCOMES)).optional().describe("Filter results by test outcome, e.g. ['Failed', 'Passed', 'Aborted']."),
141
161
  }, async ({ project, buildid, outcomes }) => {
142
162
  try {
143
163
  const connection = await connectionProvider();
144
164
  const testResultsApi = await connection.getTestResultsApi();
145
- const outcomeFilter = outcomes?.length ? `Outcome eq ${outcomes.join(",")}` : undefined;
146
- const testResultDetails = await testResultsApi.getTestResultDetailsForBuild(project, buildid, undefined, undefined, outcomeFilter, undefined, true);
165
+ const buildUri = `vstfs:///Build/Build/${buildid}`;
166
+ const testRuns = [];
167
+ for (let skip = 0;; skip += TEST_RESULT_PAGE_SIZE) {
168
+ const page = await testResultsApi.getTestRuns(project, buildUri, undefined, undefined, undefined, undefined, undefined, skip, TEST_RESULT_PAGE_SIZE);
169
+ testRuns.push(...page);
170
+ if (page.length < TEST_RESULT_PAGE_SIZE) {
171
+ break;
172
+ }
173
+ }
174
+ const outcomeFilter = outcomes?.map((outcome) => TestOutcome[outcome]);
175
+ const runsWithIds = testRuns.filter((run) => run.id !== undefined);
147
176
  const allResults = [];
148
- if (testResultDetails.resultsForGroup) {
149
- for (const group of testResultDetails.resultsForGroup) {
150
- if (group.results) {
151
- for (const result of group.results) {
152
- allResults.push(result);
177
+ for (let index = 0; index < runsWithIds.length; index += TEST_RESULT_CONCURRENCY) {
178
+ const batch = runsWithIds.slice(index, index + TEST_RESULT_CONCURRENCY);
179
+ const batchResults = await Promise.all(batch.map(async (run) => {
180
+ const results = [];
181
+ for (let skip = 0;; skip += TEST_RESULT_PAGE_SIZE) {
182
+ const page = await testResultsApi.getTestResults(project, run.id, undefined, skip, TEST_RESULT_PAGE_SIZE, outcomeFilter);
183
+ results.push(...page.map((result) => ({ result, runId: run.id })));
184
+ if (page.length < TEST_RESULT_PAGE_SIZE) {
185
+ break;
153
186
  }
154
187
  }
188
+ return results;
189
+ }));
190
+ for (const results of batchResults) {
191
+ allResults.push(...results);
155
192
  }
156
193
  }
157
- const formattedResults = allResults.map((r) => ({
158
- id: r.id,
159
- testCaseTitle: r.testCaseTitle,
160
- outcome: r.outcome,
161
- errorMessage: r.errorMessage,
162
- stackTrace: r.stackTrace,
163
- automatedTestName: r.automatedTestName,
164
- automatedTestStorage: r.automatedTestStorage,
165
- durationInMs: r.durationInMs,
166
- runId: r.testRun?.id,
194
+ const formattedResults = allResults.map(({ result, runId }) => ({
195
+ id: result.id,
196
+ testCaseTitle: result.testCaseTitle,
197
+ outcome: result.outcome,
198
+ errorMessage: result.errorMessage,
199
+ stackTrace: result.stackTrace,
200
+ automatedTestName: result.automatedTestName,
201
+ automatedTestStorage: result.automatedTestStorage,
202
+ durationInMs: result.durationInMs,
203
+ runId,
167
204
  }));
168
205
  return {
169
206
  content: [{ type: "text", text: JSON.stringify(formattedResults, null, 2) }],
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const packageVersion = "2.10.0-nightly.20261012";
1
+ export const packageVersion = "2.10.0-nightly.20261014";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-devops/mcp",
3
- "version": "2.10.0-nightly.20261012",
3
+ "version": "2.10.0-nightly.20261014",
4
4
  "mcpName": "microsoft.com/azure-devops",
5
5
  "description": "MCP server for interacting with Azure DevOps",
6
6
  "license": "MIT",