universal-mcp-agents 0.1.10__tar.gz → 0.1.12__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 (98) hide show
  1. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/GEMINI.md +6 -1
  2. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/PKG-INFO +1 -1
  3. universal_mcp_agents-0.1.12/dataset_code.py +83 -0
  4. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/pyproject.toml +6 -1
  5. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/tests/test_agents.py +6 -6
  6. universal_mcp_agents-0.1.12/src/universal_mcp/agents/__init__.py +39 -0
  7. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/base.py +10 -7
  8. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/__init__.py +2 -2
  9. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/__main__.py +0 -1
  10. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/agent.py +0 -1
  11. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/graph.py +6 -5
  12. universal_mcp_agents-0.1.12/src/universal_mcp/agents/builder/__main__.py +125 -0
  13. universal_mcp_agents-0.1.12/src/universal_mcp/agents/builder/builder.py +225 -0
  14. universal_mcp_agents-0.1.12/src/universal_mcp/agents/builder/prompts.py +173 -0
  15. universal_mcp_agents-0.1.12/src/universal_mcp/agents/builder/state.py +24 -0
  16. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/cli.py +3 -2
  17. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact/__main__.py +33 -0
  18. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact/agent.py +240 -0
  19. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact/models.py +11 -0
  20. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact/prompts.py +82 -0
  21. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact/sandbox.py +89 -0
  22. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact/state.py +11 -0
  23. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/__init__.py +3 -0
  24. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/codeact → universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0}/__main__.py +2 -2
  25. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/agent.py +136 -0
  26. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/config.py +77 -0
  27. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/langgraph_graph.py +17 -0
  28. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/legacy_codeact.py +104 -0
  29. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/llm_tool.py +379 -0
  30. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/prompts.py +156 -0
  31. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/sandbox.py +90 -0
  32. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/state.py +12 -0
  33. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/1-unsubscribe.yaml +4 -0
  34. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/10-reddit2.yaml +10 -0
  35. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/11-github.yaml +13 -0
  36. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/2-reddit.yaml +27 -0
  37. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/2.1-instructions.md +81 -0
  38. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/2.2-instructions.md +71 -0
  39. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/3-earnings.yaml +4 -0
  40. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/4-maps.yaml +41 -0
  41. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/5-gmailreply.yaml +8 -0
  42. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/6-contract.yaml +6 -0
  43. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/7-overnight.yaml +14 -0
  44. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/8-sheets_chart.yaml +25 -0
  45. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/usecases/9-learning.yaml +9 -0
  46. universal_mcp_agents-0.1.12/src/universal_mcp/agents/codeact0/utils.py +374 -0
  47. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/hil.py +4 -4
  48. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/planner/__init__.py +7 -1
  49. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/react.py +11 -3
  50. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/simple.py +12 -2
  51. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/utils.py +17 -0
  52. universal_mcp_agents-0.1.12/src/universal_mcp/applications/llm/__init__.py +3 -0
  53. universal_mcp_agents-0.1.12/src/universal_mcp/applications/llm/app.py +158 -0
  54. universal_mcp_agents-0.1.12/src/universal_mcp/applications/ui/app.py +269 -0
  55. universal_mcp_agents-0.1.12/test.py +49 -0
  56. universal_mcp_agents-0.1.12/test_code.py +78 -0
  57. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/uv.lock +10 -2
  58. universal_mcp_agents-0.1.10/src/universal_mcp/agents/__init__.py +0 -41
  59. universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtool2/__init__.py +0 -67
  60. universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtool2/__main__.py +0 -23
  61. universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtool2/agent.py +0 -13
  62. universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtool2/graph.py +0 -155
  63. universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtool2/meta_tools.py +0 -120
  64. universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtool2/prompts.py +0 -15
  65. universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache/state.py +0 -27
  66. universal_mcp_agents-0.1.10/src/universal_mcp/agents/builder.py +0 -204
  67. universal_mcp_agents-0.1.10/src/universal_mcp/agents/codeact/agent.py +0 -160
  68. universal_mcp_agents-0.1.10/src/universal_mcp/agents/codeact/prompts.py +0 -91
  69. universal_mcp_agents-0.1.10/src/universal_mcp/agents/codeact/sandbox.py +0 -51
  70. universal_mcp_agents-0.1.10/src/universal_mcp/agents/codeact/state.py +0 -10
  71. universal_mcp_agents-0.1.10/src/universal_mcp/applications/ui/app.py +0 -295
  72. universal_mcp_agents-0.1.10/test.py +0 -16
  73. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/.gitignore +0 -0
  74. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/.pre-commit-config.yaml +0 -0
  75. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/PROMPTS.md +0 -0
  76. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/README.md +0 -0
  77. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/bump_and_release.sh +0 -0
  78. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/__init__.py +0 -0
  79. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/dataset.py +0 -0
  80. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/datasets/exact.jsonl +0 -0
  81. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/datasets/tasks.jsonl +0 -0
  82. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/datasets/test.jsonl +0 -0
  83. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/evaluators.py +0 -0
  84. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/run.py +0 -0
  85. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/evals/utils.py +0 -0
  86. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/context.py +0 -0
  87. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/prompts.py +0 -0
  88. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtool2 → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/state.py +0 -0
  89. {universal_mcp_agents-0.1.10/src/universal_mcp/agents/bigtoolcache → universal_mcp_agents-0.1.12/src/universal_mcp/agents/bigtool}/tools.py +0 -0
  90. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/codeact/__init__.py +0 -0
  91. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/codeact/utils.py +0 -0
  92. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/llm.py +0 -0
  93. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/planner/__main__.py +0 -0
  94. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/planner/graph.py +0 -0
  95. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/planner/prompts.py +0 -0
  96. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/planner/state.py +0 -0
  97. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/shared/prompts.py +0 -0
  98. {universal_mcp_agents-0.1.10 → universal_mcp_agents-0.1.12}/src/universal_mcp/agents/shared/tool_node.py +0 -0
@@ -36,6 +36,7 @@ Usage in this repo
36
36
  - When adding libraries or changing versions, propose `uv add ...` changes that update both `pyproject.toml` and `uv.lock`, then run `uv run pytest -q` to validate
37
37
  - Prefer minimal diffs, explain the plan, apply changes, and run tests/tooling via `uv run`
38
38
  - If build/test fails, inspect error context, adjust constraints or code, and re-run via `uv run`
39
+ - After every file change, run `uv run ruff check .` to confirm the changes and ensure code quality.
39
40
 
40
41
  Common commands (copy/paste)
41
42
  - Initialize: `uv init` | Install deps: `uv sync`
@@ -43,4 +44,8 @@ Common commands (copy/paste)
43
44
  - Remove: `uv remove <pkg>`
44
45
  - Run app: `uv run python -m <your_module>` or `uv run main.py`
45
46
  - Tests: `uv run pytest -q`
46
- - Lint/format: `uv run ruff check .` and/or `uv run ruff format .`
47
+ - Lint/format: `uv run ruff check .` and/or `uv run ruff format .`
48
+
49
+
50
+
51
+ NEVER commit or push changes without asking explicitly.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp-agents
3
- Version: 0.1.10
3
+ Version: 0.1.12
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
@@ -0,0 +1,83 @@
1
+ import asyncio
2
+
3
+ from dotenv import load_dotenv
4
+ from langsmith import Client
5
+
6
+ from universal_mcp.agentr.registry import AgentrRegistry
7
+ from universal_mcp.agents.codeact0 import CodeActAgent
8
+
9
+ load_dotenv()
10
+
11
+ client = Client()
12
+
13
+ async def create_examples(user_input: str, tools_list: list[str]):
14
+ """Run the agent and create a LangSmith dataset example"""
15
+
16
+ # Create/get dataset
17
+ dataset_name = "codeagent-tests"
18
+ try:
19
+ dataset = client.create_dataset(
20
+ dataset_name,
21
+ description="Dataset for the codeagent"
22
+ )
23
+ except Exception:
24
+ dataset = client.read_dataset(dataset_name=dataset_name)
25
+
26
+
27
+ # Define the input
28
+ # user_input = "Send an email to Manoj from my google mail account, manoj@agentr.dev, with the subject 'Hello from auto agent' and the body 'testing'"
29
+
30
+ # Capture initial state
31
+ initial_state = {
32
+ "messages": [{"role": "user", "content": user_input}],
33
+ "tools": tools_list
34
+ }
35
+ #result = await agent.ainvoke(initial_state, context={"model": "anthropic/claude-4-sonnet-20250514", "system_time": system_time})
36
+
37
+ # Extract the final state from the result
38
+ # Note: Adjust these based on your actual result structure
39
+
40
+ # Create the dataset example with actual results
41
+ example = client.create_example(
42
+ inputs=initial_state,
43
+ outputs=None,
44
+ dataset_id=dataset.id
45
+ )
46
+
47
+ print(f"✅ Created dataset example with ID: {example.id}")
48
+ print(f"Dataset: {dataset_name}")
49
+ print(f"Input: {user_input}")
50
+
51
+ return example
52
+
53
+ import yaml
54
+ import os
55
+
56
+ if __name__ == "__main__":
57
+ usecases_dir = os.path.join("src", "universal_mcp", "agents", "codeact0", "usecases")
58
+ async def main():
59
+ for name in sorted(os.listdir(usecases_dir)):
60
+ if not name.endswith(".yaml"):
61
+ continue
62
+ path = os.path.join(usecases_dir, name)
63
+ with open(path, encoding="utf-8") as f:
64
+ content = f.read()
65
+ data = yaml.safe_load(content) or {}
66
+ base_prompt = data.get("base_prompt")
67
+ tools = data.get("tools")
68
+ if not base_prompt:
69
+ continue
70
+ # Normalize tools to a flat list[str]
71
+ tools_list: list[str] = []
72
+ if isinstance(tools, list):
73
+ tools_list = tools
74
+ elif isinstance(tools, dict):
75
+ for v in tools.values():
76
+ if isinstance(v, list):
77
+ tools_list.extend(v)
78
+ elif isinstance(v, str):
79
+ tools_list.append(v)
80
+ print(f"Creating example for {name} with {len(tools_list)} tools…")
81
+ await create_examples(base_prompt, tools_list)
82
+
83
+ asyncio.run(main())
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "universal-mcp-agents"
9
- version = "0.1.10"
9
+ version = "0.1.12"
10
10
  description = "Add your description here"
11
11
  readme = "README.md"
12
12
  authors = [
@@ -79,3 +79,8 @@ pythonpath = [
79
79
  asyncio_mode = "strict"
80
80
  asyncio_default_fixture_loop_scope = "module"
81
81
 
82
+ [dependency-groups]
83
+ dev = [
84
+ "ruff>=0.13.0",
85
+ ]
86
+
@@ -8,7 +8,7 @@ from universal_mcp.types import ToolFormat
8
8
 
9
9
  from universal_mcp.agents import get_agent
10
10
  from universal_mcp.agents.base import BaseAgent
11
- from universal_mcp.agents.builder import BuilderAgent
11
+ from universal_mcp.agents.builder.builder import BuilderAgent
12
12
  from universal_mcp.agents.llm import load_chat_model
13
13
  from universal_mcp.agents.shared.tool_node import build_tool_node_graph
14
14
 
@@ -284,9 +284,9 @@ class TestToolFinderGraph:
284
284
  "react",
285
285
  "simple",
286
286
  "builder",
287
- "planner",
288
- "bigtoolcache",
289
- "bigtool2",
287
+ "bigtool",
288
+ # "codeact-script",
289
+ # "codeact-repl",
290
290
  ],
291
291
  )
292
292
  class TestAgents:
@@ -312,7 +312,7 @@ class TestAgents:
312
312
  await agent.ainit()
313
313
  # Invoke the agent graph to get the final state
314
314
  final_state = await agent.invoke(
315
- task,
315
+ user_input=task,
316
316
  thread_id=thread_id,
317
317
  )
318
318
 
@@ -335,7 +335,7 @@ class TestAgentBuilder:
335
335
  agent = BuilderAgent(
336
336
  name="Test Builder Agent",
337
337
  instructions="Test instructions for builder",
338
- model="gemini/gemini-1.5-flash",
338
+ model="gemini/gemini-2.5-flash",
339
339
  registry=registry,
340
340
  )
341
341
  yield agent
@@ -0,0 +1,39 @@
1
+ from typing import Literal
2
+
3
+ from universal_mcp.agents.base import BaseAgent
4
+ from universal_mcp.agents.bigtool import BigToolAgent
5
+ from universal_mcp.agents.builder.builder import BuilderAgent
6
+ from universal_mcp.agents.codeact0 import CodeActAgent as CodeActRepl
7
+ from universal_mcp.agents.codeact import CodeActAgent as CodeActScript
8
+ from universal_mcp.agents.react import ReactAgent
9
+ from universal_mcp.agents.simple import SimpleAgent
10
+
11
+
12
+ def get_agent(agent_name: Literal["react", "simple", "builder", "bigtool", "codeact-script", "codeact-repl"]):
13
+ if agent_name == "react":
14
+ return ReactAgent
15
+ elif agent_name == "simple":
16
+ return SimpleAgent
17
+ elif agent_name == "builder":
18
+ return BuilderAgent
19
+ elif agent_name == "bigtool":
20
+ return BigToolAgent
21
+ elif agent_name == "codeact-script":
22
+ return CodeActScript
23
+ elif agent_name == "codeact-repl":
24
+ return CodeActRepl
25
+ else:
26
+ raise ValueError(
27
+ f"Unknown agent: {agent_name}. Possible values: react, simple, builder, bigtool, codeact-script, codeact-repl"
28
+ )
29
+
30
+
31
+ __all__ = [
32
+ "BaseAgent",
33
+ "ReactAgent",
34
+ "SimpleAgent",
35
+ "BuilderAgent",
36
+ "BigToolAgent",
37
+ "CodeActScript",
38
+ "CodeActRepl",
39
+ ]
@@ -2,7 +2,7 @@
2
2
  from typing import cast
3
3
  from uuid import uuid4
4
4
 
5
- from langchain_core.messages import AIMessage, AIMessageChunk
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
@@ -90,7 +90,7 @@ class BaseAgent:
90
90
  async def stream_interactive(self, thread_id: str, user_input: str):
91
91
  await self.ainit()
92
92
  with self.cli.display_agent_response_streaming(self.name) as stream_updater:
93
- async for event in self.stream(thread_id, user_input):
93
+ async for event in self.stream(thread_id=thread_id, user_input=user_input):
94
94
  if isinstance(event.content, list):
95
95
  thinking_content = "".join([c.get("thinking", "") for c in event.content])
96
96
  stream_updater.update(thinking_content, type_="thinking")
@@ -112,6 +112,7 @@ class BaseAgent:
112
112
  run_metadata.update(metadata)
113
113
 
114
114
  run_config = {
115
+ "recursion_limit": 25,
115
116
  "configurable": {"thread_id": thread_id},
116
117
  "metadata": run_metadata,
117
118
  }
@@ -123,6 +124,11 @@ class BaseAgent:
123
124
  )
124
125
  return result
125
126
 
127
+ async def get_state(self, thread_id: str):
128
+ await self.ainit()
129
+ state = await self._graph.aget_state(config={"configurable": {"thread_id": thread_id}})
130
+ return state
131
+
126
132
  async def run_interactive(self, thread_id: str = str(uuid4())):
127
133
  """Main application loop"""
128
134
 
@@ -133,7 +139,7 @@ class BaseAgent:
133
139
  # Main loop
134
140
  while True:
135
141
  try:
136
- state = self._graph.get_state(config={"configurable": {"thread_id": thread_id}})
142
+ state = await self.get_state(thread_id=thread_id)
137
143
  if state.interrupts:
138
144
  value = self.cli.handle_interrupt(state.interrupts[0])
139
145
  self._graph.invoke(
@@ -168,14 +174,11 @@ class BaseAgent:
168
174
  continue
169
175
 
170
176
  # Process with agent
171
- await self.stream_interactive(thread_id, user_input)
177
+ await self.stream_interactive(thread_id=thread_id, user_input=user_input)
172
178
 
173
179
  except KeyboardInterrupt:
174
180
  self.cli.display_info("\nGoodbye! 👋")
175
181
  break
176
182
  except Exception as e:
177
- import traceback
178
-
179
- traceback.print_exc()
180
183
  self.cli.display_error(f"An error occurred: {str(e)}")
181
184
  break
@@ -13,7 +13,7 @@ from .prompts import SYSTEM_PROMPT
13
13
  from .tools import create_meta_tools
14
14
 
15
15
 
16
- class BigToolAgentCache(BaseAgent):
16
+ class BigToolAgent(BaseAgent):
17
17
  def __init__(
18
18
  self,
19
19
  registry: ToolRegistry,
@@ -63,4 +63,4 @@ class BigToolAgentCache(BaseAgent):
63
63
  return self._graph
64
64
 
65
65
 
66
- __all__ = ["BigToolAgentCache"]
66
+ __all__ = ["BigToolAgent"]
@@ -2,7 +2,6 @@ import asyncio
2
2
 
3
3
  from loguru import logger
4
4
  from universal_mcp.agentr.registry import AgentrRegistry
5
-
6
5
  from universal_mcp.agents.bigtoolcache import BigToolAgentCache
7
6
 
8
7
 
@@ -1,5 +1,4 @@
1
1
  from universal_mcp.agentr.registry import AgentrRegistry
2
-
3
2
  from universal_mcp.agents.bigtoolcache import BigToolAgentCache
4
3
 
5
4
 
@@ -8,7 +8,6 @@ from langchain_core.messages import AIMessage, SystemMessage, ToolMessage
8
8
  from langchain_core.tools import BaseTool
9
9
  from langgraph.graph import StateGraph
10
10
  from langgraph.types import Command
11
- from loguru import logger
12
11
  from universal_mcp.tools.registry import ToolRegistry
13
12
  from universal_mcp.types import ToolFormat
14
13
 
@@ -35,7 +34,11 @@ def build_graph(
35
34
  current_tools = await registry.export_tools(tools=state["selected_tool_ids"], format=ToolFormat.LANGCHAIN)
36
35
  else:
37
36
  current_tools = []
38
- all_tools = [meta_tools["search_tools"], meta_tools["load_tools"], meta_tools.get("web_search")] + default_tools + current_tools
37
+ all_tools = (
38
+ [meta_tools["search_tools"], meta_tools["load_tools"], meta_tools.get("web_search")]
39
+ + default_tools
40
+ + current_tools
41
+ )
39
42
 
40
43
  # Remove duplicates based on tool name
41
44
  seen_names = set()
@@ -81,7 +84,7 @@ def build_graph(
81
84
  valid_tools = await get_valid_tools(tool_ids=tool_call["args"]["tool_ids"], registry=registry)
82
85
  new_tool_ids.extend(valid_tools)
83
86
  # Create tool message response
84
- tool_result=f"Successfully loaded {len(valid_tools)} tools: {valid_tools}"
87
+ tool_result = f"Successfully loaded {len(valid_tools)} tools: {valid_tools}"
85
88
  elif tool_call["name"] == "search_tools":
86
89
  tool_result = await meta_tools["search_tools"].ainvoke(tool_call["args"])
87
90
  elif tool_call["name"] == "web_search":
@@ -99,8 +102,6 @@ def build_graph(
99
102
 
100
103
  return Command(goto="agent", update={"messages": tool_messages, "selected_tool_ids": new_tool_ids})
101
104
 
102
-
103
-
104
105
  # Define the graph
105
106
  workflow = StateGraph(State)
106
107
 
@@ -0,0 +1,125 @@
1
+ import asyncio
2
+ import json
3
+ from uuid import uuid4
4
+
5
+ from langgraph.checkpoint.memory import MemorySaver
6
+ from loguru import logger
7
+ from universal_mcp.agentr.registry import AgentrRegistry
8
+
9
+ from universal_mcp.agents.builder.builder import BuilderAgent
10
+
11
+
12
+ async def run_interactive_build():
13
+ """Simulates a multi-turn conversation to build and then modify an agent."""
14
+ logger.info("--- SCENARIO 1: INTERACTIVE AGENT BUILD & MODIFY ---")
15
+
16
+ registry = AgentrRegistry()
17
+ memory = MemorySaver()
18
+ agent = BuilderAgent(
19
+ name="Builder Agent",
20
+ instructions="You are a builder agent that creates other agents.",
21
+ model="anthropic/claude-4-sonnet-20250514",
22
+ registry=registry,
23
+ memory=memory,
24
+ )
25
+
26
+ thread_id = str(uuid4())
27
+
28
+ conversation_script = [
29
+ "Send an email to manoj@agentr.dev with the subject 'Hello' and body 'This is a test of the Gmail agent.' from my Gmail account.",
30
+ "Use outlook instead of gmail",
31
+ ]
32
+
33
+ final_result = {}
34
+ for i, user_input in enumerate(conversation_script):
35
+ logger.info(f"\n--- Conversation Turn {i + 1} ---")
36
+ logger.info(f"User Request: '{user_input}'")
37
+
38
+ result = await agent.invoke(user_input=user_input, thread_id=thread_id)
39
+ final_result.update(result) # Keep updating the final result
40
+
41
+ generated_agent = final_result.get("generated_agent")
42
+ tool_config = final_result.get("tool_config")
43
+
44
+ if generated_agent:
45
+ logger.info("--- Generated/Modified Agent ---")
46
+ logger.info(f"Name: {generated_agent.name}")
47
+ logger.info(f"Description: {generated_agent.description}")
48
+ logger.info(f"Expertise: {generated_agent.expertise}")
49
+ logger.info(f"Instructions:\n{generated_agent.instructions}")
50
+
51
+ if tool_config:
52
+ logger.info("--- Selected Tools ---")
53
+ tools_str = "\n".join(f"- {app}: {', '.join(tool_ids)}" for app, tool_ids in tool_config.items())
54
+ logger.info(tools_str)
55
+ else:
56
+ logger.info("--- Selected Tools ---")
57
+ logger.info("No tools selected for this agent yet.")
58
+
59
+
60
+ async def run_conversation_build():
61
+ """Simulates a one-shot agent build from a conversation history payload."""
62
+ logger.info("\n\n--- SCENARIO 2: AGENT BUILD FROM CONVERSATION HISTORY ---")
63
+
64
+ registry = AgentrRegistry()
65
+ agent = BuilderAgent(
66
+ name="Builder Agent",
67
+ instructions="You build agents from conversation transcripts.",
68
+ model="anthropic/claude-4-sonnet-20250514",
69
+ registry=registry,
70
+ )
71
+
72
+ sample_conversation_history = [
73
+ {
74
+ "type": "human",
75
+ "content": "Hey, can you look at our main branch on the universal-mcp repo and tell me what the last 3 pull requests were?",
76
+ },
77
+ {
78
+ "type": "ai",
79
+ "content": "Of course. The last 3 pull requests are: #101 'Fix login bug', #102 'Update documentation', and #103 'Add new chart component'.",
80
+ },
81
+ {
82
+ "type": "human",
83
+ "content": "Awesome, thanks. Now can you draft a new Google Doc and put that list in there for me?",
84
+ },
85
+ {"type": "ai", "content": "Done. I have created a new Google Doc with the list of the last 3 pull requests."},
86
+ ]
87
+ sample_tool_config = {"github": ["get_pull_requests"], "google_docs": ["create_document"]}
88
+ wingman_payload = {"conversation_history": sample_conversation_history, "tool_config": sample_tool_config}
89
+
90
+ logger.info(f"Payload Conversation History Length: {len(sample_conversation_history)} messages")
91
+ logger.info(f"Payload Tools Provided: {list(sample_tool_config.keys())}")
92
+
93
+ # The payload must be passed as a JSON string in the 'user_input'
94
+ payload_str = json.dumps(wingman_payload)
95
+ thread_id = str(uuid4())
96
+ result = await agent.invoke(user_input=payload_str, thread_id=thread_id)
97
+
98
+ generated_agent = result.get("generated_agent")
99
+ tool_config = result.get("tool_config")
100
+
101
+ if generated_agent:
102
+ logger.info("\n--- Generated Agent Profile ---")
103
+ logger.info(f"Name: {generated_agent.name}")
104
+ logger.info(f"Description: {generated_agent.description}")
105
+ logger.info(f"Expertise: {generated_agent.expertise}")
106
+ logger.info(f"Instructions:\n{generated_agent.instructions}")
107
+ logger.info(f"Schedule: {generated_agent.schedule}")
108
+ else:
109
+ logger.error("Error: Agent profile was not generated.")
110
+
111
+ if tool_config:
112
+ logger.info("--- Final Tool Configuration ---")
113
+ tools_str = "\n".join(f"- {app}: {', '.join(tool_ids)}" for app, tool_ids in tool_config.items())
114
+ logger.info(tools_str)
115
+ else:
116
+ logger.error("Error: Tool configuration is missing.")
117
+
118
+
119
+ async def main():
120
+ await run_interactive_build()
121
+ await run_conversation_build()
122
+
123
+
124
+ if __name__ == "__main__":
125
+ asyncio.run(main())