universal-mcp-agents 0.1.19rc1__py3-none-any.whl → 0.1.20rc1__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 (35) hide show
  1. universal_mcp/agents/__init__.py +5 -9
  2. universal_mcp/agents/base.py +4 -1
  3. universal_mcp/agents/cli.py +0 -3
  4. universal_mcp/agents/codeact0/__init__.py +2 -3
  5. universal_mcp/agents/codeact0/__main__.py +2 -2
  6. universal_mcp/agents/codeact0/agent.py +231 -83
  7. universal_mcp/agents/codeact0/langgraph_agent.py +1 -1
  8. universal_mcp/agents/codeact0/prompts.py +38 -5
  9. universal_mcp/agents/codeact0/sandbox.py +31 -1
  10. universal_mcp/agents/codeact0/state.py +3 -1
  11. universal_mcp/agents/codeact0/tools.py +200 -85
  12. {universal_mcp_agents-0.1.19rc1.dist-info → universal_mcp_agents-0.1.20rc1.dist-info}/METADATA +1 -1
  13. universal_mcp_agents-0.1.20rc1.dist-info/RECORD +44 -0
  14. universal_mcp/agents/codeact/__init__.py +0 -3
  15. universal_mcp/agents/codeact/__main__.py +0 -33
  16. universal_mcp/agents/codeact/agent.py +0 -240
  17. universal_mcp/agents/codeact/models.py +0 -11
  18. universal_mcp/agents/codeact/prompts.py +0 -82
  19. universal_mcp/agents/codeact/sandbox.py +0 -85
  20. universal_mcp/agents/codeact/state.py +0 -11
  21. universal_mcp/agents/codeact/utils.py +0 -68
  22. universal_mcp/agents/codeact0/playbook_agent.py +0 -355
  23. universal_mcp/agents/unified/README.md +0 -45
  24. universal_mcp/agents/unified/__init__.py +0 -3
  25. universal_mcp/agents/unified/__main__.py +0 -28
  26. universal_mcp/agents/unified/agent.py +0 -289
  27. universal_mcp/agents/unified/langgraph_agent.py +0 -14
  28. universal_mcp/agents/unified/llm_tool.py +0 -25
  29. universal_mcp/agents/unified/prompts.py +0 -192
  30. universal_mcp/agents/unified/sandbox.py +0 -101
  31. universal_mcp/agents/unified/state.py +0 -42
  32. universal_mcp/agents/unified/tools.py +0 -188
  33. universal_mcp/agents/unified/utils.py +0 -388
  34. universal_mcp_agents-0.1.19rc1.dist-info/RECORD +0 -64
  35. {universal_mcp_agents-0.1.19rc1.dist-info → universal_mcp_agents-0.1.20rc1.dist-info}/WHEEL +0 -0
@@ -1,13 +1,13 @@
1
1
  import asyncio
2
+ import json
2
3
  from collections import defaultdict
3
- from typing import Any
4
+ from typing import Annotated, Any
4
5
 
5
6
  from langchain_core.tools import tool
6
- from universal_mcp.tools.registry import ToolRegistry
7
+ from pydantic import Field
8
+ from universal_mcp.agentr.registry import AgentrRegistry
7
9
  from universal_mcp.types import ToolFormat
8
10
 
9
- MAX_LENGHT = 100
10
-
11
11
 
12
12
  def enter_playbook_mode():
13
13
  """Call this function to enter playbook mode. Playbook mode is when the user wants to store a repeated task as a script with some inputs for the future."""
@@ -19,77 +19,180 @@ def exit_playbook_mode():
19
19
  return
20
20
 
21
21
 
22
- def create_meta_tools(tool_registry: ToolRegistry) -> dict[str, Any]:
22
+ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
23
23
  """Create the meta tools for searching and loading tools"""
24
24
 
25
25
  @tool
