Skip to content

.NET: [BREAKING] fix(dotnet): validate declarative MCP destinations - #10192

Open
Vincent Biret (baywet) wants to merge 4 commits into
mainfrom
fix/mcp-destination-validation
Open

Vincent Biret (baywet) wants to merge 4 commits into
mainfrom
fix/mcp-destination-validation

Conversation

@baywet

@baywet Vincent Biret (baywet) commented Nov 9, 2026 •

Copy link
Copy Markdown
Member

Motivation & Context

Make declarative MCP destination handling consistent with application-configured HTTP destinations. Applications should configure the servers their workflows use before the default handler creates a client.

Description & Review Guide

  • What are the major changes? Replace the DefaultMcpToolHandler(IHttpClientFactory, string) constructor with DefaultMcpToolHandler(IEnumerable<string>, IHttpMessageHandlerFactory, string), share the existing HTTP host-and-port matcher and transport validation, and reject workflow-supplied Host headers. Update callers, API baselines, guidance, and proposed ADR 0055.
  • What is the impact of these changes? The existing two-argument constructor is removed. Applications must migrate to the constructor accepting their intended hosts, an IHttpMessageHandlerFactory, and the named-client configuration. An empty list denies both tool calls and discovery. The pipeline must terminate in a supported HTTP transport with automatic redirects and cookies disabled; unknown, incomplete, or cyclic pipelines are rejected before sending. HTTP client wrappers and MCP sessions are disposed per invocation without disposing factory-owned pooled handlers. IHttpClientFactory client-configuration callbacks do not apply: move headers and authentication to delegating handlers. Protocol selection and host-matching rules are unchanged.
  • What do you want reviewers to focus on? Default-deny behavior before factory invocation, pre-send transport validation, matching consistency, pooled-handler ownership, and the caller migration. The proposed ADR requires the listed decider's approval.

Related Issue

N/A - maintainer-directed bugfix; no public issue. No matching open pull request was found.

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: Copilot assisted with implementation, regression tests, caller updates, documentation, validation, and this description.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Commands below were run from dotnet/, except formatting and the diff check, which were run from the repository root.

  • dotnet run --project tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests -f net10.0 --no-restore -- --filter-method '*InvokeToolAsync_WithoutAllowedHosts_ShouldRejectBeforeClientCreationAsync' --no-progress — before: 2 failed because client creation occurred; after: 2 passed.
  • dotnet run --project tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests -f net10.0 --no-restore -- --filter-method '*Factory_InvalidTransportSettings_RejectBeforeInitializationAsync' --progress off — before the transport change: 2 failed because both invocations proceeded; after: 2 passed.
  • dotnet run --project tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests -f net10.0 --no-restore -- --filter-method '*Factory_CyclicPipeline_RejectsBeforeSendingAsync' --progress off — before cycle detection: 2 failed because validation did not complete; after: 2 passed.
  • dotnet run --project tests\Microsoft.Agents.AI.Workflows.Declarative.UnitTests -f net10.0 --no-restore -- --filter-method '*SendAsyncRejectsCyclicPipelineBeforeTransportAsync' --progress off — before cycle detection: 2 failed because validation did not complete; after: 2 passed.
  • dotnet run --project tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests -f net10.0 --no-restore -- --progress off — passed after constructor removal, 97 tests, including cycle rejection, 307/308 redirect-response, and pooled-handler coverage. Existing denial regressions now use an explicit empty allowlist; two duplicate empty-policy cases were consolidated.
  • dotnet run --project tests\Microsoft.Agents.AI.Workflows.Declarative.UnitTests -f net10.0 --no-build -- --progress off — passed before constructor removal, 1,377 tests, including all 108 HTTP-handler cases; this follow-up does not change the declarative HTTP implementation.
  • dotnet build src\Microsoft.Agents.AI.Workflows.Declarative.Mcp -c Release --no-restore --tl:off — passed, all target frameworks and package validation; zero warnings or errors.
  • dotnet build samples\03-workflows\Declarative\InvokeMcpTool -f net10.0 --no-restore --tl:off — passed.
  • dotnet build samples\03-workflows\Declarative\InvokeFoundryToolboxMcp -f net10.0 --no-restore --tl:off — passed.
  • dotnet build tests\Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests -f net10.0 --no-restore --tl:off — passed. Credentialed integration tests and sample execution were not run.
  • dotnet format $project --no-restore --include $changed --verbosity minimal — passed with $project set to each of dotnet\src\Microsoft.Agents.AI.Workflows.Declarative, dotnet\src\Microsoft.Agents.AI.Workflows.Declarative.Mcp, dotnet\tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests, dotnet\tests\Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests, dotnet\samples\03-workflows\Declarative\InvokeMcpTool, and dotnet\samples\03-workflows\Declarative\InvokeFoundryToolboxMcp; $changed contained the absolute changed C# paths, including the new shared HTTP helper.
  • dotnet format $project --no-restore --include $changed --verbosity minimal — passed again after cycle detection for dotnet\src\Microsoft.Agents.AI.Workflows.Declarative, dotnet\src\Microsoft.Agents.AI.Workflows.Declarative.Mcp, dotnet\tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests, and dotnet\tests\Microsoft.Agents.AI.Workflows.Declarative.UnitTests; $changed contained the shared validator and both changed test files.
  • dotnet format $project --no-restore --include $changed --verbosity minimal — passed after constructor removal for dotnet\src\Microsoft.Agents.AI.Workflows.Declarative.Mcp and dotnet\tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests; $changed contained the three changed C# files.
  • git diff --check — passed.
  • dotnet build --tl:off — blocked by unavailable, unrelated A2A / A2A.Grpc version 1.0.0-preview4 packages (NU1102; available feed has preview3). The full solution unit suite was not run.
  • dotnet test --project tests\Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests -f net10.0 --filter-query '/*/*/DefaultMcpToolHandlerTests/InvokeToolAsync_WithoutAllowedHosts_ShouldRejectBeforeClientCreationAsync' --no-restore --no-progress --tl:off — blocked by an MTP handshake failure; the documented direct runner above executed the tests successfully.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3333dcdf-889a-4ccc-af0c-4e41fd966d74
