universal-mcp-agents 0.1.4__py3-none-any.whl → 0.1.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.

Potentially problematic release.


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

Files changed (39) hide show
  1. universal_mcp/agents/__init__.py +19 -0
  2. universal_mcp/agents/autoagent/__init__.py +1 -1
  3. universal_mcp/agents/autoagent/__main__.py +1 -1
  4. universal_mcp/agents/autoagent/graph.py +2 -2
  5. universal_mcp/agents/base.py +24 -13
  6. universal_mcp/agents/bigtool/__init__.py +10 -8
  7. universal_mcp/agents/bigtool/__main__.py +6 -7
  8. universal_mcp/agents/bigtool/graph.py +18 -27
  9. universal_mcp/agents/bigtool/prompts.py +3 -3
  10. universal_mcp/agents/bigtool2/__init__.py +16 -3
  11. universal_mcp/agents/bigtool2/__main__.py +6 -5
  12. universal_mcp/agents/bigtool2/agent.py +1 -1
  13. universal_mcp/agents/bigtool2/graph.py +55 -20
  14. universal_mcp/agents/bigtool2/prompts.py +8 -5
  15. universal_mcp/agents/bigtoolcache/agent.py +2 -2
  16. universal_mcp/agents/bigtoolcache/graph.py +5 -6
  17. universal_mcp/agents/bigtoolcache/prompts.py +1 -2
  18. universal_mcp/agents/builder.py +47 -14
  19. universal_mcp/agents/cli.py +19 -5
  20. universal_mcp/agents/codeact/test.py +2 -1
  21. universal_mcp/agents/llm.py +7 -3
  22. universal_mcp/agents/planner/__init__.py +8 -2
  23. universal_mcp/agents/planner/__main__.py +10 -8
  24. universal_mcp/agents/planner/graph.py +6 -2
  25. universal_mcp/agents/planner/prompts.py +14 -1
  26. universal_mcp/agents/planner/state.py +0 -1
  27. universal_mcp/agents/react.py +36 -27
  28. universal_mcp/agents/shared/tool_node.py +2 -3
  29. universal_mcp/agents/simple.py +19 -3
  30. universal_mcp/agents/utils.py +36 -36
  31. universal_mcp/applications/ui/app.py +305 -0
  32. {universal_mcp_agents-0.1.4.dist-info → universal_mcp_agents-0.1.6.dist-info}/METADATA +2 -2
  33. universal_mcp_agents-0.1.6.dist-info/RECORD +50 -0
  34. universal_mcp/agents/autoagent/studio.py +0 -19
  35. universal_mcp/agents/bigtool/context.py +0 -24
  36. universal_mcp/agents/bigtool2/context.py +0 -32
  37. universal_mcp/agents/tools.py +0 -41
  38. universal_mcp_agents-0.1.4.dist-info/RECORD +0 -53
  39. {universal_mcp_agents-0.1.4.dist-info → universal_mcp_agents-0.1.6.dist-info}/WHEEL +0 -0
@@ -11,11 +11,11 @@ from langgraph.runtime import Runtime
11
11
  from langgraph.types import Command
12
12
 
13
13
  from universal_mcp.agents.bigtoolcache.context import Context
14
+ from universal_mcp.agents.bigtoolcache.prompts import TOOLS_LIST
14
15
  from universal_mcp.agents.bigtoolcache.state import State
15
16
  from universal_mcp.logger import logger
16
17
  from universal_mcp.tools.registry import ToolRegistry
17
18
  from universal_mcp.types import ToolFormat
18
- from universal_mcp.agents.bigtoolcache.prompts import TOOLS_LIST
19
19
 
20
20
 
21
21
  class ToolSelectionOutput(TypedDict):
@@ -179,15 +179,14 @@ def build_graph(tool_registry: ToolRegistry, llm: BaseChatModel):
179
179
  content=json.dumps(tool_result),
180
180
  name=tool_id,
