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.
- universal_mcp/agents/__init__.py +19 -0
- universal_mcp/agents/autoagent/__init__.py +1 -1
- universal_mcp/agents/autoagent/__main__.py +1 -1
- universal_mcp/agents/autoagent/graph.py +2 -2
- universal_mcp/agents/base.py +24 -13
- universal_mcp/agents/bigtool/__init__.py +10 -8
- universal_mcp/agents/bigtool/__main__.py +6 -7
- universal_mcp/agents/bigtool/graph.py +18 -27
- universal_mcp/agents/bigtool/prompts.py +3 -3
- universal_mcp/agents/bigtool2/__init__.py +16 -3
- universal_mcp/agents/bigtool2/__main__.py +6 -5
- universal_mcp/agents/bigtool2/agent.py +1 -1
- universal_mcp/agents/bigtool2/graph.py +55 -20
- universal_mcp/agents/bigtool2/prompts.py +8 -5
- universal_mcp/agents/bigtoolcache/agent.py +2 -2
- universal_mcp/agents/bigtoolcache/graph.py +5 -6
- universal_mcp/agents/bigtoolcache/prompts.py +1 -2
- universal_mcp/agents/builder.py +47 -14
- universal_mcp/agents/cli.py +19 -5
- universal_mcp/agents/codeact/test.py +2 -1
- universal_mcp/agents/llm.py +7 -3
- universal_mcp/agents/planner/__init__.py +8 -2
- universal_mcp/agents/planner/__main__.py +10 -8
- universal_mcp/agents/planner/graph.py +6 -2
- universal_mcp/agents/planner/prompts.py +14 -1
- universal_mcp/agents/planner/state.py +0 -1
- universal_mcp/agents/react.py +36 -27
- universal_mcp/agents/shared/tool_node.py +2 -3
- universal_mcp/agents/simple.py +19 -3
- universal_mcp/agents/utils.py +36 -36
- universal_mcp/applications/ui/app.py +305 -0
- {universal_mcp_agents-0.1.4.dist-info → universal_mcp_agents-0.1.6.dist-info}/METADATA +2 -2
- universal_mcp_agents-0.1.6.dist-info/RECORD +50 -0
- universal_mcp/agents/autoagent/studio.py +0 -19
- universal_mcp/agents/bigtool/context.py +0 -24
- universal_mcp/agents/bigtool2/context.py +0 -32
- universal_mcp/agents/tools.py +0 -41
- universal_mcp_agents-0.1.4.dist-info/RECORD +0 -53
- {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 '{
|
|
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=
|
|
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,
|
|
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."
|
universal_mcp/agents/builder.py
CHANGED
|
@@ -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.
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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__":
|
universal_mcp/agents/cli.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from langgraph.checkpoint.memory import MemorySaver
|
|
1
2
|
from typer import Typer
|
|
2
|
-
|
|
3
|
-
from universal_mcp.
|
|
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
|
-
|
|
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
|
|
universal_mcp/agents/llm.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
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="
|
|
14
|
+
model="azure/gpt-4o",
|
|
13
15
|
registry=registry,
|
|
14
16
|
)
|
|
15
|
-
from rich
|
|
17
|
+
from rich import print
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
+
"""
|
universal_mcp/agents/react.py
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
76
|
-
agent.
|
|
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
|
-
|
|
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
|
-
|
|
248
|
-
print(result)
|
|
247
|
+
await graph.ainvoke(initial_state)
|
|
249
248
|
|
|
250
249
|
|
|
251
250
|
if __name__ == "__main__":
|
universal_mcp/agents/simple.py
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
47
|
-
asyncio.run(agent.run_interactive())
|
|
63
|
+
asyncio.run(main())
|
universal_mcp/agents/utils.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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]
|