-
Notifications
You must be signed in to change notification settings - Fork 296
Cherry pick [MCP] Added support for --mcp-stdio flag to dab start (#2983) to release 1.7
#3034
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/1.7
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| using Microsoft.Extensions.Configuration; | ||
|
|
||
| namespace Azure.DataApiBuilder.Mcp.Core | ||
| { | ||
| /// <summary> | ||
| /// Centralized defaults and configuration keys for MCP protocol settings. | ||
| /// </summary> | ||
| public static class McpProtocolDefaults | ||
| { | ||
| /// <summary> | ||
| /// Default MCP protocol version advertised when no configuration override is provided. | ||
| /// </summary> | ||
| public const string DEFAULT_PROTOCOL_VERSION = "2025-06-18"; | ||
|
|
||
| /// <summary> | ||
| /// Configuration key used to override the MCP protocol version. | ||
| /// </summary> | ||
| public const string PROTOCOL_VERSION_CONFIG_KEY = "MCP:ProtocolVersion"; | ||
|
|
||
| /// <summary> | ||
| /// Helper to resolve the effective protocol version from configuration. | ||
| /// Falls back to <see cref="DEFAULT_PROTOCOL_VERSION"/> when the key is not set. | ||
| /// </summary> | ||
| public static string ResolveProtocolVersion(IConfiguration? configuration) | ||
| { | ||
| return configuration?.GetValue<string>(PROTOCOL_VERSION_CONFIG_KEY) ?? DEFAULT_PROTOCOL_VERSION; | ||
| } | ||
| } | ||
| } | ||
|
|
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| namespace Azure.DataApiBuilder.Mcp.Core | ||
| { | ||
| public interface IMcpStdioServer | ||
| { | ||
| Task RunAsync(CancellationToken cancellationToken); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,20 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| using System; | ||
|
||
| using System.CommandLine; | ||
| using System.CommandLine.Parsing; | ||
| using System.Runtime.InteropServices; | ||
| using System.Text; | ||
| using System.Text.RegularExpressions; | ||
| using System.Threading.Tasks; | ||
| using Azure.DataApiBuilder.Config; | ||
| using Azure.DataApiBuilder.Service.Exceptions; | ||
| using Azure.DataApiBuilder.Service.Telemetry; | ||
| using Azure.DataApiBuilder.Service.Utilities; | ||
| using Microsoft.ApplicationInsights; | ||
| using Microsoft.AspNetCore; | ||
| using Microsoft.AspNetCore.Builder; | ||
| using Microsoft.AspNetCore.Hosting; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Hosting; | ||
| using Microsoft.Extensions.Logging; | ||
| using Microsoft.Extensions.Logging.ApplicationInsights; | ||
|
|
@@ -33,27 +33,41 @@ public class Program | |
|
|
||
| public static void Main(string[] args) | ||
| { | ||
| bool runMcpStdio = McpStdioHelper.ShouldRunMcpStdio(args, out string? mcpRole); | ||
|
|
||
| if (runMcpStdio) | ||
| { | ||
| Console.OutputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); | ||
| Console.InputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); | ||
| } | ||
|
|
||
| if (!ValidateAspNetCoreUrls()) | ||
| { | ||
| Console.Error.WriteLine("Invalid ASPNETCORE_URLS format. e.g.: ASPNETCORE_URLS=\"http://localhost:5000;https://localhost:5001\""); | ||
| Environment.ExitCode = -1; | ||
| return; | ||
| } | ||
|
|
||
| if (!StartEngine(args)) | ||
| if (!StartEngine(args, runMcpStdio, mcpRole)) | ||
| { | ||
| Environment.ExitCode = -1; | ||
| } | ||
| } | ||
|
|
||
| public static bool StartEngine(string[] args) | ||
| public static bool StartEngine(string[] args, bool runMcpStdio, string? mcpRole) | ||
| { | ||
| // Unable to use ILogger because this code is invoked before LoggerFactory | ||
| // is instantiated. | ||
| Console.WriteLine("Starting the runtime engine..."); | ||
| try | ||
| { | ||
| CreateHostBuilder(args).Build().Run(); | ||
| IHost host = CreateHostBuilder(args, runMcpStdio, mcpRole).Build(); | ||
|
|
||
| if (runMcpStdio) | ||
| { | ||
| return McpStdioHelper.RunMcpStdioHost(host); | ||
| } | ||
|
|
||
| // Normal web mode | ||
| host.Run(); | ||
| return true; | ||
| } | ||
| // Catch exception raised by explicit call to IHostApplicationLifetime.StopApplication() | ||
|
|
@@ -72,17 +86,28 @@ public static bool StartEngine(string[] args) | |
| } | ||
| } | ||
|
|
||
| public static IHostBuilder CreateHostBuilder(string[] args) | ||
| // Compatibility overload used by external callers that do not pass the runMcpStdio flag. | ||
| public static bool StartEngine(string[] args) | ||
| { | ||
| bool runMcpStdio = McpStdioHelper.ShouldRunMcpStdio(args, out string? mcpRole); | ||
| return StartEngine(args, runMcpStdio, mcpRole: mcpRole); | ||
| } | ||
|
|
||
| public static IHostBuilder CreateHostBuilder(string[] args, bool runMcpStdio, string? mcpRole) | ||
| { | ||
| return Host.CreateDefaultBuilder(args) | ||
| .ConfigureAppConfiguration(builder => | ||
| { | ||
| AddConfigurationProviders(builder, args); | ||
| if (runMcpStdio) | ||
| { | ||
| McpStdioHelper.ConfigureMcpStdio(builder, mcpRole); | ||
| } | ||
| }) | ||
| .ConfigureWebHostDefaults(webBuilder => | ||
| { | ||
| Startup.MinimumLogLevel = GetLogLevelFromCommandLineArgs(args, out Startup.IsLogLevelOverriddenByCli); | ||
| ILoggerFactory loggerFactory = GetLoggerFactoryForLogLevel(Startup.MinimumLogLevel); | ||
| ILoggerFactory loggerFactory = GetLoggerFactoryForLogLevel(Startup.MinimumLogLevel, stdio: runMcpStdio); | ||
| ILogger<Startup> startupLogger = loggerFactory.CreateLogger<Startup>(); | ||
| DisableHttpsRedirectionIfNeeded(args); | ||
| webBuilder.UseStartup(builder => new Startup(builder.Configuration, startupLogger)); | ||
|
|
@@ -140,7 +165,14 @@ private static ParseResult GetParseResult(Command cmd, string[] args) | |
| /// <param name="appTelemetryClient">Telemetry client</param> | ||
| /// <param name="logLevelInitializer">Hot-reloadable log level</param> | ||
| /// <param name="serilogLogger">Core Serilog logging pipeline</param> | ||
| public static ILoggerFactory GetLoggerFactoryForLogLevel(LogLevel logLevel, TelemetryClient? appTelemetryClient = null, LogLevelInitializer? logLevelInitializer = null, Logger? serilogLogger = null) | ||
| /// <param name="stdio">Whether the logger is for stdio mode</param> | ||
| /// <returns>ILoggerFactory</returns> | ||
| public static ILoggerFactory GetLoggerFactoryForLogLevel( | ||
| LogLevel logLevel, | ||
| TelemetryClient? appTelemetryClient = null, | ||
| LogLevelInitializer? logLevelInitializer = null, | ||
| Logger? serilogLogger = null, | ||
| bool stdio = false) | ||
| { | ||
| return LoggerFactory | ||
| .Create(builder => | ||
|
|
@@ -229,7 +261,19 @@ public static ILoggerFactory GetLoggerFactoryForLogLevel(LogLevel logLevel, Tele | |
| } | ||
| } | ||
|
|
||
| builder.AddConsole(); | ||
| // In stdio mode, route console logs to STDERR to keep STDOUT clean for MCP JSON | ||
| if (stdio) | ||
| { | ||
| builder.ClearProviders(); | ||
| builder.AddConsole(options => | ||
| { | ||
| options.LogToStandardErrorThreshold = LogLevel.Trace; | ||
| }); | ||
anushakolan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| else | ||
| { | ||
| builder.AddConsole(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,97 @@ | ||||||
| using System; | ||||||
| using System.Collections.Generic; | ||||||
| using Microsoft.Extensions.Configuration; | ||||||
| using Microsoft.Extensions.DependencyInjection; | ||||||
| using Microsoft.Extensions.Hosting; | ||||||
|
|
||||||
| namespace Azure.DataApiBuilder.Service.Utilities | ||||||
| { | ||||||
| /// <summary> | ||||||
| /// Helper methods for configuring and running MCP in stdio mode. | ||||||
| /// </summary> | ||||||
| internal static class McpStdioHelper | ||||||
| { | ||||||
| /// <summary> | ||||||
| /// Determines if MCP stdio mode should be run based on command line arguments. | ||||||
| /// </summary> | ||||||
| /// <param name="args"> The command line arguments.</param> | ||||||
| /// <param name="mcpRole"> The role for MCP stdio mode, if specified.</param> | ||||||
| /// <returns></returns> | ||||||
|
||||||
| /// <returns></returns> | |
| /// <returns><see langword="true"/> if MCP stdio mode should be run; otherwise, <see langword="false"/>.</returns> |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter name in the XML documentation does not match the actual parameter name. The documentation says "builder" but the parameter is named "host".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interface is missing necessary using directives. The
Tasktype is used in the method signature but theSystem.Threading.Tasksnamespace is not imported. Additionally,CancellationTokenrequiresSystem.Threadingnamespace.