mirascope 1.21.5__py3-none-any.whl → 1.22.0__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.
mirascope/mcp/client.py CHANGED
@@ -1,305 +1,151 @@
1
- import base64
2
- import string
3
- from collections.abc import AsyncGenerator, Awaitable, Callable
4
- from contextlib import asynccontextmanager
5
- from typing import Any, TypeVar
6
-
7
- from anyio import create_memory_object_stream, create_task_group
8
- from anyio.streams.memory import MemoryObjectReceiveStream
1
+ """The `MCPServer` Class and context managers."""
2
+
3
+ import contextlib
4
+ from collections.abc import AsyncIterator, Awaitable, Callable
5
+ from datetime import timedelta
6
+ from typing import Any
7
+
9
8
  from mcp import ClientSession
10
- from mcp.client.stdio import StdioServerParameters, stdio_client
9
+ from mcp.client.session import ListRootsFnT, SamplingFnT
10
+ from mcp.client.sse import sse_client as mcp_sse_client
11
+ from mcp.client.stdio import StdioServerParameters
12
+ from mcp.client.stdio import stdio_client as mcp_stdio_client
11
13
  from mcp.types import (
12
14
  BlobResourceContents,
13
- CallToolResult,
14
- EmbeddedResource,
15
- ImageContent,
16
- JSONRPCMessage,
17
- Prompt,
18
- PromptMessage,
19
15
  Resource,
20
- TextContent,
21
- TextResourceContents,
22
- Tool,
23
16
  )
24
- from pydantic import AnyUrl, BaseModel, ConfigDict, Field, create_model
25
-
26
- from mirascope.core import BaseMessageParam, BaseTool
27
- from mirascope.core.base import AudioPart, DocumentPart, ImagePart
28
-
29
- _BaseToolT = TypeVar("_BaseToolT", bound=BaseTool)
30
-
31
-
32
- def _create_tool_call(
33
- name: str,
34
- call_tool: Callable[[str, dict | None], Awaitable[CallToolResult]],
35
- ) -> Callable[..., Awaitable[list[str | ImageContent | EmbeddedResource]]]:
36
- async def call(self: BaseTool) -> list[str | ImageContent | EmbeddedResource]:
37
- result = await call_tool(name, self.args) # pyright: ignore [reportOptionalCall]
38
- if result.isError:
39
- raise RuntimeError(f"MCP Server returned error: {self._name()}")
40
- parsed_results = []
41
- for content in result.content:
42
- if isinstance(content, TextContent):
43
- parsed_results.append(content.text)
44
- else:
45
- parsed_results.append(content)
46
- return [
47
- content.text if isinstance(content, TextContent) else content
48
- for content in result.content
49
- ]
17
+ from pydantic import AnyUrl
50
18
 
51
- return call
52
-
53
-
54
- def _convert_prompt_message_to_base_message_params(
55
- prompt_message: PromptMessage,
56
- ) -> BaseMessageParam:
57
- """
58
- Convert a single PromptMessage back into a BaseMessageParam.
19
+ from mirascope.core import BaseMessageParam, BaseTool, TextPart
20
+ from mirascope.mcp import _utils
59
21
 
60
- Args:
61
- prompt_message: A PromptMessage instance.
62
22
 
63
- Returns:
64
- A BaseMessageParam instance representing the given prompt_message.
23
+ class MCPClient(ClientSession):
24
+ """The SSE client session that connects to the MCP server.
65
25
 