@baywet Vincent Biret (baywet) added the breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible label Nov 9, 2026
@baywet
Vincent Biret (baywet) requested review from westey (westey-m) and a balanced review from Copilot November 9, 2026 12:18
@github-actions github-actions Bot changed the title fix(dotnet): [BREAKING] validate declarative MCP destinations [BREAKING] fix(dotnet): validate declarative MCP destinations Nov 9, 2026
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net workflows Usage: [Issues, PRs], Target: Workflows labels Nov 9, 2026
@github-actions github-actions Bot changed the title [BREAKING] fix(dotnet): validate declarative MCP destinations .NET: [BREAKING] fix(dotnet): validate declarative MCP destinations Nov 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Proposed ADR 0055 introduces a breaking, security-sensitive destination-authorization contract that requires PR approval from its designated architectural decider, westey-m.

No open issues 🎉

What changed in this PR

This PR makes .NET declarative MCP destinations application-controlled, using the same host-and-port matching policy as declarative HTTP requests. The existing constructor remains available but now denies all destinations until callers migrate to the allowlist overload.

Changes:

  • Add destination validation before client creation and reject workflow-supplied Host headers.
  • Share HTTP host normalization and extend tests for denial, matching, and copied configuration.
  • Migrate samples and integration tests; update API baselines, guidance, and proposed ADR 0055.