181
181
  tool_call_id=tool_call["id"],
182
- )
183
- )
184
- recent_tool_ids.append(tool_id)
182
+ ))
183
+ recent_tool_ids.append(tool_call["name"])
185
184
  except Exception as e:
186
- logger.error(f"Error executing tool '{tool_id}': {e}")
185
+ logger.error(f"Error executing tool '{tool_call['name']}': {e}")
187
186
  outputs.append(
188
187
  ToolMessage(
189
188
  content=json.dumps("Error: " + str(e)),
190
- name=tool_id,
189
+ name=tool_call["name"],
191
190
  tool_call_id=tool_call["id"],
192
191
  )
193
192
  )
@@ -1,6 +1,5 @@
1
1
  """Default prompts used by the agent."""
2
2
 
3
- import os
4
3
  from pathlib import Path
5
4
 
6
5
 
@@ -14,7 +13,7 @@ def load_tools_from_file():
14
13
  tools_file = current_dir / "tools_all.txt"
15
14
 
16
15
  if tools_file.exists():
17
- with open(tools_file, "r", encoding="utf-8") as f:
16
+ with open(tools_file, encoding="utf-8") as f:
18
17
  return f.read()
19
18
  else:
20
19
  return "No tools file found. Please run tool_retrieve.py to generate the tools list."
@@ -8,12 +8,13 @@ from langgraph.checkpoint.base import BaseCheckpointSaver
8
8
  from langgraph.graph import END, START, StateGraph
9
9
  from langgraph.graph.message import add_messages
10
10
  from pydantic import BaseModel, Field
11
+ from universal_mcp.tools.registry import ToolRegistry
12
+ from universal_mcp.types import ToolConfig
11
13
 
12
14
  from universal_mcp.agents.base import BaseAgent
13
15
  from universal_mcp.agents.llm import load_chat_model
14
16
  from universal_mcp.agents.shared.tool_node import build_tool_node_graph
15
- from universal_mcp.tools.registry import ToolRegistry
16
- from universal_mcp.types import ToolConfig
17
+ from universal_mcp.agents.utils import messages_to_list
17
18
 
18
19
 
19
20
  class Agent(BaseModel):
@@ -35,17 +36,46 @@ class BuilderState(TypedDict):
35
36
  messages: Annotated[Sequence[BaseMessage], add_messages]
36
37
 
37
38
 
