universal-mcp-agents 0.1.23rc4__tar.gz → 0.1.23rc6__tar.gz

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.

Potentially problematic release.


This version of universal-mcp-agents might be problematic. Click here for more details.

Files changed (65) hide show
  1. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/PKG-INFO +1 -1
  2. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/pyproject.toml +1 -1
  3. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/base.py +43 -36
  4. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/prompts.py +2 -20
  5. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/sandbox.py +21 -18
  6. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/applications/llm/app.py +1 -1
  7. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/uv.lock +208 -208
  8. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/.github/workflows/evals.yml +0 -0
  9. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/.github/workflows/lint.yml +0 -0
  10. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/.github/workflows/release-please.yml +0 -0
  11. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/.github/workflows/tests.yml +0 -0
  12. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/.gitignore +0 -0
  13. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/.pre-commit-config.yaml +0 -0
  14. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/GEMINI.md +0 -0
  15. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/PROMPTS.md +0 -0
  16. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/README.md +0 -0
  17. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/bump_and_release.sh +0 -0
  18. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/__init__.py +0 -0
  19. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/dataset.py +0 -0
  20. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/datasets/exact.jsonl +0 -0
  21. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/datasets/tasks.jsonl +0 -0
  22. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/datasets/test.jsonl +0 -0
  23. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/evaluators.py +0 -0
  24. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/prompts.py +0 -0
  25. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/run.py +0 -0
  26. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/evals/utils.py +0 -0
  27. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/tests/test_agents.py +0 -0
  28. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/tests/test_sandbox.py +0 -0
  29. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/__init__.py +0 -0
  30. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/__init__.py +0 -0
  31. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/__main__.py +0 -0
  32. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/agent.py +0 -0
  33. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/context.py +0 -0
  34. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/graph.py +0 -0
  35. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/prompts.py +0 -0
  36. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/state.py +0 -0
  37. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/bigtool/tools.py +0 -0
  38. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/builder/__main__.py +0 -0
  39. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/builder/builder.py +0 -0
  40. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/builder/helper.py +0 -0
  41. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/builder/prompts.py +0 -0
  42. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/builder/state.py +0 -0
  43. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/cli.py +0 -0
  44. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/__init__.py +0 -0
  45. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/__main__.py +0 -0
  46. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/agent.py +0 -0
  47. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/config.py +0 -0
  48. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/langgraph_agent.py +0 -0
  49. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/llm_tool.py +0 -0
  50. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/state.py +0 -0
  51. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/tools.py +0 -0
  52. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/codeact0/utils.py +0 -0
  53. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/hil.py +0 -0
  54. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/llm.py +0 -0
  55. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/react.py +0 -0
  56. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/sandbox.py +0 -0
  57. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/shared/__main__.py +0 -0
  58. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/shared/prompts.py +0 -0
  59. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/shared/tool_node.py +0 -0
  60. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/simple.py +0 -0
  61. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/agents/utils.py +0 -0
  62. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/applications/filesystem/__init__.py +0 -0
  63. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/applications/filesystem/app.py +0 -0
  64. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/applications/llm/__init__.py +0 -0
  65. {universal_mcp_agents-0.1.23rc4 → universal_mcp_agents-0.1.23rc6}/src/universal_mcp/applications/ui/app.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp-agents
3
- Version: 0.1.23rc4
3
+ Version: 0.1.23rc6
4
4
  Summary: Add your description here
5
5
  Project-URL: Homepage, https://github.com/universal-mcp/applications
6
6
  Project-URL: Repository, https://github.com/universal-mcp/applications
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "universal-mcp-agents"
9
- version = "0.1.23-rc4"
9
+ version = "0.1.23-rc6"
10
10
  description = "Add your description here"
11
11
  readme = "README.md"
12
12
  authors = [
@@ -1,13 +1,12 @@
1
- # agents/base.py
2
- from typing import cast
1
+ from typing import Any, cast
3
2
  from uuid import uuid4
3
+ import asyncio
4
4
 
5
5
  from langchain_core.messages import AIMessageChunk
6
6
  from langgraph.checkpoint.base import BaseCheckpointSaver
7
7
  from langgraph.graph import StateGraph
8
8
  from langgraph.types import Command
9
9
  from universal_mcp.logger import logger
10
-
11
10
  from .utils import RichCLI
12
11
 
13
12
 
@@ -57,40 +56,48 @@ class BaseAgent:
57
56
  }
58
57
 
59
58
  last_ai_chunk = None
