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/experimental/graphs/__init__.py +5 -0
- mirascope/experimental/graphs/finite_state_machine.py +714 -0
- mirascope/llm/__init__.py +2 -0
- mirascope/mcp/__init__.py +4 -3
- mirascope/mcp/_utils.py +277 -0
- mirascope/mcp/client.py +105 -259
- mirascope/mcp/server.py +15 -2
- mirascope/mcp/tools.py +16 -0
- {mirascope-1.21.5.dist-info → mirascope-1.22.0.dist-info}/METADATA +2 -2
- {mirascope-1.21.5.dist-info → mirascope-1.22.0.dist-info}/RECORD +12 -9
- {mirascope-1.21.5.dist-info → mirascope-1.22.0.dist-info}/WHEEL +0 -0
- {mirascope-1.21.5.dist-info → mirascope-1.22.0.dist-info}/licenses/LICENSE +0 -0
mirascope/mcp/client.py
CHANGED
|
@@ -1,305 +1,151 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
23
|
+
class MCPClient(ClientSession):
|
|
24
|
+
"""The SSE client session that connects to the MCP server.
|
|
65
25
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
214
|
-
|
|
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
|
-
|
|
218
|
-
|
|
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[
|
|
224
|
-
|
|
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,
|
|
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[
|
|
236
|
-
|
|
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.
|
|
94
|
+
result = await self._session.get_prompt(name, kwargs) # type: ignore
|
|
244
95
|
|
|
245
96
|
return [
|
|
246
|
-
|
|
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[
|
|
253
|
-
|
|
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 =
|
|
258
|
-
|
|
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
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
) ->
|
|
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
|
-
|
|
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
|
-
"""
|
|
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.
|
|
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.
|
|
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=
|
|
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=
|
|
345
|
-
mirascope/mcp/
|
|
346
|
-
mirascope/mcp/
|
|
347
|
-
mirascope/mcp/
|
|
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.
|
|
372
|
-
mirascope-1.
|
|
373
|
-
mirascope-1.
|
|
374
|
-
mirascope-1.
|
|
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,,
|
|
File without changes
|
|
File without changes
|