66
- Raises:
67
- ValueError: If the role is invalid or if the content type is unsupported.
26
+ All of the results from the server are converted into Mirascope-friendly types.
68
27
  """
69
28
 
70
- # Validate role
71
- if prompt_message.role not in ["user", "assistant"]:
72
- raise ValueError(f"invalid role: {prompt_message.role}")
73
-
74
- content = prompt_message.content
75
-
76
- # Handle TextContent
77
- if isinstance(content, TextContent):
78
- # If it's text, we can just return a single string
79
- return BaseMessageParam(role=prompt_message.role, content=content.text)
29
+ _session: ClientSession
80
30
 
81
- # Handle ImageContent
82
- elif isinstance(content, ImageContent):
83
- decoded_image = base64.b64decode(content.data)
84
-
85
- image_part = ImagePart(
86
- type="image",
87
- image=decoded_image,
88
- media_type=content.mimeType,
89
- detail=None, # detail not provided by PromptMessage
90
- )
91
- return BaseMessageParam(role=prompt_message.role, content=[image_part])
92
-
93
- elif isinstance(content, EmbeddedResource):
94
- resource = content.resource
95
- if isinstance(resource, TextResourceContents):
96
- return BaseMessageParam(role=prompt_message.role, content=resource.text)
97
- else:
98
- mime_type = resource.mimeType
99
- if not mime_type:
100
- raise ValueError(
101
- "BlobResourceContents has no mimeType, cannot determine content type."
102
- )
103
-
104
- decoded_data = base64.b64decode(resource.blob)
105
-
106
- if mime_type.startswith("image/"):
107
- # Treat as ImagePart
108
- image_part = ImagePart(
109
- type="image",
110
- image=decoded_data,
111
- media_type=mime_type,
112
- detail=None,
113
- )
114
- return BaseMessageParam(role=prompt_message.role, content=[image_part])
115
- elif mime_type == "application/pdf":
116
- doc_part = DocumentPart(
117
- type="document", media_type=mime_type, document=decoded_data
118
- )
119
- return BaseMessageParam(role=prompt_message.role, content=[doc_part])
120
- elif mime_type.startswith("audio/"):
121
- # Treat as AudioPart
122
- audio_part = AudioPart(
123
- type="audio", media_type=mime_type, audio=decoded_data
124
- )
125
- return BaseMessageParam(role=prompt_message.role, content=[audio_part])
126
- else:
127
- raise ValueError(f"Unsupported mime type: {mime_type}")
128
-
129
- else:
130
- raise ValueError(f"Unsupported content type: {type(content)}")
131
-
132
-
133
- def snake_to_pascal(snake: str) -> str:
134
- return string.capwords(snake.replace("_", " ")).replace(" ", "")
135
-
136
-
137
- def build_object_type(
138
- properties: dict[str, Any], required: list[str], name: str
139
- ) -> type[BaseModel]:
140
- fields = {}
141
- for prop_name, prop_schema in properties.items():
142
- class_name = snake_to_pascal(prop_name)
143
- type_ = json_schema_to_python_type(prop_schema, class_name)
144
- if prop_name in required:
145
- fields[prop_name] = (
146
- type_,
147
- Field(..., description=prop_schema.get("description")),
148
- )
149
- else:
150
- fields[prop_name] = (
151
- type_ | None,
152
- Field(None, description=prop_schema.get("description")),
153
- )
154
-
155
- return create_model(name, __config__=ConfigDict(extra="allow"), **fields)
156
-
157
-
158
- def json_schema_to_python_type(schema: dict[str, Any], name: str = "Model") -> Any: # noqa: ANN401
159
- """
160
- Recursively convert a JSON Schema snippet into a Python type annotation or a dynamically generated pydantic model.
161
- """
162
- json_type = schema.get("type", "any")
163
-
164
- if json_type == "string":
165
- return str
166
- elif json_type == "number":
167
- return float
168
- elif json_type == "integer":
169
- return int
170
- elif json_type == "boolean":
171
- return bool
172
- elif json_type == "array":
173
- # Recursively determine the items type for arrays
174
- items_schema = schema.get("items", {})
175
- items_type = json_schema_to_python_type(items_schema)
176
- return list[items_type]
177
- elif json_type == "object":
178
- # Recursively build a dynamic model for objects
179
- properties = schema.get("properties", {})
180
- required = schema.get("required", [])
181
- return build_object_type(properties, required, name)
182
- else:
183
- # Default fallback
184
- return Any
185
-
186
-
187
- def create_model_from_tool(tool: Tool) -> type[BaseModel]:
188
- """
189
- Create a dynamic pydantic model from the given Tool instance based on its inputSchema.
190
- """
191
- schema = tool.inputSchema
192
- properties = schema.get("properties", {})
193
- required_fields = schema.get("required", [])
194
-
195
- fields = {}
196
- for field_name, field_schema in properties.items():
197
- field_type = json_schema_to_python_type(field_schema)
198
-
199
- if field_name in required_fields:
200
- default = Field(..., description=field_schema.get("description"))
201
- annotation = field_type
202
- else:
203
- default = Field(None, description=field_schema.get("description"))
204
- annotation = field_type | None
205
-
206
- fields[field_name] = (annotation, default)
207
-
208
- return create_model(
209
- snake_to_pascal(tool.name), __config__=ConfigDict(extra="allow"), **fields
210
- )
31
+ def __init__(self, session: ClientSession) -> None:
32
+ """Initializes an instance of `MCPClient`.
211
33
 
