Skip to content

Commit 00fff77

Browse files
authored
Remove deprecated close_and_link_workitem_duplicates tool and its tests (#316)
Removing `wit_close_and_link_workitem_duplicates`. This is no longer needed and the scenario can be accomplished using work item link and work item updates. ## GitHub issue number N/A ## **Associated Risks** Low: Existing customers could be used to using the tool ## ✅ **PR Checklist** - [x] **I have read the [contribution guidelines](https://github.com/microsoft/azure-devops-mcp/blob/main/CONTRIBUTING.md)** - [x] **I have read the [code of conduct guidelines](https://github.com/microsoft/azure-devops-mcp/blob/main/CODE_OF_CONDUCT.md)** - [ ] Title of the pull request is clear and informative. - [x] 👌 Code hygiene - [x] 🔭 Telemetry added, updated, or N/A - [x] 📄 Documentation added, updated, or N/A - [x] 🛡️ Automated tests added, or N/A ## 🧪 **How did you test it?** Tested process manually. Updated tests
1 parent d70ab9d commit 00fff77

File tree

3 files changed

+1
-138
lines changed

3 files changed

+1
-138
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ Interact with these Azure DevOps services:
7373
- **wit_get_query**: Get a query by its ID or path.
7474
- **wit_get_query_results_by_id**: Retrieve the results of a work item query given the query ID.
7575
- **wit_update_work_items_batch**: Update work items in batch.
76-
- **wit_close_and_link_workitem_duplicates**: Close duplicate work items by ID.
7776
- **wit_work_items_link**: Link work items together in batch.
7877

7978
#### Deprecated Tools
8079

8180
- **wit_add_child_work_item**: Replaced by `wit_add_child_work_items` to allow creating one or more child items per call.
81+
- **wit_close_and_link_workitem_duplicates**: This tool is no longer needed. Finding and marking duplicates can be done with other tools.
8282

8383
### 📁 Repositories
8484

src/tools/workitems.ts

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ const WORKITEM_TOOLS = {
3636
get_query: "wit_get_query",
3737
get_query_results_by_id: "wit_get_query_results_by_id",
3838
update_work_items_batch: "wit_update_work_items_batch",
39-
close_and_link_workitem_duplicates: "wit_close_and_link_workitem_duplicates",
4039
work_items_link: "wit_work_items_link",
4140
};
4241

@@ -762,65 +761,6 @@ function configureWorkItemTools(server: McpServer, tokenProvider: () => Promise<
762761
};
763762
}
764763
);
765-
766-
server.tool(
767-
WORKITEM_TOOLS.close_and_link_workitem_duplicates,
768-
"Close duplicate work items by id.",
769-
{
770-
id: z.number().describe("The ID of the work item to close and link duplicates to."),
771-
duplicateIds: z.array(z.number()).describe("An array of IDs of the duplicate work items to close and link to the specified work item."),
772-
project: z.string().describe("The name or ID of the Azure DevOps project."),
773-
state: z.string().default("Removed").describe("The state to set for the duplicate work items. Defaults to 'Removed'."),
774-
},
775-
async ({ id, duplicateIds, project, state }) => {
776-
const connection = await connectionProvider();
777-
778-
const body = duplicateIds.map((duplicateId) => ({
779-
method: "PATCH",
780-
uri: `/_apis/wit/workitems/${duplicateId}?api-version=${batchApiVersion}`,
781-
headers: {
782-
"Content-Type": "application/json-patch+json",
783-
},
784-
body: [
785-
{
786-
op: "add",
787-
path: "/fields/System.State",
788-
value: `${state}`,
789-
},
790-
{
791-
op: "add",
792-
path: "/relations/-",
793-
value: {
794-
rel: "System.LinkTypes.Duplicate-Reverse",
795-
url: `${connection.serverUrl}/${project}/_apis/wit/workItems/${id}`,
796-
},
797-
},
798-
],
799-
}));
800-
801-
const accessToken = await tokenProvider();
802-
803-
const response = await fetch(`${connection.serverUrl}/_apis/wit/$batch?api-version=${batchApiVersion}`, {
804-
method: "PATCH",
805-
headers: {
806-
"Authorization": `Bearer ${accessToken.token}`,
807-
"Content-Type": "application/json",
808-
"User-Agent": userAgentProvider(),
809-
},
810-
body: JSON.stringify(body),
811-
});
812-
813-
if (!response.ok) {
814-
throw new Error(`Failed to update work items in batch: ${response.statusText}`);
815-
}
816-
817-
const result = await response.json();
818-
819-
return {
820-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
821-
};
822-
}
823-
);
824764
}
825765

826766
export { WORKITEM_TOOLS, configureWorkItemTools };

test/src/tools/workitems.test.ts

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,83 +1055,6 @@ describe("configureWorkItemTools", () => {
10551055
});
10561056
});
10571057

1058-
describe("close_and_link_workitem_duplicates tool", () => {
1059-
it("should close and link duplicate work items successfully", async () => {
1060-
configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider);
1061-
1062-
const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wit_close_and_link_workitem_duplicates");
1063-
if (!call) throw new Error("wit_close_and_link_workitem_duplicates tool not registered");
1064-
const [, , , handler] = call;
1065-
1066-
mockConnection.serverUrl = "https://dev.azure.com/contoso";
1067-
(tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
1068-
1069-
global.fetch = jest.fn().mockResolvedValue({
1070-
ok: true,
1071-
json: jest.fn().mockResolvedValue([
1072-
{ id: 2, success: true },
1073-
{ id: 3, success: true },
1074-
]),
1075-
});
1076-
1077-
const params = {
1078-
id: 1,
1079-
duplicateIds: [2, 3],
1080-
project: "TestProject",
1081-
state: "Closed",
1082-
};
1083-
1084-
const result = await handler(params);
1085-
1086-
expect(fetch).toHaveBeenCalledWith(
1087-
"https://dev.azure.com/contoso/_apis/wit/$batch?api-version=5.0",
1088-
expect.objectContaining({
1089-
method: "PATCH",
1090-
headers: expect.objectContaining({
1091-
"Authorization": "Bearer fake-token",
1092-
"Content-Type": "application/json",
1093-
}),
1094-
})
1095-
);
1096-
1097-
expect(result.content[0].text).toBe(
1098-
JSON.stringify(
1099-
[
1100-
{ id: 2, success: true },
1101-
{ id: 3, success: true },
1102-
],
1103-
null,
1104-
2
1105-
)
1106-
);
1107-
});
1108-
1109-
it("should handle duplicate closure failure", async () => {
1110-
configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider);
1111-
1112-
const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wit_close_and_link_workitem_duplicates");
1113-
if (!call) throw new Error("wit_close_and_link_workitem_duplicates tool not registered");
1114-
const [, , , handler] = call;
1115-
1116-
mockConnection.serverUrl = "https://dev.azure.com/contoso";
1117-
(tokenProvider as jest.Mock).mockResolvedValue({ token: "fake-token" });
1118-
1119-
global.fetch = jest.fn().mockResolvedValue({
1120-
ok: false,
1121-
statusText: "Forbidden",
1122-
});
1123-
1124-
const params = {
1125-
id: 1,
1126-
duplicateIds: [2, 3],
1127-
project: "TestProject",
1128-
state: "Removed",
1129-
};
1130-
1131-
await expect(handler(params)).rejects.toThrow("Failed to update work items in batch: Forbidden");
1132-
});
1133-
});
1134-
11351058
// Add error handling tests for existing tools
11361059
describe("error handling coverage", () => {
11371060
it("should handle create_work_item errors", async () => {

0 commit comments

Comments
 (0)