universal-mcp-agents 0.1.10__py3-none-any.whl → 0.1.12__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.
Files changed (65) hide show
  1. universal_mcp/agents/__init__.py +17 -19
  2. universal_mcp/agents/base.py +10 -7
  3. universal_mcp/agents/{bigtoolcache → bigtool}/__init__.py +2 -2
  4. universal_mcp/agents/{bigtoolcache → bigtool}/__main__.py +0 -1
  5. universal_mcp/agents/{bigtoolcache → bigtool}/agent.py +0 -1
  6. universal_mcp/agents/{bigtoolcache → bigtool}/graph.py +6 -5
  7. universal_mcp/agents/builder/__main__.py +125 -0
  8. universal_mcp/agents/builder/builder.py +225 -0
  9. universal_mcp/agents/builder/prompts.py +173 -0
  10. universal_mcp/agents/builder/state.py +24 -0
  11. universal_mcp/agents/cli.py +3 -2
  12. universal_mcp/agents/codeact/__main__.py +2 -4
  13. universal_mcp/agents/codeact/agent.py +188 -108
  14. universal_mcp/agents/codeact/models.py +11 -0
  15. universal_mcp/agents/codeact/prompts.py +34 -43
  16. universal_mcp/agents/codeact/sandbox.py +78 -40
  17. universal_mcp/agents/codeact/state.py +5 -4
  18. universal_mcp/agents/codeact0/__init__.py +3 -0
  19. universal_mcp/agents/codeact0/__main__.py +35 -0
  20. universal_mcp/agents/codeact0/agent.py +136 -0
  21. universal_mcp/agents/codeact0/config.py +77 -0
  22. universal_mcp/agents/codeact0/langgraph_graph.py +17 -0
  23. universal_mcp/agents/codeact0/legacy_codeact.py +104 -0
  24. universal_mcp/agents/codeact0/llm_tool.py +379 -0
  25. universal_mcp/agents/codeact0/prompts.py +156 -0
  26. universal_mcp/agents/codeact0/sandbox.py +90 -0
  27. universal_mcp/agents/codeact0/state.py +12 -0
  28. universal_mcp/agents/codeact0/usecases/1-unsubscribe.yaml +4 -0
  29. universal_mcp/agents/codeact0/usecases/10-reddit2.yaml +10 -0
  30. universal_mcp/agents/codeact0/usecases/11-github.yaml +13 -0
  31. universal_mcp/agents/codeact0/usecases/2-reddit.yaml +27 -0
  32. universal_mcp/agents/codeact0/usecases/2.1-instructions.md +81 -0
  33. universal_mcp/agents/codeact0/usecases/2.2-instructions.md +71 -0
  34. universal_mcp/agents/codeact0/usecases/3-earnings.yaml +4 -0
  35. universal_mcp/agents/codeact0/usecases/4-maps.yaml +41 -0
  36. universal_mcp/agents/codeact0/usecases/5-gmailreply.yaml +8 -0
  37. universal_mcp/agents/codeact0/usecases/6-contract.yaml +6 -0
  38. universal_mcp/agents/codeact0/usecases/7-overnight.yaml +14 -0
  39. universal_mcp/agents/codeact0/usecases/8-sheets_chart.yaml +25 -0
  40. universal_mcp/agents/codeact0/usecases/9-learning.yaml +9 -0
  41. universal_mcp/agents/codeact0/utils.py +374 -0
  42. universal_mcp/agents/hil.py +4 -4
  43. universal_mcp/agents/planner/__init__.py +7 -1
  44. universal_mcp/agents/react.py +11 -3
  45. universal_mcp/agents/simple.py +12 -2
  46. universal_mcp/agents/utils.py +17 -0
  47. universal_mcp/applications/llm/__init__.py +3 -0
  48. universal_mcp/applications/llm/app.py +158 -0
  49. universal_mcp/applications/ui/app.py +118 -144
  50. {universal_mcp_agents-0.1.10.dist-info → universal_mcp_agents-0.1.12.dist-info}/METADATA +1 -1
  51. universal_mcp_agents-0.1.12.dist-info/RECORD +65 -0
  52. universal_mcp/agents/bigtool2/__init__.py +0 -67
  53. universal_mcp/agents/bigtool2/__main__.py +0 -23
  54. universal_mcp/agents/bigtool2/agent.py +0 -13
  55. universal_mcp/agents/bigtool2/graph.py +0 -155
  56. universal_mcp/agents/bigtool2/meta_tools.py +0 -120
  57. universal_mcp/agents/bigtool2/prompts.py +0 -15
  58. universal_mcp/agents/bigtoolcache/state.py +0 -27
  59. universal_mcp/agents/builder.py +0 -204
  60. universal_mcp_agents-0.1.10.dist-info/RECORD +0 -42
  61. /universal_mcp/agents/{bigtoolcache → bigtool}/context.py +0 -0
  62. /universal_mcp/agents/{bigtoolcache → bigtool}/prompts.py +0 -0
  63. /universal_mcp/agents/{bigtool2 → bigtool}/state.py +0 -0
  64. /universal_mcp/agents/{bigtoolcache → bigtool}/tools.py +0 -0
  65. {universal_mcp_agents-0.1.10.dist-info → universal_mcp_agents-0.1.12.dist-info}/WHEEL +0 -0
