PR feedback fixes

This commit is contained in:
Shyju Krishnankutty
2026-03-18 14:56:43 -07:00
Unverified
parent c8991857b0
commit 2fe2f4400a
6 changed files with 66 additions and 20 deletions
@@ -24,7 +24,7 @@ The sample creates two workflows exposed as MCP tools:
| Executor | Input | Output | Description |
|----------|-------|--------|-------------|
| **LookupOrder** | `string` | `OrderInfo` | Looks up an order by ID |
| **EnrichOrder** | `OrderInfo` | `OrderSummary` | Adds computed fields (total price, delivery estimate) |
| **EnrichOrder** | `OrderInfo` | `OrderSummary` | Adds computed fields (total price, status) |
## Environment Setup
@@ -76,6 +76,6 @@ For this sample, you'll also need [Node.js](https://nodejs.org/en/download) to u
- Select the `OrderLookup` tool
- Set `ORD-2025-42` as the `input` parameter
- Click **Run Tool**
- Expected result: A JSON object containing order details such as `OrderId`, `CustomerName`, `Product`, `TotalPrice`, `EstimatedDelivery`, and `Status`
- Expected result: A JSON object containing order details such as `OrderId`, `CustomerName`, `Product`, `TotalPrice`, and `Status`
You'll see the workflow executor activities logged in the terminal where you ran `func start`.
@@ -409,7 +409,22 @@ internal static class BuiltInFunctions
getInputsAndOutputs: true,
cancellation: functionContext.CancellationToken);
return metadata?.ReadOutputAs<DurableWorkflowResult>()?.Result;
if (metadata is null)
{
throw new InvalidOperationException($"Workflow orchestration '{instanceId}' returned no metadata.");
}
if (metadata.RuntimeStatus is OrchestrationRuntimeStatus.Failed)
{
throw new InvalidOperationException($"Workflow orchestration '{instanceId}' failed: {metadata.ReadOutputAs<string>()}");
}
if (metadata.RuntimeStatus is not OrchestrationRuntimeStatus.Completed)
{
throw new InvalidOperationException($"Workflow orchestration '{instanceId}' ended with unexpected status '{metadata.RuntimeStatus}'.");
}
return metadata.ReadOutputAs<DurableWorkflowResult>()?.Result;
}
/// <summary>
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json.Nodes;
using Microsoft.Agents.AI.DurableTask;
using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata;
@@ -112,28 +113,49 @@ internal static class FunctionMetadataFactory
var functionName = $"{BuiltInFunctions.McpToolPrefix}{workflowName}";
var toolDescription = description ?? $"Run the {workflowName} workflow";
const string ToolProperties =
"""[{\"propertyName\":\"input\",\"propertyType\":\"string\",\"description\":\"The input to the workflow.\",\"isRequired\":true,\"isArray\":false}]""";
var toolProperties = new JsonArray(new JsonObject
{
["propertyName"] = "input",
["propertyType"] = "string",
["description"] = "The input to the workflow.",
["isRequired"] = true,
["isArray"] = false,
});
var TriggerBinding =
$$"""{"name":"context","type":"mcpToolTrigger","direction":"In","toolName":"{{workflowName}}","description":"{{toolDescription}}","toolProperties":"{{ToolProperties}}"}""";
var triggerBinding = new JsonObject
{
["name"] = "context",
["type"] = "mcpToolTrigger",
["direction"] = "In",
["toolName"] = workflowName,
["description"] = toolDescription,
["toolProperties"] = toolProperties.ToJsonString(),
};
const string InputBinding =
"""{"name":"input","type":"mcpToolProperty","direction":"In","propertyName":"input","description":"The input to the workflow","isRequired":true,"dataType":"String","propertyType":"string"}""";
var inputBinding = new JsonObject
{
["name"] = "input",
["type"] = "mcpToolProperty",
["direction"] = "In",
["propertyName"] = "input",
["description"] = "The input to the workflow",
["isRequired"] = true,
["dataType"] = "String",
["propertyType"] = "string",
};
const string ClientBinding =
"""{"name":"client","type":"durableClient","direction":"In"}""";
var clientBinding = new JsonObject
{
["name"] = "client",
["type"] = "durableClient",
["direction"] = "In",
};
return new DefaultFunctionMetadata
{
Name = functionName,
Language = "dotnet-isolated",
RawBindings =
[
TriggerBinding,
InputBinding,
ClientBinding
],
RawBindings = [triggerBinding.ToJsonString(), inputBinding.ToJsonString(), clientBinding.ToJsonString()],
EntryPoint = BuiltInFunctions.RunWorkflowMcpToolFunctionEntryPoint,
ScriptFile = BuiltInFunctions.ScriptFile,
};
@@ -29,7 +29,7 @@ public static class DurableWorkflowOptionsExtensions
}
/// <summary>
/// Adds a workflow and optionally exposes a status HTTP endpoint and/or an MCP tool trigger.
/// Adds a workflow and configures whether to expose a status HTTP endpoint and/or an MCP tool trigger.
/// </summary>
/// <param name="options">The workflow options to add the workflow to.</param>
/// <param name="workflow">The workflow instance to add.</param>
@@ -264,7 +264,8 @@ public sealed class WorkflowSamplesValidation(ITestOutputHelper outputHelper) :
"Translate",
arguments: new Dictionary<string, object?> { { "input", "hello world" } });
string translateResponse = ((TextContentBlock)translateResult.Content[0]).Text;
Assert.NotEmpty(translateResult.Content);
string translateResponse = Assert.IsType<TextContentBlock>(translateResult.Content[0]).Text;
this._outputHelper.WriteLine($"Translate MCP tool response: {translateResponse}");
Assert.NotEmpty(translateResponse);
Assert.Contains("HELLO WORLD", translateResponse);
@@ -275,7 +276,8 @@ public sealed class WorkflowSamplesValidation(ITestOutputHelper outputHelper) :
"OrderLookup",
arguments: new Dictionary<string, object?> { { "input", "ORD-2025-42" } });
string orderResponse = ((TextContentBlock)orderResult.Content[0]).Text;
Assert.NotEmpty(orderResult.Content);
string orderResponse = Assert.IsType<TextContentBlock>(orderResult.Content[0]).Text;
this._outputHelper.WriteLine($"OrderLookup MCP tool response: {orderResponse}");
Assert.NotEmpty(orderResponse);
Assert.Contains("ORD-2025-42", orderResponse);
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata;
namespace Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests;
@@ -88,6 +89,12 @@ public sealed class FunctionMetadataFactoryTests
Assert.NotNull(metadata.RawBindings);
Assert.Equal(3, metadata.RawBindings.Count);
// Verify all bindings are valid JSON
foreach (string binding in metadata.RawBindings)
{
JsonDocument.Parse(binding);
}
// mcpToolTrigger binding
Assert.Contains("mcpToolTrigger", metadata.RawBindings[0]);
Assert.Contains("\"toolName\":\"Translate\"", metadata.RawBindings[0]);