Microsoft Agent Framework Channels Make Agent Hosting an Adapter Problem

Microsoft's new Agent Framework channel helpers show a pragmatic way to expose one agent through Responses, Telegram, A2A, and MCP without surrendering application ownership.

Microsoft published a new Agent Framework post on August 26, 2026 introducing channels for agents and workflows in Python. The announcement is easy to read as another integration list: OpenAI Responses, Telegram, A2A, MCP. The more interesting part is the architecture decision underneath it.

Microsoft is not trying to hide the application behind a new agent server. It is giving developers protocol helpers and a small shared state model, while leaving routing, authentication, authorization, storage, deployment, and policy in the application. That is the right default for production agent systems.

What Happened

On August 26, Microsoft introduced "agent and workflow channels" for Microsoft Agent Framework. The new Python packages sit around a common hosting core:

  • agent-framework-hosting for shared agent, workflow, and session-state helpers.
  • agent-framework-hosting-responses for converting OpenAI Responses requests and results.
  • agent-framework-hosting-telegram for Telegram updates and outbound operations.
  • agent-framework-hosting-a2a for exposing agents and workflows through native A2A SDK types.
  • agent-framework-hosting-mcp for exposing agents and workflows as native MCP tools.

The framing matters. Microsoft says each package focuses on its protocol boundary, while application code keeps using the web framework or native SDK it already chose. The app remains responsible for routes, auth, storage, background processing, and deployment.

The associated Microsoft Learn pages, last updated on August 25, make the same separation explicit. Hosting is split into two independent decisions. First, choose who operates the infrastructure: Microsoft-managed Foundry Hosted Agents or self-hosting in your own application, container, service, or runtime. Then choose the protocol clients should use: Responses, A2A, AG-UI, Telegram, MCP, or something else supported by the framework.

There is an important lifecycle caveat. Microsoft Learn says Microsoft Foundry Hosted Agents is generally available, but the current Python self-hosting packages are prerelease. For production systems, that means pin versions and read release notes before updates.

The GitHub tracking issue behind the work is also worth reading. Issue #6265 describes the accepted direction as "helper-first": protocol packages convert protocol-native input and output into Agent Framework run values and native responses or events. The old broader host/channel registry model is not the v1 boundary. That is not just implementation detail. It is the product's opinion about where responsibility should sit.

Why It Matters

Agent hosting is a messy backend problem because the same agent often needs to appear in several places at once.

One team wants an OpenAI-compatible endpoint because existing clients and evaluation tooling already speak that shape. Another team wants the same agent reachable as an MCP tool so a coding assistant can call it. A support group wants a Telegram or chat-style channel. A multi-agent system wants A2A discovery and task handoff. The naive implementation is to wrap the same business logic four times, with four different session models, four different auth checks, and four subtly different request policies.

That is how agent systems drift. The model prompt changes in one surface but not another. Tool approval rules differ by endpoint. One wrapper persists state by user ID, another by response ID, and a third accidentally trusts a client-provided continuation token.

Microsoft's channel design addresses a narrow but important part of that problem. It gives teams a way to keep one agent or workflow target and expose it through multiple protocol adapters. The adapters do the tedious translation: parse a Responses request into messages and options, convert a completed run back into a protocol-native result, derive an MCP tool from an agent, or map a Telegram update into an Agent Framework run. The application still owns the security and operational boundary.

That ownership is not a burden to avoid. It is the part mature teams should want to keep.

Authentication is application-specific. A Telegram chat ID, an enterprise SSO subject, an MCP client identity, and a Responses conversation_id are not interchangeable proofs of authority. Authorization depends on tenant, workspace, tool class, data classification, and whether the caller is asking for a new action or continuing existing state. Durable storage varies too: Redis, Postgres, blob-backed snapshots, append-only audit logs, or no continuity at all.

The useful abstraction is not "let the agent framework own all of that." The useful abstraction is "make protocol conversion boring enough that the application can focus on the boundary it actually owns."

How It Works

The shared Python hosting layer introduces two central state concepts: AgentState and WorkflowState.

AgentState pairs an agent target with a session store. The application chooses a session ID, asks AgentState for the corresponding AgentSession, runs the agent, then saves the updated state. The channel helpers do not decide how a Telegram user, MCP caller, or Responses conversation maps to that session ID. Microsoft's post is explicit that identity-to-session mapping is illustrative application code, not a framework-provided function.

WorkflowState performs the same kind of target resolution for workflows. It can resolve a workflow instance, factory, or builder. For workflows, checkpoint storage and the mapping from protocol continuation IDs to checkpoints remain application-owned contracts.

The protocol packages then sit at the edge.

For Responses, helper functions convert an incoming request body into Agent Framework run values, extract session-related identifiers, create response IDs, and convert the result back to Responses format. The runnable FastAPI sample demonstrates an application-owned route that decides which caller-supplied options are allowed. In that sample, the app allows max_tokens, overrides reasoning settings, and denies options such as model, temperature, store, tools, and tool_choice by default.

That pattern is more important than the exact option list. A production endpoint should not blindly pass every client-controlled field into the agent runtime. If the caller can choose the model, tools, storage behavior, or tool-choice mode, the caller is influencing cost, retention, and action authority. A protocol adapter should make conversion easy; it should not erase request policy.

For MCP, Microsoft's sample shows the same approach from the other direction. An MCP server that exposes tools must support tools/list and tools/call. The sample describes both low-level handlers and FastMCP-style derived schemas. The Agent Framework package provides conversion functions such as mcp_to_run(...) and mcp_from_run(...), plus adapters such as AgentMCPTool and WorkflowMCPTool. Application code still owns the MCP server, handler registration, transport, authentication, authorization, and deployment.

