diff --git a/src/Azure.DataApiBuilder.Mcp/Core/McpServerConfiguration.cs b/src/Azure.DataApiBuilder.Mcp/Core/McpServerConfiguration.cs
index 2b48c37a83..387ea7427f 100644
--- a/src/Azure.DataApiBuilder.Mcp/Core/McpServerConfiguration.cs
+++ b/src/Azure.DataApiBuilder.Mcp/Core/McpServerConfiguration.cs
@@ -19,7 +19,7 @@ internal static class McpServerConfiguration
///
/// Configures the MCP server with tool capabilities.
///
- internal static IServiceCollection ConfigureMcpServer(this IServiceCollection services)
+ internal static IServiceCollection ConfigureMcpServer(this IServiceCollection services, string? instructions)
{
services.AddMcpServer()
.WithListToolsHandler((RequestContext request, CancellationToken ct) =>
@@ -93,6 +93,7 @@ internal static IServiceCollection ConfigureMcpServer(this IServiceCollection se
options.ServerInfo = new() { Name = McpProtocolDefaults.MCP_SERVER_NAME, Version = McpProtocolDefaults.MCP_SERVER_VERSION };
options.Capabilities ??= new();
options.Capabilities.Tools ??= new();
+ options.ServerInstructions = !string.IsNullOrWhiteSpace(instructions) ? instructions : null;
});
return services;
diff --git a/src/Azure.DataApiBuilder.Mcp/Core/McpServiceCollectionExtensions.cs b/src/Azure.DataApiBuilder.Mcp/Core/McpServiceCollectionExtensions.cs
index bc87602da9..c88cae148d 100644
--- a/src/Azure.DataApiBuilder.Mcp/Core/McpServiceCollectionExtensions.cs
+++ b/src/Azure.DataApiBuilder.Mcp/Core/McpServiceCollectionExtensions.cs
@@ -41,8 +41,8 @@ public static IServiceCollection AddDabMcpServer(this IServiceCollection service
// Register custom tools from configuration
RegisterCustomTools(services, runtimeConfig);
- // Configure MCP server
- services.ConfigureMcpServer();
+ // Configure MCP server and propagate runtime description to MCP initialize instructions.
+ services.ConfigureMcpServer(runtimeConfig.Runtime?.Mcp?.Description);
return services;
}
diff --git a/src/Service.Tests/Configuration/ConfigurationTests.cs b/src/Service.Tests/Configuration/ConfigurationTests.cs
index a8925f3ad6..3deb8d5cb7 100644
--- a/src/Service.Tests/Configuration/ConfigurationTests.cs
+++ b/src/Service.Tests/Configuration/ConfigurationTests.cs
@@ -2804,6 +2804,44 @@ public async Task TestGlobalFlagToEnableRestGraphQLAndMcpForHostedAndNonHostedEn
}
}
+ [TestMethod]
+ [TestCategory(TestCategory.MSSQL)]
+ public async Task TestMcpInitializeIncludesInstructionsFromRuntimeDescription()
+ {
+ const string MCP_INSTRUCTIONS = "Use SQL tools to query the database.";
+ const string CUSTOM_CONFIG = "custom-config-mcp-instructions.json";
+
+ TestHelper.SetupDatabaseEnvironment(MSSQL_ENVIRONMENT);
+
+ GraphQLRuntimeOptions graphqlOptions = new(Enabled: false);
+ RestRuntimeOptions restRuntimeOptions = new(Enabled: false);
+ McpRuntimeOptions mcpRuntimeOptions = new(Enabled: true, Description: MCP_INSTRUCTIONS);
+
+ SqlConnectionStringBuilder connectionStringBuilder = new(GetConnectionStringFromEnvironmentConfig(environment: TestCategory.MSSQL))
+ {
+ TrustServerCertificate = true
+ };
+
+ DataSource dataSource = new(DatabaseType.MSSQL,
+ connectionStringBuilder.ConnectionString, Options: null);
+
+ RuntimeConfig configuration = InitMinimalRuntimeConfig(dataSource, graphqlOptions, restRuntimeOptions, mcpRuntimeOptions);
+ File.WriteAllText(CUSTOM_CONFIG, configuration.ToJson());
+
+ string[] args = new[]
+ {
+ $"--ConfigFileName={CUSTOM_CONFIG}"
+ };
+
+ using TestServer server = new(Program.CreateWebHostBuilder(args));
+ using HttpClient client = server.CreateClient();
+
+ JsonElement initializeResponse = await GetMcpInitializeResponse(client, configuration.Runtime.Mcp);
+ JsonElement result = initializeResponse.GetProperty("result");
+
+ Assert.AreEqual(MCP_INSTRUCTIONS, result.GetProperty("instructions").GetString(), "MCP initialize response should include instructions from runtime.mcp.description.");
+ }
+
///
/// For mutation operations, both the respective operation(create/update/delete) + read permissions are needed to receive a valid response.
/// In this test, Anonymous role is configured with only create permission.
@@ -6284,6 +6322,83 @@ public static async Task GetMcpResponse(HttpClient httpClient, M
return responseCode;
}
+ ///
+ /// Executes MCP initialize over HTTP and returns the parsed JSON response.
+ ///
+ public static async Task GetMcpInitializeResponse(HttpClient httpClient, McpRuntimeOptions mcp)
+ {
+ int retryCount = 0;
+ HttpStatusCode responseCode = HttpStatusCode.ServiceUnavailable;
+ string responseBody = string.Empty;
+
+ while (retryCount < RETRY_COUNT)
+ {
+ object payload = new
+ {
+ jsonrpc = "2.0",
+ id = 1,
+ method = "initialize",
+ @params = new
+ {
+ protocolVersion = "2025-03-26",
+ capabilities = new { },
+ clientInfo = new { name = "dab-test", version = "1.0.0" }
+ }
+ };
+
+ HttpRequestMessage mcpRequest = new(HttpMethod.Post, mcp.Path)
+ {
+ Content = JsonContent.Create(payload)
+ };
+ mcpRequest.Headers.Add("Accept", "application/json, text/event-stream");
+
+ HttpResponseMessage mcpResponse = await httpClient.SendAsync(mcpRequest);
+ responseCode = mcpResponse.StatusCode;
+ responseBody = await mcpResponse.Content.ReadAsStringAsync();
+
+ if (responseCode == HttpStatusCode.ServiceUnavailable || responseCode == HttpStatusCode.NotFound)
+ {
+ retryCount++;
+ await Task.Delay(TimeSpan.FromSeconds(Math.Pow(RETRY_WAIT_SECONDS, retryCount)));
+ continue;
+ }
+
+ break;
+ }
+
+ Assert.AreEqual(HttpStatusCode.OK, responseCode, "MCP initialize should return HTTP 200.");
+ Assert.IsFalse(string.IsNullOrWhiteSpace(responseBody), "MCP initialize response body should not be empty.");
+
+ // Depending on transport/content negotiation, initialize can return plain JSON
+ // or SSE-formatted text where JSON payload is carried in a data: line.
+ string payloadToParse = responseBody.TrimStart().StartsWith('{')
+ ? responseBody
+ : ExtractJsonFromSsePayload(responseBody);
+
+ Assert.IsFalse(string.IsNullOrWhiteSpace(payloadToParse), "MCP initialize response did not contain a JSON payload.");
+
+ using JsonDocument responseDocument = JsonDocument.Parse(payloadToParse);
+ return responseDocument.RootElement.Clone();
+ }
+
+ private static string ExtractJsonFromSsePayload(string ssePayload)
+ {
+ foreach (string line in ssePayload.Split('\n'))
+ {
+ string trimmed = line.Trim();
+ if (trimmed.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
+ {
+ string data = trimmed.Substring("data:".Length).Trim();
+ if (!string.IsNullOrWhiteSpace(data) && data.StartsWith('{'))
+ {
+ return data;
+ }
+ }
+ }
+
+ return string.Empty;
+ }
+
///
/// Helper method to instantiate RuntimeConfig object needed for multiple create tests.
///