34
+ Args:
35
+ session: The original MCP `ClientSession`.
36
+ """
37
+ self._session = session
212
38
 
213
- class MCPClient:
214
- def __init__(self, session: ClientSession) -> None:
215
- self.session: ClientSession = session
39
+ async def list_resources(self) -> list[Resource]: # pyright: ignore [reportIncompatibleMethodOverride]
40
+ """List all resources available on the MCP server.
216
41
 
217
- async def list_resources(self) -> list[Resource]:
218
- result = await self.session.list_resources()
42
+ Returns:
43
+ A list of Resource objects
44
+ """
45
+ result = await self._session.list_resources()
219
46
  return result.resources
220
47
 
221
- async def read_resource(
48
+ async def read_resource( # pyright: ignore [reportIncompatibleMethodOverride]
222
49
  self, uri: str | AnyUrl
223
- ) -> list[str | BlobResourceContents]:
224
- result = await self.session.read_resource(
50
+ ) -> list[TextPart | BlobResourceContents]:
51
+ """Read a resource from the MCP server.
52
+
53
+ Args:
54
+ uri: URI of the resource to read
55
+
56
+ Returns:
57
+ Contents of the resource, either as string or BlobResourceContents
58
+ """
59
+ result = await self._session.read_resource(
225
60
  uri if isinstance(uri, AnyUrl) else AnyUrl(uri)
226
61
  )
227
- parsed_results = []
62
+ parsed_results: list[TextPart | BlobResourceContents] = []
228
63
  for content in result.contents:
229
- if isinstance(content, TextResourceContents):
230
- parsed_results.append(content.text)
231
- else:
64
+ if isinstance(content, BlobResourceContents):
232
65
  parsed_results.append(content)
66
+ else:
67
+ parsed_results.append(TextPart(type="text", text=content.text))
233
68
  return parsed_results
234
69
 
235
- async def list_prompts(self) -> list[Prompt]:
236
- result = await self.session.list_prompts()
70
+ async def list_prompts(self) -> list[Any]: # pyright: ignore [reportIncompatibleMethodOverride]
71
+ """List all prompts available on the MCP server.
72
+
73
+ Returns:
74
+ A list of Prompt objects
75
+ """
76
+ result = await self._session.list_prompts()
237
77
  return result.prompts
238
78
 
239
79
  async def get_prompt_template(
240
80
  self, name: str
241
81
  ) -> Callable[..., Awaitable[list[BaseMessageParam]]]:
82
+ """Get a prompt template from the MCP server.
83
+
84
+ Args:
85
+ name: Name of the prompt template
86
+
87
+ Returns:
88
+ A callable that accepts keyword arguments and returns a list of BaseMessageParam
89
+ """
90
+
91
+ # TODO: wrap this with `llm.prompt` once it's implemented so that they prompts
92
+ # can be run super easily inside of an `llm.context` block.
242
93
  async def async_prompt(**kwargs: str) -> list[BaseMessageParam]:
243
- result = await self.session.get_prompt(name, kwargs)
94
+ result = await self._session.get_prompt(name, kwargs) # type: ignore
244
95
 
245
96
  return [
246
- _convert_prompt_message_to_base_message_params(prompt_message)
97
+ _utils.convert_prompt_message_to_base_message_params(prompt_message)
247
98
  for prompt_message in result.messages
248
99
  ]
249
100
 
250
101
  return async_prompt
251
102
 
252
- async def list_tools(self) -> list[type[BaseModel]]:
253
- list_tool_result = await self.session.list_tools()
103
+ async def list_tools(self) -> list[type[BaseTool]]: # pyright: ignore [reportIncompatibleMethodOverride]
104
+ """List all tools available on the MCP server.
105
+
106
+ Returns:
107
+ A list of dynamically created `BaseTool` types.
108
+ """
109
+ list_tool_result = await self._session.list_tools()
254
110
 
255
111
  converted_tools = []
256
112
  for tool in list_tool_result.tools:
257
- model = create_model_from_tool(tool)
258
- model.call = _create_tool_call(tool.name, self.session.call_tool) # pyright: ignore [reportAttributeAccessIssue]
113
+ model = _utils.create_tool_from_mcp_tool(tool)
114
+ tool_call_method = _utils.create_tool_call(
115
+ tool.name, self._session.call_tool
116
+ )
117
+ model.call = tool_call_method # pyright: ignore [reportAttributeAccessIssue]
259
118
  converted_tools.append(model)
260
119
  return converted_tools
261
120
 
262
121
 
263
- @asynccontextmanager
264
- async def read_stream_exception_filer(
265
- original_read: MemoryObjectReceiveStream[JSONRPCMessage | Exception],
266
- exception_handler: Callable[[Exception], None] | None = None,
267
- ) -> AsyncGenerator[MemoryObjectReceiveStream[JSONRPCMessage], None]:
268
- """
269
- Handle exceptions in the original stream.
270
- default handler is to ignore read stream exception for invalid JSON lines and log them.
271
- Args:
272
- original_read: The original stream to read from.
273
- exception_handler: An optional handler for exception.
274
- """
275
- read_stream_writer, filtered_read = create_memory_object_stream(0)
276
-
277
- async def task() -> None:
278
- try:
279
- async with original_read:
280
- async for msg in original_read:
281
- if isinstance(msg, Exception):
282
- if exception_handler:
283
- exception_handler(msg)
284
- continue
285
- await read_stream_writer.send(msg)
286
- finally:
287
- await read_stream_writer.aclose()
288
-
289
- async with create_task_group() as tg:
290
- tg.start_soon(task)
291
- try:
292
- yield filtered_read
293
- finally:
294
- await filtered_read.aclose()
295
- tg.cancel_scope.cancel()
296
-
297
-
298
- @asynccontextmanager
299
- async def create_mcp_client(
122
+ @contextlib.asynccontextmanager
123
+ async def sse_client(
124
+ url: str,
125
+ list_roots_callback: ListRootsFnT | None = None,
126
+ read_timeout_seconds: timedelta | None = None,
127
+ sampling_callback: SamplingFnT | None = None,
128
+ session: ClientSession | None = None,
129
+ ) -> AsyncIterator[MCPClient]:
130
+ async with (
131
+ mcp_sse_client(url) as (read, write),
132
+ ClientSession(
133
+ read,
134
+ write,
135
+ read_timeout_seconds=read_timeout_seconds,
136
+ sampling_callback=sampling_callback,
137
+ list_roots_callback=list_roots_callback,
138
+ ) as session,
139
+ ):
140
+ await session.initialize()
141
+ yield MCPClient(session)
142
+
143
+
144
+ @contextlib.asynccontextmanager
145
+ async def stdio_client(
300
146
  server_parameters: StdioServerParameters,
301
147
  read_stream_exception_handler: Callable[[Exception], None] | None = None,
302
- ) -> AsyncGenerator[MCPClient, None]:
148
+ ) -> AsyncIterator[MCPClient]:
303
149
  """
