mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
PR feedback fixes
This commit is contained in:
+2
-2
@@ -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,
|
||||
};
|
||||
|
||||
+1
-1
@@ -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>
|
||||
|
||||
+4
-2
@@ -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);
|
||||
|
||||
+7
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user