universal-mcp-agents 0.1.11__py3-none-any.whl → 0.1.13__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 (64) 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 +166 -64
  14. universal_mcp/agents/codeact/models.py +11 -0
  15. universal_mcp/agents/codeact/prompts.py +12 -12
  16. universal_mcp/agents/codeact/sandbox.py +69 -23
  17. universal_mcp/agents/codeact/state.py +2 -0
  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/llm_tool.py +379 -0
  23. universal_mcp/agents/codeact0/prompts.py +156 -0
  24. universal_mcp/agents/codeact0/sandbox.py +90 -0
  25. universal_mcp/agents/codeact0/state.py +12 -0
  26. universal_mcp/agents/codeact0/usecases/1-unsubscribe.yaml +4 -0
  27. universal_mcp/agents/codeact0/usecases/10-reddit2.yaml +10 -0
  28. universal_mcp/agents/codeact0/usecases/11-github.yaml +13 -0
  29. universal_mcp/agents/codeact0/usecases/2-reddit.yaml +27 -0
  30. universal_mcp/agents/codeact0/usecases/2.1-instructions.md +81 -0
  31. universal_mcp/agents/codeact0/usecases/2.2-instructions.md +71 -0
  32. universal_mcp/agents/codeact0/usecases/3-earnings.yaml +4 -0
  33. universal_mcp/agents/codeact0/usecases/4-maps.yaml +41 -0
  34. universal_mcp/agents/codeact0/usecases/5-gmailreply.yaml +8 -0
  35. universal_mcp/agents/codeact0/usecases/6-contract.yaml +6 -0
  36. universal_mcp/agents/codeact0/usecases/7-overnight.yaml +14 -0
  37. universal_mcp/agents/codeact0/usecases/8-sheets_chart.yaml +25 -0
  38. universal_mcp/agents/codeact0/usecases/9-learning.yaml +9 -0
  39. universal_mcp/agents/codeact0/utils.py +374 -0
  40. universal_mcp/agents/hil.py +4 -4
  41. universal_mcp/agents/planner/__init__.py +7 -1
  42. universal_mcp/agents/react.py +11 -3
  43. universal_mcp/agents/shared/tool_node.py +1 -34
  44. universal_mcp/agents/simple.py +12 -2
  45. universal_mcp/agents/utils.py +17 -0
  46. universal_mcp/applications/llm/__init__.py +3 -0
  47. universal_mcp/applications/llm/app.py +158 -0
  48. universal_mcp/applications/ui/app.py +118 -144
  49. {universal_mcp_agents-0.1.11.dist-info → universal_mcp_agents-0.1.13.dist-info}/METADATA +1 -1
  50. universal_mcp_agents-0.1.13.dist-info/RECORD +63 -0
  51. universal_mcp/agents/bigtool2/__init__.py +0 -67
  52. universal_mcp/agents/bigtool2/__main__.py +0 -23
  53. universal_mcp/agents/bigtool2/agent.py +0 -13
  54. universal_mcp/agents/bigtool2/graph.py +0 -155
  55. universal_mcp/agents/bigtool2/meta_tools.py +0 -120
  56. universal_mcp/agents/bigtool2/prompts.py +0 -15
  57. universal_mcp/agents/bigtoolcache/state.py +0 -27
  58. universal_mcp/agents/builder.py +0 -204
  59. universal_mcp_agents-0.1.11.dist-info/RECORD +0 -42
  60. /universal_mcp/agents/{bigtoolcache → bigtool}/context.py +0 -0
  61. /universal_mcp/agents/{bigtoolcache → bigtool}/prompts.py +0 -0
  62. /universal_mcp/agents/{bigtool2 → bigtool}/state.py +0 -0
  63. /universal_mcp/agents/{bigtoolcache → bigtool}/tools.py +0 -0
  64. {universal_mcp_agents-0.1.11.dist-info → universal_mcp_agents-0.1.13.dist-info}/WHEEL +0 -0