304
150
  Create a MCPClient instance with the given server parameters and exception handler.
305
151
 
@@ -311,11 +157,11 @@ async def create_mcp_client(
311
157
 
312
158
  """
313
159
  async with (
314
- stdio_client(server_parameters) as (read, write),
315
- read_stream_exception_filer(
160
+ mcp_stdio_client(server_parameters) as (read, write),
161
+ _utils.read_stream_exception_filer(
316
162
  read, read_stream_exception_handler
317
163
  ) as filtered_read,
318
- ClientSession(filtered_read, write) as session,
164
+ ClientSession(filtered_read, write) as session, # pyright: ignore [reportArgumentType]
319
165
  ):
320
166
  await session.initialize()
321
167
  yield MCPClient(session)
mirascope/mcp/server.py CHANGED
@@ -1,6 +1,7 @@
1
- """MCP server implementation."""
1
+ """The `MCPServer` Class and context managers."""
2
2
 
3
3
  import inspect
4
+ import warnings
4
5
  from collections.abc import Awaitable, Callable, Iterable
5
6
  from typing import Literal, ParamSpec, cast, overload
6
7
 
@@ -117,7 +118,12 @@ def _generate_prompt_from_function(fn: Callable) -> Prompt:
117
118
 
118
119
 
119
120
  class MCPServer:
120
- """MCP server implementation."""
121
+ """MCP server implementation.
122
+
123
+ DEPRECATED: The MCPServer implementation is deprecated and will be removed in a future version.
124
+ Mirascope will only implement the client-side of MCP in the future, allowing it to connect to any
125
+ MCP server implementation, even if not built with Mirascope.
126
+ """
121
127
 
122
128
  def __init__(
123
129
  self,
@@ -134,6 +140,13 @@ class MCPServer:
134
140
  ]
135
141
  | None = None,
136
142
  ) -> None:
143
+ warnings.warn(
144
+ "MCPServer is deprecated and will be removed in a future version. "
145
+ "Mirascope will only implement the client-side of MCP in the future. "
146
+ "We recommend using the official MCP SDK (e.g. `FastMCP`) for server-side implementations.",
147
+ DeprecationWarning,
148
+ stacklevel=2,
149
+ )
137
150
  self.name: str = name
138
151
  self.version: str = version
139
152
  self.server: Server = Server(name)
mirascope/mcp/tools.py CHANGED
@@ -1,10 +1,15 @@
1
1
  """The `MCPTool` class for easy tool usage with MCPTool LLM calls.
2
2
 
3
3
  usage docs: learn/tools.md
4
+
5
+ DEPRECATED: The MCPTool class is deprecated and will be removed in a future version.
6
+ Mirascope will only implement the client-side of MCP in the future, allowing it to connect to any
7
+ MCP server implementation, even if not built with Mirascope.
4
8
  """
5
9
 
6
10
  from __future__ import annotations
7
11
 
12
+ import warnings
8
13
  from typing import Any
9
14
 
10
15
  from pydantic import BaseModel
@@ -34,6 +39,9 @@ class MCPToolToolConfig(ToolConfig, total=False):
34
39
  class MCPTool(BaseTool):
35
40
  """A class for defining tools for MCP LLM calls.
36
41
 
42
+ DEPRECATED: The MCPTool class is deprecated and will be removed in a future version.
43
+ Mirascope will only implement the client-side of MCP in the future.
44
+
37
45
  Example:
38
46
 
39
47
  ```python
@@ -74,6 +82,14 @@ class MCPTool(BaseTool):
74
82
  print(tool_type.tool_schema()) # prints the MCP-specific tool schema
75
83
  ```
76
84
  """
85
+ # Show deprecation warning
86
+ warnings.warn(
87
+ "The MCPTool class is deprecated and will be removed in a future version. "
88
+ "Mirascope will only implement the client-side of MCP in the future. "
89
+ "We recommend using the official MCP SDK (e.g. `FastMCP`) for server-side implementations.",
90
+ DeprecationWarning,
91
+ stacklevel=2,
92
+ )
77
93
  kwargs = {
78
94
  "input_schema": cls.model_json_schema(),
79
95
  "name": cls._name(),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mirascope
3
- Version: 1.21.5
3
+ Version: 1.22.0
4
4
  Summary: LLM abstractions that aren't obstructions
5
5
  Project-URL: Homepage, https://mirascope.com
6
6
  Project-URL: Documentation, https://mirascope.com/WELCOME
@@ -79,7 +79,7 @@ Requires-Dist: litellm<2,>=1.42.12; extra == 'litellm'
79
79
  Provides-Extra: logfire
80
80
  Requires-Dist: logfire<4,>=1.0.0; extra == 'logfire'
81
81
  Provides-Extra: mcp
82
- Requires-Dist: mcp>=1.0.0; extra == 'mcp'
82
+ Requires-Dist: mcp>=1.3.0; extra == 'mcp'
83
83
  Provides-Extra: mistral
84
84
  Requires-Dist: mistralai<2,>=1.0.0; extra == 'mistral'
85
85
  Provides-Extra: openai
@@ -318,6 +318,8 @@ mirascope/core/xai/stream.py,sha256=qnGhY8FCbq4MRYyL_1_WagLhjL5v_Pzu3HOrcQ7D03Q,
318
318
  mirascope/core/xai/tool.py,sha256=W_UaEatgrtHkuDVILQSaBoomN3zFMS9YnXky-Py6MRY,275
319
319
  mirascope/core/xai/_utils/__init__.py,sha256=gHldtQYziYseLvCM0yvH-O87sFdvbP5pvz66hEllaj0,108
320
320
  mirascope/core/xai/_utils/_setup_call.py,sha256=0pCy8SqEQQEFUrCYTe6EEn_z7GuTY7-Gb9hnwkoBbH8,3523
321
+ mirascope/experimental/graphs/__init__.py,sha256=xwvU6T5lqfMXeHZ0IN8cepzkFkSURpeeV7ozokjzaBY,162
322
+ mirascope/experimental/graphs/finite_state_machine.py,sha256=xsiguJCW7nyMuITezYKHevM1R2LIs1rdMeqw5kmJITs,26470
321
323
  mirascope/integrations/__init__.py,sha256=ieLWknpbkO_gABIVl9790YTTCCRO9ISQ35-1SeOSU0Q,392
322
324
  mirascope/integrations/_middleware_factory.py,sha256=rv9MaNgn7AL9Z7qADMRxxD9A_abePqj80Y0SReU9LrE,17305
323
325
  mirascope/integrations/tenacity.py,sha256=jk64MBncCMbgoQMaXQgjxg9Y9UstRqTt2RCeA86pdCU,326
@@ -331,7 +333,7 @@ mirascope/integrations/otel/__init__.py,sha256=OzboYfm3fUNwKTuu08KX83hQHYI4oZYN2
331
333
  mirascope/integrations/otel/_utils.py,sha256=SCVb3MpcpqLpCpumJEbEdINceNdusnyT6iuKPz66sBc,8778
332
334
  mirascope/integrations/otel/_with_hyperdx.py,sha256=f17uxXQk5zZPtyj6zwPwJz5i7atsnUPOoq1LqT8JO0E,1637
333
335
  mirascope/integrations/otel/_with_otel.py,sha256=tbjd6BEbcSfnsm5CWHBoHwbRNrHt6-t4or-SYGQSD-w,1659
334
- mirascope/llm/__init__.py,sha256=zo7QD39zQ6HIU_N2yHrzd6bbcT9YmlikZ84JN_r5k4E,397
336
+ mirascope/llm/__init__.py,sha256=rCukXJUqSGiDGNMMM9g3NS8clnrCxgCpaFIXIG1M660,432
335
337
  mirascope/llm/_call.py,sha256=zPULSy5GLlJszphRvc3WUSFEsmP83Abp0FzbZefTa8Y,13848
336
338
  mirascope/llm/_context.py,sha256=vtHJkLlFfUwyR_hYEHXAw3xunpHhLn67k4kuFw50GR8,12481
337
339
  mirascope/llm/_override.py,sha256=m4MdOhM-aJRIGP7NBJhscq3ISNct6FBPn3jjmryFo_Q,112292
@@ -341,10 +343,11 @@ mirascope/llm/call_response.py,sha256=4w_2Dgt325lEjMaFlg7Iq2AGp8cI0uSaqCa8MR_S5G
341
343
  mirascope/llm/call_response_chunk.py,sha256=bZwO43ipc6PO1VLgGSaAPRqCIUyZD_Ty5oxdJX62yno,1966
342
344
  mirascope/llm/stream.py,sha256=GtUKyLBlKqqZTOKjdL9FLInCXJ0ZOEAe6nymbjKwyTQ,5293
343
345
  mirascope/llm/tool.py,sha256=MQRJBPhP1d-OyOz3PE_VsKmSXca0chySyYO1U9OW8ck,1824
344
- mirascope/mcp/__init__.py,sha256=mGboroTrBbzuZ_8uBssOhkqiJOJ4mNCvaJvS7mhumhg,155
345
- mirascope/mcp/client.py,sha256=ZsjaT6E5i4Cdm3iVp2-JTuDniUlzynWeeJvnBAtuffQ,11123
346
- mirascope/mcp/server.py,sha256=7BjZO3DkaiokEOgJOY9fbEX3M6YwAExN6Kw9s-Dp7Sc,11737
347
- mirascope/mcp/tools.py,sha256=IKQZCiQHh_YxJM-V90U9Z6xPQTYIfWFDBj8khhHDiw4,2254
346
+ mirascope/mcp/__init__.py,sha256=1tvG-fRD-_mr2vF3M49OgTLR7Vw8zdT6LucxnY4prF8,282
347
+ mirascope/mcp/_utils.py,sha256=jyuE28W9Gy6sF17dRNZnqXRBhD_dmJBxIXq99UWmpHk,9221
348
+ mirascope/mcp/client.py,sha256=ZK88o4UelR9htFWAWDbw084ZTrRL8tONdxlIX62adnM,5608
349
+ mirascope/mcp/server.py,sha256=weXBk9ZmUcfZ5SZcR-dGRxvVXRMpn5UsE6ancnK1HZk,12402
350
+ mirascope/mcp/tools.py,sha256=IT7CEqbBBiFc7mkaRpWIepUpuy_oSdcEa4cwGopEYWc,3079
348
351
  mirascope/retries/__init__.py,sha256=xw3jJm-vL4kR10VaKN6A8KGoP2CsAg5_Gy1eWVMgYhQ,249
349
352
  mirascope/retries/fallback.py,sha256=LPSyIfPAHajt3-M_Z1M4ZSbgHjbXghMBbb9fjNQ27UU,4876
350
353
  mirascope/retries/tenacity.py,sha256=stBJPjEpUzP53IBVBFtqY2fUSgmOV1-sIIXZZJ9pvLY,1387
@@ -368,7 +371,7 @@ mirascope/v0/base/ops_utils.py,sha256=1Qq-VIwgHBaYutiZsS2MUQ4OgPC3APyywI5bTiTAmA
368
371
  mirascope/v0/base/prompts.py,sha256=FM2Yz98cSnDceYogiwPrp4BALf3_F3d4fIOCGAkd-SE,1298
369
372
  mirascope/v0/base/types.py,sha256=ZfatJoX0Yl0e3jhv0D_MhiSVHLYUeJsdN3um3iE10zY,352
370
373
  mirascope/v0/base/utils.py,sha256=XREPENRQTu8gpMhHU8RC8qH_am3FfGUvY-dJ6x8i-mw,681
371
- mirascope-1.21.5.dist-info/METADATA,sha256=bvRDe9mKqx1E8dnJnoEz5KQ4FjHkPjMp3rIkMVCzWrc,8730
372
- mirascope-1.21.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
373
- mirascope-1.21.5.dist-info/licenses/LICENSE,sha256=LAs5Q8mdawTsVdONpDGukwsoc4KEUBmmonDEL39b23Y,1072
374
- mirascope-1.21.5.dist-info/RECORD,,
374
+ mirascope-1.22.0.dist-info/METADATA,sha256=2Tod_tF9thr1KIVCfKCVTGfgtBvd9NLgOIdIo5YMvlQ,8730
375
+ mirascope-1.22.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
376
+ mirascope-1.22.0.dist-info/licenses/LICENSE,sha256=LAs5Q8mdawTsVdONpDGukwsoc4KEUBmmonDEL39b23Y,1072
377
+ mirascope-1.22.0.dist-info/RECORD,,