@achieveai/azuredevops-mcp 1.3.4 → 1.3.6

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/dist/index.js CHANGED
@@ -17,7 +17,39 @@ const WikiTools_1 = require("./Tools/WikiTools");
17
17
  const zod_1 = require("zod");
18
18
  const Common_1 = require("./Interfaces/Common");
19
19
  /** Wrap a value so string-encoded JSON arrays are auto-parsed (some MCP clients send arrays as strings). */
20
- const coerceArray = (val) => typeof val === 'string' ? JSON.parse(val) : val;
20
+ const coerceArray = (val) => {
21
+ if (typeof val !== 'string')
22
+ return val;
23
+ try {
24
+ return JSON.parse(val);
25
+ }
26
+ catch {
27
+ throw new Error(`Expected a JSON array (e.g. [1, 2, 3]) but received: "${val.length > 50 ? val.slice(0, 50) + '...' : val}"`);
28
+ }
29
+ };
30
+ /**
31
+ * Strip common prefixes/symbols from ID values so LLMs can pass
32
+ * "123", "#123", "WI#123", "PR 456", "BUILD#789", etc.
33
+ * Returns a number or NaN (which z.number() will reject with a clear error).
34
+ */
35
+ const coerceId = (val) => {
36
+ if (typeof val === 'number')
37
+ return val;
38
+ if (typeof val !== 'string')
39
+ return NaN;
40
+ // Strip known prefixes: WI#, PR#, BUILD#, leading #, or any "Word " / "Word#" prefix
41
+ const cleaned = val.trim().replace(/^(?:[A-Za-z]+[#\s])?#?/, '');
42
+ if (cleaned === '')
43
+ return NaN;
44
+ const num = Number(cleaned);
45
+ return Number.isSafeInteger(num) && num > 0 ? num : NaN;
46
+ };
47
+ /** Zod schema for an ID parameter — accepts numbers and common LLM string formats like "#123", "WI#123", "PR 456" */
48
+ const zId = () => zod_1.z.union([zod_1.z.number(), zod_1.z.string()]).transform(coerceId).pipe(zod_1.z.number().int().min(1));
49
+ /** Zod schema for an optional ID parameter */
50
+ const zIdOptional = () => zod_1.z.union([zod_1.z.number(), zod_1.z.string()]).transform(coerceId).pipe(zod_1.z.number().int().min(1)).optional();
51
+ /** Zod schema for an array of IDs */
52
+ const zIdArray = () => zod_1.z.preprocess(coerceArray, zod_1.z.array(zod_1.z.union([zod_1.z.number(), zod_1.z.string()]).transform(coerceId).pipe(zod_1.z.number().int().min(1))));
21
53
  const EntraAuthHandler_1 = require("./Services/EntraAuthHandler");
22
54
  async function main() {
23
55
  try {
@@ -71,7 +103,7 @@ async function main() {
71
103
  };
72
104
  });
73
105
  allowedTools.has("getWorkItemById") && server.tool("getWorkItemById", "Get a specific work item by ID with summary and detailed view", {
74
- id: zod_1.z.coerce.number().describe("Work item ID")
106
+ id: zId().describe("Work item ID")
75
107
  }, async (params, extra) => {
76
108
  const result = await workItemTools.getWorkItemById({ id: params.id });
77
109
  return {
@@ -134,7 +166,7 @@ async function main() {
134
166
  };
135
167
  });
136
168
  allowedTools.has("updateWorkItem") && server.tool("updateWorkItem", "Update an existing work item", {
137
- id: zod_1.z.coerce.number().describe("ID of the work item to update"),
169
+ id: zId().describe("ID of the work item to update"),
138
170
  fields: zod_1.z.record(zod_1.z.any()).describe("Fields to update on the work item")
139
171
  }, async (params, extra) => {
140
172
  const result = await workItemTools.updateWorkItem(params);
@@ -146,9 +178,10 @@ async function main() {
146
178
  };
147
179
  });
148
180
  allowedTools.has("getWorkItemComments") && server.tool("getWorkItemComments", "Get comments on a work item", {
149
- id: zod_1.z.coerce.number().describe("ID of the work item"),
181
+ id: zId().describe("ID of the work item"),
150
182
  top: zod_1.z.coerce.number().optional().describe("Maximum number of comments to return"),
151
- order: zod_1.z.enum(['asc', 'desc']).optional().default('desc').describe("Sort order: 'asc' (oldest first) or 'desc' (newest first)")
183
+ order: zod_1.z.enum(['asc', 'desc']).optional().default('desc').describe("Sort order: 'asc' (oldest first) or 'desc' (newest first)"),
184
+ includeDeleted: zod_1.z.boolean().optional().describe("Include deleted comments")
152
185
  }, async (params, extra) => {
153
186
  const result = await workItemTools.getWorkItemComments(params);
154
187
  return {
@@ -159,7 +192,7 @@ async function main() {
159
192
  };
160
193
  });
161
194
  allowedTools.has("addWorkItemComment") && server.tool("addWorkItemComment", "Add a comment to a work item. Supports markdown formatting by default.", {
162
- id: zod_1.z.coerce.number().describe("ID of the work item"),
195
+ id: zId().describe("ID of the work item"),
163
196
  text: zod_1.z.string().describe("Comment text (supports markdown when format is 'markdown')"),
164
197
  format: zod_1.z.enum(['markdown', 'html']).optional().default('markdown').describe("Comment format: 'markdown' (default) or 'html'")
165
198
  }, async (params, extra) => {
@@ -172,7 +205,7 @@ async function main() {
172
205
  };
173
206
  });
174
207
  allowedTools.has("updateWorkItemState") && server.tool("updateWorkItemState", "Update the state of a work item", {
175
- id: zod_1.z.coerce.number().describe("ID of the work item"),
208
+ id: zId().describe("ID of the work item"),
176
209
  state: zod_1.z.string().describe("New state for the work item"),
177
210
  comment: zod_1.z.string().optional().describe("Comment explaining the state change")
178
211
  }, async (params, extra) => {
@@ -184,7 +217,7 @@ async function main() {
184
217
  };
185
218
  });
186
219
  allowedTools.has("assignWorkItem") && server.tool("assignWorkItem", "Assign a work item to a user", {
187
- id: zod_1.z.coerce.number().describe("ID of the work item"),
220
+ id: zId().describe("ID of the work item"),
188
221
  assignedTo: zod_1.z.string().describe("User to assign the work item to")
189
222
  }, async (params, extra) => {
190
223
  const result = await workItemTools.assignWorkItem(params);
@@ -198,7 +231,7 @@ async function main() {
198
231
  "Use prefixes for targetId: WI#123 (work item), PR#456 (pull request), " +
199
232
  "BUILD#789 (build), BRANCH#main (branch), COMMIT#abc123 (commit). " +
200
233
  "Plain numbers default to work item.", {
201
- sourceId: zod_1.z.coerce.number().describe("ID of the source work item"),
234
+ sourceId: zId().describe("ID of the source work item"),
202
235
  targetId: zod_1.z.string().describe("Target with prefix: WI#123, PR#456, BUILD#789, BRANCH#main, COMMIT#abc, or plain number for work item"),
203
236
  linkType: zod_1.z.string().describe("Link type (e.g., System.LinkTypes.Related). For artifact prefixes, this is still required but the actual rel type is auto-set to ArtifactLink."),
204
237
  comment: zod_1.z.string().optional().describe("Comment explaining the link"),
@@ -225,7 +258,7 @@ async function main() {
225
258
  additionalFields: zod_1.z.record(zod_1.z.any()).optional().describe("Additional fields to set on the work item")
226
259
  }),
227
260
  zod_1.z.object({
228
- id: zod_1.z.coerce.number().describe("ID of work item to update"),
261
+ id: zId().describe("ID of work item to update"),
229
262
  fields: zod_1.z.record(zod_1.z.any()).describe("Fields to update on the work item")
230
263
  })
231
264
  ])).min(1).describe("Array of work items to create or update")
@@ -238,14 +271,14 @@ async function main() {
238
271
  };
239
272
  });
240
273
  allowedTools.has("getWorkItemsBatch") && server.tool("getWorkItemsBatch", "Get multiple work items by their IDs in a single efficient call", {
241
- ids: zod_1.z.preprocess(coerceArray, zod_1.z.array(zod_1.z.coerce.number())).describe("Array of work item IDs to retrieve"),
274
+ ids: zIdArray().describe("Array of work item IDs to retrieve"),
242
275
  fields: zod_1.z.preprocess(coerceArray, zod_1.z.array(zod_1.z.string()).optional()).describe("Specific fields to return (e.g., ['System.Title', 'System.State'])"),
243
276
  }, async (params) => {
244
277
  const result = await workItemTools.getWorkItemsBatch(params);
245
278
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
246
279
  });
247
280
  allowedTools.has("getWorkItemRevisions") && server.tool("getWorkItemRevisions", "Get revision/change history for a work item showing all field changes over time", {
248
- id: zod_1.z.coerce.number().describe("Work item ID"),
281
+ id: zId().describe("Work item ID"),
249
282
  top: zod_1.z.coerce.number().optional().describe("Maximum number of revisions to return"),
250
283
  skip: zod_1.z.coerce.number().optional().describe("Number of revisions to skip"),
251
284
  }, async (params) => {
@@ -259,7 +292,7 @@ async function main() {
259
292
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
260
293
  });
261
294
  allowedTools.has("addChildWorkItem") && server.tool("addChildWorkItem", "Create a new work item and link it as a child of an existing parent work item", {
262
- parentId: zod_1.z.coerce.number().describe("Parent work item ID"),
295
+ parentId: zId().describe("Parent work item ID"),
263
296
  workItemType: zod_1.z.string().describe("Type of child work item (e.g., 'Task', 'Bug')"),
264
297
  title: zod_1.z.string().describe("Title of the child work item"),
265
298
  description: zod_1.z.string().optional().describe("Description"),
@@ -273,7 +306,7 @@ async function main() {
273
306
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
274
307
  });
275
308
  allowedTools.has("unlinkWorkItem") && server.tool("unlinkWorkItem", "Remove a link/relation from a work item by its relation index. Use getWorkItemById first to see the relations and their indices.", {
276
- id: zod_1.z.coerce.number().describe("Work item ID"),
309
+ id: zId().describe("Work item ID"),
277
310
  relationIndex: zod_1.z.coerce.number().describe("Index of the relation to remove (0-based, from the relations array)"),
278
311
  }, async (params) => {
279
312
  const result = await workItemTools.unlinkWorkItem(params);
@@ -315,7 +348,7 @@ async function main() {
315
348
  allowedTools.has("moveCardOnBoard") && server.tool("moveCardOnBoard", "Move a card on a board", {
316
349
  teamId: zod_1.z.string().optional().describe("Team ID (uses default team if not specified)"),
317
350
  boardId: zod_1.z.string().describe("ID of the board"),
318
- workItemId: zod_1.z.coerce.number().describe("ID of the work item to move"),
351
+ workItemId: zId().describe("ID of the work item to move"),
319
352
  columnId: zod_1.z.string().describe("ID of the column to move to"),
320
353
  position: zod_1.z.coerce.number().optional().describe("Position within the column")
321
354
  }, async (params, extra) => {
@@ -659,7 +692,7 @@ async function main() {
659
692
  "'files' (changed file list — prefer getAllPullRequestChanges for large diffs). " +
660
693
  "Supports both repository names and IDs.", {
661
694
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
662
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request to retrieve. This is the PR number shown in the Azure DevOps UI (e.g., PR #123)."),
695
+ pullRequestId: zId().describe("The numeric ID of the pull request to retrieve. This is the PR number shown in the Azure DevOps UI (e.g., PR #123)."),
663
696
  include: zod_1.z.array(zod_1.z.enum(['policies', 'description', 'reviewers', 'workItems', 'completionOptions', 'files'])).optional().describe("Sections to return in full detail. When omitted, returns compact overview with all sections truncated. Specify sections you need full data for.")
664
697
  }, async (params, extra) => {
665
698
  const result = await gitTools.getPullRequest(params);
@@ -671,8 +704,8 @@ async function main() {
671
704
  });
672
705
  allowedTools.has("getPullRequestComments") && server.tool("getPullRequestComments", "Retrieve all comment threads and associated comments for a pull request. Returns a summary and organized list of threads including code review comments (with file/line context) and general discussions. Optionally filter by thread status (e.g., only 'active' unresolved threads), author name, specific thread ID, or use pagination. Supports both repository names and IDs.", {
673
706
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
674
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request to retrieve comments from. This is the PR number shown in the Azure DevOps UI."),
675
- threadId: zod_1.z.coerce.number().optional().describe("Optional ID of a specific comment thread to retrieve. If provided, only returns the specified thread rather than all threads."),
707
+ pullRequestId: zId().describe("The numeric ID of the pull request to retrieve comments from. This is the PR number shown in the Azure DevOps UI."),
708
+ threadId: zIdOptional().describe("Optional ID of a specific comment thread to retrieve. If provided, only returns the specified thread rather than all threads."),
676
709
  status: zod_1.z.enum(['active', 'fixed', 'wontFix', 'closed', 'byDesign', 'pending']).optional().describe("Filter threads by status. Use 'active' to see only unresolved threads, 'fixed' for resolved ones, etc."),
677
710
  authorName: zod_1.z.string().optional().describe("Filter threads by author display name or email (case-insensitive partial match). Only threads where the root comment was authored by a matching user are returned."),
678
711
  top: zod_1.z.coerce.number().optional().describe("Maximum number of comment threads to return in the response. Use this for pagination in PRs with many comments."),
@@ -688,7 +721,7 @@ async function main() {
688
721
  });
689
722
  allowedTools.has("approvePullRequest") && server.tool("approvePullRequest", "Cast an 'Approve' vote on a pull request on behalf of the current authenticated user. This marks the PR as approved by the user and contributes toward satisfying approval requirements defined in branch policies. Equivalent to clicking 'Approve' in the Azure DevOps UI. Supports both repository names and IDs.", {
690
723
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
691
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request to approve. This is the PR number shown in the Azure DevOps UI (e.g., PR #123).")
724
+ pullRequestId: zId().describe("The numeric ID of the pull request to approve. This is the PR number shown in the Azure DevOps UI (e.g., PR #123).")
692
725
  }, async (params, extra) => {
693
726
  const result = await gitTools.approvePullRequest(params);
694
727
  return {
@@ -699,7 +732,7 @@ async function main() {
699
732
  });
700
733
  allowedTools.has("mergePullRequest") && server.tool("mergePullRequest", "Complete a pull request by merging the source branch changes into the target branch. This operation requires that all required reviewers have approved the PR and all branch policies are satisfied. Supports different merge strategies (squash, rebase, etc.) and allows adding a custom commit message. Supports both repository names and IDs.", {
701
734
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
702
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request to merge. This is the PR number shown in the Azure DevOps UI (e.g., PR #123)."),
735
+ pullRequestId: zId().describe("The numeric ID of the pull request to merge. This is the PR number shown in the Azure DevOps UI (e.g., PR #123)."),
703
736
  mergeStrategy: zod_1.z.enum(['noFastForward', 'rebase', 'rebaseMerge', 'squash']).optional().describe("The strategy to use when merging changes: 'noFastForward' creates a merge commit, 'rebase' updates the source branch commits onto the target branch, 'rebaseMerge' combines rebase with a merge commit, 'squash' combines all changes into a single commit."),
704
737
  comment: zod_1.z.string().optional().describe("Optional comment to include in the merge commit message. Use this to provide additional context about the merge beyond the default message.")
705
738
  }, async (params, extra) => {
@@ -713,7 +746,7 @@ async function main() {
713
746
  // Register new Pull Request Comment Tools
714
747
  allowedTools.has("addPullRequestInlineComment") && server.tool("addPullRequestInlineComment", "Add an inline code comment anchored to a SPECIFIC LINE of code in a file. The comment appears directly on that line in the Files tab. WHEN TO USE: Point out specific code issues, suggest improvements to a particular line, or ask questions about specific implementation details. EXAMPLES: 'This variable should be null-checked here', 'Consider using async/await on line 45', 'Why is this hardcoded?'. The system automatically retrieves the correct change tracking ID from the PR diff.", {
715
748
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
716
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request where the comment will be added. This is the PR number shown in the Azure DevOps UI."),
749
+ pullRequestId: zId().describe("The numeric ID of the pull request where the comment will be added. This is the PR number shown in the Azure DevOps UI."),
717
750
  comment: zod_1.z.string().describe("The text content of the comment to add. Can include markdown formatting. Should be specific to the line of code being commented on."),
718
751
  position: zod_1.z.object({
719
752
  line: zod_1.z.coerce.number().describe("The 1-based line number in the file where the comment starts. Must be a line visible in the PR diff (added, removed, or context line)."),
@@ -732,7 +765,7 @@ async function main() {
732
765
  });
733
766
  allowedTools.has("addPullRequestFileComment") && server.tool("addPullRequestFileComment", "Add a comment about an ENTIRE FILE (not a specific line). The comment appears at the file level in the Files tab. WHEN TO USE: Discuss overall file structure, architecture decisions, naming conventions, or when feedback applies to the whole file. EXAMPLES: 'This file should be split into smaller modules', 'Consider moving this to a different namespace', 'Great refactoring of this entire service!', 'This file needs unit tests'.", {
734
767
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
735
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request where the comment will be added. This is the PR number shown in the Azure DevOps UI."),
768
+ pullRequestId: zId().describe("The numeric ID of the pull request where the comment will be added. This is the PR number shown in the Azure DevOps UI."),
736
769
  path: zod_1.z.string().describe("The full path to the file within the repository that the comment relates to. Must be a file changed in the PR (e.g., '/src/Models/User.cs')."),
737
770
  comment: zod_1.z.string().describe("The text content of the comment about the entire file. Can include markdown formatting. Should address file-level concerns, not specific lines.")
738
771
  }, async (params, extra) => {
@@ -745,7 +778,7 @@ async function main() {
745
778
  });
746
779
  allowedTools.has("addPullRequestComment") && server.tool("addPullRequestComment", "Add a GENERAL comment about the entire pull request (not tied to any file or code). Appears in the Overview/Conversation tab. WHEN TO USE: Provide overall feedback, discuss architecture, approve/reject the PR, ask general questions, or comment on the PR description. EXAMPLES: 'This feature looks great! LGTM after CI passes', 'Can you add integration tests for this feature?', 'What's the performance impact of these changes?', 'Please update the documentation before merging'.", {
747
780
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
748
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request where the comment will be added. This is the PR number shown in the Azure DevOps UI."),
781
+ pullRequestId: zId().describe("The numeric ID of the pull request where the comment will be added. This is the PR number shown in the Azure DevOps UI."),
749
782
  comment: zod_1.z.string().describe("The text content of the general comment about the PR. Can include markdown formatting for rich text, code blocks, links, etc. Should address PR-level concerns, not specific files or lines.")
750
783
  }, async (params, extra) => {
751
784
  const result = await gitTools.addPullRequestComment(params);
@@ -758,7 +791,7 @@ async function main() {
758
791
  // Register new Pull Request Diff Tools
759
792
  allowedTools.has("getPullRequestFileChanges") && server.tool("getPullRequestFileChanges", "Retrieve detailed file diff information for a specific file changed within a pull request. Returns change metadata including change type (add, edit, delete), before/after content identifiers, and file path information. Optionally filter to a specific file path.", {
760
793
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
761
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request to examine. This is the PR number shown in the Azure DevOps UI."),
794
+ pullRequestId: zId().describe("The numeric ID of the pull request to examine. This is the PR number shown in the Azure DevOps UI."),
762
795
  path: zod_1.z.string().optional().describe("Optional path to a specific file to return changes for. If omitted, changes for all files will be returned but filtered to match this specific path.")
763
796
  }, async (params, extra) => {
764
797
  const result = await gitTools.getPullRequestFileChanges(params);
@@ -770,7 +803,7 @@ async function main() {
770
803
  });
771
804
  allowedTools.has("getPullRequestChangesCount") && server.tool("getPullRequestChangesCount", "Get statistical summary of changes in a pull request, including total count of files changed and breakdowns by change type (added, modified, deleted). Useful for understanding the scope of changes in a PR at a glance.", {
772
805
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
773
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request to analyze. This is the PR number shown in the Azure DevOps UI.")
806
+ pullRequestId: zId().describe("The numeric ID of the pull request to analyze. This is the PR number shown in the Azure DevOps UI.")
774
807
  }, async (params, extra) => {
775
808
  const result = await gitTools.getPullRequestChangesCount(params);
776
809
  return {
@@ -781,7 +814,7 @@ async function main() {
781
814
  });
782
815
  allowedTools.has("getAllPullRequestChanges") && server.tool("getAllPullRequestChanges", "Retrieve a comprehensive list of all file changes in a pull request with pagination support. Returns a summary and table of changed files with change types. Use pagination parameters (top/skip) to handle large PRs with many file changes. This does NOT include diff content - use getPullRequestFileChanges for detailed diffs.", {
783
816
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
784
- pullRequestId: zod_1.z.coerce.number().describe("The numeric ID of the pull request to retrieve changes for. This is the PR number shown in the Azure DevOps UI."),
817
+ pullRequestId: zId().describe("The numeric ID of the pull request to retrieve changes for. This is the PR number shown in the Azure DevOps UI."),
785
818
  top: zod_1.z.coerce.number().optional().describe("Maximum number of change entries to return in a single request. Use this for pagination to avoid large response payloads."),
786
819
  skip: zod_1.z.coerce.number().optional().describe("Number of change entries to skip before starting to return results. Use with 'top' for implementing pagination.")
787
820
  }, async (params, extra) => {
@@ -795,7 +828,7 @@ async function main() {
795
828
  });
796
829
  allowedTools.has("updatePullRequest") && server.tool("updatePullRequest", "Update pull request properties (title, description, status, auto-complete, draft mode, target branch)", {
797
830
  repository: zod_1.z.string().describe("Repository name or ID"),
798
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
831
+ pullRequestId: zId().describe("Pull request ID"),
799
832
  title: zod_1.z.string().optional().describe("New title for the PR"),
800
833
  description: zod_1.z.string().optional().describe("New description for the PR"),
801
834
  status: zod_1.z.enum(['active', 'abandoned', 'completed']).optional().describe("New status"),
@@ -810,7 +843,7 @@ async function main() {
810
843
  });
811
844
  allowedTools.has("updatePullRequestReviewers") && server.tool("updatePullRequestReviewers", "Add or remove reviewers on an existing pull request", {
812
845
  repository: zod_1.z.string().describe("Repository name or ID"),
813
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
846
+ pullRequestId: zId().describe("Pull request ID"),
814
847
  reviewersToAdd: zod_1.z.array(zod_1.z.string()).optional().describe("User IDs or email addresses to add as reviewers"),
815
848
  reviewersToRemove: zod_1.z.array(zod_1.z.string()).optional().describe("User IDs or email addresses to remove from reviewers"),
816
849
  makeRequired: zod_1.z.boolean().optional().describe("Make added reviewers required (default false)"),
@@ -820,8 +853,8 @@ async function main() {
820
853
  });
821
854
  allowedTools.has("replyToComment") && server.tool("replyToComment", "Reply to an existing comment thread on a pull request", {
822
855
  repository: zod_1.z.string().describe("Repository name or ID"),
823
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
824
- threadId: zod_1.z.coerce.number().describe("Thread ID to reply to"),
856
+ pullRequestId: zId().describe("Pull request ID"),
857
+ threadId: zId().describe("Thread ID to reply to"),
825
858
  comment: zod_1.z.string().describe("Reply text content (supports markdown)"),
826
859
  }, async (params) => {
827
860
  const result = await gitTools.replyToComment(params);
@@ -829,8 +862,8 @@ async function main() {
829
862
  });
830
863
  allowedTools.has("updatePullRequestThread") && server.tool("updatePullRequestThread", "Update a comment thread's status (resolve, reactivate, close, etc.)", {
831
864
  repository: zod_1.z.string().describe("Repository name or ID"),
832
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
833
- threadId: zod_1.z.coerce.number().describe("Thread ID to update"),
865
+ pullRequestId: zId().describe("Pull request ID"),
866
+ threadId: zId().describe("Thread ID to update"),
834
867
  status: zod_1.z.enum(['active', 'byDesign', 'closed', 'fixed', 'pending', 'unknown', 'wontFix']).describe("New thread status"),
835
868
  }, async (params) => {
836
869
  const result = await gitTools.updatePullRequestThread(params);
@@ -846,8 +879,8 @@ async function main() {
846
879
  });
847
880
  // Register Testing Capabilities Tools
848
881
  allowedTools.has("runAutomatedTests") && server.tool("runAutomatedTests", "Execute automated test suites", {
849
- testSuiteId: zod_1.z.coerce.number().optional().describe("ID of the test suite to run"),
850
- testPlanId: zod_1.z.coerce.number().optional().describe("ID of the test plan to run"),
882
+ testSuiteId: zIdOptional().describe("ID of the test suite to run"),
883
+ testPlanId: zIdOptional().describe("ID of the test plan to run"),
851
884
  testEnvironment: zod_1.z.string().optional().describe("Environment to run tests in"),
852
885
  parallelExecution: zod_1.z.boolean().optional().describe("Whether to run tests in parallel")
853
886
  }, async (params, extra) => {
@@ -859,7 +892,7 @@ async function main() {
859
892
  };
860
893
  });
861
894
  allowedTools.has("getTestAutomationStatus") && server.tool("getTestAutomationStatus", "Check status of automated test execution", {
862
- testRunId: zod_1.z.coerce.number().describe("ID of the test run to check status for")
895
+ testRunId: zId().describe("ID of the test run to check status for")
863
896
  }, async (params, extra) => {
864
897
  const result = await testingCapabilitiesTools.getTestAutomationStatus(params);
865
898
  return {
@@ -905,8 +938,8 @@ async function main() {
905
938
  };
906
939
  });
907
940
  allowedTools.has("getTestFlakiness") && server.tool("getTestFlakiness", "Analyze and report on test flakiness", {
908
- testId: zod_1.z.coerce.number().optional().describe("ID of a specific test to analyze"),
909
- testRunIds: zod_1.z.array(zod_1.z.coerce.number()).optional().describe("Specific test runs to analyze"),
941
+ testId: zIdOptional().describe("ID of a specific test to analyze"),
942
+ testRunIds: zIdArray().optional().describe("Specific test runs to analyze"),
910
943
  timeRange: zod_1.z.string().optional().describe("Time range for analysis (e.g., '30d')")
911
944
  }, async (params, extra) => {
912
945
  const result = await testingCapabilitiesTools.getTestFlakiness(params);
@@ -928,7 +961,7 @@ async function main() {
928
961
  };
929
962
  });
930
963
  allowedTools.has("runTestImpactAnalysis") && server.tool("runTestImpactAnalysis", "Determine which tests to run based on code changes", {
931
- buildId: zod_1.z.coerce.number().describe("ID of the build to analyze"),
964
+ buildId: zId().describe("ID of the build to analyze"),
932
965
  changedFiles: zod_1.z.array(zod_1.z.string()).optional().describe("List of changed files")
933
966
  }, async (params, extra) => {
934
967
  const result = await testingCapabilitiesTools.runTestImpactAnalysis(params);
@@ -950,7 +983,7 @@ async function main() {
950
983
  };
951
984
  });
952
985
  allowedTools.has("runTestOptimization") && server.tool("runTestOptimization", "Optimize test suite execution for faster feedback", {
953
- testPlanId: zod_1.z.coerce.number().describe("ID of the test plan to optimize"),
986
+ testPlanId: zId().describe("ID of the test plan to optimize"),
954
987
  optimizationGoal: zod_1.z.enum(['time', 'coverage', 'reliability']).describe("Optimization goal")
955
988
  }, async (params, extra) => {
956
989
  const result = await testingCapabilitiesTools.runTestOptimization(params);
@@ -973,7 +1006,7 @@ async function main() {
973
1006
  };
974
1007
  });
975
1008
  allowedTools.has("recordExploratoryTestResults") && server.tool("recordExploratoryTestResults", "Record findings during exploratory testing", {
976
- sessionId: zod_1.z.coerce.number().describe("ID of the exploratory session"),
1009
+ sessionId: zId().describe("ID of the exploratory session"),
977
1010
  findings: zod_1.z.array(zod_1.z.string()).describe("List of findings to record"),
978
1011
  attachments: zod_1.z.array(zod_1.z.object({
979
1012
  name: zod_1.z.string().describe("Name of the attachment"),
@@ -989,8 +1022,8 @@ async function main() {
989
1022
  };
990
1023
  });
991
1024
  allowedTools.has("convertFindingsToWorkItems") && server.tool("convertFindingsToWorkItems", "Convert exploratory test findings to work items", {
992
- sessionId: zod_1.z.coerce.number().describe("ID of the exploratory session"),
993
- findingIds: zod_1.z.array(zod_1.z.coerce.number()).describe("IDs of findings to convert"),
1025
+ sessionId: zId().describe("ID of the exploratory session"),
1026
+ findingIds: zIdArray().describe("IDs of findings to convert"),
994
1027
  workItemType: zod_1.z.string().optional().describe("Type of work item to create")
995
1028
  }, async (params, extra) => {
996
1029
  const result = await testingCapabilitiesTools.convertFindingsToWorkItems(params);
@@ -1316,7 +1349,7 @@ async function main() {
1316
1349
  });
1317
1350
  // AI Assisted Development Tools
1318
1351
  allowedTools.has("getAICodeReview") && server.tool("getAICodeReview", "Get AI-based code review suggestions", {
1319
- pullRequestId: zod_1.z.coerce.number().optional().describe("ID of the pull request to review"),
1352
+ pullRequestId: zIdOptional().describe("ID of the pull request to review"),
1320
1353
  repositoryId: zod_1.z.string().optional().describe("ID of the repository"),
1321
1354
  commitId: zod_1.z.string().optional().describe("ID of the commit to review"),
1322
1355
  filePath: zod_1.z.string().optional().describe("Path to the file to review")
@@ -1357,7 +1390,7 @@ async function main() {
1357
1390
  });
1358
1391
  allowedTools.has("getPredictiveBugAnalysis") && server.tool("getPredictiveBugAnalysis", "Predict potential bugs in code changes. Supports both repository names and IDs.", {
1359
1392
  repository: zod_1.z.string().describe("Repository name (e.g., 'MyProject') or ID (GUID). Repository names are case-insensitive."),
1360
- pullRequestId: zod_1.z.coerce.number().optional().describe("ID of the pull request"),
1393
+ pullRequestId: zIdOptional().describe("ID of the pull request"),
1361
1394
  branch: zod_1.z.string().optional().describe("Branch to analyze"),
1362
1395
  filePath: zod_1.z.string().optional().describe("Path to the file to analyze")
1363
1396
  }, async (params, extra) => {
@@ -1382,7 +1415,7 @@ async function main() {
1382
1415
  };
1383
1416
  });
1384
1417
  allowedTools.has("getPredictiveEffortEstimation") && server.tool("getPredictiveEffortEstimation", "AI-based effort estimation for work items", {
1385
- workItemIds: zod_1.z.array(zod_1.z.coerce.number()).optional().describe("IDs of work items to estimate"),
1418
+ workItemIds: zIdArray().optional().describe("IDs of work items to estimate"),
1386
1419
  workItemType: zod_1.z.string().optional().describe("Type of work items to estimate"),
1387
1420
  areaPath: zod_1.z.string().optional().describe("Area path to filter work items")
1388
1421
  }, async (params, extra) => {
@@ -1407,7 +1440,7 @@ async function main() {
1407
1440
  };
1408
1441
  });
1409
1442
  allowedTools.has("suggestWorkItemRefinements") && server.tool("suggestWorkItemRefinements", "Get AI suggestions for work item refinements", {
1410
- workItemId: zod_1.z.coerce.number().optional().describe("ID of the work item to refine"),
1443
+ workItemId: zIdOptional().describe("ID of the work item to refine"),
1411
1444
  workItemType: zod_1.z.string().optional().describe("Type of work item"),
1412
1445
  areaPath: zod_1.z.string().optional().describe("Area path to filter work items")
1413
1446
  }, async (params, extra) => {
@@ -1443,7 +1476,7 @@ async function main() {
1443
1476
  };
1444
1477
  });
1445
1478
  allowedTools.has("predictBuildFailures") && server.tool("predictBuildFailures", "Predict potential build failures before they occur", {
1446
- buildDefinitionId: zod_1.z.coerce.number().describe("ID of the build definition"),
1479
+ buildDefinitionId: zId().describe("ID of the build definition"),
1447
1480
  lookbackPeriod: zod_1.z.string().optional().describe("Period to analyze for patterns (e.g., '30d')")
1448
1481
  }, async (params, extra) => {
1449
1482
  const result = await aiAssistedDevelopmentTools.predictBuildFailures(params);
@@ -1454,7 +1487,7 @@ async function main() {
1454
1487
  };
1455
1488
  });
1456
1489
  allowedTools.has("optimizeTestSelection") && server.tool("optimizeTestSelection", "Intelligently select tests to run based on changes", {
1457
- buildId: zod_1.z.coerce.number().describe("ID of the build"),
1490
+ buildId: zId().describe("ID of the build"),
1458
1491
  changedFiles: zod_1.z.array(zod_1.z.string()).optional().describe("List of changed files"),
1459
1492
  maxTestCount: zod_1.z.coerce.number().optional().describe("Maximum number of tests to select")
1460
1493
  }, async (params, extra) => {
@@ -1508,7 +1541,7 @@ async function main() {
1508
1541
  });
1509
1542
  // Register Build/Pipeline Tools
1510
1543
  allowedTools.has("getBuilds") && server.tool("getBuilds", "List builds with optional filters (status, result, branch, definition, tags)", {
1511
- definitions: zod_1.z.preprocess(coerceArray, zod_1.z.array(zod_1.z.coerce.number()).optional()).describe("Filter by definition IDs"),
1544
+ definitions: zIdArray().optional().describe("Filter by definition IDs"),
1512
1545
  statusFilter: zod_1.z.string().optional().describe("Filter by status: inProgress, completed, cancelling, postponed, notStarted, all"),
1513
1546
  resultFilter: zod_1.z.string().optional().describe("Filter by result: succeeded, partiallySucceeded, failed, canceled"),
1514
1547
  branchName: zod_1.z.string().optional().describe("Filter by source branch (e.g., 'refs/heads/main')"),
@@ -1523,14 +1556,14 @@ async function main() {
1523
1556
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1524
1557
  });
1525
1558
  allowedTools.has("getBuild") && server.tool("getBuild", "Get detailed information about a specific build by ID", {
1526
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1559
+ buildId: zId().describe("Build ID"),
1527
1560
  }, async (params) => {
1528
1561
  const result = await buildTools.getBuild(params);
1529
1562
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1530
1563
  });
1531
1564
  allowedTools.has("getBuildLog") && server.tool("getBuildLog", "Get build logs. Without logId returns log metadata list; with logId returns specific log content", {
1532
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1533
- logId: zod_1.z.coerce.number().optional().describe("Specific log ID to retrieve content for"),
1565
+ buildId: zId().describe("Build ID"),
1566
+ logId: zIdOptional().describe("Specific log ID to retrieve content for"),
1534
1567
  startLine: zod_1.z.coerce.number().optional().describe("Start line for log content"),
1535
1568
  endLine: zod_1.z.coerce.number().optional().describe("End line for log content"),
1536
1569
  }, async (params) => {
@@ -1538,7 +1571,7 @@ async function main() {
1538
1571
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1539
1572
  });
1540
1573
  allowedTools.has("getBuildChanges") && server.tool("getBuildChanges", "Get changes (commits) associated with a build", {
1541
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1574
+ buildId: zId().describe("Build ID"),
1542
1575
  top: zod_1.z.coerce.number().optional().describe("Maximum number of changes to return (default 50)"),
1543
1576
  }, async (params) => {
1544
1577
  const result = await buildTools.getBuildChanges(params);
@@ -1556,14 +1589,14 @@ async function main() {
1556
1589
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1557
1590
  });
1558
1591
  allowedTools.has("getDefinition") && server.tool("getDefinition", "Get detailed information about a specific pipeline/build definition", {
1559
- definitionId: zod_1.z.coerce.number().describe("Definition ID"),
1592
+ definitionId: zId().describe("Definition ID"),
1560
1593
  includeLatestBuilds: zod_1.z.boolean().optional().describe("Include latest build info"),
1561
1594
  }, async (params) => {
1562
1595
  const result = await buildTools.getDefinition(params);
1563
1596
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1564
1597
  });
1565
1598
  allowedTools.has("runPipeline") && server.tool("runPipeline", "Queue/trigger a pipeline run", {
1566
- definitionId: zod_1.z.coerce.number().describe("Pipeline definition ID to run"),
1599
+ definitionId: zId().describe("Pipeline definition ID to run"),
1567
1600
  sourceBranch: zod_1.z.string().optional().describe("Source branch (e.g., 'refs/heads/main')"),
1568
1601
  parameters: zod_1.z.record(zod_1.z.string()).optional().describe("Pipeline parameters as key-value pairs"),
1569
1602
  }, async (params) => {
@@ -1571,19 +1604,19 @@ async function main() {
1571
1604
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1572
1605
  });
1573
1606
  allowedTools.has("getBuildArtifacts") && server.tool("getBuildArtifacts", "List artifacts produced by a build", {
1574
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1607
+ buildId: zId().describe("Build ID"),
1575
1608
  }, async (params) => {
1576
1609
  const result = await buildTools.getBuildArtifacts(params);
1577
1610
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1578
1611
  });
1579
1612
  allowedTools.has("getBuildTimeline") && server.tool("getBuildTimeline", "Get build timeline showing stages, jobs, and tasks with their status", {
1580
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1613
+ buildId: zId().describe("Build ID"),
1581
1614
  }, async (params) => {
1582
1615
  const result = await buildTools.getBuildTimeline(params);
1583
1616
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1584
1617
  });
1585
1618
  allowedTools.has("getBuildWorkItems") && server.tool("getBuildWorkItems", "Get work items associated with a build", {
1586
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1619
+ buildId: zId().describe("Build ID"),
1587
1620
  top: zod_1.z.coerce.number().optional().describe("Maximum number of work items to return (default 50)"),
1588
1621
  }, async (params) => {
1589
1622
  const result = await buildTools.getBuildWorkItems(params);