File Description
skills/​agent-framework-builder-dotnet/​references/​declarative.md Emphasizes application-owned destination policy.
dotnet/​tests/​Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/​DefaultMcpToolHandlerTests.cs Tests default denial, invalid hosts, and Host header rejection.
dotnet/​tests/​Microsoft.Agents.AI.Workflows.Declarative.Mcp.UnitTests/​DefaultMcpToolHandlerLifetimeTests.cs Migrates lifetime tests and covers matching and configuration copying.
dotnet/​tests/​Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/​InvokeToolWorkflowTest.cs Explicitly allows the integration-test destination.
dotnet/​src/​Shared/​Http/​AllowedHttpHosts.cs Extracts shared host-and-port validation and matching.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative/​README.md Documents destination policy and constructor migration.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative/​DefaultHttpRequestHandler.cs Uses the shared matcher without changing matching rules.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative.Mcp/​PublicAPI/​netstandard2.0/​PublicAPI.Unshipped.txt Records the allowlist constructor for .NET Standard 2.0.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative.Mcp/​PublicAPI/​net9.0/​PublicAPI.Unshipped.txt Records the allowlist constructor for .NET 9.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative.Mcp/​PublicAPI/​net8.0/​PublicAPI.Unshipped.txt Records the allowlist constructor for .NET 8.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative.Mcp/​PublicAPI/​net472/​PublicAPI.Unshipped.txt Records the allowlist constructor for .NET Framework 4.7.2.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative.Mcp/​PublicAPI/​net10.0/​PublicAPI.Unshipped.txt Records the allowlist constructor for .NET 10.
dotnet/​src/​Microsoft.Agents.AI.Workflows.Declarative.Mcp/​DefaultMcpToolHandler.cs Adds the allowlist overload and pre-client destination checks.
dotnet/​samples/​03-workflows/​Declarative/​InvokeMcpTool/​Program.cs Configures explicit sample destinations.
dotnet/​samples/​03-workflows/​Declarative/​InvokeFoundryToolboxMcp/​Program.cs Allows the configured toolbox endpoint’s authority.
docs/​decisions/​0055-dotnet-declarative-mcp-allowed-hosts.md Proposes the default-deny policy and documents compatibility trade-offs.

Review effort: Balanced

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (1 commit(s)): 71fde3437c4d
Model: gpt-5.6-sol

Overview

The PR introduces default-deny, exact host-and-port authorization for declarative MCP calls, rejects workflow-supplied Host overrides, and preserves per-invocation cleanup. Its direct-destination and lifecycle tests provide strong coverage, but the authorization check does not constrain automatic redirects in the application-provided HTTP pipeline. As a result, a redirect can escape the newly introduced destination boundary.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 high) across 1 file. Details are attached to the affected lines below.

Affected areas: dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs

@github-code-quality

github-code-quality Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage overview

Languages: C#

C# / code-coverage/dotnet

main 056484b fix/mcp-destination-... 66993a1 +/-
86% 86% 0%
View coverage of the 5 most impacted files.
File main 056484b fix/mcp-destination-... 66993a1 +/-
/home/runner/wo...SessionStore.cs 100% 56% -44%
/home/runner/wo...questHandler.cs 96% 95% -1%
/home/runner/wo...pToolHandler.cs 83% 83% 0%
/home/runner/wo...rtValidation.cs 0% 64% +64%
/home/runner/wo...wedHttpHosts.cs 0% 65% +65%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated November 10, 2026 19:20 UTC

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3333dcdf-889a-4ccc-af0c-4e41fd966d74

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Proposed ADR 0055 introduces breaking destination-authorization and HTTP-factory changes that require approval from its listed decider, westey-m.

No open issues 🎉

Review effort: Balanced

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

MAF Automated Review — Iteration 2

Result: Findings reported
Scope: 1 net-new commit(s): 5e6ed6ef2406
Model: gpt-5.6-sol

Overview

The incremental change hardens declarative MCP destination enforcement by requiring application-owned handler pipelines with redirects and cookies disabled, while preserving default-deny behavior and per-invocation cleanup. Regression tests cover redirect responses, malformed pipelines, and pooled-handler ownership. One Medium issue remains: transport validation can spin indefinitely on a cyclic delegating-handler chain.

Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: dotnet/src/Shared/Http/HttpTransportValidation.cs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3333dcdf-889a-4ccc-af0c-4e41fd966d74

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

ADR 0055 remains proposed, and its breaking destination-authorization and HTTP-factory contract changes require approval from listed decider westey-m for the architectural and security trade-offs.

No open issues 🎉

Review effort: Balanced

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3333dcdf-889a-4ccc-af0c-4e41fd966d74

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The PR description incorrectly promises to retain the two-argument constructor, while the implementation removes it and the README and ADR document that breaking change.

1 open issue

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The breaking public-constructor replacement and security-sensitive destination-authorization changes require approval from westey-m, the listed decider for proposed ADR 0055.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

This branch was successfully deployed

1 active and 1 inactive deployments
github-app-auth — 66993a14 Deployed Nov 10, 2026 by baywet via add_label #27536
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Usage: [PRs], Target: all PRs that introduce changes that are not backward compatible documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net workflows Usage: [Issues, PRs], Target: Workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants