@azure-devops/mcp 0.1.0 → 0.2.0-preview-oauth

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.
@@ -1,31 +1,39 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
+ import { apiVersion, getEnumKeys, safeEnumConvert } from "../utils.js";
3
4
  import { BuildQueryOrder, DefinitionQueryOrder } from "azure-devops-node-api/interfaces/BuildInterfaces.js";
4
5
  import { z } from "zod";
6
+ import { StageUpdateType } from "azure-devops-node-api/interfaces/BuildInterfaces.js";
5
7
  const BUILD_TOOLS = {
8
+ get_builds: "build_get_builds",
9
+ get_changes: "build_get_changes",
6
10
  get_definitions: "build_get_definitions",
7
11
  get_definition_revisions: "build_get_definition_revisions",
8
- get_builds: "build_get_builds",
9
12
  get_log: "build_get_log",
10
13
  get_log_by_id: "build_get_log_by_id",
11
- get_changes: "build_get_changes",
12
- run_build: "build_run_build",
13
- get_status: "build_get_status"
14
+ get_status: "build_get_status",
15
+ pipelines_get_run: "pipelines_get_run",
16
+ pipelines_list_runs: "pipelines_list_runs",
17
+ pipelines_run_pipeline: "pipelines_run_pipeline",
18
+ update_build_stage: "build_update_build_stage",
14
19
  };
