@achieveai/azuredevops-mcp 1.3.5 → 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,7 +178,7 @@ 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
183
  order: zod_1.z.enum(['asc', 'desc']).optional().default('desc').describe("Sort order: 'asc' (oldest first) or 'desc' (newest first)"),
152
184
  includeDeleted: zod_1.z.boolean().optional().describe("Include deleted comments")
@@ -160,7 +192,7 @@ async function main() {
160
192
  };
161
193
  });
162
194
  allowedTools.has("addWorkItemComment") && server.tool("addWorkItemComment", "Add a comment to a work item. Supports markdown formatting by default.", {
163
- id: zod_1.z.coerce.number().describe("ID of the work item"),
195
+ id: zId().describe("ID of the work item"),
164
196
  text: zod_1.z.string().describe("Comment text (supports markdown when format is 'markdown')"),
165
197
  format: zod_1.z.enum(['markdown', 'html']).optional().default('markdown').describe("Comment format: 'markdown' (default) or 'html'")
166
198
  }, async (params, extra) => {
@@ -173,7 +205,7 @@ async function main() {
173
205
  };
174
206
  });
175
207
  allowedTools.has("updateWorkItemState") && server.tool("updateWorkItemState", "Update the state of a work item", {
176
- id: zod_1.z.coerce.number().describe("ID of the work item"),
208
+ id: zId().describe("ID of the work item"),
177
209
  state: zod_1.z.string().describe("New state for the work item"),
178
210
  comment: zod_1.z.string().optional().describe("Comment explaining the state change")
179
211
  }, async (params, extra) => {
@@ -185,7 +217,7 @@ async function main() {
185
217
  };
186
218
  });
187
219
  allowedTools.has("assignWorkItem") && server.tool("assignWorkItem", "Assign a work item to a user", {
188
- id: zod_1.z.coerce.number().describe("ID of the work item"),
220
+ id: zId().describe("ID of the work item"),
189
221
  assignedTo: zod_1.z.string().describe("User to assign the work item to")
190
222
  }, async (params, extra) => {
191
223
  const result = await workItemTools.assignWorkItem(params);
@@ -199,7 +231,7 @@ async function main() {
199
231
  "Use prefixes for targetId: WI#123 (work item), PR#456 (pull request), " +
200
232
  "BUILD#789 (build), BRANCH#main (branch), COMMIT#abc123 (commit). " +
201
233
  "Plain numbers default to work item.", {
202
- sourceId: zod_1.z.coerce.number().describe("ID of the source work item"),
234
+ sourceId: zId().describe("ID of the source work item"),
203
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"),
204
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."),
205
237
  comment: zod_1.z.string().optional().describe("Comment explaining the link"),
@@ -226,7 +258,7 @@ async function main() {
226
258
  additionalFields: zod_1.z.record(zod_1.z.any()).optional().describe("Additional fields to set on the work item")
227
259
  }),
228
260
  zod_1.z.object({
229
- id: zod_1.z.coerce.number().describe("ID of work item to update"),
261
+ id: zId().describe("ID of work item to update"),
230
262
  fields: zod_1.z.record(zod_1.z.any()).describe("Fields to update on the work item")
231
263
  })
232
264
  ])).min(1).describe("Array of work items to create or update")
@@ -239,14 +271,14 @@ async function main() {
239
271
  };
240
272
  });
241
273
  allowedTools.has("getWorkItemsBatch") && server.tool("getWorkItemsBatch", "Get multiple work items by their IDs in a single efficient call", {
242
- 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"),
243
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'])"),
244
276
  }, async (params) => {
245
277
  const result = await workItemTools.getWorkItemsBatch(params);
246
278
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
247
279
  });
