hammad-python 0.0.30__py3-none-any.whl → 0.0.32__py3-none-any.whl
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.
- ham/__init__.py +200 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.32.dist-info}/METADATA +6 -32
- hammad_python-0.0.32.dist-info/RECORD +6 -0
- hammad/__init__.py +0 -84
- hammad/_internal.py +0 -256
- hammad/_main.py +0 -226
- hammad/cache/__init__.py +0 -40
- hammad/cache/base_cache.py +0 -181
- hammad/cache/cache.py +0 -169
- hammad/cache/decorators.py +0 -261
- hammad/cache/file_cache.py +0 -80
- hammad/cache/ttl_cache.py +0 -74
- hammad/cli/__init__.py +0 -33
- hammad/cli/animations.py +0 -573
- hammad/cli/plugins.py +0 -867
- hammad/cli/styles/__init__.py +0 -55
- hammad/cli/styles/settings.py +0 -139
- hammad/cli/styles/types.py +0 -358
- hammad/cli/styles/utils.py +0 -634
- hammad/data/__init__.py +0 -90
- hammad/data/collections/__init__.py +0 -49
- hammad/data/collections/collection.py +0 -326
- hammad/data/collections/indexes/__init__.py +0 -37
- hammad/data/collections/indexes/qdrant/__init__.py +0 -1
- hammad/data/collections/indexes/qdrant/index.py +0 -723
- hammad/data/collections/indexes/qdrant/settings.py +0 -94
- hammad/data/collections/indexes/qdrant/utils.py +0 -210
- hammad/data/collections/indexes/tantivy/__init__.py +0 -1
- hammad/data/collections/indexes/tantivy/index.py +0 -426
- hammad/data/collections/indexes/tantivy/settings.py +0 -40
- hammad/data/collections/indexes/tantivy/utils.py +0 -176
- hammad/data/configurations/__init__.py +0 -35
- hammad/data/configurations/configuration.py +0 -564
- hammad/data/models/__init__.py +0 -50
- hammad/data/models/extensions/__init__.py +0 -4
- hammad/data/models/extensions/pydantic/__init__.py +0 -42
- hammad/data/models/extensions/pydantic/converters.py +0 -759
- hammad/data/models/fields.py +0 -546
- hammad/data/models/model.py +0 -1078
- hammad/data/models/utils.py +0 -280
- hammad/data/sql/__init__.py +0 -24
- hammad/data/sql/database.py +0 -576
- hammad/data/sql/types.py +0 -127
- hammad/data/types/__init__.py +0 -75
- hammad/data/types/file.py +0 -431
- hammad/data/types/multimodal/__init__.py +0 -36
- hammad/data/types/multimodal/audio.py +0 -200
- hammad/data/types/multimodal/image.py +0 -182
- hammad/data/types/text.py +0 -1308
- hammad/formatting/__init__.py +0 -33
- hammad/formatting/json/__init__.py +0 -27
- hammad/formatting/json/converters.py +0 -158
- hammad/formatting/text/__init__.py +0 -63
- hammad/formatting/text/converters.py +0 -723
- hammad/formatting/text/markdown.py +0 -131
- hammad/formatting/yaml/__init__.py +0 -26
- hammad/formatting/yaml/converters.py +0 -5
- hammad/genai/__init__.py +0 -217
- hammad/genai/a2a/__init__.py +0 -32
- hammad/genai/a2a/workers.py +0 -552
- hammad/genai/agents/__init__.py +0 -59
- hammad/genai/agents/agent.py +0 -1973
- hammad/genai/agents/run.py +0 -1024
- hammad/genai/agents/types/__init__.py +0 -42
- hammad/genai/agents/types/agent_context.py +0 -13
- hammad/genai/agents/types/agent_event.py +0 -128
- hammad/genai/agents/types/agent_hooks.py +0 -220
- hammad/genai/agents/types/agent_messages.py +0 -31
- hammad/genai/agents/types/agent_response.py +0 -125
- hammad/genai/agents/types/agent_stream.py +0 -327
- hammad/genai/graphs/__init__.py +0 -125
- hammad/genai/graphs/_utils.py +0 -190
- hammad/genai/graphs/base.py +0 -1828
- hammad/genai/graphs/plugins.py +0 -316
- hammad/genai/graphs/types.py +0 -638
- hammad/genai/models/__init__.py +0 -1
- hammad/genai/models/embeddings/__init__.py +0 -43
- hammad/genai/models/embeddings/model.py +0 -226
- hammad/genai/models/embeddings/run.py +0 -163
- hammad/genai/models/embeddings/types/__init__.py +0 -37
- hammad/genai/models/embeddings/types/embedding_model_name.py +0 -75
- hammad/genai/models/embeddings/types/embedding_model_response.py +0 -76
- hammad/genai/models/embeddings/types/embedding_model_run_params.py +0 -66
- hammad/genai/models/embeddings/types/embedding_model_settings.py +0 -47
- hammad/genai/models/language/__init__.py +0 -57
- hammad/genai/models/language/model.py +0 -1098
- hammad/genai/models/language/run.py +0 -878
- hammad/genai/models/language/types/__init__.py +0 -40
- hammad/genai/models/language/types/language_model_instructor_mode.py +0 -47
- hammad/genai/models/language/types/language_model_messages.py +0 -28
- hammad/genai/models/language/types/language_model_name.py +0 -239
- hammad/genai/models/language/types/language_model_request.py +0 -127
- hammad/genai/models/language/types/language_model_response.py +0 -217
- hammad/genai/models/language/types/language_model_response_chunk.py +0 -56
- hammad/genai/models/language/types/language_model_settings.py +0 -89
- hammad/genai/models/language/types/language_model_stream.py +0 -600
- hammad/genai/models/language/utils/__init__.py +0 -28
- hammad/genai/models/language/utils/requests.py +0 -421
- hammad/genai/models/language/utils/structured_outputs.py +0 -135
- hammad/genai/models/model_provider.py +0 -4
- hammad/genai/models/multimodal.py +0 -47
- hammad/genai/models/reranking.py +0 -26
- hammad/genai/types/__init__.py +0 -1
- hammad/genai/types/base.py +0 -215
- hammad/genai/types/history.py +0 -290
- hammad/genai/types/tools.py +0 -507
- hammad/logging/__init__.py +0 -35
- hammad/logging/decorators.py +0 -834
- hammad/logging/logger.py +0 -1018
- hammad/mcp/__init__.py +0 -53
- hammad/mcp/client/__init__.py +0 -35
- hammad/mcp/client/client.py +0 -624
- hammad/mcp/client/client_service.py +0 -400
- hammad/mcp/client/settings.py +0 -178
- hammad/mcp/servers/__init__.py +0 -26
- hammad/mcp/servers/launcher.py +0 -1161
- hammad/runtime/__init__.py +0 -32
- hammad/runtime/decorators.py +0 -142
- hammad/runtime/run.py +0 -299
- hammad/service/__init__.py +0 -49
- hammad/service/create.py +0 -527
- hammad/service/decorators.py +0 -283
- hammad/types.py +0 -288
- hammad/typing/__init__.py +0 -435
- hammad/web/__init__.py +0 -43
- hammad/web/http/__init__.py +0 -1
- hammad/web/http/client.py +0 -944
- hammad/web/models.py +0 -275
- hammad/web/openapi/__init__.py +0 -1
- hammad/web/openapi/client.py +0 -740
- hammad/web/search/__init__.py +0 -1
- hammad/web/search/client.py +0 -1023
- hammad/web/utils.py +0 -472
- hammad_python-0.0.30.dist-info/RECORD +0 -135
- {hammad → ham}/py.typed +0 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.32.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.30.dist-info → hammad_python-0.0.32.dist-info}/licenses/LICENSE +0 -0
@@ -1,400 +0,0 @@
|
|
1
|
-
"""hammad.mcp.client.client_service
|
2
|
-
|
3
|
-
Contains the `MCPClientService` class. (Ported from
|
4
|
-
OpenAI-Agents)
|
5
|
-
"""
|
6
|
-
|
7
|
-
from __future__ import annotations
|
8
|
-
|
9
|
-
import abc
|
10
|
-
import asyncio
|
11
|
-
from contextlib import AbstractAsyncContextManager, AsyncExitStack
|
12
|
-
from datetime import timedelta
|
13
|
-
from pathlib import Path
|
14
|
-
from typing import Any, Literal
|
15
|
-
|
16
|
-
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
17
|
-
|
18
|
-
try:
|
19
|
-
from mcp import ClientSession, StdioServerParameters, Tool as MCPTool, stdio_client
|
20
|
-
from mcp.client.sse import sse_client
|
21
|
-
from mcp.client.streamable_http import GetSessionIdCallback, streamablehttp_client
|
22
|
-
from mcp.shared.message import SessionMessage
|
23
|
-
from mcp.types import CallToolResult, InitializeResult
|
24
|
-
except ImportError:
|
25
|
-
MCPTool = Any
|
26
|
-
ClientSession = Any
|
27
|
-
StdioServerParameters = Any
|
28
|
-
stdio_client = Any
|
29
|
-
sse_client = Any
|
30
|
-
GetSessionIdCallback = Any
|
31
|
-
streamablehttp_client = Any
|
32
|
-
SessionMessage = Any
|
33
|
-
CallToolResult = Any
|
34
|
-
InitializeResult = Any
|
35
|
-
|
36
|
-
import logging
|
37
|
-
|
38
|
-
logger = logging.getLogger(__name__)
|
39
|
-
|
40
|
-
from .settings import (
|
41
|
-
MCPClientStdioSettings,
|
42
|
-
MCPClientSseSettings,
|
43
|
-
MCPClientStreamableHttpSettings,
|
44
|
-
)
|
45
|
-
|
46
|
-
|
47
|
-
__all__ = (
|
48
|
-
"MCPClientService",
|
49
|
-
"MCPClientServiceStdio",
|
50
|
-
"MCPClientServiceSse",
|
51
|
-
"MCPClientServiceStreamableHttp",
|
52
|
-
"UserError",
|
53
|
-
)
|
54
|
-
|
55
|
-
|
56
|
-
class UserError(Exception):
|
57
|
-
"""Exception raised when the user makes an error."""
|
58
|
-
|
59
|
-
pass
|
60
|
-
|
61
|
-
|
62
|
-
class MCPClientService(abc.ABC):
|
63
|
-
"""Base class for Model Context Protocol client services."""
|
64
|
-
|
65
|
-
@abc.abstractmethod
|
66
|
-
async def connect(self):
|
67
|
-
"""Connect to the server. For example, this might mean spawning a subprocess or
|
68
|
-
opening a network connection. The server is expected to remain connected until
|
69
|
-
`cleanup()` is called.
|
70
|
-
"""
|
71
|
-
pass
|
72
|
-
|
73
|
-
@property
|
74
|
-
@abc.abstractmethod
|
75
|
-
def name(self) -> str:
|
76
|
-
"""A readable name for the client service."""
|
77
|
-
pass
|
78
|
-
|
79
|
-
@abc.abstractmethod
|
80
|
-
async def cleanup(self):
|
81
|
-
"""Cleanup the client service. For example, this might mean closing a subprocess or
|
82
|
-
closing a network connection.
|
83
|
-
"""
|
84
|
-
pass
|
85
|
-
|
86
|
-
@abc.abstractmethod
|
87
|
-
async def list_tools(self) -> list[MCPTool]:
|
88
|
-
"""List the tools available on the server."""
|
89
|
-
pass
|
90
|
-
|
91
|
-
@abc.abstractmethod
|
92
|
-
async def call_tool(
|
93
|
-
self, tool_name: str, arguments: dict[str, Any] | None
|
94
|
-
) -> CallToolResult:
|
95
|
-
"""Invoke a tool on the server."""
|
96
|
-
pass
|
97
|
-
|
98
|
-
|
99
|
-
class _MCPClientServiceWithClientSession(MCPClientService, abc.ABC):
|
100
|
-
"""Base class for MCP client services that use a `ClientSession` to communicate with the server."""
|
101
|
-
|
102
|
-
def __init__(
|
103
|
-
self, cache_tools_list: bool, client_session_timeout_seconds: float | None
|
104
|
-
):
|
105
|
-
"""
|
106
|
-
Args:
|
107
|
-
cache_tools_list: Whether to cache the tools list. If `True`, the tools list will be
|
108
|
-
cached and only fetched from the server once. If `False`, the tools list will be
|
109
|
-
fetched from the server on each call to `list_tools()`. The cache can be invalidated
|
110
|
-
by calling `invalidate_tools_cache()`. You should set this to `True` if you know the
|
111
|
-
server will not change its tools list, because it can drastically improve latency
|
112
|
-
(by avoiding a round-trip to the server every time).
|
113
|
-
|
114
|
-
client_session_timeout_seconds: the read timeout passed to the MCP ClientSession.
|
115
|
-
"""
|
116
|
-
self.session: ClientSession | None = None
|
117
|
-
self.exit_stack: AsyncExitStack = AsyncExitStack()
|
118
|
-
self._cleanup_lock: asyncio.Lock = asyncio.Lock()
|
119
|
-
self.cache_tools_list = cache_tools_list
|
120
|
-
self.server_initialize_result: InitializeResult | None = None
|
121
|
-
|
122
|
-
self.client_session_timeout_seconds = client_session_timeout_seconds
|
123
|
-
|
124
|
-
# The cache is always dirty at startup, so that we fetch tools at least once
|
125
|
-
self._cache_dirty = True
|
126
|
-
self._tools_list: list[MCPTool] | None = None
|
127
|
-
|
128
|
-
@abc.abstractmethod
|
129
|
-
def create_streams(
|
130
|
-
self,
|
131
|
-
) -> AbstractAsyncContextManager[
|
132
|
-
tuple[
|
133
|
-
MemoryObjectReceiveStream[SessionMessage | Exception],
|
134
|
-
MemoryObjectSendStream[SessionMessage],
|
135
|
-
GetSessionIdCallback | None,
|
136
|
-
]
|
137
|
-
]:
|
138
|
-
"""Create the streams for the server."""
|
139
|
-
pass
|
140
|
-
|
141
|
-
async def __aenter__(self):
|
142
|
-
await self.connect()
|
143
|
-
return self
|
144
|
-
|
145
|
-
async def __aexit__(self, exc_type, exc_value, traceback):
|
146
|
-
await self.cleanup()
|
147
|
-
|
148
|
-
def invalidate_tools_cache(self):
|
149
|
-
"""Invalidate the tools cache."""
|
150
|
-
self._cache_dirty = True
|
151
|
-
|
152
|
-
async def connect(self):
|
153
|
-
"""Connect to the server."""
|
154
|
-
try:
|
155
|
-
transport = await self.exit_stack.enter_async_context(self.create_streams())
|
156
|
-
# streamablehttp_client returns (read, write, get_session_id)
|
157
|
-
# sse_client returns (read, write)
|
158
|
-
|
159
|
-
read, write, *_ = transport
|
160
|
-
|
161
|
-
session = await self.exit_stack.enter_async_context(
|
162
|
-
ClientSession(
|
163
|
-
read,
|
164
|
-
write,
|
165
|
-
timedelta(seconds=self.client_session_timeout_seconds)
|
166
|
-
if self.client_session_timeout_seconds
|
167
|
-
else None,
|
168
|
-
)
|
169
|
-
)
|
170
|
-
server_result = await session.initialize()
|
171
|
-
self.server_initialize_result = server_result
|
172
|
-
self.session = session
|
173
|
-
except Exception as e:
|
174
|
-
logger.error(f"Error initializing MCP server: {e}")
|
175
|
-
await self.cleanup()
|
176
|
-
raise
|
177
|
-
|
178
|
-
async def list_tools(self) -> list[MCPTool]:
|
179
|
-
"""List the tools available on the server."""
|
180
|
-
if not self.session:
|
181
|
-
raise UserError(
|
182
|
-
"Server not initialized. Make sure you call `connect()` first."
|
183
|
-
)
|
184
|
-
|
185
|
-
# Return from cache if caching is enabled, we have tools, and the cache is not dirty
|
186
|
-
if self.cache_tools_list and not self._cache_dirty and self._tools_list:
|
187
|
-
return self._tools_list
|
188
|
-
|
189
|
-
# Reset the cache dirty to False
|
190
|
-
self._cache_dirty = False
|
191
|
-
|
192
|
-
# Fetch the tools from the server
|
193
|
-
self._tools_list = (await self.session.list_tools()).tools
|
194
|
-
return self._tools_list
|
195
|
-
|
196
|
-
async def call_tool(
|
197
|
-
self, tool_name: str, arguments: dict[str, Any] | None
|
198
|
-
) -> CallToolResult:
|
199
|
-
"""Invoke a tool on the server."""
|
200
|
-
if not self.session:
|
201
|
-
raise UserError(
|
202
|
-
"Server not initialized. Make sure you call `connect()` first."
|
203
|
-
)
|
204
|
-
|
205
|
-
return await self.session.call_tool(tool_name, arguments)
|
206
|
-
|
207
|
-
async def cleanup(self):
|
208
|
-
"""Cleanup the server."""
|
209
|
-
async with self._cleanup_lock:
|
210
|
-
try:
|
211
|
-
await self.exit_stack.aclose()
|
212
|
-
except Exception as e:
|
213
|
-
logger.error(f"Error cleaning up server: {e}")
|
214
|
-
finally:
|
215
|
-
self.session = None
|
216
|
-
|
217
|
-
|
218
|
-
class MCPClientServiceStdio(_MCPClientServiceWithClientSession):
|
219
|
-
"""MCP client service implementation that uses the stdio transport. See the [spec]
|
220
|
-
(https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#stdio) for
|
221
|
-
details.
|
222
|
-
"""
|
223
|
-
|
224
|
-
def __init__(
|
225
|
-
self,
|
226
|
-
settings: MCPClientStdioSettings,
|
227
|
-
cache_tools_list: bool = False,
|
228
|
-
name: str | None = None,
|
229
|
-
client_session_timeout_seconds: float | None = 5,
|
230
|
-
):
|
231
|
-
"""Create a new MCP client service based on the stdio transport.
|
232
|
-
|
233
|
-
Args:
|
234
|
-
settings: The settings that configure the client service. This includes the command to run to
|
235
|
-
start the server, the args to pass to the command, the environment variables to
|
236
|
-
set for the server, the working directory to use when spawning the process, and
|
237
|
-
the text encoding used when sending/receiving messages to the server.
|
238
|
-
cache_tools_list: Whether to cache the tools list. If `True`, the tools list will be
|
239
|
-
cached and only fetched from the server once. If `False`, the tools list will be
|
240
|
-
fetched from the server on each call to `list_tools()`. The cache can be
|
241
|
-
invalidated by calling `invalidate_tools_cache()`. You should set this to `True`
|
242
|
-
if you know the server will not change its tools list, because it can drastically
|
243
|
-
improve latency (by avoiding a round-trip to the server every time).
|
244
|
-
name: A readable name for the client service. If not provided, we'll create one from the
|
245
|
-
command.
|
246
|
-
client_session_timeout_seconds: the read timeout passed to the MCP ClientSession.
|
247
|
-
"""
|
248
|
-
super().__init__(cache_tools_list, client_session_timeout_seconds)
|
249
|
-
|
250
|
-
self.params = StdioServerParameters(
|
251
|
-
command=settings["command"],
|
252
|
-
args=settings.get("args", []),
|
253
|
-
env=settings.get("env"),
|
254
|
-
cwd=settings.get("cwd"),
|
255
|
-
encoding=settings.get("encoding", "utf-8"),
|
256
|
-
encoding_error_handler=settings.get("encoding_error_handler", "strict"),
|
257
|
-
)
|
258
|
-
|
259
|
-
self._name = name or f"stdio: {self.params.command}"
|
260
|
-
|
261
|
-
def create_streams(
|
262
|
-
self,
|
263
|
-
) -> AbstractAsyncContextManager[
|
264
|
-
tuple[
|
265
|
-
MemoryObjectReceiveStream[SessionMessage | Exception],
|
266
|
-
MemoryObjectSendStream[SessionMessage],
|
267
|
-
GetSessionIdCallback | None,
|
268
|
-
]
|
269
|
-
]:
|
270
|
-
"""Create the streams for the server."""
|
271
|
-
return stdio_client(self.params)
|
272
|
-
|
273
|
-
@property
|
274
|
-
def name(self) -> str:
|
275
|
-
"""A readable name for the client service."""
|
276
|
-
return self._name
|
277
|
-
|
278
|
-
|
279
|
-
class MCPClientServiceSse(_MCPClientServiceWithClientSession):
|
280
|
-
"""MCP client service implementation that uses the HTTP with SSE transport. See the [spec]
|
281
|
-
(https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#http-with-sse)
|
282
|
-
for details.
|
283
|
-
"""
|
284
|
-
|
285
|
-
def __init__(
|
286
|
-
self,
|
287
|
-
settings: MCPClientSseSettings,
|
288
|
-
cache_tools_list: bool = False,
|
289
|
-
name: str | None = None,
|
290
|
-
client_session_timeout_seconds: float | None = 5,
|
291
|
-
):
|
292
|
-
"""Create a new MCP client service based on the HTTP with SSE transport.
|
293
|
-
|
294
|
-
Args:
|
295
|
-
settings: The settings that configure the client service. This includes the URL of the server,
|
296
|
-
the headers to send to the server, the timeout for the HTTP request, and the
|
297
|
-
timeout for the SSE connection.
|
298
|
-
|
299
|
-
cache_tools_list: Whether to cache the tools list. If `True`, the tools list will be
|
300
|
-
cached and only fetched from the server once. If `False`, the tools list will be
|
301
|
-
fetched from the server on each call to `list_tools()`. The cache can be
|
302
|
-
invalidated by calling `invalidate_tools_cache()`. You should set this to `True`
|
303
|
-
if you know the server will not change its tools list, because it can drastically
|
304
|
-
improve latency (by avoiding a round-trip to the server every time).
|
305
|
-
|
306
|
-
name: A readable name for the client service. If not provided, we'll create one from the
|
307
|
-
URL.
|
308
|
-
|
309
|
-
client_session_timeout_seconds: the read timeout passed to the MCP ClientSession.
|
310
|
-
"""
|
311
|
-
super().__init__(cache_tools_list, client_session_timeout_seconds)
|
312
|
-
|
313
|
-
self.settings = settings
|
314
|
-
self._name = name or f"sse: {self.settings['url']}"
|
315
|
-
|
316
|
-
def create_streams(
|
317
|
-
self,
|
318
|
-
) -> AbstractAsyncContextManager[
|
319
|
-
tuple[
|
320
|
-
MemoryObjectReceiveStream[SessionMessage | Exception],
|
321
|
-
MemoryObjectSendStream[SessionMessage],
|
322
|
-
GetSessionIdCallback | None,
|
323
|
-
]
|
324
|
-
]:
|
325
|
-
"""Create the streams for the server."""
|
326
|
-
return sse_client(
|
327
|
-
url=self.settings["url"],
|
328
|
-
headers=self.settings.get("headers", None),
|
329
|
-
timeout=self.settings.get("timeout", 5),
|
330
|
-
sse_read_timeout=self.settings.get("sse_read_timeout", 60 * 5),
|
331
|
-
)
|
332
|
-
|
333
|
-
@property
|
334
|
-
def name(self) -> str:
|
335
|
-
"""A readable name for the client service."""
|
336
|
-
return self._name
|
337
|
-
|
338
|
-
|
339
|
-
class MCPClientServiceStreamableHttp(_MCPClientServiceWithClientSession):
|
340
|
-
"""MCP client service implementation that uses the Streamable HTTP transport. See the [spec]
|
341
|
-
(https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http)
|
342
|
-
for details.
|
343
|
-
"""
|
344
|
-
|
345
|
-
def __init__(
|
346
|
-
self,
|
347
|
-
settings: MCPClientStreamableHttpSettings,
|
348
|
-
cache_tools_list: bool = False,
|
349
|
-
name: str | None = None,
|
350
|
-
client_session_timeout_seconds: float | None = 5,
|
351
|
-
):
|
352
|
-
"""Create a new MCP client service based on the Streamable HTTP transport.
|
353
|
-
|
354
|
-
Args:
|
355
|
-
settings: The settings that configure the client service. This includes the URL of the server,
|
356
|
-
the headers to send to the server, the timeout for the HTTP request, and the
|
357
|
-
timeout for the Streamable HTTP connection and whether we need to
|
358
|
-
terminate on close.
|
359
|
-
|
360
|
-
cache_tools_list: Whether to cache the tools list. If `True`, the tools list will be
|
361
|
-
cached and only fetched from the server once. If `False`, the tools list will be
|
362
|
-
fetched from the server on each call to `list_tools()`. The cache can be
|
363
|
-
invalidated by calling `invalidate_tools_cache()`. You should set this to `True`
|
364
|
-
if you know the server will not change its tools list, because it can drastically
|
365
|
-
improve latency (by avoiding a round-trip to the server every time).
|
366
|
-
|
367
|
-
name: A readable name for the client service. If not provided, we'll create one from the
|
368
|
-
URL.
|
369
|
-
|
370
|
-
client_session_timeout_seconds: the read timeout passed to the MCP ClientSession.
|
371
|
-
"""
|
372
|
-
super().__init__(cache_tools_list, client_session_timeout_seconds)
|
373
|
-
|
374
|
-
self.settings = settings
|
375
|
-
self._name = name or f"streamable_http: {self.settings['url']}"
|
376
|
-
|
377
|
-
def create_streams(
|
378
|
-
self,
|
379
|
-
) -> AbstractAsyncContextManager[
|
380
|
-
tuple[
|
381
|
-
MemoryObjectReceiveStream[SessionMessage | Exception],
|
382
|
-
MemoryObjectSendStream[SessionMessage],
|
383
|
-
GetSessionIdCallback | None,
|
384
|
-
]
|
385
|
-
]:
|
386
|
-
"""Create the streams for the server."""
|
387
|
-
return streamablehttp_client(
|
388
|
-
url=self.settings["url"],
|
389
|
-
headers=self.settings.get("headers", None),
|
390
|
-
timeout=timedelta(seconds=self.settings.get("timeout", 30)),
|
391
|
-
sse_read_timeout=timedelta(
|
392
|
-
seconds=self.settings.get("sse_read_timeout", 60 * 5)
|
393
|
-
),
|
394
|
-
terminate_on_close=self.settings.get("terminate_on_close", True),
|
395
|
-
)
|
396
|
-
|
397
|
-
@property
|
398
|
-
def name(self) -> str:
|
399
|
-
"""A readable name for the client service."""
|
400
|
-
return self._name
|
hammad/mcp/client/settings.py
DELETED
@@ -1,178 +0,0 @@
|
|
1
|
-
"""hammad.mcp.client.settings
|
2
|
-
|
3
|
-
Contains settings for the 3 different MCP client
|
4
|
-
types.
|
5
|
-
"""
|
6
|
-
|
7
|
-
from __future__ import annotations
|
8
|
-
|
9
|
-
from dataclasses import dataclass
|
10
|
-
from pathlib import Path
|
11
|
-
from typing import Literal
|
12
|
-
from typing_extensions import TypedDict, Required, NotRequired
|
13
|
-
|
14
|
-
__all__ = (
|
15
|
-
"MCPClientStdioSettings",
|
16
|
-
"MCPClientSseSettings",
|
17
|
-
"MCPClientStreamableHttpSettings",
|
18
|
-
"MCPClientSettings",
|
19
|
-
)
|
20
|
-
|
21
|
-
|
22
|
-
class MCPClientStdioSettings(TypedDict, total=False):
|
23
|
-
"""Settings for the stdio MCP client."""
|
24
|
-
|
25
|
-
command: Required[str]
|
26
|
-
"""The executable to run to start the server. For example, `python` or `node`."""
|
27
|
-
args: NotRequired[list[str]]
|
28
|
-
"""Command line args to pass to the `command` executable."""
|
29
|
-
env: NotRequired[dict[str, str]]
|
30
|
-
"""The environment variables to set for the server."""
|
31
|
-
cwd: NotRequired[str | Path]
|
32
|
-
"""The working directory to use when spawning the process."""
|
33
|
-
encoding: NotRequired[str]
|
34
|
-
"""The text encoding used when sending/receiving messages. Defaults to `utf-8`."""
|
35
|
-
encoding_error_handler: NotRequired[Literal["strict", "ignore", "replace"]]
|
36
|
-
"""The text encoding error handler. Defaults to `strict`."""
|
37
|
-
|
38
|
-
|
39
|
-
class MCPClientSseSettings(TypedDict, total=False):
|
40
|
-
"""Settings for the SSE MCP client."""
|
41
|
-
|
42
|
-
url: Required[str]
|
43
|
-
"""The URL to connect to the server."""
|
44
|
-
headers: NotRequired[dict[str, str]]
|
45
|
-
"""The HTTP headers to send with the request."""
|
46
|
-
timeout: NotRequired[float]
|
47
|
-
"""The timeout for the request in seconds."""
|
48
|
-
sse_read_timeout: NotRequired[float]
|
49
|
-
"""The timeout for the SSE event reads in seconds."""
|
50
|
-
|
51
|
-
|
52
|
-
class MCPClientStreamableHttpSettings(TypedDict, total=False):
|
53
|
-
"""Settings for the streamable HTTP MCP client."""
|
54
|
-
|
55
|
-
url: Required[str]
|
56
|
-
"""The URL to connect to the server."""
|
57
|
-
headers: NotRequired[dict[str, str]]
|
58
|
-
"""The HTTP headers to send with the request."""
|
59
|
-
timeout: NotRequired[float]
|
60
|
-
"""The timeout for the request in seconds."""
|
61
|
-
sse_read_timeout: NotRequired[float]
|
62
|
-
"""The timeout for the SSE event reads in seconds."""
|
63
|
-
terminate_on_close: NotRequired[bool]
|
64
|
-
"""Whether to terminate the connection on close."""
|
65
|
-
|
66
|
-
|
67
|
-
MCPClientSettingsType = (
|
68
|
-
MCPClientStdioSettings | MCPClientSseSettings | MCPClientStreamableHttpSettings
|
69
|
-
)
|
70
|
-
"""Union type of the 3 different MCP client settings types."""
|
71
|
-
|
72
|
-
|
73
|
-
@dataclass
|
74
|
-
class MCPClientSettings:
|
75
|
-
"""
|
76
|
-
Helper class to define the settings for the 3 different
|
77
|
-
MCP server types.
|
78
|
-
|
79
|
-
This object can be used within a `MCPClient` object
|
80
|
-
to create a connection to an MCP server.
|
81
|
-
"""
|
82
|
-
|
83
|
-
type: Literal["stdio", "sse", "streamable_http"]
|
84
|
-
"""The type of MCP client this object represents."""
|
85
|
-
settings: MCPClientSettingsType
|
86
|
-
"""The settings for the MCP client."""
|
87
|
-
|
88
|
-
@classmethod
|
89
|
-
def stdio(
|
90
|
-
cls,
|
91
|
-
command: str,
|
92
|
-
args: list[str] | None = None,
|
93
|
-
env: dict[str, str] | None = None,
|
94
|
-
cwd: str | Path | None = None,
|
95
|
-
encoding: str | None = None,
|
96
|
-
encoding_error_handler: Literal["strict", "ignore", "replace"] | None = None,
|
97
|
-
) -> MCPClientSettings:
|
98
|
-
"""Create a settings object for a stdio MCP client.
|
99
|
-
|
100
|
-
Args:
|
101
|
-
command: The executable to run to start the server. For example, `python` or `node`.
|
102
|
-
args: Command line args to pass to the `command` executable.
|
103
|
-
env: The environment variables to set for the server.
|
104
|
-
cwd: The working directory to use when spawning the process.
|
105
|
-
encoding: The text encoding used when sending/receiving messages. Defaults to `utf-8`.
|
106
|
-
encoding_error_handler: The text encoding error handler. Defaults to `strict`.
|
107
|
-
"""
|
108
|
-
return cls(
|
109
|
-
type="stdio",
|
110
|
-
settings={
|
111
|
-
"command": command,
|
112
|
-
"args": args,
|
113
|
-
"env": env,
|
114
|
-
"cwd": cwd,
|
115
|
-
"encoding": encoding,
|
116
|
-
"encoding_error_handler": encoding_error_handler,
|
117
|
-
},
|
118
|
-
)
|
119
|
-
|
120
|
-
@classmethod
|
121
|
-
def sse(
|
122
|
-
cls,
|
123
|
-
url: str,
|
124
|
-
headers: dict[str, str] | None = None,
|
125
|
-
timeout: float | None = None,
|
126
|
-
sse_read_timeout: float | None = None,
|
127
|
-
) -> MCPClientSettings:
|
128
|
-
"""Create a settings object for an SSE MCP client.
|
129
|
-
|
130
|
-
Args:
|
131
|
-
url: The URL to connect to the server.
|
132
|
-
headers: The HTTP headers to send with the request.
|
133
|
-
timeout: The timeout for the request in seconds.
|
134
|
-
sse_read_timeout: The timeout for the SSE event reads in seconds.
|
135
|
-
"""
|
136
|
-
return cls(
|
137
|
-
type="sse",
|
138
|
-
settings={
|
139
|
-
"url": url,
|
140
|
-
"headers": headers,
|
141
|
-
"timeout": timeout,
|
142
|
-
"sse_read_timeout": sse_read_timeout,
|
143
|
-
},
|
144
|
-
)
|
145
|
-
|
146
|
-
@classmethod
|
147
|
-
def streamable_http(
|
148
|
-
cls,
|
149
|
-
url: str,
|
150
|
-
headers: dict[str, str] | None = None,
|
151
|
-
timeout: float | None = None,
|
152
|
-
sse_read_timeout: float | None = None,
|
153
|
-
terminate_on_close: bool | None = None,
|
154
|
-
) -> MCPClientSettings:
|
155
|
-
"""Create a settings object for a streamable HTTP MCP client.
|
156
|
-
|
157
|
-
Args:
|
158
|
-
url: The URL to connect to the server.
|
159
|
-
headers: The HTTP headers to send with the request.
|
160
|
-
timeout: The timeout for the request in seconds.
|
161
|
-
sse_read_timeout: The timeout for the SSE event reads in seconds.
|
162
|
-
terminate_on_close: Whether to terminate the connection on close.
|
163
|
-
"""
|
164
|
-
return cls(
|
165
|
-
type="streamable_http",
|
166
|
-
settings={
|
167
|
-
"url": url,
|
168
|
-
"headers": headers,
|
169
|
-
"timeout": timeout,
|
170
|
-
"sse_read_timeout": sse_read_timeout,
|
171
|
-
"terminate_on_close": terminate_on_close,
|
172
|
-
},
|
173
|
-
)
|
174
|
-
|
175
|
-
|
176
|
-
__all__ = [
|
177
|
-
"MCPClientSettings",
|
178
|
-
]
|
hammad/mcp/servers/__init__.py
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
"""hammad.mcp.servers"""
|
2
|
-
|
3
|
-
from typing import TYPE_CHECKING
|
4
|
-
from ..._internal import create_getattr_importer
|
5
|
-
|
6
|
-
if TYPE_CHECKING:
|
7
|
-
from .launcher import (
|
8
|
-
launch_mcp_servers,
|
9
|
-
MCPServerStdioSettings,
|
10
|
-
MCPServerSseSettings,
|
11
|
-
MCPServerStreamableHttpSettings,
|
12
|
-
)
|
13
|
-
|
14
|
-
__all__ = (
|
15
|
-
"launch_mcp_servers",
|
16
|
-
"MCPServerStdioSettings",
|
17
|
-
"MCPServerSseSettings",
|
18
|
-
"MCPServerStreamableHttpSettings",
|
19
|
-
)
|
20
|
-
|
21
|
-
__getattr__ = create_getattr_importer(__all__)
|
22
|
-
|
23
|
-
|
24
|
-
def __dir__() -> list[str]:
|
25
|
-
"""Get the attributes of the servers module."""
|
26
|
-
return list(__all__)
|