Conversation
|
/azp run |
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
There was a problem hiding this comment.
Pull request overview
This PR addresses MCP stdio clients receiving Method not found: logging/setLevel by adding support for the logging/setLevel JSON-RPC method and wiring it to DAB’s dynamic log-level infrastructure with explicit precedence rules (CLI > config > MCP).
Changes:
- Added
logging/setLeveldispatch + handler inMcpStdioServerto accept the standard MCP request and (when allowed) update runtime log level. - Introduced
ILogLevelControllerand implemented it inDynamicLogLevelProvider, including CLI/config precedence tracking. - Updated CLI engine-launch argument construction to only pass
--LogLevelwhen explicitly provided, plus added unit tests for MCP log-level updates.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Service/Telemetry/DynamicLogLevelProvider.cs | Implements ILogLevelController, adds MCP level mapping and precedence handling (CLI/config/MCP). |
| src/Service/Program.cs | Registers ILogLevelController in DI so MCP can resolve the controller. |
| src/Service.Tests/UnitTests/DynamicLogLevelProviderTests.cs | Adds unit tests for MCP-driven log-level changes and override behavior. |
| src/Core/Telemetry/ILogLevelController.cs | New interface to decouple MCP from the concrete log-level provider. |
| src/Cli/ConfigGenerator.cs | Only forwards --LogLevel when explicitly set by the user (to avoid treating defaults as CLI overrides). |
| src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs | Adds logging/setLevel handler that calls into ILogLevelController. |
| if (!IsCliOverridden) | ||
| { | ||
| CurrentLogLevel = runtimeConfig.GetConfiguredLogLevel(); | ||
|
|
||
| // Track if config explicitly set a log level (not just using defaults) | ||
| IsConfigOverridden = !runtimeConfig.IsLogLevelNull(); | ||
| } |
There was a problem hiding this comment.
IsConfigOverridden is derived from !runtimeConfig.IsLogLevelNull(), but the schema allows runtime.telemetry.log-level values to be null (meaning “use host-mode defaults”). In that case IsLogLevelNull() returns false (because the dictionary exists), causing MCP logging/setLevel to be blocked even though config did not actually pin a log level. Consider treating config as “overridden” only when at least one configured log-level value is non-null (e.g., any entry value != null), or add a dedicated RuntimeConfig helper for this distinction.
| minimumLogLevel = deserializedRuntimeConfig.GetConfiguredLogLevel(); | ||
| HostMode hostModeType = deserializedRuntimeConfig.IsDevelopmentMode() ? HostMode.Development : HostMode.Production; | ||
|
|
||
| _logger.LogInformation($"Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.", minimumLogLevel, hostModeType); |
There was a problem hiding this comment.
In the default log-level branch, _logger.LogInformation($"Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.", minimumLogLevel, hostModeType); uses an interpolated string while also passing structured args—those args won’t be captured as structured fields. Use a message template without $"..." (or remove the extra args) so logging behaves as intended.
| _logger.LogInformation($"Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.", minimumLogLevel, hostModeType); | |
| _logger.LogInformation("Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.", minimumLogLevel, hostModeType); |
| @@ -2597,17 +2598,22 @@ public static bool TryStartEngineWithOptions(StartOptions options, FileSystemRun | |||
|
|
|||
| minimumLogLevel = (LogLevel)options.LogLevel; | |||
| _logger.LogInformation("Setting minimum LogLevel: {minimumLogLevel}.", minimumLogLevel); | |||
|
|
|||
| // Only add --LogLevel when user explicitly specified it via CLI. | |||
| // This allows MCP logging/setLevel to work when no CLI override is present. | |||
| args.Add("--LogLevel"); | |||
| args.Add(minimumLogLevel.ToString()); | |||
| } | |||
| else | |||
| { | |||
| minimumLogLevel = deserializedRuntimeConfig.GetConfiguredLogLevel(); | |||
| HostMode hostModeType = deserializedRuntimeConfig.IsDevelopmentMode() ? HostMode.Development : HostMode.Production; | |||
|
|
|||
| _logger.LogInformation($"Setting default minimum LogLevel: {minimumLogLevel} for {hostModeType} mode.", minimumLogLevel, hostModeType); | |||
| } | |||
|
|
|||
| args.Add("--LogLevel"); | |||
| args.Add(minimumLogLevel.ToString()); | |||
| // Don't add --LogLevel arg since user didn't explicitly set it. | |||
| // Service will determine default log level based on config or host mode. | |||
| } | |||
There was a problem hiding this comment.
After this change, when options.LogLevel is not provided the CLI no longer passes --LogLevel to the engine. The Service defaults to LogLevel.Error when --LogLevel is absent (Program.GetLogLevelFromCommandLineArgs), so early startup logs will be suppressed even in Development until DynamicLogLevelProvider.UpdateFromRuntimeConfig(...) runs. If the intent is to keep the existing “Debug in Development / Error in Production” default behavior while still allowing MCP to change the level, consider setting the initial log level from the loaded config earlier in host construction (or introducing a non-override mechanism distinct from the CLI override flag).
Aniruddh25
left a comment
There was a problem hiding this comment.
Dont understand how the log level set by MCP tool call takes into effect. Can you show a working demo?
| bool changed = logLevelController.UpdateFromMcp(level); | ||
| if (changed) | ||
| { | ||
| Console.Error.WriteLine($"[MCP DEBUG] Log level changed to: {level}"); |
There was a problem hiding this comment.
Where are these debug messages output? Will those confuse the agent?
Also, what if the LogLevel set by CLI is None. Do these debug messages still be shown? If not, how do you prevent them if you send them to Console.Error.WriteLine?
There was a problem hiding this comment.
Do we really need these console messages?
|
|
||
| // Attempt to update the log level | ||
| // If CLI or Config overrode, this returns false but we still return success to the client | ||
| bool changed = logLevelController.UpdateFromMcp(level); |
There was a problem hiding this comment.
Even though the level is modified using the level controller, how does it take into effect in STDIO mode? HandleToolCallAsync seems to be writing to the console using WriteError function regardless of the logging level defined by the configuration/CLI/even the logging/setLevel call.
Aniruddh25
left a comment
There was a problem hiding this comment.
Manual Test 2: CLI override (MCP blocked)
Start MCP server with --LogLevel None
MCP sends logging/setLevel with level: info
Result: Log level stays at None, debug message shows "Log level not changed (CLI override active)"
As far as I understand, there should be no debug message altogether given that LogLevel has been defined as None through CLI.
Why make this change?
Closes #3274 - MCP Server returns "Method not found: logging/setLevel" error when clients send the standard MCP logging/setLevel request.
What is this change?
logging/setLevelJSON-RPC method inMcpStdioServer.csILogLevelControllerinterface to allow MCP to update log levels dynamicallyIsConfigOverriddenproperty to enforce precedence rulesPrecedence (highest to lowest):
--LogLevelflag - cannot be changedruntime.telemetry.log-level- cannot be changed by MCPlogging/setLevel- only works if neither CLI nor config set a levelIf CLI or config set a level, MCP requests are accepted but silently ignored (no error returned).
How was this tested?
Manual Test 1: No override (MCP can change level)
--LogLeveland without configlog-levellogging/setLevelwithlevel: infoManual Test 2: CLI override (MCP blocked)
--LogLevel Nonelogging/setLevelwithlevel: infoManual Test 3: Config override (MCP blocked)
"telemetry": { "log-level": { "default": "Warning" } }to config--LogLevellogging/setLevelwithlevel: infoSample Request(s)
MCP client sends:
{ "jsonrpc": "2.0", "id": 1, "method": "logging/setLevel", "params": { "level": "info" } }Server responds with empty result (success) and updates log level if no CLI/config override is active.