@mcp-consultant-tools/azure-devops 24.0.0-beta.2 → 25.0.0-beta.4

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/build/index.js CHANGED
@@ -38,6 +38,21 @@ export function registerAzureDevOpsTools(server, azuredevopsService) {
38
38
  enableWorkItemWrite: process.env.AZUREDEVOPS_ENABLE_WORK_ITEM_WRITE === "true",
39
39
  enableWorkItemDelete: process.env.AZUREDEVOPS_ENABLE_WORK_ITEM_DELETE === "true",
40
40
  enableWikiWrite: process.env.AZUREDEVOPS_ENABLE_WIKI_WRITE === "true",
41
+ // DevOps Admin Tools - Three-tier permission model
42
+ // Tier 1: Read-only (master switch)
43
+ showDevOpsAdminReadonly: process.env.AZUREDEVOPS_SHOW_DEVOPS_ADMIN_READONLY === "true",
44
+ // Tier 2: Upsert (create + update)
45
+ enablePipelineUpsert: process.env.AZUREDEVOPS_ENABLE_PIPELINE_UPSERT === "true",
46
+ enableServiceConnUpsert: process.env.AZUREDEVOPS_ENABLE_SERVICE_CONN_UPSERT === "true",
47
+ enableVariableGroupUpsert: process.env.AZUREDEVOPS_ENABLE_VARIABLE_GROUP_UPSERT === "true",
48
+ enableAgentPoolUpsert: process.env.AZUREDEVOPS_ENABLE_AGENT_POOL_UPSERT === "true",
49
+ enableEnvironmentUpsert: process.env.AZUREDEVOPS_ENABLE_ENVIRONMENT_UPSERT === "true",
50
+ // Tier 3: Delete/Disable (destructive operations)
51
+ enablePipelineDelete: process.env.AZUREDEVOPS_ENABLE_PIPELINE_DELETE === "true",
52
+ enableServiceConnDelete: process.env.AZUREDEVOPS_ENABLE_SERVICE_CONN_DELETE === "true",
53
+ enableVariableGroupDelete: process.env.AZUREDEVOPS_ENABLE_VARIABLE_GROUP_DELETE === "true",
54
+ enableAgentPoolDisable: process.env.AZUREDEVOPS_ENABLE_AGENT_POOL_DISABLE === "true",
55
+ enableEnvironmentDelete: process.env.AZUREDEVOPS_ENABLE_ENVIRONMENT_DELETE === "true",
41
56
  };
42
57
  service = new AzureDevOpsService(config);
43
58
  console.error("Azure DevOps service initialized");
@@ -778,7 +793,837 @@ export function registerAzureDevOpsTools(server, azuredevopsService) {
778
793
  };
779
794
  }
780
795
  });
