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