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.
- universal_mcp/agents/__init__.py +17 -19
- universal_mcp/agents/base.py +10 -7
- universal_mcp/agents/{bigtoolcache → bigtool}/__init__.py +2 -2
- universal_mcp/agents/{bigtoolcache → bigtool}/__main__.py +0 -1
- universal_mcp/agents/{bigtoolcache → bigtool}/agent.py +0 -1
- universal_mcp/agents/{bigtoolcache → bigtool}/graph.py +6 -5
- universal_mcp/agents/builder/__main__.py +125 -0
- universal_mcp/agents/builder/builder.py +225 -0
- universal_mcp/agents/builder/prompts.py +173 -0
- universal_mcp/agents/builder/state.py +24 -0
- universal_mcp/agents/cli.py +3 -2
- universal_mcp/agents/codeact/__main__.py +2 -4
- universal_mcp/agents/codeact/agent.py +188 -108
- universal_mcp/agents/codeact/models.py +11 -0
- universal_mcp/agents/codeact/prompts.py +34 -43
- universal_mcp/agents/codeact/sandbox.py +78 -40
- universal_mcp/agents/codeact/state.py +5 -4
- universal_mcp/agents/codeact0/__init__.py +3 -0
- universal_mcp/agents/codeact0/__main__.py +35 -0
- universal_mcp/agents/codeact0/agent.py +136 -0
- universal_mcp/agents/codeact0/config.py +77 -0
- universal_mcp/agents/codeact0/langgraph_graph.py +17 -0
- universal_mcp/agents/codeact0/legacy_codeact.py +104 -0
- universal_mcp/agents/codeact0/llm_tool.py +379 -0
- universal_mcp/agents/codeact0/prompts.py +156 -0
- universal_mcp/agents/codeact0/sandbox.py +90 -0
- universal_mcp/agents/codeact0/state.py +12 -0
- universal_mcp/agents/codeact0/usecases/1-unsubscribe.yaml +4 -0
- universal_mcp/agents/codeact0/usecases/10-reddit2.yaml +10 -0
- universal_mcp/agents/codeact0/usecases/11-github.yaml +13 -0
- universal_mcp/agents/codeact0/usecases/2-reddit.yaml +27 -0
- universal_mcp/agents/codeact0/usecases/2.1-instructions.md +81 -0
- universal_mcp/agents/codeact0/usecases/2.2-instructions.md +71 -0
- universal_mcp/agents/codeact0/usecases/3-earnings.yaml +4 -0
- universal_mcp/agents/codeact0/usecases/4-maps.yaml +41 -0
- universal_mcp/agents/codeact0/usecases/5-gmailreply.yaml +8 -0
- universal_mcp/agents/codeact0/usecases/6-contract.yaml +6 -0
- universal_mcp/agents/codeact0/usecases/7-overnight.yaml +14 -0
- universal_mcp/agents/codeact0/usecases/8-sheets_chart.yaml +25 -0
- universal_mcp/agents/codeact0/usecases/9-learning.yaml +9 -0
- universal_mcp/agents/codeact0/utils.py +374 -0
- universal_mcp/agents/hil.py +4 -4
- universal_mcp/agents/planner/__init__.py +7 -1
- universal_mcp/agents/react.py +11 -3
- universal_mcp/agents/simple.py +12 -2
- universal_mcp/agents/utils.py +17 -0
- universal_mcp/applications/llm/__init__.py +3 -0
- universal_mcp/applications/llm/app.py +158 -0
- universal_mcp/applications/ui/app.py +118 -144
- {universal_mcp_agents-0.1.10.dist-info → universal_mcp_agents-0.1.12.dist-info}/METADATA +1 -1
- universal_mcp_agents-0.1.12.dist-info/RECORD +65 -0
- universal_mcp/agents/bigtool2/__init__.py +0 -67
- universal_mcp/agents/bigtool2/__main__.py +0 -23
- universal_mcp/agents/bigtool2/agent.py +0 -13
- universal_mcp/agents/bigtool2/graph.py +0 -155
- universal_mcp/agents/bigtool2/meta_tools.py +0 -120
- universal_mcp/agents/bigtool2/prompts.py +0 -15
- universal_mcp/agents/bigtoolcache/state.py +0 -27
- universal_mcp/agents/builder.py +0 -204
- universal_mcp_agents-0.1.10.dist-info/RECORD +0 -42
- /universal_mcp/agents/{bigtoolcache → bigtool}/context.py +0 -0
- /universal_mcp/agents/{bigtoolcache → bigtool}/prompts.py +0 -0
- /universal_mcp/agents/{bigtool2 → bigtool}/state.py +0 -0
- /universal_mcp/agents/{bigtoolcache → bigtool}/tools.py +0 -0
- {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
|
|
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
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
144
|
+
"isError": True,
|
|
145
|
+
"error": str(e),
|
|
146
|
+
"solution": error_solution,
|
|
135
147
|
}
|
|
136
148
|
|
|
137
|
-
def
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
238
|
+
important
|
|
256
239
|
"""
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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.
|
|
290
|
-
self.
|
|
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
|
]
|
|
@@ -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
|