From 496d20f9dd7b93620077dd344a41bd915e6e9f1b Mon Sep 17 00:00:00 2001 From: abdulanu0 Date: Sun, 14 Dec 2025 16:26:49 -0800 Subject: [PATCH 1/3] Fix Agent Framework sample --- .../sample-agent/Agent/MyAgent.cs | 65 ++++++++++++------- .../sample-agent/appsettings.json | 1 + 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs b/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs index d147c768..e7e30508 100644 --- a/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs +++ b/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs @@ -43,9 +43,8 @@ Otherwise you should use the tools available to you to help answer the user's qu private readonly IExporterTokenCache? _agentTokenCache = null; private readonly ILogger? _logger = null; private readonly IMcpToolRegistrationService? _toolService = null; - // Setup reusable auto sign-in handlers + // Setup reusable auto sign-in handler for agentic requests private readonly string AgenticIdAuthHanlder = "agentic"; - private readonly string MyAuthHanlder = "me"; // Temp private static readonly ConcurrentDictionary> _agentToolCache = new(); @@ -68,8 +67,10 @@ public MyAgent(AgentApplicationOptions options, // Handle A365 Notification Messages. // Listen for ANY message to be received. MUST BE AFTER ANY OTHER MESSAGE HANDLERS + // Agentic requests require the "agentic" handler for user authorization OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: true, autoSignInHandlers: new[] { AgenticIdAuthHanlder }); - OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: false , autoSignInHandlers: new[] { MyAuthHanlder }); + // Non-agentic requests (local testing with Playground) - no auth handler required + OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: false); } protected async Task WelcomeMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken) @@ -98,12 +99,14 @@ await AgentMetrics.InvokeObservedAgentOperation( /// protected async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken) { + // For agentic requests, use the agentic auth handler for observability and MCP tools + // For non-agentic requests (local testing), leave empty to skip MCP tools that require auth string ObservabilityAuthHandlerName = ""; string ToolAuthHandlerName = ""; if (turnContext.IsAgenticRequest()) + { ObservabilityAuthHandlerName = ToolAuthHandlerName = AgenticIdAuthHanlder; - else - ObservabilityAuthHandlerName = ToolAuthHandlerName = MyAuthHanlder; + } await A365OtelWrapper.InvokeObservedAgentOperation( @@ -178,32 +181,48 @@ await A365OtelWrapper.InvokeObservedAgentOperation( toolList.Add(AIFunctionFactory.Create(weatherLookupTool.GetCurrentWeatherForLocation)); toolList.Add(AIFunctionFactory.Create(weatherLookupTool.GetWeatherForecastForLocation)); - if (toolService != null) + // Check if agentic auth is enabled for MCP tools + var useAgenticAuth = _configuration?.GetValue("UseAgenticAuth", true) ?? true; + + if (toolService != null && useAgenticAuth) { - string toolCacheKey = GetToolCacheKey(turnState); - if (_agentToolCache.ContainsKey(toolCacheKey)) + // MCP tools require agentic authentication - only attempt if enabled + try { - var cachedTools = _agentToolCache[toolCacheKey]; - if (cachedTools != null && cachedTools.Count > 0) + string toolCacheKey = GetToolCacheKey(turnState); + if (_agentToolCache.ContainsKey(toolCacheKey)) { - toolList.AddRange(cachedTools); + var cachedTools = _agentToolCache[toolCacheKey]; + if (cachedTools != null && cachedTools.Count > 0) + { + toolList.AddRange(cachedTools); + } } - } - else - { - // Notify the user we are loading tools - await context.StreamingResponse.QueueInformativeUpdateAsync("Loading tools..."); + else + { + // Notify the user we are loading tools + await context.StreamingResponse.QueueInformativeUpdateAsync("Loading tools..."); - string agentId = Utility.ResolveAgentIdentity(context, await UserAuthorization.GetTurnTokenAsync(context, authHandlerName)); - var a365Tools = await toolService.GetMcpToolsAsync(agentId, UserAuthorization, authHandlerName, context).ConfigureAwait(false); + string agentId = Utility.ResolveAgentIdentity(context, await UserAuthorization.GetTurnTokenAsync(context, authHandlerName)); + var a365Tools = await toolService.GetMcpToolsAsync(agentId, UserAuthorization, authHandlerName, context).ConfigureAwait(false); - // Add the A365 tools to the tool options - if (a365Tools != null && a365Tools.Count > 0) - { - toolList.AddRange(a365Tools); - _agentToolCache.TryAdd(toolCacheKey, [.. a365Tools]); + // Add the A365 tools to the tool options + if (a365Tools != null && a365Tools.Count > 0) + { + toolList.AddRange(a365Tools); + _agentToolCache.TryAdd(toolCacheKey, [.. a365Tools]); + } } } + catch (Exception ex) + { + // Log warning and continue with local tools only + _logger?.LogWarning(ex, "Failed to register MCP tool servers. Continuing with local tools only."); + } + } + else if (toolService != null && !useAgenticAuth) + { + _logger?.LogInformation("UseAgenticAuth is disabled. Skipping MCP tools - using local tools only."); } // Create Chat Options with tools: diff --git a/dotnet/agent-framework/sample-agent/appsettings.json b/dotnet/agent-framework/sample-agent/appsettings.json index 9e346c48..65a0098d 100644 --- a/dotnet/agent-framework/sample-agent/appsettings.json +++ b/dotnet/agent-framework/sample-agent/appsettings.json @@ -60,5 +60,6 @@ "ApiKey": "----" // This is the API Key of the Azure OpenAI model deployment } }, + "UseAgenticAuth": true, // Set to false for local testing without agentic authentication (MCP tools will be skipped) "OpenWeatherApiKey": "----" //https://openweathermap.org/price - You will need to create a free account to get an API key (its at the bottom of the page). } From 396aed65b0096e921901d5c0683c475f513ad4db Mon Sep 17 00:00:00 2001 From: abdulanu0 Date: Mon, 15 Dec 2025 11:28:13 -0800 Subject: [PATCH 2/3] Fix Agent Framework sample: add missing 'me' handler config for OBO auth - Add MsalUserAuthorization configuration for 'me' handler in appsettings.json - Change MCP tool registration error handling to throw instead of swallow - Remove UseAgenticAuth skip logic - use mock MCP servers for local testing --- .../sample-agent/Agent/MyAgent.cs | 29 ++++++++----------- .../sample-agent/appsettings.json | 11 ++++++- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs b/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs index e7e30508..6058a42a 100644 --- a/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs +++ b/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs @@ -43,8 +43,11 @@ Otherwise you should use the tools available to you to help answer the user's qu private readonly IExporterTokenCache? _agentTokenCache = null; private readonly ILogger? _logger = null; private readonly IMcpToolRegistrationService? _toolService = null; + // Setup reusable auto sign-in handlers // Setup reusable auto sign-in handler for agentic requests private readonly string AgenticIdAuthHanlder = "agentic"; + // Setup reusable auto sign-in handler for OBO (On-Behalf-Of) authentication + private readonly string MyAuthHanlder = "me"; // Temp private static readonly ConcurrentDictionary> _agentToolCache = new(); @@ -69,8 +72,8 @@ public MyAgent(AgentApplicationOptions options, // Listen for ANY message to be received. MUST BE AFTER ANY OTHER MESSAGE HANDLERS // Agentic requests require the "agentic" handler for user authorization OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: true, autoSignInHandlers: new[] { AgenticIdAuthHanlder }); - // Non-agentic requests (local testing with Playground) - no auth handler required - OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: false); + // Non-agentic requests use OBO authentication via the "me" handler + OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: false, autoSignInHandlers: new[] { MyAuthHanlder }); } protected async Task WelcomeMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken) @@ -99,14 +102,13 @@ await AgentMetrics.InvokeObservedAgentOperation( /// protected async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken) { - // For agentic requests, use the agentic auth handler for observability and MCP tools - // For non-agentic requests (local testing), leave empty to skip MCP tools that require auth + // Select the appropriate auth handler based on request type string ObservabilityAuthHandlerName = ""; string ToolAuthHandlerName = ""; if (turnContext.IsAgenticRequest()) - { ObservabilityAuthHandlerName = ToolAuthHandlerName = AgenticIdAuthHanlder; - } + else + ObservabilityAuthHandlerName = ToolAuthHandlerName = MyAuthHanlder; await A365OtelWrapper.InvokeObservedAgentOperation( @@ -181,12 +183,8 @@ await A365OtelWrapper.InvokeObservedAgentOperation( toolList.Add(AIFunctionFactory.Create(weatherLookupTool.GetCurrentWeatherForLocation)); toolList.Add(AIFunctionFactory.Create(weatherLookupTool.GetWeatherForecastForLocation)); - // Check if agentic auth is enabled for MCP tools - var useAgenticAuth = _configuration?.GetValue("UseAgenticAuth", true) ?? true; - - if (toolService != null && useAgenticAuth) + if (toolService != null) { - // MCP tools require agentic authentication - only attempt if enabled try { string toolCacheKey = GetToolCacheKey(turnState); @@ -216,14 +214,11 @@ await A365OtelWrapper.InvokeObservedAgentOperation( } catch (Exception ex) { - // Log warning and continue with local tools only - _logger?.LogWarning(ex, "Failed to register MCP tool servers. Continuing with local tools only."); + // Log error and rethrow - MCP tool registration is required + _logger?.LogError(ex, "Failed to register MCP tool servers. Ensure MCP servers are configured correctly or use mock MCP servers for local testing."); + throw; } } - else if (toolService != null && !useAgenticAuth) - { - _logger?.LogInformation("UseAgenticAuth is disabled. Skipping MCP tools - using local tools only."); - } // Create Chat Options with tools: var toolOptions = new ChatOptions diff --git a/dotnet/agent-framework/sample-agent/appsettings.json b/dotnet/agent-framework/sample-agent/appsettings.json index 65a0098d..7afd0ef1 100644 --- a/dotnet/agent-framework/sample-agent/appsettings.json +++ b/dotnet/agent-framework/sample-agent/appsettings.json @@ -13,6 +13,16 @@ "https://graph.microsoft.com/.default" ] } + }, + "me": { + "Type": "MsalUserAuthorization", + "Settings": { + "ClientId": "{{ClientId}}", + "TenantId": "{{TenantId}}", + "Scopes": [ + "https://graph.microsoft.com/.default" + ] + } } } } @@ -60,6 +70,5 @@ "ApiKey": "----" // This is the API Key of the Azure OpenAI model deployment } }, - "UseAgenticAuth": true, // Set to false for local testing without agentic authentication (MCP tools will be skipped) "OpenWeatherApiKey": "----" //https://openweathermap.org/price - You will need to create a free account to get an API key (its at the bottom of the page). } From 8e329227093ebf564832ec69c3fa8bb4aae8f866 Mon Sep 17 00:00:00 2001 From: abdulanu0 Date: Mon, 15 Dec 2025 12:16:37 -0800 Subject: [PATCH 3/3] Fix typo: AuthHanlder -> AuthHandler --- .../sample-agent/Agent/MyAgent.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs b/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs index 6058a42a..eed9f0c6 100644 --- a/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs +++ b/dotnet/agent-framework/sample-agent/Agent/MyAgent.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Agent365AgentFrameworkSampleAgent.telemetry; @@ -45,9 +45,9 @@ Otherwise you should use the tools available to you to help answer the user's qu private readonly IMcpToolRegistrationService? _toolService = null; // Setup reusable auto sign-in handlers // Setup reusable auto sign-in handler for agentic requests - private readonly string AgenticIdAuthHanlder = "agentic"; + private readonly string AgenticIdAuthHandler = "agentic"; // Setup reusable auto sign-in handler for OBO (On-Behalf-Of) authentication - private readonly string MyAuthHanlder = "me"; + private readonly string MyAuthHandler = "me"; // Temp private static readonly ConcurrentDictionary> _agentToolCache = new(); @@ -71,9 +71,9 @@ public MyAgent(AgentApplicationOptions options, // Listen for ANY message to be received. MUST BE AFTER ANY OTHER MESSAGE HANDLERS // Agentic requests require the "agentic" handler for user authorization - OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: true, autoSignInHandlers: new[] { AgenticIdAuthHanlder }); + OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: true, autoSignInHandlers: new[] { AgenticIdAuthHandler }); // Non-agentic requests use OBO authentication via the "me" handler - OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: false, autoSignInHandlers: new[] { MyAuthHanlder }); + OnActivity(ActivityTypes.Message, OnMessageAsync, isAgenticOnly: false, autoSignInHandlers: new[] { MyAuthHandler }); } protected async Task WelcomeMessageAsync(ITurnContext turnContext, ITurnState turnState, CancellationToken cancellationToken) @@ -106,9 +106,9 @@ protected async Task OnMessageAsync(ITurnContext turnContext, ITurnState turnSta string ObservabilityAuthHandlerName = ""; string ToolAuthHandlerName = ""; if (turnContext.IsAgenticRequest()) - ObservabilityAuthHandlerName = ToolAuthHandlerName = AgenticIdAuthHanlder; + ObservabilityAuthHandlerName = ToolAuthHandlerName = AgenticIdAuthHandler; else - ObservabilityAuthHandlerName = ToolAuthHandlerName = MyAuthHanlder; + ObservabilityAuthHandlerName = ToolAuthHandlerName = MyAuthHandler; await A365OtelWrapper.InvokeObservedAgentOperation( @@ -280,4 +280,4 @@ private string GetToolCacheKey(ITurnState turnState) return userToolCacheKey; } } -} \ No newline at end of file +}