@azure-devops/mcp 2.5.0-nightly.20260323 → 2.5.0-nightly.20260325
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/tools/advanced-security.js +2 -2
- package/dist/tools/core.js +5 -5
- package/dist/tools/pipelines.js +25 -24
- package/dist/tools/repositories.js +405 -30
- package/dist/tools/search.js +6 -6
- package/dist/tools/test-plans.js +11 -11
- package/dist/tools/wiki.js +5 -5
- package/dist/tools/work-items.js +83 -27
- package/dist/tools/work.js +2 -2
- package/dist/utils.js +15 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -37,7 +37,7 @@ function configureAdvSecTools(server, _, connectionProvider) {
|
|
|
37
37
|
.array(z.enum(getEnumKeys(AlertValidityStatus)))
|
|
38
38
|
.optional()
|
|
39
39
|
.describe("Filter alerts by validity status. Only applicable for secret alerts."),
|
|
40
|
-
top: z.number().optional().default(100).describe("Maximum number of alerts to return. Defaults to 100."),
|
|
40
|
+
top: z.coerce.number().optional().default(100).describe("Maximum number of alerts to return. Defaults to 100."),
|
|
41
41
|
orderBy: z.enum(["id", "firstSeen", "lastSeen", "fixedOn", "severity"]).optional().default("severity").describe("Order results by specified field. Defaults to 'severity'."),
|
|
42
42
|
continuationToken: z.string().optional().describe("Continuation token for pagination."),
|
|
43
43
|
}, async ({ project, repository, alertType, states, severities, ruleId, ruleName, toolName, ref, onlyDefaultBranch, confidenceLevels, validity, top, orderBy, continuationToken }) => {
|
|
@@ -79,7 +79,7 @@ function configureAdvSecTools(server, _, connectionProvider) {
|
|
|
79
79
|
server.tool(ADVSEC_TOOLS.get_alert_details, "Get detailed information about a specific Advanced Security alert.", {
|
|
80
80
|
project: z.string().describe("The name or ID of the Azure DevOps project."),
|
|
81
81
|
repository: z.string().describe("The name or ID of the repository containing the alert."),
|
|
82
|
-
alertId: z.number().describe("The ID of the alert to retrieve details for."),
|
|
82
|
+
alertId: z.coerce.number().min(1).describe("The ID of the alert to retrieve details for."),
|
|
83
83
|
ref: z.string().optional().describe("Git reference (branch) to filter the alert."),
|
|
84
84
|
}, async ({ project, repository, alertId, ref }) => {
|
|
85
85
|
try {
|
package/dist/tools/core.js
CHANGED
|
@@ -16,8 +16,8 @@ function configureCoreTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
16
16
|
server.tool(CORE_TOOLS.list_project_teams, "Retrieve a list of teams for an Azure DevOps project. If a project is not specified, you will be prompted to select one.", {
|
|
17
17
|
project: z.string().optional().describe("The name or ID of the Azure DevOps project. Reuse from prior context if already known. If not provided, a project selection prompt will be shown."),
|
|
18
18
|
mine: z.boolean().optional().describe("If true, only return teams that the authenticated user is a member of."),
|
|
19
|
-
top: z.number().optional().describe("The maximum number of teams to return. Defaults to 100."),
|
|
20
|
-
skip: z.number().optional().describe("The number of teams to skip for pagination. Defaults to 0."),
|
|
19
|
+
top: z.coerce.number().optional().describe("The maximum number of teams to return. Defaults to 100."),
|
|
20
|
+
skip: z.coerce.number().optional().describe("The number of teams to skip for pagination. Defaults to 0."),
|
|
21
21
|
}, async ({ project, mine, top, skip }) => {
|
|
22
22
|
try {
|
|
23
23
|
const connection = await connectionProvider();
|
|
@@ -47,9 +47,9 @@ function configureCoreTools(server, tokenProvider, connectionProvider, userAgent
|
|
|
47
47
|
});
|
|
48
48
|
server.tool(CORE_TOOLS.list_projects, "Retrieve a list of projects in your Azure DevOps organization.", {
|
|
49
49
|
stateFilter: z.enum(["all", "wellFormed", "createPending", "deleted"]).default("wellFormed").describe("Filter projects by their state. Defaults to 'wellFormed'."),
|
|
50
|
-
top: z.number().optional().describe("The maximum number of projects to return. Defaults to 100."),
|
|
51
|
-
skip: z.number().optional().describe("The number of projects to skip for pagination. Defaults to 0."),
|
|
52
|
-
continuationToken: z.number().optional().describe("Continuation token for pagination. Used to fetch the next set of results if available."),
|
|
50
|
+
top: z.coerce.number().optional().describe("The maximum number of projects to return. Defaults to 100."),
|
|
51
|
+
skip: z.coerce.number().optional().describe("The number of projects to skip for pagination. Defaults to 0."),
|
|
52
|
+
continuationToken: z.coerce.number().optional().describe("Continuation token for pagination. Used to fetch the next set of results if available."),
|
|
53
53
|
projectNameFilter: z.string().optional().describe("Filter projects by name. Supports partial matches."),
|
|
54
54
|
}, async ({ stateFilter, top, skip, continuationToken, projectNameFilter }) => {
|
|
55
55
|
try {
|
package/dist/tools/pipelines.js
CHANGED
|
@@ -37,7 +37,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
37
37
|
top: z.number().optional().describe("Maximum number of build definitions to return"),
|
|
38
38
|
continuationToken: z.string().optional().describe("Token for continuing paged results"),
|
|
39
39
|
minMetricsTime: z.coerce.date().optional().describe("Minimum metrics time to filter build definitions"),
|
|
40
|
-
definitionIds: z.array(z.number()).optional().describe("Array of build definition IDs to filter"),
|
|
40
|
+
definitionIds: z.array(z.coerce.number().min(1)).optional().describe("Array of build definition IDs to filter"),
|
|
41
41
|
builtAfter: z.coerce.date().optional().describe("Return definitions that have builds after this date"),
|
|
42
42
|
notBuiltAfter: z.coerce.date().optional().describe("Return definitions that do not have builds after this date"),
|
|
43
43
|
includeAllProperties: z.boolean().optional().describe("Whether to include all properties in the results"),
|
|
@@ -105,7 +105,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
105
105
|
});
|
|
106
106
|
server.tool(PIPELINE_TOOLS.pipelines_get_build_definition_revisions, "Retrieves a list of revisions for a specific build definition.", {
|
|
107
107
|
project: z.string().describe("Project ID or name to get the build definition revisions for"),
|
|
108
|
-
definitionId: z.number().describe("ID of the build definition to get revisions for"),
|
|
108
|
+
definitionId: z.coerce.number().min(1).describe("ID of the build definition to get revisions for"),
|
|
109
109
|
}, async ({ project, definitionId }) => {
|
|
110
110
|
const connection = await connectionProvider();
|
|
111
111
|
const buildApi = await connection.getBuildApi();
|
|
@@ -116,8 +116,8 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
116
116
|
});
|
|
117
117
|
server.tool(PIPELINE_TOOLS.pipelines_get_builds, "Retrieves a list of builds for a given project.", {
|
|
118
118
|
project: z.string().describe("Project ID or name to get builds for"),
|
|
119
|
-
definitions: z.array(z.number()).optional().describe("Array of build definition IDs to filter builds"),
|
|
120
|
-
queues: z.array(z.number()).optional().describe("Array of queue IDs to filter builds"),
|
|
119
|
+
definitions: z.array(z.coerce.number().min(1)).optional().describe("Array of build definition IDs to filter builds"),
|
|
120
|
+
queues: z.array(z.coerce.number().min(1)).optional().describe("Array of queue IDs to filter builds"),
|
|
121
121
|
buildNumber: z.string().optional().describe("Build number to filter builds"),
|
|
122
122
|
minTime: z.coerce.date().optional().describe("Minimum finish time to filter builds"),
|
|
123
123
|
maxTime: z.coerce.date().optional().describe("Maximum finish time to filter builds"),
|
|
@@ -137,7 +137,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
137
137
|
.optional()
|
|
138
138
|
.describe("Order in which builds are returned"),
|
|
139
139
|
branchName: z.string().optional().describe("Branch name to filter builds"),
|
|
140
|
-
buildIds: z.array(z.number()).optional().describe("Array of build IDs to retrieve"),
|
|
140
|
+
buildIds: z.array(z.coerce.number().min(1)).optional().describe("Array of build IDs to retrieve"),
|
|
141
141
|
repositoryId: z.string().optional().describe("Repository ID to filter builds"),
|
|
142
142
|
repositoryType: z.enum(["TfsGit", "GitHub", "BitbucketCloud"]).optional().describe("Type of repository to filter builds"),
|
|
143
143
|
}, async ({ project, definitions, queues, buildNumber, minTime, maxTime, requestedFor, reasonFilter, statusFilter, resultFilter, tagFilters, properties, top, continuationToken, maxBuildsPerDefinition, deletedFilter, queryOrder, branchName, buildIds, repositoryId, repositoryType, }) => {
|
|
@@ -150,7 +150,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
150
150
|
});
|
|
151
151
|
server.tool(PIPELINE_TOOLS.pipelines_get_build_log, "Retrieves the logs for a specific build.", {
|
|
152
152
|
project: z.string().describe("Project ID or name to get the build log for"),
|
|
153
|
-
buildId: z.number().describe("ID of the build to get the log for"),
|
|
153
|
+
buildId: z.coerce.number().min(1).describe("ID of the build to get the log for"),
|
|
154
154
|
}, async ({ project, buildId }) => {
|
|
155
155
|
const connection = await connectionProvider();
|
|
156
156
|
const buildApi = await connection.getBuildApi();
|
|
@@ -161,10 +161,10 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
161
161
|
});
|
|
162
162
|
server.tool(PIPELINE_TOOLS.pipelines_get_build_log_by_id, "Get a specific build log by log ID.", {
|
|
163
163
|
project: z.string().describe("Project ID or name to get the build log for"),
|
|
164
|
-
buildId: z.number().describe("ID of the build to get the log for"),
|
|
165
|
-
logId: z.number().describe("ID of the log to retrieve"),
|
|
166
|
-
startLine: z.number().optional().describe("Starting line number for the log content, defaults to 0"),
|
|
167
|
-
endLine: z.number().optional().describe("Ending line number for the log content, defaults to the end of the log"),
|
|
164
|
+
buildId: z.coerce.number().min(1).describe("ID of the build to get the log for"),
|
|
165
|
+
logId: z.coerce.number().min(1).describe("ID of the log to retrieve"),
|
|
166
|
+
startLine: z.coerce.number().optional().describe("Starting line number for the log content, defaults to 0"),
|
|
167
|
+
endLine: z.coerce.number().optional().describe("Ending line number for the log content, defaults to the end of the log"),
|
|
168
168
|
}, async ({ project, buildId, logId, startLine, endLine }) => {
|
|
169
169
|
const connection = await connectionProvider();
|
|
170
170
|
const buildApi = await connection.getBuildApi();
|
|
@@ -175,7 +175,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
175
175
|
});
|
|
176
176
|
server.tool(PIPELINE_TOOLS.pipelines_get_build_changes, "Get the changes associated with a specific build.", {
|
|
177
177
|
project: z.string().describe("Project ID or name to get the build changes for"),
|
|
178
|
-
buildId: z.number().describe("ID of the build to get changes for"),
|
|
178
|
+
buildId: z.coerce.number().min(1).describe("ID of the build to get changes for"),
|
|
179
179
|
continuationToken: z.string().optional().describe("Continuation token for pagination"),
|
|
180
180
|
top: z.number().default(100).describe("Number of changes to retrieve, defaults to 100"),
|
|
181
181
|
includeSourceChange: z.boolean().optional().describe("Whether to include source changes in the results, defaults to false"),
|
|
@@ -189,8 +189,8 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
189
189
|
});
|
|
190
190
|
server.tool(PIPELINE_TOOLS.pipelines_get_run, "Gets a run for a particular pipeline.", {
|
|
191
191
|
project: z.string().describe("Project ID or name to run the build in"),
|
|
192
|
-
pipelineId: z.number().describe("ID of the pipeline to run"),
|
|
193
|
-
runId: z.number().describe("ID of the run to get"),
|
|
192
|
+
pipelineId: z.coerce.number().min(1).describe("ID of the pipeline to run"),
|
|
193
|
+
runId: z.coerce.number().min(1).describe("ID of the run to get"),
|
|
194
194
|
}, async ({ project, pipelineId, runId }) => {
|
|
195
195
|
const connection = await connectionProvider();
|
|
196
196
|
const pipelinesApi = await connection.getPipelinesApi();
|
|
@@ -201,7 +201,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
201
201
|
});
|
|
202
202
|
server.tool(PIPELINE_TOOLS.pipelines_list_runs, "Gets top 10000 runs for a particular pipeline.", {
|
|
203
203
|
project: z.string().describe("Project ID or name to run the build in"),
|
|
204
|
-
pipelineId: z.number().describe("ID of the pipeline to run"),
|
|
204
|
+
pipelineId: z.coerce.number().min(1).describe("ID of the pipeline to run"),
|
|
205
205
|
}, async ({ project, pipelineId }) => {
|
|
206
206
|
const connection = await connectionProvider();
|
|
207
207
|
const pipelinesApi = await connection.getPipelinesApi();
|
|
@@ -227,7 +227,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
227
227
|
}))
|
|
228
228
|
.optional(),
|
|
229
229
|
pipelines: z.record(z.string().describe("Name of the pipeline resource."), z.object({
|
|
230
|
-
runId: z.number().describe("Id of the source pipeline run that triggered or is referenced by this pipeline run."),
|
|
230
|
+
runId: z.coerce.number().min(1).describe("Id of the source pipeline run that triggered or is referenced by this pipeline run."),
|
|
231
231
|
version: z.string().optional().describe("Version of the source pipeline run."),
|
|
232
232
|
})),
|
|
233
233
|
repositories: z
|
|
@@ -241,8 +241,8 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
241
241
|
});
|
|
242
242
|
server.tool(PIPELINE_TOOLS.pipelines_run_pipeline, "Starts a new run of a pipeline.", {
|
|
243
243
|
project: z.string().describe("Project ID or name to run the build in"),
|
|
244
|
-
pipelineId: z.number().describe("ID of the pipeline to run"),
|
|
245
|
-
pipelineVersion: z.number().optional().describe("Version of the pipeline to run. If not provided, the latest version will be used."),
|
|
244
|
+
pipelineId: z.coerce.number().min(1).describe("ID of the pipeline to run"),
|
|
245
|
+
pipelineVersion: z.coerce.number().min(1).optional().describe("Version of the pipeline to run. If not provided, the latest version will be used."),
|
|
246
246
|
previewRun: z.boolean().optional().describe("If true, returns the final YAML document after parsing templates without creating a new run."),
|
|
247
247
|
resources: resourcesSchema.optional().describe("A dictionary of resources to pass to the pipeline."),
|
|
248
248
|
stagesToSkip: z.array(z.string()).optional().describe("A list of stages to skip."),
|
|
@@ -277,7 +277,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
277
277
|
});
|
|
278
278
|
server.tool(PIPELINE_TOOLS.pipelines_get_build_status, "Fetches the status of a specific build.", {
|
|
279
279
|
project: z.string().describe("Project ID or name to get the build status for"),
|
|
280
|
-
buildId: z.number().describe("ID of the build to get the status for"),
|
|
280
|
+
buildId: z.coerce.number().min(1).describe("ID of the build to get the status for"),
|
|
281
281
|
}, async ({ project, buildId }) => {
|
|
282
282
|
const connection = await connectionProvider();
|
|
283
283
|
const buildApi = await connection.getBuildApi();
|
|
@@ -288,14 +288,14 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
288
288
|
});
|
|
289
289
|
server.tool(PIPELINE_TOOLS.pipelines_update_build_stage, "Updates the stage of a specific build.", {
|
|
290
290
|
project: z.string().describe("Project ID or name to update the build stage for"),
|
|
291
|
-
buildId: z.number().describe("ID of the build to update"),
|
|
291
|
+
buildId: z.coerce.number().min(1).describe("ID of the build to update"),
|
|
292
292
|
stageName: z.string().describe("Name of the stage to update"),
|
|
293
293
|
status: z.enum(getEnumKeys(StageUpdateType)).describe("New status for the stage"),
|
|
294
294
|
forceRetryAllJobs: z.boolean().default(false).describe("Whether to force retry all jobs in the stage."),
|
|
295
295
|
}, async ({ project, buildId, stageName, status, forceRetryAllJobs }) => {
|
|
296
296
|
const connection = await connectionProvider();
|
|
297
297
|
const orgUrl = connection.serverUrl;
|
|
298
|
-
const endpoint = `${orgUrl}/${project}/_apis/build/builds/${buildId}/stages/${stageName}?api-version=${apiVersion}`;
|
|
298
|
+
const endpoint = `${orgUrl}/${encodeURIComponent(project)}/_apis/build/builds/${buildId}/stages/${encodeURIComponent(stageName)}?api-version=${apiVersion}`;
|
|
299
299
|
const token = await tokenProvider();
|
|
300
300
|
const body = {
|
|
301
301
|
forceRetryAllJobs: forceRetryAllJobs,
|
|
@@ -321,7 +321,7 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
321
321
|
});
|
|
322
322
|
server.tool(PIPELINE_TOOLS.pipelines_list_artifacts, "Lists artifacts for a given build.", {
|
|
323
323
|
project: z.string().describe("The name or ID of the project."),
|
|
324
|
-
buildId: z.number().describe("The ID of the build."),
|
|
324
|
+
buildId: z.coerce.number().min(1).describe("The ID of the build."),
|
|
325
325
|
}, async ({ project, buildId }) => {
|
|
326
326
|
const connection = await connectionProvider();
|
|
327
327
|
const buildApi = await connection.getBuildApi();
|
|
@@ -332,16 +332,17 @@ function configurePipelineTools(server, tokenProvider, connectionProvider, userA
|
|
|
332
332
|
});
|
|
333
333
|
server.tool(PIPELINE_TOOLS.pipelines_download_artifact, "Downloads a pipeline artifact.", {
|
|
334
334
|
project: z.string().describe("The name or ID of the project."),
|
|
335
|
-
buildId: z.number().describe("The ID of the build."),
|
|
335
|
+
buildId: z.coerce.number().min(1).describe("The ID of the build."),
|
|
336
336
|
artifactName: z.string().describe("The name of the artifact to download."),
|
|
337
337
|
destinationPath: z.string().optional().describe("The local path to download the artifact to. If not provided, returns binary content as base64."),
|
|
338
338
|
}, async ({ project, buildId, artifactName, destinationPath }) => {
|
|
339
339
|
const isAbsolutePath = (value) => posix.isAbsolute(value) || win32.isAbsolute(value);
|
|
340
|
+
const hasDriveLetter = (value) => /^[a-zA-Z]:/.test(value);
|
|
340
341
|
if (artifactName.includes("..")) {
|
|
341
342
|
throw new Error("Invalid artifactName: path traversal is not allowed.");
|
|
342
343
|
}
|
|
343
|
-
if (destinationPath && (destinationPath.includes("..") || isAbsolutePath(destinationPath))) {
|
|
344
|
-
throw new Error("Invalid destinationPath: absolute paths and
|
|
344
|
+
if (destinationPath && (destinationPath.includes("..") || isAbsolutePath(destinationPath) || hasDriveLetter(destinationPath))) {
|
|
345
|
+
throw new Error("Invalid destinationPath: absolute paths and path traversals are not allowed.");
|
|
345
346
|
}
|
|
346
347
|
const connection = await connectionProvider();
|
|
347
348
|
const buildApi = await connection.getBuildApi();
|