38
- AGENT_BUILDER_INSTRUCTIONS = """
39
- You are an agent builder. Your goal is to create an agent that can accomplish the user's task.
40
- Your will be given a task and you need to generate an agent that can accomplish the task.
41
- The agent should have a name, role, instructions, and a model.
42
- - The name should be a short and descriptive name for the agent.
43
- - The description should be a small description of the agent. For example, research a stock and write a buy sell analysis report.
44
- - The expertise should be the expertise of the agent. For example, GTM Expert, SEO Expert, etc.
45
- - The instructions should be a detailed description of what the agent should do. This should include the input, the output, and the tool usage. The agent will be provided a set of tools, you can use that to give a more accurate response.
46
- - The model should be the model to use for the agent.
47
- - The reasoning should be a detailed explanation of why you are creating this agent with these parameters.
48
- - If the user specifies a schedule, you should also provide a cron expression for the agent to run on. The schedule should be in a proper cron expression and nothing more.
39
+ AGENT_BUILDER_INSTRUCTIONS = r"""
40
+ You are a specialized Agent Generation AI, tasked with creating intelligent, effective, and context-aware AI agents based on user requests.
41
+
42
+ When given a user's request, immediately follow this structured process:
43
+
44
+ # 1. Intent Breakdown
45
+ - Clearly identify the primary goal the user wants the agent to achieve.
46
+ - Recognize any special requirements, constraints, formatting requests, or interaction rules.
47
+ - Summarize your understanding briefly to ensure alignment with user intent.
48
+
49
+ # 2. Agent Profile Definition
50
+ - **Name (2-4 words)**: Concise, clear, and memorable name reflecting core functionality.
51
+ - **Description (1-2 sentences)**: Captures the unique value and primary benefit to users.
52
+ - **Expertise**: Precise domain-specific expertise area. Avoid vague or overly general titles.
53
+ - **Instructions**: Compose detailed, highly actionable system instructions that directly command the agent's behavior. Respond in markdown as this text will be rendered in a rich text editor. Write instructions as clear imperatives, without preamble, assuming the agent identity is already established externally.
54
+ - **Schedule**: If the user specifies a schedule, you should also provide a cron expression for the agent to run on. The schedule should be in a proper cron expression and nothing more. Do not respond with any other information or explain your reasoning for the schedule, otherwise this will cause a parsing error that is undesirable.
55
+
56
+ ## ROLE & RESPONSIBILITY
57
+ - Clearly state the agent's primary mission, e.g., "Your primary mission is...", "Your core responsibility is...".
58
+ - Outline the exact tasks it handles, specifying expected input/output clearly.
59
+
60
+ ## INTERACTION STYLE
61
+ - Define exactly how to communicate with users: tone, format, response structure.
62
+ - Include explicit commands, e.g., "Always wrap responses in \`\`\`text\`\`\` blocks.", "Never add greetings or meta-information.", "Always provide outputs in user's requested languages."
63
+
64
+ ## OUTPUT FORMATTING RULES
65
+ - Clearly specify formatting standards required by the user (e.g., JSON, plain text, markdown).
66
+ - Include explicit examples to illustrate correct formatting.
67
+
68
+ ## LIMITATIONS & CONSTRAINTS
69
+ - Explicitly define boundaries of the agent's capabilities.
70
+ - Clearly state what the agent must never do or say.
71
+ - Include exact phrases for declining requests outside scope.
72
+
73
+ ## REAL-WORLD EXAMPLES
74
+ Provide two explicit interaction examples showing:
75
+ - User's typical request.
76
+ - Final agent response demonstrating perfect compliance.
77
+
78
+ Create an agent that feels thoughtfully designed, intelligent, and professionally reliable, perfectly matched to the user's original intent.
49
79
  """
50
80
 
51
81
 
@@ -153,7 +183,10 @@ async def main():
153
183
  result = await agent.invoke(
154
184
  "Send a daily email to manoj@agentr.dev with daily agenda of the day",
155
185
  )
156
- print(result)
186
+ from rich import print
187
+ print(messages_to_list(result["messages"]))
188
+ print(result["generated_agent"])
189
+ print(result["tool_config"])
157
190
 
158
191
 
159
192
  if __name__ == "__main__":
@@ -1,8 +1,11 @@
1
+ from langgraph.checkpoint.memory import MemorySaver
1
2
  from typer import Typer
2
-
3
- from universal_mcp.agents import ReactAgent
3
+ from universal_mcp.agentr.client import AgentrClient
4
+ from universal_mcp.agentr.registry import AgentrRegistry
4
5
  from universal_mcp.logger import setup_logger
5
6
 
7
+ from universal_mcp.agents import get_agent
8
+
6
9
  app = Typer()
7
10
 
8
11
 
@@ -13,13 +16,24 @@ app = Typer()
13
16
  mcp client run --config client_config.json
14
17
  """,
15
18
  )
16
- def run():
19
+ def run(name: str = "react"):
17
20
  """Run the agent CLI"""
18
21
  import asyncio
19
22
 
20
- setup_logger(log_file=None, level="WARNING")
21
23
 
22
- agent = ReactAgent("React Agent", "You are a helpful assistant", "openrouter/auto")
24
+ setup_logger(log_file=None, level="ERROR")
25
+ client = AgentrClient()
26
+ params = {
27
+ "instructions": "You are a helpful assistant",
28
+ "model": "anthropic/claude-sonnet-4-20250514",
29
+ "registry": AgentrRegistry(client=client),
30
+ "memory": MemorySaver(),
31
+ "tools": {
32
+ "google_mail": ["send_email"],
33
+ }
34
+ }
35
+ agent_cls = get_agent(name)
36
+ agent = agent_cls(name, **params)
23
37
  asyncio.run(agent.run_interactive())