248
280
  allowedTools.has("getWorkItemRevisions") && server.tool("getWorkItemRevisions", "Get revision/change history for a work item showing all field changes over time", {
249
- id: zod_1.z.coerce.number().describe("Work item ID"),
281
+ id: zId().describe("Work item ID"),
250
282
  top: zod_1.z.coerce.number().optional().describe("Maximum number of revisions to return"),
251
283
  skip: zod_1.z.coerce.number().optional().describe("Number of revisions to skip"),
252
284
  }, async (params) => {
@@ -260,7 +292,7 @@ async function main() {
260
292
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
261
293
  });
262
294
  allowedTools.has("addChildWorkItem") && server.tool("addChildWorkItem", "Create a new work item and link it as a child of an existing parent work item", {
263
- parentId: zod_1.z.coerce.number().describe("Parent work item ID"),
295
+ parentId: zId().describe("Parent work item ID"),
264
296
  workItemType: zod_1.z.string().describe("Type of child work item (e.g., 'Task', 'Bug')"),
265
297
  title: zod_1.z.string().describe("Title of the child work item"),
266
298
  description: zod_1.z.string().optional().describe("Description"),
@@ -274,7 +306,7 @@ async function main() {
274
306
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
275
307
  });
276
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.", {
277
- id: zod_1.z.coerce.number().describe("Work item ID"),
309
+ id: zId().describe("Work item ID"),
278
310
  relationIndex: zod_1.z.coerce.number().describe("Index of the relation to remove (0-based, from the relations array)"),
279
311
  }, async (params) => {
280
312
  const result = await workItemTools.unlinkWorkItem(params);
@@ -316,7 +348,7 @@ async function main() {
316
348
  allowedTools.has("moveCardOnBoard") && server.tool("moveCardOnBoard", "Move a card on a board", {
317
349
  teamId: zod_1.z.string().optional().describe("Team ID (uses default team if not specified)"),
318
350
  boardId: zod_1.z.string().describe("ID of the board"),
319
- 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"),
320
352
  columnId: zod_1.z.string().describe("ID of the column to move to"),
321
353
  position: zod_1.z.coerce.number().optional().describe("Position within the column")
322
354
  }, async (params, extra) => {
@@ -446,8 +478,7 @@ async function main() {
446
478
  allowedTools.has("getIterations") && server.tool("getIterations", "Get iterations for a project. Use 'path' to drill into a subtree instead of fetching the entire hierarchy.", {
447
479
  projectId: zod_1.z.string().describe("ID or name of the project"),
448
480
  path: zod_1.z.string().optional().describe("Iteration path to start from (e.g., 'Sprint 1'). Omit for root."),
449
- depth: zod_1.z.coerce.number().optional().describe("Maximum depth of children to return (default: 2)"),
450
- includeDeleted: zod_1.z.boolean().optional().describe("Include deleted iterations")
481
+ depth: zod_1.z.coerce.number().optional().describe("Maximum depth of children to return (default: 2)")
451
482
  }, async (params, extra) => {
452
483
  const result = await projectTools.getIterations(params);
453
484
  return {
@@ -661,7 +692,7 @@ async function main() {
661
692
  "'files' (changed file list — prefer getAllPullRequestChanges for large diffs). " +
662
693
  "Supports both repository names and IDs.", {
663
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."),
664
- 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)."),
665
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.")
666
697
  }, async (params, extra) => {
667
698
  const result = await gitTools.getPullRequest(params);
@@ -673,8 +704,8 @@ async function main() {
673
704
  });
674
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.", {
675
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."),
676
- 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."),
677
- 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."),
678
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."),
679
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."),
680
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."),
@@ -690,7 +721,7 @@ async function main() {
690
721
  });
691
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.", {
692
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."),
693
- 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).")
694
725
  }, async (params, extra) => {
695
726
  const result = await gitTools.approvePullRequest(params);
696
727
  return {
@@ -701,7 +732,7 @@ async function main() {
701
732
  });
702
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.", {
703
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."),
704
- 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)."),
705
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."),
706
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.")
707
738
  }, async (params, extra) => {
@@ -715,7 +746,7 @@ async function main() {
715
746
  // Register new Pull Request Comment Tools
716
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.", {
717
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."),
718
- 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."),
719
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."),
720
751
  position: zod_1.z.object({
721
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)."),
@@ -734,7 +765,7 @@ async function main() {
734
765
  });
735
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'.", {
736
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."),
737
- 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."),
738
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')."),
739
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.")
740
771
  }, async (params, extra) => {
@@ -747,7 +778,7 @@ async function main() {
747
778
  });
748
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'.", {
749
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."),
750
- 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."),
751
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.")
752
783
  }, async (params, extra) => {
753
784
  const result = await gitTools.addPullRequestComment(params);
@@ -760,7 +791,7 @@ async function main() {
760
791
  // Register new Pull Request Diff Tools
761
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.", {
762
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."),
763
- 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."),
764
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.")
765
796
  }, async (params, extra) => {
766
797
  const result = await gitTools.getPullRequestFileChanges(params);
@@ -772,7 +803,7 @@ async function main() {
772
803
  });
773
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.", {
774
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."),
775
- 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.")
776
807
  }, async (params, extra) => {
777
808
  const result = await gitTools.getPullRequestChangesCount(params);
778
809
  return {
@@ -783,7 +814,7 @@ async function main() {
783
814
  });
784
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.", {
785
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."),
786
- 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."),
787
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."),
788
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.")
789
820
  }, async (params, extra) => {
@@ -797,7 +828,7 @@ async function main() {
797
828
  });
798
829
  allowedTools.has("updatePullRequest") && server.tool("updatePullRequest", "Update pull request properties (title, description, status, auto-complete, draft mode, target branch)", {
799
830
  repository: zod_1.z.string().describe("Repository name or ID"),
800
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
831
+ pullRequestId: zId().describe("Pull request ID"),
801
832
  title: zod_1.z.string().optional().describe("New title for the PR"),
802
833
  description: zod_1.z.string().optional().describe("New description for the PR"),
803
834
  status: zod_1.z.enum(['active', 'abandoned', 'completed']).optional().describe("New status"),
@@ -812,7 +843,7 @@ async function main() {
812
843
  });
813
844
  allowedTools.has("updatePullRequestReviewers") && server.tool("updatePullRequestReviewers", "Add or remove reviewers on an existing pull request", {
814
845
  repository: zod_1.z.string().describe("Repository name or ID"),
815
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
846
+ pullRequestId: zId().describe("Pull request ID"),
816
847
  reviewersToAdd: zod_1.z.array(zod_1.z.string()).optional().describe("User IDs or email addresses to add as reviewers"),
817
848
  reviewersToRemove: zod_1.z.array(zod_1.z.string()).optional().describe("User IDs or email addresses to remove from reviewers"),
818
849
  makeRequired: zod_1.z.boolean().optional().describe("Make added reviewers required (default false)"),
@@ -822,8 +853,8 @@ async function main() {
822
853
  });
823
854
  allowedTools.has("replyToComment") && server.tool("replyToComment", "Reply to an existing comment thread on a pull request", {
824
855
  repository: zod_1.z.string().describe("Repository name or ID"),
825
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
826
- 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"),
827
858
  comment: zod_1.z.string().describe("Reply text content (supports markdown)"),
828
859
  }, async (params) => {
829
860
  const result = await gitTools.replyToComment(params);
@@ -831,8 +862,8 @@ async function main() {
831
862
  });
832
863
  allowedTools.has("updatePullRequestThread") && server.tool("updatePullRequestThread", "Update a comment thread's status (resolve, reactivate, close, etc.)", {
833
864
  repository: zod_1.z.string().describe("Repository name or ID"),
834
- pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
835
- 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"),
836
867
  status: zod_1.z.enum(['active', 'byDesign', 'closed', 'fixed', 'pending', 'unknown', 'wontFix']).describe("New thread status"),
837
868
  }, async (params) => {
838
869
  const result = await gitTools.updatePullRequestThread(params);
@@ -848,8 +879,8 @@ async function main() {
848
879
  });
849
880
  // Register Testing Capabilities Tools
850
881
  allowedTools.has("runAutomatedTests") && server.tool("runAutomatedTests", "Execute automated test suites", {
851
- testSuiteId: zod_1.z.coerce.number().optional().describe("ID of the test suite to run"),
852
- 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"),
853
884
  testEnvironment: zod_1.z.string().optional().describe("Environment to run tests in"),
854
885
  parallelExecution: zod_1.z.boolean().optional().describe("Whether to run tests in parallel")
855
886
  }, async (params, extra) => {
@@ -861,7 +892,7 @@ async function main() {
861
892
  };
862
893
  });
863
894
  allowedTools.has("getTestAutomationStatus") && server.tool("getTestAutomationStatus", "Check status of automated test execution", {
864
- 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")
865
896
  }, async (params, extra) => {
866
897
  const result = await testingCapabilitiesTools.getTestAutomationStatus(params);
867
898
  return {
@@ -907,8 +938,8 @@ async function main() {
907
938
  };
908
939
  });
909
940
  allowedTools.has("getTestFlakiness") && server.tool("getTestFlakiness", "Analyze and report on test flakiness", {
910
- testId: zod_1.z.coerce.number().optional().describe("ID of a specific test to analyze"),
911
- 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"),
912
943
  timeRange: zod_1.z.string().optional().describe("Time range for analysis (e.g., '30d')")
913
944
  }, async (params, extra) => {
914
945
  const result = await testingCapabilitiesTools.getTestFlakiness(params);
@@ -930,7 +961,7 @@ async function main() {
930
961
  };
931
962
  });
932
963
  allowedTools.has("runTestImpactAnalysis") && server.tool("runTestImpactAnalysis", "Determine which tests to run based on code changes", {
933
- buildId: zod_1.z.coerce.number().describe("ID of the build to analyze"),
964
+ buildId: zId().describe("ID of the build to analyze"),
934
965
  changedFiles: zod_1.z.array(zod_1.z.string()).optional().describe("List of changed files")
935
966
  }, async (params, extra) => {
936
967
  const result = await testingCapabilitiesTools.runTestImpactAnalysis(params);
@@ -952,7 +983,7 @@ async function main() {
952
983
  };
953
984
  });
954
985
  allowedTools.has("runTestOptimization") && server.tool("runTestOptimization", "Optimize test suite execution for faster feedback", {
955
- 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"),
956
987
  optimizationGoal: zod_1.z.enum(['time', 'coverage', 'reliability']).describe("Optimization goal")
957
988
  }, async (params, extra) => {
958
989
  const result = await testingCapabilitiesTools.runTestOptimization(params);
@@ -975,7 +1006,7 @@ async function main() {
975
1006
  };
976
1007
  });
977
1008
  allowedTools.has("recordExploratoryTestResults") && server.tool("recordExploratoryTestResults", "Record findings during exploratory testing", {
978
- sessionId: zod_1.z.coerce.number().describe("ID of the exploratory session"),
1009
+ sessionId: zId().describe("ID of the exploratory session"),
979
1010
  findings: zod_1.z.array(zod_1.z.string()).describe("List of findings to record"),
980
1011
  attachments: zod_1.z.array(zod_1.z.object({
981
1012
  name: zod_1.z.string().describe("Name of the attachment"),
@@ -991,8 +1022,8 @@ async function main() {
991
1022
  };
992
1023
  });
993
1024
  allowedTools.has("convertFindingsToWorkItems") && server.tool("convertFindingsToWorkItems", "Convert exploratory test findings to work items", {
994
- sessionId: zod_1.z.coerce.number().describe("ID of the exploratory session"),
995
- 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"),
996
1027
  workItemType: zod_1.z.string().optional().describe("Type of work item to create")
997
1028
  }, async (params, extra) => {
998
1029
  const result = await testingCapabilitiesTools.convertFindingsToWorkItems(params);
@@ -1318,7 +1349,7 @@ async function main() {
1318
1349
  });
1319
1350
  // AI Assisted Development Tools
1320
1351
  allowedTools.has("getAICodeReview") && server.tool("getAICodeReview", "Get AI-based code review suggestions", {
1321
- 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"),
1322
1353
  repositoryId: zod_1.z.string().optional().describe("ID of the repository"),
1323
1354
  commitId: zod_1.z.string().optional().describe("ID of the commit to review"),
1324
1355
  filePath: zod_1.z.string().optional().describe("Path to the file to review")
@@ -1359,7 +1390,7 @@ async function main() {
1359
1390
  });
1360
1391
  allowedTools.has("getPredictiveBugAnalysis") && server.tool("getPredictiveBugAnalysis", "Predict potential bugs in code changes. Supports both repository names and IDs.", {
1361
1392
  repository: zod_1.z.string().describe("Repository name (e.g., 'MyProject') or ID (GUID). Repository names are case-insensitive."),
1362
- pullRequestId: zod_1.z.coerce.number().optional().describe("ID of the pull request"),
1393
+ pullRequestId: zIdOptional().describe("ID of the pull request"),
1363
1394
  branch: zod_1.z.string().optional().describe("Branch to analyze"),
1364
1395
  filePath: zod_1.z.string().optional().describe("Path to the file to analyze")
1365
1396
  }, async (params, extra) => {
@@ -1384,7 +1415,7 @@ async function main() {
1384
1415
  };
1385
1416
  });
1386
1417
  allowedTools.has("getPredictiveEffortEstimation") && server.tool("getPredictiveEffortEstimation", "AI-based effort estimation for work items", {
1387
- 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"),
1388
1419
  workItemType: zod_1.z.string().optional().describe("Type of work items to estimate"),
1389
1420
  areaPath: zod_1.z.string().optional().describe("Area path to filter work items")
1390
1421
  }, async (params, extra) => {
@@ -1409,7 +1440,7 @@ async function main() {
1409
1440
  };
1410
1441
  });
1411
1442
  allowedTools.has("suggestWorkItemRefinements") && server.tool("suggestWorkItemRefinements", "Get AI suggestions for work item refinements", {
1412
- 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"),
1413
1444
  workItemType: zod_1.z.string().optional().describe("Type of work item"),
1414
1445
  areaPath: zod_1.z.string().optional().describe("Area path to filter work items")
1415
1446
  }, async (params, extra) => {
@@ -1445,7 +1476,7 @@ async function main() {
1445
1476
  };
1446
1477
  });
1447
1478
  allowedTools.has("predictBuildFailures") && server.tool("predictBuildFailures", "Predict potential build failures before they occur", {
1448
- buildDefinitionId: zod_1.z.coerce.number().describe("ID of the build definition"),
1479
+ buildDefinitionId: zId().describe("ID of the build definition"),
1449
1480
  lookbackPeriod: zod_1.z.string().optional().describe("Period to analyze for patterns (e.g., '30d')")
1450
1481
  }, async (params, extra) => {
1451
1482
  const result = await aiAssistedDevelopmentTools.predictBuildFailures(params);
@@ -1456,7 +1487,7 @@ async function main() {
1456
1487
  };
1457
1488
  });
1458
1489
  allowedTools.has("optimizeTestSelection") && server.tool("optimizeTestSelection", "Intelligently select tests to run based on changes", {
1459
- buildId: zod_1.z.coerce.number().describe("ID of the build"),
1490
+ buildId: zId().describe("ID of the build"),
1460
1491
  changedFiles: zod_1.z.array(zod_1.z.string()).optional().describe("List of changed files"),
1461
1492
  maxTestCount: zod_1.z.coerce.number().optional().describe("Maximum number of tests to select")
1462
1493
  }, async (params, extra) => {
@@ -1510,7 +1541,7 @@ async function main() {
1510
1541
  });
1511
1542
  // Register Build/Pipeline Tools
1512
1543
  allowedTools.has("getBuilds") && server.tool("getBuilds", "List builds with optional filters (status, result, branch, definition, tags)", {
1513
- 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"),
1514
1545
  statusFilter: zod_1.z.string().optional().describe("Filter by status: inProgress, completed, cancelling, postponed, notStarted, all"),
1515
1546
  resultFilter: zod_1.z.string().optional().describe("Filter by result: succeeded, partiallySucceeded, failed, canceled"),
1516
1547
  branchName: zod_1.z.string().optional().describe("Filter by source branch (e.g., 'refs/heads/main')"),
@@ -1525,14 +1556,14 @@ async function main() {
1525
1556
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1526
1557
  });
1527
1558
  allowedTools.has("getBuild") && server.tool("getBuild", "Get detailed information about a specific build by ID", {
1528
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1559
+ buildId: zId().describe("Build ID"),
1529
1560
  }, async (params) => {
1530
1561
  const result = await buildTools.getBuild(params);
1531
1562
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1532
1563
  });
1533
1564
  allowedTools.has("getBuildLog") && server.tool("getBuildLog", "Get build logs. Without logId returns log metadata list; with logId returns specific log content", {
1534
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1535
- 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"),
1536
1567
  startLine: zod_1.z.coerce.number().optional().describe("Start line for log content"),
1537
1568
  endLine: zod_1.z.coerce.number().optional().describe("End line for log content"),
1538
1569
  }, async (params) => {
@@ -1540,7 +1571,7 @@ async function main() {
1540
1571
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1541
1572
  });
1542
1573
  allowedTools.has("getBuildChanges") && server.tool("getBuildChanges", "Get changes (commits) associated with a build", {
1543
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1574
+ buildId: zId().describe("Build ID"),
1544
1575
  top: zod_1.z.coerce.number().optional().describe("Maximum number of changes to return (default 50)"),
1545
1576
  }, async (params) => {
1546
1577
  const result = await buildTools.getBuildChanges(params);
@@ -1558,14 +1589,14 @@ async function main() {
1558
1589
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1559
1590
  });
1560
1591
  allowedTools.has("getDefinition") && server.tool("getDefinition", "Get detailed information about a specific pipeline/build definition", {
1561
- definitionId: zod_1.z.coerce.number().describe("Definition ID"),
1592
+ definitionId: zId().describe("Definition ID"),
1562
1593
  includeLatestBuilds: zod_1.z.boolean().optional().describe("Include latest build info"),
1563
1594
  }, async (params) => {
1564
1595
  const result = await buildTools.getDefinition(params);
1565
1596
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1566
1597
  });
1567
1598
  allowedTools.has("runPipeline") && server.tool("runPipeline", "Queue/trigger a pipeline run", {
1568
- definitionId: zod_1.z.coerce.number().describe("Pipeline definition ID to run"),
1599
+ definitionId: zId().describe("Pipeline definition ID to run"),
1569
1600
  sourceBranch: zod_1.z.string().optional().describe("Source branch (e.g., 'refs/heads/main')"),
1570
1601
  parameters: zod_1.z.record(zod_1.z.string()).optional().describe("Pipeline parameters as key-value pairs"),
1571
1602
  }, async (params) => {
@@ -1573,19 +1604,19 @@ async function main() {
1573
1604
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1574
1605
  });
1575
1606
  allowedTools.has("getBuildArtifacts") && server.tool("getBuildArtifacts", "List artifacts produced by a build", {
1576
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1607
+ buildId: zId().describe("Build ID"),
1577
1608
  }, async (params) => {
1578
1609
  const result = await buildTools.getBuildArtifacts(params);
1579
1610
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1580
1611
  });
1581
1612
  allowedTools.has("getBuildTimeline") && server.tool("getBuildTimeline", "Get build timeline showing stages, jobs, and tasks with their status", {
1582
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1613
+ buildId: zId().describe("Build ID"),
1583
1614
  }, async (params) => {
1584
1615
  const result = await buildTools.getBuildTimeline(params);
1585
1616
  return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1586
1617
  });
1587
1618
  allowedTools.has("getBuildWorkItems") && server.tool("getBuildWorkItems", "Get work items associated with a build", {
1588
- buildId: zod_1.z.coerce.number().describe("Build ID"),
1619
+ buildId: zId().describe("Build ID"),
1589
1620
  top: zod_1.z.coerce.number().optional().describe("Maximum number of work items to return (default 50)"),
1590
1621
  }, async (params) => {
1591
1622
  const result = await buildTools.getBuildWorkItems(params);