// Copyright (c) Microsoft. All rights reserved. using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; using Microsoft.Extensions.Logging; namespace Microsoft.Agents.AI.Hosting.AzureFunctions; /// /// Transforms function metadata by registering durable agent functions for each explicitly configured agent. /// /// /// This transformer adds entity, HTTP, and MCP tool trigger functions for agents that have /// explicit . Agents auto-registered by workflows /// (which lack explicit options) are handled by . /// internal sealed class DurableAgentFunctionMetadataTransformer : IFunctionMetadataTransformer { private readonly ILogger _logger; private readonly IReadOnlyDictionary> _agents; private readonly IServiceProvider _serviceProvider; private readonly IFunctionsAgentOptionsProvider _functionsAgentOptionsProvider; public DurableAgentFunctionMetadataTransformer( IReadOnlyDictionary> agents, ILogger logger, IServiceProvider serviceProvider, IFunctionsAgentOptionsProvider functionsAgentOptionsProvider) { this._agents = agents ?? throw new ArgumentNullException(nameof(agents)); this._logger = logger ?? throw new ArgumentNullException(nameof(logger)); this._serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); this._functionsAgentOptionsProvider = functionsAgentOptionsProvider ?? throw new ArgumentNullException(nameof(functionsAgentOptionsProvider)); } public string Name => nameof(DurableAgentFunctionMetadataTransformer); public void Transform(IList original) { this._logger.LogTransformingFunctionMetadata(original.Count); foreach (KeyValuePair> kvp in this._agents) { string agentName = kvp.Key; // Only generate triggers for agents with explicit Functions agent options. // Agents auto-registered by workflows are handled by DurableWorkflowsFunctionMetadataTransformer. if (!this._functionsAgentOptionsProvider.TryGet(agentName, out FunctionsAgentOptions? agentTriggerOptions)) { continue; } this._logger.LogRegisteringTriggerForAgent(agentName, "entity"); original.Add(FunctionMetadataFactory.CreateEntityTrigger(agentName)); if (agentTriggerOptions.HttpTrigger.IsEnabled) { this._logger.LogRegisteringTriggerForAgent(agentName, "http"); original.Add(FunctionMetadataFactory.CreateHttpTrigger(agentName, $"agents/{agentName}/run", BuiltInFunctions.RunAgentHttpFunctionEntryPoint)); } if (agentTriggerOptions.McpToolTrigger.IsEnabled) { AIAgent agent = kvp.Value(this._serviceProvider); this._logger.LogRegisteringTriggerForAgent(agentName, "mcpTool"); original.Add(CreateMcpToolTrigger(agentName, agent.Description)); } } } private static DefaultFunctionMetadata CreateMcpToolTrigger(string agentName, string? description) { return new DefaultFunctionMetadata { Name = $"{BuiltInFunctions.McpToolPrefix}{agentName}", Language = "dotnet-isolated", RawBindings = [ $$"""{"name":"context","type":"mcpToolTrigger","direction":"In","toolName":"{{agentName}}","description":"{{description}}","toolProperties":"[{\"propertyName\":\"query\",\"propertyType\":\"string\",\"description\":\"The query to send to the agent.\",\"isRequired\":true,\"isArray\":false},{\"propertyName\":\"threadId\",\"propertyType\":\"string\",\"description\":\"Optional thread identifier.\",\"isRequired\":false,\"isArray\":false}]"}""", """{"name":"query","type":"mcpToolProperty","direction":"In","propertyName":"query","description":"The query to send to the agent","isRequired":true,"dataType":"String","propertyType":"string"}""", """{"name":"threadId","type":"mcpToolProperty","direction":"In","propertyName":"threadId","description":"The thread identifier.","isRequired":false,"dataType":"String","propertyType":"string"}""", """{"name":"client","type":"durableClient","direction":"In"}""" ], EntryPoint = BuiltInFunctions.RunAgentMcpToolFunctionEntryPoint, ScriptFile = BuiltInFunctions.ScriptFile, }; } }