mcp-use 1.2.11__tar.gz → 1.2.13__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.

Potentially problematic release.


This version of mcp-use might be problematic. Click here for more details.

Files changed (86) hide show
  1. {mcp_use-1.2.11 → mcp_use-1.2.13}/.github/workflows/publish.yml +8 -4
  2. mcp_use-1.2.11/.github/workflows/tests.yml → mcp_use-1.2.13/.github/workflows/unittests.yml +1 -1
  3. {mcp_use-1.2.11 → mcp_use-1.2.13}/PKG-INFO +32 -16
  4. {mcp_use-1.2.11 → mcp_use-1.2.13}/README.md +31 -15
  5. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/adapters/base.py +31 -10
  6. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/adapters/langchain_adapter.py +112 -2
  7. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/client.py +6 -5
  8. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/connectors/base.py +83 -20
  9. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/session.py +3 -29
  10. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/task_managers/base.py +3 -5
  11. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/task_managers/sse.py +2 -5
  12. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/task_managers/stdio.py +2 -6
  13. mcp_use-1.2.13/mcp_use/task_managers/websocket.py +63 -0
  14. {mcp_use-1.2.11 → mcp_use-1.2.13}/pyproject.toml +1 -1
  15. mcp_use-1.2.13/static/logo_black.svg +8 -0
  16. mcp_use-1.2.13/static/logo_white.svg +8 -0
  17. {mcp_use-1.2.11 → mcp_use-1.2.13}/tests/unit/test_client.py +3 -3
  18. {mcp_use-1.2.11 → mcp_use-1.2.13}/tests/unit/test_http_connector.py +69 -27
  19. {mcp_use-1.2.11 → mcp_use-1.2.13}/tests/unit/test_session.py +0 -58
  20. {mcp_use-1.2.11 → mcp_use-1.2.13}/tests/unit/test_stdio_connector.py +75 -22
  21. mcp_use-1.2.11/mcp_use/task_managers/websocket.py +0 -63
  22. mcp_use-1.2.11/static/image.jpg +0 -0
  23. {mcp_use-1.2.11 → mcp_use-1.2.13}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  24. {mcp_use-1.2.11 → mcp_use-1.2.13}/.github/pull_request_template.md +0 -0
  25. {mcp_use-1.2.11 → mcp_use-1.2.13}/.gitignore +0 -0
  26. {mcp_use-1.2.11 → mcp_use-1.2.13}/.pre-commit-config.yaml +0 -0
  27. {mcp_use-1.2.11 → mcp_use-1.2.13}/CONTRIBUTING.md +0 -0
  28. {mcp_use-1.2.11 → mcp_use-1.2.13}/LICENSE +0 -0
  29. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/README.md +0 -0
  30. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/api-reference/introduction.mdx +0 -0
  31. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/api-reference/mcpagent.mdx +0 -0
  32. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/building-custom-agents.mdx +0 -0
  33. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/development.mdx +0 -0
  34. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/docs.json +0 -0
  35. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/essentials/configuration.mdx +0 -0
  36. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/essentials/connection-types.mdx +0 -0
  37. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/essentials/debugging.mdx +0 -0
  38. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/essentials/llm-integration.mdx +0 -0
  39. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/essentials/server-manager.mdx +0 -0
  40. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/favicon.svg +0 -0
  41. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/images/hero-dark.png +0 -0
  42. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/images/hero-light.png +0 -0
  43. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/introduction.mdx +0 -0
  44. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/logo/dark.svg +0 -0
  45. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/logo/light.svg +0 -0
  46. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/quickstart.mdx +0 -0
  47. {mcp_use-1.2.11 → mcp_use-1.2.13}/docs/snippets/snippet-intro.mdx +0 -0
  48. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/airbnb_mcp.json +0 -0
  49. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/airbnb_use.py +0 -0
  50. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/blender_use.py +0 -0
  51. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/browser_mcp.json +0 -0
  52. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/browser_use.py +0 -0
  53. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/chat_example.py +0 -0
  54. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/filesystem_use.py +0 -0
  55. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/http_example.py +0 -0
  56. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/mcp_everything.py +0 -0
  57. {mcp_use-1.2.11 → mcp_use-1.2.13}/examples/multi_server_example.py +0 -0
  58. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/__init__.py +0 -0
  59. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/adapters/__init__.py +0 -0
  60. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/agents/__init__.py +0 -0
  61. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/agents/base.py +0 -0
  62. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/agents/mcpagent.py +0 -0
  63. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/agents/prompts/system_prompt_builder.py +0 -0
  64. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/agents/prompts/templates.py +0 -0
  65. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/config.py +0 -0
  66. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/connectors/__init__.py +0 -0
  67. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/connectors/http.py +0 -0
  68. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/connectors/stdio.py +0 -0
  69. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/connectors/websocket.py +0 -0
  70. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/logging.py +0 -0
  71. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/__init__.py +0 -0
  72. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/server_manager.py +0 -0
  73. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/__init__.py +0 -0
  74. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/base_tool.py +0 -0
  75. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/connect_server.py +0 -0
  76. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/disconnect_server.py +0 -0
  77. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/get_active_server.py +0 -0
  78. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/list_servers_tool.py +0 -0
  79. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/search_tools.py +0 -0
  80. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/managers/tools/use_tool.py +0 -0
  81. {mcp_use-1.2.11 → mcp_use-1.2.13}/mcp_use/task_managers/__init__.py +0 -0
  82. {mcp_use-1.2.11 → mcp_use-1.2.13}/pytest.ini +0 -0
  83. {mcp_use-1.2.11 → mcp_use-1.2.13}/ruff.toml +0 -0
  84. {mcp_use-1.2.11 → mcp_use-1.2.13}/tests/conftest.py +0 -0
  85. {mcp_use-1.2.11 → mcp_use-1.2.13}/tests/unit/test_config.py +0 -0
  86. {mcp_use-1.2.11 → mcp_use-1.2.13}/tests/unit/test_logging.py +0 -0
