langroid 0.53.5__py3-none-any.whl → 0.53.6__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.
@@ -1,10 +1,10 @@
1
1
  from .decorators import mcp_tool
2
2
  from .fastmcp_client import (
3
3
  FastMCPClient,
4
- get_langroid_tool,
5
- get_langroid_tool_async,
6
- get_langroid_tools,
7
- get_langroid_tools_async,
4
+ get_tool,
5
+ get_tool_async,
6
+ get_tools,
7
+ get_tools_async,
8
8
  get_mcp_tool_async,
9
9
  get_mcp_tools_async,
10
10
  )
@@ -13,10 +13,10 @@ from .fastmcp_client import (
13
13
  __all__ = [
14
14
  "mcp_tool",
15
15
  "FastMCPClient",
16
- "get_langroid_tool",
17
- "get_langroid_tool_async",
18
- "get_langroid_tools",
19
- "get_langroid_tools_async",
16
+ "get_tool",
17
+ "get_tool_async",
18
+ "get_tools",
19
+ "get_tools_async",
20
20
  "get_mcp_tool_async",
21
21
  "get_mcp_tools_async",
22
22
  ]
@@ -1,7 +1,7 @@
1
1
  from typing import Callable, Type
2
2
 
3
3
  from langroid.agent.tool_message import ToolMessage
4
- from langroid.agent.tools.mcp.fastmcp_client import get_langroid_tool
4
+ from langroid.agent.tools.mcp.fastmcp_client import get_tool
5
5
 
6
6
 
7
7
  def mcp_tool(
@@ -18,7 +18,7 @@ def mcp_tool(
18
18
 
19
19
  def decorator(user_cls: Type[ToolMessage]) -> Type[ToolMessage]:
20
20
  # build the “real” ToolMessage subclass for this server/tool
21
- RealTool: Type[ToolMessage] = get_langroid_tool(server, tool_name)
21
+ RealTool: Type[ToolMessage] = get_tool(server, tool_name)
22
22
 
23
23
  # copy user‐defined methods / attributes onto RealTool
24
24
  for name, attr in user_cls.__dict__.items():
@@ -1,18 +1,30 @@
1
1
  import asyncio
2
+ import datetime
2
3
  import logging
3
- from typing import Any, Dict, List, Optional, Tuple, Type, cast
4
+ from typing import Any, Dict, List, Optional, Tuple, Type, TypeAlias, cast
4
5
 
5
6
  from dotenv import load_dotenv
6
7
  from fastmcp.client import Client
8
+ from fastmcp.client.roots import (
9
+ RootsHandler,
10
+ RootsList,
11
+ )
12
+ from fastmcp.client.sampling import SamplingHandler
7
13
  from fastmcp.client.transports import ClientTransport
8
14
  from fastmcp.server import FastMCP
15
+ from mcp.client.session import (
16
+ LoggingFnT,
17
+ MessageHandlerFnT,
18
+ )
9
19
  from mcp.types import CallToolResult, TextContent, Tool
10
20
 
11
21
  from langroid.agent.tool_message import ToolMessage
12
- from langroid.pydantic_v1 import BaseModel, Field, create_model
22
+ from langroid.pydantic_v1 import AnyUrl, BaseModel, Field, create_model
13
23
 
14
24
  load_dotenv() # load environment variables from .env
15
25
 
26
+ FastMCPServerSpec: TypeAlias = str | FastMCP[Any] | ClientTransport | AnyUrl
27
+
16
28
 
17
29
  class FastMCPClient:
18
30
  """A client for interacting with a FastMCP server.
@@ -24,7 +36,15 @@ class FastMCPClient:
24
36
  _cm: Optional[Client] = None
25
37
  client: Optional[Client] = None
26
38
 
27
- def __init__(self, server: str | FastMCP[Any] | ClientTransport) -> None:
39
+ def __init__(
40
+ self,
41
+ server: FastMCPServerSpec,
42
+ sampling_handler: SamplingHandler | None = None, # type: ignore
43
+ roots: RootsList | RootsHandler | None = None, # type: ignore
44
+ log_handler: LoggingFnT | None = None,
45
+ message_handler: MessageHandlerFnT | None = None,
46
+ read_timeout_seconds: datetime.timedelta | None = None,
47
+ ) -> None:
28
48
  """Initialize the FastMCPClient.
29
49
 
30
50
  Args:
@@ -33,11 +53,23 @@ class FastMCPClient:
33
53
  self.server = server
34
54
  self.client = None
35
55
  self._cm = None
56
+ self.sampling_handler = sampling_handler
57
+ self.roots = roots
58
+ self.log_handler = log_handler
59
+ self.message_handler = message_handler
60
+ self.read_timeout_seconds = read_timeout_seconds
36
61
 
37
62
  async def __aenter__(self) -> "FastMCPClient":
38
63
  """Enter the async context manager and connect inner client."""
39
64
  # create inner client context manager
40
- self._cm = Client(self.server)
65
+ self._cm = Client(
66
+ self.server,
67
+ sampling_handler=self.sampling_handler,
68
+ roots=self.roots,
69
+ log_handler=self.log_handler,
70
+ message_handler=self.message_handler,
71
+ read_timeout_seconds=self.read_timeout_seconds,
72
+ )
41
73
  # actually enter it (opens the session)
42
74
  self.client = await self._cm.__aenter__() # type: ignore
43
75
  return self
@@ -113,7 +145,7 @@ class FastMCPClient:
113
145
  # Default fallback
114
146
  return Any, Field(default=default, description=desc)
115
147
 
116
- async def get_langroid_tool(self, tool_name: str) -> Type[ToolMessage]:
148
+ async def get_tool_async(self, tool_name: str) -> Type[ToolMessage]:
117
149
  """
118
150
  Create a Langroid ToolMessage subclass from the MCP Tool
119
151
  with the given `tool_name`.
@@ -163,7 +195,17 @@ class FastMCPClient:
163
195
  **fields,
164
196
  ),
165
197
  )
166
- tool_model._server = self.server # type: ignore[attr-defined]
198
+ # Store ALL client configuration needed to recreate a client
199
+ client_config = {
200
+ "server": self.server,
201
+ "sampling_handler": self.sampling_handler,
202
+ "roots": self.roots,
203
+ "log_handler": self.log_handler,
204
+ "message_handler": self.message_handler,
205
+ "read_timeout_seconds": self.read_timeout_seconds,
206
+ }
207
+
208
+ tool_model._client_config = client_config # type: ignore [attr-defined]
167
209
  tool_model._renamed_fields = renamed # type: ignore[attr-defined]
168
210
 
169
211
  # 2) define an arg-free call_tool_async()
@@ -171,15 +213,23 @@ class FastMCPClient:
171
213
  from langroid.agent.tools.mcp.fastmcp_client import FastMCPClient
172
214
 
173
215
  # pack up the payload
174
- payload = self.dict(exclude=self.Config.schema_extra["exclude"])
216
+ payload = self.dict(
217
+ exclude=self.Config.schema_extra["exclude"].union(
218
+ ["request", "purpose"]
219
+ ),
220
+ )
175
221
 
176
222
  # restore any renamed fields
177
223
  for orig, new in self.__class__._renamed_fields.items(): # type: ignore
178
224
  if new in payload:
179
225
  payload[orig] = payload.pop(new)
180
226
 
227
+ client_cfg = getattr(self.__class__, "_client_config", None) # type: ignore
228
+ if not client_cfg:
229
+ # Fallback or error - ideally _client_config should always exist
230
+ raise RuntimeError(f"Client config missing on {self.__class__}")
181
231
  # open a fresh client, call the tool, then close
182
- async with FastMCPClient(self.__class__._server) as client: # type: ignore
232
+ async with FastMCPClient(**client_cfg) as client: # type: ignore
183
233
  return await client.call_mcp_tool(self.request, payload)
184
234
 
185
235
  tool_model.call_tool_async = call_tool_async # type: ignore
@@ -195,7 +245,7 @@ class FastMCPClient:
195
245
 
196
246
  return tool_model
197
247
 
198
- async def get_langroid_tools(self) -> List[Type[ToolMessage]]:
248
+ async def get_tools_async(self) -> List[Type[ToolMessage]]:
199
249
  """
200
250
  Get all available tools as Langroid ToolMessage classes,
201
251
  handling nested schemas, with `handle_async` methods
@@ -203,10 +253,7 @@ class FastMCPClient:
203
253
  if not self.client:
204
254
  raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
205
255
  resp = await self.client.list_tools()
206
- tools: List[Type[ToolMessage]] = []
207
- for t in resp:
208
- tools.append(await self.get_langroid_tool(t.name))
209
- return tools
256
+ return [await self.get_tool_async(t.name) for t in resp]
210
257
 
211
258
  async def get_mcp_tool_async(self, name: str) -> Optional[Tool]:
212
259
  """Find the "original" MCP Tool (i.e. of type mcp.types.Tool) on the server
@@ -270,46 +317,144 @@ class FastMCPClient:
270
317
  return self._convert_tool_result(tool_name, result)
271
318
 
272
319
 
273
- async def get_langroid_tool_async(
274
- server: str | ClientTransport,
320
+ # ==============================================================================
321
+ # Convenience functions (wrappers around FastMCPClient methods)
322
+ # These are useful for one-off calls without needing to manage the
323
+ # FastMCPClient context explicitly.
324
+ # ==============================================================================
325
+
326
+
327
+ async def get_tool_async(
328
+ server: FastMCPServerSpec,
275
329
  tool_name: str,
330
+ **client_kwargs: Any,
276
331
  ) -> Type[ToolMessage]:
277
- async with FastMCPClient(server) as client:
278
- return await client.get_langroid_tool(tool_name)
332
+ """Get a single Langroid ToolMessage subclass for a specific MCP tool name (async).
333
+
334
+ This is a convenience wrapper that creates a temporary FastMCPClient.
279
335
 
336
+ Args:
337
+ server: Specification of the FastMCP server to connect to.
338
+ tool_name: The name of the tool to retrieve.
339
+ **client_kwargs: Additional keyword arguments to pass to the
340
+ FastMCPClient constructor (e.g., sampling_handler, roots).
280
341
 
281
- def get_langroid_tool(
282
- server: str | ClientTransport,
342
+ Returns:
343
+ A dynamically created Langroid ToolMessage subclass representing the
344
+ requested tool.
345
+ """
346
+ async with FastMCPClient(server, **client_kwargs) as client:
347
+ return await client.get_tool_async(tool_name)
348
+
349
+
350
+ def get_tool(
351
+ server: FastMCPServerSpec,
283
352
  tool_name: str,
353
+ **client_kwargs: Any,
284
354
  ) -> Type[ToolMessage]:
285
- return asyncio.run(get_langroid_tool_async(server, tool_name))
355
+ """Get a single Langroid ToolMessage subclass
356
+ for a specific MCP tool name (synchronous).
357
+
358
+ This is a convenience wrapper that creates a temporary FastMCPClient and runs the
359
+ async `get_tool_async` function using `asyncio.run()`.
286
360
 
361
+ Args:
362
+ server: Specification of the FastMCP server to connect to.
363
+ tool_name: The name of the tool to retrieve.
364
+ **client_kwargs: Additional keyword arguments to pass to the
365
+ FastMCPClient constructor (e.g., sampling_handler, roots).
287
366
 
288
- async def get_langroid_tools_async(
289
- server: str | ClientTransport,
367
+ Returns:
368
+ A dynamically created Langroid ToolMessage subclass representing the
369
+ requested tool.
370
+ """
371
+ return asyncio.run(get_tool_async(server, tool_name, **client_kwargs))
372
+
373
+
374
+ async def get_tools_async(
375
+ server: FastMCPServerSpec,
376
+ **client_kwargs: Any,
290
377
  ) -> List[Type[ToolMessage]]:
291
- async with FastMCPClient(server) as client:
292
- return await client.get_langroid_tools()
378
+ """Get all available tools as Langroid ToolMessage subclasses (async).
379
+
380
+ This is a convenience wrapper that creates a temporary FastMCPClient.
293
381
 
382
+ Args:
383
+ server: Specification of the FastMCP server to connect to.
384
+ **client_kwargs: Additional keyword arguments to pass to the
385
+ FastMCPClient constructor (e.g., sampling_handler, roots).
294
386
 
295
- def get_langroid_tools(
296
- server: str | ClientTransport,
387
+ Returns:
388
+ A list of dynamically created Langroid ToolMessage subclasses
389
+ representing all available tools on the server.
390
+ """
391
+ async with FastMCPClient(server, **client_kwargs) as client:
392
+ return await client.get_tools_async()
393
+
394
+
395
+ def get_tools(
396
+ server: FastMCPServerSpec,
397
+ **client_kwargs: Any,
297
398
  ) -> List[Type[ToolMessage]]:
298
- return asyncio.run(get_langroid_tools_async(server))
399
+ """Get all available tools as Langroid ToolMessage subclasses (synchronous).
400
+
401
+ This is a convenience wrapper that creates a temporary FastMCPClient and runs the
402
+ async `get_tools_async` function using `asyncio.run()`.
403
+
404
+ Args:
405
+ server: Specification of the FastMCP server to connect to.
406
+ **client_kwargs: Additional keyword arguments to pass to the
407
+ FastMCPClient constructor (e.g., sampling_handler, roots).
408
+
409
+ Returns:
410
+ A list of dynamically created Langroid ToolMessage subclasses
411
+ representing all available tools on the server.
412
+ """
413
+ return asyncio.run(get_tools_async(server, **client_kwargs))
299
414
 
300
415
 
301
416
  async def get_mcp_tool_async(
302
- server: str | ClientTransport,
417
+ server: FastMCPServerSpec,
303
418
  name: str,
419
+ **client_kwargs: Any,
304
420
  ) -> Optional[Tool]:
305
- async with FastMCPClient(server) as client:
421
+ """Get the raw MCP Tool object for a specific tool name (async).
422
+
423
+ This is a convenience wrapper that creates a temporary FastMCPClient to
424
+ retrieve the tool definition from the server.
425
+
426
+ Args:
427
+ server: Specification of the FastMCP server to connect to.
428
+ name: The name of the tool to look up.
429
+ **client_kwargs: Additional keyword arguments to pass to the
430
+ FastMCPClient constructor.
431
+
432
+ Returns:
433
+ The raw `mcp.types.Tool` object from the server, or `None` if the tool
434
+ is not found.
435
+ """
436
+ async with FastMCPClient(server, **client_kwargs) as client:
306
437
  return await client.get_mcp_tool_async(name)
307
438
 
308
439
 
309
440
  async def get_mcp_tools_async(
310
- server: str | ClientTransport,
441
+ server: FastMCPServerSpec,
442
+ **client_kwargs: Any,
311
443
  ) -> List[Tool]:
312
- async with FastMCPClient(server) as client:
444
+ """Get all available raw MCP Tool objects from the server (async).
445
+
446
+ This is a convenience wrapper that creates a temporary FastMCPClient to
447
+ retrieve the list of tool definitions from the server.
448
+
449
+ Args:
450
+ server: Specification of the FastMCP server to connect to.
451
+ **client_kwargs: Additional keyword arguments to pass to the
452
+ FastMCPClient constructor.
453
+
454
+ Returns:
455
+ A list of raw `mcp.types.Tool` objects available on the server.
456
+ """
457
+ async with FastMCPClient(server, **client_kwargs) as client:
313
458
  if not client.client:
314
459
  raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
315
460
  return await client.client.list_tools()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.53.5
3
+ Version: 0.53.6
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -54,9 +54,9 @@ langroid/agent/tools/retrieval_tool.py,sha256=zcAV20PP_6VzSd-UE-IJcabaBseFL_QNz5
54
54
  langroid/agent/tools/rewind_tool.py,sha256=XAXL3BpNhCmBGYq_qi_sZfHJuIw7NY2jp4wnojJ7WRs,5606
55
55
  langroid/agent/tools/segment_extract_tool.py,sha256=__srZ_VGYLVOdPrITUM8S0HpmX4q7r5FHWMDdHdEv8w,1440
56
56
  langroid/agent/tools/tavily_search_tool.py,sha256=soI-j0HdgVQLf09wRQScaEK4b5RpAX9C4cwOivRFWWI,1903
57
- langroid/agent/tools/mcp/__init__.py,sha256=cQb3gYxXk0YZ23QCqbVNMbMeCeWCJj6w3gqGnvyqv7w,459
58
- langroid/agent/tools/mcp/decorators.py,sha256=mWnlTjyI9PMNi750PWzC_2B6V5K_XdxH0Co9kE2yAj0,1145
59
- langroid/agent/tools/mcp/fastmcp_client.py,sha256=ffOV0lJOtE3DLfA6y3Fib28jyACySJPgbqjNJpSAEQA,11815
57
+ langroid/agent/tools/mcp/__init__.py,sha256=DJNM0VeFnFS3pJKCyFGggT8JVjVu0rBzrGzasT1HaSM,387
58
+ langroid/agent/tools/mcp/decorators.py,sha256=h7dterhsmvWJ8q4mp_OopmuG2DF71ty8cZwOyzdDZuk,1127
59
+ langroid/agent/tools/mcp/fastmcp_client.py,sha256=g2mJe6cCpzF0XBmW6zAqCz5AvIEt0ZWwe8uAMM4jNS0,17445
60
60
  langroid/cachedb/__init__.py,sha256=G2KyNnk3Qkhv7OKyxTOnpsxfDycx3NY0O_wXkJlalNY,96
61
61
  langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
62
62
  langroid/cachedb/redis_cachedb.py,sha256=7kgnbf4b5CKsCrlL97mHWKvdvlLt8zgn7lc528jEpiE,5141
@@ -132,7 +132,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
132
132
  langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
133
133
  langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
134
134
  langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
135
- langroid-0.53.5.dist-info/METADATA,sha256=ExVMihbLql8jTdQQ-6DuM8Ocjyqc0JV057Sp17U3y8Y,64945
136
- langroid-0.53.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
137
- langroid-0.53.5.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
138
- langroid-0.53.5.dist-info/RECORD,,
135
+ langroid-0.53.6.dist-info/METADATA,sha256=kOJSlrle7MZXPeosRhCuqg25rdUJFF21wif68zBUkcQ,64945
136
+ langroid-0.53.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
137
+ langroid-0.53.6.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
138
+ langroid-0.53.6.dist-info/RECORD,,