microsoft / microsoft/agent-governance-toolkit

Introduce an IResponseSanitizer seam so MCP response sanitization can be replaced or extended

Open
#3,776 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
6.3k
Forks
1.1k
Avg merge
5d 11h
Merged PRs (30d)
142

Description

## Summary

`McpResponseSanitizer` is `sealed` with no interface, and `McpGovernanceRuntime` depends on the concrete type. There's no supported way to replace or extend response sanitization with a custom implementation. Could the toolkit introduce an `IResponseSanitizer` abstraction that `McpGovernanceRuntime` depends on, resolved from DI?

Related: #3775 (async `PolicyEngine`) — same theme of making the toolkit's seams pluggable for custom/external backends.

## Current behavior (v5.0.0)

- `McpResponseSanitizer` is `public sealed`, exposing `McpSanitizedResponse ScanText(string text)`, with no interface.
- `McpGovernanceRuntime` takes the concrete `McpResponseSanitizer` and calls it in `Sanitize(...)`.
- `WithGovernance` registers it via `TryAddSingleton()`.

Because the dependency is the concrete sealed type, a consumer can't substitute their own sanitizer or run an additional one through the same pipeline.

## Why it's a problem

We're building a custom response sanitizer backed by a managed service that redacts additional categories (for example, a broad set of PII entity types) beyond what the built-in sanitizer covers (prompt-injection tags, imperative phrasing, exfiltration URLs, and credentials). With no seam, our only option is to layer a **second** `IPostConfigureOptions` that wraps every tool in another decorator, parallel to the toolkit's own. That works, but:

- It double-wraps tools and duplicates the decorator plumbing.
- Ordering between the two sanitizers is implicit.
- It can't cleanly *replace* the built-in sanitizer, only run beside it.

## Proposed solution

Introduce an interface the runtime depends on:

```csharp
public interface IResponseSanitizer
{
McpSanitizedResponse ScanText(string text);
}
```

- `McpResponseSanitizer` implements `IResponseSanitizer` (no behavior change).
- `McpGovernanceRuntime` depends on `IResponseSanitizer`; `WithGovernance` registers the default via `TryAddSingleton()`, so a consumer can replace it.
- Optionally support composition (an ordered set of `IResponseSanitizer`) so custom sanitizers run alongside the built-in one through a single pipeline.

## Alternatives considered

- **A parallel post-configure decorator** (current workaround): functional but double-wraps tools and can only layer beside the built-in sanitizer, not replace it.

## Willing to contribute

Happy to open a PR (interface extraction + DI registration, optionally the composition model) if the maintainers are open to it. Wanted to align on the shape first.

Contributor guide

Open the contributing guide

Research direction

Start with McpResponseSanitizer, McpGovernanceRuntime, and the WithGovernance registration described in the issue. Trace how Sanitize is called and how the current singleton is resolved from DI, then determine whether replacement alone or ordered composition is in scope. Done means the runtime uses the abstraction, the default behavior remains unchanged, and consumers can replace the sanitizer through DI.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend-api-design, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.