Skip to content

Commit 3ea5d35

Browse files
vblagojeoryx1729mpangrazzibilgeyucelsjrl
authored
feat: Add MCPToolset (#1626)
Co-authored-by: oryx1729 <oryx1729@protonmail.com> Co-authored-by: Michele Pangrazzi <xmikex83@gmail.com> Co-authored-by: Bilge Yücel <bilge.yucel@deepset.ai> Co-authored-by: Sebastian Husch Lee <sjrl@users.noreply.github.com>
1 parent ba0a310 commit 3ea5d35

11 files changed

Lines changed: 896 additions & 87 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
### Overview
6+
# This script shows how to use the **Google Maps MCP Server** in combination with the **Haystack Agent** and **OpenAI's Chat Generator** to search for places via the Google Places API.
7+
8+
# ---
9+
10+
# ### 🔧 Step 1: Start the Google Maps MCP Server
11+
12+
# Make sure you have a valid **Google Maps API key**.
13+
# See [Google Maps API Key](https://developers.google.com/maps/documentation/places/web-service/get-api-key) for more information.
14+
15+
# Run the following Docker command to start the MCP Server locally on port 8000:
16+
17+
# ```bash
18+
# docker run -it --rm -p 8000:8000 \
19+
# -e GOOGLE_MAPS_API_KEY=$GOOGLE_MAPS_API_KEY \
20+
# supercorp/supergateway \
21+
# --stdio "npx -y @modelcontextprotocol/server-google-maps" \
22+
# --port 8000
23+
# ```
24+
25+
# ---
26+
27+
# ### 🕵️ Step 2: Inspect Available Tools (Optional but useful)
28+
29+
# You can verify that the MCP server is running and see the available tools using:
30+
31+
# ```bash
32+
# npx -y @modelcontextprotocol/inspector
33+
# ```
34+
35+
# Connect MCP Inspector to the server at `http://localhost:8000/sse` and click on "List Tools" to display tools such as:
36+
37+
# - `maps_geocode`: Convert address → coordinates
38+
# - `maps_reverse_geocode`: Convert coordinates → address
39+
# - `maps_search_places`: Search places (e.g., restaurants)
40+
# - `maps_place_details`: Get details of a specific place
41+
# - `maps_distance_matrix`: Calculate travel distance/time
42+
# - `maps_elevation`: Get elevation info
43+
# - `maps_directions`: Get route directions
44+
45+
# ---
46+
47+
# ### ▶️ Step 3: Run the Python Script
48+
49+
# This script sets up:
50+
# - A **Langfuse trace**
51+
# - A **MCPToolset** with selected tools (`maps_geocode`, `maps_search_places`)
52+
# - An **Agent** using `gpt-4.1` to process a natural language request
53+
54+
# To run the script:
55+
56+
# ```bash
57+
# python examples/google_maps_agent.py
58+
# ```
59+
60+
# The agent will respond to this example query:
61+
62+
# > "Find the five best Persian restaurants close to Zinnowitzer Str. 1, 10115 Berlin, Germany"
63+
64+
from haystack.components.agents import Agent
65+
from haystack.components.generators.chat import OpenAIChatGenerator
66+
from haystack.dataclasses import ChatMessage
67+
68+
# from haystack_integrations.components.connectors.langfuse.langfuse_connector import LangfuseConnector
69+
from haystack_integrations.tools.mcp.mcp_tool import SSEServerInfo
70+
from haystack_integrations.tools.mcp.mcp_toolset import MCPToolset
71+
72+
73+
def main():
74+
# tracer = LangfuseConnector("Agent google maps search")
75+
# Optionally, you can use Langfuse to trace the agent's activity but it needs
76+
# additional configuration.
77+
# See [Langfuse integration](https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/langfuse) for more information.
78+
toolset = MCPToolset(
79+
SSEServerInfo(base_url="http://localhost:8000"), tool_names=["maps_geocode", "maps_search_places"]
80+
)
81+
agent = Agent(chat_generator=OpenAIChatGenerator(model="gpt-4.1"), tools=toolset)
82+
result = agent.run(
83+
messages=[
84+
ChatMessage.from_user(
85+
text="Find the five best persian restaurants close to Zinnowitzer Str. 1, 10115 Berlin, Germany"
86+
)
87+
]
88+
)
89+
print(result["messages"][-1].text)
90+
91+
92+
if __name__ == "__main__":
93+
main()

integrations/mcp/examples/hayhooks/pipeline_wrapper.py

Lines changed: 0 additions & 82 deletions
This file was deleted.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
from haystack_integrations.tools.mcp import MCPToolset, SSEServerInfo
6+
7+
# This example demonstrates using MCPToolset with SSE transport
8+
# and filtering tools by name
9+
# Run this client after running the server mcp_sse_server.py
10+
# It shows how MCPToolset can selectively include only specific tools
11+
12+
13+
def main():
14+
"""Example of using MCPToolset with filtered tools."""
15+
16+
try:
17+
print("Creating toolset with all available tools:")
18+
# Create a toolset with all available tools
19+
full_toolset = MCPToolset(
20+
server_info=SSEServerInfo(base_url="http://localhost:8000"),
21+
)
22+
23+
# Print all discovered tools
24+
print(f"Discovered {len(full_toolset)} tools:")
25+
for tool in full_toolset:
26+
print(f" - {tool.name}: {tool.description}")
27+
28+
print("\nCreating toolset with filtered tools:")
29+
# Create a toolset with only specific tools
30+
# In this example, we're only including the 'add' tool
31+
filtered_toolset = MCPToolset(
32+
server_info=SSEServerInfo(base_url="http://localhost:8000"),
33+
tool_names=["add"], # Only include the 'add' tool
34+
)
35+
36+
# Print filtered tools
37+
print(f"Filtered toolset has {len(filtered_toolset)} tools:")
38+
for tool in filtered_toolset:
39+
print(f" - {tool.name}: {tool.description}")
40+
41+
# Use the filtered toolset
42+
if len(filtered_toolset) > 0:
43+
add_tool = filtered_toolset.tools[0] # The only tool should be 'add'
44+
result = add_tool.invoke(a=10, b=5)
45+
print(f"\nInvoking {add_tool.name}: 10 + 5 = {result.content[0].text}")
46+
else:
47+
print("No tools available in the filtered toolset")
48+
49+
except Exception as e:
50+
print(f"Error in filtered toolset example: {e}")
51+
52+
53+
if __name__ == "__main__":
54+
main()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
from haystack_integrations.tools.mcp import MCPToolset, SSEServerInfo
6+
7+
# This example demonstrates using MCPToolset with SSE transport
8+
# Run this client after running the server mcp_sse_server.py
9+
# It shows how MCPToolset automatically discovers and creates tools from the MCP server
10+
11+
12+
def find_tool(toolset, tool_name):
13+
"""
14+
Find a tool by name in the toolset.
15+
16+
:param toolset: The toolset to search in
17+
:param tool_name: The name of the tool to find
18+
:returns: The tool if found, None otherwise
19+
"""
20+
for tool in toolset:
21+
if tool.name == tool_name:
22+
return tool
23+
return None
24+
25+
26+
def main():
27+
"""Example of using MCPToolset with SSE transport."""
28+
29+
try:
30+
# Create the toolset - this automatically discovers all available tools
31+
# from the MCP server and creates Tool instances for each one
32+
sse_toolset = MCPToolset(
33+
server_info=SSEServerInfo(base_url="http://localhost:8000"),
34+
)
35+
36+
# Print discovered tools
37+
print(f"Discovered {len(sse_toolset)} tools:")
38+
for tool in sse_toolset:
39+
print(f" - {tool.name}: {tool.description}")
40+
41+
# Get tools by name from the toolset
42+
add_tool = find_tool(sse_toolset, "add")
43+
if not add_tool:
44+
print("Add tool not found!")
45+
return
46+
47+
subtract_tool = find_tool(sse_toolset, "subtract")
48+
if not subtract_tool:
49+
print("Subtract tool not found!")
50+
return
51+
52+
# Use the tools
53+
result = add_tool.invoke(a=7, b=3)
54+
print(f"7 + 3 = {result.content[0].text}")
55+
56+
result = subtract_tool.invoke(a=5, b=3)
57+
print(f"5 - 3 = {result.content[0].text}")
58+
59+
result = add_tool.invoke(a=10, b=20)
60+
print(f"10 + 20 = {result.content[0].text}")
61+
62+
except Exception as e:
63+
print(f"Error in SSE toolset example: {e}")
64+
65+
66+
if __name__ == "__main__":
67+
main()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
from haystack_integrations.tools.mcp import MCPToolset, StdioServerInfo
6+
7+
# This example shows how to use MCPToolset with stdio transport
8+
# MCPToolset automatically discovers all available tools from the MCP server
9+
# Here we use the mcp-server-time server
10+
# See https://github.com/modelcontextprotocol/servers/tree/main/src/time for more details
11+
# prior to running this script, pip install mcp-server-time
12+
13+
14+
def find_tool(toolset, tool_name):
15+
"""
16+
Find a tool by name in the toolset.
17+
18+
:param toolset: The toolset to search in
19+
:param tool_name: The name of the tool to find
20+
:returns: The tool if found, None otherwise
21+
"""
22+
for tool in toolset:
23+
if tool.name == tool_name:
24+
return tool
25+
return None
26+
27+
28+
def main():
29+
"""Example of using the MCPToolset implementation with stdio transport."""
30+
31+
try:
32+
# Create server info for the time service
33+
server_info = StdioServerInfo(command="uvx", args=["mcp-server-time", "--local-timezone=Europe/Berlin"])
34+
35+
# Create the toolset - this will automatically discover all available tools
36+
stdio_toolset = MCPToolset(server_info=server_info)
37+
38+
# Print discovered tools
39+
print(f"Discovered {len(stdio_toolset)} tools:")
40+
for tool in stdio_toolset:
41+
print(f" - {tool.name}: {tool.description}")
42+
43+
# Find tools by name using the helper function
44+
time_tool = find_tool(stdio_toolset, "get_current_time")
45+
if not time_tool:
46+
print("Time tool not found!")
47+
return
48+
49+
# Use the get_current_time tool
50+
result = time_tool.invoke(timezone="America/New_York")
51+
print(f"Current time in New York: {result.content[0].text}")
52+
53+
result = time_tool.invoke(timezone="America/Los_Angeles")
54+
print(f"Current time in Los Angeles: {result.content[0].text}")
55+
except Exception as e:
56+
print(f"Error in stdio toolset example: {e}")
57+
58+
59+
if __name__ == "__main__":
60+
main()

0 commit comments

Comments
 (0)