24
38
 
25
39
 
@@ -1,8 +1,9 @@
1
1
  from universal_mcp.agentr import Agentr
2
+ from universal_mcp.tools.adapters import ToolFormat
3
+
2
4
  from universal_mcp.agents.codeact import create_codeact
3
5
  from universal_mcp.agents.codeact.sandbox import eval_unsafe
4
6
  from universal_mcp.agents.llm import load_chat_model
5
- from universal_mcp.tools.adapters import ToolFormat
6
7
 
7
8
  model = load_chat_model("gpt-4.1")
8
9
 
@@ -1,22 +1,26 @@
1
+ from functools import lru_cache
2
+
1
3
  from langchain_anthropic import ChatAnthropic
2
4
  from langchain_core.language_models import BaseChatModel
3
5
  from langchain_google_genai import ChatGoogleGenerativeAI
4
6
  from langchain_openai import AzureChatOpenAI
5
7
 
6
8
 
9
+ @lru_cache(maxsize=8)
7
10
  def load_chat_model(
8
- fully_specified_name: str, temperature: float = 1.0, tags: list[str] | None = None
11
+ fully_specified_name: str, temperature: float = 1.0, tags: list[str] | None = None, thinking: bool = False
9
12
  ) -> BaseChatModel:
10
13
  """Load a chat model from a fully specified name.
11
14
  Args:
12
15
  fully_specified_name (str): String in the format 'provider/model'.
13
16
  """
14
- provider, model = fully_specified_name.split("/", maxsplit=1)
17
+ fully_specified_name = fully_specified_name.replace("/", ":")
18
+ provider, model = fully_specified_name.split(":", maxsplit=1)
15
19
  if provider == "anthropic":
