1- // Copyright (c) Microsoft Corporation.
2- // Licensed under the MIT License.
3-
41using System ;
52using System . CommandLine ;
63using System . CommandLine . Parsing ;
74using System . Runtime . InteropServices ;
5+ using System . Text ;
86using System . Text . RegularExpressions ;
97using System . Threading . Tasks ;
108using Azure . DataApiBuilder . Config ;
119using Azure . DataApiBuilder . Service . Exceptions ;
1210using Azure . DataApiBuilder . Service . Telemetry ;
11+ using Azure . DataApiBuilder . Service . Utilities ;
1312using Microsoft . ApplicationInsights ;
1413using Microsoft . AspNetCore ;
1514using Microsoft . AspNetCore . Builder ;
1615using Microsoft . AspNetCore . Hosting ;
1716using Microsoft . Extensions . Configuration ;
17+ using Microsoft . Extensions . DependencyInjection ;
1818using Microsoft . Extensions . Hosting ;
1919using Microsoft . Extensions . Logging ;
2020using Microsoft . Extensions . Logging . ApplicationInsights ;
@@ -33,27 +33,41 @@ public class Program
3333
3434 public static void Main ( string [ ] args )
3535 {
36+ bool runMcpStdio = McpStdioHelper . ShouldRunMcpStdio ( args , out string ? mcpRole ) ;
37+
38+ if ( runMcpStdio )
39+ {
40+ Console . OutputEncoding = new UTF8Encoding ( encoderShouldEmitUTF8Identifier : false ) ;
41+ Console . InputEncoding = new UTF8Encoding ( encoderShouldEmitUTF8Identifier : false ) ;
42+ }
43+
3644 if ( ! ValidateAspNetCoreUrls ( ) )
3745 {
3846 Console . Error . WriteLine ( "Invalid ASPNETCORE_URLS format. e.g.: ASPNETCORE_URLS=\" http://localhost:5000;https://localhost:5001\" " ) ;
3947 Environment . ExitCode = - 1 ;
4048 return ;
4149 }
4250
43- if ( ! StartEngine ( args ) )
51+ if ( ! StartEngine ( args , runMcpStdio , mcpRole ) )
4452 {
4553 Environment . ExitCode = - 1 ;
4654 }
4755 }
4856
49- public static bool StartEngine ( string [ ] args )
57+ public static bool StartEngine ( string [ ] args , bool runMcpStdio , string ? mcpRole )
5058 {
51- // Unable to use ILogger because this code is invoked before LoggerFactory
52- // is instantiated.
5359 Console . WriteLine ( "Starting the runtime engine..." ) ;
5460 try
5561 {
56- CreateHostBuilder ( args ) . Build ( ) . Run ( ) ;
62+ IHost host = CreateHostBuilder ( args , runMcpStdio , mcpRole ) . Build ( ) ;
63+
64+ if ( runMcpStdio )
65+ {
66+ return McpStdioHelper . RunMcpStdioHost ( host ) ;
67+ }
68+
69+ // Normal web mode
70+ host . Run ( ) ;
5771 return true ;
5872 }
5973 // Catch exception raised by explicit call to IHostApplicationLifetime.StopApplication()
@@ -72,17 +86,28 @@ public static bool StartEngine(string[] args)
7286 }
7387 }
7488
75- public static IHostBuilder CreateHostBuilder ( string [ ] args )
89+ // Compatibility overload used by external callers that do not pass the runMcpStdio flag.
90+ public static bool StartEngine ( string [ ] args )
91+ {
92+ bool runMcpStdio = McpStdioHelper . ShouldRunMcpStdio ( args , out string ? mcpRole ) ;
93+ return StartEngine ( args , runMcpStdio , mcpRole : mcpRole ) ;
94+ }
95+
96+ public static IHostBuilder CreateHostBuilder ( string [ ] args , bool runMcpStdio , string ? mcpRole )
7697 {
7798 return Host . CreateDefaultBuilder ( args )
7899 . ConfigureAppConfiguration ( builder =>
79100 {
80101 AddConfigurationProviders ( builder , args ) ;
102+ if ( runMcpStdio )
103+ {
104+ McpStdioHelper . ConfigureMcpStdio ( builder , mcpRole ) ;
105+ }
81106 } )
82107 . ConfigureWebHostDefaults ( webBuilder =>
83108 {
84109 Startup . MinimumLogLevel = GetLogLevelFromCommandLineArgs ( args , out Startup . IsLogLevelOverriddenByCli ) ;
85- ILoggerFactory loggerFactory = GetLoggerFactoryForLogLevel ( Startup . MinimumLogLevel ) ;
110+ ILoggerFactory loggerFactory = GetLoggerFactoryForLogLevel ( Startup . MinimumLogLevel , stdio : runMcpStdio ) ;
86111 ILogger < Startup > startupLogger = loggerFactory . CreateLogger < Startup > ( ) ;
87112 DisableHttpsRedirectionIfNeeded ( args ) ;
88113 webBuilder . UseStartup ( builder => new Startup ( builder . Configuration , startupLogger ) ) ;
@@ -140,7 +165,14 @@ private static ParseResult GetParseResult(Command cmd, string[] args)
140165 /// <param name="appTelemetryClient">Telemetry client</param>
141166 /// <param name="logLevelInitializer">Hot-reloadable log level</param>
142167 /// <param name="serilogLogger">Core Serilog logging pipeline</param>
143- public static ILoggerFactory GetLoggerFactoryForLogLevel ( LogLevel logLevel , TelemetryClient ? appTelemetryClient = null , LogLevelInitializer ? logLevelInitializer = null , Logger ? serilogLogger = null )
168+ /// <param name="stdio">Whether the logger is for stdio mode</param>
169+ /// <returns>ILoggerFactory</returns>
170+ public static ILoggerFactory GetLoggerFactoryForLogLevel (
171+ LogLevel logLevel ,
172+ TelemetryClient ? appTelemetryClient = null ,
173+ LogLevelInitializer ? logLevelInitializer = null ,
174+ Logger ? serilogLogger = null ,
175+ bool stdio = false )
144176 {
145177 return LoggerFactory
146178 . Create ( builder =>
@@ -229,7 +261,19 @@ public static ILoggerFactory GetLoggerFactoryForLogLevel(LogLevel logLevel, Tele
229261 }
230262 }
231263
232- builder . AddConsole ( ) ;
264+ // In stdio mode, route console logs to STDERR to keep STDOUT clean for MCP JSON
265+ if ( stdio )
266+ {
267+ builder . ClearProviders ( ) ;
268+ builder . AddConsole ( options =>
269+ {
270+ options . LogToStandardErrorThreshold = LogLevel . Trace ;
271+ } ) ;
272+ }
273+ else
274+ {
275+ builder . AddConsole ( ) ;
276+ }
233277 } ) ;
234278 }
235279
0 commit comments