26
- async def search_functions(queries: list[str]) -> str:
27
- """Search for relevant functions given list of queries.
28
- Each single query should be atomic (doable with a single function).
29
- For tasks requiring multiple functions, add separate queries for each subtask"""
30
- try:
31
- # Fetch all connections
32
- connections = await tool_registry.list_connected_apps()
33
- connected_apps = {connection["app_id"] for connection in connections}
34
-
35
- app_tools = defaultdict(set)
36
- MAX_LENGTH = 20
37
-
38
- # Process all queries concurrently
39
- search_tasks = []
40
- for query in queries:
41
- search_tasks.append(_search_query_tools(query))
42
-
43
- query_results = await asyncio.gather(*search_tasks)
44
-
45
- # Aggregate results with limit per app and automatic deduplication
46
- for tools_list in query_results:
47
- for tool in tools_list:
48
- app = tool["id"].split("__")[0]
49
- tool_id = tool["id"]
50
-
51
- # Check if within limit and add to set (automatically deduplicates)
52
- if len(app_tools[app]) < MAX_LENGTH:
53
- cleaned_desc = tool["description"].split("Context:")[0].strip()
54
- app_tools[app].add(f"{tool_id}: {cleaned_desc}")
55
-
56
- # Build result string efficiently
57
- result_parts = []
58
- for app, tools in app_tools.items():
59
- app_status = "connected" if app in connected_apps else "NOT connected"
60
- result_parts.append(f"Tools from {app} (status: {app_status} by user):")
61
- # Convert set to sorted list for consistent output
62
- for tool in sorted(tools):
63
- result_parts.append(f" - {tool}")
64
- result_parts.append("") # Empty line between apps
65
-
66
- result_parts.append("Call load_functions to select the required functions only.")
67
- return "\n".join(result_parts)
68
-
69
- except Exception as e:
70
- return f"Error: {e}"
71
-
72
- async def _search_query_tools(query: str) -> list[dict]:
73
- """Helper function to search apps and tools for a single query."""
74
- # Start both searches concurrently
75
- tools_search_task = tool_registry.search_tools(query, limit=10)
76
- apps_search_task = tool_registry.search_apps(query, limit=4)
77
-
78
- # Wait for both to complete
79
- tools_from_general_search, apps_list = await asyncio.gather(tools_search_task, apps_search_task)
80
-
81
- # Create tasks for searching tools from each app
82
- app_tool_tasks = [tool_registry.search_tools(query, limit=5, app_id=app["id"]) for app in apps_list]
83
-
84
- # Wait for all app-specific tool searches to complete
85
- app_tools_results = await asyncio.gather(*app_tool_tasks)
86
-
87
- # Combine all results
88
- tools_list = list(tools_from_general_search)
89
- for app_tools in app_tools_results:
90
- tools_list.extend(app_tools)
91
-
92
- return tools_list
26
+ async def search_functions(
27
+ queries: Annotated[
28
+ list[str] | str | None,
29
+ Field(description="A single query or a list of queries to search for relevant functions"),
30
+ ] = None,
31
+ app_id: Annotated[
32
+ str | None,
33
+ Field(description="The ID or common name of a specific application to search within"),
34
+ ] = None,
35
+ ) -> str:
36
+ """
37
+ Searches for relevant functions across applications based on queries and/or a specific app.
38
+ This function operates in three modes:
39
+
40
+ 1. **Global Search (provide `queries` only):**
41
+ - Use when the user wants to perform an action without specifying an application.
42
+ - The system will search across all available functions.
43
+ - Example: For "how can I create a presentation?", call with queries=["create presentation"].
44
+
45
+ 2. **App Discovery (provide `app_id` only):**
46
+ - Use when the user asks about the capabilities of a specific application.
47
+ - The `app_id` can be the common name of the app (e.g., "Gmail", "Google Drive").
48
+ - This will return all available functions for that application, up to a limit.
49
+ - Example: For "what can you do with Gmail?", call with app_id="Gmail".
50
+
51
+ 3. **Scoped Search (provide `queries` AND `app_id`):**
52
+ - Use when the user wants to perform an action within a specific application.
53
+ - This performs a targeted search only within the specified app's functions.
54
+ - Example: For "how do I find an email in Gmail?", call with queries=["find email"], app_id="Gmail".
55
+ """
56
+ if isinstance(queries, str): # Handle JSON string input
57
+ try:
58
+ queries = json.loads(queries)
59
+ except json.JSONDecodeError:
60
+ # If it's a single query as a string, convert to list
61
+ queries = [queries] if queries else None
62
+
63
+ if not queries and not app_id:
64
+ raise ValueError("You must provide 'queries', an 'app_id', or both.")
65
+
66
+ registry = tool_registry
67
+ connections = await registry.list_connected_apps()
68
+ connected_app_ids = {connection["app_id"] for connection in connections}
69
+
70
+ canonical_app_id = None
71
+ found_tools_result = []
72
+
73
+ if app_id:
74
+ relevant_apps = await registry.search_apps(query=app_id, distance_threshold=0.7)
75
+ if not relevant_apps:
76
+ return {
77
+ "found_tools": [],
78
+ "message": f"Search failed. Application '{app_id}' was not found.",
79
+ }
80
+ canonical_app_id = relevant_apps[0]["id"]
81
+
82
+ if canonical_app_id and not queries:
83
+ all_app_tools = await registry.search_tools(query="", app_id=canonical_app_id, limit=20)
84
+
85
+ tool_list = []
86
+ for tool in all_app_tools:
87
+ cleaned_description = tool.get("description", "").split("Context:")[0].strip()
88
+ tool_list.append({"id": tool["id"], "description": cleaned_description})
89
+
90
+ found_tools_result.append(
91
+ {
92
+ "app_id": canonical_app_id,
93
+ "connection_status": "connected" if canonical_app_id in connected_app_ids else "not_connected",
94
+ "tools": tool_list,
95
+ }
96
+ )
97
+
98
+ else:
99
+ query_results = []
100
+ prioritized_app_id_list = []
101
+
102
+ if canonical_app_id:
103
+ prioritized_app_id_list = [canonical_app_id]
104
+ else:
105
+ # 1. Perform an initial broad search for tools.
106
+ initial_tool_search_tasks = [registry.search_tools(query=q, distance_threshold=0.7) for q in queries]
107
+ initial_tool_results = await asyncio.gather(*initial_tool_search_tasks)
108
+
109
+ # 2. Search for relevant apps.
110
+ app_search_tasks = [registry.search_apps(query=q, distance_threshold=0.7) for q in queries]
111
+ app_search_results = await asyncio.gather(*app_search_tasks)
112
+
113
+ # 3. Create a prioritized list of app IDs for the final search.
114
+ # Apps found via search_apps are considered higher priority and come first.
115
+ app_ids_from_apps = {app["id"] for result_list in app_search_results for app in result_list}
116
+ # Use a list to maintain order.
117
+ prioritized_app_id_list.extend(list(app_ids_from_apps))
118
+
119
+ # Add app_ids from the initial tool search, ensuring no duplicates.
120
+ app_ids_from_tools = {tool["app_id"] for result_list in initial_tool_results for tool in result_list}
121
+
122
+ for tool_app_id in app_ids_from_tools:
123
+ if tool_app_id not in app_ids_from_apps:
124
+ prioritized_app_id_list.append(tool_app_id)
125
+
126
+ # 4. Perform the final, comprehensive tool search across the prioritized list of apps.
127
+ if prioritized_app_id_list:
128
+ # print(f"Prioritized app IDs for final search: {prioritized_app_id_list}")
129
+ final_tool_search_tasks = []
130
+ for app_id_to_search in prioritized_app_id_list:
131
+ for query in queries:
132
+ final_tool_search_tasks.append(
133
+ registry.search_tools(query=query, app_id=app_id_to_search, distance_threshold=0.7)
134
+ )
135
+ query_results = await asyncio.gather(*final_tool_search_tasks)
136
+
137
+ # 5. Aggregate all found tools for easy lookup.
138
+ aggregated_tools = defaultdict(dict)
139
+ for tool_list in query_results:
140
+ for tool in tool_list:
141
+ app_id_from_tool = tool.get("app_id", "unknown")
142
+ tool_id = tool.get("id")
143
+ if not tool_id or tool_id in aggregated_tools[app_id_from_tool]:
144
+ continue
145
+ cleaned_description = tool.get("description", "").split("Context:")[0].strip()
146
+ aggregated_tools[app_id_from_tool][tool_id] = {
147
+ "id": tool_id,
148
+ "description": cleaned_description,
149
+ }
150
+
151
+ # 6. Build the final results list, respecting the prioritized app order.
152
+ for app_id_from_list in prioritized_app_id_list:
153
+ if app_id_from_list in aggregated_tools and aggregated_tools[app_id_from_list]:
154
+ found_tools_result.append(
155
+ {
156
+ "app_id": app_id_from_list,
157
+ "connection_status": "connected"
158
+ if app_id_from_list in connected_app_ids
159
+ else "not_connected",
160
+ "tools": list(aggregated_tools[app_id_from_list].values()),
161
+ }
162
+ )
163
+
164
+ # Build result string efficiently
165
+ result_parts = []
166
+ apps_in_results = {app["app_id"] for app in found_tools_result}
167
+ connected_apps_in_results = apps_in_results.intersection(connected_app_ids)
168
+
169
+ for app in found_tools_result:
170
+ app_id = app["app_id"]
171
+ connection_status = app["connection_status"]
172
+ tools = app["tools"]
173
+
174
+ app_status = "connected" if connection_status == "connected" else "NOT connected"
175
+ result_parts.append(f"Tools from {app_id} (status: {app_status} by user):")
176
+
177
+ for tool in tools:
178
+ tool_id = tool["id"]
179
+ description = tool["description"]
180
+ result_parts.append(f" - {tool_id}: {description}")
181
+ result_parts.append("") # Empty line between apps
182
+
183
+ # Add connection status information
184
+ if len(connected_apps_in_results) == 0 and len(apps_in_results) > 0:
185
+ result_parts.append(
186
+ "Connection Status: None of the apps in the results are connected. You must ask the user to choose the application."
187
+ )
188
+ elif len(connected_apps_in_results) > 1:
189
+ connected_list = ", ".join(connected_apps_in_results)
190
+ result_parts.append(
191
+ f"Connection Status: Multiple apps are connected ({connected_list}). You must ask the user to select which application they want to use."
192
+ )
193
+
194
+ result_parts.append("Call load_functions to select the required functions only.")
195
+ return "\n".join(result_parts)
93
196
 