@@ -1,11 +1,12 @@
1
1
  name: Check Version Bump and Publish to PyPI
2
2
 
3
3
  on:
4
- push:
4
+ workflow_run:
5
+ workflows: ["Python Tests"]
5
6
  branches:
6
7
  - main
7
- paths:
8
- - 'pyproject.toml'
8
+ types:
9
+ - completed
9
10
 
10
11
  # Required for PyPI trusted publishing
11
12
  permissions:
@@ -14,11 +15,14 @@ permissions:
14
15
 
15
16
  jobs:
16
17
  check-version-and-publish:
18
+ # Only proceed if the referenced workflow completed successfully
19
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
17
20
  runs-on: ubuntu-latest
18
21
  steps:
19
22
  - uses: actions/checkout@v3
20
23
  with:
21
- fetch-depth: 0 # This fetches all history for comparing versions
24
+ fetch-depth: 0
25
+ ref: ${{ github.event.workflow_run.head_sha }}
22
26
 
23
27
  - name: Set up Python
24
28
  uses: actions/setup-python@v4
@@ -28,4 +28,4 @@ jobs:
28
28
  ruff check .
29
29
  - name: Test with pytest
30
30
  run: |
31
- pytest
31
+ pytest tests/unit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-use
3
- Version: 1.2.11
3
+ Version: 1.2.13
4
4
  Summary: MCP Library for LLMs
5
5
  Author-email: Pietro Zullo <pietro.zullo@gmail.com>
6
6
  License: MIT
@@ -39,23 +39,39 @@ Provides-Extra: search
39
39
  Requires-Dist: fastembed>=0.0.1; extra == 'search'
40
40
  Description-Content-Type: text/markdown
41
41
 
42
- <picture>
43
- <img alt="" src="./static/image.jpg" width="full">
44
- </picture>
42
+ <div align="center" style="margin: 0 auto; max-width: 80%;">
43
+ <picture>
44
+ <source media="(prefers-color-scheme: dark)" srcset="static/logo_white.svg">
45
+ <source media="(prefers-color-scheme: light)" srcset="static/logo_black.svg">
46
+ <img alt="mcp use logo" src="./static/logo-white.svg" width="80%" style="margin: 20px auto;">
47
+ </picture>
48
+ </div>
45
49
 
46
50
  <h1 align="center">Unified MCP Client Library </h1>