16
20
  return ChatAnthropic(
17
21
  model=model,
18
22
  temperature=temperature,
19
- # thinking={"type": "enabled", "budget_tokens": 2048},
23
+ thinking={"type": "enabled", "budget_tokens": 2048} if thinking else None,
20
24
  max_tokens=4096,
21
25
  tags=tags,
22
26
  stream_usage=True,
@@ -1,11 +1,12 @@
1
1
  from langgraph.checkpoint.base import BaseCheckpointSaver
2
+ from universal_mcp.tools.registry import ToolRegistry
2
3
 
3
4
  from universal_mcp.agents.base import BaseAgent
4
5
  from universal_mcp.agents.llm import load_chat_model
5
6
  from universal_mcp.agents.react import ReactAgent
6
- from universal_mcp.tools.registry import ToolRegistry
7
7
 
8
8
  from .graph import build_graph
9
+ from .prompts import DEVELOPER_PROMPT
9
10
 
10
11
 
11
12
  class PlannerAgent(BaseAgent):
@@ -24,11 +25,16 @@ class PlannerAgent(BaseAgent):
24
25
  self.llm = load_chat_model(model)
25
26
  self.executor_agent_cls = executor_agent_cls
26
27
 
28
+ def _build_system_message(self):
29
+ return DEVELOPER_PROMPT.format(
30
+ name=self.name, instructions=self.instructions
31
+ )
32
+
27
33
  async def _build_graph(self):
28
34
  return build_graph(
29
35
  self.llm,
30
36
  self.app_registry,
31
- self.instructions,
37
+ self._build_system_message(),
32
38
  self.model,
33
39
  self.executor_agent_cls,
34
40
  ).compile(checkpointer=self.memory)
@@ -1,7 +1,9 @@
1
1
  import asyncio
2
2
 
3
3
  from universal_mcp.agentr.registry import AgentrRegistry
4
+
4
5
  from universal_mcp.agents.planner import PlannerAgent
6
+ from universal_mcp.agents.utils import messages_to_list
5
7
 
6
8
 
7
9
  async def main():
@@ -9,17 +11,17 @@ async def main():
9
11
  agent = PlannerAgent(
10
12
  name="planner-agent",
11
13
  instructions="You are a helpful assistant.",
12
- model="gemini/gemini-2.5-flash",
14
+ model="azure/gpt-4o",
13
15
  registry=registry,
14
16
  )
15
- from rich.console import Console
17
+ from rich import print
16
18
 
17
- console = Console()
18
- console.print("Starting agent...", style="yellow")
19
- async for event in agent.stream(
20
- user_input="Send an email to manoj@agentr.dev'", thread_id="xyz"
21
- ):
22
- console.print(event.content, style="red")
19
+ print("Starting agent...")
20
+ result = await agent.invoke(
21
+ user_input="Send an email to manoj@agentr.dev with the subject 'testing planner' and body 'This is a test of the planner agent.'",
22
+ thread_id="xyz",
23
+ )
24
+ print(messages_to_list(result["messages"]))
23
25
 
24
26
 
25
27
  if __name__ == "__main__":
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from langchain_core.messages import AIMessage
3
+ from langchain_core.messages import AIMessage, SystemMessage
4
4
  from langgraph.graph import END, START, StateGraph
5
5
  from loguru import logger
6
6
 
@@ -61,7 +61,11 @@ def build_graph(llm, registry, instructions, model, executor_agent_cls):
61
61
  async def _no_tools_node(state: State) -> dict[str, Any]:
62
62
  """Handles tasks that don't require tools by invoking the LLM directly."""
63
63
  logger.info("No tools required. Invoking LLM directly.")
64
- response = await llm.ainvoke(state["messages"])
64
+ messages = [
65
+ SystemMessage(content=instructions),
66
+ *state["messages"],
67
+ ]
68
+ response = await llm.ainvoke(messages)
65
69
  return {"messages": [response]}
66
70
 
67
71
  graph_builder.add_node("tool_finder", _tool_finder_node)
@@ -1 +1,14 @@
1
- # Prompts for the planner agent
1
+ # prompts.py
2
+
3
+ DEVELOPER_PROMPT = """
4
+ You are a planner agent that orchestrates tasks by selecting the right tools.
5
+
6
+ Your primary goal is to analyze a user's request and determine the most effective sequence of tools to accomplish it. You have access to a registry of applications and their corresponding tools.
7
+
8
+ Here's your process:
9
+ 1. **Assess the Task**: Understand the user's intent and what they want to achieve.
10
+ 2. **Identify Necessary Tools**: Based on the task, identify which applications and tools are required.
11
+ 3. **Orchestrate Execution**: Pass the selected tools and instructions to an executor agent to perform the task.
12
+
13
+ {instructions}
14
+ """
@@ -2,7 +2,6 @@ from typing import Annotated
2
2
 
3
3
  from langgraph.graph.message import add_messages
4
4
  from typing_extensions import TypedDict
5
-
6
5
  from universal_mcp.types import ToolConfig
7
6
 
8
7
 
@@ -1,13 +1,28 @@
1
1
  from langgraph.checkpoint.base import BaseCheckpointSaver
2
2
  from langgraph.prebuilt import create_react_agent
3
3
  from loguru import logger
4
-
5
4
  from universal_mcp.agentr.registry import AgentrRegistry
6
- from universal_mcp.agents.base import BaseAgent
7
- from universal_mcp.agents.llm import load_chat_model
8
5
  from universal_mcp.tools.registry import ToolRegistry
9
6
  from universal_mcp.types import ToolConfig, ToolFormat
10
7
 
8
+ from universal_mcp.agents.base import BaseAgent
9
+ from universal_mcp.agents.llm import load_chat_model
10
+ from universal_mcp.agents.utils import messages_to_list
11
+
12
+ DEVELOPER_PROMPT = """You are {name}.
13
+
14
+ You have access to various tools that can help you answer questions and complete tasks. When you need to use a tool:
15
+
16
+ 1. Think about what information you need
17
+ 2. Call the appropriate tool with the right parameters
18
+ 3. Use the tool results to provide a comprehensive answer
19
+
20
+ Always explain your reasoning and be thorough in your responses. If you need to use multiple tools to answer a question completely, do so.
21
+
22
+ Adhere to the following instructions strictly:
23
+ {instructions}
24
+ """
25
+
11
26
 
12
27
  class ReactAgent(BaseAgent):
13
28
  def __init__(
@@ -23,7 +38,9 @@ class ReactAgent(BaseAgent):
23
38
  ):
24
39
  super().__init__(name, instructions, model, memory, **kwargs)
25
40
  self.llm = load_chat_model(model)
26
- self.tools = tools
41
+ self.tools = tools or {}
42
+ if "ui" not in self.tools:
43
+ self.tools["ui"] = ["create_bar_chart", "create_line_chart", "create_pie_chart", "create_table", "http_get", "http_post", "http_put", "http_delete", "http_patch", "read_file"]
27
44
  self.max_iterations = max_iterations
28
45
  self.registry = registry
29
46
 
@@ -32,12 +49,12 @@ class ReactAgent(BaseAgent):
32
49
  if self.tools:
33
50
  if not self.registry:
34
51
  raise ValueError("Tools are configured but no registry is provided")
35
-
36
52
  tools = await self.registry.export_tools(self.tools, ToolFormat.LANGCHAIN)
37
53
  logger.debug(tools)
38
54
  else:
39
55
  tools = []
40
56
 
57
+
41
58
  logger.debug(f"Initialized ReactAgent: name={self.name}, model={self.model}")
42
59
  return create_react_agent(
43
60
  self.llm,
@@ -47,34 +64,26 @@ class ReactAgent(BaseAgent):
47
64
  )
48
65
 
49
66
  def _build_system_message(self):
50
- system_message = f"""You are {self.name}.
67
+ return DEVELOPER_PROMPT.format(name=self.name, instructions=self.instructions)
51
68
 
52
- You have access to various tools that can help you answer questions and complete tasks. When you need to use a tool:
53
-
54
- 1. Think about what information you need
55
- 2. Call the appropriate tool with the right parameters
56
- 3. Use the tool results to provide a comprehensive answer
57
-
58
- Always explain your reasoning and be thorough in your responses. If you need to use multiple tools to answer a question completely, do so.
59
-
60
- {self.instructions}
61
- """
62
- return system_message
63
-
64
-
65
- if __name__ == "__main__":
66
- import asyncio
67
69
 
70
+ async def main():
68
71
  agent = ReactAgent(
69
72
  "Universal React Agent",
70
- instructions="",
73
+ instructions="Be very concise in your answers.",
71
74
  model="azure/gpt-4o",
72
75
  tools={"google-mail": ["send_email"]},
73
76
  registry=AgentrRegistry(),
74
77
  )
75
- result = asyncio.run(
76
- agent.invoke(
77
- "Send an email with the subject 'testing react agent' to manoj@agentr.dev"
78
- )
78
+ result = await agent.invoke(
79
+ "Send an email with the subject 'testing react agent' to manoj@agentr.dev"
79
80
  )
80
- logger.info(result["messages"][-1].content)
81
+ from rich import print
82
+
83
+ print(messages_to_list(result["messages"]))
84
+
85
+
86
+ if __name__ == "__main__":
87
+ import asyncio
88
+
89
+ asyncio.run(main())
@@ -9,7 +9,6 @@ from langgraph.graph import END, StateGraph
9
9
  from langgraph.graph.message import add_messages
10
10
  from loguru import logger
11
11
  from pydantic import BaseModel, Field
12
-
13
12
  from universal_mcp.tools.registry import ToolRegistry
14
13
  from universal_mcp.types import ToolConfig
15
14
 
@@ -235,6 +234,7 @@ Task: "{task}"
235
234
 
236
235
  async def main():
237
236
  from universal_mcp.agentr.registry import AgentrRegistry
237
+
238
238
  from universal_mcp.agents.llm import load_chat_model
239
239
 
240
240
  registry = AgentrRegistry()
@@ -244,8 +244,7 @@ async def main():
244
244
  "task": "Send an email to manoj@agentr.dev",
245
245
  "messages": [HumanMessage(content="Send an email to manoj@agentr.dev")],
246
246
  }
247
- result = await graph.ainvoke(initial_state)
248
- print(result)
247
+ await graph.ainvoke(initial_state)
249
248
 
250
249
 
251
250
  if __name__ == "__main__":
@@ -8,6 +8,14 @@ from typing_extensions import TypedDict
8
8
 
9
9
  from universal_mcp.agents.base import BaseAgent
10
10
  from universal_mcp.agents.llm import load_chat_model
11
+ from universal_mcp.agents.utils import messages_to_list
12
+
13
+ DEVELOPER_PROMPT = """
14
+ You are {name}, an helpful assistant who can answer simple questions.
15
+
16
+ Adhere to the following instructions strictly:
17
+ {instructions}
18
+ """
11
19
 
12
20
 
13
21
  class State(TypedDict):
@@ -26,12 +34,15 @@ class SimpleAgent(BaseAgent):
26
34
  super().__init__(name, instructions, model, memory, **kwargs)
27
35
  self.llm = load_chat_model(model)
28
36
 
37
+ def _build_system_message(self):
38
+ return DEVELOPER_PROMPT.format(name=self.name, instructions=self.instructions)
39
+
29
40
  async def _build_graph(self):
30
41
  graph_builder = StateGraph(State)
31
42
 
32
43
  async def chatbot(state: State):
33
44
  messages = [
34
- {"role": "system", "content": self.instructions},
45
+ {"role": "system", "content": self._build_system_message()},
35
46
  *state["messages"],
36
47
  ]
37
48
  return {"messages": [await self.llm.ainvoke(messages)]}
@@ -41,7 +52,12 @@ class SimpleAgent(BaseAgent):
41
52
  graph_builder.add_edge("chatbot", END)
42
53
  return graph_builder.compile(checkpointer=self.memory)
43
54
 
55
+ async def main():
56
+ agent = SimpleAgent("Simple Agent", "Act as a 14 year old kid, reply in Gen-Z lingo", "azure/gpt-5-mini")
57
+ output = await agent.invoke("What is the capital of France?")
58
+ from rich import print
59
+ print(messages_to_list(output["messages"]))
60
+
44
61
 
45
62
  if __name__ == "__main__":
46
- agent = SimpleAgent("Simple Agent", "You are a helpful assistant", "azure/gpt-4o")
47
- asyncio.run(agent.run_interactive())
63
+ asyncio.run(main())
@@ -1,12 +1,16 @@
1
1
  import json
2
2
  from contextlib import contextmanager
3
3
 
4
+ from langchain_core.messages.base import BaseMessage
4
5
  from rich.console import Console
5
6
  from rich.live import Live
6
7
  from rich.markdown import Markdown
7
8
  from rich.panel import Panel
8
9
  from rich.prompt import Prompt
9
10
  from rich.table import Table
11
+ from universal_mcp.tools.manager import ToolManager
12
+ from universal_mcp.types import ToolFormat
13
+
10
14
 
11
15
 
12
16
  class RichCLI:
@@ -30,14 +34,8 @@ Available commands:
30
34
 
31
35
  def display_agent_response(self, response: str, agent_name: str):
32
36
  """Display agent response with formatting"""
33
- self.console.print(
34
- Panel(
35
- Markdown(response),
36
- title=f"🤖 {agent_name}",
37
- border_style="green",
38
- padding=(1, 2),
39
- )
40
- )
37
+ self.console.print(f"[green]🤖 {agent_name}:[/green]")
38
+ self.console.print(Markdown(response), style="green")
41
39
 
42
40
  @contextmanager
43
41
  def display_agent_response_streaming(self, agent_name: str):
@@ -46,28 +44,35 @@ Available commands:
46
44
  with Live(refresh_per_second=10, console=self.console) as live:
47
45
 
48
46
  class StreamUpdater:
47
+ type_ = ""
49
48
  content = []
50
49
 
51
- def update(self, chunk: str):
52
- self.content.append(chunk)
53
- panel = Panel(
54
- Markdown("".join(self.content)),
55
- title=f"🤖 {agent_name}",
56
- border_style="green",
57
- padding=(1, 2),
58
- )
59
- live.update(panel)
50
+ def update(self, chunk: str, type_: str):
51
+ if not chunk:
52
+ return
53
+
54
+ # Check if type has changed and reset content if so
55
+ if self.type_ != type_:
56
+ if type_ == "thinking":
57
+ self.content += (
58
+ "\n[bold yellow]💭 Thinking:[/bold yellow] :"
59
+ )
60
+ elif type_ == "text":
61
+ self.content += (
62
+ f"\n[bold green]🤖 {agent_name}[/bold green] :"
63
+ )
64
+ self.type_ = type_
65
+ self.content += chunk
66
+ content_text = "".join(self.content)
67
+ live.update(content_text)
60
68
 
61
69
  yield StreamUpdater()
62
70
 
63
71
  def display_thinking(self, thought: str):
64
72
  """Display agent's thinking process"""
65
73
  if thought:
66
- self.console.print(
67
- Panel(
68
- thought, title="💭 Thinking", border_style="yellow", padding=(1, 2)
69
- )
70
- )
74
+ self.console.print("[bold yellow]💭 Thinking:[/bold yellow]")
75
+ self.console.print(thought, style="yellow")
71
76
 
72
77
  def display_tools(self, tools: list):
73
78
  """Display available tools in a table"""
@@ -84,27 +89,18 @@ Available commands:
84
89
  def display_tool_call(self, tool_call: dict):
85
90
  """Display tool call"""
86
91
  tool_call_str = json.dumps(tool_call, indent=2)
87
- self.console.print(
88
- Panel(
89
- tool_call_str, title="🛠️ Tool Call", border_style="green", padding=(1, 2)
90
- )
91
- )
92
+ self.console.print("[green]🛠️ Tool Call:[/green]")
93
+ self.console.print(tool_call_str, style="green")
92
94
 
93
95
  def display_tool_result(self, tool_result: dict):
94
96
  """Display tool result"""
95
97
  tool_result_str = json.dumps(tool_result, indent=2)
96
- self.console.print(
97
- Panel(
98
- tool_result_str,
99
- title="🛠️ Tool Result",
100
- border_style="green",
101
- padding=(1, 2),
102
- )
103
- )
98
+ self.console.print("[green]🛠️ Tool Result:[/green]")
99
+ self.console.print(tool_result_str, style="green")
104
100
 
105
101
  def display_error(self, error: str):
106
102
  """Display error message"""
107
- self.console.print(Panel(error, title="❌ Error", border_style="red"))
103
+ self.console.print(f"[red]❌ Error: {error}[/red]")
108
104
 
109
105
  def get_user_input(self) -> str:
110
106
  """Get user input with rich prompt"""
@@ -137,3 +133,7 @@ Available commands:
137
133
  return value
138
134
  else:
139
135
  raise ValueError(f"Invalid interrupt type: {interrupt.value['type']}")
136
+
137
+
138
+ def messages_to_list(messages: list[BaseMessage]):
139
+ return [{"type": message.type, "content": message.content} for message in messages]