94
197
  @tool
95
198
  async def load_functions(tool_ids: list[str]) -> str:
@@ -105,34 +208,46 @@ def create_meta_tools(tool_registry: ToolRegistry) -> dict[str, Any]:
105
208
 
106
209
  @tool
107
210
  async def web_search(query: str) -> dict:
108
- """
109
- Get an LLM answer to a question informed by Exa search results.
211
+ """Get an LLM answer to a question informed by Perplexity web search results.
212
+ Useful when you need information from a wide range of real-time sources on the web.
213
+ Do not use this when you need to access contents of a specific webpage.
110
214
 
111
- This tool performs an Exa `/answer` request, which:
112
- 1. Provides a **direct answer** for factual queries (e.g., "What is the capital of France?" → "Paris")
215
+ This tool performs a Perplexity request via `perplexity__answer_with_search`, which:
216
+ 1. Provides a **direct answer** for factual queries with citation(s)
113
217
  2. Generates a **summary with citations** for open-ended questions
114
- (e.g., "What is the state of AI in healthcare?" → A detailed summary with source links)
115
218
 
116
219
  Args:
117
220
  query (str): The question or topic to answer.
221
+
118
222
  Returns:
119
- dict: A structured response containing only:
120
- - answer (str): Generated answer
121
- - citations (list[dict]): List of cited sources
223
+ dict: A structured response containing:
224
+ - answer (str): Generated answer with markdown formatting and citation numbers [1][2]
225
+ - citations (list[str]): List of source URLs corresponding to citation numbers
122
226
  """
123
- await tool_registry.export_tools(["exa__answer"], ToolFormat.LANGCHAIN)
124
- response = await tool_registry.call_tool("exa__answer", {"query": query, "text": True})
227
+ await tool_registry.export_tools(["perplexity__answer_with_search"], ToolFormat.LANGCHAIN)
228
+
229
+ response = await tool_registry.call_tool(
230
+ "perplexity__answer_with_search",
231
+ {
232
+ "query": query,
233
+ "model": "sonar",
234
+ "temperature": 1.0,
235
+ "system_prompt": (
236
+ "You are a helpful AI assistant that answers questions using real-time information from the web."
237
+ ),
238
+ },
239
+ )
125
240
 
126
241
  # Extract only desired fields
127
242
  return {
128
- "answer": response.get("answer"),
243
+ "answer": response.get("content"),
129
244
  "citations": response.get("citations", []),
130
245
  }
131
246
 
132
247
  return {"search_functions": search_functions, "load_functions": load_functions, "web_search": web_search}
133
248
 
134
249
 
135
- async def get_valid_tools(tool_ids: list[str], registry: ToolRegistry) -> tuple[list[str], list[str]]:
250
+ async def get_valid_tools(tool_ids: list[str], registry: AgentrRegistry) -> tuple[list[str], list[str]]:
136
251
  """For a given list of tool_ids, validates the tools and returns a list of links for the apps that have not been logged in"""
137
252
  correct, incorrect = [], []
138
253
  connections = await registry.list_connected_apps()
@@ -173,7 +288,7 @@ async def get_valid_tools(tool_ids: list[str], registry: ToolRegistry) -> tuple[
173
288
  continue
174
289
  if app not in connected_apps and app not in unconnected:
175
290
  unconnected.add(app)
176
- text = registry.client.get_authorization_url(app)
291
+ text = registry.authorise_app(app_id=app)
177
292
  start = text.find(":") + 1
178
293
  end = text.find(". R", start)
179
294
  url = text[start:end].strip()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp-agents
3
- Version: 0.1.19rc1
3
+ Version: 0.1.20rc1
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,44 @@
1
+ universal_mcp/agents/__init__.py,sha256=Rh6vKqpwuZ2joC9nzLFHUI1G7jzbwwC3p0mgpH5qRgo,1112
2
+ universal_mcp/agents/base.py,sha256=wkqa_W2R6sFgjTMXn2bWVfjarHYDsLvGbG8nN-jRp6E,7175
3
+ universal_mcp/agents/cli.py,sha256=bXdpgxsOMjclm1STHJgx10ocX9EebQ11DrxH0p6KMZk,943
4
+ universal_mcp/agents/hil.py,sha256=_5PCK6q0goGm8qylJq44aSp2MadP-yCPvhOJYKqWLMo,3808
5
+ universal_mcp/agents/llm.py,sha256=hVRwjZs3MHl5_3BWedmurs2Jt1oZDfFX0Zj9F8KH7fk,1787
6
+ universal_mcp/agents/react.py,sha256=8XQvJ0HLVgc-K0qn9Ml48WGcgUGuIKtL67HatlT6Da0,3334
7
+ universal_mcp/agents/sandbox.py,sha256=Int2O8JNFPlB8c7gb86KRxlNbuV0zdz5_NCo_GMcCds,2876
8
+ universal_mcp/agents/simple.py,sha256=NSATg5TWzsRNS7V3LFiDG28WSOCIwCdcC1g7NRwg2nM,2095
9
+ universal_mcp/agents/utils.py,sha256=P6W9k6XAOBp6tdjC2VTP4tE0B2M4-b1EDmr-ylJ47Pw,7765
10
+ universal_mcp/agents/bigtool/__init__.py,sha256=mZG8dsaCVyKlm82otxtiTA225GIFLUCUUYPEIPF24uw,2299
11
+ universal_mcp/agents/bigtool/__main__.py,sha256=0i-fbd2yQ90qa8n2nM3luqoJVN9Reh5HZXR5oK7SAck,445
12
+ universal_mcp/agents/bigtool/agent.py,sha256=mtCDNN8WjE2hjJjooDqusmbferKBHeJMHrhXUPUWaVc,252
13
+ universal_mcp/agents/bigtool/context.py,sha256=ny7gd-vvVpUOYAeQbAEUT0A6Vm6Nn2qGywxTzPBzYFg,929
14
+ universal_mcp/agents/bigtool/graph.py,sha256=2Sy0dtevTWeT3hJDq4BDerZFvk_zJqx15j8VH2XLq8Y,5848
15
+ universal_mcp/agents/bigtool/prompts.py,sha256=Joi5mCzZX63aM_6eBrMOKuNRHjTkceVIibSsGBGqhYE,2041
16
+ universal_mcp/agents/bigtool/state.py,sha256=TQeGZD99okclkoCh5oz-VYIlEsC9yLQyDpnBnm7QCN8,759
17
+ universal_mcp/agents/bigtool/tools.py,sha256=-u80ta6xEaqzEMSzDVe3QZiTZm3YlgLkBD8WTghzClw,6315
18
+ universal_mcp/agents/builder/__main__.py,sha256=VJDJOr-dJJerT53ibh5LVqIsMJ0m0sG2UlzFB784pKw,11680
19
+ universal_mcp/agents/builder/builder.py,sha256=mh3MZpMVB1FE1DWzvMW9NnfiaF145VGn8cJzKSYUlzY,8587
20
+ universal_mcp/agents/builder/helper.py,sha256=8igR1b3Gy_N2u3WxHYKIWzvw7F5BMnfpO2IU74v6vsw,2680
21
+ universal_mcp/agents/builder/prompts.py,sha256=8Xs6uzTUHguDRngVMLak3lkXFkk2VV_uQXaDllzP5cI,4670
22
+ universal_mcp/agents/builder/state.py,sha256=7DeWllxfN-yD6cd9wJ3KIgjO8TctkJvVjAbZT8W_zqk,922
23
+ universal_mcp/agents/codeact0/__init__.py,sha256=8-fvUo1Sm6dURGI-lW-X3Kd78LqySYbb5NMkNJ4NDwg,76
24
+ universal_mcp/agents/codeact0/__main__.py,sha256=_7qSz97YnRgYJTESkALS5_eBIGHiMjA5rhr3IAeBvVo,896
25
+ universal_mcp/agents/codeact0/agent.py,sha256=qUcNniw8DyvEfpEuaWA9LDqDe0eiRPEHrZtysfdKY8k,14238
26
+ universal_mcp/agents/codeact0/config.py,sha256=H-1woj_nhSDwf15F63WYn723y4qlRefXzGxuH81uYF0,2215
27
+ universal_mcp/agents/codeact0/langgraph_agent.py,sha256=8nz2wq-LexImx-l1y9_f81fK72IQetnCeljwgnduNGY,420
28
+ universal_mcp/agents/codeact0/llm_tool.py,sha256=q-hiqkKtjVmpyNceFoRgo7hvKh4HtQf_I1VudRUEPR0,11075
29
+ universal_mcp/agents/codeact0/prompts.py,sha256=IZwiGLYISr_oXeNwhPJLNpE5wYd5zyntEdEUGH21OD8,10720
30
+ universal_mcp/agents/codeact0/sandbox.py,sha256=Xw4tbUV_6haYIZZvteJi6lIYsW6ni_3DCRCOkslTKgM,4459
31
+ universal_mcp/agents/codeact0/state.py,sha256=B1XF5ni9RvN6p5POHkPTm-KjEx5c8wRB7O1EJM8pbSQ,1297
32
+ universal_mcp/agents/codeact0/tools.py,sha256=G580vy-m_BqlHc_j0yvLV5T_2d2G0JrD_eDhvDdJ9JA,13614
33
+ universal_mcp/agents/codeact0/utils.py,sha256=jAZItSd3KGDkY9PquSWRIFCj9N26K9Kt0HKQ_jwvvSQ,15944
34
+ universal_mcp/agents/shared/__main__.py,sha256=XxH5qGDpgFWfq7fwQfgKULXGiUgeTp_YKfcxftuVZq8,1452
35
+ universal_mcp/agents/shared/prompts.py,sha256=yjP3zbbuKi87qCj21qwTTicz8TqtkKgnyGSeEjMu3ho,3761
36
+ universal_mcp/agents/shared/tool_node.py,sha256=DC9F-Ri28Pam0u3sXWNODVgmj9PtAEUb5qP1qOoGgfs,9169
37
+ universal_mcp/applications/filesystem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ universal_mcp/applications/filesystem/app.py,sha256=0TRjjm8YnslVRSmfkXI7qQOAlqWlD1eEn8Jm0xBeigs,5561
39
+ universal_mcp/applications/llm/__init__.py,sha256=_XGRxN3O1--ZS5joAsPf8IlI9Qa6negsJrwJ5VJXno0,46
40
+ universal_mcp/applications/llm/app.py,sha256=oqX3byvlFRmeRo4jJJxUBGy-iTDGm2fplMEKA2pcMtw,12743
41
+ universal_mcp/applications/ui/app.py,sha256=c7OkZsO2fRtndgAzAQbKu-1xXRuRp9Kjgml57YD2NR4,9459
42
+ universal_mcp_agents-0.1.20rc1.dist-info/METADATA,sha256=49lAde7iqPxxgwPAb7BZCERlIkajP0EoiiuvtA7hy24,881
43
+ universal_mcp_agents-0.1.20rc1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
+ universal_mcp_agents-0.1.20rc1.dist-info/RECORD,,
@@ -1,3 +0,0 @@
1
- from .agent import CodeActAgent
2
-
3
- __all__ = ["CodeActAgent"]
@@ -1,33 +0,0 @@
1
- import asyncio
2
-
3
- from langgraph.checkpoint.memory import MemorySaver
4
- from rich import print
5
- from universal_mcp.agentr.registry import AgentrRegistry
6
-
7
- from universal_mcp.agents.codeact.agent import CodeActAgent
8
- from universal_mcp.agents.utils import messages_to_list
9
-
10
-
11
- async def main():
12
- memory = MemorySaver()
13
- agent = CodeActAgent(
14
- name="CodeAct Agent",
15
- instructions="Be very concise in your answers.",
16
- model="anthropic:claude-4-sonnet-20250514",
17
- tools={"google_mail": ["list_messages"]},
18
- registry=AgentrRegistry(),
19
- memory=memory,
20
- )
21
- print("Starting agent...")
22
- # await agent.ainit()
23
- # await agent.run_interactive()
24
- # async for event in agent.stream(
25
- # user_input="Fetch unsubscribe links from my Gmail inbox for promo emails I have received in the last 7 days"
26
- # ):
27
- # print(event.content, end="")
28
- result = await agent.invoke(user_input="Get the 50th fibonacci number")
29
- print(messages_to_list(result["messages"]))
30
-
31
-
32
- if __name__ == "__main__":
33
- asyncio.run(main())