Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 37 additions & 14 deletions src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using Azure.DataApiBuilder.Config.ObjectModel;
using Azure.DataApiBuilder.Core.AuthenticationHelpers.AuthenticationSimulator;
using Azure.DataApiBuilder.Core.Configurations;
using Azure.DataApiBuilder.Mcp.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -161,25 +163,46 @@ private void HandleInitialize(JsonElement? id)
// Extract the actual id value from the request
object? requestId = id.HasValue ? GetIdValue(id.Value) : null;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, Even though HandleInitialize of the McpStdioServer class has been modified to include the description. We need similar changes to the Mcp Http Server that provides the tool registry in McpServerConfiguration.

Also, please dont update PR description. Retain the original PR description that described this complete change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated MCP HTTP server configuration in McpServerConfiguration.cs:

  • Changed server name to "SQL MCP Server" to match stdio server
  • Modified ConfigureMcpServer to accept RuntimeConfig parameter
  • Added comment noting that instructions support will be added when ModelContextProtocol.AspNetCore library supports it

The HTTP server now receives the runtime config but can't set instructions yet as the library doesn't expose this property on McpServerOptions. The description is ready to be wired when library support is available.

Original PR description has been retained as requested. Commit: b162ad4

// Get the description from runtime config if available
string? instructions = null;
RuntimeConfigProvider? runtimeConfigProvider = _serviceProvider.GetService<RuntimeConfigProvider>();
if (runtimeConfigProvider != null)
{
try
{
RuntimeConfig runtimeConfig = runtimeConfigProvider.GetConfig();
instructions = runtimeConfig.Runtime?.Mcp?.Description;
}
catch (Exception ex)
{
// Log to stderr for diagnostics and rethrow to avoid masking configuration errors
Console.Error.WriteLine($"[MCP WARNING] Failed to retrieve MCP description from config: {ex.Message}");
throw;
}
}

// Create the initialize response
var result = new
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, Instead of var, please provide the accurate type of the variable result.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced var with explicit object type for both result and response variables. Since these use anonymous types for JSON serialization, object is the appropriate explicit type. Commit: c6c45f0

{
protocolVersion = _protocolVersion,
capabilities = new
{
tools = new { listChanged = true },
logging = new { }
},
serverInfo = new
{
name = "SQL MCP Server",
version = "1.0.0"
},
instructions = !string.IsNullOrWhiteSpace(instructions) ? instructions : null
};

var response = new
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, same comment, replace var with the accurate type of response

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already addressed in commit c6c45f0. Both result and response variables now use explicit object type instead of var.

{
jsonrpc = "2.0",
id = requestId,
result = new
{
protocolVersion = _protocolVersion,
capabilities = new
{
tools = new { listChanged = true },
logging = new { }
},
serverInfo = new
{
name = "Data API Builder",
version = "1.0.0"
}
}
result
};

string json = JsonSerializer.Serialize(response);
Expand Down
57 changes: 57 additions & 0 deletions src/Cli.Tests/ConfigureOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,63 @@ public void TestFailureWhenAddingSetSessionContextToMySQLDatabase()
Assert.IsFalse(isSuccess);
}

/// <summary>
/// Tests that running "dab configure --runtime.mcp.description {value}" on a config with various values results
/// in runtime config update. Takes in updated value for mcp.description and
/// validates whether the runtime config reflects those updated values
/// </summary>
[DataTestMethod]
[DataRow("This MCP provides access to the Products database and should be used to answer product-related or inventory-related questions from the user.", DisplayName = "Set MCP description.")]
[DataRow("Use this server for customer data queries.", DisplayName = "Set MCP description with short text.")]
public void TestUpdateDescriptionForMcpSettings(string descriptionValue)
{
// Arrange -> all the setup which includes creating options.
SetupFileSystemWithInitialConfig(INITIAL_CONFIG);

// Act: Attempts to update mcp.description value
ConfigureOptions options = new(
runtimeMcpDescription: descriptionValue,
config: TEST_RUNTIME_CONFIG_FILE
);
bool isSuccess = TryConfigureSettings(options, _runtimeConfigLoader!, _fileSystem!);

// Assert: Validate the Description is updated
Assert.IsTrue(isSuccess);
string updatedConfig = _fileSystem!.File.ReadAllText(TEST_RUNTIME_CONFIG_FILE);
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(updatedConfig, out RuntimeConfig? runtimeConfig));
Assert.IsNotNull(runtimeConfig.Runtime?.Mcp?.Description);
Assert.AreEqual(descriptionValue, runtimeConfig.Runtime.Mcp.Description);
}

