mcp-haystack 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/CHANGELOG.md +14 -0
  2. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/PKG-INFO +1 -1
  3. mcp_haystack-0.3.0/examples/mcp_client.py +69 -0
  4. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/examples/mcp_filtered_tools.py +10 -3
  5. mcp_haystack-0.3.0/examples/mcp_server.py +42 -0
  6. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/examples/mcp_sse_toolset.py +7 -2
  7. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/examples/mcp_stdio_client.py +4 -0
  8. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/examples/mcp_stdio_toolset.py +4 -0
  9. mcp_haystack-0.3.0/examples/time_pipeline.py +69 -0
  10. mcp_haystack-0.3.0/examples/time_pipeline_toolset.py +67 -0
  11. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/src/haystack_integrations/tools/mcp/__init__.py +4 -0
  12. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/src/haystack_integrations/tools/mcp/mcp_tool.py +240 -45
  13. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/src/haystack_integrations/tools/mcp/mcp_toolset.py +46 -16
  14. mcp_haystack-0.3.0/tests/conftest.py +27 -0
  15. mcp_haystack-0.3.0/tests/mcp_memory_transport.py +66 -0
  16. mcp_haystack-0.3.0/tests/mcp_servers_fixtures.py +38 -0
  17. mcp_haystack-0.3.0/tests/test_mcp_integration.py +239 -0
  18. mcp_haystack-0.3.0/tests/test_mcp_server_info.py +106 -0
  19. mcp_haystack-0.3.0/tests/test_mcp_tool.py +126 -0
  20. mcp_haystack-0.3.0/tests/test_mcp_toolset.py +405 -0
  21. mcp_haystack-0.2.0/examples/google_maps_agent.py +0 -93
  22. mcp_haystack-0.2.0/examples/mcp_sse_client.py +0 -54
  23. mcp_haystack-0.2.0/examples/mcp_sse_server.py +0 -29
  24. mcp_haystack-0.2.0/examples/time_pipeline.py +0 -64
  25. mcp_haystack-0.2.0/examples/time_pipeline_toolset.py +0 -61
  26. mcp_haystack-0.2.0/tests/test_mcp_tool.py +0 -493
  27. mcp_haystack-0.2.0/tests/test_mcp_toolset.py +0 -296
  28. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/.gitignore +0 -0
  29. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/LICENSE.txt +0 -0
  30. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/README.md +0 -0
  31. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/pydoc/config.yml +0 -0
  32. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/pyproject.toml +0 -0
  33. {mcp_haystack-0.2.0 → mcp_haystack-0.3.0}/tests/__init__.py +0 -0
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [integrations/mcp-v0.2.0] - 2025-05-06
4
+
5
+ ### 🚀 Features
6
+
7
+ - [**breaking**] Deprecate SSEServerInfo base_url in favour of url (#1662)
8
+
9
+ ### 📚 Documentation
10
+
11
+ - Add_mcptoolset_docs (#1668)
12
+
3
13
  ## [integrations/mcp-v0.1.0] - 2025-04-22
4
14
 
5
15
  ### 🚀 Features
@@ -13,6 +23,10 @@
13
23
 
14
24
  - Fix MCPTool import in code example (#1578)
15
25
 
26
+ ### ⚙️ CI
27
+
28
+ - Review testing workflows (#1541)
29
+
16
30
  ### 🌀 Miscellaneous
17
31
 
18
32
  - Feat: Add AsyncExecutor in MCPTool, ensure MCPTool works in hayhooks (#1643)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-haystack
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Haystack integration for Model Context Protocol (MCP)
5
5
  Project-URL: Documentation, https://github.com/deepset-ai/haystack-core-integrations/tree/main/integrations/mcp#readme
6
6
  Project-URL: Issues, https://github.com/deepset-ai/haystack-core-integrations/issues
@@ -0,0 +1,69 @@
1
+ # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
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()
@@ -6,18 +6,20 @@ from haystack_integrations.tools.mcp import MCPToolset, SSEServerInfo
6
6
 
7
7
  # This example demonstrates using MCPToolset with SSE transport
8
8
  # 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
10
10
  # It shows how MCPToolset can selectively include only specific tools
11
11
 
12
12
 
13
13
  def main():
14
14
  """Example of using MCPToolset with filtered tools."""
15
15
 
16
+ full_toolset = None
17
+ filtered_toolset = None
16
18
  try:
17
19
  print("Creating toolset with all available tools:")
18
20
  # Create a toolset with all available tools
19
21
  full_toolset = MCPToolset(
20
- server_info=SSEServerInfo(base_url="http://localhost:8000"),
22
+ server_info=SSEServerInfo(url="http://localhost:8000/sse"),
21
23
  )
22
24
 
23
25
  # Print all discovered tools
@@ -29,7 +31,7 @@ def main():
29
31
  # Create a toolset with only specific tools
30
32
  # In this example, we're only including the 'add' tool
31
33
  filtered_toolset = MCPToolset(
32
- server_info=SSEServerInfo(base_url="http://localhost:8000"),
34
+ server_info=SSEServerInfo(url="http://localhost:8000/sse"),
33
35
  tool_names=["add"], # Only include the 'add' tool
34
36
  )
35
37
 
@@ -48,6 +50,11 @@ def main():
48
50
 
49
51
  except Exception as e:
50
52
  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()
51
58
 
52
59
 
53
60
  if __name__ == "__main__":
@@ -0,0 +1,42 @@
1
+ # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
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)
@@ -5,7 +5,7 @@
5
5
  from haystack_integrations.tools.mcp import MCPToolset, SSEServerInfo
6
6
 
7
7
  # 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
9
9
  # It shows how MCPToolset automatically discovers and creates tools from the MCP server
10
10
 
11
11
 
@@ -26,11 +26,12 @@ def find_tool(toolset, tool_name):
26
26
  def main():
27
27
  """Example of using MCPToolset with SSE transport."""
28
28
 
29
+ sse_toolset = None
29
30
  try:
30
31
  # Create the toolset - this automatically discovers all available tools
31
32
  # from the MCP server and creates Tool instances for each one
32
33
  sse_toolset = MCPToolset(
33
- server_info=SSEServerInfo(base_url="http://localhost:8000"),
34
+ server_info=SSEServerInfo(url="http://localhost:8000/sse"),
34
35
  )
35
36
 
36
37
  # Print discovered tools
@@ -62,6 +63,10 @@ def main():
62
63
  except Exception as e:
63
64
  print(f"Error in SSE toolset example: {e}")
64
65
 
66
+ finally:
67
+ if sse_toolset:
68
+ sse_toolset.close()
69
+
65
70
 
66
71
  if __name__ == "__main__":
67
72
  main()
@@ -20,6 +20,7 @@ logger.setLevel(logging.DEBUG)
20
20
  def main():
21
21
  """Example of using the MCPTool implementation with stdio transport."""
22
22
 
23
+ stdio_tool = None
23
24
  try:
24
25
 
25
26
  stdio_tool = MCPTool(
@@ -36,6 +37,9 @@ def main():
36
37
  print(f"Current time in Los Angeles: {result}")
37
38
  except Exception as e:
38
39
  print(f"Error in stdio example: {e}")
40
+ finally:
41
+ if stdio_tool:
42
+ stdio_tool.close()
39
43
 
40
44
 
41
45
  if __name__ == "__main__":
@@ -28,6 +28,7 @@ def find_tool(toolset, tool_name):
28
28
  def main():
29
29
  """Example of using the MCPToolset implementation with stdio transport."""
30
30
 
31
+ stdio_toolset = None
31
32
  try:
32
33
  # Create server info for the time service
33
34
  server_info = StdioServerInfo(command="uvx", args=["mcp-server-time", "--local-timezone=Europe/Berlin"])
@@ -54,6 +55,9 @@ def main():
54
55
  print(f"Current time in Los Angeles: {result.content[0].text}")
55
56
  except Exception as e:
56
57
  print(f"Error in stdio toolset example: {e}")
58
+ finally:
59
+ if stdio_toolset:
60
+ stdio_toolset.close()
57
61
 
58
62
 
59
63
  if __name__ == "__main__":
@@ -0,0 +1,69 @@
1
+ # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ # Full example of a pipeline that uses MCPTool to get the current time
6
+ # and then uses the time to answer a user question.
7
+ # Here we use the mcp-server-time mcp package
8
+ # See https://github.com/modelcontextprotocol/servers/tree/main/src/time for more details
9
+ # prior to running this script, pip install mcp-server-time
10
+
11
+ import logging
12
+
13
+ from haystack import Pipeline
14
+ from haystack.components.converters import OutputAdapter
15
+ from haystack.components.generators.chat import OpenAIChatGenerator
16
+ from haystack.components.tools import ToolInvoker
17
+ from haystack.dataclasses import ChatMessage
18
+
19
+ from haystack_integrations.tools.mcp.mcp_tool import MCPTool, StdioServerInfo
20
+
21
+ # Setup targeted logging - only show debug logs from our package
22
+ logging.basicConfig(level=logging.WARNING) # Set root logger to WARNING
23
+ mcp_logger = logging.getLogger("haystack_integrations.tools.mcp")
24
+ mcp_logger.setLevel(logging.DEBUG)
25
+ # Ensure we have at least one handler to avoid messages going to root logger
26
+ if not mcp_logger.handlers:
27
+ handler = logging.StreamHandler()
28
+ handler.setFormatter(logging.Formatter("%(levelname)s:%(name)s:%(message)s"))
29
+ mcp_logger.addHandler(handler)
30
+ mcp_logger.propagate = False # Prevent propagation to root logger
31
+
32
+
33
+ def main():
34
+ time_tool = None
35
+ try:
36
+ time_tool = MCPTool(
37
+ name="get_current_time",
38
+ server_info=StdioServerInfo(command="uvx", args=["mcp-server-time", "--local-timezone=Europe/Berlin"]),
39
+ )
40
+ pipeline = Pipeline()
41
+ pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o-mini", tools=[time_tool]))
42
+ pipeline.add_component("tool_invoker", ToolInvoker(tools=[time_tool]))
43
+ pipeline.add_component(
44
+ "adapter",
45
+ OutputAdapter(
46
+ template="{{ initial_msg + initial_tool_messages + tool_messages }}",
47
+ output_type=list[ChatMessage],
48
+ unsafe=True,
49
+ ),
50
+ )
51
+ pipeline.add_component("response_llm", OpenAIChatGenerator(model="gpt-4o-mini"))
52
+ pipeline.connect("llm.replies", "tool_invoker.messages")
53
+ pipeline.connect("llm.replies", "adapter.initial_tool_messages")
54
+ pipeline.connect("tool_invoker.tool_messages", "adapter.tool_messages")
55
+ pipeline.connect("adapter.output", "response_llm.messages")
56
+
57
+ user_input = "What is the time in New York? Be brief." # can be any city
58
+ user_input_msg = ChatMessage.from_user(text=user_input)
59
+
60
+ result = pipeline.run({"llm": {"messages": [user_input_msg]}, "adapter": {"initial_msg": [user_input_msg]}})
61
+
62
+ print(result["response_llm"]["replies"][0].text)
63
+ finally:
64
+ if time_tool:
65
+ time_tool.close()
66
+
67
+
68
+ if __name__ == "__main__":
69
+ main()
@@ -0,0 +1,67 @@
1
+ # SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ # Full example of a pipeline that uses MCPToolset to get the current time
6
+ # and then uses the time to answer a user question.
7
+ # Here we use the mcp-server-time mcp package
8
+ # See https://github.com/modelcontextprotocol/servers/tree/main/src/time for more details
9
+ # prior to running this script, pip install mcp-server-time
10
+
11
+ import os
12
+
13
+ from haystack import Pipeline
14
+ from haystack.components.converters import OutputAdapter
15
+ from haystack.components.generators.chat import OpenAIChatGenerator
16
+ from haystack.components.tools import ToolInvoker
17
+ from haystack.dataclasses import ChatMessage
18
+
19
+ from haystack_integrations.tools.mcp import MCPToolset, StdioServerInfo
20
+
21
+
22
+ def main():
23
+ # Create server info for the time service
24
+ server_info = StdioServerInfo(command="uvx", args=["mcp-server-time", "--local-timezone=Europe/Berlin"])
25
+
26
+ mcp_toolset = None
27
+ try:
28
+ # Create the toolset - this will automatically discover all available tools
29
+ mcp_toolset = MCPToolset(server_info)
30
+ # Check if OpenAI API key is set
31
+ api_key = os.environ.get("OPENAI_API_KEY")
32
+ if not api_key:
33
+ print("OPENAI_API_KEY environment variable is not set.")
34
+ print("You need to set it to run the pipeline example.")
35
+ print("For now, demonstrating direct tool usage:")
36
+
37
+ pipeline = Pipeline()
38
+ pipeline.add_component("llm", OpenAIChatGenerator(model="gpt-4o-mini", tools=mcp_toolset))
39
+ pipeline.add_component("tool_invoker", ToolInvoker(tools=mcp_toolset))
40
+ pipeline.add_component(
41
+ "adapter",
42
+ OutputAdapter(
43
+ template="{{ initial_msg + initial_tool_messages + tool_messages }}",
44
+ output_type=list[ChatMessage],
45
+ unsafe=True,
46
+ ),
47
+ )
48
+ pipeline.add_component("response_llm", OpenAIChatGenerator(model="gpt-4o-mini"))
49
+ pipeline.connect("llm.replies", "tool_invoker.messages")
50
+ pipeline.connect("llm.replies", "adapter.initial_tool_messages")
51
+ pipeline.connect("tool_invoker.tool_messages", "adapter.tool_messages")
52
+ pipeline.connect("adapter.output", "response_llm.messages")
53
+
54
+ user_input = "What is the time in New York? Be brief." # can be any city
55
+ user_input_msg = ChatMessage.from_user(text=user_input)
56
+
57
+ result = pipeline.run({"llm": {"messages": [user_input_msg]}, "adapter": {"initial_msg": [user_input_msg]}})
58
+
59
+ print(result["response_llm"]["replies"][0].text)
60
+
61
+ finally:
62
+ if mcp_toolset:
63
+ mcp_toolset.close()
64
+
65
+
66
+ if __name__ == "__main__":
67
+ main()
@@ -14,6 +14,8 @@ from .mcp_tool import (
14
14
  SSEServerInfo,
15
15
  StdioClient,
16
16
  StdioServerInfo,
17
+ StreamableHttpClient,
18
+ StreamableHttpServerInfo,
17
19
  )
18
20
  from .mcp_toolset import MCPToolset
19
21
 
@@ -30,4 +32,6 @@ __all__ = [
30
32
  "SSEServerInfo",
31
33
  "StdioClient",
32
34
  "StdioServerInfo",
35
+ "StreamableHttpClient",
36
+ "StreamableHttpServerInfo",
33
37
  ]