Skip to content

Connect: Microsoft Agent Framework (.NET)

Recipe · through our MCP server · their docs checked 2026-10-30

MAF .NET uses the official MCP C# SDK. Your process opens an MCP client to our server, lists the tools and hands them to the agent as AITools. The calls go from your process to us; Foundry never sees our URL. (For a Foundry-hosted agent, see Azure AI Foundry.)

Our side

def public_app(tools: Registry, host: str, agent_token: str, reviewer_token: str) -> Any:
    """REST at /, MCP at /mcp, one bearer check for both: for a host on the internet."""
    from mcp.server.transport_security import TransportSecuritySettings

    from agent_adapter.server import create_app

    tokens = {
        agent_token: Principal("hosted-agent"),  # the token the remote framework sends
        reviewer_token: Principal("reviewer@example.com", scopes=frozenset({APPROVER_SCOPE})),
    }
    security = TransportSecuritySettings(allowed_hosts=[host], allowed_origins=[f"https://{host}"])
    return create_app(
        tools,
        authenticate=bearer_auth(tokens),
        host="0.0.0.0",  # a public bind: the public-host checks apply
        allowed_hosts=[host],  # REST: any other Host header gets 421
        mcp_options={"transport_security": security},  # MCP: the same rule
    )

In development on one machine, local_app() and http://127.0.0.1:8000/mcp are enough.

Their side

Packages: ModelContextProtocol and Microsoft.Agents.AI.Foundry (or another model provider).

using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;

await using var mcpClient = await McpClient.CreateAsync(new HttpClientTransport(new()
{
    Endpoint = new Uri("https://tools.example.com/mcp"),
    TransportMode = HttpTransportMode.StreamableHttp,
    AdditionalHeaders = new Dictionary<string, string>
    {
        ["Authorization"] = $"Bearer {Environment.GetEnvironmentVariable("AA_AGENT_TOKEN")}",
    },
}));
var mcpTools = await mcpClient.ListToolsAsync();
var safeTools = mcpTools.Where(t => t.Name != "send_invoice");  // confirm tools stay out

AIAgent agent = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential())
    .AsAIAgent(
        model: deploymentName,
        instructions: "You answer customer-support questions.",
        tools: [.. safeTools.Cast<AITool>()]);
Name Meaning
HttpClientTransportOptions.Endpoint our /mcp URL (required)
TransportMode = HttpTransportMode.StreamableHttp skip the SSE fallback that AutoDetect tries
AdditionalHeaders sent on every request: the bearer token
ListToolsAsync() our tools as McpClientTool (an AIFunction)
AsAIAgent(model:, instructions:, tools:) the agent with those tools (any MAF chat client works the same way)

Checklist

Check Why
leave confirm tools out of tools, or serve hosted MAF does not send our resume keys back; a parked call never runs
MAF's own approval modes are extra it adds a gate; our executor still decides confirm tools
one McpClient per principal the header is the identity: do not share a client between users
everything on the public-host checklist

Sources