Skip to content

Commit b77b16c

Browse files
vblagojeAmnah199
andauthored
feat: Add streamable-http transport MCP support (#1777)
* Add streamable-http transport * Improve error message for tool invocation * Add streamable MCPTool example, update examples * Improve examples * Add unit tests * Update integrations/mcp/examples/mcp_client.py Co-authored-by: Amna Mubashar <[email protected]> * initialize vars outside try block * Small fix * Fix linting --------- Co-authored-by: Amna Mubashar <[email protected]>
1 parent 79da052 commit b77b16c

16 files changed

+430
-256
lines changed

integrations/mcp/examples/google_maps_agent.py

Lines changed: 0 additions & 93 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <[email protected]>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import argparse
6+
import logging
7+
8+
from haystack_integrations.tools.mcp import MCPTool, SSEServerInfo, StreamableHttpServerInfo
9+
10+
# Setup targeted logging - only show debug logs from our package
11+
logging.basicConfig(level=logging.WARNING) # Set root logger to WARNING
12+
mcp_logger = logging.getLogger("haystack_integrations.tools.mcp")
13+
mcp_logger.setLevel(logging.DEBUG)
14+
# Ensure we have at least one handler to avoid messages going to root logger
15+
if not mcp_logger.handlers:
16+
handler = logging.StreamHandler()
17+
handler.setFormatter(logging.Formatter("%(levelname)s:%(name)s:%(message)s"))
18+
mcp_logger.addHandler(handler)
19+
mcp_logger.propagate = False # Prevent propagation to root logger
20+
21+
# Run this client after running the server mcp_server.py
22+
# It shows how easy it is to use the MCPTool with different transport options
23+
24+
25+
def main():
26+
"""Example of MCPTool usage with server connection."""
27+
28+
# Parse command line arguments
29+
parser = argparse.ArgumentParser(description="Run an MCP client to connect to the server")
30+
parser.add_argument(
31+
"--transport",
32+
type=str,
33+
default="sse",
34+
choices=["sse", "streamable-http"],
35+
help="Transport mechanism for the MCP client (default: sse)",
36+
)
37+
args = parser.parse_args()
38+
39+
# Construct the appropriate URL based on transport type
40+
if args.transport == "sse":
41+
server_info = SSEServerInfo(url="http://localhost:8000/sse")
42+
else: # streamable-http
43+
server_info = StreamableHttpServerInfo(url="http://localhost:8000/mcp")
44+
tool = None
45+
tool_subtract = None
46+
try:
47+
tool = MCPTool(name="add", server_info=server_info)
48+
tool_subtract = MCPTool(name="subtract", server_info=server_info)
49+
50+
result = tool.invoke(a=7, b=3)
51+
print(f"7 + 3 = {result}")
52+
53+
result = tool_subtract.invoke(a=5, b=3)
54+
print(f"5 - 3 = {result}")
55+
56+
result = tool.invoke(a=10, b=20)
57+
print(f"10 + 20 = {result}")
58+
59+
except Exception as e:
60+
print(f"Error in client example: {e}")
61+
finally:
62+
if tool:
63+
tool.close()
64+
if tool_subtract:
65+
tool_subtract.close()
66+
67+
68+
if __name__ == "__main__":
69+
main()

integrations/mcp/examples/mcp_filtered_tools.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66

77
# This example demonstrates using MCPToolset with SSE transport
88
# and filtering tools by name
9-
# Run this client after running the server mcp_sse_server.py
9+
# Run this client after running the server mcp_server.py with sse transport
1010
# It shows how MCPToolset can selectively include only specific tools
1111

1212

1313
def main():
1414
"""Example of using MCPToolset with filtered tools."""
1515

16+
full_toolset = None
17+
filtered_toolset = None
1618
try:
1719
print("Creating toolset with all available tools:")
1820
# Create a toolset with all available tools
1921
full_toolset = MCPToolset(
20-
server_info=SSEServerInfo(base_url="http://localhost:8000"),
22+
server_info=SSEServerInfo(url="http://localhost:8000/sse"),
2123
)
2224

2325
# Print all discovered tools
@@ -29,7 +31,7 @@ def main():
2931
# Create a toolset with only specific tools
3032
# In this example, we're only including the 'add' tool
3133
filtered_toolset = MCPToolset(
32-
server_info=SSEServerInfo(base_url="http://localhost:8000"),
34+
server_info=SSEServerInfo(url="http://localhost:8000/sse"),
3335
tool_names=["add"], # Only include the 'add' tool
3436
)
3537

@@ -48,6 +50,11 @@ def main():
4850

4951
except Exception as e:
5052
print(f"Error in filtered toolset example: {e}")
53+
finally:
54+
if full_toolset:
55+
full_toolset.close()
56+
if filtered_toolset:
57+
filtered_toolset.close()
5158

5259

5360
if __name__ == "__main__":
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <[email protected]>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import argparse
6+
7+
from mcp.server.fastmcp import FastMCP
8+
9+
# run this server first before running the client mcp_filtered_tools.py or mcp_client.py
10+
# it shows how easy it is to create a MCP server in just a few lines of code
11+
# then we'll use the MCPTool to invoke the server
12+
13+
14+
mcp = FastMCP("MCP Calculator")
15+
16+
17+
@mcp.tool()
18+
def add(a: int, b: int) -> int:
19+
"""Add two numbers"""
20+
return a + b
21+
22+
23+
@mcp.tool()
24+
def subtract(a: int, b: int) -> int:
25+
"""Subtract two numbers"""
26+
return a - b
27+
28+
29+
if __name__ == "__main__":
30+
# Parse command line arguments
31+
parser = argparse.ArgumentParser(
32+
description="Run an MCP server with different transport options (sse or streamable-http)"
33+
)
34+
parser.add_argument(
35+
"--transport",
36+
type=str,
37+
default="sse",
38+
choices=["sse", "streamable-http"],
39+
help="Transport mechanism for the MCP server (default: sse)",
40+
)
41+
args = parser.parse_args()
42+
mcp.run(transport=args.transport)

integrations/mcp/examples/mcp_sse_client.py

Lines changed: 0 additions & 54 deletions
This file was deleted.

integrations/mcp/examples/mcp_sse_server.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

integrations/mcp/examples/mcp_sse_toolset.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from haystack_integrations.tools.mcp import MCPToolset, SSEServerInfo
66

77
# This example demonstrates using MCPToolset with SSE transport
8-
# Run this client after running the server mcp_sse_server.py
8+
# Run this client after running the server mcp_server.py with sse transport
99
# It shows how MCPToolset automatically discovers and creates tools from the MCP server
1010

1111

@@ -26,11 +26,12 @@ def find_tool(toolset, tool_name):
2626
def main():
2727
"""Example of using MCPToolset with SSE transport."""
2828

29+
sse_toolset = None
2930
try:
3031
# Create the toolset - this automatically discovers all available tools
3132
# from the MCP server and creates Tool instances for each one
3233
sse_toolset = MCPToolset(
33-
server_info=SSEServerInfo(base_url="http://localhost:8000"),
34+
server_info=SSEServerInfo(url="http://localhost:8000/sse"),
3435
)
3536

3637
# Print discovered tools
@@ -62,6 +63,10 @@ def main():
6263
except Exception as e:
6364
print(f"Error in SSE toolset example: {e}")
6465

66+
finally:
67+
if sse_toolset:
68+
sse_toolset.close()
69+
6570

6671
if __name__ == "__main__":
6772
main()

integrations/mcp/examples/mcp_stdio_client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
def main():
2121
"""Example of using the MCPTool implementation with stdio transport."""
2222

23+
stdio_tool = None
2324
try:
2425

2526
stdio_tool = MCPTool(
@@ -36,6 +37,9 @@ def main():
3637
print(f"Current time in Los Angeles: {result}")
3738
except Exception as e:
3839
print(f"Error in stdio example: {e}")
40+
finally:
41+
if stdio_tool:
42+
stdio_tool.close()
3943

4044

4145
if __name__ == "__main__":

0 commit comments

Comments
 (0)