@achieveai/azuredevops-mcp 1.2.1 → 1.2.2

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
@@ -12,14 +12,28 @@ const TestingCapabilitiesTools_1 = require("./Tools/TestingCapabilitiesTools");
12
12
  const DevSecOpsTools_1 = require("./Tools/DevSecOpsTools");
13
13
  const ArtifactManagementTools_1 = require("./Tools/ArtifactManagementTools");
14
14
  const AIAssistedDevelopmentTools_1 = require("./Tools/AIAssistedDevelopmentTools");
15
+ const BuildTools_1 = require("./Tools/BuildTools");
16
+ const WikiTools_1 = require("./Tools/WikiTools");
15
17
  const zod_1 = require("zod");
18
+ /** Wrap a value so string-encoded JSON arrays are auto-parsed (some MCP clients send arrays as strings). */
19
+ const coerceArray = (val) => typeof val === 'string' ? JSON.parse(val) : val;
16
20
  const EntraAuthHandler_1 = require("./Services/EntraAuthHandler");
17
21
  async function main() {
18
22
  try {
19
23
  // Load configuration
20
24
  const azureDevOpsConfig = (0, config_1.getAzureDevOpsConfig)();
25
+ // Initialize token credential auth handler based on auth type
21
26
  if (azureDevOpsConfig.auth?.type === 'entra') {
22
- azureDevOpsConfig.entraAuthHandler = await EntraAuthHandler_1.EntraAuthHandler.getInstance();
27
+ azureDevOpsConfig.tokenCredentialAuthHandler = await EntraAuthHandler_1.TokenCredentialAuthHandler.createEntra();
28
+ }
29
+ else if (azureDevOpsConfig.auth?.type === 'azcli') {
30
+ azureDevOpsConfig.tokenCredentialAuthHandler = await EntraAuthHandler_1.TokenCredentialAuthHandler.createAzureCli(azureDevOpsConfig.auth.tenantId);
31
+ }
32
+ else if (azureDevOpsConfig.auth?.type === 'interactive') {
33
+ azureDevOpsConfig.tokenCredentialAuthHandler = await EntraAuthHandler_1.TokenCredentialAuthHandler.createInteractive({
34
+ tenantId: azureDevOpsConfig.auth.tenantId,
35
+ clientId: azureDevOpsConfig.auth.clientId,
36
+ });
23
37
  }
24
38
  // Load allowed tools
25
39
  const allowedTools = (0, config_1.getAllowedTools)();
@@ -32,6 +46,8 @@ async function main() {
32
46
  const devSecOpsTools = new DevSecOpsTools_1.DevSecOpsTools(azureDevOpsConfig);
33
47
  const artifactManagementTools = new ArtifactManagementTools_1.ArtifactManagementTools(azureDevOpsConfig);
34
48
  const aiAssistedDevelopmentTools = new AIAssistedDevelopmentTools_1.AIAssistedDevelopmentTools(azureDevOpsConfig);
49
+ const buildTools = new BuildTools_1.BuildTools(azureDevOpsConfig);
50
+ const wikiTools = new WikiTools_1.WikiTools(azureDevOpsConfig);
35
51
  // Create MCP server
36
52
  const server = new mcp_js_1.McpServer({
37
53
  name: 'azure-devops-mcp',
@@ -49,7 +65,7 @@ async function main() {
49
65
  };
50
66
  });
51
67
  allowedTools.has("getWorkItemById") && server.tool("getWorkItemById", "Get a specific work item by ID with summary and detailed view", {
52
- id: zod_1.z.number().describe("Work item ID")
68
+ id: zod_1.z.coerce.number().describe("Work item ID")
53
69
  }, async (params, extra) => {
54
70
  const result = await workItemTools.getWorkItemById({ id: params.id });
55
71
  return {
@@ -61,7 +77,7 @@ async function main() {
61
77
  });
62
78
  allowedTools.has("searchWorkItems") && server.tool("searchWorkItems", "Search for work items by text with summary and organized results", {
63
79
  searchText: zod_1.z.string().describe("Text to search for in work items"),
64
- top: zod_1.z.number().optional().describe("Maximum number of work items to return")
80
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of work items to return")
65
81
  }, async (params, extra) => {
66
82
  const result = await workItemTools.searchWorkItems(params);
67
83
  return {
@@ -72,8 +88,8 @@ async function main() {
72
88
  };
73
89
  });
74
90
  allowedTools.has("getRecentlyUpdatedWorkItems") && server.tool("getRecentlyUpdatedWorkItems", "Get recently updated work items", {
75
- top: zod_1.z.number().optional().describe("Maximum number of work items to return"),
76
- skip: zod_1.z.number().optional().describe("Number of work items to skip")
91
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of work items to return"),
92
+ skip: zod_1.z.coerce.number().optional().describe("Number of work items to skip")
77
93
  }, async (params, extra) => {
78
94
  const result = await workItemTools.getRecentlyUpdatedWorkItems(params);
79
95
  return {
@@ -84,7 +100,7 @@ async function main() {
84
100
  });
85
101
  allowedTools.has("getMyWorkItems") && server.tool("getMyWorkItems", "Get work items assigned to you", {
86
102
  state: zod_1.z.string().optional().describe("Filter by work item state"),
87
- top: zod_1.z.number().optional().describe("Maximum number of work items to return")
103
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of work items to return")
88
104
  }, async (params, extra) => {
89
105
  const result = await workItemTools.getMyWorkItems(params);
90
106
  return {
@@ -112,7 +128,7 @@ async function main() {
112
128
  };
113
129
  });
114
130
  allowedTools.has("updateWorkItem") && server.tool("updateWorkItem", "Update an existing work item", {
115
- id: zod_1.z.number().describe("ID of the work item to update"),
131
+ id: zod_1.z.coerce.number().describe("ID of the work item to update"),
116
132
  fields: zod_1.z.record(zod_1.z.any()).describe("Fields to update on the work item")
117
133
  }, async (params, extra) => {
118
134
  const result = await workItemTools.updateWorkItem(params);
@@ -124,7 +140,7 @@ async function main() {
124
140
  };
125
141
  });
126
142
  allowedTools.has("addWorkItemComment") && server.tool("addWorkItemComment", "Add a comment to a work item", {
127
- id: zod_1.z.number().describe("ID of the work item"),
143
+ id: zod_1.z.coerce.number().describe("ID of the work item"),
128
144
  text: zod_1.z.string().describe("Comment text")
129
145
  }, async (params, extra) => {
130
146
  const result = await workItemTools.addWorkItemComment(params);
@@ -136,7 +152,7 @@ async function main() {
136
152
  };
137
153
  });
138
154
  allowedTools.has("updateWorkItemState") && server.tool("updateWorkItemState", "Update the state of a work item", {
139
- id: zod_1.z.number().describe("ID of the work item"),
155
+ id: zod_1.z.coerce.number().describe("ID of the work item"),
140
156
  state: zod_1.z.string().describe("New state for the work item"),
141
157
  comment: zod_1.z.string().optional().describe("Comment explaining the state change")
142
158
  }, async (params, extra) => {
@@ -148,7 +164,7 @@ async function main() {
148
164
  };
149
165
  });
150
166
  allowedTools.has("assignWorkItem") && server.tool("assignWorkItem", "Assign a work item to a user", {
151
- id: zod_1.z.number().describe("ID of the work item"),
167
+ id: zod_1.z.coerce.number().describe("ID of the work item"),
152
168
  assignedTo: zod_1.z.string().describe("User to assign the work item to")
153
169
  }, async (params, extra) => {
154
170
  const result = await workItemTools.assignWorkItem(params);
@@ -162,7 +178,7 @@ async function main() {
162
178
  "Use prefixes for targetId: WI#123 (work item), PR#456 (pull request), " +
163
179
  "BUILD#789 (build), BRANCH#main (branch), COMMIT#abc123 (commit). " +
164
180
  "Plain numbers default to work item.", {
165
- sourceId: zod_1.z.number().describe("ID of the source work item"),
181
+ sourceId: zod_1.z.coerce.number().describe("ID of the source work item"),
166
182
  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"),
167
183
  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."),
168
184
  comment: zod_1.z.string().optional().describe("Comment explaining the link"),
@@ -189,7 +205,7 @@ async function main() {
189
205
  additionalFields: zod_1.z.record(zod_1.z.any()).optional().describe("Additional fields to set on the work item")
190
206
  }),
191
207
  zod_1.z.object({
192
- id: zod_1.z.number().describe("ID of work item to update"),
208
+ id: zod_1.z.coerce.number().describe("ID of work item to update"),
193
209
  fields: zod_1.z.record(zod_1.z.any()).describe("Fields to update on the work item")
194
210
  })
195
211
  ])).min(1).describe("Array of work items to create or update")
@@ -201,6 +217,48 @@ async function main() {
201
217
  isError: result.isError
202
218
  };
203
219
  });
220
+ allowedTools.has("getWorkItemsBatch") && server.tool("getWorkItemsBatch", "Get multiple work items by their IDs in a single efficient call", {
221
+ ids: zod_1.z.preprocess(coerceArray, zod_1.z.array(zod_1.z.coerce.number())).describe("Array of work item IDs to retrieve"),
222
+ 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'])"),
223
+ }, async (params) => {
224
+ const result = await workItemTools.getWorkItemsBatch(params);
225
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
226
+ });
227
+ allowedTools.has("getWorkItemRevisions") && server.tool("getWorkItemRevisions", "Get revision/change history for a work item showing all field changes over time", {
228
+ id: zod_1.z.coerce.number().describe("Work item ID"),
229
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of revisions to return"),
230
+ skip: zod_1.z.coerce.number().optional().describe("Number of revisions to skip"),
231
+ }, async (params) => {
232
+ const result = await workItemTools.getWorkItemRevisions(params);
233
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
234
+ });
235
+ allowedTools.has("getQueryResults") && server.tool("getQueryResults", "Execute a saved WIQL query by its query ID and return the resulting work items", {
236
+ queryId: zod_1.z.string().describe("The GUID of the saved query to execute"),
237
+ }, async (params) => {
238
+ const result = await workItemTools.getQueryResults(params);
239
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
240
+ });
241
+ allowedTools.has("addChildWorkItem") && server.tool("addChildWorkItem", "Create a new work item and link it as a child of an existing parent work item", {
242
+ parentId: zod_1.z.coerce.number().describe("Parent work item ID"),
243
+ workItemType: zod_1.z.string().describe("Type of child work item (e.g., 'Task', 'Bug')"),
244
+ title: zod_1.z.string().describe("Title of the child work item"),
245
+ description: zod_1.z.string().optional().describe("Description"),
246
+ assignedTo: zod_1.z.string().optional().describe("User to assign to"),
247
+ state: zod_1.z.string().optional().describe("Initial state"),
248
+ areaPath: zod_1.z.string().optional().describe("Area path"),
249
+ iterationPath: zod_1.z.string().optional().describe("Iteration path"),
250
+ additionalFields: zod_1.z.record(zod_1.z.any()).optional().describe("Additional fields"),
251
+ }, async (params) => {
252
+ const result = await workItemTools.addChildWorkItem(params);
253
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
254
+ });
255
+ 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.", {
256
+ id: zod_1.z.coerce.number().describe("Work item ID"),
257
+ relationIndex: zod_1.z.coerce.number().describe("Index of the relation to remove (0-based, from the relations array)"),
258
+ }, async (params) => {
259
+ const result = await workItemTools.unlinkWorkItem(params);
260
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
261
+ });
204
262
  // Register Boards & Sprints Tools
205
263
  allowedTools.has("getBoards") && server.tool("getBoards", "Get all boards for a team", {
206
264
  teamId: zod_1.z.string().optional().describe("Team ID (uses default team if not specified)")
@@ -237,9 +295,9 @@ async function main() {
237
295
  allowedTools.has("moveCardOnBoard") && server.tool("moveCardOnBoard", "Move a card on a board", {
238
296
  teamId: zod_1.z.string().optional().describe("Team ID (uses default team if not specified)"),
239
297
  boardId: zod_1.z.string().describe("ID of the board"),
240
- workItemId: zod_1.z.number().describe("ID of the work item to move"),
298
+ workItemId: zod_1.z.coerce.number().describe("ID of the work item to move"),
241
299
  columnId: zod_1.z.string().describe("ID of the column to move to"),
242
- position: zod_1.z.number().optional().describe("Position within the column")
300
+ position: zod_1.z.coerce.number().optional().describe("Position within the column")
243
301
  }, async (params, extra) => {
244
302
  const result = await boardsSprintsTools.moveCardOnBoard(params);
245
303
  return {
@@ -311,8 +369,8 @@ async function main() {
311
369
  // Register Project Tools
312
370
  allowedTools.has("listProjects") && server.tool("listProjects", "List all projects", {
313
371
  stateFilter: zod_1.z.enum(['all', 'createPending', 'deleted', 'deleting', 'new', 'unchanged', 'wellFormed']).optional().describe("Filter by project state"),
314
- top: zod_1.z.number().optional().describe("Maximum number of projects to return"),
315
- skip: zod_1.z.number().optional().describe("Number of projects to skip")
372
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of projects to return"),
373
+ skip: zod_1.z.coerce.number().optional().describe("Number of projects to skip")
316
374
  }, async (params, extra) => {
317
375
  const result = await projectTools.listProjects(params);
318
376
  return {
@@ -349,7 +407,7 @@ async function main() {
349
407
  });
350
408
  allowedTools.has("getAreas") && server.tool("getAreas", "Get areas for a project", {
351
409
  projectId: zod_1.z.string().describe("ID of the project"),
352
- depth: zod_1.z.number().optional().describe("Maximum depth of the area hierarchy")
410
+ depth: zod_1.z.coerce.number().optional().describe("Maximum depth of the area hierarchy")
353
411
  }, async (params, extra) => {
354
412
  const result = await projectTools.getAreas(params);
355
413
  return {
@@ -464,7 +522,7 @@ async function main() {
464
522
  allowedTools.has("listBranches") && server.tool("listBranches", "List all branches in a Git repository with optional name pattern filtering and pagination. Returns branch details including name, commit ID, and object ID. Results can be filtered using wildcards (e.g., 'feature/*' for all feature branches). Supports both repository names and IDs.", {
465
523
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) to list branches from. Repository names are case-insensitive."),
466
524
  filter: zod_1.z.string().optional().describe("Optional wildcard pattern to filter branch names (e.g., 'feature/*', 'release/*'). Use this to narrow down results to specific branch types."),
467
- top: zod_1.z.number().optional().describe("Maximum number of branches to return in the response. Use this to limit large result sets, especially for repositories with many branches.")
525
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of branches to return in the response. Use this to limit large result sets, especially for repositories with many branches.")
468
526
  }, async (params, extra) => {
469
527
  const result = await gitTools.listBranches(params);
470
528
  return {
@@ -478,7 +536,7 @@ async function main() {
478
536
  projectId: zod_1.z.string().optional().describe("Optional project ID or name to limit the search scope to a specific project. Omit to search across all accessible projects."),
479
537
  repositoryId: zod_1.z.string().optional().describe("Optional repository ID or name to limit the search to a single repository. Most effective when combined with projectId."),
480
538
  fileExtension: zod_1.z.string().optional().describe("Optional file extension to filter results by (e.g., '.js', '.ts', '.cs'). Provide without the dot to search for specific file types."),
481
- top: zod_1.z.number().optional().describe("Maximum number of search results to return. Use this to limit large result sets for common search terms.")
539
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of search results to return. Use this to limit large result sets for common search terms.")
482
540
  }, async (params, extra) => {
483
541
  const result = await gitTools.searchCode(params);
484
542
  return {
@@ -506,8 +564,8 @@ async function main() {
506
564
  allowedTools.has("getFileContent") && server.tool("getFileContent", "Retrieve the content of a specific file from a Git repository with line numbers (arrow notation) for easy LLM comprehension. Returns formatted content with comprehensive metadata including line ranges, file size, and truncation status. Content is automatically truncated to 200 lines or 8K characters (whichever comes first) for optimal context usage. Use startLine and lineCount parameters to retrieve specific ranges for large files. Optionally specify a branch, tag, or commit to retrieve file content from a specific version. Supports both repository names and IDs.", {
507
565
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the file. Repository names are case-insensitive."),
508
566
  path: zod_1.z.string().describe("The full path to the file within the repository, including filename and extension (e.g., 'src/utils/helpers.js')."),
509
- startLine: zod_1.z.number().optional().describe("Starting line number (1-based) to begin reading from. Default: 1. Use this to navigate through large files by requesting specific ranges (e.g., startLine=201 for lines starting at 201)."),
510
- lineCount: zod_1.z.number().optional().describe("Number of lines to return starting from startLine. Default: all lines, Maximum: 200 lines per request. Use this with startLine to retrieve specific sections of large files (e.g., lineCount=100 to get 100 lines)."),
567
+ startLine: zod_1.z.coerce.number().optional().describe("Starting line number (1-based) to begin reading from. Default: 1. Use this to navigate through large files by requesting specific ranges (e.g., startLine=201 for lines starting at 201)."),
568
+ lineCount: zod_1.z.coerce.number().optional().describe("Number of lines to return starting from startLine. Default: all lines, Maximum: 200 lines per request. Use this with startLine to retrieve specific sections of large files (e.g., lineCount=100 to get 100 lines)."),
511
569
  versionDescriptor: zod_1.z.object({
512
570
  version: zod_1.z.string().optional().describe("The name of the branch (e.g., 'main'), tag, or commit ID to retrieve the file from. Defaults to the default branch if not specified."),
513
571
  versionOptions: zod_1.z.string().optional().describe("Additional version options: 'None', 'PreviousChange', 'FirstParent'. Usually leave this undefined."),
@@ -523,8 +581,8 @@ async function main() {
523
581
  allowedTools.has("getCommitHistory") && server.tool("getCommitHistory", "Retrieve the commit history for a Git repository with metadata (ID, author, date, message, file changes). Returns a chronological list of commits with summary statistics at the top. Optionally filter to commits affecting a specific file path and use pagination to handle repositories with extensive history. Supports both repository names and IDs.", {
524
582
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) to get history for. Repository names are case-insensitive."),
525
583
  itemPath: zod_1.z.string().optional().describe("Optional path to a specific file or folder to filter commits to only those that modified the specified path."),
526
- top: zod_1.z.number().optional().describe("Maximum number of commits to return in the response. Use this to limit results for repositories with extensive history."),
527
- skip: zod_1.z.number().optional().describe("Number of commits to skip before starting to return results. Use with 'top' for implementing pagination through commit history."),
584
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of commits to return in the response. Use this to limit results for repositories with extensive history."),
585
+ skip: zod_1.z.coerce.number().optional().describe("Number of commits to skip before starting to return results. Use with 'top' for implementing pagination through commit history."),
528
586
  projectId: zod_1.z.string().optional().describe("The project ID or name to filter repositories by. If omitted, uses the default project from configuration.")
529
587
  }, async (params, extra) => {
530
588
  const result = await gitTools.getCommitHistory(params);
@@ -540,8 +598,8 @@ async function main() {
540
598
  status: zod_1.z.enum(['abandoned', 'active', 'all', 'completed', 'notSet']).optional().describe("Filter pull requests by their current status: 'active' for open PRs, 'completed' for merged PRs, 'abandoned' for closed/rejected PRs, 'all' for all PRs regardless of status."),
541
599
  creatorId: zod_1.z.string().optional().describe("Filter pull requests to only those created by a specific user ID or email address."),
542
600
  reviewerId: zod_1.z.string().optional().describe("Filter pull requests to only those where a specific user ID or email address has been assigned as a reviewer."),
543
- top: zod_1.z.number().optional().describe("Maximum number of pull requests to return in the response. Use this for pagination to handle repositories with many PRs."),
544
- skip: zod_1.z.number().optional().describe("Number of pull requests to skip before starting to return results. Use with 'top' for implementing pagination.")
601
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of pull requests to return in the response. Use this for pagination to handle repositories with many PRs."),
602
+ skip: zod_1.z.coerce.number().optional().describe("Number of pull requests to skip before starting to return results. Use with 'top' for implementing pagination.")
545
603
  }, async (params, extra) => {
546
604
  const result = await gitTools.listPullRequests(params);
547
605
  return {
@@ -566,7 +624,7 @@ async function main() {
566
624
  });
567
625
  allowedTools.has("getPullRequest") && server.tool("getPullRequest", "Fetch comprehensive details about a specific pull request by its ID within a repository. Returns all PR metadata including title, description, status, source and target branches, creator, reviewers, and voting status. Use this to get the full state of a pull request. Supports both repository names and IDs.", {
568
626
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
569
- pullRequestId: zod_1.z.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).")
627
+ 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).")
570
628
  }, async (params, extra) => {
571
629
  const result = await gitTools.getPullRequest(params);
572
630
  return {
@@ -577,10 +635,10 @@ async function main() {
577
635
  });
578
636
  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 to a specific thread by ID or use pagination for PRs with extensive discussions. Supports both repository names and IDs.", {
579
637
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
580
- pullRequestId: zod_1.z.number().describe("The numeric ID of the pull request to retrieve comments from. This is the PR number shown in the Azure DevOps UI."),
581
- threadId: zod_1.z.number().optional().describe("Optional ID of a specific comment thread to retrieve. If provided, only returns the specified thread rather than all threads."),
582
- top: zod_1.z.number().optional().describe("Maximum number of comment threads to return in the response. Use this for pagination in PRs with many comments."),
583
- skip: zod_1.z.number().optional().describe("Number of comment threads to skip before starting to return results. Use with 'top' for implementing pagination.")
638
+ 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."),
639
+ 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."),
640
+ 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."),
641
+ skip: zod_1.z.coerce.number().optional().describe("Number of comment threads to skip before starting to return results. Use with 'top' for implementing pagination.")
584
642
  }, async (params, extra) => {
585
643
  const result = await gitTools.getPullRequestComments(params);
586
644
  return {
@@ -592,7 +650,7 @@ async function main() {
592
650
  });
593
651
  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.", {
594
652
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
595
- pullRequestId: zod_1.z.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).")
653
+ 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).")
596
654
  }, async (params, extra) => {
597
655
  const result = await gitTools.approvePullRequest(params);
598
656
  return {
@@ -603,7 +661,7 @@ async function main() {
603
661
  });
604
662
  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.", {
605
663
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
606
- pullRequestId: zod_1.z.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)."),
664
+ 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)."),
607
665
  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."),
608
666
  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.")
609
667
  }, async (params, extra) => {
@@ -617,11 +675,11 @@ async function main() {
617
675
  // Register new Pull Request Comment Tools
618
676
  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.", {
619
677
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
620
- pullRequestId: zod_1.z.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."),
678
+ 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."),
621
679
  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."),
622
680
  position: zod_1.z.object({
623
- line: zod_1.z.number().describe("The 1-based line number in the file where the comment should be anchored. Must be a line visible in the PR diff (added, removed, or context line)."),
624
- offset: zod_1.z.number().describe("The character offset within the line where the comment should be anchored. Typically use 1 for beginning of line.")
681
+ line: zod_1.z.coerce.number().describe("The 1-based line number in the file where the comment should be anchored. Must be a line visible in the PR diff (added, removed, or context line)."),
682
+ offset: zod_1.z.coerce.number().describe("The character offset within the line where the comment should be anchored. Typically use 1 for beginning of line.")
625
683
  }).describe("The exact position within the file where the comment will be anchored. Both line and offset are required."),
626
684
  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/Services/UserService.cs').")
627
685
  }, async (params, extra) => {
@@ -634,7 +692,7 @@ async function main() {
634
692
  });
635
693
  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'.", {
636
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."),
637
- pullRequestId: zod_1.z.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."),
695
+ 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."),
638
696
  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')."),
639
697
  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.")
640
698
  }, async (params, extra) => {
@@ -647,7 +705,7 @@ async function main() {
647
705
  });
648
706
  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'.", {
649
707
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
650
- pullRequestId: zod_1.z.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."),
708
+ 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."),
651
709
  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.")
652
710
  }, async (params, extra) => {
653
711
  const result = await gitTools.addPullRequestComment(params);
@@ -660,7 +718,7 @@ async function main() {
660
718
  // Register new Pull Request Diff Tools
661
719
  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.", {
662
720
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
663
- pullRequestId: zod_1.z.number().describe("The numeric ID of the pull request to examine. This is the PR number shown in the Azure DevOps UI."),
721
+ 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."),
664
722
  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.")
665
723
  }, async (params, extra) => {
666
724
  const result = await gitTools.getPullRequestFileChanges(params);
@@ -672,7 +730,7 @@ async function main() {
672
730
  });
673
731
  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.", {
674
732
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
675
- pullRequestId: zod_1.z.number().describe("The numeric ID of the pull request to analyze. This is the PR number shown in the Azure DevOps UI.")
733
+ 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.")
676
734
  }, async (params, extra) => {
677
735
  const result = await gitTools.getPullRequestChangesCount(params);
678
736
  return {
@@ -683,9 +741,9 @@ async function main() {
683
741
  });
684
742
  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.", {
685
743
  repository: zod_1.z.string().describe("The repository name (e.g., 'MyProject') or ID (GUID) containing the pull request. Repository names are case-insensitive."),
686
- pullRequestId: zod_1.z.number().describe("The numeric ID of the pull request to retrieve changes for. This is the PR number shown in the Azure DevOps UI."),
687
- top: zod_1.z.number().optional().describe("Maximum number of change entries to return in a single request. Use this for pagination to avoid large response payloads."),
688
- skip: zod_1.z.number().optional().describe("Number of change entries to skip before starting to return results. Use with 'top' for implementing pagination.")
744
+ 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."),
745
+ 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."),
746
+ 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.")
689
747
  }, async (params, extra) => {
690
748
  const result = await gitTools.getAllPullRequestChanges(params);
691
749
  return {
@@ -695,10 +753,61 @@ async function main() {
695
753
  structuredContent: result.structuredContent
696
754
  };
697
755
  });
756
+ allowedTools.has("updatePullRequest") && server.tool("updatePullRequest", "Update pull request properties (title, description, status, auto-complete, draft mode, target branch)", {
757
+ repository: zod_1.z.string().describe("Repository name or ID"),
758
+ pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
759
+ title: zod_1.z.string().optional().describe("New title for the PR"),
760
+ description: zod_1.z.string().optional().describe("New description for the PR"),
761
+ status: zod_1.z.enum(['active', 'abandoned', 'completed']).optional().describe("New status"),
762
+ autoCompleteSetBy: zod_1.z.string().optional().describe("User ID to set as auto-complete initiator"),
763
+ mergeStrategy: zod_1.z.enum(['noFastForward', 'rebase', 'rebaseMerge', 'squash']).optional().describe("Merge strategy for auto-complete"),
764
+ deleteSourceBranch: zod_1.z.boolean().optional().describe("Delete source branch on completion"),
765
+ isDraft: zod_1.z.boolean().optional().describe("Set PR as draft or publish it"),
766
+ targetRefName: zod_1.z.string().optional().describe("Change target branch (e.g., 'refs/heads/main')"),
767
+ }, async (params) => {
768
+ const result = await gitTools.updatePullRequest(params);
769
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
770
+ });
771
+ allowedTools.has("updatePullRequestReviewers") && server.tool("updatePullRequestReviewers", "Add or remove reviewers on an existing pull request", {
772
+ repository: zod_1.z.string().describe("Repository name or ID"),
773
+ pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
774
+ reviewersToAdd: zod_1.z.array(zod_1.z.string()).optional().describe("User IDs or email addresses to add as reviewers"),
775
+ reviewersToRemove: zod_1.z.array(zod_1.z.string()).optional().describe("User IDs or email addresses to remove from reviewers"),
776
+ makeRequired: zod_1.z.boolean().optional().describe("Make added reviewers required (default false)"),
777
+ }, async (params) => {
778
+ const result = await gitTools.updatePullRequestReviewers(params);
779
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
780
+ });
781
+ allowedTools.has("replyToComment") && server.tool("replyToComment", "Reply to an existing comment thread on a pull request", {
782
+ repository: zod_1.z.string().describe("Repository name or ID"),
783
+ pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
784
+ threadId: zod_1.z.coerce.number().describe("Thread ID to reply to"),
785
+ comment: zod_1.z.string().describe("Reply text content (supports markdown)"),
786
+ }, async (params) => {
787
+ const result = await gitTools.replyToComment(params);
788
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
789
+ });
790
+ allowedTools.has("updatePullRequestThread") && server.tool("updatePullRequestThread", "Update a comment thread's status (resolve, reactivate, close, etc.)", {
791
+ repository: zod_1.z.string().describe("Repository name or ID"),
792
+ pullRequestId: zod_1.z.coerce.number().describe("Pull request ID"),
793
+ threadId: zod_1.z.coerce.number().describe("Thread ID to update"),
794
+ status: zod_1.z.enum(['active', 'byDesign', 'closed', 'fixed', 'pending', 'unknown', 'wontFix']).describe("New thread status"),
795
+ }, async (params) => {
796
+ const result = await gitTools.updatePullRequestThread(params);
797
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
798
+ });
799
+ allowedTools.has("createBranch") && server.tool("createBranch", "Create a new branch from a source branch name or commit SHA", {
800
+ repository: zod_1.z.string().describe("Repository name or ID"),
801
+ branchName: zod_1.z.string().describe("New branch name (e.g., 'feature/my-feature')"),
802
+ sourceRef: zod_1.z.string().describe("Source branch name (e.g., 'main') or commit SHA to branch from"),
803
+ }, async (params) => {
804
+ const result = await gitTools.createBranch(params);
805
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
806
+ });
698
807
  // Register Testing Capabilities Tools
699
808
  allowedTools.has("runAutomatedTests") && server.tool("runAutomatedTests", "Execute automated test suites", {
700
- testSuiteId: zod_1.z.number().optional().describe("ID of the test suite to run"),
701
- testPlanId: zod_1.z.number().optional().describe("ID of the test plan to run"),
809
+ testSuiteId: zod_1.z.coerce.number().optional().describe("ID of the test suite to run"),
810
+ testPlanId: zod_1.z.coerce.number().optional().describe("ID of the test plan to run"),
702
811
  testEnvironment: zod_1.z.string().optional().describe("Environment to run tests in"),
703
812
  parallelExecution: zod_1.z.boolean().optional().describe("Whether to run tests in parallel")
704
813
  }, async (params, extra) => {
@@ -710,7 +819,7 @@ async function main() {
710
819
  };
711
820
  });
712
821
  allowedTools.has("getTestAutomationStatus") && server.tool("getTestAutomationStatus", "Check status of automated test execution", {
713
- testRunId: zod_1.z.number().describe("ID of the test run to check status for")
822
+ testRunId: zod_1.z.coerce.number().describe("ID of the test run to check status for")
714
823
  }, async (params, extra) => {
715
824
  const result = await testingCapabilitiesTools.getTestAutomationStatus(params);
716
825
  return {
@@ -734,7 +843,7 @@ async function main() {
734
843
  allowedTools.has("createTestDataGenerator") && server.tool("createTestDataGenerator", "Generate test data for automated tests", {
735
844
  name: zod_1.z.string().describe("Name of the test data generator"),
736
845
  dataSchema: zod_1.z.record(zod_1.z.any()).describe("Schema for the test data to generate"),
737
- recordCount: zod_1.z.number().optional().describe("Number of records to generate")
846
+ recordCount: zod_1.z.coerce.number().optional().describe("Number of records to generate")
738
847
  }, async (params, extra) => {
739
848
  const result = await testingCapabilitiesTools.createTestDataGenerator(params);
740
849
  return {
@@ -756,8 +865,8 @@ async function main() {
756
865
  };
757
866
  });
758
867
  allowedTools.has("getTestFlakiness") && server.tool("getTestFlakiness", "Analyze and report on test flakiness", {
759
- testId: zod_1.z.number().optional().describe("ID of a specific test to analyze"),
760
- testRunIds: zod_1.z.array(zod_1.z.number()).optional().describe("Specific test runs to analyze"),
868
+ testId: zod_1.z.coerce.number().optional().describe("ID of a specific test to analyze"),
869
+ testRunIds: zod_1.z.array(zod_1.z.coerce.number()).optional().describe("Specific test runs to analyze"),
761
870
  timeRange: zod_1.z.string().optional().describe("Time range for analysis (e.g., '30d')")
762
871
  }, async (params, extra) => {
763
872
  const result = await testingCapabilitiesTools.getTestFlakiness(params);
@@ -779,7 +888,7 @@ async function main() {
779
888
  };
780
889
  });
781
890
  allowedTools.has("runTestImpactAnalysis") && server.tool("runTestImpactAnalysis", "Determine which tests to run based on code changes", {
782
- buildId: zod_1.z.number().describe("ID of the build to analyze"),
891
+ buildId: zod_1.z.coerce.number().describe("ID of the build to analyze"),
783
892
  changedFiles: zod_1.z.array(zod_1.z.string()).optional().describe("List of changed files")
784
893
  }, async (params, extra) => {
785
894
  const result = await testingCapabilitiesTools.runTestImpactAnalysis(params);
@@ -801,7 +910,7 @@ async function main() {
801
910
  };
802
911
  });
803
912
  allowedTools.has("runTestOptimization") && server.tool("runTestOptimization", "Optimize test suite execution for faster feedback", {
804
- testPlanId: zod_1.z.number().describe("ID of the test plan to optimize"),
913
+ testPlanId: zod_1.z.coerce.number().describe("ID of the test plan to optimize"),
805
914
  optimizationGoal: zod_1.z.enum(['time', 'coverage', 'reliability']).describe("Optimization goal")
806
915
  }, async (params, extra) => {
807
916
  const result = await testingCapabilitiesTools.runTestOptimization(params);
@@ -824,7 +933,7 @@ async function main() {
824
933
  };
825
934
  });
826
935
  allowedTools.has("recordExploratoryTestResults") && server.tool("recordExploratoryTestResults", "Record findings during exploratory testing", {
827
- sessionId: zod_1.z.number().describe("ID of the exploratory session"),
936
+ sessionId: zod_1.z.coerce.number().describe("ID of the exploratory session"),
828
937
  findings: zod_1.z.array(zod_1.z.string()).describe("List of findings to record"),
829
938
  attachments: zod_1.z.array(zod_1.z.object({
830
939
  name: zod_1.z.string().describe("Name of the attachment"),
@@ -840,8 +949,8 @@ async function main() {
840
949
  };
841
950
  });
842
951
  allowedTools.has("convertFindingsToWorkItems") && server.tool("convertFindingsToWorkItems", "Convert exploratory test findings to work items", {
843
- sessionId: zod_1.z.number().describe("ID of the exploratory session"),
844
- findingIds: zod_1.z.array(zod_1.z.number()).describe("IDs of findings to convert"),
952
+ sessionId: zod_1.z.coerce.number().describe("ID of the exploratory session"),
953
+ findingIds: zod_1.z.array(zod_1.z.coerce.number()).describe("IDs of findings to convert"),
845
954
  workItemType: zod_1.z.string().optional().describe("Type of work item to create")
846
955
  }, async (params, extra) => {
847
956
  const result = await testingCapabilitiesTools.convertFindingsToWorkItems(params);
@@ -1028,7 +1137,7 @@ async function main() {
1028
1137
  allowedTools.has("getPackageVersions") && server.tool("getPackageVersions", "Get versions of a package in a feed", {
1029
1138
  feedId: zod_1.z.string().describe("ID of the feed"),
1030
1139
  packageName: zod_1.z.string().describe("Name of the package"),
1031
- top: zod_1.z.number().optional().describe("Maximum number of versions to return")
1140
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of versions to return")
1032
1141
  }, async (params, extra) => {
1033
1142
  const result = await artifactManagementTools.getPackageVersions(params);
1034
1143
  return {
@@ -1092,7 +1201,7 @@ async function main() {
1092
1201
  allowedTools.has("getContainerImageTags") && server.tool("getContainerImageTags", "Get tags for a container image", {
1093
1202
  repositoryName: zod_1.z.string().describe("Name of the container repository"),
1094
1203
  imageName: zod_1.z.string().describe("Name of the container image"),
1095
- top: zod_1.z.number().optional().describe("Maximum number of tags to return")
1204
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of tags to return")
1096
1205
  }, async (params, extra) => {
1097
1206
  const result = await artifactManagementTools.getContainerImageTags(params);
1098
1207
  return {
@@ -1167,7 +1276,7 @@ async function main() {
1167
1276
  });
1168
1277
  // AI Assisted Development Tools
1169
1278
  allowedTools.has("getAICodeReview") && server.tool("getAICodeReview", "Get AI-based code review suggestions", {
1170
- pullRequestId: zod_1.z.number().optional().describe("ID of the pull request to review"),
1279
+ pullRequestId: zod_1.z.coerce.number().optional().describe("ID of the pull request to review"),
1171
1280
  repositoryId: zod_1.z.string().optional().describe("ID of the repository"),
1172
1281
  commitId: zod_1.z.string().optional().describe("ID of the commit to review"),
1173
1282
  filePath: zod_1.z.string().optional().describe("Path to the file to review")
@@ -1182,8 +1291,8 @@ async function main() {
1182
1291
  allowedTools.has("suggestCodeOptimization") && server.tool("suggestCodeOptimization", "Suggest code optimizations using AI. Supports both repository names and IDs.", {
1183
1292
  repository: zod_1.z.string().describe("Repository name (e.g., 'MyProject') or ID (GUID). Repository names are case-insensitive."),
1184
1293
  filePath: zod_1.z.string().describe("Path to the file to optimize"),
1185
- lineStart: zod_1.z.number().optional().describe("Starting line number"),
1186
- lineEnd: zod_1.z.number().optional().describe("Ending line number"),
1294
+ lineStart: zod_1.z.coerce.number().optional().describe("Starting line number"),
1295
+ lineEnd: zod_1.z.coerce.number().optional().describe("Ending line number"),
1187
1296
  optimizationType: zod_1.z.enum(['performance', 'memory', 'readability', 'all']).optional().describe("Type of optimization to focus on")
1188
1297
  }, async (params, extra) => {
1189
1298
  const result = await aiAssistedDevelopmentTools.suggestCodeOptimization(params);
@@ -1208,7 +1317,7 @@ async function main() {
1208
1317
  });
1209
1318
  allowedTools.has("getPredictiveBugAnalysis") && server.tool("getPredictiveBugAnalysis", "Predict potential bugs in code changes. Supports both repository names and IDs.", {
1210
1319
  repository: zod_1.z.string().describe("Repository name (e.g., 'MyProject') or ID (GUID). Repository names are case-insensitive."),
1211
- pullRequestId: zod_1.z.number().optional().describe("ID of the pull request"),
1320
+ pullRequestId: zod_1.z.coerce.number().optional().describe("ID of the pull request"),
1212
1321
  branch: zod_1.z.string().optional().describe("Branch to analyze"),
1213
1322
  filePath: zod_1.z.string().optional().describe("Path to the file to analyze")
1214
1323
  }, async (params, extra) => {
@@ -1233,7 +1342,7 @@ async function main() {
1233
1342
  };
1234
1343
  });
1235
1344
  allowedTools.has("getPredictiveEffortEstimation") && server.tool("getPredictiveEffortEstimation", "AI-based effort estimation for work items", {
1236
- workItemIds: zod_1.z.array(zod_1.z.number()).optional().describe("IDs of work items to estimate"),
1345
+ workItemIds: zod_1.z.array(zod_1.z.coerce.number()).optional().describe("IDs of work items to estimate"),
1237
1346
  workItemType: zod_1.z.string().optional().describe("Type of work items to estimate"),
1238
1347
  areaPath: zod_1.z.string().optional().describe("Area path to filter work items")
1239
1348
  }, async (params, extra) => {
@@ -1258,7 +1367,7 @@ async function main() {
1258
1367
  };
1259
1368
  });
1260
1369
  allowedTools.has("suggestWorkItemRefinements") && server.tool("suggestWorkItemRefinements", "Get AI suggestions for work item refinements", {
1261
- workItemId: zod_1.z.number().optional().describe("ID of the work item to refine"),
1370
+ workItemId: zod_1.z.coerce.number().optional().describe("ID of the work item to refine"),
1262
1371
  workItemType: zod_1.z.string().optional().describe("Type of work item"),
1263
1372
  areaPath: zod_1.z.string().optional().describe("Area path to filter work items")
1264
1373
  }, async (params, extra) => {
@@ -1294,7 +1403,7 @@ async function main() {
1294
1403
  };
1295
1404
  });
1296
1405
  allowedTools.has("predictBuildFailures") && server.tool("predictBuildFailures", "Predict potential build failures before they occur", {
1297
- buildDefinitionId: zod_1.z.number().describe("ID of the build definition"),
1406
+ buildDefinitionId: zod_1.z.coerce.number().describe("ID of the build definition"),
1298
1407
  lookbackPeriod: zod_1.z.string().optional().describe("Period to analyze for patterns (e.g., '30d')")
1299
1408
  }, async (params, extra) => {
1300
1409
  const result = await aiAssistedDevelopmentTools.predictBuildFailures(params);
@@ -1305,9 +1414,9 @@ async function main() {
1305
1414
  };
1306
1415
  });
1307
1416
  allowedTools.has("optimizeTestSelection") && server.tool("optimizeTestSelection", "Intelligently select tests to run based on changes", {
1308
- buildId: zod_1.z.number().describe("ID of the build"),
1417
+ buildId: zod_1.z.coerce.number().describe("ID of the build"),
1309
1418
  changedFiles: zod_1.z.array(zod_1.z.string()).optional().describe("List of changed files"),
1310
- maxTestCount: zod_1.z.number().optional().describe("Maximum number of tests to select")
1419
+ maxTestCount: zod_1.z.coerce.number().optional().describe("Maximum number of tests to select")
1311
1420
  }, async (params, extra) => {
1312
1421
  const result = await aiAssistedDevelopmentTools.optimizeTestSelection(params);
1313
1422
  return {
@@ -1316,6 +1425,130 @@ async function main() {
1316
1425
  structuredContent: result.structuredContent
1317
1426
  };
1318
1427
  });
1428
+ // Register Wiki Tools
1429
+ allowedTools.has("listWikis") && server.tool("listWikis", "List all wikis in the project", {
1430
+ project: zod_1.z.string().optional().describe("Project name or ID (defaults to configured project)"),
1431
+ }, async (params) => {
1432
+ const result = await wikiTools.listWikis(params);
1433
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1434
+ });
1435
+ allowedTools.has("getWiki") && server.tool("getWiki", "Get details about a specific wiki", {
1436
+ wikiIdentifier: zod_1.z.string().describe("Wiki name or ID"),
1437
+ project: zod_1.z.string().optional().describe("Project name or ID"),
1438
+ }, async (params) => {
1439
+ const result = await wikiTools.getWiki(params);
1440
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1441
+ });
1442
+ allowedTools.has("listWikiPages") && server.tool("listWikiPages", "List wiki pages under a path", {
1443
+ wikiIdentifier: zod_1.z.string().describe("Wiki name or ID"),
1444
+ path: zod_1.z.string().optional().describe("Path to list pages under (default: root '/')"),
1445
+ recursionLevel: zod_1.z.string().optional().describe("'oneLevel' (default) or 'full' for all descendants"),
1446
+ project: zod_1.z.string().optional().describe("Project name or ID"),
1447
+ }, async (params) => {
1448
+ const result = await wikiTools.listWikiPages(params);
1449
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1450
+ });
1451
+ allowedTools.has("getWikiPageContent") && server.tool("getWikiPageContent", "Get the content of a wiki page", {
1452
+ wikiIdentifier: zod_1.z.string().describe("Wiki name or ID"),
1453
+ path: zod_1.z.string().optional().describe("Page path (default: root '/')"),
1454
+ project: zod_1.z.string().optional().describe("Project name or ID"),
1455
+ }, async (params) => {
1456
+ const result = await wikiTools.getWikiPageContent(params);
1457
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1458
+ });
1459
+ allowedTools.has("createOrUpdateWikiPage") && server.tool("createOrUpdateWikiPage", "Create or update a wiki page with the given content", {
1460
+ wikiIdentifier: zod_1.z.string().describe("Wiki name or ID"),
1461
+ path: zod_1.z.string().describe("Page path (e.g., '/My Page' or '/Section/Sub Page')"),
1462
+ content: zod_1.z.string().describe("Page content in markdown format"),
1463
+ comment: zod_1.z.string().optional().describe("Optional commit comment for the change"),
1464
+ project: zod_1.z.string().optional().describe("Project name or ID"),
1465
+ }, async (params) => {
1466
+ const result = await wikiTools.createOrUpdateWikiPage(params);
1467
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1468
+ });
1469
+ // Register Build/Pipeline Tools
1470
+ allowedTools.has("getBuilds") && server.tool("getBuilds", "List builds with optional filters (status, result, branch, definition, tags)", {
1471
+ definitions: zod_1.z.preprocess(coerceArray, zod_1.z.array(zod_1.z.coerce.number()).optional()).describe("Filter by definition IDs"),
1472
+ statusFilter: zod_1.z.string().optional().describe("Filter by status: inProgress, completed, cancelling, postponed, notStarted, all"),
1473
+ resultFilter: zod_1.z.string().optional().describe("Filter by result: succeeded, partiallySucceeded, failed, canceled"),
1474
+ branchName: zod_1.z.string().optional().describe("Filter by source branch (e.g., 'refs/heads/main')"),
1475
+ repositoryId: zod_1.z.string().optional().describe("Filter by repository ID"),
1476
+ repositoryType: zod_1.z.string().optional().describe("Repository type (e.g., 'TfsGit')"),
1477
+ requestedFor: zod_1.z.string().optional().describe("Filter by who requested the build"),
1478
+ tagFilters: zod_1.z.preprocess(coerceArray, zod_1.z.array(zod_1.z.string()).optional()).describe("Filter by tags"),
1479
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of builds to return (default 25)"),
1480
+ queryOrder: zod_1.z.string().optional().describe("Order: startTimeDescending (default) or startTimeAscending"),
1481
+ }, async (params) => {
1482
+ const result = await buildTools.getBuilds(params);
1483
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1484
+ });
1485
+ allowedTools.has("getBuild") && server.tool("getBuild", "Get detailed information about a specific build by ID", {
1486
+ buildId: zod_1.z.coerce.number().describe("Build ID"),
1487
+ }, async (params) => {
1488
+ const result = await buildTools.getBuild(params);
1489
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1490
+ });
1491
+ allowedTools.has("getBuildLog") && server.tool("getBuildLog", "Get build logs. Without logId returns log metadata list; with logId returns specific log content", {
1492
+ buildId: zod_1.z.coerce.number().describe("Build ID"),
1493
+ logId: zod_1.z.coerce.number().optional().describe("Specific log ID to retrieve content for"),
1494
+ startLine: zod_1.z.coerce.number().optional().describe("Start line for log content"),
1495
+ endLine: zod_1.z.coerce.number().optional().describe("End line for log content"),
1496
+ }, async (params) => {
1497
+ const result = await buildTools.getBuildLog(params);
1498
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1499
+ });
1500
+ allowedTools.has("getBuildChanges") && server.tool("getBuildChanges", "Get changes (commits) associated with a build", {
1501
+ buildId: zod_1.z.coerce.number().describe("Build ID"),
1502
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of changes to return (default 50)"),
1503
+ }, async (params) => {
1504
+ const result = await buildTools.getBuildChanges(params);
1505
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1506
+ });
1507
+ allowedTools.has("getDefinitions") && server.tool("getDefinitions", "List pipeline/build definitions with optional filters", {
1508
+ name: zod_1.z.string().optional().describe("Filter by definition name (wildcard supported)"),
1509
+ repositoryId: zod_1.z.string().optional().describe("Filter by repository ID"),
1510
+ repositoryType: zod_1.z.string().optional().describe("Repository type (e.g., 'TfsGit')"),
1511
+ path: zod_1.z.string().optional().describe("Filter by folder path (e.g., '\\\\folder')"),
1512
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of definitions to return (default 25)"),
1513
+ includeLatestBuilds: zod_1.z.boolean().optional().describe("Include latest build info for each definition"),
1514
+ }, async (params) => {
1515
+ const result = await buildTools.getDefinitions(params);
1516
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1517
+ });
1518
+ allowedTools.has("getDefinition") && server.tool("getDefinition", "Get detailed information about a specific pipeline/build definition", {
1519
+ definitionId: zod_1.z.coerce.number().describe("Definition ID"),
1520
+ includeLatestBuilds: zod_1.z.boolean().optional().describe("Include latest build info"),
1521
+ }, async (params) => {
1522
+ const result = await buildTools.getDefinition(params);
1523
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1524
+ });
1525
+ allowedTools.has("runPipeline") && server.tool("runPipeline", "Queue/trigger a pipeline run", {
1526
+ definitionId: zod_1.z.coerce.number().describe("Pipeline definition ID to run"),
1527
+ sourceBranch: zod_1.z.string().optional().describe("Source branch (e.g., 'refs/heads/main')"),
1528
+ parameters: zod_1.z.record(zod_1.z.string()).optional().describe("Pipeline parameters as key-value pairs"),
1529
+ }, async (params) => {
1530
+ const result = await buildTools.runPipeline(params);
1531
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1532
+ });
1533
+ allowedTools.has("getBuildArtifacts") && server.tool("getBuildArtifacts", "List artifacts produced by a build", {
1534
+ buildId: zod_1.z.coerce.number().describe("Build ID"),
1535
+ }, async (params) => {
1536
+ const result = await buildTools.getBuildArtifacts(params);
1537
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1538
+ });
1539
+ allowedTools.has("getBuildTimeline") && server.tool("getBuildTimeline", "Get build timeline showing stages, jobs, and tasks with their status", {
1540
+ buildId: zod_1.z.coerce.number().describe("Build ID"),
1541
+ }, async (params) => {
1542
+ const result = await buildTools.getBuildTimeline(params);
1543
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1544
+ });
1545
+ allowedTools.has("getBuildWorkItems") && server.tool("getBuildWorkItems", "Get work items associated with a build", {
1546
+ buildId: zod_1.z.coerce.number().describe("Build ID"),
1547
+ top: zod_1.z.coerce.number().optional().describe("Maximum number of work items to return (default 50)"),
1548
+ }, async (params) => {
1549
+ const result = await buildTools.getBuildWorkItems(params);
1550
+ return { content: result.content, rawData: result.rawData, structuredContent: result.structuredContent };
1551
+ });
1319
1552
  // Create a transport (use stdio for simplicity)
1320
1553
  const transport = new stdio_js_1.StdioServerTransport();
1321
1554
  // Connect to the transport and start listening