47
-
48
- [![](https://img.shields.io/pypi/dw/mcp_use.svg)](https://pypi.org/project/mcp_use/)
49
- [![PyPI Downloads](https://img.shields.io/pypi/dm/mcp_use.svg)](https://pypi.org/project/mcp_use/)
50
- [![PyPI Version](https://img.shields.io/pypi/v/mcp_use.svg)](https://pypi.org/project/mcp_use/)
51
- [![Python Versions](https://img.shields.io/pypi/pyversions/mcp_use.svg)](https://pypi.org/project/mcp_use/)
52
- [![Documentation](https://img.shields.io/badge/docs-mcp--use.io-blue)](https://docs.mcp-use.io)
53
- [![Website](https://img.shields.io/badge/website-mcp--use.io-blue)](https://mcp-use.io)
54
- [![License](https://img.shields.io/github/license/pietrozullo/mcp-use)](https://github.com/pietrozullo/mcp-use/blob/main/LICENSE)
55
- [![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
56
- [![GitHub stars](https://img.shields.io/github/stars/pietrozullo/mcp-use?style=social)](https://github.com/pietrozullo/mcp-use/stargazers)
57
- [![Twitter Follow](https://img.shields.io/twitter/follow/Pietro?style=social)](https://x.com/pietrozullo)
58
-
51
+ <p align="center">
52
+ <a href="https://pypi.org/project/mcp_use/" alt="PyPI Version">
53
+ <img src="https://img.shields.io/pypi/v/mcp_use.svg"/></a>
54
+ <a href="https://pypi.org/project/mcp_use/" alt="PyPI Downloads">
55
+ <img src="https://static.pepy.tech/badge/mcp-use" /></a>
56
+ <a href="https://pypi.org/project/mcp_use/" alt="Python Versions">
57
+ <img src="https://img.shields.io/pypi/pyversions/mcp_use.svg" /></a>
58
+ <a href="https://docs.mcp-use.io" alt="Documentation">
59
+ <img src="https://img.shields.io/badge/docs-mcp--use.io-blue" /></a>
60
+ <a href="https://mcp-use.io" alt="Website">
61
+ <img src="https://img.shields.io/badge/website-mcp--use.io-blue" /></a>
62
+ <a href="https://github.com/pietrozullo/mcp-use/blob/main/LICENSE" alt="License">
63
+ <img src="https://img.shields.io/github/license/pietrozullo/mcp-use" /></a>
64
+ <a href="https://github.com/astral-sh/ruff" alt="Code style: Ruff">
65
+ <img src="https://img.shields.io/badge/code%20style-ruff-000000.svg" /></a>
66
+ <a href="https://github.com/pietrozullo/mcp-use/stargazers" alt="GitHub stars">
67
+ <img src="https://img.shields.io/github/stars/pietrozullo/mcp-use?style=social" /></a>
68
+ </p>
69
+ <p align="center">
70
+ <a href="https://x.com/pietrozullo" alt="Twitter Follow">
71
+ <img src="https://img.shields.io/twitter/follow/Pietro?style=social" /></a>
72
+ <a href="https://discord.gg/XkNkSkMz3V" alt="Discord">
73
+ <img src="https://dcbadge.limes.pink/api/server/https://discord.gg/XkNkSkMz3V?style=flat" /></a>
74
+ </p>
59
75
  🌐 MCP-Use is the open source way to connect **any LLM to any MCP server** and build custom agents that have tool access, without using closed source or application clients.
60
76
 
61
77
  💡 Let developers easily connect any LLM to tools like web browsing, file operations, and more.
@@ -1,20 +1,36 @@
1
- <picture>
2
- <img alt="" src="./static/image.jpg" width="full">
3
- </picture>
1
+ <div align="center" style="margin: 0 auto; max-width: 80%;">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="static/logo_white.svg">
4
+ <source media="(prefers-color-scheme: light)" srcset="static/logo_black.svg">
5
+ <img alt="mcp use logo" src="./static/logo-white.svg" width="80%" style="margin: 20px auto;">
6
+ </picture>
7
+ </div>
4
8
 
5
9
  <h1 align="center">Unified MCP Client Library </h1>
6
-
7
- [![](https://img.shields.io/pypi/dw/mcp_use.svg)](https://pypi.org/project/mcp_use/)
8
- [![PyPI Downloads](https://img.shields.io/pypi/dm/mcp_use.svg)](https://pypi.org/project/mcp_use/)
9
- [![PyPI Version](https://img.shields.io/pypi/v/mcp_use.svg)](https://pypi.org/project/mcp_use/)
10
- [![Python Versions](https://img.shields.io/pypi/pyversions/mcp_use.svg)](https://pypi.org/project/mcp_use/)
11
- [![Documentation](https://img.shields.io/badge/docs-mcp--use.io-blue)](https://docs.mcp-use.io)
12
- [![Website](https://img.shields.io/badge/website-mcp--use.io-blue)](https://mcp-use.io)
13
- [![License](https://img.shields.io/github/license/pietrozullo/mcp-use)](https://github.com/pietrozullo/mcp-use/blob/main/LICENSE)
14
- [![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://github.com/astral-sh/ruff)
15
- [![GitHub stars](https://img.shields.io/github/stars/pietrozullo/mcp-use?style=social)](https://github.com/pietrozullo/mcp-use/stargazers)
16
- [![Twitter Follow](https://img.shields.io/twitter/follow/Pietro?style=social)](https://x.com/pietrozullo)
17
-
10
+ <p align="center">
11
+ <a href="https://pypi.org/project/mcp_use/" alt="PyPI Version">
12
+ <img src="https://img.shields.io/pypi/v/mcp_use.svg"/></a>
13
+ <a href="https://pypi.org/project/mcp_use/" alt="PyPI Downloads">
14
+ <img src="https://static.pepy.tech/badge/mcp-use" /></a>
15
+ <a href="https://pypi.org/project/mcp_use/" alt="Python Versions">
16
+ <img src="https://img.shields.io/pypi/pyversions/mcp_use.svg" /></a>
17
+ <a href="https://docs.mcp-use.io" alt="Documentation">
18
+ <img src="https://img.shields.io/badge/docs-mcp--use.io-blue" /></a>
19
+ <a href="https://mcp-use.io" alt="Website">
20
+ <img src="https://img.shields.io/badge/website-mcp--use.io-blue" /></a>
21
+ <a href="https://github.com/pietrozullo/mcp-use/blob/main/LICENSE" alt="License">
22
+ <img src="https://img.shields.io/github/license/pietrozullo/mcp-use" /></a>
23
+ <a href="https://github.com/astral-sh/ruff" alt="Code style: Ruff">
24
+ <img src="https://img.shields.io/badge/code%20style-ruff-000000.svg" /></a>
25
+ <a href="https://github.com/pietrozullo/mcp-use/stargazers" alt="GitHub stars">
26
+ <img src="https://img.shields.io/github/stars/pietrozullo/mcp-use?style=social" /></a>
27
+ </p>
28
+ <p align="center">
29
+ <a href="https://x.com/pietrozullo" alt="Twitter Follow">
30
+ <img src="https://img.shields.io/twitter/follow/Pietro?style=social" /></a>
31
+ <a href="https://discord.gg/XkNkSkMz3V" alt="Discord">
32
+ <img src="https://dcbadge.limes.pink/api/server/https://discord.gg/XkNkSkMz3V?style=flat" /></a>
33
+ </p>
18
34
  🌐 MCP-Use is the open source way to connect **any LLM to any MCP server** and build custom agents that have tool access, without using closed source or application clients.
19
35
 
20
36
  💡 Let developers easily connect any LLM to tools like web browsing, file operations, and more.
@@ -5,7 +5,9 @@ This module provides the abstract base class that all MCP tool adapters should i
5
5
  """
6
6
 
7
7
  from abc import ABC, abstractmethod
8
- from typing import Any, TypeVar
8
+ from typing import TypeVar
9
+
10
+ from mcp.types import Prompt, Resource, Tool
9
11
 
10
12
  from ..client import MCPClient
11
13
  from ..connectors.base import BaseConnector
@@ -90,13 +92,13 @@ class BaseAdapter(ABC):
90
92
  return self._connector_tool_map[connector]
91
93
 
92
94
  # Create tools for this connector
93
- connector_tools = []
94
95
 
95
96
  # Make sure the connector is initialized and has tools
96
97
  success = await self._ensure_connector_initialized(connector)
97
98
  if not success:
98
99
  return []
99
100
 
101
+ connector_tools = []
100
102
  # Now create tools for each MCP tool
101
103
  for tool in connector.tools:
102
104
  # Convert the tool and add it to the list if conversion was successful
@@ -104,6 +106,23 @@ class BaseAdapter(ABC):
104
106
  if converted_tool:
105
107
  connector_tools.append(converted_tool)
106
108
 
109
+ # Convert resources to tools so that agents can access resource content directly
110
+ resources_list = connector.resources or []
111
+ if resources_list:
112
+ for resource in resources_list:
113
+ converted_resource = self._convert_resource(resource, connector)
114
+ if converted_resource:
115
+ connector_tools.append(converted_resource)
116
+
117
+ # Convert prompts to tools so that agents can retrieve prompt content
118
+ prompts_list = connector.prompts or []
119
+ if prompts_list:
120
+ for prompt in prompts_list:
121
+ converted_prompt = self._convert_prompt(prompt, connector)
122
+ if converted_prompt:
123
+ connector_tools.append(converted_prompt)
124
+ # ------------------------------
125
+
107
126
  # Store the tools for this connector
108
127
  self._connector_tool_map[connector] = connector_tools
109
128
 
@@ -116,16 +135,18 @@ class BaseAdapter(ABC):
116
135
  return connector_tools
117
136
 
118
137
  @abstractmethod
119
- def _convert_tool(self, mcp_tool: dict[str, Any], connector: BaseConnector) -> T:
120
- """Convert an MCP tool to the target framework's tool format.
138
+ def _convert_tool(self, mcp_tool: Tool, connector: BaseConnector) -> T:
139
+ """Convert an MCP tool to the target framework's tool format."""
140
+ pass
121
141
 
122
- Args:
123
- mcp_tool: The MCP tool to convert.
124
- connector: The connector that provides this tool.
142
+ @abstractmethod
143
+ def _convert_resource(self, mcp_resource: Resource, connector: BaseConnector) -> T:
144
+ """Convert an MCP resource to the target framework's resource format."""
145
+ pass
125
146
 
126
- Returns:
127
- A tool in the target framework's format.
128
- """
147
+ @abstractmethod
148
+ def _convert_prompt(self, mcp_prompt: Prompt, connector: BaseConnector) -> T:
149
+ """Convert an MCP prompt to the target framework's prompt format."""
129
150
  pass
130
151
 
131
152
  async def _create_tools_from_connectors(self, connectors: list[BaseConnector]) -> list[T]:
@@ -4,12 +4,21 @@ LangChain adapter for MCP tools.
4
4
  This module provides utilities to convert MCP tools to LangChain tools.
5
5
  """
6
6
 
7
+ import re
7
8
  from typing import Any, NoReturn
8
9
 
9
10
  from jsonschema_pydantic import jsonschema_to_pydantic
10
11
  from langchain_core.tools import BaseTool, ToolException
11
- from mcp.types import CallToolResult, EmbeddedResource, ImageContent, TextContent
12
- from pydantic import BaseModel
12
+ from mcp.types import (
13
+ CallToolResult,
14
+ EmbeddedResource,
15
+ ImageContent,
16
+ Prompt,
17
+ ReadResourceRequestParams,
18
+ Resource,
19
+ TextContent,
20
+ )
21
+ from pydantic import BaseModel, Field, create_model
13
22
 
14
23
  from ..connectors.base import BaseConnector
15
24
  from ..logging import logger
@@ -162,3 +171,104 @@ class LangChainAdapter(BaseAdapter):
162
171
  raise
163
172
 
164
173
  return McpToLangChainAdapter()
174
+
175
+ def _convert_resource(self, mcp_resource: Resource, connector: BaseConnector) -> BaseTool:
176
+ """Convert an MCP resource to LangChain's tool format.
177
+
178
+ Each resource becomes an async tool that returns its content when called.
179
+ The tool takes **no** arguments because the resource URI is fixed.
180
+ """
181
+
182
+ def _sanitize(name: str) -> str:
183
+ return re.sub(r"[^A-Za-z0-9_]+", "_", name).lower().strip("_")
184
+
185
+ class ResourceTool(BaseTool):
186
+ name: str = _sanitize(mcp_resource.name or f"resource_{mcp_resource.uri}")
187
+ description: str = (
188
+ mcp_resource.description
189
+ or f"Return the content of the resource located at URI {mcp_resource.uri}."
190
+ )
191
+ args_schema: type[BaseModel] = ReadResourceRequestParams
192
+ tool_connector: BaseConnector = connector
193
+ handle_tool_error: bool = True
194
+
195
+ def _run(self, **kwargs: Any) -> NoReturn:
196
+ raise NotImplementedError("Resource tools only support async operations")
197
+
198
+ async def _arun(self, **kwargs: Any) -> Any:
199
+ logger.debug(f'Resource tool: "{self.name}" called')
200
+ try:
201
+ result = await self.tool_connector.read_resource(mcp_resource.uri)
202
+ for content in result.contents:
203
+ # Attempt to decode bytes if necessary
204
+ if isinstance(content, bytes):
205
+ content_decoded = content.decode()
206
+ else:
207
+ content_decoded = str(content)
208
+
209
+ return content_decoded
210
+ except Exception as e:
211
+ if self.handle_tool_error:
212
+ return f"Error reading resource: {str(e)}"
213
+ raise
214
+
215
+ return ResourceTool()
216
+
217
+ def _convert_prompt(self, mcp_prompt: Prompt, connector: BaseConnector) -> BaseTool:
218
+ """Convert an MCP prompt to LangChain's tool format.
219
+
220
+ The resulting tool executes `get_prompt` on the connector with the prompt's name and
221
+ the user-provided arguments (if any). The tool returns the decoded prompt content.
222
+ """
223
+ prompt_arguments = mcp_prompt.arguments
224
+
225
+ # Sanitize the prompt name to create a valid Python identifier for the model name
226
+ base_model_name = re.sub(r"[^a-zA-Z0-9_]", "_", mcp_prompt.name)
227
+ if not base_model_name or base_model_name[0].isdigit():
228
+ base_model_name = "PromptArgs_" + base_model_name
229
+ dynamic_model_name = f"{base_model_name}_InputSchema"
230
+
231
+ if prompt_arguments:
232
+ field_definitions_for_create: dict[str, Any] = {}
233
+ for arg in prompt_arguments:
234
+ param_type: type = getattr(arg, "type", str)
235
+ if arg.required:
236
+ field_definitions_for_create[arg.name] = (
237
+ param_type,
238
+ Field(description=arg.description),
239
+ )
240
+ else:
241
+ field_definitions_for_create[arg.name] = (
242
+ param_type | None,
243
+ Field(None, description=arg.description),
244
+ )
245
+
246
+ InputSchema = create_model(
247
+ dynamic_model_name, **field_definitions_for_create, __base__=BaseModel
248
+ )
249
+ else:
250
+ # Create an empty Pydantic model if there are no arguments
251
+ InputSchema = create_model(dynamic_model_name, __base__=BaseModel)
252
+
253
+ class PromptTool(BaseTool):
254
+ name: str = mcp_prompt.name
255
+ description: str = mcp_prompt.description
256
+
257
+ args_schema: type[BaseModel] = InputSchema
258
+ tool_connector: BaseConnector = connector
259
+ handle_tool_error: bool = True
260
+
261
+ def _run(self, **kwargs: Any) -> NoReturn:
262
+ raise NotImplementedError("Prompt tools only support async operations")
263
+
264
+ async def _arun(self, **kwargs: Any) -> Any:
265
+ logger.debug(f'Prompt tool: "{self.name}" called with args: {kwargs}')
266
+ try:
267
+ result = await self.tool_connector.get_prompt(self.name, kwargs)
268
+ return result.messages
269
+ except Exception as e:
270
+ if self.handle_tool_error:
271
+ return f"Error fetching prompt: {str(e)}"
272
+ raise
273
+
274
+ return PromptTool()
@@ -6,6 +6,7 @@ and sessions from configuration.
6
6
  """
7
7
 
8
8
  import json
9
+ import warnings
9
10
  from typing import Any
10
11
 
11
12
  from .config import create_connector_from_config, load_config_file
@@ -115,12 +116,12 @@ class MCPClient:
115
116
  The created MCPSession.
116
117
 
117
118
  Raises:
118
- ValueError: If no servers are configured or the specified server doesn't exist.
119
+ ValueError: If the specified server doesn't exist.
119
120
  """
120
121
  # Get server config
121
122
  servers = self.config.get("mcpServers", {})
122
123
  if not servers:
123
- logger.warning("No MCP servers defined in config")
124
+ warnings.warn("No MCP servers defined in config", UserWarning, stacklevel=2)
124
125
  return None
125
126
 
126
127
  if server_name not in servers:
@@ -153,13 +154,13 @@ class MCPClient:
153
154
  Returns:
154
155
  The created MCPSession. If server_name is None, returns the first created session.
155
156
 
156
- Raises:
157
- ValueError: If no servers are configured or the specified server doesn't exist.
157
+ Warns:
158
+ Warning: If no servers are configured.
158
159
  """
159
160
  # Get server config
160
161
  servers = self.config.get("mcpServers", {})
161
162
  if not servers:
162
- logger.warning("No MCP servers defined in config")
163
+ warnings.warn("No MCP servers defined in config", UserWarning, stacklevel=2)
163
164
  return {}
164
165
 
165
166
  # Create sessions for all servers
@@ -9,7 +9,8 @@ from abc import ABC, abstractmethod
9
9
  from typing import Any
10
10
 
11
11
  from mcp import ClientSession
12
- from mcp.types import CallToolResult, Tool
12
+ from mcp.shared.exceptions import McpError
13
+ from mcp.types import CallToolResult, GetPromptResult, Prompt, ReadResourceResult, Resource, Tool
13
14
 
14
15
  from ..logging import logger
15
16
  from ..task_managers import ConnectionManager
@@ -26,6 +27,8 @@ class BaseConnector(ABC):
26
27
  self.client: ClientSession | None = None
27
28
  self._connection_manager: ConnectionManager | None = None
28
29
  self._tools: list[Tool] | None = None
30
+ self._resources: list[Resource] | None = None
31
+ self._prompts: list[Prompt] | None = None
29
32
  self._connected = False
30
33
 
31
34
  @abstractmethod
@@ -74,6 +77,8 @@ class BaseConnector(ABC):
74
77
 
75
78
  # Reset tools
76
79
  self._tools = None
80
+ self._resources = None
81
+ self._prompts = None
77
82
 
78
83
  if errors:
79
84
  logger.warning(f"Encountered {len(errors)} errors during resource cleanup")
@@ -88,21 +93,58 @@ class BaseConnector(ABC):
88
93
  # Initialize the session
89
94
  result = await self.client.initialize()
90
95
 
91
- # Get available tools
92
- tools_result = await self.client.list_tools()
93
- self._tools = tools_result.tools
94
-
95
- logger.debug(f"MCP session initialized with {len(self._tools)} tools")
96
+ server_capabilities = result.capabilities
97
+
98
+ if server_capabilities.tools:
99
+ # Get available tools
100
+ tools_result = await self.list_tools()
101
+ self._tools = tools_result or []
102
+ else:
103
+ self._tools = []
104
+
105
+ if server_capabilities.resources:
106
+ # Get available resources
107
+ resources_result = await self.list_resources()
108
+ self._resources = resources_result or []
109
+ else:
110
+ self._resources = []
111
+
112
+ if server_capabilities.prompts:
113
+ # Get available prompts
114
+ prompts_result = await self.list_prompts()
115
+ self._prompts = prompts_result or []
116
+ else:
117
+ self._prompts = []
118
+
119
+ logger.debug(
120
+ f"MCP session initialized with {len(self._tools)} tools, "
121
+ f"{len(self._resources)} resources, "
122
+ f"and {len(self._prompts)} prompts"
123
+ )
96
124
 
97
125
  return result
98
126
 
99
127
  @property
100
128
  def tools(self) -> list[Tool]:
101
129
  """Get the list of available tools."""
102
- if not self._tools:
130
+ if self._tools is None:
103
131
  raise RuntimeError("MCP client is not initialized")
104
132
  return self._tools
105
133
 
134
+ @property
135
+ def resources(self) -> list[Resource]:
136
+ """Get the list of available resources."""
137
+ if self._resources is None:
138
+ raise RuntimeError("MCP client is not initialized")
139
+ return self._resources
140
+
141
+ @property
142
+ def prompts(self) -> list[Prompt]:
143
+ """Get the list of available prompts."""
144
+ if self._prompts is None:
145
+ raise RuntimeError("MCP client is not initialized")
146
+ return self._prompts
147
+
106
148
  async def call_tool(self, name: str, arguments: dict[str, Any]) -> CallToolResult:
107
149
  """Call an MCP tool with the given arguments."""
108
150
  if not self.client:
@@ -113,43 +155,64 @@ class BaseConnector(ABC):
113
155
  logger.debug(f"Tool '{name}' called with result: {result}")
114
156
  return result
115
157
 
116
- async def list_resources(self) -> list[dict[str, Any]]:
158
+ async def list_tools(self) -> list[Tool]:
159
+ """List all available tools from the MCP implementation."""
160
+ if not self.client:
161
+ raise RuntimeError("MCP client is not connected")
162
+
163
+ logger.debug("Listing tools")
164
+ try:
165
+ result = await self.client.list_tools()
166
+ return result.tools
167
+ except McpError as e:
168
+ logger.error(f"Error listing tools: {e}")
169
+ return []
170
+
171
+ async def list_resources(self) -> list[Resource]:
117
172
  """List all available resources from the MCP implementation."""
118
173
  if not self.client:
119
174
  raise RuntimeError("MCP client is not connected")
120
175
 
121
176
  logger.debug("Listing resources")
122
- resources = await self.client.list_resources()
123
- return resources
124
-
125
- async def read_resource(self, uri: str) -> tuple[bytes, str]:
177
+ try:
178
+ result = await self.client.list_resources()
179
+ return result.resources
180
+ except McpError as e:
181
+ logger.error(f"Error listing resources: {e}")
182
+ return []
183
+
184
+ async def read_resource(self, uri: str) -> ReadResourceResult:
126
185
  """Read a resource by URI."""
127
186
  if not self.client:
128
187
  raise RuntimeError("MCP client is not connected")
129
188
 
130
189
  logger.debug(f"Reading resource: {uri}")
131
- resource = await self.client.read_resource(uri)
132
- return resource.content, resource.mimeType
190
+ result = await self.client.read_resource(uri)
191
+ return result
133
192
 
134
- async def list_prompts(self) -> list[dict[str, Any]]:
193
+ async def list_prompts(self) -> list[Prompt]:
135
194
  """List all available prompts from the MCP implementation."""
136
195
  if not self.client:
137
196
  raise RuntimeError("MCP client is not connected")
138
197
 
139
198
  logger.debug("Listing prompts")
140
- prompts = await self.client.list_prompts()
141
- return prompts
199
+ try:
200
+ result = await self.client.list_prompts()
201
+ return result.prompts
202
+ except McpError as e:
203
+ logger.error(f"Error listing prompts: {e}")
204
+ return []
142
205
 
143
206
  async def get_prompt(
144
207
  self, name: str, arguments: dict[str, Any] | None = None
145
- ) -> tuple[bytes, str]:
208
+ ) -> GetPromptResult:
146
209
  """Get a prompt by name."""
147
210
  if not self.client:
148
211
  raise RuntimeError("MCP client is not connected")
149
212
 
150
213
  logger.debug(f"Getting prompt: {name}")
151
- prompt = await self.client.get_prompt(name, arguments)
152
- return prompt
214
+ result = await self.client.get_prompt(name, arguments)
215
+ return result
153
216
 
154
217
  async def request(self, method: str, params: dict[str, Any] | None = None) -> Any:
155
218
  """Send a raw request to the MCP implementation."""
@@ -7,6 +7,8 @@ which handles authentication, initialization, and tool discovery.
7
7
 
8
8
  from typing import Any
9
9
 
10
+ from mcp.types import Tool
11
+
10
12
  from .connectors.base import BaseConnector
11
13
 
12
14
 
@@ -30,7 +32,7 @@ class MCPSession:
30
32
  """
31
33
  self.connector = connector
32
34
  self.session_info: dict[str, Any] | None = None
33
- self.tools: list[dict[str, Any]] = []
35
+ self.tools: list[Tool] = []
34
36
  self.auto_connect = auto_connect
35
37
 
36
38
  async def __aenter__(self) -> "MCPSession":
@@ -73,9 +75,6 @@ class MCPSession:
73
75
  # Initialize the session
74
76
  self.session_info = await self.connector.initialize()
75
77
 
76
- # Discover available tools
77
- await self.discover_tools()
78
-
79
78
  return self.session_info
80
79
 
81
80
  @property
@@ -86,28 +85,3 @@ class MCPSession:
86
85
  True if the connector is connected, False otherwise.
87
86
  """
88
87
  return hasattr(self.connector, "client") and self.connector.client is not None
89
-
90
- async def discover_tools(self) -> list[dict[str, Any]]:
91
- """Discover available tools from the MCP implementation.
92
-
93
- Returns:
94
- The list of available tools in MCP format.
95
- """
96
- self.tools = self.connector.tools
97
- return self.tools
98
-
99
- async def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
100
- """Call an MCP tool with the given arguments.
101
-
102
- Args:
103
- name: The name of the tool to call.
104
- arguments: The arguments to pass to the tool.
105
-
106
- Returns:
107
- The result of the tool call.
108
- """
109
- # Make sure we're connected
110
- if not self.is_connected and self.auto_connect:
111
- await self.connect()
112
-
113
- return await self.connector.call_tool(name, arguments)
@@ -46,14 +46,12 @@ class ConnectionManager(Generic[T], ABC):
46
46
  pass
47
47
 
48
48
  @abstractmethod
49
- async def _close_connection(self, connection: T) -> None:
49
+ async def _close_connection(self) -> None:
50
50
  """Close the connection.
51
51
 
52
52
  This method should be implemented by subclasses to close
53
53
  the specific type of connection.
54
54
 
55
- Args:
56
- connection: The connection to close.
57
55
  """
58
56
  pass
59
57
 
@@ -139,10 +137,10 @@ class ConnectionManager(Generic[T], ABC):
139
137
  self._ready_event.set()
140
138
 
141
139
  finally:
142
- # Close the connection if it was established
140
+ # Close the connection if it was establishedSUPABASE_URL
143
141
  if self._connection is not None:
144
142
  try:
145
- await self._close_connection(self._connection)
143
+ await self._close_connection()
146
144
  except Exception as e:
147
145
  logger.warning(f"Error closing connection in {self.__class__.__name__}: {e}")
148
146
  self._connection = None