/// <summary>
/// Tests that the MCP description can be added to a config that doesn't already have one
/// </summary>
[TestMethod]
public void TestAddDescriptionToMcpSettings()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestAddDescriptionToMcpSettings is exactly same as TestUpdateDescriptionForMcpSettings, can be removed. The test TestUpdateDescriptionForMcpSettings can be renamed to TestConfigureDescriptionForMcpSettings.

{
// Arrange
SetupFileSystemWithInitialConfig(INITIAL_CONFIG);

// Initial config should not have a description
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(INITIAL_CONFIG, out RuntimeConfig? config));
Assert.IsNull(config.Runtime?.Mcp?.Description);

// Act: Add description
string descriptionValue = "This is a test description for MCP server.";
ConfigureOptions options = new(
runtimeMcpDescription: descriptionValue,
config: TEST_RUNTIME_CONFIG_FILE
);
bool isSuccess = TryConfigureSettings(options, _runtimeConfigLoader!, _fileSystem!);

// Assert: Validate the Description is added
Assert.IsTrue(isSuccess);
string updatedConfig = _fileSystem!.File.ReadAllText(TEST_RUNTIME_CONFIG_FILE);
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(updatedConfig, out RuntimeConfig? runtimeConfig));
Assert.IsNotNull(runtimeConfig.Runtime?.Mcp?.Description);
Assert.AreEqual(descriptionValue, runtimeConfig.Runtime.Mcp.Description);
}

