praisonaiagents 0.0.65__py3-none-any.whl → 0.0.66__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.
@@ -10,6 +10,7 @@ from .tools.tools import Tools
10
10
  from .agents.autoagents import AutoAgents
11
11
  from .knowledge.knowledge import Knowledge
12
12
  from .knowledge.chunking import Chunking
13
+ from .mcp.mcp import MCP
13
14
  from .main import (
14
15
  TaskOutput,
15
16
  ReflectionOutput,
@@ -51,5 +52,6 @@ __all__ = [
51
52
  'sync_display_callbacks',
52
53
  'async_display_callbacks',
53
54
  'Knowledge',
54
- 'Chunking'
55
+ 'Chunking',
56
+ 'MCP'
55
57
  ]
@@ -0,0 +1,278 @@
1
+ import asyncio
2
+ import threading
3
+ import queue
4
+ import time
5
+ import inspect
6
+ import shlex
7
+ from typing import Dict, Any, List, Optional, Callable, Iterable, Union
8
+ from functools import wraps, partial
9
+
10
+ from mcp import ClientSession, StdioServerParameters
11
+ from mcp.client.stdio import stdio_client
12
+
13
+ class MCPToolRunner(threading.Thread):
14
+ """A dedicated thread for running MCP operations."""
15
+
16
+ def __init__(self, server_params):
17
+ super().__init__(daemon=True)
18
+ self.server_params = server_params
19
+ self.queue = queue.Queue()
20
+ self.result_queue = queue.Queue()
21
+ self.initialized = threading.Event()
22
+ self.tools = []
23
+ self.start()
24
+
25
+ def run(self):
26
+ """Main thread function that processes MCP requests."""
27
+ asyncio.run(self._run_async())
28
+
29
+ async def _run_async(self):
30
+ """Async entry point for MCP operations."""
31
+ try:
32
+ # Set up MCP session
33
+ async with stdio_client(self.server_params) as (read, write):
34
+ async with ClientSession(read, write) as session:
35
+ # Initialize connection
36
+ await session.initialize()
37
+
38
+ # Get tools
39
+ tools_result = await session.list_tools()
40
+ self.tools = tools_result.tools
41
+
42
+ # Signal that initialization is complete
43
+ self.initialized.set()
44
+
45
+ # Process requests
46
+ while True:
47
+ try:
48
+ # Check for new requests
49
+ try:
50
+ item = self.queue.get(block=False)
51
+ if item is None: # Shutdown signal
52
+ break
53
+
54
+ tool_name, arguments = item
55
+ try:
56
+ result = await session.call_tool(tool_name, arguments)
57
+ self.result_queue.put((True, result))
58
+ except Exception as e:
59
+ self.result_queue.put((False, str(e)))
60
+ except queue.Empty:
61
+ pass
62
+
63
+ # Give other tasks a chance to run
64
+ await asyncio.sleep(0.01)
65
+ except asyncio.CancelledError:
66
+ break
67
+ except Exception as e:
68
+ self.initialized.set() # Ensure we don't hang
69
+ self.result_queue.put((False, f"MCP initialization error: {str(e)}"))
70
+
71
+ def call_tool(self, tool_name, arguments):
72
+ """Call an MCP tool and wait for the result."""
73
+ if not self.initialized.is_set():
74
+ self.initialized.wait(timeout=30)
75
+ if not self.initialized.is_set():
76
+ return "Error: MCP initialization timed out"
77
+
78
+ # Put request in queue
79
+ self.queue.put((tool_name, arguments))
80
+
81
+ # Wait for result
82
+ success, result = self.result_queue.get()
83
+ if not success:
84
+ return f"Error: {result}"
85
+
86
+ # Process result
87
+ if hasattr(result, 'content') and result.content:
88
+ if hasattr(result.content[0], 'text'):
89
+ return result.content[0].text
90
+ return str(result.content[0])
91
+ return str(result)
92
+
93
+ def shutdown(self):
94
+ """Signal the thread to shut down."""
95
+ self.queue.put(None)
96
+
97
+
98
+ class MCP:
99
+ """
100
+ Model Context Protocol (MCP) integration for PraisonAI Agents.
101
+
102
+ This class provides a simple way to connect to MCP servers and use their tools
103
+ within PraisonAI agents.
104
+
105
+ Example:
106
+ ```python
107
+ from praisonaiagents import Agent
108
+ from praisonaiagents.mcp import MCP
109
+
110
+ # Method 1: Using command and args separately
111
+ agent = Agent(
112
+ instructions="You are a helpful assistant...",
113
+ llm="gpt-4o-mini",
114
+ tools=MCP(
115
+ command="/path/to/python",
116
+ args=["/path/to/app.py"]
117
+ )
118
+ )
119
+
120
+ # Method 2: Using a single command string
121
+ agent = Agent(
122
+ instructions="You are a helpful assistant...",
123
+ llm="gpt-4o-mini",
124
+ tools=MCP("/path/to/python /path/to/app.py")
125
+ )
126
+
127
+ agent.start("What is the stock price of Tesla?")
128
+ ```
129
+ """
130
+
131
+ def __init__(self, command_or_string=None, args=None, *, command=None, **kwargs):
132
+ """
133
+ Initialize the MCP connection and get tools.
134
+
135
+ Args:
136
+ command_or_string: Either:
137
+ - The command to run the MCP server (e.g., Python path)
138
+ - A complete command string (e.g., "/path/to/python /path/to/app.py")
139
+ args: Arguments to pass to the command (when command_or_string is the command)
140
+ command: Alternative parameter name for backward compatibility
141
+ **kwargs: Additional parameters for StdioServerParameters
142
+ """
143
+ # Handle backward compatibility with named parameter 'command'
144
+ if command_or_string is None and command is not None:
145
+ command_or_string = command
146
+
147
+ # Handle the single string format
148
+ if isinstance(command_or_string, str) and args is None:
149
+ # Split the string into command and args using shell-like parsing
150
+ parts = shlex.split(command_or_string)
151
+ if not parts:
152
+ raise ValueError("Empty command string")
153
+
154
+ cmd = parts[0]
155
+ arguments = parts[1:] if len(parts) > 1 else []
156
+ else:
157
+ # Use the original format with separate command and args
158
+ cmd = command_or_string
159
+ arguments = args or []
160
+
161
+ self.server_params = StdioServerParameters(
162
+ command=cmd,
163
+ args=arguments,
164
+ **kwargs
165
+ )
166
+ self.runner = MCPToolRunner(self.server_params)
167
+
168
+ # Wait for initialization
169
+ if not self.runner.initialized.wait(timeout=30):
170
+ print("Warning: MCP initialization timed out")
171
+
172
+ # Generate tool functions immediately and store them
173
+ self._tools = self._generate_tool_functions()
174
+
175
+ def _generate_tool_functions(self) -> List[Callable]:
176
+ """
177
+ Generate functions for each MCP tool.
178
+
179
+ Returns:
180
+ List[Callable]: Functions that can be used as tools
181
+ """
182
+ tool_functions = []
183
+
184
+ for tool in self.runner.tools:
185
+ wrapper = self._create_tool_wrapper(tool)
186
+ tool_functions.append(wrapper)
187
+
188
+ return tool_functions
189
+
190
+ def _create_tool_wrapper(self, tool):
191
+ """Create a wrapper function for an MCP tool."""
192
+ # Determine parameter names from the schema
193
+ param_names = []
194
+ param_annotations = {}
195
+ required_params = []
196
+
197
+ if hasattr(tool, 'inputSchema') and tool.inputSchema:
198
+ properties = tool.inputSchema.get("properties", {})
199
+ required = tool.inputSchema.get("required", [])
200
+
201
+ for name, prop in properties.items():
202
+ param_names.append(name)
203
+
204
+ # Set annotation based on property type
205
+ prop_type = prop.get("type", "string")
206
+ if prop_type == "string":
207
+ param_annotations[name] = str
208
+ elif prop_type == "integer":
209
+ param_annotations[name] = int
210
+ elif prop_type == "number":
211
+ param_annotations[name] = float
212
+ elif prop_type == "boolean":
213
+ param_annotations[name] = bool
214
+ elif prop_type == "array":
215
+ param_annotations[name] = list
216
+ elif prop_type == "object":
217
+ param_annotations[name] = dict
218
+ else:
219
+ param_annotations[name] = Any
220
+
221
+ if name in required:
222
+ required_params.append(name)
223
+
224
+ # Create the function signature
225
+ params = []
226
+ for name in param_names:
227
+ is_required = name in required_params
228
+ param = inspect.Parameter(
229
+ name=name,
230
+ kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
231
+ default=inspect.Parameter.empty if is_required else None,
232
+ annotation=param_annotations.get(name, Any)
233
+ )
234
+ params.append(param)
235
+
236
+ # Create function template to be properly decorated
237
+ def template_function(*args, **kwargs):
238
+ return None
239
+
240
+ # Create a proper function with the correct signature
241
+ template_function.__signature__ = inspect.Signature(params)
242
+ template_function.__annotations__ = param_annotations
243
+ template_function.__name__ = tool.name
244
+ template_function.__qualname__ = tool.name
245
+ template_function.__doc__ = tool.description
246
+
247
+ # Create the actual function using a decorator
248
+ @wraps(template_function)
249
+ def wrapper(*args, **kwargs):
250
+ # Map positional args to parameter names
251
+ all_args = {}
252
+ for i, arg in enumerate(args):
253
+ if i < len(param_names):
254
+ all_args[param_names[i]] = arg
255
+
256
+ # Add keyword args
257
+ all_args.update(kwargs)
258
+
259
+ # Call the tool
260
+ return self.runner.call_tool(tool.name, all_args)
261
+
262
+ # Make sure the wrapper has the correct signature for inspection
263
+ wrapper.__signature__ = inspect.Signature(params)
264
+
265
+ return wrapper
266
+
267
+ def __iter__(self) -> Iterable[Callable]:
268
+ """
269
+ Allow the MCP instance to be used directly as an iterable of tools.
270
+
271
+ This makes it possible to pass the MCP instance directly to the Agent's tools parameter.
272
+ """
273
+ return iter(self._tools)
274
+
275
+ def __del__(self):
276
+ """Clean up resources when the object is garbage collected."""
277
+ if hasattr(self, 'runner'):
278
+ self.runner.shutdown()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: praisonaiagents
3
- Version: 0.0.65
3
+ Version: 0.0.66
4
4
  Summary: Praison AI agents for completing complex tasks with Self Reflection Agents
5
5
  Author: Mervin Praison
6
6
  Requires-Dist: pydantic
@@ -1,4 +1,4 @@
1
- praisonaiagents/__init__.py,sha256=frdIvimDY-kU9j-9yXV1z4NtXypfPvyvlnac5mgBCuQ,1288
1
+ praisonaiagents/__init__.py,sha256=Z2_rSA6mYozz0r3ioUgKzl3QV8uWRDS_QaqPg2oGjqg,1324
2
2
  praisonaiagents/main.py,sha256=l29nGEbV2ReBi4szURbnH0Fk0w2F_QZTmECysyZjYcA,15066
3
3
  praisonaiagents/agent/__init__.py,sha256=j0T19TVNbfZcClvpbZDDinQxZ0oORgsMrMqx16jZ-bA,128
4
4
  praisonaiagents/agent/agent.py,sha256=h3s0-1M88zujllDHnKijHmYeVihD75d-K9s2Y3IHLY4,61850
@@ -11,6 +11,7 @@ praisonaiagents/knowledge/chunking.py,sha256=FzoNY0q8MkvG4gADqk4JcRhmH3lcEHbRdon
11
11
  praisonaiagents/knowledge/knowledge.py,sha256=fQNREDiwdoisfIxJBLVkteXgq_8Gbypfc3UaZbxf5QY,13210
12
12
  praisonaiagents/llm/__init__.py,sha256=ttPQQJQq6Tah-0updoEXDZFKWtJAM93rBWRoIgxRWO8,689
13
13
  praisonaiagents/llm/llm.py,sha256=6QMRW47fgFozibzaqxa3dwxlD756rMLCRjL3eNsw8QQ,74088
14
+ praisonaiagents/mcp/mcp.py,sha256=xvORgbsbsdaY_j3jKCP8BH3ERoKi_ZK8zM57mobC2Ac,10488
14
15
  praisonaiagents/memory/memory.py,sha256=I8dOTkrl1i-GgQbDcrFOsSruzJ7MiI6Ys37DK27wrUs,35537
15
16
  praisonaiagents/process/__init__.py,sha256=lkYbL7Hn5a0ldvJtkdH23vfIIZLIcanK-65C0MwaorY,52
16
17
  praisonaiagents/process/process.py,sha256=HPw84OhnKQW3EyrDkpoQu0DcpxThbrzR2hWUgwQh9Pw,59955
@@ -37,7 +38,7 @@ praisonaiagents/tools/xml_tools.py,sha256=iYTMBEk5l3L3ryQ1fkUnNVYK-Nnua2Kx2S0dxN
37
38
  praisonaiagents/tools/yaml_tools.py,sha256=uogAZrhXV9O7xvspAtcTfpKSQYL2nlOTvCQXN94-G9A,14215
38
39
  praisonaiagents/tools/yfinance_tools.py,sha256=s2PBj_1v7oQnOobo2fDbQBACEHl61ftG4beG6Z979ZE,8529
39
40
  praisonaiagents/tools/train/data/generatecot.py,sha256=H6bNh-E2hqL5MW6kX3hqZ05g9ETKN2-kudSjiuU_SD8,19403
40
- praisonaiagents-0.0.65.dist-info/METADATA,sha256=yns65YUATN0vnXt8y4TMvoNUTgvp1fPkKKjqZAVSjcY,830
41
- praisonaiagents-0.0.65.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
42
- praisonaiagents-0.0.65.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
43
- praisonaiagents-0.0.65.dist-info/RECORD,,
41
+ praisonaiagents-0.0.66.dist-info/METADATA,sha256=mZDRbV3kjDru4ehwjOVkMLJAKZeYq79yZB_8VjQDzUA,830
42
+ praisonaiagents-0.0.66.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
43
+ praisonaiagents-0.0.66.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
44
+ praisonaiagents-0.0.66.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (76.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5