@@ -0,0 +1,158 @@
1
+ import json
2
+ from typing import Any, Literal, cast
3
+
4
+ from langchain.chat_models import init_chat_model
5
+ from langchain_openai import AzureChatOpenAI
6
+ from pydantic import BaseModel, Field
7
+ from universal_mcp.applications.application import BaseApplication
8
+
9
+ MAX_RETRIES = 3
10
+
11
+
12
+ def _get_context_as_string(source: Any | list[Any] | dict[str, Any]) -> str:
13
+ """Converts context to a string representation.
14
+
15
+ Args:
16
+ source: The source data to be converted. Can be a single value, a list of values, or a dictionary.
17
+
18
+ Returns:
19
+ A string representation of the source data, formatted with XML-like tags for dictionaries.
20
+ """
21
+
22
+ if not isinstance(source, dict):
23
+ if isinstance(source, list):
24
+ source = {f"doc_{i + 1}": str(doc) for i, doc in enumerate(source)}
25
+ else:
26
+ source = {"content": str(source)}
27
+
28
+ return "\n".join(f"<{k}>\n{str(v)}\n</{k}>" for k, v in source.items())
29
+
30
+
31
+ class LLMApp(BaseApplication):
32
+ """
33
+ An application for leveraging Large Language Models (LLMs) for advanced text processing tasks.
34
+ """
35
+
36
+ def __init__(self, **kwargs):
37
+ """Initialize the LLMApp."""
38
+ super().__init__(name="llm")
39
+
40
+ def generate_text(
41
+ self,
42
+ task: str,
43
+ context: Any | list[Any] | dict[str, Any],
44
+ tone: str = "normal",
45
+ output_format: Literal["markdown", "html", "plain"] = "markdown",
46
+ length: Literal["very-short", "concise", "normal", "long"] = "concise",
47
+ ) -> str:
48
+ """
49
+ Generates well-written text for a high-level task using the provided context.
50
+
51
+ Use this function for creative writing, summarization, and other text generation tasks.
52
+
53
+ Args:
54
+ task: The main writing task or directive.
55
+ context: A single string, list of strings, or dictionary mapping labels to content.
56
+ tone: The desired tone of the output (e.g., "formal", "casual", "technical").
57
+ output_format: The desired output format ('markdown', 'html', 'plain').
58
+ length: The desired length of the output ('very-short', 'concise', 'normal', 'long').
59
+
60
+ Returns:
61
+ The generated text as a string.
62
+ """
63
+ context_str = _get_context_as_string(context)
64
+
65
+ prompt = f"{task.strip()}\n\n"
66
+ if output_format == "markdown":
67
+ prompt += "Please write in Markdown format.\n\n"
68
+ elif output_format == "html":
69
+ prompt += "Please write in HTML format.\n\n"
70
+ else:
71
+ prompt += "Please write in plain text format. Do not use markdown or HTML.\n\n"
72
+
73
+ if tone not in ["normal", "default", ""]:
74
+ prompt = f"{prompt} (Tone instructions: {tone})"
75
+
76
+ if length not in ["normal", "default", ""]:
77
+ prompt = f"{prompt} (Length instructions: {length})"
78
+
79
+ full_prompt = f"{prompt}\n\nContext:\n{context_str}\n\n"
80
+
81
+ model = AzureChatOpenAI(model="gpt-4o", temperature=0.7)
82
+ response = model.with_retry(stop_after_attempt=MAX_RETRIES).invoke(full_prompt)
83
+ return str(response.content)
84
+
85
+ def classify_data(
86
+ self,
87
+ task: str,
88
+ context: Any | list[Any] | dict[str, Any],
89
+ class_descriptions: dict[str, str],
90
+ ) -> dict[str, Any]:
91
+ """
92
+ Classifies data into one of several categories based on a given task and context.
93
+
94
+ Args:
95
+ task: The classification question and any specific rules or requirements.
96
+ context: The data to be classified, provided as a string, list, or dictionary.
97
+ class_descriptions: A dictionary mapping class names to their descriptions.
98
+
99
+ Returns:
100
+ A dictionary containing the classification probabilities, the reasoning, and the top class.
101
+ """
102
+ context_str = _get_context_as_string(context)
103
+
104
+ prompt = (
105
+ f"{task}\n\n"
106
+ f"This is a classification task.\nPossible classes and descriptions:\n"
107
+ f"{json.dumps(class_descriptions, indent=2)}\n\n"
108
+ f"Context:\n{context_str}\n\n"
109
+ "Return ONLY a valid JSON object, no extra text."
110
+ )
111
+
112
+ model = init_chat_model(model="claude-4-sonnet-20250514", temperature=0)
113
+
114
+ class ClassificationResult(BaseModel):
115
+ probabilities: dict[str, float] = Field(..., description="The probabilities for each class.")
116
+ reason: str = Field(..., description="The reasoning behind the classification.")
117
+ top_class: str = Field(..., description="The class with the highest probability.")
118
+
119
+ response = (
120
+ model.with_structured_output(schema=ClassificationResult, method="json_mode")
121
+ .with_retry(stop_after_attempt=MAX_RETRIES)
122
+ .invoke(prompt)
123
+ )
124
+ return cast(dict[str, Any], response)
125
+
126
+ def extract_data(
127
+ self,
128
+ task: str,
129
+ source: Any | list[Any] | dict[str, Any],
130
+ output_schema: dict[str, Any],
131
+ ) -> dict[str, Any]:
132
+ """
133
+ Extracts structured data from unstructured text based on a provided JSON schema.
134
+
135
+ Args:
136
+ task: A description of the data to be extracted.
137
+ source: The unstructured data to extract from (e.g., document, webpage content).
138
+ output_schema: A valid JSON schema with a 'title' and 'description'.
139
+
140
+ Returns:
141
+ A dictionary containing the extracted data, matching the provided schema.
142
+ """
143
+ context_str = _get_context_as_string(source)
144
+
145
+ prompt = (
146
+ f"{task}\n\n"
147
+ f"Context:\n{context_str}\n\n"
148
+ "Return ONLY a valid JSON object that conforms to the provided schema, with no extra text."
149
+ )
150
+
151
+ model = init_chat_model(model="claude-4-sonnet-20250514", temperature=0)
152
+
153
+ response = (
154
+ model.with_structured_output(schema=output_schema, method="json_mode")
155
+ .with_retry(stop_after_attempt=MAX_RETRIES)
156
+ .invoke(prompt)
157
+ )
158
+ return cast(dict[str, Any], response)
@@ -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.11
3
+ Version: 0.1.13
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,63 @@
1
+ universal_mcp/agents/__init__.py,sha256=kM4mC6Pf6lmaaZF1volo7VtKgA8FDyzb1sNenpB7Ulk,1244
2
+ universal_mcp/agents/base.py,sha256=le5vy02eXN15u8ntHWCu6Y-LgOmE_DA8tWmPOmLohAk,6908
3
+ universal_mcp/agents/cli.py,sha256=AG9e4iSX3GazT537573YrYT1wSaZYOr42rrYQ7xP3YA,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=sKZWokTHcuL68Y6SNyaaHe6_XkWxaIq36TrNmPJfQto,9762
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=NjN6ISj8psFtHf8V0w24ChJdUMUWkq7OrlbHdzm4wBc,2299
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/llm_tool.py,sha256=GEG8L2crRPyO5la_wlHohuqI9m6xH9KXgQxcP5fCFuU,13814
34
+ universal_mcp/agents/codeact0/prompts.py,sha256=GTUwnxyyUVdeAoAwo4ODGOlEtZStMkoP_lJEFRbDglY,7324
35
+ universal_mcp/agents/codeact0/sandbox.py,sha256=tkrhQoV7sAIT5rtd5kpNYEgDz4y82WSHKpMGE6bqthE,3108
36
+ universal_mcp/agents/codeact0/state.py,sha256=kcoCoz-kd0Ukw1YNGDHNggixCK34KzMl2t6GztakSo4,412
37
+ universal_mcp/agents/codeact0/utils.py,sha256=QIT4XIUln9o3H7MOmMen7i-dhDgvM8q0p--oy9Kbkss,15345
38
+ universal_mcp/agents/codeact0/usecases/1-unsubscribe.yaml,sha256=DiChHW-mNOcaaiec7f_04_A0Xyf9a2ihzXiPA9-Fw1I,239
39
+ universal_mcp/agents/codeact0/usecases/10-reddit2.yaml,sha256=R3vrZZNv_E-m_SuSJ5tSv11HqMomm8Gtxp_LDhkrnAw,618
40
+ universal_mcp/agents/codeact0/usecases/11-github.yaml,sha256=IRom0gDPcj0w0qa2LSARQbvrwJhLGQRaHI2U2rglcG0,650
41
+ universal_mcp/agents/codeact0/usecases/2-reddit.yaml,sha256=-dstvFMjsuP9UQM3B_G1HBn7ImNIMxSCNbITpfwgwAY,2525
42
+ universal_mcp/agents/codeact0/usecases/2.1-instructions.md,sha256=0gQBY_A3jT_lgKNiu6GdyEYupbX0Xz2unlCoTQwv-Es,5098
43
+ universal_mcp/agents/codeact0/usecases/2.2-instructions.md,sha256=Cx-VkcC55MrgFxlMBMBCD83jEge_yZgBWKwtuK1OPFc,4458
44
+ universal_mcp/agents/codeact0/usecases/3-earnings.yaml,sha256=c1ipGMmqafz8zTzGNYyn_4VldC-PB5FY87l8HS9QkOM,344
45
+ universal_mcp/agents/codeact0/usecases/4-maps.yaml,sha256=2KwGE1sFGtP8vEjKZ5Yjlro2j5LzafWbVlFy0E3a1CA,2462
46
+ universal_mcp/agents/codeact0/usecases/5-gmailreply.yaml,sha256=2ExMgk3z453phZocmCw0bmnJXugy79YNaoAfZV5CfMU,593
47
+ universal_mcp/agents/codeact0/usecases/6-contract.yaml,sha256=yVvoz1-pmJVsJSkCtTCuO57Ln5y1Kj7ErkjPyRweJb0,574
48
+ universal_mcp/agents/codeact0/usecases/7-overnight.yaml,sha256=Vod5md6wxmhY4MFkc1soMOpNNDXquVufo48QkM2orz0,731
49
+ universal_mcp/agents/codeact0/usecases/8-sheets_chart.yaml,sha256=aZpdyDMnrHjmV9wMqgV3VXsYGvNPFD58px6ZPjva5PM,1097
50
+ universal_mcp/agents/codeact0/usecases/9-learning.yaml,sha256=MXAgTU1p0Ek1md8dvb5Srb1W6QU4xPUgb6KgNfoza10,535
51
+ universal_mcp/agents/planner/__init__.py,sha256=7nGDDtmPk2LN4s5poIFkflm5ZoyRuQSQ73Qw_4GOov4,1415
52
+ universal_mcp/agents/planner/__main__.py,sha256=OfhTfYDZK_ZUfc8sX-Sa6TWk-dNqD2rl13Ln64mNAtw,771
53
+ universal_mcp/agents/planner/graph.py,sha256=70hhIoEZOcYojpiyVSCedgYpnmxVP7aqdn8s6VBu-D4,3228
54
+ universal_mcp/agents/planner/prompts.py,sha256=_JoHqiAvswtqCDu90AGUHmfsu8eWE1-_yI4LLn3pqMU,657
55
+ universal_mcp/agents/planner/state.py,sha256=qqyp-jSGsCxe1US-PRLT4-y1sITAcVE6nCMlQLnvop0,278
56
+ universal_mcp/agents/shared/prompts.py,sha256=VOsXSUEwBXPaAuxJTUF6bgDGr41u6uctUNQSMRt_OJc,6414
57
+ universal_mcp/agents/shared/tool_node.py,sha256=KPF_nONfF12BmUTb9stHnxveqL7enGBi7wBGLrOjMsQ,9489
58
+ universal_mcp/applications/llm/__init__.py,sha256=xnpxq4Wl_pevvwtSUtEwcty8_d61ywO1V2YnEXyCREY,46
59
+ universal_mcp/applications/llm/app.py,sha256=iNLU6z2LRZc01GfSKvV0vNzT1LhKAjq_UrSJEmjthjw,6032
60
+ universal_mcp/applications/ui/app.py,sha256=56h9GvkHiELyAVOZvi0YjiowSymlqkJ5GW4O7LmGIPs,9459
61
+ universal_mcp_agents-0.1.13.dist-info/METADATA,sha256=wiruytjCRQN0GSX6yMToFwnIlmqWJPZbdbUOHXpUXJY,878
62
+ universal_mcp_agents-0.1.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
63
+ universal_mcp_agents-0.1.13.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