781
- console.error("azure-devops tools registered: 15 tools, 4 prompts");
796
+ // ========================================
797
+ // DEVOPS ADMIN TOOLS - CONDITIONAL REGISTRATION
798
+ // Tools are only registered when corresponding flags are enabled
799
+ // ========================================
800
+ // Helper to check if admin readonly is enabled (master switch)
801
+ const showAdminReadonly = process.env.AZUREDEVOPS_SHOW_DEVOPS_ADMIN_READONLY === "true";
802
+ // Tier 2: Upsert flags
803
+ const enablePipelineUpsert = process.env.AZUREDEVOPS_ENABLE_PIPELINE_UPSERT === "true";
804
+ const enableServiceConnUpsert = process.env.AZUREDEVOPS_ENABLE_SERVICE_CONN_UPSERT === "true";
805
+ const enableVariableGroupUpsert = process.env.AZUREDEVOPS_ENABLE_VARIABLE_GROUP_UPSERT === "true";
806
+ const enableAgentPoolUpsert = process.env.AZUREDEVOPS_ENABLE_AGENT_POOL_UPSERT === "true";
807
+ const enableEnvironmentUpsert = process.env.AZUREDEVOPS_ENABLE_ENVIRONMENT_UPSERT === "true";
808
+ // Tier 3: Delete/Disable flags
809
+ const enablePipelineDelete = process.env.AZUREDEVOPS_ENABLE_PIPELINE_DELETE === "true";
810
+ const enableServiceConnDelete = process.env.AZUREDEVOPS_ENABLE_SERVICE_CONN_DELETE === "true";
811
+ const enableVariableGroupDelete = process.env.AZUREDEVOPS_ENABLE_VARIABLE_GROUP_DELETE === "true";
812
+ const enableAgentPoolDisable = process.env.AZUREDEVOPS_ENABLE_AGENT_POOL_DISABLE === "true";
813
+ const enableEnvironmentDelete = process.env.AZUREDEVOPS_ENABLE_ENVIRONMENT_DELETE === "true";
814
+ // Track registered tools count
815
+ let adminToolsCount = 0;
816
+ // ========================================
817
+ // PIPELINE READ-ONLY TOOLS (Tier 1)
818
+ // ========================================
819
+ if (showAdminReadonly) {
820
+ server.tool("list-pipelines", "List all YAML pipeline definitions in an Azure DevOps project. Returns pipeline ID, name, path, repository, and YAML file path.", {
821
+ project: z.string().describe("The project name"),
822
+ }, async ({ project }) => {
823
+ try {
824
+ const service = getAzureDevOpsService();
825
+ const result = await service.listPipelineDefinitions(project);
826
+ return { content: [{ type: "text", text: `Pipeline definitions in project '${project}':\n\n${JSON.stringify(result, null, 2)}` }] };
827
+ }
828
+ catch (error) {
829
+ console.error("Error listing pipelines:", error);
830
+ return { content: [{ type: "text", text: `Failed to list pipelines: ${error.message}` }] };
831
+ }
832
+ });
833
+ adminToolsCount++;
834
+ server.tool("get-pipeline-definition", "Get detailed YAML pipeline definition including triggers, variables (secrets masked), queue settings, and repository configuration.", {
835
+ project: z.string().describe("The project name"),
836
+ definitionId: z.number().describe("The pipeline definition ID"),
837
+ }, async ({ project, definitionId }) => {
838
+ try {
839
+ const service = getAzureDevOpsService();
840
+ const result = await service.getPipelineDefinition(project, definitionId);
841
+ return { content: [{ type: "text", text: `Pipeline definition ${definitionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
842
+ }
843
+ catch (error) {
844
+ console.error("Error getting pipeline definition:", error);
845
+ return { content: [{ type: "text", text: `Failed to get pipeline definition: ${error.message}` }] };
846
+ }
847
+ });
848
+ adminToolsCount++;
849
+ server.tool("get-pipeline-yaml", "Get the YAML content for a pipeline definition. Returns the raw azure-pipelines.yml content.", {
850
+ project: z.string().describe("The project name"),
851
+ definitionId: z.number().describe("The pipeline definition ID"),
852
+ }, async ({ project, definitionId }) => {
853
+ try {
854
+ const service = getAzureDevOpsService();
855
+ const result = await service.getPipelineYaml(project, definitionId);
856
+ return { content: [{ type: "text", text: `Pipeline YAML for definition ${definitionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
857
+ }
858
+ catch (error) {
859
+ console.error("Error getting pipeline YAML:", error);
860
+ return { content: [{ type: "text", text: `Failed to get pipeline YAML: ${error.message}` }] };
861
+ }
862
+ });
863
+ adminToolsCount++;
864
+ server.tool("list-pipeline-runs", "List recent pipeline runs for a definition. Returns build ID, status, result, branch, timestamps, and who triggered it.", {
865
+ project: z.string().describe("The project name"),
866
+ definitionId: z.number().describe("The pipeline definition ID"),
867
+ top: z.number().optional().describe("Maximum number of results (default: 10)"),
868
+ }, async ({ project, definitionId, top }) => {
869
+ try {
870
+ const service = getAzureDevOpsService();
871
+ const result = await service.listPipelineRuns(project, definitionId, top || 10);
872
+ return { content: [{ type: "text", text: `Recent runs for pipeline ${definitionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
873
+ }
874
+ catch (error) {
875
+ console.error("Error listing pipeline runs:", error);
876
+ return { content: [{ type: "text", text: `Failed to list pipeline runs: ${error.message}` }] };
877
+ }
878
+ });
879
+ adminToolsCount++;
880
+ server.tool("get-build-status", "Get build status and details. Use detail='summary' for basic status, 'timeline' to include step breakdown, or 'full' for logs.", {
881
+ project: z.string().describe("The project name"),
882
+ buildId: z.number().describe("The build ID"),
883
+ detail: z.enum(["summary", "timeline", "full"]).optional().describe("Level of detail: 'summary' (default), 'timeline' (include steps), or 'full' (include logs)"),
884
+ }, async ({ project, buildId, detail }) => {
885
+ try {
886
+ const service = getAzureDevOpsService();
887
+ const result = await service.getBuildStatus(project, buildId, detail || 'summary');
888
+ return { content: [{ type: "text", text: `Build ${buildId} status:\n\n${JSON.stringify(result, null, 2)}` }] };
889
+ }
890
+ catch (error) {
891
+ console.error("Error getting build status:", error);
892
+ return { content: [{ type: "text", text: `Failed to get build status: ${error.message}` }] };
893
+ }
894
+ });
895
+ adminToolsCount++;
896
+ server.tool("get-build-timeline", "Get step-by-step breakdown of a build. Shows stages, jobs, and tasks with timing, status, and error/warning counts. NOTE: For complex builds, consider using a sub-agent to analyze the timeline data.", {
897
+ project: z.string().describe("The project name"),
898
+ buildId: z.number().describe("The build ID"),
899
+ }, async ({ project, buildId }) => {
900
+ try {
901
+ const service = getAzureDevOpsService();
902
+ const result = await service.getBuildTimeline(project, buildId);
903
+ return { content: [{ type: "text", text: `Build ${buildId} timeline:\n\n${JSON.stringify(result, null, 2)}` }] };
904
+ }
905
+ catch (error) {
906
+ console.error("Error getting build timeline:", error);
907
+ return { content: [{ type: "text", text: `Failed to get build timeline: ${error.message}` }] };
908
+ }
909
+ });
910
+ adminToolsCount++;
911
+ server.tool("get-build-logs", "Get build logs. Without logId, returns list of available logs with line counts. With logId, returns that log's content. NOTE: For verbose logs, consider using a sub-agent to analyze the content.", {
912
+ project: z.string().describe("The project name"),
913
+ buildId: z.number().describe("The build ID"),
914
+ logId: z.number().optional().describe("Optional specific log ID to retrieve content"),
915
+ }, async ({ project, buildId, logId }) => {
916
+ try {
917
+ const service = getAzureDevOpsService();
918
+ const result = await service.getBuildLogs(project, buildId, logId);
919
+ return { content: [{ type: "text", text: `Build ${buildId} logs:\n\n${JSON.stringify(result, null, 2)}` }] };
920
+ }
921
+ catch (error) {
922
+ console.error("Error getting build logs:", error);
923
+ return { content: [{ type: "text", text: `Failed to get build logs: ${error.message}` }] };
924
+ }
925
+ });
926
+ adminToolsCount++;
927
+ // ========================================
928
+ // SERVICE CONNECTION READ-ONLY TOOLS (Tier 1)
929
+ // ========================================
930
+ server.tool("list-service-connections", "List all service connections in a project. Shows connection type, URL, authorization scheme, and sharing status. Credentials are masked.", {
931
+ project: z.string().describe("The project name"),
932
+ }, async ({ project }) => {
933
+ try {
934
+ const service = getAzureDevOpsService();
935
+ const result = await service.listServiceConnections(project);
936
+ return { content: [{ type: "text", text: `Service connections in project '${project}':\n\n${JSON.stringify(result, null, 2)}` }] };
937
+ }
938
+ catch (error) {
939
+ console.error("Error listing service connections:", error);
940
+ return { content: [{ type: "text", text: `Failed to list service connections: ${error.message}` }] };
941
+ }
942
+ });
943
+ adminToolsCount++;
944
+ server.tool("get-service-connection", "Get detailed service connection configuration. Returns type, URL, authorization scheme, data fields, and project references. Secrets are masked.", {
945
+ project: z.string().describe("The project name"),
946
+ connectionId: z.string().describe("The service connection ID (GUID)"),
947
+ }, async ({ project, connectionId }) => {
948
+ try {
949
+ const service = getAzureDevOpsService();
950
+ const result = await service.getServiceConnection(project, connectionId);
951
+ return { content: [{ type: "text", text: `Service connection ${connectionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
952
+ }
953
+ catch (error) {
954
+ console.error("Error getting service connection:", error);
955
+ return { content: [{ type: "text", text: `Failed to get service connection: ${error.message}` }] };
956
+ }
957
+ });
958
+ adminToolsCount++;
959
+ server.tool("get-service-connection-types", "Get all available service connection types (Azure, AWS, Docker, GitHub, etc.) with their authentication schemes and configuration options.", {}, async () => {
960
+ try {
961
+ const service = getAzureDevOpsService();
962
+ const result = await service.getServiceConnectionTypes();
963
+ return { content: [{ type: "text", text: `Available service connection types:\n\n${JSON.stringify(result, null, 2)}` }] };
964
+ }
965
+ catch (error) {
966
+ console.error("Error getting service connection types:", error);
967
+ return { content: [{ type: "text", text: `Failed to get service connection types: ${error.message}` }] };
968
+ }
969
+ });
970
+ adminToolsCount++;
971
+ // ========================================
972
+ // AGENT POOL READ-ONLY TOOLS (Tier 1)
973
+ // ========================================
974
+ server.tool("list-agent-pools", "List all agent pools in the organization. Shows pool type (automation/deployment), size, hosted status, and auto-provision settings.", {
975
+ poolType: z.enum(["automation", "deployment"]).optional().describe("Optional filter: 'automation' or 'deployment'"),
976
+ }, async ({ poolType }) => {
977
+ try {
978
+ const service = getAzureDevOpsService();
979
+ const result = await service.listAgentPools(poolType);
980
+ return { content: [{ type: "text", text: `Agent pools:\n\n${JSON.stringify(result, null, 2)}` }] };
981
+ }
982
+ catch (error) {
983
+ console.error("Error listing agent pools:", error);
984
+ return { content: [{ type: "text", text: `Failed to list agent pools: ${error.message}` }] };
985
+ }
986
+ });
987
+ adminToolsCount++;
988
+ server.tool("get-agent-pool", "Get detailed agent pool configuration including auto-provision, auto-update, auto-size settings, and owner information.", {
989
+ poolId: z.number().describe("The agent pool ID"),
990
+ }, async ({ poolId }) => {
991
+ try {
992
+ const service = getAzureDevOpsService();
993
+ const result = await service.getAgentPool(poolId);
994
+ return { content: [{ type: "text", text: `Agent pool ${poolId}:\n\n${JSON.stringify(result, null, 2)}` }] };
995
+ }
996
+ catch (error) {
997
+ console.error("Error getting agent pool:", error);
998
+ return { content: [{ type: "text", text: `Failed to get agent pool: ${error.message}` }] };
999
+ }
1000
+ });
1001
+ adminToolsCount++;
1002
+ server.tool("list-agents", "List all agents in a pool. Shows agent name, version, OS, enabled status, and current status (online/offline).", {
1003
+ poolId: z.number().describe("The agent pool ID"),
1004
+ includeCapabilities: z.boolean().optional().describe("Include system and user capabilities (default: false)"),
1005
+ }, async ({ poolId, includeCapabilities }) => {
1006
+ try {
1007
+ const service = getAzureDevOpsService();
1008
+ const result = await service.listAgents(poolId, includeCapabilities || false);
1009
+ return { content: [{ type: "text", text: `Agents in pool ${poolId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1010
+ }
1011
+ catch (error) {
1012
+ console.error("Error listing agents:", error);
1013
+ return { content: [{ type: "text", text: `Failed to list agents: ${error.message}` }] };
1014
+ }
1015
+ });
1016
+ adminToolsCount++;
1017
+ server.tool("get-agent", "Get detailed agent information including capabilities, current assignment, and last completed request.", {
1018
+ poolId: z.number().describe("The agent pool ID"),
1019
+ agentId: z.number().describe("The agent ID"),
1020
+ }, async ({ poolId, agentId }) => {
1021
+ try {
1022
+ const service = getAzureDevOpsService();
1023
+ const result = await service.getAgent(poolId, agentId);
1024
+ return { content: [{ type: "text", text: `Agent ${agentId} in pool ${poolId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1025
+ }
1026
+ catch (error) {
1027
+ console.error("Error getting agent:", error);
1028
+ return { content: [{ type: "text", text: `Failed to get agent: ${error.message}` }] };
1029
+ }
1030
+ });
1031
+ adminToolsCount++;
1032
+ // ========================================
1033
+ // ENVIRONMENT READ-ONLY TOOLS (Tier 1)
1034
+ // ========================================
1035
+ server.tool("list-environments", "List all deployment environments in a project. Shows environment name, description, and modification info.", {
1036
+ project: z.string().describe("The project name"),
1037
+ }, async ({ project }) => {
1038
+ try {
1039
+ const service = getAzureDevOpsService();
1040
+ const result = await service.listEnvironments(project);
1041
+ return { content: [{ type: "text", text: `Environments in project '${project}':\n\n${JSON.stringify(result, null, 2)}` }] };
1042
+ }
1043
+ catch (error) {
1044
+ console.error("Error listing environments:", error);
1045
+ return { content: [{ type: "text", text: `Failed to list environments: ${error.message}` }] };
1046
+ }
1047
+ });
1048
+ adminToolsCount++;
1049
+ server.tool("get-environment", "Get detailed environment configuration including associated resources (Kubernetes, VMs, etc.).", {
1050
+ project: z.string().describe("The project name"),
1051
+ environmentId: z.number().describe("The environment ID"),
1052
+ }, async ({ project, environmentId }) => {
1053
+ try {
1054
+ const service = getAzureDevOpsService();
1055
+ const result = await service.getEnvironment(project, environmentId);
1056
+ return { content: [{ type: "text", text: `Environment ${environmentId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1057
+ }
1058
+ catch (error) {
1059
+ console.error("Error getting environment:", error);
1060
+ return { content: [{ type: "text", text: `Failed to get environment: ${error.message}` }] };
1061
+ }
1062
+ });
1063
+ adminToolsCount++;
1064
+ server.tool("get-environment-deployments", "Get deployment history for an environment. Shows pipeline, owner, start/finish times, and result.", {
1065
+ project: z.string().describe("The project name"),
1066
+ environmentId: z.number().describe("The environment ID"),
1067
+ top: z.number().optional().describe("Maximum number of results (default: 10)"),
1068
+ }, async ({ project, environmentId, top }) => {
1069
+ try {
1070
+ const service = getAzureDevOpsService();
1071
+ const result = await service.getEnvironmentDeployments(project, environmentId, top || 10);
1072
+ return { content: [{ type: "text", text: `Deployments to environment ${environmentId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1073
+ }
1074
+ catch (error) {
1075
+ console.error("Error getting environment deployments:", error);
1076
+ return { content: [{ type: "text", text: `Failed to get environment deployments: ${error.message}` }] };
1077
+ }
1078
+ });
1079
+ adminToolsCount++;
1080
+ server.tool("get-environment-checks", "Get all checks (approvals, business hours, branch control, etc.) configured for an environment. Essential for understanding pipeline approval requirements.", {
1081
+ project: z.string().describe("The project name"),
1082
+ environmentId: z.number().describe("The environment ID"),
1083
+ }, async ({ project, environmentId }) => {
1084
+ try {
1085
+ const service = getAzureDevOpsService();
1086
+ const result = await service.getEnvironmentChecks(project, environmentId);
1087
+ return { content: [{ type: "text", text: `Checks for environment ${environmentId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1088
+ }
1089
+ catch (error) {
1090
+ console.error("Error getting environment checks:", error);
1091
+ return { content: [{ type: "text", text: `Failed to get environment checks: ${error.message}` }] };
1092
+ }
1093
+ });
1094
+ adminToolsCount++;
1095
+ }
1096
+ // ========================================
1097
+ // PIPELINE UPSERT TOOLS (Tier 2)
1098
+ // ========================================
1099
+ if (enablePipelineUpsert) {
1100
+ server.tool("admin-create-pipeline", "Create a new YAML pipeline definition. Requires repository ID and path to YAML file. (requires AZUREDEVOPS_ENABLE_PIPELINE_UPSERT=true)", {
1101
+ project: z.string().describe("The project name"),
1102
+ name: z.string().describe("Pipeline name"),
1103
+ repositoryId: z.string().describe("Repository ID (GUID from get-repos or Azure DevOps UI)"),
1104
+ yamlPath: z.string().describe("Path to YAML file in repository (e.g., 'azure-pipelines.yml' or 'pipelines/build.yml')"),
1105
+ folder: z.string().optional().describe("Optional folder path (default: root)"),
1106
+ }, async ({ project, name, repositoryId, yamlPath, folder }) => {
1107
+ try {
1108
+ const service = getAzureDevOpsService();
1109
+ const result = await service.createPipelineDefinition(project, name, repositoryId, yamlPath, folder);
1110
+ return { content: [{ type: "text", text: `Created pipeline '${name}':\n\n${JSON.stringify(result, null, 2)}` }] };
1111
+ }
1112
+ catch (error) {
1113
+ console.error("Error creating pipeline:", error);
1114
+ return { content: [{ type: "text", text: `Failed to create pipeline: ${error.message}` }] };
1115
+ }
1116
+ });
1117
+ adminToolsCount++;
1118
+ server.tool("admin-update-pipeline", "Update a pipeline definition (name, path, queue status, triggers, or variables). (requires AZUREDEVOPS_ENABLE_PIPELINE_UPSERT=true)", {
1119
+ project: z.string().describe("The project name"),
1120
+ definitionId: z.number().describe("The pipeline definition ID"),
1121
+ name: z.string().optional().describe("New pipeline name"),
1122
+ path: z.string().optional().describe("New folder path"),
1123
+ queueStatus: z.enum(["enabled", "disabled", "paused"]).optional().describe("Queue status"),
1124
+ variables: z.record(z.object({
1125
+ value: z.string(),
1126
+ isSecret: z.boolean().optional(),
1127
+ allowOverride: z.boolean().optional()
1128
+ })).optional().describe("Pipeline variables to set"),
1129
+ }, async ({ project, definitionId, name, path, queueStatus, variables }) => {
1130
+ try {
1131
+ const service = getAzureDevOpsService();
1132
+ const updates = {};
1133
+ if (name)
1134
+ updates.name = name;
1135
+ if (path)
1136
+ updates.path = path;
1137
+ if (queueStatus)
1138
+ updates.queueStatus = queueStatus;
1139
+ if (variables)
1140
+ updates.variables = variables;
1141
+ const result = await service.updatePipelineDefinition(project, definitionId, updates);
1142
+ return { content: [{ type: "text", text: `Updated pipeline ${definitionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1143
+ }
1144
+ catch (error) {
1145
+ console.error("Error updating pipeline:", error);
1146
+ return { content: [{ type: "text", text: `Failed to update pipeline: ${error.message}` }] };
1147
+ }
1148
+ });
1149
+ adminToolsCount++;
1150
+ server.tool("admin-rename-pipeline", "Rename a pipeline definition. (requires AZUREDEVOPS_ENABLE_PIPELINE_UPSERT=true)", {
1151
+ project: z.string().describe("The project name"),
1152
+ definitionId: z.number().describe("The pipeline definition ID"),
1153
+ newName: z.string().describe("The new pipeline name"),
1154
+ }, async ({ project, definitionId, newName }) => {
1155
+ try {
1156
+ const service = getAzureDevOpsService();
1157
+ const result = await service.renamePipelineDefinition(project, definitionId, newName);
1158
+ return { content: [{ type: "text", text: `Renamed pipeline ${definitionId} to '${newName}':\n\n${JSON.stringify(result, null, 2)}` }] };
1159
+ }
1160
+ catch (error) {
1161
+ console.error("Error renaming pipeline:", error);
1162
+ return { content: [{ type: "text", text: `Failed to rename pipeline: ${error.message}` }] };
1163
+ }
1164
+ });
1165
+ adminToolsCount++;
1166
+ server.tool("admin-queue-build", "Queue a new pipeline build/run. Optionally specify branch, variables, or template parameters. (requires AZUREDEVOPS_ENABLE_PIPELINE_UPSERT=true)", {
1167
+ project: z.string().describe("The project name"),
1168
+ definitionId: z.number().describe("The pipeline definition ID"),
1169
+ branch: z.string().optional().describe("Source branch (e.g., 'refs/heads/main')"),
1170
+ variables: z.record(z.string()).optional().describe("Runtime variables to pass"),
1171
+ parameters: z.record(z.any()).optional().describe("Template parameters for YAML pipelines"),
1172
+ }, async ({ project, definitionId, branch, variables, parameters }) => {
1173
+ try {
1174
+ const service = getAzureDevOpsService();
1175
+ const result = await service.queueBuild(project, definitionId, branch, variables, parameters);
1176
+ return { content: [{ type: "text", text: `Queued build for pipeline ${definitionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1177
+ }
1178
+ catch (error) {
1179
+ console.error("Error queuing build:", error);
1180
+ return { content: [{ type: "text", text: `Failed to queue build: ${error.message}` }] };
1181
+ }
1182
+ });
1183
+ adminToolsCount++;
1184
+ server.tool("admin-cancel-build", "Cancel a running build. The build will finish its current task before stopping. (requires AZUREDEVOPS_ENABLE_PIPELINE_UPSERT=true)", {
1185
+ project: z.string().describe("The project name"),
1186
+ buildId: z.number().describe("The build ID to cancel"),
1187
+ }, async ({ project, buildId }) => {
1188
+ try {
1189
+ const service = getAzureDevOpsService();
1190
+ const result = await service.cancelBuild(project, buildId);
1191
+ return { content: [{ type: "text", text: `Cancelled build ${buildId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1192
+ }
1193
+ catch (error) {
1194
+ console.error("Error cancelling build:", error);
1195
+ return { content: [{ type: "text", text: `Failed to cancel build: ${error.message}` }] };
1196
+ }
1197
+ });
1198
+ adminToolsCount++;
1199
+ server.tool("admin-retry-build", "Retry a failed build with the same configuration. Creates a new build with same definition, branch, and parameters. (requires AZUREDEVOPS_ENABLE_PIPELINE_UPSERT=true)", {
1200
+ project: z.string().describe("The project name"),
1201
+ buildId: z.number().describe("The failed build ID to retry"),
1202
+ }, async ({ project, buildId }) => {
1203
+ try {
1204
+ const service = getAzureDevOpsService();
1205
+ const result = await service.retryBuild(project, buildId);
1206
+ return { content: [{ type: "text", text: `Retried build ${buildId}, new build:\n\n${JSON.stringify(result, null, 2)}` }] };
1207
+ }
1208
+ catch (error) {
1209
+ console.error("Error retrying build:", error);
1210
+ return { content: [{ type: "text", text: `Failed to retry build: ${error.message}` }] };
1211
+ }
1212
+ });
1213
+ adminToolsCount++;
1214
+ }
1215
+ // ========================================
1216
+ // PIPELINE DELETE TOOLS (Tier 3)
1217
+ // ========================================
1218
+ if (enablePipelineDelete) {
1219
+ server.tool("admin-delete-pipeline", "⚠️ DESTRUCTIVE: Delete a pipeline definition. This cannot be undone. (requires AZUREDEVOPS_ENABLE_PIPELINE_DELETE=true)", {
1220
+ project: z.string().describe("The project name"),
1221
+ definitionId: z.number().describe("The pipeline definition ID to delete"),
1222
+ }, async ({ project, definitionId }) => {
1223
+ try {
1224
+ const service = getAzureDevOpsService();
1225
+ const result = await service.deletePipelineDefinition(project, definitionId);
1226
+ return { content: [{ type: "text", text: `Deleted pipeline ${definitionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1227
+ }
1228
+ catch (error) {
1229
+ console.error("Error deleting pipeline:", error);
1230
+ return { content: [{ type: "text", text: `Failed to delete pipeline: ${error.message}` }] };
1231
+ }
1232
+ });
1233
+ adminToolsCount++;
1234
+ }
1235
+ // ========================================
1236
+ // SERVICE CONNECTION UPSERT TOOLS (Tier 2)
1237
+ // ========================================
1238
+ if (enableServiceConnUpsert) {
1239
+ server.tool("admin-create-service-connection", "Create a new service connection. Use get-service-connection-types to see available types and auth schemes. (requires AZUREDEVOPS_ENABLE_SERVICE_CONN_UPSERT=true)", {
1240
+ project: z.string().describe("The project name"),
1241
+ name: z.string().describe("Connection name"),
1242
+ type: z.string().describe("Connection type (e.g., 'azurerm', 'github', 'docker', 'aws')"),
1243
+ url: z.string().optional().describe("Service URL (required for some types)"),
1244
+ description: z.string().optional().describe("Connection description"),
1245
+ authorization: z.object({
1246
+ scheme: z.string().describe("Auth scheme (e.g., 'ServicePrincipal', 'PersonalAccessToken')"),
1247
+ parameters: z.record(z.string()).optional().describe("Auth parameters (credentials)")
1248
+ }).optional().describe("Authorization configuration"),
1249
+ data: z.record(z.string()).optional().describe("Type-specific configuration data"),
1250
+ }, async ({ project, name, type, url, description, authorization, data }) => {
1251
+ try {
1252
+ const service = getAzureDevOpsService();
1253
+ const result = await service.createServiceConnection(project, name, type, { url, description, authorization, data });
1254
+ return { content: [{ type: "text", text: `Created service connection '${name}':\n\n${JSON.stringify(result, null, 2)}` }] };
1255
+ }
1256
+ catch (error) {
1257
+ console.error("Error creating service connection:", error);
1258
+ return { content: [{ type: "text", text: `Failed to create service connection: ${error.message}` }] };
1259
+ }
1260
+ });
1261
+ adminToolsCount++;
1262
+ server.tool("admin-update-service-connection", "Update a service connection's metadata (name, description, URL, data). Cannot update credentials for security. (requires AZUREDEVOPS_ENABLE_SERVICE_CONN_UPSERT=true)", {
1263
+ project: z.string().describe("The project name"),
1264
+ connectionId: z.string().describe("The service connection ID (GUID)"),
1265
+ name: z.string().optional().describe("New connection name"),
1266
+ description: z.string().optional().describe("New description"),
1267
+ url: z.string().optional().describe("New service URL"),
1268
+ data: z.record(z.string()).optional().describe("Updated data fields"),
1269
+ }, async ({ project, connectionId, name, description, url, data }) => {
1270
+ try {
1271
+ const service = getAzureDevOpsService();
1272
+ const updates = {};
1273
+ if (name)
1274
+ updates.name = name;
1275
+ if (description)
1276
+ updates.description = description;
1277
+ if (url)
1278
+ updates.url = url;
1279
+ if (data)
1280
+ updates.data = data;
1281
+ const result = await service.updateServiceConnection(project, connectionId, updates);
1282
+ return { content: [{ type: "text", text: `Updated service connection ${connectionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1283
+ }
1284
+ catch (error) {
1285
+ console.error("Error updating service connection:", error);
1286
+ return { content: [{ type: "text", text: `Failed to update service connection: ${error.message}` }] };
1287
+ }
1288
+ });
1289
+ adminToolsCount++;
1290
+ server.tool("admin-share-service-connection", "Share a service connection with other projects. (requires AZUREDEVOPS_ENABLE_SERVICE_CONN_UPSERT=true)", {
1291
+ connectionId: z.string().describe("The service connection ID (GUID)"),
1292
+ projectIds: z.array(z.string()).describe("Array of project IDs to share with"),
1293
+ }, async ({ connectionId, projectIds }) => {
1294
+ try {
1295
+ const service = getAzureDevOpsService();
1296
+ const result = await service.shareServiceConnection(connectionId, projectIds);
1297
+ return { content: [{ type: "text", text: `Shared service connection ${connectionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1298
+ }
1299
+ catch (error) {
1300
+ console.error("Error sharing service connection:", error);
1301
+ return { content: [{ type: "text", text: `Failed to share service connection: ${error.message}` }] };
1302
+ }
1303
+ });
1304
+ adminToolsCount++;
1305
+ }
1306
+ // ========================================
1307
+ // SERVICE CONNECTION DELETE TOOLS (Tier 3)
1308
+ // ========================================
1309
+ if (enableServiceConnDelete) {
1310
+ server.tool("admin-delete-service-connection", "⚠️ DESTRUCTIVE: Delete a service connection. Pipelines using this connection will fail. (requires AZUREDEVOPS_ENABLE_SERVICE_CONN_DELETE=true)", {
1311
+ project: z.string().describe("The project name"),
1312
+ connectionId: z.string().describe("The service connection ID (GUID) to delete"),
1313
+ }, async ({ project, connectionId }) => {
1314
+ try {
1315
+ const service = getAzureDevOpsService();
1316
+ const result = await service.deleteServiceConnection(project, connectionId);
1317
+ return { content: [{ type: "text", text: `Deleted service connection ${connectionId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1318
+ }
1319
+ catch (error) {
1320
+ console.error("Error deleting service connection:", error);
1321
+ return { content: [{ type: "text", text: `Failed to delete service connection: ${error.message}` }] };
1322
+ }
1323
+ });
1324
+ adminToolsCount++;
1325
+ }
1326
+ // ========================================
1327
+ // VARIABLE GROUP UPSERT TOOLS (Tier 2)
1328
+ // ========================================
1329
+ if (enableVariableGroupUpsert) {
1330
+ server.tool("admin-create-variable-group", "Create a new variable group. Variables can be marked as secret. (requires AZUREDEVOPS_ENABLE_VARIABLE_GROUP_UPSERT=true)", {
1331
+ project: z.string().describe("The project name"),
1332
+ name: z.string().describe("Variable group name"),
1333
+ description: z.string().optional().describe("Variable group description"),
1334
+ variables: z.record(z.object({
1335
+ value: z.string(),
1336
+ isSecret: z.boolean().optional()
1337
+ })).optional().describe("Initial variables to set"),
1338
+ }, async ({ project, name, description, variables }) => {
1339
+ try {
1340
+ const service = getAzureDevOpsService();
1341
+ const result = await service.createVariableGroup(project, name, description, variables);
1342
+ return { content: [{ type: "text", text: `Created variable group '${name}':\n\n${JSON.stringify(result, null, 2)}` }] };
1343
+ }
1344
+ catch (error) {
1345
+ console.error("Error creating variable group:", error);
1346
+ return { content: [{ type: "text", text: `Failed to create variable group: ${error.message}` }] };
1347
+ }
1348
+ });
1349
+ adminToolsCount++;
1350
+ server.tool("admin-update-variable-group", "Update a variable group's name or description. Use admin-set-variable to modify variables. (requires AZUREDEVOPS_ENABLE_VARIABLE_GROUP_UPSERT=true)", {
1351
+ project: z.string().describe("The project name"),
1352
+ groupId: z.number().describe("The variable group ID"),
1353
+ name: z.string().optional().describe("New name"),
1354
+ description: z.string().optional().describe("New description"),
1355
+ }, async ({ project, groupId, name, description }) => {
1356
+ try {
1357
+ const service = getAzureDevOpsService();
1358
+ const updates = {};
1359
+ if (name)
1360
+ updates.name = name;
1361
+ if (description)
1362
+ updates.description = description;
1363
+ const result = await service.updateVariableGroupMetadata(project, groupId, updates);
1364
+ return { content: [{ type: "text", text: `Updated variable group ${groupId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1365
+ }
1366
+ catch (error) {
1367
+ console.error("Error updating variable group:", error);
1368
+ return { content: [{ type: "text", text: `Failed to update variable group: ${error.message}` }] };
1369
+ }
1370
+ });
1371
+ adminToolsCount++;
1372
+ server.tool("admin-set-variable", "Set or update a variable in a variable group. Creates the variable if it doesn't exist. (requires AZUREDEVOPS_ENABLE_VARIABLE_GROUP_UPSERT=true)", {
1373
+ project: z.string().describe("The project name"),
1374
+ groupId: z.number().describe("The variable group ID"),
1375
+ variableName: z.string().describe("Variable name"),
1376
+ value: z.string().describe("Variable value"),
1377
+ isSecret: z.boolean().optional().describe("Mark as secret (default: false)"),
1378
+ }, async ({ project, groupId, variableName, value, isSecret }) => {
1379
+ try {
1380
+ const service = getAzureDevOpsService();
1381
+ const result = await service.setVariable(project, groupId, variableName, value, isSecret || false);
1382
+ return { content: [{ type: "text", text: `Set variable '${variableName}' in group ${groupId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1383
+ }
1384
+ catch (error) {
1385
+ console.error("Error setting variable:", error);
1386
+ return { content: [{ type: "text", text: `Failed to set variable: ${error.message}` }] };
1387
+ }
1388
+ });
1389
+ adminToolsCount++;
1390
+ }
1391
+ // ========================================
1392
+ // VARIABLE GROUP DELETE TOOLS (Tier 3)
1393
+ // ========================================
1394
+ if (enableVariableGroupDelete) {
1395
+ server.tool("admin-remove-variable", "Remove a variable from a variable group. (requires AZUREDEVOPS_ENABLE_VARIABLE_GROUP_DELETE=true)", {
1396
+ project: z.string().describe("The project name"),
1397
+ groupId: z.number().describe("The variable group ID"),
1398
+ variableName: z.string().describe("Variable name to remove"),
1399
+ }, async ({ project, groupId, variableName }) => {
1400
+ try {
1401
+ const service = getAzureDevOpsService();
1402
+ const result = await service.removeVariable(project, groupId, variableName);
1403
+ return { content: [{ type: "text", text: `Removed variable '${variableName}' from group ${groupId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1404
+ }
1405
+ catch (error) {
1406
+ console.error("Error removing variable:", error);
1407
+ return { content: [{ type: "text", text: `Failed to remove variable: ${error.message}` }] };
1408
+ }
1409
+ });
1410
+ adminToolsCount++;
1411
+ server.tool("admin-delete-variable-group", "⚠️ DESTRUCTIVE: Delete a variable group. Pipelines using this group will fail. (requires AZUREDEVOPS_ENABLE_VARIABLE_GROUP_DELETE=true)", {
1412
+ project: z.string().describe("The project name"),
1413
+ groupId: z.number().describe("The variable group ID to delete"),
1414
+ }, async ({ project, groupId }) => {
1415
+ try {
1416
+ const service = getAzureDevOpsService();
1417
+ const result = await service.deleteVariableGroup(project, groupId);
1418
+ return { content: [{ type: "text", text: `Deleted variable group ${groupId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1419
+ }
1420
+ catch (error) {
1421
+ console.error("Error deleting variable group:", error);
1422
+ return { content: [{ type: "text", text: `Failed to delete variable group: ${error.message}` }] };
1423
+ }
1424
+ });
1425
+ adminToolsCount++;
1426
+ }
1427
+ // ========================================
1428
+ // AGENT POOL UPSERT TOOLS (Tier 2)
1429
+ // ========================================
1430
+ if (enableAgentPoolUpsert) {
1431
+ server.tool("admin-update-agent-pool", "Update agent pool settings (auto-provision, auto-update, auto-size, target size). (requires AZUREDEVOPS_ENABLE_AGENT_POOL_UPSERT=true)", {
1432
+ poolId: z.number().describe("The agent pool ID"),
1433
+ autoProvision: z.boolean().optional().describe("Auto-provision pool to new projects"),
1434
+ autoUpdate: z.boolean().optional().describe("Auto-update agents"),
1435
+ autoSize: z.boolean().optional().describe("Auto-size pool based on demand"),
1436
+ targetSize: z.number().optional().describe("Target pool size for auto-scaling"),
1437
+ }, async ({ poolId, autoProvision, autoUpdate, autoSize, targetSize }) => {
1438
+ try {
1439
+ const service = getAzureDevOpsService();
1440
+ const updates = {};
1441
+ if (autoProvision !== undefined)
1442
+ updates.autoProvision = autoProvision;
1443
+ if (autoUpdate !== undefined)
1444
+ updates.autoUpdate = autoUpdate;
1445
+ if (autoSize !== undefined)
1446
+ updates.autoSize = autoSize;
1447
+ if (targetSize !== undefined)
1448
+ updates.targetSize = targetSize;
1449
+ const result = await service.updateAgentPool(poolId, updates);
1450
+ return { content: [{ type: "text", text: `Updated agent pool ${poolId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1451
+ }
1452
+ catch (error) {
1453
+ console.error("Error updating agent pool:", error);
1454
+ return { content: [{ type: "text", text: `Failed to update agent pool: ${error.message}` }] };
1455
+ }
1456
+ });
1457
+ adminToolsCount++;
1458
+ server.tool("admin-enable-agent", "Enable a disabled agent to accept new jobs. (requires AZUREDEVOPS_ENABLE_AGENT_POOL_UPSERT=true)", {
1459
+ poolId: z.number().describe("The agent pool ID"),
1460
+ agentId: z.number().describe("The agent ID"),
1461
+ }, async ({ poolId, agentId }) => {
1462
+ try {
1463
+ const service = getAzureDevOpsService();
1464
+ const result = await service.enableAgent(poolId, agentId);
1465
+ return { content: [{ type: "text", text: `Enabled agent ${agentId} in pool ${poolId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1466
+ }
1467
+ catch (error) {
1468
+ console.error("Error enabling agent:", error);
1469
+ return { content: [{ type: "text", text: `Failed to enable agent: ${error.message}` }] };
1470
+ }
1471
+ });
1472
+ adminToolsCount++;
1473
+ }
1474
+ // ========================================
1475
+ // AGENT POOL DISABLE TOOLS (Tier 3)
1476
+ // ========================================
1477
+ if (enableAgentPoolDisable) {
1478
+ server.tool("admin-disable-agent", "⚠️ Disable an agent. It will complete current job then stop accepting new jobs. (requires AZUREDEVOPS_ENABLE_AGENT_POOL_DISABLE=true)", {
1479
+ poolId: z.number().describe("The agent pool ID"),
1480
+ agentId: z.number().describe("The agent ID to disable"),
1481
+ }, async ({ poolId, agentId }) => {
1482
+ try {
1483
+ const service = getAzureDevOpsService();
1484
+ const result = await service.disableAgent(poolId, agentId);
1485
+ return { content: [{ type: "text", text: `Disabled agent ${agentId} in pool ${poolId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1486
+ }
1487
+ catch (error) {
1488
+ console.error("Error disabling agent:", error);
1489
+ return { content: [{ type: "text", text: `Failed to disable agent: ${error.message}` }] };
1490
+ }
1491
+ });
1492
+ adminToolsCount++;
1493
+ }
1494
+ // ========================================
1495
+ // ENVIRONMENT UPSERT TOOLS (Tier 2)
1496
+ // ========================================
1497
+ if (enableEnvironmentUpsert) {
1498
+ server.tool("admin-create-environment", "Create a new deployment environment. (requires AZUREDEVOPS_ENABLE_ENVIRONMENT_UPSERT=true)", {
1499
+ project: z.string().describe("The project name"),
1500
+ name: z.string().describe("Environment name"),
1501
+ description: z.string().optional().describe("Environment description"),
1502
+ }, async ({ project, name, description }) => {
1503
+ try {
1504
+ const service = getAzureDevOpsService();
1505
+ const result = await service.createEnvironment(project, name, description);
1506
+ return { content: [{ type: "text", text: `Created environment '${name}':\n\n${JSON.stringify(result, null, 2)}` }] };
1507
+ }
1508
+ catch (error) {
1509
+ console.error("Error creating environment:", error);
1510
+ return { content: [{ type: "text", text: `Failed to create environment: ${error.message}` }] };
1511
+ }
1512
+ });
1513
+ adminToolsCount++;
1514
+ server.tool("admin-update-environment", "Update an environment's name or description. (requires AZUREDEVOPS_ENABLE_ENVIRONMENT_UPSERT=true)", {
1515
+ project: z.string().describe("The project name"),
1516
+ environmentId: z.number().describe("The environment ID"),
1517
+ name: z.string().optional().describe("New name"),
1518
+ description: z.string().optional().describe("New description"),
1519
+ }, async ({ project, environmentId, name, description }) => {
1520
+ try {
1521
+ const service = getAzureDevOpsService();
1522
+ const updates = {};
1523
+ if (name)
1524
+ updates.name = name;
1525
+ if (description)
1526
+ updates.description = description;
1527
+ const result = await service.updateEnvironment(project, environmentId, updates);
1528
+ return { content: [{ type: "text", text: `Updated environment ${environmentId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1529
+ }
1530
+ catch (error) {
1531
+ console.error("Error updating environment:", error);
1532
+ return { content: [{ type: "text", text: `Failed to update environment: ${error.message}` }] };
1533
+ }
1534
+ });
1535
+ adminToolsCount++;
1536
+ server.tool("admin-create-environment-check", "Add an approval, business hours, branch control, or other check to an environment. Supports all Azure DevOps check types. (requires AZUREDEVOPS_ENABLE_ENVIRONMENT_UPSERT=true)", {
1537
+ project: z.string().describe("The project name"),
1538
+ environmentId: z.number().describe("The environment ID"),
1539
+ checkType: z.enum([
1540
+ "Approval",
1541
+ "BusinessHours",
1542
+ "BranchControl",
1543
+ "InvokeRESTAPI",
1544
+ "InvokeAzureFunction",
1545
+ "ExclusiveLock",
1546
+ "RequiredTemplate"
1547
+ ]).describe("Check type"),
1548
+ settings: z.any().describe("Check-specific settings. For Approval: {approvers: [{id: 'user-guid'}], minRequiredApprovers: 1, instructions: '...'}. For BusinessHours: {businessHours: {startTime: '09:00', endTime: '17:00', timeZoneId: 'UTC'}}. For BranchControl: {allowedBranches: ['refs/heads/main']}"),
1549
+ timeout: z.number().optional().describe("Timeout in minutes (default: 43200 = 30 days)"),
1550
+ }, async ({ project, environmentId, checkType, settings, timeout }) => {
1551
+ try {
1552
+ const service = getAzureDevOpsService();
1553
+ const configuration = { ...settings };
1554
+ if (timeout)
1555
+ configuration.timeout = timeout;
1556
+ const result = await service.addEnvironmentCheck(project, environmentId, checkType, configuration);
1557
+ return { content: [{ type: "text", text: `Created ${checkType} check on environment ${environmentId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1558
+ }
1559
+ catch (error) {
1560
+ console.error("Error creating environment check:", error);
1561
+ return { content: [{ type: "text", text: `Failed to create environment check: ${error.message}` }] };
1562
+ }
1563
+ });
1564
+ adminToolsCount++;
1565
+ server.tool("admin-update-environment-check", "Update an existing environment check's settings or timeout. (requires AZUREDEVOPS_ENABLE_ENVIRONMENT_UPSERT=true)", {
1566
+ project: z.string().describe("The project name"),
1567
+ checkId: z.number().describe("The check configuration ID to update"),
1568
+ settings: z.any().optional().describe("Updated check-specific settings"),
1569
+ timeout: z.number().optional().describe("Updated timeout in minutes"),
1570
+ }, async ({ project, checkId, settings, timeout }) => {
1571
+ try {
1572
+ const service = getAzureDevOpsService();
1573
+ const updates = {};
1574
+ if (settings !== undefined)
1575
+ updates.settings = settings;
1576
+ if (timeout !== undefined)
1577
+ updates.timeout = timeout;
1578
+ const result = await service.updateEnvironmentCheck(project, checkId, updates);
1579
+ return { content: [{ type: "text", text: `Updated check ${checkId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1580
+ }
1581
+ catch (error) {
1582
+ console.error("Error updating environment check:", error);
1583
+ return { content: [{ type: "text", text: `Failed to update environment check: ${error.message}` }] };
1584
+ }
1585
+ });
1586
+ adminToolsCount++;
1587
+ }
1588
+ // ========================================
1589
+ // ENVIRONMENT DELETE TOOLS (Tier 3)
1590
+ // ========================================
1591
+ if (enableEnvironmentDelete) {
1592
+ server.tool("admin-delete-environment", "⚠️ DESTRUCTIVE: Delete a deployment environment. Pipelines targeting this environment will fail. (requires AZUREDEVOPS_ENABLE_ENVIRONMENT_DELETE=true)", {
1593
+ project: z.string().describe("The project name"),
1594
+ environmentId: z.number().describe("The environment ID to delete"),
1595
+ }, async ({ project, environmentId }) => {
1596
+ try {
1597
+ const service = getAzureDevOpsService();
1598
+ const result = await service.deleteEnvironment(project, environmentId);
1599
+ return { content: [{ type: "text", text: `Deleted environment ${environmentId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1600
+ }
1601
+ catch (error) {
1602
+ console.error("Error deleting environment:", error);
1603
+ return { content: [{ type: "text", text: `Failed to delete environment: ${error.message}` }] };
1604
+ }
1605
+ });
1606
+ adminToolsCount++;
1607
+ server.tool("admin-delete-environment-check", "Delete an approval, business hours, branch control, or other check from an environment. (requires AZUREDEVOPS_ENABLE_ENVIRONMENT_DELETE=true)", {
1608
+ project: z.string().describe("The project name"),
1609
+ checkId: z.number().describe("The check configuration ID to delete (get from get-environment-checks)"),
1610
+ }, async ({ project, checkId }) => {
1611
+ try {
1612
+ const service = getAzureDevOpsService();
1613
+ const result = await service.removeEnvironmentCheck(project, checkId);
1614
+ return { content: [{ type: "text", text: `Deleted check ${checkId}:\n\n${JSON.stringify(result, null, 2)}` }] };
1615
+ }
1616
+ catch (error) {
1617
+ console.error("Error deleting environment check:", error);
1618
+ return { content: [{ type: "text", text: `Failed to delete environment check: ${error.message}` }] };
1619
+ }
1620
+ });
1621
+ adminToolsCount++;
1622
+ }
1623
+ // Log registration summary
1624
+ const baseToolsCount = 15;
1625
+ const totalToolsCount = baseToolsCount + adminToolsCount;
1626
+ console.error(`azure-devops tools registered: ${totalToolsCount} tools (${baseToolsCount} base + ${adminToolsCount} admin), 4 prompts`);
782
1627
  }
783
1628
  /**
784
1629
  * Export service class for direct usage