15
- function configureBuildTools(server, tokenProvider, connectionProvider) {
20
+ function configureBuildTools(server, tokenProvider, connectionProvider, userAgentProvider) {
16
21
  server.tool(BUILD_TOOLS.get_definitions, "Retrieves a list of build definitions for a given project.", {
17
22
  project: z.string().describe("Project ID or name to get build definitions for"),
18
23
  repositoryId: z.string().optional().describe("Repository ID to filter build definitions"),
19
24
  repositoryType: z.enum(["TfsGit", "GitHub", "BitbucketCloud"]).optional().describe("Type of repository to filter build definitions"),
20
25
  name: z.string().optional().describe("Name of the build definition to filter"),
21
26
  path: z.string().optional().describe("Path of the build definition to filter"),
22
- queryOrder: z.nativeEnum(DefinitionQueryOrder).optional().describe("Order in which build definitions are returned"),
27
+ queryOrder: z
28
+ .enum(getEnumKeys(DefinitionQueryOrder))
29
+ .optional()
30
+ .describe("Order in which build definitions are returned"),
23
31
  top: z.number().optional().describe("Maximum number of build definitions to return"),
24
32
  continuationToken: z.string().optional().describe("Token for continuing paged results"),
25
- minMetricsTime: z.date().optional().describe("Minimum metrics time to filter build definitions"),
33
+ minMetricsTime: z.coerce.date().optional().describe("Minimum metrics time to filter build definitions"),
26
34
  definitionIds: z.array(z.number()).optional().describe("Array of build definition IDs to filter"),
27
- builtAfter: z.date().optional().describe("Return definitions that have builds after this date"),
28
- notBuiltAfter: z.date().optional().describe("Return definitions that do not have builds after this date"),
35
+ builtAfter: z.coerce.date().optional().describe("Return definitions that have builds after this date"),
36
+ notBuiltAfter: z.coerce.date().optional().describe("Return definitions that do not have builds after this date"),
29
37
  includeAllProperties: z.boolean().optional().describe("Whether to include all properties in the results"),
30
38
  includeLatestBuilds: z.boolean().optional().describe("Whether to include the latest builds for each definition"),
31
39
  taskIdFilter: z.string().optional().describe("Task ID to filter build definitions"),
@@ -34,7 +42,7 @@ function configureBuildTools(server, tokenProvider, connectionProvider) {
34
42
  }, async ({ project, repositoryId, repositoryType, name, path, queryOrder, top, continuationToken, minMetricsTime, definitionIds, builtAfter, notBuiltAfter, includeAllProperties, includeLatestBuilds, taskIdFilter, processType, yamlFilename, }) => {
35
43
  const connection = await connectionProvider();
36
44
  const buildApi = await connection.getBuildApi();
37
- const buildDefinitions = await buildApi.getDefinitions(project, name, repositoryId, repositoryType, queryOrder, top, continuationToken, minMetricsTime, definitionIds, path, builtAfter, notBuiltAfter, includeAllProperties, includeLatestBuilds, taskIdFilter, processType, yamlFilename);
45
+ const buildDefinitions = await buildApi.getDefinitions(project, name, repositoryId, repositoryType, safeEnumConvert(DefinitionQueryOrder, queryOrder), top, continuationToken, minMetricsTime, definitionIds, path, builtAfter, notBuiltAfter, includeAllProperties, includeLatestBuilds, taskIdFilter, processType, yamlFilename);
38
46
  return {
39
47
  content: [{ type: "text", text: JSON.stringify(buildDefinitions, null, 2) }],
40
48
  };
@@ -55,8 +63,8 @@ function configureBuildTools(server, tokenProvider, connectionProvider) {
55
63
  definitions: z.array(z.number()).optional().describe("Array of build definition IDs to filter builds"),
56
64
  queues: z.array(z.number()).optional().describe("Array of queue IDs to filter builds"),
57
65
  buildNumber: z.string().optional().describe("Build number to filter builds"),
58
- minTime: z.date().optional().describe("Minimum finish time to filter builds"),
59
- maxTime: z.date().optional().describe("Maximum finish time to filter builds"),
66
+ minTime: z.coerce.date().optional().describe("Minimum finish time to filter builds"),
67
+ maxTime: z.coerce.date().optional().describe("Maximum finish time to filter builds"),
60
68
  requestedFor: z.string().optional().describe("User ID or name who requested the build"),
61
69
  reasonFilter: z.number().optional().describe("Reason filter for the build (see BuildReason enum)"),
62
70
  statusFilter: z.number().optional().describe("Status filter for the build (see BuildStatus enum)"),
@@ -67,7 +75,11 @@ function configureBuildTools(server, tokenProvider, connectionProvider) {
67
75
  continuationToken: z.string().optional().describe("Token for continuing paged results"),
68
76
  maxBuildsPerDefinition: z.number().optional().describe("Maximum number of builds per definition"),
69
77
  deletedFilter: z.number().optional().describe("Filter for deleted builds (see QueryDeletedOption enum)"),
70
- queryOrder: z.nativeEnum(BuildQueryOrder).default(BuildQueryOrder.QueueTimeDescending).optional().describe("Order in which builds are returned"),
78
+ queryOrder: z
79
+ .enum(getEnumKeys(BuildQueryOrder))
80
+ .default("QueueTimeDescending")
81
+ .optional()
82
+ .describe("Order in which builds are returned"),
71
83
  branchName: z.string().optional().describe("Branch name to filter builds"),
72
84
  buildIds: z.array(z.number()).optional().describe("Array of build IDs to retrieve"),
73
85
  repositoryId: z.string().optional().describe("Repository ID to filter builds"),
@@ -75,7 +87,7 @@ function configureBuildTools(server, tokenProvider, connectionProvider) {
75
87
  }, async ({ project, definitions, queues, buildNumber, minTime, maxTime, requestedFor, reasonFilter, statusFilter, resultFilter, tagFilters, properties, top, continuationToken, maxBuildsPerDefinition, deletedFilter, queryOrder, branchName, buildIds, repositoryId, repositoryType, }) => {
76
88
  const connection = await connectionProvider();
77
89
  const buildApi = await connection.getBuildApi();
78
- const builds = await buildApi.getBuilds(project, definitions, queues, buildNumber, minTime, maxTime, requestedFor, reasonFilter, statusFilter, resultFilter, tagFilters, properties, top, continuationToken, maxBuildsPerDefinition, deletedFilter, queryOrder, branchName, buildIds, repositoryId, repositoryType);
90
+ const builds = await buildApi.getBuilds(project, definitions, queues, buildNumber, minTime, maxTime, requestedFor, reasonFilter, statusFilter, resultFilter, tagFilters, properties, top, continuationToken, maxBuildsPerDefinition, deletedFilter, safeEnumConvert(BuildQueryOrder, queryOrder), branchName, buildIds, repositoryId, repositoryType);
79
91
  return {
80
92
  content: [{ type: "text", text: JSON.stringify(builds, null, 2) }],
81
93
  };
@@ -119,16 +131,96 @@ function configureBuildTools(server, tokenProvider, connectionProvider) {
119
131
  content: [{ type: "text", text: JSON.stringify(changes, null, 2) }],
120
132
  };
121
133
  });
122
- server.tool(BUILD_TOOLS.run_build, "Triggers a new build for a specified definition.", {
134
+ server.tool(BUILD_TOOLS.pipelines_get_run, "Gets a run for a particular pipeline.", {
123
135
  project: z.string().describe("Project ID or name to run the build in"),
124
- definitionId: z.number().describe("ID of the build definition to run"),
125
- sourceBranch: z.string().optional().describe("Source branch to run the build from. If not provided, the default branch will be used."),
126
- }, async ({ project, definitionId, sourceBranch }) => {
136
+ pipelineId: z.number().describe("ID of the pipeline to run"),
137
+ runId: z.number().describe("ID of the run to get"),
138
+ }, async ({ project, pipelineId, runId }) => {
127
139
  const connection = await connectionProvider();
128
- const buildApi = await connection.getBuildApi();
129
- const build = await buildApi.queueBuild({ definition: { id: definitionId }, sourceBranch }, project);
140
+ const pipelinesApi = await connection.getPipelinesApi();
141
+ const pipelineRun = await pipelinesApi.getRun(project, pipelineId, runId);
130
142
  return {
131
- content: [{ type: "text", text: JSON.stringify(build, null, 2) }],
143
+ content: [{ type: "text", text: JSON.stringify(pipelineRun, null, 2) }],
144
+ };
145
+ });
146
+ server.tool(BUILD_TOOLS.pipelines_list_runs, "Gets top 10000 runs for a particular pipeline.", {
147
+ project: z.string().describe("Project ID or name to run the build in"),
148
+ pipelineId: z.number().describe("ID of the pipeline to run"),
149
+ }, async ({ project, pipelineId }) => {
150
+ const connection = await connectionProvider();
151
+ const pipelinesApi = await connection.getPipelinesApi();
152
+ const pipelineRuns = await pipelinesApi.listRuns(project, pipelineId);
153
+ return {
154
+ content: [{ type: "text", text: JSON.stringify(pipelineRuns, null, 2) }],
155
+ };
156
+ });
157
+ const variableSchema = z.object({
158
+ value: z.string().optional(),
159
+ isSecret: z.boolean().optional(),
160
+ });
161
+ const resourcesSchema = z.object({
162
+ builds: z
163
+ .record(z.string().describe("Name of the build resource."), z.object({
164
+ version: z.string().optional().describe("Version of the build resource."),
165
+ }))
166
+ .optional(),
167
+ containers: z
168
+ .record(z.string().describe("Name of the container resource."), z.object({
169
+ version: z.string().optional().describe("Version of the container resource."),
170
+ }))
171
+ .optional(),
172
+ packages: z
173
+ .record(z.string().describe("Name of the package resource."), z.object({
174
+ version: z.string().optional().describe("Version of the package resource."),
175
+ }))
176
+ .optional(),
177
+ pipelines: z.record(z.string().describe("Name of the pipeline resource."), z.object({
178
+ runId: z.number().describe("Id of the source pipeline run that triggered or is referenced by this pipeline run."),
179
+ version: z.string().optional().describe("Version of the source pipeline run."),
180
+ })),
181
+ repositories: z
182
+ .record(z.string().describe("Name of the repository resource."), z.object({
183
+ refName: z.string().describe("Reference name, e.g., refs/heads/main."),
184
+ token: z.string().optional(),
185
+ tokenType: z.string().optional(),
186
+ version: z.string().optional().describe("Version of the repository resource, git commit sha."),
187
+ }))
188
+ .optional(),
189
+ });
190
+ server.tool(BUILD_TOOLS.pipelines_run_pipeline, "Starts a new run of a pipeline.", {
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
+ pipelineVersion: z.number().optional().describe("Version of the pipeline to run. If not provided, the latest version will be used."),
194
+ previewRun: z.boolean().optional().describe("If true, returns the final YAML document after parsing templates without creating a new run."),
195
+ resources: resourcesSchema.optional().describe("A dictionary of resources to pass to the pipeline."),
196
+ stagesToSkip: z.array(z.string()).optional().describe("A list of stages to skip."),
197
+ templateParameters: z.record(z.string(), z.string()).optional().describe("Custom build parameters as key-value pairs"),
198
+ variables: z.record(z.string(), variableSchema).optional().describe("A dictionary of variables to pass to the pipeline."),
199
+ yamlOverride: z.string().optional().describe("YAML override for the pipeline run."),
200
+ }, async ({ project, pipelineId, pipelineVersion, previewRun, resources, stagesToSkip, templateParameters, variables, yamlOverride }) => {
201
+ if (!previewRun && yamlOverride) {
202
+ throw new Error("Parameter 'yamlOverride' can only be specified together with parameter 'previewRun'.");
203
+ }
204
+ const connection = await connectionProvider();
205
+ const pipelinesApi = await connection.getPipelinesApi();
206
+ const runRequest = {
207
+ previewRun: previewRun,
208
+ resources: {
209
+ ...resources,
210
+ },
211
+ stagesToSkip: stagesToSkip,
212
+ templateParameters: templateParameters,
213
+ variables: variables,
214
+ yamlOverride: yamlOverride,
215
+ };
216
+ const pipelineRun = await pipelinesApi.runPipeline(runRequest, project, pipelineId, pipelineVersion);
217
+ const queuedBuild = { id: pipelineRun.id };
218
+ const buildId = queuedBuild.id;
219
+ if (buildId === undefined) {
220
+ throw new Error("Failed to get build ID from pipeline run");
221
+ }
222
+ return {
223
+ content: [{ type: "text", text: JSON.stringify(pipelineRun, null, 2) }],
132
224
  };
133
225
  });
134
226
  server.tool(BUILD_TOOLS.get_status, "Fetches the status of a specific build.", {
@@ -142,5 +234,38 @@ function configureBuildTools(server, tokenProvider, connectionProvider) {
142
234
  content: [{ type: "text", text: JSON.stringify(build, null, 2) }],
143
235
  };
144
236
  });
237
+ server.tool(BUILD_TOOLS.update_build_stage, "Updates the stage of a specific build.", {
238
+ project: z.string().describe("Project ID or name to update the build stage for"),
239
+ buildId: z.number().describe("ID of the build to update"),
240
+ stageName: z.string().describe("Name of the stage to update"),
241
+ status: z.enum(getEnumKeys(StageUpdateType)).describe("New status for the stage"),
242
+ forceRetryAllJobs: z.boolean().default(false).describe("Whether to force retry all jobs in the stage."),
243
+ }, async ({ project, buildId, stageName, status, forceRetryAllJobs }) => {
244
+ const connection = await connectionProvider();
245
+ const orgUrl = connection.serverUrl;
246
+ const endpoint = `${orgUrl}/${project}/_apis/build/builds/${buildId}/stages/${stageName}?api-version=${apiVersion}`;
247
+ const token = await tokenProvider();
248
+ const body = {
249
+ forceRetryAllJobs: forceRetryAllJobs,
250
+ state: safeEnumConvert(StageUpdateType, status),
251
+ };
252
+ const response = await fetch(endpoint, {
253
+ method: "PATCH",
254
+ headers: {
255
+ "Content-Type": "application/json",
256
+ "Authorization": `Bearer ${token}`,
257
+ "User-Agent": userAgentProvider(),
258
+ },
259
+ body: JSON.stringify(body),
260
+ });
261
+ if (!response.ok) {
262
+ const errorText = await response.text();
263
+ throw new Error(`Failed to update build stage: ${response.status} ${errorText}`);
264
+ }
265
+ const updatedBuild = await response.text();
266
+ return {
267
+ content: [{ type: "text", text: JSON.stringify(updatedBuild, null, 2) }],
268
+ };
269
+ });
145
270
  }
146
271
  export { BUILD_TOOLS, configureBuildTools };
@@ -1,36 +1,95 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
3
  import { z } from "zod";
4
+ import { searchIdentities } from "./auth.js";
4
5
  const CORE_TOOLS = {
5
6
  list_project_teams: "core_list_project_teams",
6
7
  list_projects: "core_list_projects",
8
+ get_identity_ids: "core_get_identity_ids",
7
9
  };
8
- function configureCoreTools(server, tokenProvider, connectionProvider) {
10
+ function filterProjectsByName(projects, projectNameFilter) {
11
+ const lowerCaseFilter = projectNameFilter.toLowerCase();
12
+ return projects.filter((project) => project.name?.toLowerCase().includes(lowerCaseFilter));
13
+ }
14
+ function configureCoreTools(server, tokenProvider, connectionProvider, userAgentProvider) {
9
15
  server.tool(CORE_TOOLS.list_project_teams, "Retrieve a list of teams for the specified Azure DevOps project.", {
10
16
  project: z.string().describe("The name or ID of the Azure DevOps project."),
11
17
  mine: z.boolean().optional().describe("If true, only return teams that the authenticated user is a member of."),
12
18
  top: z.number().optional().describe("The maximum number of teams to return. Defaults to 100."),
13
19
  skip: z.number().optional().describe("The number of teams to skip for pagination. Defaults to 0."),
14
20
  }, async ({ project, mine, top, skip }) => {
15
- const connection = await connectionProvider();
16
- const coreApi = await connection.getCoreApi();
17
- const teams = await coreApi.getTeams(project, mine, top, skip, false);
18
- return {
19
- content: [{ type: "text", text: JSON.stringify(teams, null, 2) }],
20
- };
21
+ try {
22
+ const connection = await connectionProvider();
23
+ const coreApi = await connection.getCoreApi();
24
+ const teams = await coreApi.getTeams(project, mine, top, skip, false);
25
+ if (!teams) {
26
+ return { content: [{ type: "text", text: "No teams found" }], isError: true };
27
+ }
28
+ return {
29
+ content: [{ type: "text", text: JSON.stringify(teams, null, 2) }],
30
+ };
31
+ }
32
+ catch (error) {
33
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
34
+ return {
35
+ content: [{ type: "text", text: `Error fetching project teams: ${errorMessage}` }],
36
+ isError: true,
37
+ };
38
+ }
21
39
  });
22
40
  server.tool(CORE_TOOLS.list_projects, "Retrieve a list of projects in your Azure DevOps organization.", {
23
41
  stateFilter: z.enum(["all", "wellFormed", "createPending", "deleted"]).default("wellFormed").describe("Filter projects by their state. Defaults to 'wellFormed'."),
24
42
  top: z.number().optional().describe("The maximum number of projects to return. Defaults to 100."),
25
43
  skip: z.number().optional().describe("The number of projects to skip for pagination. Defaults to 0."),
26
44
  continuationToken: z.number().optional().describe("Continuation token for pagination. Used to fetch the next set of results if available."),
27
- }, async ({ stateFilter, top, skip, continuationToken }) => {
28
- const connection = await connectionProvider();
29
- const coreApi = await connection.getCoreApi();
30
- const projects = await coreApi.getProjects(stateFilter, top, skip, continuationToken, false);
31
- return {
32
- content: [{ type: "text", text: JSON.stringify(projects, null, 2) }],
33
- };
45
+ projectNameFilter: z.string().optional().describe("Filter projects by name. Supports partial matches."),
46
+ }, async ({ stateFilter, top, skip, continuationToken, projectNameFilter }) => {
47
+ try {
48
+ const connection = await connectionProvider();
49
+ const coreApi = await connection.getCoreApi();
50
+ const projects = await coreApi.getProjects(stateFilter, top, skip, continuationToken, false);
51
+ if (!projects) {
52
+ return { content: [{ type: "text", text: "No projects found" }], isError: true };
53
+ }
54
+ const filteredProject = projectNameFilter ? filterProjectsByName(projects, projectNameFilter) : projects;
55
+ return {
56
+ content: [{ type: "text", text: JSON.stringify(filteredProject, null, 2) }],
57
+ };
58
+ }
59
+ catch (error) {
60
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
61
+ return {
62
+ content: [{ type: "text", text: `Error fetching projects: ${errorMessage}` }],
63
+ isError: true,
64
+ };
65
+ }
66
+ });
67
+ server.tool(CORE_TOOLS.get_identity_ids, "Retrieve Azure DevOps identity IDs for a provided search filter.", {
68
+ searchFilter: z.string().describe("Search filter (unique namme, display name, email) to retrieve identity IDs for."),
69
+ }, async ({ searchFilter }) => {
70
+ try {
71
+ const identities = await searchIdentities(searchFilter, tokenProvider, connectionProvider, userAgentProvider);
72
+ if (!identities || identities.value?.length === 0) {
73
+ return { content: [{ type: "text", text: "No identities found" }], isError: true };
74
+ }
75
+ const identitiesTrimmed = identities.value?.map((identity) => {
76
+ return {
77
+ id: identity.id,
78
+ displayName: identity.providerDisplayName,
79
+ descriptor: identity.descriptor,
80
+ };
81
+ });
82
+ return {
83
+ content: [{ type: "text", text: JSON.stringify(identitiesTrimmed, null, 2) }],
84
+ };
85
+ }
86
+ catch (error) {
87
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
88
+ return {
89
+ content: [{ type: "text", text: `Error fetching identities: ${errorMessage}` }],
90
+ isError: true,
91
+ };
92
+ }
34
93
  });
35
94
  }
36
95
  export { CORE_TOOLS, configureCoreTools };
@@ -1,21 +1,28 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT License.
3
- import { ReleaseDefinitionExpands, ReleaseDefinitionQueryOrder, ReleaseExpands, ReleaseStatus, ReleaseQueryOrder, } from "azure-devops-node-api/interfaces/ReleaseInterfaces.js";
3
+ import { ReleaseDefinitionExpands, ReleaseDefinitionQueryOrder, ReleaseExpands, ReleaseStatus, ReleaseQueryOrder } from "azure-devops-node-api/interfaces/ReleaseInterfaces.js";
4
4
  import { z } from "zod";
5
+ import { getEnumKeys, safeEnumConvert } from "../utils.js";
5
6
  const RELEASE_TOOLS = {
6
7
  get_release_definitions: "release_get_definitions",
7
8
  get_releases: "release_get_releases",
8
9
  };
9
- function configureReleaseTools(server, tokenProvider, connectionProvider) {
10
+ function configureReleaseTools(server, _, connectionProvider) {
10
11
  server.tool(RELEASE_TOOLS.get_release_definitions, "Retrieves list of release definitions for a given project.", {
11
12
  project: z.string().describe("Project ID or name to get release definitions for"),
12
13
  searchText: z.string().optional().describe("Search text to filter release definitions"),
13
- expand: z.nativeEnum(ReleaseDefinitionExpands).default(ReleaseDefinitionExpands.None).describe("Expand options for release definitions"),
14
+ expand: z
15
+ .enum(getEnumKeys(ReleaseDefinitionExpands))
16
+ .default("None")
17
+ .describe("Expand options for release definitions"),
14
18
  artifactType: z.string().optional().describe("Filter by artifact type"),
15
19
  artifactSourceId: z.string().optional().describe("Filter by artifact source ID"),
16
20
  top: z.number().optional().describe("Number of results to return (for pagination)"),
17
21
  continuationToken: z.string().optional().describe("Continuation token for pagination"),
18
- queryOrder: z.nativeEnum(ReleaseDefinitionQueryOrder).default(ReleaseDefinitionQueryOrder.NameAscending).describe("Order of the results"),
22
+ queryOrder: z
23
+ .enum(getEnumKeys(ReleaseDefinitionQueryOrder))
24
+ .default("NameAscending")
25
+ .describe("Order of the results"),
19
26
  path: z.string().optional().describe("Path to filter release definitions"),
20
27
  isExactNameMatch: z.boolean().optional().default(false).describe("Whether to match the exact name of the release definition. Default is false."),
21
28
  tagFilter: z.array(z.string()).optional().describe("Filter by tags associated with the release definitions"),
@@ -26,11 +33,9 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
26
33
  }, async ({ project, searchText, expand, artifactType, artifactSourceId, top, continuationToken, queryOrder, path, isExactNameMatch, tagFilter, propertyFilters, definitionIdFilter, isDeleted, searchTextContainsFolderName, }) => {
27
34
  const connection = await connectionProvider();
28
35
  const releaseApi = await connection.getReleaseApi();
29
- const releaseDefinitions = await releaseApi.getReleaseDefinitions(project, searchText, expand, artifactType, artifactSourceId, top, continuationToken, queryOrder, path, isExactNameMatch, tagFilter, propertyFilters, definitionIdFilter, isDeleted, searchTextContainsFolderName);
36
+ const releaseDefinitions = await releaseApi.getReleaseDefinitions(project, searchText, safeEnumConvert(ReleaseDefinitionExpands, expand), artifactType, artifactSourceId, top, continuationToken, safeEnumConvert(ReleaseDefinitionQueryOrder, queryOrder), path, isExactNameMatch, tagFilter, propertyFilters, definitionIdFilter, isDeleted, searchTextContainsFolderName);
30
37
  return {
31
- content: [
32
- { type: "text", text: JSON.stringify(releaseDefinitions, null, 2) },
33
- ],
38
+ content: [{ type: "text", text: JSON.stringify(releaseDefinitions, null, 2) }],
34
39
  };
35
40
  });
36
41
  server.tool(RELEASE_TOOLS.get_releases, "Retrieves a list of releases for a given project.", {
@@ -39,18 +44,38 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
39
44
  definitionEnvironmentId: z.number().optional().describe("ID of the definition environment to filter releases"),
40
45
  searchText: z.string().optional().describe("Search text to filter releases"),
41
46
  createdBy: z.string().optional().describe("User ID or name who created the release"),
42
- statusFilter: z.nativeEnum(ReleaseStatus).optional().default(ReleaseStatus.Active).describe("Status of the releases to filter (default: Active)"),
47
+ statusFilter: z
48
+ .enum(getEnumKeys(ReleaseStatus))
49
+ .optional()
50
+ .default("Active")
51
+ .describe("Status of the releases to filter (default: Active)"),
43
52
  environmentStatusFilter: z.number().optional().describe("Environment status to filter releases"),
44
- minCreatedTime: z.date().optional().default(() => {
53
+ minCreatedTime: z.coerce
54
+ .date()
55
+ .optional()
56
+ .default(() => {
45
57
  const sevenDaysAgo = new Date();
46
58
  sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
47
59
  return sevenDaysAgo;
48
- }).describe("Minimum created time for releases (default: 7 days ago)"),
49
- maxCreatedTime: z.date().optional().default(() => new Date()).describe("Maximum created time for releases (default: now)"),
50
- queryOrder: z.nativeEnum(ReleaseQueryOrder).optional().default(ReleaseQueryOrder.Ascending).describe("Order in which to return releases (default: Ascending)"),
60
+ })
61
+ .describe("Minimum created time for releases (default: 7 days ago)"),
62
+ maxCreatedTime: z.coerce
63
+ .date()
64
+ .optional()
65
+ .default(() => new Date())
66
+ .describe("Maximum created time for releases (default: now)"),
67
+ queryOrder: z
68
+ .enum(getEnumKeys(ReleaseQueryOrder))
69
+ .optional()
70
+ .default("Ascending")
71
+ .describe("Order in which to return releases (default: Ascending)"),
51
72
  top: z.number().optional().describe("Number of releases to return"),
52
73
  continuationToken: z.number().optional().describe("Continuation token for pagination"),
53
- expand: z.nativeEnum(ReleaseExpands).optional().default(ReleaseExpands.None).describe("Expand options for releases"),
74
+ expand: z
75
+ .enum(getEnumKeys(ReleaseExpands))
76
+ .optional()
77
+ .default("None")
78
+ .describe("Expand options for releases"),
54
79
  artifactTypeId: z.string().optional().describe("Filter releases by artifact type ID"),
55
80
  sourceId: z.string().optional().describe("Filter releases by artifact source ID"),
56
81
  artifactVersionId: z.string().optional().describe("Filter releases by artifact version ID"),
@@ -63,7 +88,7 @@ function configureReleaseTools(server, tokenProvider, connectionProvider) {
63
88
  }, async ({ project, definitionId, definitionEnvironmentId, searchText, createdBy, statusFilter, environmentStatusFilter, minCreatedTime, maxCreatedTime, queryOrder, top, continuationToken, expand, artifactTypeId, sourceId, artifactVersionId, sourceBranchFilter, isDeleted, tagFilter, propertyFilters, releaseIdFilter, path, }) => {
64
89
  const connection = await connectionProvider();
65
90
  const releaseApi = await connection.getReleaseApi();
66
- const releases = await releaseApi.getReleases(project, definitionId, definitionEnvironmentId, searchText, createdBy, statusFilter, environmentStatusFilter, minCreatedTime, maxCreatedTime, queryOrder, top, continuationToken, expand, artifactTypeId, sourceId, artifactVersionId, sourceBranchFilter, isDeleted, tagFilter, propertyFilters, releaseIdFilter, path);
91
+ const releases = await releaseApi.getReleases(project, definitionId, definitionEnvironmentId, searchText, createdBy, safeEnumConvert(ReleaseStatus, statusFilter), environmentStatusFilter, minCreatedTime, maxCreatedTime, safeEnumConvert(ReleaseQueryOrder, queryOrder), top, continuationToken, safeEnumConvert(ReleaseExpands, expand), artifactTypeId, sourceId, artifactVersionId, sourceBranchFilter, isDeleted, tagFilter, propertyFilters, releaseIdFilter, path);
67
92
  return {
68
93
  content: [{ type: "text", text: JSON.stringify(releases, null, 2) }],
69
94
  };