That is a clean boundary. MCP remains MCP. FastAPI remains FastAPI. Agent Framework owns the translation between protocol-native shapes and the agent or workflow target.

The docs also call out session continuation as a security problem. Treat every protocol-provided identifier as untrusted input before using it to load a session, checkpoint, task, or any other state. Authenticate the caller, authorize access to the referenced state, partition durable state by tenant or user, and persist session or checkpoint state only after the run or stream has completed.

That advice should be standard in agent platforms. A continuation ID is a pointer. It is not proof that the caller is allowed to read or mutate the state behind that pointer.

Example

Imagine an internal engineering assistant that can answer release questions, inspect runbooks, and open follow-up tasks. The team wants three surfaces:

  • A /responses endpoint for internal automation and evaluation jobs.
  • An MCP tool so coding agents can ask the assistant about service ownership and deployment policy.
  • A Telegram bot for on-call engineers who need quick answers from a phone.

The bad implementation creates three separate wrappers around the same model prompt and tools. Each wrapper invents its own session key and each one decides independently which request options are safe.

A better implementation keeps one target and makes the protocol boundary explicit:

agent_state = AgentState(engineering_agent, session_store=durable_store)

async def responses_route(body, authenticated_user):
    run = responses_to_run(body)
    requested_session = responses_session_id(body)
    session_key = authorize_responses_session(authenticated_user, requested_session)
    session = await agent_state.get_or_create_session(session_key)

    options = allow_only(run["options"], ["max_tokens"])
    result = await engineering_agent.run(run["messages"], session=session, options=options)

    await agent_state.set_session(session_key, session)
    return responses_from_run(result, response_id=create_response_id())

The MCP path can expose the same agent as one tool:

mcp_tool = AgentMCPTool(
    agent_state,
    name="engineering_assistant",
    description="Answer internal engineering and release-process questions."
)

async def call_tool(request, authenticated_client):
    authorize_mcp_tool_call(authenticated_client, request.name)
    return await mcp_tool.call(request.arguments)

The real production work is hidden in the functions with uncomfortable names: authorize_responses_session, allow_only, and authorize_mcp_tool_call. That is exactly where it belongs. The protocol helper can normalize requests, but it cannot know your tenant model, data classifications, cost controls, or incident-response rules.

For a workflow instead of a single agent, the same pattern applies. Use WorkflowState to resolve the target. Let the Responses or MCP helper convert protocol input into workflow input. Keep checkpoint storage and continuation authorization in your app. If a caller supplies a previous response ID or checkpoint ID, treat it as an untrusted lookup key until the caller is authenticated and authorized for the underlying state.

This also helps testing. Test the agent target once, then test each adapter as a boundary:

Responses request -> run values -> policy filter -> agent.run
MCP tool call -> run values -> policy check -> agent.run
Telegram update -> run values -> per-chat ordering -> agent.run

The tests should include denied options, invalid continuation IDs, cross-tenant session attempts, interrupted streams, and concurrent updates to the same conversation head. Those are normal backend failure modes.

My Take

This is the right kind of framework feature because it avoids pretending the hard part is just invoking an LLM.

Agent demos optimize for the shortest path from prompt to response. Production systems optimize for boundaries: who is calling, what state they can resume, which tools are visible, which model options are allowed, how costs are capped, where logs go, and how failures are retried. A framework that owns all of those choices too early usually becomes either too rigid for real applications or too permissive for safe defaults.

Microsoft's helper-first direction is less magical, but it is more honest. It says: here is the conversion layer; your application still owns the application. That is a good trade.

It also fits where the ecosystem is going. Agents increasingly need to be multi-protocol. MCP is becoming a tool boundary. A2A is becoming a way to expose agent capabilities to other agents. Responses-compatible endpoints are useful because clients, evals, and existing service code already know how to call them. Messaging channels still matter because users do not live inside agent consoles.

Trying to make one runtime own all of those surfaces will usually produce an abstraction that leaks at the first serious security review. A protocol helper package has a better chance of aging well. Protocols can evolve independently. Applications can adopt one adapter at a time. Teams can keep their existing FastAPI, Django, Flask, ASP.NET, Azure Functions, or native protocol SDK stack instead of migrating into a new agent-hosting universe.

The caution is that this gives developers enough rope to build sharp systems. If your app owns the session mapping, your app can get it wrong. If your route decides allowed model options, your route can allow too much. If your durable store is process-local, every restart loses continuity. If two callers advance the same conversation head concurrently, state gets inconsistent.

That is not a flaw in the design. It is the consequence of choosing explicit application ownership. The right response is to build the missing backend controls deliberately: scoped session keys, tenant-partitioned storage, optimistic concurrency or per-session locking, request option allowlists, tool authorization, structured audit events, and adapter-level conformance tests.

I would also be careful with production rollout because the Python self-hosting packages are still prerelease. Depending on your risk tolerance, production use may require pinned versions, adapter compatibility tests, and a rollback path.

Conclusion

Microsoft's August 26 Agent Framework channel announcement is not important because it adds a few more integrations. It is important because it draws a practical line between protocol conversion and application responsibility.

Experienced engineers should read it as a backend architecture signal. The agent or workflow should be reusable. The protocol adapters should be thin and testable. The application should keep control of identity, authorization, routing, storage, request policy, deployment, and failure handling.

That shape is less glamorous than a fully managed agent runtime, but it is closer to how production systems survive contact with real users, real tenants, and real audits. Agent hosting is becoming another distributed-systems surface. Treating channels as adapters, not as a replacement for the application boundary, is a useful step.

Sources

Enjoying this article?

Subscribe to get new NestJS, Node.js, and backend engineering posts directly in your inbox.

Subscribe for free