Skip to content

Commit 9065c6f

Browse files
feat: Support the Streamable HTTP transport for MCP (#6615)
<!-- Thank you for your contribution! Please review https://microsoft.github.io/autogen/docs/Contribute before opening a pull request. --> <!-- Please add a reviewer to the assignee section when you create a PR. If you don't have the access to it, we will shortly find a reviewer and assign them to your PR. --> ## Why are these changes needed? MCP Python-sdk has started to support a new transport protocol named `Streamble HTTP` since [v1.8.0](https://github.com/modelcontextprotocol/python-sdk/releases/tag/v1.8.0) last month. I heard it supersedes the SSE transport. Therefore, AutoGen have to support it as soon as possible. ## Related issue number #6517 ## Checks - [x] I've included any doc changes needed for <https://microsoft.github.io/autogen/>. See <https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to build and test documentation locally. - [x] I've added tests (if relevant) corresponding to the changes introduced in this PR. - [x] I've made sure all auto checks have passed. --------- Co-authored-by: Victor Dibia <victordibia@microsoft.com> Co-authored-by: Victor Dibia <victor.dibia@gmail.com>
1 parent 1858799 commit 9065c6f

File tree

9 files changed

+325
-21
lines changed

9 files changed

+325
-21
lines changed

python/packages/autogen-ext/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ semantic-kernel-all = [
144144

145145
rich = ["rich>=13.9.4"]
146146

147-
mcp = ["mcp>=1.6.0"]
147+
mcp = ["mcp>=1.8.1"]
148148
canvas = [
149149
"unidiff>=0.7.5",
150150
]

python/packages/autogen-ext/src/autogen_ext/tools/mcp/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from ._actor import McpSessionActor
2-
from ._config import McpServerParams, SseServerParams, StdioServerParams
2+
from ._config import McpServerParams, SseServerParams, StdioServerParams, StreamableHttpServerParams
33
from ._factory import mcp_server_tools
44
from ._session import create_mcp_server_session
55
from ._sse import SseMcpToolAdapter
66
from ._stdio import StdioMcpToolAdapter
7+
from ._streamable_http import StreamableHttpMcpToolAdapter
78
from ._workbench import McpWorkbench
89

910
__all__ = [
@@ -13,6 +14,8 @@
1314
"StdioServerParams",
1415
"SseMcpToolAdapter",
1516
"SseServerParams",
17+
"StreamableHttpMcpToolAdapter",
18+
"StreamableHttpServerParams",
1619
"McpServerParams",
1720
"mcp_server_tools",
1821
"McpWorkbench",

python/packages/autogen-ext/src/autogen_ext/tools/mcp/_config.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import timedelta
12
from typing import Any, Literal
23

34
from mcp import StdioServerParameters
@@ -18,10 +19,24 @@ class SseServerParams(BaseModel):
1819

1920
type: Literal["SseServerParams"] = "SseServerParams"
2021

21-
url: str
22-
headers: dict[str, Any] | None = None
23-
timeout: float = 5
24-
sse_read_timeout: float = 60 * 5
22+
url: str # The SSE endpoint URL.
23+
headers: dict[str, Any] | None = None # Optional headers to include in requests.
24+
timeout: float = 5 # HTTP timeout for regular operations.
25+
sse_read_timeout: float = 60 * 5 # Timeout for SSE read operations.
2526

2627

27-
McpServerParams = Annotated[StdioServerParams | SseServerParams, Field(discriminator="type")]
28+
class StreamableHttpServerParams(BaseModel):
29+
"""Parameters for connecting to an MCP server over Streamable HTTP."""
30+
31+
type: Literal["StreamableHttpServerParams"] = "StreamableHttpServerParams"
32+
33+
url: str # The endpoint URL.
34+
headers: dict[str, Any] | None = None # Optional headers to include in requests.
35+
timeout: timedelta = timedelta(seconds=30) # HTTP timeout for regular operations.
36+
sse_read_timeout: timedelta = timedelta(seconds=60 * 5) # Timeout for SSE read operations.
37+
terminate_on_close: bool = True
38+
39+
40+
McpServerParams = Annotated[
41+
StdioServerParams | SseServerParams | StreamableHttpServerParams, Field(discriminator="type")
42+
]

python/packages/autogen-ext/src/autogen_ext/tools/mcp/_factory.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
from mcp import ClientSession
22

3-
from ._config import McpServerParams, SseServerParams, StdioServerParams
3+
from ._config import McpServerParams, SseServerParams, StdioServerParams, StreamableHttpServerParams
44
from ._session import create_mcp_server_session
55
from ._sse import SseMcpToolAdapter
66
from ._stdio import StdioMcpToolAdapter
7+
from ._streamable_http import StreamableHttpMcpToolAdapter
78

89

910
async def mcp_server_tools(
1011
server_params: McpServerParams,
1112
session: ClientSession | None = None,
12-
) -> list[StdioMcpToolAdapter | SseMcpToolAdapter]:
13+
) -> list[StdioMcpToolAdapter | SseMcpToolAdapter | StreamableHttpMcpToolAdapter]:
1314
"""Creates a list of MCP tool adapters that can be used with AutoGen agents.
1415
1516
This factory function connects to an MCP server and returns adapters for all available tools.
@@ -26,14 +27,14 @@ async def mcp_server_tools(
2627
Args:
2728
server_params (McpServerParams): Connection parameters for the MCP server.
2829
Can be either StdioServerParams for command-line tools or
29-
SseServerParams for HTTP/SSE services.
30+
SseServerParams and StreamableHttpServerParams for HTTP/SSE services.
3031
session (ClientSession | None): Optional existing session to use. This is used
3132
when you want to reuse an existing connection to the MCP server. The session
3233
will be reused when creating the MCP tool adapters.
3334
3435
Returns:
35-
list[StdioMcpToolAdapter | SseMcpToolAdapter]: A list of tool adapters ready to use
36-
with AutoGen agents.
36+
list[StdioMcpToolAdapter | SseMcpToolAdapter | StreamableHttpMcpToolAdapter]:
37+
A list of tool adapters ready to use with AutoGen agents.
3738
3839
Examples:
3940
@@ -200,4 +201,9 @@ async def main() -> None:
200201
return [StdioMcpToolAdapter(server_params=server_params, tool=tool, session=session) for tool in tools.tools]
201202
elif isinstance(server_params, SseServerParams):
202203
return [SseMcpToolAdapter(server_params=server_params, tool=tool, session=session) for tool in tools.tools]
204+
elif isinstance(server_params, StreamableHttpServerParams):
205+
return [
206+
StreamableHttpMcpToolAdapter(server_params=server_params, tool=tool, session=session)
207+
for tool in tools.tools
208+
]
203209
raise ValueError(f"Unsupported server params type: {type(server_params)}")

python/packages/autogen-ext/src/autogen_ext/tools/mcp/_session.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
from mcp import ClientSession
66
from mcp.client.sse import sse_client
77
from mcp.client.stdio import stdio_client
8+
from mcp.client.streamable_http import streamablehttp_client
89

9-
from ._config import McpServerParams, SseServerParams, StdioServerParams
10+
from ._config import McpServerParams, SseServerParams, StdioServerParams, StreamableHttpServerParams
1011

1112

1213
@asynccontextmanager
@@ -24,5 +25,22 @@ async def create_mcp_server_session(
2425
yield session
2526
elif isinstance(server_params, SseServerParams):
2627
async with sse_client(**server_params.model_dump(exclude={"type"})) as (read, write):
27-
async with ClientSession(read_stream=read, write_stream=write) as session:
28+
async with ClientSession(
29+
read_stream=read,
30+
write_stream=write,
31+
read_timeout_seconds=timedelta(seconds=server_params.sse_read_timeout),
32+
) as session:
33+
yield session
34+
elif isinstance(server_params, StreamableHttpServerParams):
35+
async with streamablehttp_client(**server_params.model_dump(exclude={"type"})) as (
36+
read,
37+
write,
38+
session_id_callback, # type: ignore[assignment, unused-variable]
39+
):
40+
# TODO: Handle session_id_callback if needed
41+
async with ClientSession(
42+
read_stream=read,
43+
write_stream=write,
44+
read_timeout_seconds=server_params.sse_read_timeout,
45+
) as session:
2846
yield session
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
from autogen_core import Component
2+
from mcp import ClientSession, Tool
3+
from pydantic import BaseModel
4+
from typing_extensions import Self
5+
6+
from ._base import McpToolAdapter
7+
from ._config import StreamableHttpServerParams
8+
9+
10+
class StreamableHttpMcpToolAdapterConfig(BaseModel):
11+
"""Configuration for the MCP tool adapter."""
12+
13+
server_params: StreamableHttpServerParams
14+
tool: Tool
15+
16+
17+
class StreamableHttpMcpToolAdapter(
18+
McpToolAdapter[StreamableHttpServerParams],
19+
Component[StreamableHttpMcpToolAdapterConfig],
20+
):
21+
"""
22+
Allows you to wrap an MCP tool running over Streamable HTTP and make it available to AutoGen.
23+
24+
This adapter enables using MCP-compatible tools that communicate over Streamable HTTP
25+
with AutoGen agents. Common use cases include integrating with remote MCP services,
26+
cloud-based tools, and web APIs that implement the Model Context Protocol (MCP).
27+
28+
.. note::
29+
30+
To use this class, you need to install `mcp` extra for the `autogen-ext` package.
31+
32+
.. code-block:: bash
33+
34+
pip install -U "autogen-ext[mcp]"
35+
36+
37+
Args:
38+
server_params (StreamableHttpServerParams): Parameters for the MCP server connection,
39+
including URL, headers, and timeouts.
40+
tool (Tool): The MCP tool to wrap.
41+
session (ClientSession, optional): The MCP client session to use. If not provided,
42+
it will create a new session. This is useful for testing or when you want to
43+
manage the session lifecycle yourself.
44+
45+
Examples:
46+
Use a remote translation service that implements MCP over Streamable HTTP to
47+
create tools that allow AutoGen agents to perform translations:
48+
49+
.. code-block:: python
50+
51+
import asyncio
52+
from datetime import timedelta
53+
from autogen_ext.models.openai import OpenAIChatCompletionClient
54+
from autogen_ext.tools.mcp import StreamableHttpMcpToolAdapter, StreamableHttpServerParams
55+
from autogen_agentchat.agents import AssistantAgent
56+
from autogen_agentchat.ui import Console
57+
from autogen_core import CancellationToken
58+
59+
60+
async def main() -> None:
61+
# Create server params for the remote MCP service
62+
server_params = StreamableHttpServerParams(
63+
url="https://api.example.com/mcp",
64+
headers={"Authorization": "Bearer your-api-key", "Content-Type": "application/json"},
65+
timeout=timedelta(seconds=30),
66+
sse_read_timeout=timedelta(seconds=60 * 5),
67+
terminate_on_close=True,
68+
)
69+
70+
# Get the translation tool from the server
71+
adapter = await StreamableHttpMcpToolAdapter.from_server_params(server_params, "translate")
72+
73+
# Create an agent that can use the translation tool
74+
model_client = OpenAIChatCompletionClient(model="gpt-4")
75+
agent = AssistantAgent(
76+
name="translator",
77+
model_client=model_client,
78+
tools=[adapter],
79+
system_message="You are a helpful translation assistant.",
80+
)
81+
82+
# Let the agent translate some text
83+
await Console(
84+
agent.run_stream(task="Translate 'Hello, how are you?' to Spanish", cancellation_token=CancellationToken())
85+
)
86+
87+
88+
if __name__ == "__main__":
89+
asyncio.run(main())
90+
91+
"""
92+
93+
component_config_schema = StreamableHttpMcpToolAdapterConfig
94+
component_provider_override = "autogen_ext.tools.mcp.StreamableHttpMcpToolAdapter"
95+
96+
def __init__(
97+
self, server_params: StreamableHttpServerParams, tool: Tool, session: ClientSession | None = None
98+
) -> None:
99+
super().__init__(server_params=server_params, tool=tool, session=session)
100+
101+
def _to_config(self) -> StreamableHttpMcpToolAdapterConfig:
102+
"""
103+
Convert the adapter to its configuration representation.
104+
105+
Returns:
106+
StreamableHttpMcpToolAdapterConfig: The configuration of the adapter.
107+
"""
108+
return StreamableHttpMcpToolAdapterConfig(server_params=self._server_params, tool=self._tool)
109+
110+
@classmethod
111+
def _from_config(cls, config: StreamableHttpMcpToolAdapterConfig) -> Self:
112+
"""
113+
Create an instance of StreamableHttpMcpToolAdapter from its configuration.
114+
115+
Args:
116+
config (StreamableHttpMcpToolAdapterConfig): The configuration of the adapter.
117+
118+
Returns:
119+
StreamableHttpMcpToolAdapter: An instance of StreamableHttpMcpToolAdapter.
120+
"""
121+
return cls(server_params=config.server_params, tool=config.tool)

python/packages/autogen-ext/src/autogen_ext/tools/mcp/_workbench.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from typing_extensions import Self
1818

1919
from ._actor import McpSessionActor
20-
from ._config import McpServerParams, SseServerParams, StdioServerParams
20+
from ._config import McpServerParams, SseServerParams, StdioServerParams, StreamableHttpServerParams
2121

2222

2323
class McpWorkbenchConfig(BaseModel):
@@ -252,7 +252,7 @@ async def start(self) -> None:
252252
)
253253
return # Already initialized, no need to start again
254254

255-
if isinstance(self._server_params, (StdioServerParams, SseServerParams)):
255+
if isinstance(self._server_params, (StdioServerParams, SseServerParams, StreamableHttpServerParams)):
256256
self._actor = McpSessionActor(self._server_params)
257257
await self._actor.initialize()
258258
self._actor_loop = asyncio.get_event_loop()

0 commit comments

Comments
 (0)