From 1997c5699626cfe8af5ee7662bfa9abdb5fae254 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 17:27:49 +0000 Subject: [PATCH 1/3] Initial plan From 553d9749aa0386ed5b2692f09c6cd3c87a43a733 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 17:42:55 +0000 Subject: [PATCH 2/3] Add build_bicep and build_bicepparam tools to MCP server Agent-Logs-Url: https://github.com/Azure/bicep/sessions/1df630fb-e75b-4bfa-b696-f58824b9827f Co-authored-by: anthony-c-martin <38542602+anthony-c-martin@users.noreply.github.com> --- .../BicepCompilerTools.cs | 97 ++++++++- .../BicepCompilerToolsTests.cs | 79 ++++++++ .../Files/ServerTests/tools.json | 184 ++++++++++++++++++ 3 files changed, 350 insertions(+), 10 deletions(-) diff --git a/src/Bicep.McpServer.Core/BicepCompilerTools.cs b/src/Bicep.McpServer.Core/BicepCompilerTools.cs index b086dce9ecb..9b2dd1a4ce0 100644 --- a/src/Bicep.McpServer.Core/BicepCompilerTools.cs +++ b/src/Bicep.McpServer.Core/BicepCompilerTools.cs @@ -4,8 +4,10 @@ using System.Collections.Immutable; using System.ComponentModel; using Bicep.Core; +using Bicep.Core.Diagnostics; using Bicep.Core.Extensions; using Bicep.Core.PrettyPrintV2; +using Bicep.Core.SourceGraph; using Bicep.IO.Abstraction; using ModelContextProtocol.Server; @@ -39,6 +41,24 @@ public record GetFileReferencesResult( [Description("The list of files being referenced by the specified Bicep or Bicep parameters file")] ImmutableArray FileUris); + public record BuildBicepResult( + [Description("Whether the compilation succeeded without errors")] + bool Success, + [Description("The compiled ARM template JSON, or null if compilation failed")] + string? Template, + [Description("The list of diagnostics from compilation")] + ImmutableArray Diagnostics); + + public record BuildBicepparamResult( + [Description("Whether the compilation succeeded without errors")] + bool Success, + [Description("The compiled parameters JSON, or null if compilation failed")] + string? Parameters, + [Description("The compiled ARM template JSON, or null if not applicable or compilation failed")] + string? Template, + [Description("The list of diagnostics from compilation")] + ImmutableArray Diagnostics); + [McpServerTool(Title = "Get Bicep File Diagnostics", Destructive = false, Idempotent = true, OpenWorld = true, ReadOnly = true, UseStructuredContent = true)] [Description(""" Analyzes a Bicep file (.bicep) or Bicep parameters file (.bicepparam) and returns all compilation diagnostics including errors, warnings, and informational messages. @@ -63,16 +83,61 @@ public async Task> GetBicepFileDiagnostics( var compilation = await compiler.CreateCompilation(fileUri); - return [.. compilation - .GetAllDiagnosticsByBicepFile() - .SelectMany(kvp => kvp.Value.Select(x => new DiagnosticDefinition( - kvp.Key.FileHandle.Uri.ToUri(), - x.Code, - x.Level.ToString(), - x.Message, - x.Uri, - x.Span.Position, - x.Span.Length)))]; + return GetDiagnostics(compilation.GetAllDiagnosticsByBicepFile()); + } + + [McpServerTool(Title = "Build Bicep", Destructive = false, Idempotent = true, OpenWorld = true, ReadOnly = true, UseStructuredContent = true)] + [Description(""" + Compiles a Bicep file (.bicep) to an ARM template JSON string and returns the result along with any diagnostics. + + Use this tool to: + - Compile Bicep source code to ARM template JSON + - Check for compilation errors before deployment + - Obtain the ARM template output for inspection or deployment + + The compiled ARM template JSON is returned along with any compilation diagnostics (errors, warnings, and informational messages). + The file path must be absolute. If compilation fails due to errors, the Template field will be null. + """)] + public async Task BuildBicep( + [Description("The path to the .bicep file")] string filePath) + { + var fileUri = IOUri.FromFilePath(filePath); + if (!fileUri.HasBicepExtension()) + { + throw new ArgumentException("The specified file must have a .bicep extension.", nameof(filePath)); + } + + var compilation = await compiler.CreateCompilation(fileUri); + var result = compilation.Emitter.Template(); + + return new BuildBicepResult(result.Success, result.Template, GetDiagnostics(result.Diagnostics)); + } + + [McpServerTool(Title = "Build Bicep Parameters", Destructive = false, Idempotent = true, OpenWorld = true, ReadOnly = true, UseStructuredContent = true)] + [Description(""" + Compiles a Bicep parameters file (.bicepparam) to a parameters JSON string and returns the result along with any diagnostics. + + Use this tool to: + - Compile Bicep parameters source code to ARM parameters JSON + - Check for compilation errors before deployment + - Obtain the parameters JSON output for inspection or deployment + + The compiled parameters JSON and the associated ARM template JSON are returned along with any compilation diagnostics (errors, warnings, and informational messages). + The file path must be absolute. If compilation fails due to errors, the Parameters field will be null. + """)] + public async Task BuildBicepparam( + [Description("The path to the .bicepparam file")] string filePath) + { + var fileUri = IOUri.FromFilePath(filePath); + if (!fileUri.HasBicepParamExtension()) + { + throw new ArgumentException("The specified file must have a .bicepparam extension.", nameof(filePath)); + } + + var compilation = await compiler.CreateCompilation(fileUri); + var result = compilation.Emitter.Parameters(); + + return new BuildBicepparamResult(result.Success, result.Parameters, result.Template?.Template, GetDiagnostics(result.Diagnostics)); } [McpServerTool(Title = "Format Bicep File", Destructive = false, Idempotent = true, OpenWorld = true, ReadOnly = true, UseStructuredContent = true)] @@ -148,4 +213,16 @@ public async Task GetFileReferences( return new( [.. fileUris.Select(x => x.ToUri()).OrderBy(x => x.ToString())]); } + + private static ImmutableArray GetDiagnostics(ImmutableDictionary> diagnosticsByFile) + { + return [.. diagnosticsByFile.SelectMany(kvp => kvp.Value.Select(x => new DiagnosticDefinition( + kvp.Key.FileHandle.Uri.ToUri(), + x.Code, + x.Level.ToString(), + x.Message, + x.Uri, + x.Span.Position, + x.Span.Length)))]; + } } diff --git a/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs b/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs index b0a282712fa..2af3b4794d7 100644 --- a/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs +++ b/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs @@ -88,4 +88,83 @@ param location string "location.txt", ]); } + + [TestMethod] + public async Task BuildBicep_returns_compiled_template() + { + var bicepFilePath = FileHelper.SaveResultFile(TestContext, "main.bicep", """ + param location string = 'westus' + output loc string = location + """); + + var response = await tools.BuildBicep(bicepFilePath); + + response.Success.Should().BeTrue(); + response.Template.Should().NotBeNullOrEmpty(); + response.Template.Should().Contain("\"$schema\""); + response.Diagnostics.Should().NotContain(x => x.Level == "Error"); + } + + [TestMethod] + public async Task BuildBicep_returns_diagnostics_on_error() + { + var bicepFilePath = FileHelper.SaveResultFile(TestContext, "main.bicep", """ + var foo string = 123 + """); + + var response = await tools.BuildBicep(bicepFilePath); + + response.Success.Should().BeFalse(); + response.Template.Should().BeNull(); + response.Diagnostics.Should().HaveCountGreaterThanOrEqualTo(2); + var diagnostic = response.Diagnostics.Should().ContainSingle(x => x.Code == "BCP033").Subject; + diagnostic.Level.Should().Be("Error"); + } + + [TestMethod] + public async Task BuildBicepparam_returns_compiled_parameters() + { + var outputFolder = FileHelper.SaveResultFiles(TestContext, [ + new("main.bicep", """ + param location string + output loc string = location + """), + new("main.bicepparam", """ + using 'main.bicep' + + param location = 'westus' + """), + ]); + + var response = await tools.BuildBicepparam(Path.Combine(outputFolder, "main.bicepparam")); + + response.Success.Should().BeTrue(); + response.Parameters.Should().NotBeNullOrEmpty(); + response.Parameters.Should().Contain("\"$schema\""); + response.Template.Should().NotBeNullOrEmpty(); + response.Template.Should().Contain("\"$schema\""); + response.Diagnostics.Should().NotContain(x => x.Level == "Error"); + } + + [TestMethod] + public async Task BuildBicepparam_returns_diagnostics_on_error() + { + var outputFolder = FileHelper.SaveResultFiles(TestContext, [ + new("main.bicep", """ + param location string + """), + new("main.bicepparam", """ + using 'main.bicep' + + param location = 123 + """), + ]); + + var response = await tools.BuildBicepparam(Path.Combine(outputFolder, "main.bicepparam")); + + response.Success.Should().BeFalse(); + response.Parameters.Should().BeNull(); + response.Diagnostics.Should().HaveCountGreaterThanOrEqualTo(1); + response.Diagnostics.Should().Contain(x => x.Level == "Error"); + } } diff --git a/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json b/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json index c336629d626..baf904be4cf 100644 --- a/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json +++ b/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json @@ -1,4 +1,188 @@ [ + { + "name": "build_bicep", + "description": "Compiles a Bicep file (.bicep) to an ARM template JSON string and returns the result along with any diagnostics.\n\nUse this tool to:\n- Compile Bicep source code to ARM template JSON\n- Check for compilation errors before deployment\n- Obtain the ARM template output for inspection or deployment\n\nThe compiled ARM template JSON is returned along with any compilation diagnostics (errors, warnings, and informational messages).\nThe file path must be absolute. If compilation fails due to errors, the Template field will be null.", + "jsonSchema": { + "type": "object", + "properties": { + "filePath": { + "description": "The path to the .bicep file", + "type": "string" + } + }, + "required": [ + "filePath" + ] + }, + "returnJsonSchema": { + "type": "object", + "properties": { + "success": { + "description": "Whether the compilation succeeded without errors", + "type": "boolean" + }, + "template": { + "description": "The compiled ARM template JSON, or null if compilation failed", + "type": [ + "string", + "null" + ] + }, + "diagnostics": { + "description": "The list of diagnostics from compilation", + "type": "array", + "items": { + "type": "object", + "properties": { + "fileUri": { + "description": "The .bicep or .bicepparam file URI", + "type": "string", + "format": "uri" + }, + "code": { + "description": "The diagnostic code", + "type": "string" + }, + "level": { + "description": "The diagnostic level", + "type": "string" + }, + "message": { + "description": "The diagnostic message", + "type": "string" + }, + "documentationUri": { + "description": "The documentation URI, if present", + "type": [ + "string", + "null" + ], + "format": "uri" + }, + "position": { + "description": "The character position of the diagnostic in the file, as a zero-based index", + "type": "integer" + }, + "length": { + "description": "The character length of the diagnostic", + "type": "integer" + } + }, + "required": [ + "fileUri", + "code", + "level", + "message", + "documentationUri", + "position", + "length" + ] + } + } + }, + "required": [ + "success", + "template", + "diagnostics" + ] + } + }, + { + "name": "build_bicepparam", + "description": "Compiles a Bicep parameters file (.bicepparam) to a parameters JSON string and returns the result along with any diagnostics.\n\nUse this tool to:\n- Compile Bicep parameters source code to ARM parameters JSON\n- Check for compilation errors before deployment\n- Obtain the parameters JSON output for inspection or deployment\n\nThe compiled parameters JSON and the associated ARM template JSON are returned along with any compilation diagnostics (errors, warnings, and informational messages).\nThe file path must be absolute. If compilation fails due to errors, the Parameters field will be null.", + "jsonSchema": { + "type": "object", + "properties": { + "filePath": { + "description": "The path to the .bicepparam file", + "type": "string" + } + }, + "required": [ + "filePath" + ] + }, + "returnJsonSchema": { + "type": "object", + "properties": { + "success": { + "description": "Whether the compilation succeeded without errors", + "type": "boolean" + }, + "parameters": { + "description": "The compiled parameters JSON, or null if compilation failed", + "type": [ + "string", + "null" + ] + }, + "template": { + "description": "The compiled ARM template JSON, or null if not applicable or compilation failed", + "type": [ + "string", + "null" + ] + }, + "diagnostics": { + "description": "The list of diagnostics from compilation", + "type": "array", + "items": { + "type": "object", + "properties": { + "fileUri": { + "description": "The .bicep or .bicepparam file URI", + "type": "string", + "format": "uri" + }, + "code": { + "description": "The diagnostic code", + "type": "string" + }, + "level": { + "description": "The diagnostic level", + "type": "string" + }, + "message": { + "description": "The diagnostic message", + "type": "string" + }, + "documentationUri": { + "description": "The documentation URI, if present", + "type": [ + "string", + "null" + ], + "format": "uri" + }, + "position": { + "description": "The character position of the diagnostic in the file, as a zero-based index", + "type": "integer" + }, + "length": { + "description": "The character length of the diagnostic", + "type": "integer" + } + }, + "required": [ + "fileUri", + "code", + "level", + "message", + "documentationUri", + "position", + "length" + ] + } + } + }, + "required": [ + "success", + "parameters", + "template", + "diagnostics" + ] + } + }, { "name": "decompile_arm_parameters_file", "description": "Converts an ARM template parameters JSON file into Bicep parameters syntax (.bicepparam).\n\nUse this tool to:\n- Migrate ARM JSON parameter files to Bicep parameters format\n- Convert deployment parameter files when modernizing to Bicep\n- Generate .bicepparam files from existing ARM deployments\n\nAccepts files with .json, .jsonc, or .arm extensions. The file path must be absolute.\n\nThe generated .bicepparam file includes a 'using' statement placeholder that must be completed, all parameters with their values preserved, and KeyVault references converted to az.getSecret() function calls if present.", From 03f962accccf58a8966a7d7ecdbfe7935a8c64d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 20:53:21 +0000 Subject: [PATCH 3/3] Remove GetBicepFileDiagnostics tool as it's no longer needed Agent-Logs-Url: https://github.com/Azure/bicep/sessions/38d034d1-aa8f-4a67-9af6-f7c63026bc0d Co-authored-by: anthony-c-martin <38542602+anthony-c-martin@users.noreply.github.com> --- global.json | 2 +- .../BicepCompilerTools.cs | 27 ------- .../BicepCompilerToolsTests.cs | 23 ------ .../Files/ServerTests/tools.json | 74 ------------------- src/Bicep.McpServer.UnitTests/ServerTests.cs | 2 +- 5 files changed, 2 insertions(+), 126 deletions(-) diff --git a/global.json b/global.json index 765e8a70781..39992fe60d7 100644 --- a/global.json +++ b/global.json @@ -7,4 +7,4 @@ "version": "10.0.203", "rollForward": "latestPatch" } -} \ No newline at end of file +} diff --git a/src/Bicep.McpServer.Core/BicepCompilerTools.cs b/src/Bicep.McpServer.Core/BicepCompilerTools.cs index 9b2dd1a4ce0..5896065c4b9 100644 --- a/src/Bicep.McpServer.Core/BicepCompilerTools.cs +++ b/src/Bicep.McpServer.Core/BicepCompilerTools.cs @@ -59,33 +59,6 @@ public record BuildBicepparamResult( [Description("The list of diagnostics from compilation")] ImmutableArray Diagnostics); - [McpServerTool(Title = "Get Bicep File Diagnostics", Destructive = false, Idempotent = true, OpenWorld = true, ReadOnly = true, UseStructuredContent = true)] - [Description(""" - Analyzes a Bicep file (.bicep) or Bicep parameters file (.bicepparam) and returns all compilation diagnostics including errors, warnings, and informational messages. - - Use this tool to: - - Validate Bicep syntax and identify compilation errors before deployment - - Check for warnings about deprecated features, security issues, or best practice violations - - Troubleshoot why a Bicep file isn't compiling - - Understand the severity and location of issues in the code - - Each diagnostic includes the error code (e.g., BCP033), severity level (Error/Warning/Info), descriptive message, exact position in the file, and a link to documentation for more details. - The file path must be absolute. Diagnostics are returned for the specified file and any modules it references. - """)] - public async Task> GetBicepFileDiagnostics( - [Description("The path to the .bicep or .bicepparam file")] string filePath) - { - var fileUri = IOUri.FromFilePath(filePath); - if (!fileUri.HasBicepExtension() && !fileUri.HasBicepParamExtension()) - { - throw new ArgumentException("The specified file must have a .bicep or .bicepparam extension.", nameof(filePath)); - } - - var compilation = await compiler.CreateCompilation(fileUri); - - return GetDiagnostics(compilation.GetAllDiagnosticsByBicepFile()); - } - [McpServerTool(Title = "Build Bicep", Destructive = false, Idempotent = true, OpenWorld = true, ReadOnly = true, UseStructuredContent = true)] [Description(""" Compiles a Bicep file (.bicep) to an ARM template JSON string and returns the result along with any diagnostics. diff --git a/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs b/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs index 2af3b4794d7..a185d10618e 100644 --- a/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs +++ b/src/Bicep.McpServer.UnitTests/BicepCompilerToolsTests.cs @@ -1,12 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; -using Bicep.Core.UnitTests.Assertions; -using Bicep.Core.UnitTests.Baselines; using Bicep.Core.UnitTests.Utils; -using Bicep.IO.Abstraction; using Bicep.McpServer.Core; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; @@ -30,25 +26,6 @@ private static IServiceProvider GetServiceProvider() private readonly BicepCompilerTools tools = GetServiceProvider().GetRequiredService(); - [TestMethod] - public async Task GetBicepFileDiagnostics_returns_list_of_diagnostics() - { - var bicepFilePath = FileHelper.SaveResultFile(TestContext, "main.bicep", """ - var foo string = 123 - """); - - var response = await tools.GetBicepFileDiagnostics(bicepFilePath); - - response.Should().HaveCountGreaterThanOrEqualTo(2); - var diagnostic = response.Should().ContainSingle(x => x.Code == "BCP033").Subject; - diagnostic.FileUri.Should().Be(IOUri.FromFilePath(bicepFilePath).ToUri()); - diagnostic.Level.Should().Be("Error"); - diagnostic.Message.Should().Be("Expected a value of type \"string\" but the provided value is of type \"123\"."); - diagnostic.Position.Should().Be(17); - diagnostic.Length.Should().Be(3); - diagnostic.DocumentationUri.Should().Be(new Uri("https://aka.ms/bicep/core-diagnostics#BCP033")); - } - [TestMethod] public async Task FormatBicepFile_returns_formatted_bicep_content() { diff --git a/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json b/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json index baf904be4cf..1e761711de4 100644 --- a/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json +++ b/src/Bicep.McpServer.UnitTests/Files/ServerTests/tools.json @@ -338,80 +338,6 @@ ] } }, - { - "name": "get_bicep_file_diagnostics", - "description": "Analyzes a Bicep file (.bicep) or Bicep parameters file (.bicepparam) and returns all compilation diagnostics including errors, warnings, and informational messages.\n\nUse this tool to:\n- Validate Bicep syntax and identify compilation errors before deployment\n- Check for warnings about deprecated features, security issues, or best practice violations\n- Troubleshoot why a Bicep file isn't compiling\n- Understand the severity and location of issues in the code\n\nEach diagnostic includes the error code (e.g., BCP033), severity level (Error/Warning/Info), descriptive message, exact position in the file, and a link to documentation for more details.\nThe file path must be absolute. Diagnostics are returned for the specified file and any modules it references.", - "jsonSchema": { - "type": "object", - "properties": { - "filePath": { - "description": "The path to the .bicep or .bicepparam file", - "type": "string" - } - }, - "required": [ - "filePath" - ] - }, - "returnJsonSchema": { - "type": "object", - "properties": { - "result": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fileUri": { - "description": "The .bicep or .bicepparam file URI", - "type": "string", - "format": "uri" - }, - "code": { - "description": "The diagnostic code", - "type": "string" - }, - "level": { - "description": "The diagnostic level", - "type": "string" - }, - "message": { - "description": "The diagnostic message", - "type": "string" - }, - "documentationUri": { - "description": "The documentation URI, if present", - "type": [ - "string", - "null" - ], - "format": "uri" - }, - "position": { - "description": "The character position of the diagnostic in the file, as a zero-based index", - "type": "integer" - }, - "length": { - "description": "The character length of the diagnostic", - "type": "integer" - } - }, - "required": [ - "fileUri", - "code", - "level", - "message", - "documentationUri", - "position", - "length" - ] - } - } - }, - "required": [ - "result" - ] - } - }, { "name": "get_deployment_snapshot", "description": "Creates a deployment snapshot from a Bicep parameters file (.bicepparam) by compiling it and pre-expanding the resulting ARM template.\nThe snapshot contains the predicted resources (as they would appear in a deployment) and any diagnostics produced during preflight.\n\nUse this tool to:\n- Preview what resources a deployment would create without running a deployment\n- Validate that parameters resolve as expected\n- Troubleshoot why a deployment would produce unexpected resource IDs, names, or locations\n- Inspect preflight diagnostics produced by template expansion\n\nThis tool can also be used to perform a semantic diff between two Bicep implementations:\n- Generate a snapshot for each version (using the same parameter values and metadata)\n- Compare the resulting predicted resources and diagnostics to verify both produce the same deployment outcome\n\nThis is especially useful for automated refactoring, where text-level diffs are noisy but the intended deployment result should remain unchanged.\n\nThe file path must be absolute and must point to a .bicepparam file.\nThe optional tenant/subscription/resource group/location/deployment name values are used as deployment metadata during snapshot generation.\nIf a value is omitted, the snapshot may contain unresolved placeholder expressions for the corresponding metadata.", diff --git a/src/Bicep.McpServer.UnitTests/ServerTests.cs b/src/Bicep.McpServer.UnitTests/ServerTests.cs index 07610883fd6..868585a082f 100644 --- a/src/Bicep.McpServer.UnitTests/ServerTests.cs +++ b/src/Bicep.McpServer.UnitTests/ServerTests.cs @@ -63,7 +63,7 @@ public async Task Error_filter_returns_error_information() { await using var helper = await McpServerHelper.StartServer(TestContext); - var instructions = await helper.Client.CallToolAsync("get_bicep_file_diagnostics", new Dictionary + var instructions = await helper.Client.CallToolAsync("build_bicep", new Dictionary { ["filePath"] = "nonexistent.bicep", });