Skip to content

Commit c7ac0ef

Browse files
fbeltraoFranciscodanhellem
authored
Transform all possible identity fields to formatted strings in wit_get_work_items_batch_by_ids (#410)
The function `get_work_items_batch_by_ids` transforms the field `System.AssignedTo` to contain a simple `name <email>`. With pull request #385 , users can choose returned fields. It would be great to transform all possible identity fields: - System.AssignedTo - System.CreatedBy - System.ChangedBy - System.AuthorizedAs - Microsoft.VSTS.Common.ActivatedBy - Microsoft.VSTS.Common.ResolvedBy - Microsoft.VSTS.Common.ClosedBy The proposed implementation leverages a list of known identity fields. An alternative would be to detect if the field looks like an identity. The risk is that it might find false positives. ## GitHub issue number #409 ## **Associated Risks** _Replace_ by possible risks this pull request can bring you might have thought of ## ✅ **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)** - [x] 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?** - Added new unit test Co-authored-by: Francisco <[email protected]> Co-authored-by: Dan Hellem <[email protected]>
1 parent 51acb5f commit c7ac0ef

File tree

2 files changed

+98
-6
lines changed

2 files changed

+98
-6
lines changed

src/tools/workitems.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,30 @@ function configureWorkItemTools(server: McpServer, tokenProvider: () => Promise<
142142

143143
const workitems = await workItemApi.getWorkItemsBatch({ ids, fields: fieldsToUse }, project);
144144

145-
// Format the assignedTo field to include displayName and uniqueName
145+
// List of identity fields that need to be transformed from objects to formatted strings
146+
const identityFields = [
147+
"System.AssignedTo",
148+
"System.CreatedBy",
149+
"System.ChangedBy",
150+
"System.AuthorizedAs",
151+
"Microsoft.VSTS.Common.ActivatedBy",
152+
"Microsoft.VSTS.Common.ResolvedBy",
153+
"Microsoft.VSTS.Common.ClosedBy",
154+
];
155+
156+
// Format identity fields to include displayName and uniqueName
146157
// Removing the identity object as the response. It's too much and not needed
147158
if (workitems && Array.isArray(workitems)) {
148159
workitems.forEach((item) => {
149-
if (item.fields && item.fields["System.AssignedTo"] && typeof item.fields["System.AssignedTo"] === "object") {
150-
const assignedTo = item.fields["System.AssignedTo"];
151-
const name = assignedTo.displayName || "";
152-
const email = assignedTo.uniqueName || "";
153-
item.fields["System.AssignedTo"] = `${name} <${email}>`.trim();
160+
if (item.fields) {
161+
identityFields.forEach((fieldName) => {
162+
if (item.fields && item.fields[fieldName] && typeof item.fields[fieldName] === "object") {
163+
const identityField = item.fields[fieldName];
164+
const name = identityField.displayName || "";
165+
const email = identityField.uniqueName || "";
166+
item.fields[fieldName] = `${name} <${email}>`.trim();
167+
}
168+
});
154169
}
155170
});
156171
}

test/src/tools/workitems.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,83 @@ describe("configureWorkItemTools", () => {
571571

572572
expect(result.content[0].text).toBe(JSON.stringify(null, null, 2));
573573
});
574+
575+
it("should transform all user fields to formatted strings", async () => {
576+
configureWorkItemTools(server, tokenProvider, connectionProvider, userAgentProvider);
577+
578+
const call = (server.tool as jest.Mock).mock.calls.find(([toolName]) => toolName === "wit_get_work_items_batch_by_ids");
579+
580+
if (!call) throw new Error("wit_get_work_items_batch_by_ids tool not registered");
581+
const [, , , handler] = call;
582+
583+
// Mock work items with all user fields as objects
584+
const mockWorkItemsWithUserFields = [
585+
{
586+
id: 297,
587+
fields: {
588+
"System.Id": 297,
589+
"System.WorkItemType": "Bug",
590+
"System.Title": "Test Bug",
591+
"System.AssignedTo": {
592+
displayName: "John Doe",
593+
uniqueName: "[email protected]",
594+
id: "12345",
595+
},
596+
"System.CreatedBy": {
597+
displayName: "Jane Smith",
598+
uniqueName: "[email protected]",
599+
id: "67890",
600+
},
601+
"System.ChangedBy": {
602+
displayName: "Bob Johnson",
603+
uniqueName: "[email protected]",
604+
id: "11111",
605+
},
606+
"System.AuthorizedAs": {
607+
displayName: "Alice Brown",
608+
uniqueName: "[email protected]",
609+
id: "22222",
610+
},
611+
"Microsoft.VSTS.Common.ActivatedBy": {
612+
displayName: "Charlie Wilson",
613+
uniqueName: "[email protected]",
614+
id: "33333",
615+
},
616+
"Microsoft.VSTS.Common.ResolvedBy": {
617+
displayName: "Diana Clark",
618+
uniqueName: "[email protected]",
619+
id: "44444",
620+
},
621+
"Microsoft.VSTS.Common.ClosedBy": {
622+
displayName: "Edward Davis",
623+
uniqueName: "[email protected]",
624+
id: "55555",
625+
},
626+
},
627+
},
628+
];
629+
630+
(mockWorkItemTrackingApi.getWorkItemsBatch as jest.Mock).mockResolvedValue(mockWorkItemsWithUserFields);
631+
632+
const params = {
633+
ids: [297],
634+
project: "Contoso",
635+
};
636+
637+
const result = await handler(params);
638+
639+
// Parse the returned JSON to verify transformation
640+
const resultData = JSON.parse(result.content[0].text);
641+
642+
// Verify that all user fields are transformed to formatted strings
643+
expect(resultData[0].fields["System.AssignedTo"]).toBe("John Doe <[email protected]>");
644+
expect(resultData[0].fields["System.CreatedBy"]).toBe("Jane Smith <[email protected]>");
645+
expect(resultData[0].fields["System.ChangedBy"]).toBe("Bob Johnson <[email protected]>");
646+
expect(resultData[0].fields["System.AuthorizedAs"]).toBe("Alice Brown <[email protected]>");
647+
expect(resultData[0].fields["Microsoft.VSTS.Common.ActivatedBy"]).toBe("Charlie Wilson <[email protected]>");
648+
expect(resultData[0].fields["Microsoft.VSTS.Common.ResolvedBy"]).toBe("Diana Clark <[email protected]>");
649+
expect(resultData[0].fields["Microsoft.VSTS.Common.ClosedBy"]).toBe("Edward Davis <[email protected]>");
650+
});
574651
});
575652

576653
describe("get_work_item tool", () => {

0 commit comments

Comments
 (0)