@azure-devops/mcp 1.3.0 → 1.3.1-nightly.20250807

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.
package/LICENSE.md CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) Microsoft Corporation.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
package/README.md CHANGED
@@ -208,7 +208,8 @@ Click "Select Tools" and choose the available tools.
208
208
 
209
209
  Open GitHub Copilot Chat and try a prompt like `List ADO projects`.
210
210
 
211
- > 💥 We strongly recommend creating a `.github\copilot-instructions.md` in your project and copying the contents from this [copilot-instructions.md](./.github/copilot-instructions.md) file. This will enhance your experience using the Azure DevOps MCP Server with GitHub Copilot Chat.
211
+ > 💥 We strongly recommend creating a `.github\copilot-instructions.md` in your project. This will enhance your experience using the Azure DevOps MCP Server with GitHub Copilot Chat.
212
+ > To start, just include "`This project uses Azure DevOps. Always check to see if the Azure DevOps MCP server has a tool relevant to the user's request`" in your copilot instructions file.
212
213
 
213
214
  See the [getting started documentation](./docs/GETTINGSTARTED.md) to use our MCP Server with other tools such as Visual Studio 2022, Claude Code, and Cursor.
214
215
 
package/dist/index.js CHANGED
File without changes
package/dist/prompts.js CHANGED
File without changes
@@ -96,11 +96,14 @@ function configureWorkItemTools(server, tokenProvider, connectionProvider, userA
96
96
  server.tool(WORKITEM_TOOLS.get_work_items_batch_by_ids, "Retrieve list of work items by IDs in batch.", {
97
97
  project: z.string().describe("The name or ID of the Azure DevOps project."),
98
98
  ids: z.array(z.number()).describe("The IDs of the work items to retrieve."),
99
- }, async ({ project, ids }) => {
99
+ fields: z.array(z.string()).optional().describe("Optional list of fields to include in the response. If not provided, a hardcoded default set of fields will be used."),
100
+ }, async ({ project, ids, fields }) => {
100
101
  const connection = await connectionProvider();
101
102
  const workItemApi = await connection.getWorkItemTrackingApi();
102
- const fields = ["System.Id", "System.WorkItemType", "System.Title", "System.State", "System.Parent", "System.Tags", "Microsoft.VSTS.Common.StackRank", "System.AssignedTo"];
103
- const workitems = await workItemApi.getWorkItemsBatch({ ids, fields }, project);
103
+ const defaultFields = ["System.Id", "System.WorkItemType", "System.Title", "System.State", "System.Parent", "System.Tags", "Microsoft.VSTS.Common.StackRank", "System.AssignedTo"];
104
+ // If no fields are provided, use the default set of fields
105
+ const fieldsToUse = !fields || fields.length === 0 ? defaultFields : fields;
106
+ const workitems = await workItemApi.getWorkItemsBatch({ ids, fields: fieldsToUse }, project);
104
107
  // Format the assignedTo field to include displayName and uniqueName
105
108
  // Removing the identity object as the response. It's too much and not needed
106
109
  if (workitems && Array.isArray(workitems)) {
package/dist/tools.js CHANGED
File without changes
package/dist/useragent.js CHANGED
File without changes
package/dist/utils.js CHANGED
File without changes
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const packageVersion = "1.3.0";
1
+ export const packageVersion = "1.3.1-nightly.20250807";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-devops/mcp",
3
- "version": "1.3.0",
3
+ "version": "1.3.1-nightly.20250807",
4
4
  "description": "MCP server for interacting with Azure DevOps",
5
5
  "license": "MIT",
6
6
  "author": "Microsoft Corporation",
@@ -1 +0,0 @@
1
- export {};
@@ -1,125 +0,0 @@
1
- // Copyright (c) Microsoft Corporation.
2
- // Licensed under the MIT License.
3
- import { describe, expect, it } from '@jest/globals';
4
- import { configureTestPlanTools } from '../../../src/tools/testplans';
5
- describe("configureTestPlanTools", () => {
6
- let server;
7
- let tokenProvider;
8
- let connectionProvider;
9
- let mockConnection;
10
- let mockTestPlanApi;
11
- let mockTestResultsApi;
12
- beforeEach(() => {
13
- server = { tool: jest.fn() };
14
- tokenProvider = jest.fn();
15
- mockTestPlanApi = {
16
- getTestPlans: jest.fn(),
17
- createTestPlan: jest.fn(),
18
- addTestCasesToSuite: jest.fn(),
19
- getTestCaseList: jest.fn(),
20
- };
21
- mockTestResultsApi = {
22
- getTestResultDetailsForBuild: jest.fn(),
23
- };
24
- mockConnection = {
25
- getTestPlanApi: jest.fn().mockResolvedValue(mockTestPlanApi),
26
- getTestResultsApi: jest.fn().mockResolvedValue(mockTestResultsApi),
27
- };
28
- connectionProvider = jest.fn().mockResolvedValue(mockConnection);
29
- });
30
- describe("tool registration", () => {
31
- it("registers test plan tools on the server", () => {
32
- configureTestPlanTools(server, tokenProvider, connectionProvider);
33
- expect(server.tool.mock.calls.map(call => call[0])).toEqual(expect.arrayContaining([
34
- "ado_list_test_plans",
35
- "ado_create_test_plan",
36
- "ado_add_test_cases_to_suite",
37
- "ado_list_test_cases",
38
- "ado_show_test_results_from_build_id",
39
- ]));
40
- });
41
- });
42
- describe("list_test_plans tool", () => {
43
- it("should call getTestPlans with the correct parameters and return the expected result", async () => {
44
- configureTestPlanTools(server, tokenProvider, connectionProvider);
45
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_list_test_plans");
46
- if (!call)
47
- throw new Error("ado_list_test_plans tool not registered");
48
- const [, , , handler] = call;
49
- mockTestPlanApi.getTestPlans.mockResolvedValue([{ id: 1, name: "Test Plan 1" }]);
50
- const params = {
51
- project: "proj1",
52
- filterActivePlans: true,
53
- includePlanDetails: false,
54
- continuationToken: undefined,
55
- };
56
- const result = await handler(params);
57
- expect(mockTestPlanApi.getTestPlans).toHaveBeenCalledWith("proj1", "", undefined, false, true);
58
- expect(result.content[0].text).toBe(JSON.stringify([{ id: 1, name: "Test Plan 1" }], null, 2));
59
- });
60
- });
61
- describe("create_test_plan tool", () => {
62
- it("should call createTestPlan with the correct parameters and return the expected result", async () => {
63
- configureTestPlanTools(server, tokenProvider, connectionProvider);
64
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_create_test_plan");
65
- if (!call)
66
- throw new Error("ado_create_test_plan tool not registered");
67
- const [, , , handler] = call;
68
- mockTestPlanApi.createTestPlan.mockResolvedValue({ id: 1, name: "New Test Plan" });
69
- const params = {
70
- project: "proj1",
71
- name: "New Test Plan",
72
- iteration: "Iteration 1",
73
- description: "Description",
74
- startDate: "2025-05-01",
75
- endDate: "2025-05-31",
76
- areaPath: "Area 1",
77
- };
78
- const result = await handler(params);
79
- expect(mockTestPlanApi.createTestPlan).toHaveBeenCalledWith({
80
- name: "New Test Plan",
81
- iteration: "Iteration 1",
82
- description: "Description",
83
- startDate: new Date("2025-05-01"),
84
- endDate: new Date("2025-05-31"),
85
- areaPath: "Area 1",
86
- }, "proj1");
87
- expect(result.content[0].text).toBe(JSON.stringify({ id: 1, name: "New Test Plan" }, null, 2));
88
- });
89
- });
90
- describe("list_test_cases tool", () => {
91
- it("should call getTestCaseList with the correct parameters and return the expected result", async () => {
92
- configureTestPlanTools(server, tokenProvider, connectionProvider);
93
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_list_test_cases");
94
- if (!call)
95
- throw new Error("ado_list_test_cases tool not registered");
96
- const [, , , handler] = call;
97
- mockTestPlanApi.getTestCaseList.mockResolvedValue([{ id: 1, name: "Test Case 1" }]);
98
- const params = {
99
- project: "proj1",
100
- planid: 1,
101
- suiteid: 2,
102
- };
103
- const result = await handler(params);
104
- expect(mockTestPlanApi.getTestCaseList).toHaveBeenCalledWith("proj1", 1, 2);
105
- expect(result.content[0].text).toBe(JSON.stringify([{ id: 1, name: "Test Case 1" }], null, 2));
106
- });
107
- });
108
- describe("test_results_from_build_id tool", () => {
109
- it("should call getTestResultDetailsForBuild with the correct parameters and return the expected result", async () => {
110
- configureTestPlanTools(server, tokenProvider, connectionProvider);
111
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_show_test_results_from_build_id");
112
- if (!call)
113
- throw new Error("ado_show_test_results_from_build_id tool not registered");
114
- const [, , , handler] = call;
115
- mockTestResultsApi.getTestResultDetailsForBuild.mockResolvedValue({ results: ["Result 1"] });
116
- const params = {
117
- project: "proj1",
118
- buildid: 123,
119
- };
120
- const result = await handler(params);
121
- expect(mockTestResultsApi.getTestResultDetailsForBuild).toHaveBeenCalledWith("proj1", 123);
122
- expect(result.content[0].text).toBe(JSON.stringify({ results: ["Result 1"] }, null, 2));
123
- });
124
- });
125
- });
@@ -1,6 +0,0 @@
1
- // Copyright (c) Microsoft Corporation.
2
- // Licensed under the MIT License.
3
- import { packageVersion } from "./version.js";
4
- export const apiVersion = "7.2-preview.1";
5
- export const batchApiVersion = "5.0";
6
- export const userAgent = `AzureDevOps.MCP/${packageVersion} (local)`;
@@ -1,87 +0,0 @@
1
- // Copyright (c) Microsoft Corporation.
2
- // Licensed under the MIT License.
3
- import { describe, expect, it } from '@jest/globals';
4
- import { configureWikiTools } from '../../../src/tools/wiki';
5
- describe("configureWikiTools", () => {
6
- let server;
7
- let tokenProvider;
8
- let connectionProvider;
9
- let mockConnection;
10
- let mockWikiApi;
11
- beforeEach(() => {
12
- server = { tool: jest.fn() };
13
- tokenProvider = jest.fn();
14
- mockWikiApi = {
15
- getWiki: jest.fn(),
16
- getAllWikis: jest.fn(),
17
- getPagesBatch: jest.fn(),
18
- getPageText: jest.fn(),
19
- };
20
- mockConnection = {
21
- getWikiApi: jest.fn().mockResolvedValue(mockWikiApi),
22
- };
23
- connectionProvider = jest.fn().mockResolvedValue(mockConnection);
24
- });
25
- describe("tool registration", () => {
26
- it("registers wiki tools on the server", () => {
27
- configureWikiTools(server, tokenProvider, connectionProvider);
28
- expect(server.tool).toHaveBeenCalled();
29
- });
30
- });
31
- describe("get_wiki_page_content tool", () => {
32
- it("should call getPageText with the correct parameters and return the expected result", async () => {
33
- configureWikiTools(server, tokenProvider, connectionProvider);
34
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_get_wiki_page_content");
35
- if (!call)
36
- throw new Error("ado_get_wiki_page_content tool not registered");
37
- const [, , , handler] = call;
38
- // Mock a stream-like object for getPageText
39
- const mockStream = {
40
- setEncoding: jest.fn(),
41
- on: function (event, cb) {
42
- if (event === "data") {
43
- setImmediate(() => cb("mock page text"));
44
- }
45
- if (event === "end") {
46
- setImmediate(() => cb());
47
- }
48
- return this;
49
- }
50
- };
51
- mockWikiApi.getPageText.mockResolvedValue(mockStream);
52
- const params = {
53
- wikiIdentifier: "wiki1",
54
- project: "proj1",
55
- path: "/page1"
56
- };
57
- const result = await handler(params);
58
- expect(mockWikiApi.getPageText).toHaveBeenCalledWith("proj1", "wiki1", "/page1", undefined, undefined, true);
59
- expect(result.content[0].text).toBe("\"mock page text\"");
60
- });
61
- });
62
- describe("list_wiki_pages tool", () => {
63
- it("should call getPagesBatch with the correct parameters and return the expected result", async () => {
64
- configureWikiTools(server, tokenProvider, connectionProvider);
65
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_list_wiki_pages");
66
- if (!call)
67
- throw new Error("ado_list_wiki_pages tool not registered");
68
- const [, , , handler] = call;
69
- mockWikiApi.getPagesBatch.mockResolvedValue({ value: ["page1", "page2"] });
70
- const params = {
71
- wikiIdentifier: "wiki2",
72
- project: "proj2",
73
- top: 10,
74
- continuationToken: "token123",
75
- pageViewsForDays: 7
76
- };
77
- const result = await handler(params);
78
- const parsedResult = JSON.parse(result.content[0].text);
79
- expect(mockWikiApi.getPagesBatch).toHaveBeenCalledWith({
80
- top: 10,
81
- continuationToken: "token123",
82
- pageViewsForDays: 7
83
- }, "proj2", "wiki2");
84
- expect(parsedResult.value).toEqual(["page1", "page2"]);
85
- });
86
- });
87
- });
@@ -1,101 +0,0 @@
1
- import { describe, expect, it } from "@jest/globals";
2
- import { configureWorkItemTools } from "../../../src/tools/workitems";
3
- describe("configureWorkItemTools", () => {
4
- let server;
5
- let tokenProvider;
6
- let connectionProvider;
7
- let mockConnection;
8
- let mockWorkApi;
9
- let mockWorkItemTrackingApi;
10
- beforeEach(() => {
11
- server = { tool: jest.fn() };
12
- tokenProvider = jest.fn();
13
- mockWorkApi = {
14
- getBacklogs: jest.fn(),
15
- getPredefinedQueryResults: jest.fn(),
16
- getTeamIterations: jest.fn(),
17
- getIterationWorkItems: jest.fn(),
18
- };
19
- mockWorkItemTrackingApi = {
20
- getWorkItemsBatch: jest.fn(),
21
- getWorkItem: jest.fn(),
22
- getComments: jest.fn(),
23
- addComment: jest.fn(),
24
- updateWorkItem: jest.fn(),
25
- createWorkItem: jest.fn(),
26
- getWorkItemType: jest.fn(),
27
- getQuery: jest.fn(),
28
- queryById: jest.fn(),
29
- };
30
- mockConnection = {
31
- getWorkApi: jest.fn().mockResolvedValue(mockWorkApi),
32
- getWorkItemTrackingApi: jest.fn().mockResolvedValue(mockWorkItemTrackingApi),
33
- };
34
- connectionProvider = jest.fn().mockResolvedValue(mockConnection);
35
- });
36
- describe("tool registration", () => {
37
- it("registers core tools on the server", () => {
38
- configureWorkItemTools(server, tokenProvider, connectionProvider);
39
- expect(server.tool).toHaveBeenCalled();
40
- });
41
- });
42
- describe("list_backlogs tool", () => {
43
- it("should call getBacklogs API with the correct parameters and return the expected result", async () => {
44
- configureWorkItemTools(server, tokenProvider, connectionProvider);
45
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_list_backlogs");
46
- if (!call)
47
- throw new Error("ado_list_backlogs tool not registered");
48
- const [, , , handler] = call;
49
- mockWorkApi.getBacklogs.mockResolvedValue([
50
- {
51
- id: "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
52
- name: "Fabrikam-Fiber-TFVC",
53
- description: "Team Foundation Version Control projects.",
54
- url: "https://dev.azure.com/fabrikam/_apis/projects/eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
55
- state: "wellFormed",
56
- },
57
- {
58
- id: "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
59
- name: "Fabrikam-Fiber-Git",
60
- description: "Git projects",
61
- url: "https://dev.azure.com/fabrikam/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
62
- state: "wellFormed",
63
- },
64
- {
65
- id: "281f9a5b-af0d-49b4-a1df-fe6f5e5f84d0",
66
- name: "TestGit",
67
- url: "https://dev.azure.com/fabrikam/_apis/projects/281f9a5b-af0d-49b4-a1df-fe6f5e5f84d0",
68
- state: "wellFormed",
69
- },
70
- ]);
71
- const params = {
72
- project: "Contoso",
73
- team: "Fabrikam",
74
- };
75
- const result = await handler(params);
76
- expect(mockWorkApi.getBacklogs).toHaveBeenCalledWith({ project: "Contoso", team: "Fabrikam" });
77
- expect(result.content[0].text).toBe(JSON.stringify([
78
- {
79
- id: "eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
80
- name: "Fabrikam-Fiber-TFVC",
81
- description: "Team Foundation Version Control projects.",
82
- url: "https://dev.azure.com/fabrikam/_apis/projects/eb6e4656-77fc-42a1-9181-4c6d8e9da5d1",
83
- state: "wellFormed",
84
- },
85
- {
86
- id: "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
87
- name: "Fabrikam-Fiber-Git",
88
- description: "Git projects",
89
- url: "https://dev.azure.com/fabrikam/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
90
- state: "wellFormed",
91
- },
92
- {
93
- id: "281f9a5b-af0d-49b4-a1df-fe6f5e5f84d0",
94
- name: "TestGit",
95
- url: "https://dev.azure.com/fabrikam/_apis/projects/281f9a5b-af0d-49b4-a1df-fe6f5e5f84d0",
96
- state: "wellFormed",
97
- },
98
- ], null, 2));
99
- });
100
- });
101
- });
@@ -1,530 +0,0 @@
1
- // Copyright (c) Microsoft Corporation.
2
- // Licensed under the MIT License.
3
- import { describe, expect, it } from "@jest/globals";
4
- import { configureWorkItemTools } from "../../../src/tools/workitems";
5
- import { _mockBacklogs, _mockQuery, _mockQueryResults, _mockWorkItem, _mockWorkItemComment, _mockWorkItemComments, _mockWorkItems, _mockWorkItemsForIteration, _mockWorkItemType } from "../../mocks/work-items";
6
- describe("configureWorkItemTools", () => {
7
- let server;
8
- let tokenProvider;
9
- let connectionProvider;
10
- let mockConnection;
11
- let mockWorkApi;
12
- let mockWorkItemTrackingApi;
13
- beforeEach(() => {
14
- server = { tool: jest.fn() };
15
- tokenProvider = jest.fn();
16
- mockWorkApi = {
17
- getBacklogs: jest.fn(),
18
- getBacklogLevelWorkItems: jest.fn(),
19
- getPredefinedQueryResults: jest.fn(),
20
- getTeamIterations: jest.fn(),
21
- getIterationWorkItems: jest.fn(),
22
- };
23
- mockWorkItemTrackingApi = {
24
- getWorkItemsBatch: jest.fn(),
25
- getWorkItem: jest.fn(),
26
- getComments: jest.fn(),
27
- addComment: jest.fn(),
28
- updateWorkItem: jest.fn(),
29
- createWorkItem: jest.fn(),
30
- getWorkItemType: jest.fn(),
31
- getQuery: jest.fn(),
32
- queryById: jest.fn(),
33
- };
34
- mockConnection = {
35
- getWorkApi: jest.fn().mockResolvedValue(mockWorkApi),
36
- getWorkItemTrackingApi: jest.fn().mockResolvedValue(mockWorkItemTrackingApi),
37
- };
38
- connectionProvider = jest.fn().mockResolvedValue(mockConnection);
39
- });
40
- describe("tool registration", () => {
41
- it("registers core tools on the server", () => {
42
- configureWorkItemTools(server, tokenProvider, connectionProvider);
43
- expect(server.tool).toHaveBeenCalled();
44
- });
45
- });
46
- describe("list_backlogs tool", () => {
47
- it("should call getBacklogs API with the correct parameters and return the expected result", async () => {
48
- configureWorkItemTools(server, tokenProvider, connectionProvider);
49
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_list_backlogs");
50
- if (!call)
51
- throw new Error("ado_list_backlogs tool not registered");
52
- const [, , , handler] = call;
53
- mockWorkApi.getBacklogs.mockResolvedValue([
54
- _mockBacklogs,
55
- ]);
56
- const params = {
57
- project: "Contoso",
58
- team: "Fabrikam",
59
- };
60
- const result = await handler(params);
61
- expect(mockWorkApi.getBacklogs).toHaveBeenCalledWith({
62
- project: params.project,
63
- team: params.team,
64
- });
65
- expect(result.content[0].text).toBe(JSON.stringify([
66
- _mockBacklogs,
67
- ], null, 2));
68
- });
69
- });
70
- describe("list_backlog_work_items tool", () => {
71
- it("should call getBacklogLevelWorkItems API with the correct parameters and return the expected result", async () => {
72
- configureWorkItemTools(server, tokenProvider, connectionProvider);
73
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_list_backlog_work_items");
74
- if (!call)
75
- throw new Error("ado_list_backlog_work_items tool not registered");
76
- const [, , , handler] = call;
77
- mockWorkApi.getBacklogLevelWorkItems.mockResolvedValue([
78
- {
79
- workItems: [
80
- {
81
- rel: null,
82
- source: null,
83
- target: {
84
- id: 50,
85
- },
86
- },
87
- {
88
- rel: null,
89
- source: null,
90
- target: {
91
- id: 49,
92
- },
93
- },
94
- ],
95
- },
96
- ]);
97
- const params = {
98
- project: "Contoso",
99
- team: "Fabrikam",
100
- backlogId: "Microsoft.FeatureCategory",
101
- };
102
- const result = await handler(params);
103
- expect(mockWorkApi.getBacklogLevelWorkItems).toHaveBeenCalledWith({ project: params.project, team: params.team }, params.backlogId);
104
- expect(result.content[0].text).toBe(JSON.stringify([
105
- {
106
- workItems: [
107
- {
108
- rel: null,
109
- source: null,
110
- target: {
111
- id: 50,
112
- },
113
- },
114
- {
115
- rel: null,
116
- source: null,
117
- target: {
118
- id: 49,
119
- },
120
- },
121
- ],
122
- }
123
- ], null, 2));
124
- });
125
- });
126
- describe("my_work_items tool", () => {
127
- it("should call getPredefinedQueryResults API with the correct parameters and return the expected result", async () => {
128
- configureWorkItemTools(server, tokenProvider, connectionProvider);
129
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_my_work_items");
130
- if (!call)
131
- throw new Error("ado_my_work_items tool not registered");
132
- const [, , , handler] = call;
133
- mockWorkApi.getPredefinedQueryResults.mockResolvedValue([
134
- {
135
- id: "assignedtome",
136
- name: "Assigned to me",
137
- url: "https://dev.azure.com/org/project/_apis/work/predefinedQueries/assignedtome",
138
- webUrl: "https://dev.azure.com/org/project/project/_workitems/assignedtome",
139
- hasMore: false,
140
- results: [
141
- {
142
- id: 115784,
143
- url: "https://dev.azure.com/org/_apis/wit/workItems/115784",
144
- },
145
- {
146
- id: 115794,
147
- url: "https://dev.azure.com/org/_apis/wit/workItems/115794",
148
- },
149
- {
150
- id: 115792,
151
- url: "https://dev.azure.com/org/_apis/wit/workItems/115792",
152
- },
153
- ],
154
- },
155
- ]);
156
- const params = {
157
- project: "Contoso",
158
- type: "assignedtome",
159
- top: 10,
160
- includeCompleted: false,
161
- };
162
- const result = await handler(params);
163
- expect(mockWorkApi.getPredefinedQueryResults).toHaveBeenCalledWith(params.project, params.type, params.top, params.includeCompleted);
164
- expect(result.content[0].text).toBe(JSON.stringify([
165
- {
166
- id: "assignedtome",
167
- name: "Assigned to me",
168
- url: "https://dev.azure.com/org/project/_apis/work/predefinedQueries/assignedtome",
169
- webUrl: "https://dev.azure.com/org/project/project/_workitems/assignedtome",
170
- hasMore: false,
171
- results: [
172
- {
173
- id: 115784,
174
- url: "https://dev.azure.com/org/_apis/wit/workItems/115784",
175
- },
176
- {
177
- id: 115794,
178
- url: "https://dev.azure.com/org/_apis/wit/workItems/115794",
179
- },
180
- {
181
- id: 115792,
182
- url: "https://dev.azure.com/org/_apis/wit/workItems/115792",
183
- },
184
- ],
185
- },
186
- ], null, 2));
187
- });
188
- });
189
- describe("getWorkItemsBatch tool", () => {
190
- it("should call workItemApi.getWorkItemsBatch API with the correct parameters and return the expected result", async () => {
191
- configureWorkItemTools(server, tokenProvider, connectionProvider);
192
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_get_work_items_batch_by_ids");
193
- if (!call)
194
- throw new Error("ado_get_work_items_batch_by_ids tool not registered");
195
- const [, , , handler] = call;
196
- mockWorkItemTrackingApi.getWorkItemsBatch.mockResolvedValue([
197
- _mockWorkItems,
198
- ]);
199
- const params = {
200
- ids: [297, 299, 300],
201
- project: "Contoso",
202
- };
203
- const result = await handler(params);
204
- expect(mockWorkItemTrackingApi.getWorkItemsBatch).toHaveBeenCalledWith({
205
- ids: params.ids,
206
- fields: ["System.Id", "System.WorkItemType", "System.Title", "System.State", "System.Parent", "System.Tags"]
207
- }, params.project);
208
- expect(result.content[0].text).toBe(JSON.stringify([
209
- _mockWorkItems,
210
- ], null, 2));
211
- });
212
- });
213
- describe("get_work_item tool", () => {
214
- it("should call workItemApi.getWorkItem API with the correct parameters and return the expected result", async () => {
215
- configureWorkItemTools(server, tokenProvider, connectionProvider);
216
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_get_work_item");
217
- if (!call)
218
- throw new Error("ado_get_work_item tool not registered");
219
- const [, , , handler] = call;
220
- mockWorkItemTrackingApi.getWorkItem.mockResolvedValue([
221
- _mockWorkItem
222
- ]);
223
- const params = {
224
- id: 12,
225
- fields: undefined,
226
- asOf: undefined,
227
- expand: "none",
228
- project: "Contoso",
229
- };
230
- const result = await handler(params);
231
- expect(mockWorkItemTrackingApi.getWorkItem).toHaveBeenCalledWith(params.id, params.fields, params.asOf, params.expand, params.project);
232
- expect(result.content[0].text).toBe(JSON.stringify([
233
- _mockWorkItem,
234
- ], null, 2));
235
- });
236
- });
237
- describe("list_work_item_comments tool", () => {
238
- it("should call workItemApi.getComments API with the correct parameters and return the expected result", async () => {
239
- configureWorkItemTools(server, tokenProvider, connectionProvider);
240
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_list_work_item_comments");
241
- if (!call)
242
- throw new Error("ado_list_work_item_comments tool not registered");
243
- const [, , , handler] = call;
244
- mockWorkItemTrackingApi.getComments.mockResolvedValue([
245
- _mockWorkItemComments
246
- ]);
247
- const params = {
248
- project: "Contoso",
249
- workItemId: 299,
250
- top: 10,
251
- };
252
- const result = await handler(params);
253
- expect(mockWorkItemTrackingApi.getComments).toHaveBeenCalledWith(params.project, params.workItemId, params.top);
254
- expect(result.content[0].text).toBe(JSON.stringify([
255
- _mockWorkItemComments,
256
- ], null, 2));
257
- });
258
- });
259
- describe("add_work_item_comment tool", () => {
260
- it("should call workItemApi.addComment API with the correct parameters and return the expected result", async () => {
261
- configureWorkItemTools(server, tokenProvider, connectionProvider);
262
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_add_work_item_comment");
263
- if (!call)
264
- throw new Error("ado_add_work_item_comment tool not registered");
265
- const [, , , handler] = call;
266
- mockWorkItemTrackingApi.addComment.mockResolvedValue([
267
- _mockWorkItemComment,
268
- ]);
269
- const params = {
270
- comment: "hello world!",
271
- project: "Contoso",
272
- workItemId: 299,
273
- };
274
- const result = await handler(params);
275
- expect(mockWorkItemTrackingApi.addComment).toHaveBeenCalledWith({ text: params.comment }, params.project, params.workItemId);
276
- expect(result.content[0].text).toBe(JSON.stringify([
277
- _mockWorkItemComment
278
- ], null, 2));
279
- });
280
- });
281
- describe("add_child_work_item tool", () => {
282
- it("should call workItemApi.add_child_work_item API with the correct parameters and return the expected result", async () => {
283
- configureWorkItemTools(server, tokenProvider, connectionProvider);
284
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_add_child_work_item");
285
- if (!call)
286
- throw new Error("ado_add_child_work_item tool not registered");
287
- const [, , , handler] = call;
288
- mockWorkItemTrackingApi.createWorkItem.mockResolvedValue([
289
- _mockWorkItem,
290
- ]);
291
- const params = {
292
- parentId: 299,
293
- project: "Contoso",
294
- workItemType: "Task",
295
- title: "Sample task",
296
- description: "This is a sample task",
297
- areaPath: "Contoso\\Development",
298
- iterationPath: "Contoso\\Sprint 1",
299
- };
300
- const document = [
301
- {
302
- op: "add",
303
- path: "/fields/System.Title",
304
- value: params.title
305
- },
306
- {
307
- op: "add",
308
- path: "/fields/System.Description",
309
- value: params.description
310
- },
311
- {
312
- op: "add",
313
- path: "/relations/-",
314
- value: {
315
- rel: "System.LinkTypes.Hierarchy-Reverse",
316
- url: `undefined/${params.project}/_apis/wit/workItems/${params.parentId}`,
317
- },
318
- },
319
- {
320
- op: "add",
321
- path: "/fields/System.AreaPath",
322
- value: params.areaPath,
323
- },
324
- {
325
- op: "add",
326
- path: "/fields/System.IterationPath",
327
- value: params.iterationPath,
328
- }
329
- ];
330
- const result = await handler(params);
331
- expect(mockWorkItemTrackingApi.createWorkItem).toHaveBeenCalledWith(null, document, params.project, params.workItemType);
332
- expect(result.content[0].text).toBe(JSON.stringify([
333
- _mockWorkItem
334
- ], null, 2));
335
- });
336
- });
337
- describe("link_work_item_to_pull_request tool", () => {
338
- it("should call workItemApi.updateWorkItem API with the correct parameters and return the expected result", async () => {
339
- configureWorkItemTools(server, tokenProvider, connectionProvider);
340
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_link_work_item_to_pull_request");
341
- if (!call)
342
- throw new Error("ado_link_work_item_to_pull_request tool not registered");
343
- const [, , , handler] = call;
344
- mockWorkItemTrackingApi.updateWorkItem.mockResolvedValue([
345
- _mockWorkItem,
346
- ]);
347
- const params = {
348
- project: "Contoso",
349
- repositoryId: 12345,
350
- pullRequestId: 67890,
351
- workItemId: 131489,
352
- };
353
- const artifactPathValue = `${params.project}/${params.repositoryId}/${params.pullRequestId}`;
354
- const vstfsUrl = `vstfs:///Git/PullRequestId/${encodeURIComponent(artifactPathValue)}`;
355
- const document = [
356
- {
357
- op: "add",
358
- path: "/relations/-",
359
- value: {
360
- rel: "ArtifactLink",
361
- url: vstfsUrl,
362
- attributes: {
363
- name: "Pull Request",
364
- },
365
- },
366
- },
367
- ];
368
- const result = await handler(params);
369
- expect(mockWorkItemTrackingApi.updateWorkItem).toHaveBeenCalledWith({}, document, params.workItemId, params.project);
370
- expect(result.content[0].text).toBe(JSON.stringify({
371
- workItemId: 131489,
372
- pullRequestId: 67890,
373
- success: true,
374
- }, null, 2));
375
- });
376
- });
377
- describe("get_work_items_for_iteration tool", () => {
378
- it("should call workApi.getIterationWorkItems API with the correct parameters and return the expected result", async () => {
379
- configureWorkItemTools(server, tokenProvider, connectionProvider);
380
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_get_work_items_for_iteration");
381
- if (!call)
382
- throw new Error("ado_get_work_items_for_iterationt tool not registered");
383
- const [, , , handler] = call;
384
- mockWorkApi.getIterationWorkItems.mockResolvedValue([
385
- _mockWorkItemsForIteration,
386
- ]);
387
- const params = {
388
- project: "Contoso",
389
- team: "Fabrikam",
390
- iterationId: "6bfde89e-b22e-422e-814a-e8db432f5a58",
391
- };
392
- const result = await handler(params);
393
- expect(mockWorkApi.getIterationWorkItems).toHaveBeenCalledWith({
394
- project: params.project,
395
- team: params.team
396
- }, params.iterationId);
397
- expect(result.content[0].text).toBe(JSON.stringify([
398
- _mockWorkItemsForIteration,
399
- ], null, 2));
400
- });
401
- });
402
- describe("update_work_item tool", () => {
403
- it("should call workItemApi.updateWorkItem API with the correct parameters and return the expected result", async () => {
404
- configureWorkItemTools(server, tokenProvider, connectionProvider);
405
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_update_work_item");
406
- if (!call)
407
- throw new Error("ado_update_work_item tool not registered");
408
- const [, , , handler] = call;
409
- mockWorkItemTrackingApi.updateWorkItem.mockResolvedValue([
410
- _mockWorkItem,
411
- ]);
412
- const params = {
413
- id: 131489,
414
- updates: [
415
- {
416
- op: "add",
417
- path: "/fields/System.Title",
418
- value: "Updated Sample Task"
419
- }
420
- ]
421
- };
422
- const result = await handler(params);
423
- expect(mockWorkItemTrackingApi.updateWorkItem).toHaveBeenCalledWith(null, params.updates, params.id);
424
- expect(result.content[0].text).toBe(JSON.stringify([
425
- _mockWorkItem
426
- ], null, 2));
427
- });
428
- });
429
- describe("get_work_item_type tool", () => {
430
- it("should call workItemApi.getWorkItemType API with the correct parameters and return the expected result", async () => {
431
- configureWorkItemTools(server, tokenProvider, connectionProvider);
432
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_get_work_item_type");
433
- if (!call)
434
- throw new Error("ado_get_work_item_type tool not registered");
435
- const [, , , handler] = call;
436
- mockWorkItemTrackingApi.getWorkItemType.mockResolvedValue([
437
- _mockWorkItemType,
438
- ]);
439
- const params = {
440
- project: "Contoso",
441
- workItemType: "Bug",
442
- };
443
- const result = await handler(params);
444
- expect(mockWorkItemTrackingApi.getWorkItemType).toHaveBeenCalledWith(params.project, params.workItemType);
445
- expect(result.content[0].text).toBe(JSON.stringify([
446
- _mockWorkItemType
447
- ], null, 2));
448
- });
449
- });
450
- describe("create_work_item tool", () => {
451
- it("should call workItemApi.createWorkItem API with the correct parameters and return the expected result", async () => {
452
- configureWorkItemTools(server, tokenProvider, connectionProvider);
453
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_create_work_item");
454
- if (!call)
455
- throw new Error("ado_create_work_item tool not registered");
456
- const [, , , handler] = call;
457
- mockWorkItemTrackingApi.createWorkItem.mockResolvedValue([
458
- _mockWorkItem,
459
- ]);
460
- const params = {
461
- project: "Contoso",
462
- workItemType: "Task",
463
- fields: [
464
- "System.Title", "Hello World!",
465
- "System.Description", "This is a sample task",
466
- "System.AreaPath", "Contoso\\Development",
467
- ]
468
- };
469
- const document = Object.entries(params.fields).map(([key, value]) => ({
470
- op: "add",
471
- path: `/fields/${key}`,
472
- value,
473
- }));
474
- const result = await handler(params);
475
- expect(mockWorkItemTrackingApi.createWorkItem).toHaveBeenCalledWith(null, document, params.project, params.workItemType);
476
- expect(result.content[0].text).toBe(JSON.stringify([
477
- _mockWorkItem
478
- ], null, 2));
479
- });
480
- });
481
- describe("get_query tool", () => {
482
- it("should call workItemApi.getQuery API with the correct parameters and return the expected result", async () => {
483
- configureWorkItemTools(server, tokenProvider, connectionProvider);
484
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_get_query");
485
- if (!call)
486
- throw new Error("ado_get_query tool not registered");
487
- const [, , , handler] = call;
488
- mockWorkItemTrackingApi.getQuery.mockResolvedValue([
489
- _mockQuery,
490
- ]);
491
- const params = {
492
- project: "Contoso",
493
- query: "342f0f44-4069-46b1-a940-3d0468979ceb",
494
- expand: "none",
495
- depth: 1,
496
- includeDeleted: false,
497
- useIsoDateFormat: false,
498
- };
499
- const result = await handler(params);
500
- expect(mockWorkItemTrackingApi.getQuery).toHaveBeenCalledWith(params.project, params.query, params.expand, params.depth, params.includeDeleted, params.useIsoDateFormat);
501
- expect(result.content[0].text).toBe(JSON.stringify([
502
- _mockQuery,
503
- ], null, 2));
504
- });
505
- });
506
- describe("get_query_results_by_id tool", () => {
507
- it("should call workItemApi.getQueryById API with the correct parameters and return the expected result", async () => {
508
- configureWorkItemTools(server, tokenProvider, connectionProvider);
509
- const call = server.tool.mock.calls.find(([toolName]) => toolName === "ado_get_query_results_by_id");
510
- if (!call)
511
- throw new Error("ado_get_query_results_by_id tool not registered");
512
- const [, , , handler] = call;
513
- mockWorkItemTrackingApi.queryById.mockResolvedValue([
514
- _mockQueryResults,
515
- ]);
516
- const params = {
517
- id: "342f0f44-4069-46b1-a940-3d0468979ceb",
518
- project: "Contoso",
519
- team: "Fabrikam",
520
- timePrecision: false,
521
- top: 50,
522
- };
523
- const result = await handler(params);
524
- expect(mockWorkItemTrackingApi.queryById).toHaveBeenCalledWith(params.id, { project: params.project, team: params.team }, params.timePrecision, params.top);
525
- expect(result.content[0].text).toBe(JSON.stringify([
526
- _mockQueryResults,
527
- ], null, 2));
528
- });
529
- });
530
- });