Skip to content

Comments

.NET: Support InvokeMcpTool for declarative workflows#4204

Open
peibekwe wants to merge 6 commits intomainfrom
peibekwe/declarative-invoke-mcp-tool
Open

.NET: Support InvokeMcpTool for declarative workflows#4204
peibekwe wants to merge 6 commits intomainfrom
peibekwe/declarative-invoke-mcp-tool

Conversation

@peibekwe
Copy link
Contributor

Motivation and Context

This implementation supports the InvokeMcpTool action in a declarative workflow to call configured mcp servers directly from the workflow without going through an AI agent first. This is different from the standard InvokeAzureAgent pattern where the AI decides which functions to call based on the conversation.

Includes tests and samples for the new action type and sample yaml implementations.

Fixes #3415
Fixes #3738

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

@markwallace-microsoft markwallace-microsoft added .NET workflows Related to Workflows in agent-framework labels Feb 24, 2026
@github-actions github-actions bot changed the title Support InvokeMcpTool for declarative workflows .NET: Support InvokeMcpTool for declarative workflows Feb 24, 2026
@peibekwe peibekwe marked this pull request as ready for review February 24, 2026 04:41
Copilot AI review requested due to automatic review settings February 24, 2026 04:41
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for invoking MCP (Model Context Protocol) server tools directly from .NET declarative workflows (without going through an AI agent tool-selection step), including approval handling, result parsing, integration tests, and a Getting Started sample.

Changes:

  • Introduces InvokeMcpToolExecutor plus IMcpToolHandler and a DefaultMcpToolHandler implementation based on the MCP C# SDK.
  • Wires MCP tool handling through workflow options and factory plumbing; adds workflow interpreter support for InvokeMcpTool.
  • Adds unit + integration tests and a new declarative workflow sample demonstrating direct MCP tool invocation.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs New executor implementing InvokeMcpTool execution + approval + output assignment.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/IMcpToolHandler.cs New abstraction for MCP tool invocation used by declarative workflows.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/DefaultMcpToolHandler.cs Default MCP SDK-based handler implementation with client caching and content mapping.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/DeclarativeWorkflowOptions.cs Adds McpToolHandler option to enable MCP tool invocation in workflows.
dotnet/src/Shared/Workflows/Execution/WorkflowFactory.cs Plumbs McpToolHandler through factory-created workflow options.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowActionVisitor.cs Adds interpreter support to build workflow graph nodes/ports for InvokeMcpTool (incl. approval flow).
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Interpreter/WorkflowTemplateVisitor.cs Marks InvokeMcpTool as not supported for template generation (consistent with other tool-invocation actions).
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs Refactors array schema inference to use GetListTypeFromJson extension.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Extensions/JsonDocumentExtensions.cs Adds GetListTypeFromJson and improves nested/empty-array parsing behavior.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs New unit tests covering InvokeMcpTool executor behavior and result parsing scenarios.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Extensions/JsonDocumentExtensionsTests.cs Adds tests for list-type inference from JSON arrays.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/IntegrationTest.cs Extends test options creation to optionally provide an MCP tool handler.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs New integration tests covering InvokeFunctionTool + InvokeMcpTool workflows and approval paths.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeFunctionToolWorkflowTest.cs Removes older InvokeFunctionTool-only integration test (superseded by combined test).
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpTool.yaml New workflow YAML for invoking an MCP tool without approval.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Workflows/InvokeMcpToolWithApproval.yaml New workflow YAML for invoking an MCP tool with approval required.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/Microsoft.Agents.AI.Workflows.Declarative.csproj Adds ModelContextProtocol package dependency for MCP support.
dotnet/Directory.Packages.props Updates workflow-related package versions (and central MCP package version already present).
dotnet/samples/GettingStarted/Workflows/Declarative/InvokeMcpTool/Program.cs New sample showing how to configure McpToolHandler and run an MCP-enabled workflow.
dotnet/samples/GettingStarted/Workflows/Declarative/InvokeMcpTool/InvokeMcpTool.yaml New sample workflow chaining MCP calls and an agent summarization step.
dotnet/samples/GettingStarted/Workflows/Declarative/InvokeMcpTool/InvokeMcpTool.csproj New sample project for the InvokeMcpTool workflow demo.
dotnet/agent-framework-dotnet.slnx Adds the new InvokeMcpTool sample project to the solution.
Comments suppressed due to low confidence (2)

dotnet/samples/GettingStarted/Workflows/Declarative/InvokeMcpTool/Program.cs:63

  • This sample's DefaultAzureCredential warning is less specific than the standard warning used elsewhere in the repo (latency, unintended credential probing, and security risks from fallback mechanisms). Consider aligning the comment text with the established wording so readers get consistent production guidance.
        // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
        DefaultAzureCredential credential = new();
        DefaultMcpToolHandler mcpToolHandler = new(

dotnet/samples/GettingStarted/Workflows/Declarative/InvokeMcpTool/Program.cs:121

  • There is a second DefaultAzureCredential warning here that also doesn’t include the repo-standard details (latency, unintended credential probing, and security risks from fallback mechanisms). Consider using the same warning wording used in other GettingStarted samples for consistency.
        // WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
        AIProjectClient aiProjectClient = new(foundryEndpoint, new DefaultAzureCredential());


/// <inheritdoc/>
[SendsMessage(typeof(ExternalInputRequest))]
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken = default)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question to make sure I understand this properly: ExecuteAsync resolves serverUrl, toolName, arguments, etc. and builds the approval request from them. Then in CaptureResponseAsync (after the user approves), we re-call GetServerUrl(), GetToolName(), GetArguments(), etc. instead of reusing the already-resolved values? If a workflow expression like =Local.SomeValue resolved differently between those two points, the user would have approved parameters that differ from what actually executes. Could this scenario happen?

CancellationToken cancellationToken = default)
{
//TODO: Handle connectionName and server label appropriately when Hosted scenario supports them. For now, ignore
McpServerToolResultContent resultContent = new("McpServerToolcallId");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a hard-coded string?

}

/// <inheritdoc/>
public async Task<McpServerToolResultContent> InvokeToolAsync(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need exception handling around this?

/// <summary>
/// Creates a VariableType.List with schema inferred from the first object element in the array.
/// </summary>
internal static VariableType GetListTypeFromJson(this JsonElement arrayElement)
Copy link
Contributor

@crickman crickman Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - This can be public as the class is internal (or private?)

JsonValueKind.True => typeof(bool),
JsonValueKind.False => typeof(bool),
JsonValueKind.Number => typeof(decimal),
JsonValueKind.Array => (VariableType)VariableType.ListType, // Add support for nested arrays
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder about this. As a private method isnt ParseRecord only invoked when the object type is Record/Object?

/// Integration tests for InvokeFunctionTool and InvokeMcpTool actions.
/// </summary>
public sealed class InvokeFunctionToolWorkflowTest(ITestOutputHelper output) : IntegrationTest(output)
public sealed class InvokeToolWorkflowTest(ITestOutputHelper output) : IntegrationTest(output)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice test update!

/// <summary>
/// Tests for <see cref="InvokeMcpToolExecutor"/>.
/// </summary>
public sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : WorkflowActionExecutorTest(output)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice coverage!

<PackageReference Include="Microsoft.PowerFx.Interpreter" />
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="ModelContextProtocol" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Would it make sense to have a Microsoft.Agents.AI.Workflows.Declarative.Mcp package so that people only pull in ModelContextProtocol when they need it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET workflows Related to Workflows in agent-framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.NET Workflows - Create InvokeMCPTool sample .NET Workflows - Implement InvokeMCPTools support in declarative workflows

5 participants