@@ -1,10 +1,12 @@
1
- import re
2
- from typing import Literal, TypedDict
1
+ import os
2
+ from typing import Any, Literal, TypedDict
3
3
 
4
+ from dotenv import load_dotenv
4
5
  import httpx
5
- from loguru import logger
6
- from markitdown import MarkItDown
7
6
  from universal_mcp.applications.application import BaseApplication
7
+ from markitdown import MarkItDown
8
+
9
+ load_dotenv()
8
10
 
9
11
 
10
12
  class SeriesItem(TypedDict):
@@ -35,6 +37,8 @@ class UiApp(BaseApplication):
35
37
  """Initialize the DefaultToolsApp"""
36
38
  super().__init__(name="ui")
37
39
  self.markitdown = MarkItDown(enable_plugins=True)
40
+ self.exa_base_url = "https://api.exa.ai"
41
+ self.exa_api_key = os.getenv("EXA_API_KEY")
38
42
 
39
43
  def create_bar_chart(
40
44
  self,
@@ -118,162 +122,136 @@ class UiApp(BaseApplication):
118
122
  """
119
123
  return "Success"
120
124
 
121
- def _handle_response(self, response: httpx.Response):
122
- """
123
- Handle the HTTP response, returning JSON if possible, otherwise text.
124
- """
125
+ def _fetch_exa(self, endpoint: str, body: dict, error_solution: str) -> dict[str, Any]:
126
+ if not self.exa_api_key:
127
+ return {
128
+ "isError": True,
129
+ "error": "EXA_API_KEY is not configured",
130
+ "solution": error_solution,
131
+ }
132
+
133
+ url = f"{self.exa_base_url}{endpoint}"
134
+ headers = {
135
+ "Content-Type": "application/json",
136
+ "x-api-key": self.exa_api_key,
137
+ }
125
138
  try:
139
+ response = httpx.post(url, json=body, headers=headers)
140
+ response.raise_for_status()
126
141
  return response.json()
127
- except Exception:
128
- logger.warning(
129
- f"Response is not JSON, returning text. Content-Type: {response.headers.get('content-type')}"
130
- )
142
+ except Exception as e:
131
143
  return {
132
- "text": response.text,
133
- "status_code": response.status_code,
134
- "headers": dict(response.headers),
144
+ "isError": True,
145
+ "error": str(e),
146
+ "solution": error_solution,
135
147
  }
136
148
 
137
- def http_get(self, url: str, headers: dict | None = None, query_params: dict | None = None):
138
- """
139
- Perform a GET request to the specified URL with optional parameters.
140
-
141
- Args:
142
- url (str): The URL to send the GET request to. Example: "https://api.example.com/data"
143
- headers (dict, optional): Optional HTTP headers to include in the request. Example: {"Authorization": "Bearer token"}
144
- query_params (dict, optional): Optional dictionary of query parameters to include in the request. Example: {"page": 1}
145
-
146
- Returns:
147
- dict: The JSON response from the GET request, or text if not JSON.
148
- Tags:
149
- get, important
150
- """
151
- logger.debug(f"GET request to {url} with headers {headers} and query params {query_params}")
152
- response = httpx.get(url, params=query_params, headers=headers)
153
- response.raise_for_status()
154
- return self._handle_response(response)
155
-
156
- def http_post(self, url: str, headers: dict | None = None, body: dict | None = None):
157
- """
158
- Perform a POST request to the specified URL with optional parameters.
159
-
160
- Args:
161
- url (str): The URL to send the POST request to. Example: "https://api.example.com/data"
162
- headers (dict, optional): Optional HTTP headers to include in the request. Example: {"Content-Type": "application/json"}
163
- body (dict, optional): Optional JSON body to include in the request. Example: {"name": "John"}
164
-
165
- Returns:
166
- dict: The JSON response from the POST request, or text if not JSON.
167
- Tags:
168
- post, important
169
- """
170
- logger.debug(f"POST request to {url} with headers {headers} and body {body}")
171
- response = httpx.post(url, json=body, headers=headers)
172
- response.raise_for_status()
173
- return self._handle_response(response)
174
-
175
- def http_put(self, url: str, headers: dict | None = None, body: dict | None = None):
149
+ def web_search(
150
+ self,
151
+ query: str,
152
+ numResults: int | None = None,
153
+ type: Literal["auto", "keyword", "neural"] | None = None,
154
+ category: str | None = None,
155
+ includeDomains: list[str] | None = None,
156
+ excludeDomains: list[str] | None = None,
157
+ startPublishedDate: str | None = None,
158
+ endPublishedDate: str | None = None,
159
+ maxCharacters: int | None = None,
160
+ ) -> dict[str, Any]:
176
161
  """
177
- Perform a PUT request to the specified URL with optional parameters.
162
+ Search the web using Exa AI - performs real-time web searches with semantic and neural search capabilities. Returns high-quality, relevant results with full content extraction.
178
163
 
179
164
  Args:
180
- url (str): The URL to send the PUT request to. Example: "https://api.example.com/data/1"
181
- headers (dict, optional): Optional HTTP headers to include in the request. Example: {"Authorization": "Bearer token"}
182
- body (dict, optional): Optional JSON body to include in the request. Example: {"name": "Jane"}
165
+ query (str): Search query.
166
+ numResults (Optional[int]): Number of search results to return.
167
+ type (Optional[str]): Search type - auto lets Exa decide, keyword for exact matches, neural for semantic search.
168
+ category (Optional[str]): Category to focus the search on.
169
+ includeDomains (Optional[List[str]]): List of domains to specifically include in search results.
170
+ excludeDomains (Optional[List[str]]): List of domains to specifically exclude from search results.
171
+ startPublishedDate (Optional[str]): Start date for published content (YYYY-MM-DD format).
172
+ endPublishedDate (Optional[str]): End date for published content (YYYY-MM-DD format).
173
+ maxCharacters (Optional[int]): Maximum characters to extract from each result.
183
174
 
184
175
  Returns:
185
- dict: The JSON response from the PUT request, or text if not JSON.
186
- Tags:
187
- put, important
188
- """
189
- logger.debug(f"PUT request to {url} with headers {headers} and body {body}")
190
- response = httpx.put(url, json=body, headers=headers)
191
- response.raise_for_status()
192
- return self._handle_response(response)
193
-
194
- def http_delete(self, url: str, headers: dict | None = None, body: dict | None = None):
195
- """
196
- Perform a DELETE request to the specified URL with optional parameters.
197
-
198
- Args:
199
- url (str): The URL to send the DELETE request to. Example: "https://api.example.com/data/1"
200
- headers (dict, optional): Optional HTTP headers to include in the request. Example: {"Authorization": "Bearer token"}
201
- body (dict, optional): Optional JSON body to include in the request. Example: {"reason": "obsolete"}
176
+ dict[str, Any]: Exa search response. On error, returns { isError, error, solution }.
202
177
 
203
- Returns:
204
- dict: The JSON response from the DELETE request, or text if not JSON.
205
178
  Tags:
206
- delete, important
207
- """
208
- logger.debug(f"DELETE request to {url} with headers {headers} and body {body}")
209
- response = httpx.delete(url, json=body, headers=headers)
210
- response.raise_for_status()
211
- return self._handle_response(response)
212
-
213
- def http_patch(self, url: str, headers: dict | None = None, body: dict | None = None):
214
- """
215
- Perform a PATCH request to the specified URL with optional parameters.
216
-
217
- Args:
218
- url (str): The URL to send the PATCH request to. Example: "https://api.example.com/data/1"
219
- headers (dict, optional): Optional HTTP headers to include in the request. Example: {"Authorization": "Bearer token"}
220
- body (dict, optional): Optional JSON body to include in the request. Example: {"status": "active"}
221
-
222
- Returns:
223
- dict: The JSON response from the PATCH request, or text if not JSON.
224
- Tags:
225
- patch, important
179
+ important
226
180
  """
227
- logger.debug(f"PATCH request to {url} with headers {headers} and body {body}")
228
- response = httpx.patch(url, json=body, headers=headers)
229
- response.raise_for_status()
230
- return self._handle_response(response)
181
+ # Build request body
182
+ request_body: dict[str, Any] = {
183
+ "query": query,
184
+ "type": type or "auto",
185
+ "numResults": numResults or 5,
186
+ "contents": {
187
+ "text": {"maxCharacters": maxCharacters or 3000},
188
+ "livecrawl": "preferred",
189
+ },
190
+ }
191
+
192
+ # Optional fields (only include if provided)
193
+ if category is not None:
194
+ request_body["category"] = category
195
+ if includeDomains:
196
+ request_body["includeDomains"] = includeDomains
197
+ if excludeDomains:
198
+ request_body["excludeDomains"] = excludeDomains
199
+ if startPublishedDate is not None:
200
+ request_body["startPublishedDate"] = startPublishedDate
201
+ if endPublishedDate is not None:
202
+ request_body["endPublishedDate"] = endPublishedDate
203
+
204
+ error_solution = (
205
+ "A web search error occurred. First, explain to the user what caused this specific "
206
+ "error and how they can resolve it. Then provide helpful information based on your "
207
+ "existing knowledge to answer their question."
208
+ )
209
+
210
+ result = self._fetch_exa("/search", request_body, error_solution)
211
+
212
+ # Add guide if not an error
213
+ if not result.get("isError"):
214
+ result["guide"] = (
215
+ "Use the search results to answer the user's question. Summarize the content and ask if "
216
+ "they have any additional questions about the topic."
217
+ )
218
+ return result
231
219
 
232
- async def read_file(self, uri: str) -> str:
220
+ def web_content(
221
+ self,
222
+ urls: list[str],
223
+ maxCharacters: int | None = None,
224
+ livecrawl: Literal["always", "fallback", "preferred"] | None = None,
225
+ ) -> dict[str, Any]:
233
226
  """
234
- This tool aims to extract the main text content from any url.
235
- When faced with a question you do not know the answer to, call this tool as often as
236
- needed to get the full context from any file in the user file attachments section.
227
+ Extract detailed content from specific URLs using Exa AI - retrieves full text content, metadata, and structured information from web pages with live crawling capabilities.
237
228
 
238
- Asynchronously converts a URI or local file path to markdown format
239
- using the markitdown converter.
240
229
  Args:
241
- uri (str): The URI pointing to the resource or a local file path.
242
- Supported schemes:
243
- - http:// or https:// (Web pages, feeds, APIs)
244
- - file:// (Local or accessible network files)
245
- - data: (Embedded data)
230
+ urls (List[str]): List of URLs to extract content from.
231
+ maxCharacters (Optional[int]): Maximum characters to extract from each URL.
232
+ livecrawl (Optional[str]): Live crawling preference - always forces live crawl, fallback uses cache first, preferred tries live first.
246
233
 
247
234
  Returns:
248
- A string containing the markdown representation of the content at the specified URI
249
-
250
- Raises:
251
- ValueError: If the URI is invalid, empty, or uses an unsupported scheme
252
- after automatic prefixing.
235
+ dict[str, Any]: Exa contents response. On error, returns { isError, error, solution }.
253
236
 
254
237
  Tags:
255
- convert, markdown, async, uri, transform, document, important
238
+ important
256
239
  """
257
- if not uri:
258
- raise ValueError("URI cannot be empty")
259
-
260
- known_schemes = ["http://", "https://", "file://", "data:"]
261
- has_scheme = any(uri.lower().startswith(scheme) for scheme in known_schemes)
262
- if not has_scheme and not re.match(r"^[a-zA-Z]+:", uri):
263
- if re.match(r"^[a-zA-Z]:[\\/]", uri): # Check for Windows drive letter path
264
- normalized_path = uri.replace("\\", "/") # Normalize backslashes
265
- processed_uri = f"file:///{normalized_path}"
266
- else: # Assume Unix-like path or simple relative path
267
- processed_uri = (
268
- f"file://{uri}" if uri.startswith("/") else f"file:///{uri}"
269
- ) # Add leading slash if missing for absolute paths
270
-
271
- uri_to_process = processed_uri
272
- else:
273
- # Use the uri as provided
274
- uri_to_process = uri
275
-
276
- return self.markitdown.convert_uri(uri_to_process).markdown
240
+ request_body: dict[str, Any] = {
241
+ "ids": urls,
242
+ "contents": {
243
+ "text": {"maxCharacters": maxCharacters or 3000},
244
+ "livecrawl": livecrawl or "preferred",
245
+ },
246
+ }
247
+
248
+ error_solution = (
249
+ "A web content extraction error occurred. First, explain to the user what caused this "
250
+ "specific error and how they can resolve it. Then provide helpful information based on "
251
+ "your existing knowledge to answer their question."
252
+ )
253
+
254
+ return self._fetch_exa("/contents", request_body, error_solution)
277
255
 
278
256
  def list_tools(self):
279
257
  """List all available tool methods in this application.
@@ -286,10 +264,6 @@ class UiApp(BaseApplication):
286
264
  self.create_line_chart,
287
265
  self.create_pie_chart,
288
266
  self.create_table,
289
- self.http_get,
290
- self.http_post,
291
- self.http_put,
292
- self.http_delete,
293
- self.http_patch,
294
- self.read_file,
267
+ self.web_search,
268
+ self.web_content,
295
269
  ]