60
- async for event, meta in self._graph.astream(
61
- {"messages": [{"role": "user", "content": user_input}]},
62
- config=run_config,
63
- context={"system_prompt": self.instructions, "model": self.model},
64
- stream_mode=["messages", "custom"],
65
- stream_usage=True,
66
- ):
67
- if event == "messages" and isinstance(meta, (tuple, list)) and len(meta) == 2: # noqa: PLR2004
68
- payload, meta_dict = meta
69
- is_agent_builder = isinstance(meta_dict, dict) and meta_dict.get("langgraph_node") == "agent_builder"
70
- additional_kwargs = getattr(payload, "additional_kwargs", {}) or {}
71
- if is_agent_builder and not additional_kwargs.get("stream"):
72
- continue
73
- if isinstance(payload, AIMessageChunk):
74
- last_ai_chunk = payload
75
- aggregate = payload if aggregate is None else aggregate + payload
76
- if "finish_reason" in payload.response_metadata:
77
- logger.debug(
78
- f"Finish event: {payload}, reason: {payload.response_metadata['finish_reason']}, Metadata: {meta_dict}"
59
+ try:
60
+ async for event, meta in self._graph.astream(
61
+ {"messages": [{"role": "user", "content": user_input}]},
62
+ config=run_config,
63
+ context={"system_prompt": self.instructions, "model": self.model},
64
+ stream_mode=["messages", "custom"],
65
+ stream_usage=True,
66
+ ):
67
+ if event == "messages" and isinstance(meta, (tuple, list)) and len(meta) == 2: # noqa: PLR2004
68
+ payload, meta_dict = meta
69
+ is_agent_builder = (
70
+ isinstance(meta_dict, dict) and meta_dict.get("langgraph_node") == "agent_builder"
79
71
  )
80
- pass
81
- logger.debug(f"Event: {payload}, Metadata: {meta_dict}")
82
- yield payload
83
-
84
- if event == "custom":
85
- yield meta
86
-
87
- # Send a final finished message if we saw any AI chunks (to carry usage)
88
- if last_ai_chunk is not None and aggregate is not None:
89
- event = cast(AIMessageChunk, last_ai_chunk)
90
- event.usage_metadata = aggregate.usage_metadata
91
- logger.debug(f"Usage metadata: {event.usage_metadata}")
92
- event.content = "" # Clear the message since it would have already been streamed above
93
- yield event
72
+ additional_kwargs = getattr(payload, "additional_kwargs", {}) or {}
73
+ if is_agent_builder and not additional_kwargs.get("stream"):
74
+ continue
75
+ if isinstance(payload, AIMessageChunk):
76
+ last_ai_chunk = payload
77
+ aggregate = payload if aggregate is None else aggregate + payload
78
+ if "finish_reason" in payload.response_metadata:
79
+ logger.debug(
80
+ f"Finish event: {payload}, reason: {payload.response_metadata['finish_reason']}, Metadata: {meta_dict}"
81
+ )
82
+ pass
83
+ logger.debug(f"Event: {payload}, Metadata: {meta_dict}")
84
+ yield payload
85
+
86
+ if event == "custom":
87
+ yield meta
88
+
89
+ except asyncio.CancelledError:
90
+ logger.info(f"Stream for thread_id {thread_id} was cancelled by the user.")
91
+ # Perform any cleanup here if necessary
92
+ finally:
93
+ # This block will run whether the stream finished normally or was cancelled
94
+ # Send a final finished message if we saw any AI chunks (to carry usage)
95
+ if last_ai_chunk is not None and aggregate is not None:
96
+ event = cast(AIMessageChunk, last_ai_chunk)
97
+ event.usage_metadata = aggregate.usage_metadata
98
+ logger.debug(f"Usage metadata: {event.usage_metadata}")
99
+ event.content = "" # Clear the message
100
+ yield event
94
101
 
95
102
  async def stream_interactive(self, thread_id: str, user_input: str):
96
103
  await self.ainit()
@@ -25,6 +25,7 @@ Your job is to answer the user's question or perform the task they ask for.
25
25
  - If needed, feel free to ask for more information from the user (without using the `execute_ipython_cell` tool) to clarify the task.
26
26
  - Always describe in 2-3 lines about the current progress. In each step, mention what has been achieved and what you are planning to do next.
27
27
  - DO NOT use the code execution to communicate with the user. The user is not able to see the output of the code cells.
28
+ - Always use `await` when calling an async function. Since this is a Jupyter/async environment, you must not use asyncio.run().
28
29
 
29
30
  **Coding Best Practices:**
30
31
  - Variables defined at the top level of previous code snippets can be referenced in your code.
@@ -35,25 +36,6 @@ Your job is to answer the user's question or perform the task they ask for.
35
36
  - For displaying final results to the user, you must present your output in markdown format, including image links, so that they are rendered and displayed to the user. The code output is NOT visible to the user.
36
37
  - Call all functions using keyword arguments only, never positional arguments.
37
38
 
38
- **Async Functions (Critical Rules):**
39
- Use async functions only as follows:
40
- - Case 1: Top-level await without asyncio.run()
41
- Wrap in async function and call with asyncio.run():
42
- ```python
43
- async def main():
44
- result = await some_async_function()
45
- return result
46
- asyncio.run(main())
47
- ```
48
- - Case 2: Using asyncio.run() directly
49
- If code already contains asyncio.run(), use as-is — do not wrap again:
50
- ```python
51
- asyncio.run(some_async_function())
52
- ```
53
- Rules:
54
- - Never use await outside an async function
55
- - Never use await asyncio.run()
56
- - Never nest asyncio.run() calls
57
39
 
58
40
  **Final Output Requirements:**
59
41
  - Once you have all the information about the task, return the text directly to user in markdown format. No need to call `execute_ipython_cell` again.
@@ -188,7 +170,7 @@ def create_default_prompt(
188
170
  plan_block = str(plan)
189
171
  system_prompt += f"Plan Steps:\n{plan_block}\n"
190
172
  if code:
191
- system_prompt += f"\nScript:\n```python\n{str(code)}\n```\nThis function can be called by you using `execute_ipython_code`, either directly or using asyncio.run (if an async function). Do NOT redefine the function, unless it has to be modified. For modifying it, you must enter agent_builder mode first so that it is modified in the database and not just the chat locally."
173
+ system_prompt += f"\nScript:\n```python\n{str(code)}\n```\nThis function can be called by you using `execute_ipython_code`. Do NOT redefine the function, unless it has to be modified. For modifying it, you must enter agent_builder mode first so that it is modified in the database and not just the chat locally."
192
174
  except Exception:
193
175
  # Silently ignore formatting issues
194
176
  pass
@@ -7,13 +7,15 @@ import socket
7
7
  import threading
8
8
  import types
9
9
  from typing import Any
10
+ import pickle
11
+ import ast
10
12
 
11
13
  from langchain_core.tools import tool
12
14
 
13
15
  from universal_mcp.agents.codeact0.utils import derive_context, inject_context, smart_truncate
14
16
 
15
17
 
16
- def eval_unsafe(
18
+ async def eval_unsafe(
17
19
  code: str, _locals: dict[str, Any], add_context: dict[str, Any], timeout: int = 180
18
20
  ) -> tuple[str, dict[str, Any], dict[str, Any]]:
19
21
  """
@@ -38,21 +40,17 @@ def eval_unsafe(
38
40
  )
39
41
 
40
42
  result_container = {"output": "<no output>"}
41
-
42
- def target():
43
- try:
44
- with contextlib.redirect_stdout(io.StringIO()) as f:
45
- exec(code, _locals, _locals)
46
- result_container["output"] = f.getvalue() or "<code ran, no output printed to stdout>"
47
- except Exception as e:
48
- result_container["output"] = f"Error during execution: {type(e).__name__}: {e}"
49
-
50
- thread = threading.Thread(target=target)
51
- thread.start()
52
- thread.join(timeout)
53
-
54
- if thread.is_alive():
55
- result_container["output"] = f"Code timeout: code execution exceeded {timeout} seconds."
43
+
44
+ try:
45
+ compiled_code = compile(code, "<string>", "exec", flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT)
46
+ with contextlib.redirect_stdout(io.StringIO()) as f:
47
+ coroutine = eval(compiled_code, _locals, _locals)
48
+ # Await the coroutine to run the code if it's async
49
+ if coroutine:
50
+ await coroutine
51
+ result_container["output"] = f.getvalue() or "<code ran, no output printed to stdout>"
52
+ except Exception as e:
53
+ result_container["output"] = f"Error during execution: {type(e).__name__}: {e}"
56
54
 
57
55
  # If NameError for provider__tool occurred, append guidance (no retry)
58
56
  try:
@@ -65,7 +63,7 @@ def eval_unsafe(
65
63
  # Filter locals for picklable/storable variables
66
64
  all_vars = {}
67
65
  for key, value in _locals.items():
68
- if key == "__builtins__":
66
+ if key.startswith("__"):
69
67
  continue
70
68
  if inspect.iscoroutine(value) or inspect.iscoroutinefunction(value):
71
69
  continue
@@ -74,7 +72,12 @@ def eval_unsafe(
74
72
  if isinstance(value, EXCLUDE_TYPES):
75
73
  continue
76
74
  if not callable(value) or not hasattr(value, "__name__"):
77
- all_vars[key] = value
75
+ # Only keep if it can be pickled (serialized) successfully
76
+ try:
77
+ pickle.dumps(value)
78
+ all_vars[key] = value
79
+ except Exception:
80
+ pass
78
81
 
79
82
  # Safely derive context
80
83
  try:
@@ -166,7 +166,7 @@ class LlmApp(BaseApplication):
166
166
  .with_retry(stop_after_attempt=MAX_RETRIES)
167
167
  .invoke(prompt)
168
168
  )
169
- return response.model_dump_json(indent=2)
169
+ return response.model_dump()
170
170
 
171
171
  def extract_data(
172
172
  self,