/// <summary>
/// Sets up the mock file system with an initial configuration file.
/// This method adds a config file to the mock file system and verifies its existence.
Expand Down
5 changes: 5 additions & 0 deletions src/Cli/Commands/ConfigureOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public ConfigureOptions(
bool? runtimeRestRequestBodyStrict = null,
bool? runtimeMcpEnabled = null,
string? runtimeMcpPath = null,
string? runtimeMcpDescription = null,
bool? runtimeMcpDmlToolsEnabled = null,
bool? runtimeMcpDmlToolsDescribeEntitiesEnabled = null,
bool? runtimeMcpDmlToolsCreateRecordEnabled = null,
Expand Down Expand Up @@ -93,6 +94,7 @@ public ConfigureOptions(
// Mcp
RuntimeMcpEnabled = runtimeMcpEnabled;
RuntimeMcpPath = runtimeMcpPath;
RuntimeMcpDescription = runtimeMcpDescription;
RuntimeMcpDmlToolsEnabled = runtimeMcpDmlToolsEnabled;
RuntimeMcpDmlToolsDescribeEntitiesEnabled = runtimeMcpDmlToolsDescribeEntitiesEnabled;
RuntimeMcpDmlToolsCreateRecordEnabled = runtimeMcpDmlToolsCreateRecordEnabled;
Expand Down Expand Up @@ -180,6 +182,9 @@ public ConfigureOptions(
[Option("runtime.mcp.path", Required = false, HelpText = "Customize DAB's MCP endpoint path. Default: '/mcp' Conditions: Prefix path with '/'.")]
public string? RuntimeMcpPath { get; }

[Option("runtime.mcp.description", Required = false, HelpText = "Set the MCP server description to be exposed in the initialize response.")]
public string? RuntimeMcpDescription { get; }

[Option("runtime.mcp.dml-tools.enabled", Required = false, HelpText = "Enable DAB's MCP DML tools endpoint. Default: true (boolean).")]
public bool? RuntimeMcpDmlToolsEnabled { get; }

Expand Down
18 changes: 17 additions & 1 deletion src/Cli/ConfigGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,15 @@ private static bool TryUpdateConfiguredRuntimeOptions(

// MCP: Enabled and Path
if (options.RuntimeMcpEnabled != null ||
options.RuntimeMcpPath != null)
options.RuntimeMcpPath != null ||
options.RuntimeMcpDescription != null ||
options.RuntimeMcpDmlToolsEnabled != null ||
options.RuntimeMcpDmlToolsDescribeEntitiesEnabled != null ||
options.RuntimeMcpDmlToolsCreateRecordEnabled != null ||
options.RuntimeMcpDmlToolsReadRecordsEnabled != null ||
options.RuntimeMcpDmlToolsUpdateRecordEnabled != null ||
options.RuntimeMcpDmlToolsDeleteRecordEnabled != null ||
options.RuntimeMcpDmlToolsExecuteEntityEnabled != null)
{
McpRuntimeOptions updatedMcpOptions = runtimeConfig?.Runtime?.Mcp ?? new();
bool status = TryUpdateConfiguredMcpValues(options, ref updatedMcpOptions);
Expand Down Expand Up @@ -1066,6 +1074,14 @@ private static bool TryUpdateConfiguredMcpValues(
}
}

// Runtime.Mcp.Description
updatedValue = options?.RuntimeMcpDescription;
if (updatedValue != null)
{
updatedMcpOptions = updatedMcpOptions! with { Description = (string)updatedValue };
_logger.LogInformation("Updated RuntimeConfig with Runtime.Mcp.Description as '{updatedValue}'", updatedValue);
}

// Handle DML tools configuration
bool hasToolUpdates = false;
DmlToolsConfig? currentDmlTools = updatedMcpOptions?.DmlTools;
Expand Down
18 changes: 17 additions & 1 deletion src/Config/Converters/McpRuntimeOptionsConverterFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,13 @@ internal McpRuntimeOptionsConverter(DeserializationVariableReplacementSettings?
bool enabled = true;
string? path = null;
DmlToolsConfig? dmlTools = null;
string? description = null;

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return new McpRuntimeOptions(enabled, path, dmlTools);
return new McpRuntimeOptions(enabled, path, dmlTools, description);
}

string? propertyName = reader.GetString();
Expand Down Expand Up @@ -98,6 +99,14 @@ internal McpRuntimeOptionsConverter(DeserializationVariableReplacementSettings?
dmlTools = dmlToolsConfigConverter.Read(ref reader, typeToConvert, options);
break;

case "description":
if (reader.TokenType is not JsonTokenType.Null)
{
description = reader.DeserializeString(_replacementSettings);
}

break;

default:
throw new JsonException($"Unexpected property {propertyName}");
}
Expand Down Expand Up @@ -134,6 +143,13 @@ public override void Write(Utf8JsonWriter writer, McpRuntimeOptions value, JsonS
dmlToolsOptionsConverter.Write(writer, value.DmlTools, options);
}

// Write description if it's provided
if (value is not null && !string.IsNullOrWhiteSpace(value.Description))
{
writer.WritePropertyName("description");
JsonSerializer.Serialize(writer, value.Description, options);
}

writer.WriteEndObject();
}
}
Expand Down
11 changes: 10 additions & 1 deletion src/Config/ObjectModel/McpRuntimeOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ public record McpRuntimeOptions
[JsonConverter(typeof(DmlToolsConfigConverter))]
public DmlToolsConfig? DmlTools { get; init; }

/// <summary>
/// Description of the MCP server to be exposed in the initialize response
/// </summary>
[JsonPropertyName("description")]
public string? Description { get; init; }

[JsonConstructor]
public McpRuntimeOptions(
bool? Enabled = null,
string? Path = null,
DmlToolsConfig? DmlTools = null)
DmlToolsConfig? DmlTools = null,
string? Description = null)
{
this.Enabled = Enabled ?? true;

Expand All @@ -58,6 +65,8 @@ public McpRuntimeOptions(
{
this.DmlTools = DmlTools;
}

this.Description = Description;
}

/// <summary>
Expand Down
Loading
Loading