@@ -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,65 @@
1
+ universal_mcp/agents/__init__.py,sha256=xQ0Yoln3f0MQrqTQhAZ0im7Tp1DawEJ04YPp20Nw1DQ,1244
2
+ universal_mcp/agents/base.py,sha256=le5vy02eXN15u8ntHWCu6Y-LgOmE_DA8tWmPOmLohAk,6908
3
+ universal_mcp/agents/cli.py,sha256=Yv9g8VHOyK__V7me4UcWsWIXiviPz7F1vqMYH1jkCms,1016
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/simple.py,sha256=NSATg5TWzsRNS7V3LFiDG28WSOCIwCdcC1g7NRwg2nM,2095
8
+ universal_mcp/agents/utils.py,sha256=QeysgwRCHvoe7LpIQkD05y9FGG7us4kWA8ApNj2ACr8,5274
9
+ universal_mcp/agents/bigtool/__init__.py,sha256=uTA6KzZlRhL2reAUazwYmwCHFt9q4hERo27e13ifBX4,2250
10
+ universal_mcp/agents/bigtool/__main__.py,sha256=a15OUoqPR938x7eseWtxu0aLX7lRS3nu8k5Ks3giUY4,472
11
+ universal_mcp/agents/bigtool/agent.py,sha256=anXjMgFWS95RYrnQsZJJxYq8HoCB6z9Poi-Uv5fhPis,266
12
+ universal_mcp/agents/bigtool/context.py,sha256=ny7gd-vvVpUOYAeQbAEUT0A6Vm6Nn2qGywxTzPBzYFg,929
13
+ universal_mcp/agents/bigtool/graph.py,sha256=DEsuSm9OYWBTeGEmvPk_nv_yb7EvCflHe4pbja5-jC0,4337
14
+ universal_mcp/agents/bigtool/prompts.py,sha256=Rz30qNGdscDG65vMj9d0Vfe7X1pQjBDQBBNc3BuyC94,1886
15
+ universal_mcp/agents/bigtool/state.py,sha256=TQeGZD99okclkoCh5oz-VYIlEsC9yLQyDpnBnm7QCN8,759
16
+ universal_mcp/agents/bigtool/tools.py,sha256=ynyEj9mVwKKDhxm76sjspyH51SFi63g2Vydi39pY0qY,5562
17
+ universal_mcp/agents/builder/__main__.py,sha256=XaXpJNwkj5Os1Vn1er8wokPKd4O4WW_L2XkGkvqEhao,4912
18
+ universal_mcp/agents/builder/builder.py,sha256=1p32txpFlG1l6XP1N5UOFAhNnQS02nmvA_uMDO5i-m4,9015
19
+ universal_mcp/agents/builder/prompts.py,sha256=RFzDBdGxF5BsAQL09BVas9RjAwt_Q2ZACghG5pOxqas,8623
20
+ universal_mcp/agents/builder/state.py,sha256=7DeWllxfN-yD6cd9wJ3KIgjO8TctkJvVjAbZT8W_zqk,922
21
+ universal_mcp/agents/codeact/__init__.py,sha256=rLE8gvOo5H4YSr71DRq76b3RV3uuotxuAy_VnBVaVwk,60
22
+ universal_mcp/agents/codeact/__main__.py,sha256=W2cHXRwH1dZG3ETIkMwUqA_d62K3IctHP-FDZWDjxdw,1067
23
+ universal_mcp/agents/codeact/agent.py,sha256=RRMeXVgn3Ow2gxhDKszx9hSUYGDTk6yqqDVyNIOu5kI,9778
24
+ universal_mcp/agents/codeact/models.py,sha256=2fdAcF5bxWDpljjEwDEdPBflTMShSPwwncHrphRjsYg,222
25
+ universal_mcp/agents/codeact/prompts.py,sha256=EMI-imnd0Ps0Bd2FOvSqgiicvvtFFu0MF9s93PiC_3k,4493
26
+ universal_mcp/agents/codeact/sandbox.py,sha256=gEAmZ08PovnFmCKP7uIQaKXHFLOyrnuSmoh7E_SHhjQ,2307
27
+ universal_mcp/agents/codeact/state.py,sha256=WTPfpxDlGRnlr5tZuXMg_KU7GS7TZbnrIKslOvZLbQI,565
28
+ universal_mcp/agents/codeact/utils.py,sha256=JUbT_HYGS_D1BzmzoVpORIe7SGur1KgJguTZ_1tZ4JY,1918
29
+ universal_mcp/agents/codeact0/__init__.py,sha256=rLE8gvOo5H4YSr71DRq76b3RV3uuotxuAy_VnBVaVwk,60
30
+ universal_mcp/agents/codeact0/__main__.py,sha256=V2wLWW9ym3rtiSvPEs-N0Mki7G5dYHzV5dAsAoF-ygQ,1148
31
+ universal_mcp/agents/codeact0/agent.py,sha256=e1v_XVRqblKrAgrkyiPz7bs4atgDRTVHPnZQUjnT_4o,6495
32
+ universal_mcp/agents/codeact0/config.py,sha256=H-1woj_nhSDwf15F63WYn723y4qlRefXzGxuH81uYF0,2215
33
+ universal_mcp/agents/codeact0/langgraph_graph.py,sha256=mP3JrZ1I-Cqhk6E2DMRHUdLFevpyyad-Y3qhERn45JA,550
34
+ universal_mcp/agents/codeact0/legacy_codeact.py,sha256=Lj4oH-AaP1zg6ifBkUxzWJcwMs9X5SepGzzZ6S5QMSg,4096
35
+ universal_mcp/agents/codeact0/llm_tool.py,sha256=GEG8L2crRPyO5la_wlHohuqI9m6xH9KXgQxcP5fCFuU,13814
36
+ universal_mcp/agents/codeact0/prompts.py,sha256=GTUwnxyyUVdeAoAwo4ODGOlEtZStMkoP_lJEFRbDglY,7324
37
+ universal_mcp/agents/codeact0/sandbox.py,sha256=tkrhQoV7sAIT5rtd5kpNYEgDz4y82WSHKpMGE6bqthE,3108
38
+ universal_mcp/agents/codeact0/state.py,sha256=kcoCoz-kd0Ukw1YNGDHNggixCK34KzMl2t6GztakSo4,412
39
+ universal_mcp/agents/codeact0/utils.py,sha256=QIT4XIUln9o3H7MOmMen7i-dhDgvM8q0p--oy9Kbkss,15345
40
+ universal_mcp/agents/codeact0/usecases/1-unsubscribe.yaml,sha256=DiChHW-mNOcaaiec7f_04_A0Xyf9a2ihzXiPA9-Fw1I,239
41
+ universal_mcp/agents/codeact0/usecases/10-reddit2.yaml,sha256=R3vrZZNv_E-m_SuSJ5tSv11HqMomm8Gtxp_LDhkrnAw,618
42
+ universal_mcp/agents/codeact0/usecases/11-github.yaml,sha256=IRom0gDPcj0w0qa2LSARQbvrwJhLGQRaHI2U2rglcG0,650
43
+ universal_mcp/agents/codeact0/usecases/2-reddit.yaml,sha256=-dstvFMjsuP9UQM3B_G1HBn7ImNIMxSCNbITpfwgwAY,2525
44
+ universal_mcp/agents/codeact0/usecases/2.1-instructions.md,sha256=0gQBY_A3jT_lgKNiu6GdyEYupbX0Xz2unlCoTQwv-Es,5098
45
+ universal_mcp/agents/codeact0/usecases/2.2-instructions.md,sha256=Cx-VkcC55MrgFxlMBMBCD83jEge_yZgBWKwtuK1OPFc,4458
46
+ universal_mcp/agents/codeact0/usecases/3-earnings.yaml,sha256=c1ipGMmqafz8zTzGNYyn_4VldC-PB5FY87l8HS9QkOM,344
47
+ universal_mcp/agents/codeact0/usecases/4-maps.yaml,sha256=2KwGE1sFGtP8vEjKZ5Yjlro2j5LzafWbVlFy0E3a1CA,2462
48
+ universal_mcp/agents/codeact0/usecases/5-gmailreply.yaml,sha256=2ExMgk3z453phZocmCw0bmnJXugy79YNaoAfZV5CfMU,593
49
+ universal_mcp/agents/codeact0/usecases/6-contract.yaml,sha256=yVvoz1-pmJVsJSkCtTCuO57Ln5y1Kj7ErkjPyRweJb0,574
50
+ universal_mcp/agents/codeact0/usecases/7-overnight.yaml,sha256=Vod5md6wxmhY4MFkc1soMOpNNDXquVufo48QkM2orz0,731
51
+ universal_mcp/agents/codeact0/usecases/8-sheets_chart.yaml,sha256=aZpdyDMnrHjmV9wMqgV3VXsYGvNPFD58px6ZPjva5PM,1097
52
+ universal_mcp/agents/codeact0/usecases/9-learning.yaml,sha256=MXAgTU1p0Ek1md8dvb5Srb1W6QU4xPUgb6KgNfoza10,535
53
+ universal_mcp/agents/planner/__init__.py,sha256=7nGDDtmPk2LN4s5poIFkflm5ZoyRuQSQ73Qw_4GOov4,1415
54
+ universal_mcp/agents/planner/__main__.py,sha256=OfhTfYDZK_ZUfc8sX-Sa6TWk-dNqD2rl13Ln64mNAtw,771
55
+ universal_mcp/agents/planner/graph.py,sha256=70hhIoEZOcYojpiyVSCedgYpnmxVP7aqdn8s6VBu-D4,3228
56
+ universal_mcp/agents/planner/prompts.py,sha256=_JoHqiAvswtqCDu90AGUHmfsu8eWE1-_yI4LLn3pqMU,657
57
+ universal_mcp/agents/planner/state.py,sha256=qqyp-jSGsCxe1US-PRLT4-y1sITAcVE6nCMlQLnvop0,278
58
+ universal_mcp/agents/shared/prompts.py,sha256=VOsXSUEwBXPaAuxJTUF6bgDGr41u6uctUNQSMRt_OJc,6414
59
+ universal_mcp/agents/shared/tool_node.py,sha256=Ua_wzMt4YgIx4zLp3_ZCow-28qORwrZ2FvKqLPt3RlI,10415
60
+ universal_mcp/applications/llm/__init__.py,sha256=xnpxq4Wl_pevvwtSUtEwcty8_d61ywO1V2YnEXyCREY,46
61
+ universal_mcp/applications/llm/app.py,sha256=iNLU6z2LRZc01GfSKvV0vNzT1LhKAjq_UrSJEmjthjw,6032
62
+ universal_mcp/applications/ui/app.py,sha256=56h9GvkHiELyAVOZvi0YjiowSymlqkJ5GW4O7LmGIPs,9459
63
+ universal_mcp_agents-0.1.12.dist-info/METADATA,sha256=hqXAJNr5hrlsdZHsQwHed6ELV0b0h4q-l8Ar1IpsoEI,878
64
+ universal_mcp_agents-0.1.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
65
+ universal_mcp_agents-0.1.12.dist-info/RECORD,,
@@ -1,67 +0,0 @@
1
- from langgraph.checkpoint.base import BaseCheckpointSaver
2
- from universal_mcp.logger import logger
3
- from universal_mcp.tools.registry import ToolRegistry
4
- from universal_mcp.types import ToolConfig, ToolFormat
5
-
6
- from universal_mcp.agents.base import BaseAgent
7
- from universal_mcp.agents.llm import load_chat_model
8
-
9
- from .graph import build_graph
10
- from .meta_tools import create_meta_tools
11
- from .prompts import SYSTEM_PROMPT
12
-
13
-
14
- class BigToolAgent2(BaseAgent):
15
- def __init__(
16
- self,
17
- name: str,
18
- instructions: str,
19
- model: str,
20
- registry: ToolRegistry,
21
- memory: BaseCheckpointSaver | None = None,
22
- tools: ToolConfig | None = None,
23
- **kwargs,
24
- ):
25
- super().__init__(name, instructions, model, memory, **kwargs)
26
-
27
- self.registry = registry
28
- self.llm = load_chat_model(self.model)
29
- self.recursion_limit = kwargs.get("recursion_limit", 10)
30
- self.tools = tools or {}
31
- if "ui" not in self.tools:
32
- # 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"]
33
- self.tools["ui"] = ["create_table"]
34
-
35
- logger.info(f"BigToolAgent '{self.name}' initialized with model '{self.model}'.")
36
-
37
- def _build_system_message(self):
38
- return SYSTEM_PROMPT.format(
39
- name=self.name,
40
- instructions=f"**User Instructions:**\n{self.instructions}",
41
- )
42
-
43
- async def _build_graph(self):
44
- """Build the bigtool agent graph using the existing create_agent function."""
45
- logger.info(f"Building graph for BigToolAgent '{self.name}'...")
46
- try:
47
- default_tools = await self.registry.export_tools(self.tools, ToolFormat.LANGCHAIN)
48
- graph_builder = build_graph(
49
- tool_registry=self.registry,
50
- llm=self.llm,
51
- system_prompt=self._build_system_message(),
52
- default_tools=default_tools,
53
- )
54
-
55
- compiled_graph = graph_builder.compile(checkpointer=self.memory)
56
- logger.info("Graph built and compiled successfully.")
57
- return compiled_graph
58
- except Exception as e:
59
- logger.error(f"Error building graph for BigToolAgent '{self.name}': {e}")
60
- raise
61
-
62
- @property
63
- def graph(self):
64
- return self._graph
65
-
66
-
67
- __all__ = ["BigToolAgent2", "create_meta_tools"]
@@ -1,23 +0,0 @@
1
- import asyncio
2
-
3
- from loguru import logger
4
- from universal_mcp.agentr.registry import AgentrRegistry
5
-
6
- from universal_mcp.agents.bigtool2 import BigToolAgent2
7
- from universal_mcp.agents.utils import messages_to_list
8
-
9
-
10
- async def main():
11
- agent = BigToolAgent2(
12
- name="bigtool",
13
- instructions="You are a helpful assistant that can use tools to help the user.",
14
- model="azure/gpt-4.1",
15
- registry=AgentrRegistry(),
16
- )
17
- await agent.ainit()
18
- output = await agent.invoke(user_input="Send an email to manoj@agentr.dev")
19
- logger.info(messages_to_list(output["messages"]))
20
-
21
-
22
- if __name__ == "__main__":
23
- asyncio.run(main())
@@ -1,13 +0,0 @@
1
- from universal_mcp.agentr.registry import AgentrRegistry
2
-
3
- from universal_mcp.agents.bigtool2 import BigToolAgent2
4
-
5
-
6
- async def agent():
7
- agent_object = await BigToolAgent2(
8
- name="BigTool Agent 2",
9
- instructions="You are a helpful assistant that can use various tools to complete tasks.",
10
- model="anthropic/claude-4-sonnet-20250514",
11
- registry=AgentrRegistry(),
12
- )._build_graph()
13
- return agent_object
@@ -1,155 +0,0 @@
1
- import json
2
- from datetime import UTC, datetime
3
- from typing import Literal, cast
4
-
5
- from langchain_core.language_models import BaseChatModel
6
- from langchain_core.messages import AIMessage, ToolMessage
7
- from langchain_core.tools import BaseTool
8
- from langgraph.graph import StateGraph
9
- from langgraph.types import Command
10
- from universal_mcp.logger import logger
11
- from universal_mcp.tools.registry import ToolRegistry
12
- from universal_mcp.types import ToolFormat
13
-
14
- from universal_mcp.agents.bigtool2.meta_tools import create_meta_tools
15
- from universal_mcp.agents.bigtool2.state import State
16
-
17
-
18
- def build_graph(tool_registry: ToolRegistry, llm: BaseChatModel, system_prompt: str, default_tools: list[BaseTool]):
19
- # Instantiate meta tools (search, load, web_search)
20
- search_tools, load_tools, web_search = create_meta_tools(tool_registry)
21
-
22
- async def call_model(
23
- state: State,
24
- ) -> Command[Literal["select_tools", "call_tools"]]:
25
- logger.info("Calling model...")
26
- try:
27
- system_message = system_prompt.format(system_time=datetime.now(tz=UTC).isoformat())
28
- messages = [
29
- {"role": "system", "content": system_message},
30
- *state["messages"],
31
- ]
32
-
33
- logger.info(f"Selected tool IDs: {state['selected_tool_ids']}")
34
- if len(state["selected_tool_ids"]) > 0:
35
- selected_tools = await tool_registry.export_tools(
36
- tools=state["selected_tool_ids"], format=ToolFormat.LANGCHAIN
37
- )
38
- logger.info(f"Exported {len(selected_tools)} tools for model.")
39
- else:
40
- selected_tools = []
41
-
42
- model = llm
43
-
44
- tools = [search_tools, load_tools, web_search, *default_tools, *selected_tools]
45
- # Remove duplicates based on tool name
46
- seen_names = set()
47
- unique_tools = []
48
- for tool in tools:
49
- if tool.name not in seen_names:
50
- seen_names.add(tool.name)
51
- unique_tools.append(tool)
52
- tools = unique_tools
53
- model_with_tools = model.bind_tools(
54
- tools,
55
- tool_choice="auto",
56
- )
57
- response = cast(AIMessage, await model_with_tools.ainvoke(messages))
58
-
59
- if response.tool_calls:
60
- logger.info(f"Model responded with {len(response.tool_calls)} tool calls.")
61
- return Command(goto="call_tools", update={"messages": [response]})
62
- else:
63
- logger.info("Model responded with a message, ending execution.")
64
- return Command(update={"messages": [response]})
65
- except Exception as e:
66
- logger.error(f"Error in call_model: {e}")
67
- raise
68
-
69
- async def select_tools(state: State) -> Command[Literal["call_model"]]:
70
- logger.info("Selecting tools...")
71
- try:
72
- tool_call = state["messages"][-1].tool_calls[0]
73
- searched_tools = await search_tools.ainvoke(input=tool_call["args"])
74
- tool_msg = ToolMessage(
75
- f"Available tool_ids: {searched_tools}. Call load_tools to select the required tools only.",
76
- tool_call_id=tool_call["id"],
77
- )
78
- return Command(goto="call_model", update={"messages": [tool_msg]})
79
- except Exception as e:
80
- logger.error(f"Error in select_tools: {e}")
81
- raise
82
-
83
- async def call_tools(state: State) -> Command[Literal["call_model"]]:
84
- logger.info("Calling tools...")
85
- outputs = []
86
- recent_tool_ids = []
87
- for tool_call in state["messages"][-1].tool_calls:
88
- try:
89
- # Handle special tools internally (no export needed)
90
- if tool_call["name"] == search_tools.name:
91
- search_result = await search_tools.ainvoke(input=tool_call["args"])
92
- outputs.append(
93
- ToolMessage(
94
- content=search_result,
95
- name=tool_call["name"],
96
- tool_call_id=tool_call["id"],
97
- )
98
- )
99
- continue
100
-
101
- if tool_call["name"] == load_tools.name:
102
- selected_tool_ids = await load_tools.ainvoke(tool_call["args"])
103
- outputs.append(
104
- ToolMessage(
105
- content=json.dumps(f"Loaded tools- {selected_tool_ids}"),
106
- name=tool_call["name"],
107
- tool_call_id=tool_call["id"],
108
- )
109
- )
110
- recent_tool_ids = selected_tool_ids
111
- continue
112
-
113
- if tool_call["name"] == web_search.name:
114
- web_search_result = await web_search.ainvoke(input=tool_call["args"])
115
- outputs.append(
116
- ToolMessage(
117
- content=json.dumps(f"Web search result: {web_search_result}"),
118
- name=tool_call["name"],
119
- tool_call_id=tool_call["id"],
120
- )
121
- )
122
- continue
123
-
124
- # For other tools: export and call via registry
125
- await tool_registry.export_tools([tool_call["name"]], ToolFormat.LANGCHAIN)
126
- tool_result = await tool_registry.call_tool(tool_call["name"], tool_call["args"])
127
- outputs.append(
128
- ToolMessage(
129
- content=json.dumps(tool_result),
130
- name=tool_call["name"],
131
- tool_call_id=tool_call["id"],
132
- )
133
- )
134
- recent_tool_ids.append(tool_call["name"])
135
- except Exception as e:
136
- logger.error(f"Error executing tool '{tool_call['name']}': {e}")
137
- outputs.append(
138
- ToolMessage(
139
- content=json.dumps("Error: " + str(e)),
140
- name=tool_call["name"],
141
- tool_call_id=tool_call["id"],
142
- )
143
- )
144
- return Command(
145
- goto="call_model",
146
- update={"messages": outputs, "selected_tool_ids": recent_tool_ids},
147
- )
148
-
149
- builder = StateGraph(State)
150
-
151
- builder.add_node(call_model)
152
- builder.add_node(select_tools)
153
- builder.add_node(call_tools)
154
- builder.set_entry_point("call_model")
155
- return builder