universal-mcp-agents 0.1.13__py3-none-any.whl → 0.1.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of universal-mcp-agents might be problematic. Click here for more details.

@@ -1,227 +1,206 @@
1
+ import asyncio
2
+ from collections import defaultdict
1
3
  from typing import Annotated, TypedDict
2
4
 
3
5
  from langchain_core.language_models import BaseChatModel
4
6
  from langchain_core.messages import AIMessage, AnyMessage
5
7
  from langgraph.graph import END, StateGraph
6
8
  from langgraph.graph.message import add_messages
9
+ from langgraph.types import Command
7
10
  from loguru import logger
8
11
  from pydantic import BaseModel, Field
9
12
  from universal_mcp.tools.registry import ToolRegistry
13
+ from universal_mcp.types import ToolConfig
10
14
 
11
15
  from universal_mcp.agents.shared.prompts import (
12
- APP_SEARCH_QUERY_PROMPT,
13
- REVISE_DECOMPOSITION_PROMPT,
14
- TASK_DECOMPOSITION_PROMPT,
15
- TOOL_SEARCH_QUERY_PROMPT,
16
+ APP_SELECTION_PROMPT,
17
+ TOOL_SEARCH_QUERIES_PROMPT,
16
18
  TOOL_SELECTION_PROMPT,
17
19
  )
18
20
 
19
- MAX_DECOMPOSITION_ATTEMPTS = 2
21
+ MAX_RETRIES = 1
20
22
 
21
- # --- Pydantic Models for Structured LLM Outputs ---
22
23
 
24
+ class SearchQueries(BaseModel):
25
+ queries: list[str] = Field(description="A list of search queries for finding tools.")
23
26
 
24
- class TaskDecomposition(BaseModel):
25
- sub_tasks: list[str] = Field(description="A list of sub-task descriptions.")
26
27
 
27
-
28
- class SearchQuery(BaseModel):
29
- query: str = Field(description="A concise search query.")
28
+ class AppSelection(BaseModel):
29
+ app_ids: list[str] = Field(description="The IDs of the selected applications.")
30
30
 
31
31
 
32
32
  class ToolSelection(BaseModel):
33
33
  tool_ids: list[str] = Field(description="The IDs of the selected tools.")
34
34
 
35
35
 
36
- # --- LangGraph Agent State ---
36
+ class AgentState(TypedDict):
37
+ """The central state of our agent graph."""
37
38
 
39
+ original_task: str
40
+ queries: list[str]
41
+ candidate_tools: list[dict]
42
+ execution_plan: ToolConfig
43
+ messages: Annotated[list[AnyMessage], add_messages]
44
+ retry_count: int
38
45
 
39
- class SubTask(TypedDict, total=False):
40
- """Represents a single step in the execution plan."""
41
46
 
42
- task: str
43
- status: str # "pending", "success", "failed"
44
- app_id: str
45
- tool_ids: list[str]
46
- reasoning: str
47
+ def build_tool_node_graph(llm: BaseChatModel, registry: ToolRegistry) -> StateGraph:
48
+ """Builds a workflow for tool selection with a retry mechanism."""
47
49
 
50
+ async def _search_for_tools(state: AgentState) -> Command:
51
+ """
52
+ Performs a hierarchical search:
53
+ 1. Generates search queries for the task.
54
+ 2. Searches for candidate *applications*.
55
+ 3. Uses an LLM to select the most relevant applications.
56
+ 4. Searches for tools only within the selected applications.
57
+ If any step fails, it can trigger a retry.
58
+ """
59
+ task = state["original_task"]
60
+ logger.info(f"Starting hierarchical tool search for task: '{task}'")
61
+
62
+ prompt = TOOL_SEARCH_QUERIES_PROMPT.format(task=task)
63
+ response = await llm.with_structured_output(SearchQueries).ainvoke(prompt)
64
+ queries = response.queries
65
+ logger.info(f"Generated search queries: {queries}")
66
+
67
+ if not queries:
68
+ logger.error("LLM failed to generate any search queries.")
69
+ return Command(
70
+ update={"messages": [AIMessage(content="I could not understand the task to search for tools.")]},
71
+ goto="handle_failure",
72
+ )
48
73
 
49
- class AgentState(TypedDict):
50
- """The central state of our agent graph."""
74
+ # Always store queries for potential retry
75
+ update_state = {"queries": queries}
51
76
 
52
- original_task: str
53
- decomposition_attempts: int
54
- failed_sub_task_info: str # To inform re-decomposition
55
- sub_tasks: list[SubTask]
56
- execution_plan: list[SubTask]
57
- messages: Annotated[list[AnyMessage], add_messages]
77
+ app_search_tasks = [registry.search_apps(query, distance_threshold=0.7) for query in queries]
78
+ app_results = await asyncio.gather(*app_search_tasks)
79
+ unique_apps = {app["id"]: app for app_list in app_results for app in app_list}
58
80
 
81
+ if not unique_apps:
82
+ logger.warning(f"No applications found for queries: {queries}. Triggering retry.")
83
+ return Command(update=update_state, goto="general_search_and_select")
59
84
 
60
- # --- Graph Builder ---
85
+ logger.info(f"Found {len(unique_apps)} candidate applications.")
61
86
 
87
+ app_candidates_str = "\n - ".join([f"{app['id']}: {app['description']}" for app in unique_apps.values()])
88
+ app_selection_prompt = APP_SELECTION_PROMPT.format(task=task, app_candidates=app_candidates_str)
89
+ app_selection_response = await llm.with_structured_output(AppSelection).ainvoke(app_selection_prompt)
90
+ selected_app_ids = app_selection_response.app_ids
62
91
 
63
- def build_tool_node_graph(llm: BaseChatModel, registry: ToolRegistry) -> StateGraph:
64
- """Builds the adaptive LangGraph workflow for tool selection."""
92
+ if not selected_app_ids:
93
+ logger.warning("LLM did not select any applications from the candidate list. Triggering retry.")
94
+ return Command(update=update_state, goto="general_search_and_select")
65
95
 
66
- async def _decompose_task(state: AgentState) -> AgentState:
67
- """Decomposes the main task or revises a failed decomposition."""
68
- attempts = state.get("decomposition_attempts", 0)
69
- task = state["original_task"]
70
- failed_info = state.get("failed_sub_task_info")
96
+ logger.success(f"Selected {len(selected_app_ids)} applications: {selected_app_ids}")
71
97
 
72
- if attempts > 0 and failed_info:
73
- logger.warning(f"Revising decomposition. Attempt {attempts + 1}.")
74
- prompt = REVISE_DECOMPOSITION_PROMPT.format(task=task, failed_sub_task=failed_info)
75
- else:
76
- logger.info("Performing initial task decomposition.")
77
- prompt = TASK_DECOMPOSITION_PROMPT.format(task=task)
78
-
79
- response = await llm.with_structured_output(TaskDecomposition).ainvoke(prompt)
80
- sub_tasks = [{"task": sub_task_str, "status": "pending"} for sub_task_str in response.sub_tasks]
81
-
82
- return {
83
- "sub_tasks": sub_tasks,
84
- "decomposition_attempts": attempts + 1,
85
- "messages": [AIMessage(content=f"New plan created with {len(sub_tasks)} steps.")],
86
- }
87
-
88
- async def _resolve_sub_tasks(state: AgentState) -> AgentState:
89
- """Iterates through sub-tasks, providing full plan context to the app selection prompt."""
90
- sub_tasks = state["sub_tasks"]
91
- original_task = state["original_task"]
92
- current_plan = []
93
-
94
- for i, sub_task in enumerate(sub_tasks):
95
- task_desc = sub_task["task"]
96
- logger.info(f"Resolving sub-task: '{task_desc}'")
97
-
98
- # 1. Build the FULL context string from the entire plan so far
99
- if not current_plan:
100
- plan_context_str = "None. This is the first step."
101
- else:
102
- context_lines = [
103
- f"- The sub-task '{step['task']}' was assigned to app '{step['app_id']}'." for step in current_plan
104
- ]
105
- plan_context_str = "\n".join(context_lines)
106
-
107
- # 2. Generate the App-specific query using the NEW full-context prompt
108
- app_query_prompt = APP_SEARCH_QUERY_PROMPT.format(
109
- original_task=original_task, plan_context=plan_context_str, sub_task=task_desc
110
- )
111
- app_query_response = await llm.with_structured_output(SearchQuery).ainvoke(app_query_prompt)
112
- app_search_query = app_query_response.query
113
- logger.info(f"Generated context-aware app search query: '{app_search_query}'")
114
-
115
- # 3. Search for candidate apps (the rest of the logic is the same)
116
- candidate_apps = await registry.search_apps(query=app_search_query, limit=5)
117
- if not candidate_apps:
118
- logger.error(f"No apps found for query '{app_search_query}' from sub-task: '{task_desc}'")
119
- return {"failed_sub_task_info": task_desc, "sub_tasks": []}
120
-
121
- # 4. Generate Action-specific query for finding the tool
122
- tool_query_prompt = TOOL_SEARCH_QUERY_PROMPT.format(sub_task=task_desc)
123
- tool_query_response = await llm.with_structured_output(SearchQuery).ainvoke(tool_query_prompt)
124
- tool_search_query = tool_query_response.query
125
- logger.info(f"Generated tool search query: '{tool_search_query}'")
126
-
127
- # 5. Find a suitable tool within the candidate apps
128
- tool_found = False
129
- for app in candidate_apps:
130
- app_id = app["id"]
131
- logger.info(f"Searching for tools in app '{app_id}' with query '{tool_search_query}'...")
132
-
133
- found_tools = await registry.search_tools(query=tool_search_query, app_id=app_id, limit=5)
134
- if not found_tools:
135
- continue
136
-
137
- tool_candidates_str = "\n - ".join([f"{tool['name']}: {tool['description']}" for tool in found_tools])
138
- selection_prompt = TOOL_SELECTION_PROMPT.format(sub_task=task_desc, tool_candidates=tool_candidates_str)
139
- selection_response = await llm.with_structured_output(ToolSelection).ainvoke(selection_prompt)
140
-
141
- if selection_response.tool_ids:
142
- logger.success(f"Found and selected tool(s) {selection_response.tool_ids} in app '{app_id}'.")
143
- sub_task.update(
144
- {
145
- "status": "success",
146
- "app_id": app_id,
147
- "tool_ids": selection_response.tool_ids,
148
- "reasoning": f"Selected tool(s) {selection_response.tool_ids} from app '{app_id}' for sub-task.",
149
- }
150
- )
151
- current_plan.append(sub_task)
152
- tool_found = True
153
- break
154
-
155
- if not tool_found:
156
- logger.error(f"Could not find any suitable tool for sub-task: '{task_desc}'")
157
- return {"failed_sub_task_info": task_desc, "sub_tasks": []}
158
-
159
- return {"execution_plan": current_plan, "sub_tasks": []}
160
-
161
- def _handle_planning_failure(state: AgentState) -> AgentState:
162
- """Handles the case where all decomposition attempts have failed."""
163
- logger.error("Maximum decomposition attempts reached. Planning failed.")
164
- return {
165
- "messages": [
166
- AIMessage(
167
- content="I am unable to create a complete plan for this task with the available tools. Please try rephrasing your request."
168
- )
169
- ]
170
- }
98
+ tool_search_tasks = [
99
+ registry.search_tools(task, app_id=app_id, distance_threshold=0.8) for app_id in selected_app_ids
100
+ ]
101
+ tool_results = await asyncio.gather(*tool_search_tasks)
102
+ candidate_tools = [tool for tool_list in tool_results for tool in tool_list]
103
+
104
+ if not candidate_tools:
105
+ logger.warning(f"No tools found within the selected applications: {selected_app_ids}. Triggering retry.")
106
+ return Command(update=update_state, goto="general_search_and_select")
171
107
 
172
- def _consolidate_plan(state: AgentState) -> AgentState:
108
+ logger.success(f"Found {len(candidate_tools)} candidate tools from selected apps.")
109
+ update_state["candidate_tools"] = candidate_tools
110
+ return Command(update=update_state, goto="select_tools_for_plan")
111
+
112
+ async def _general_search_and_select(state: AgentState) -> Command:
173
113
  """
174
- NEW: Merges steps in the execution plan that use the same app_id.
175
- It combines their tool_ids into a single unique list.
114
+ A retry node that performs a general tool search without app filters.
176
115
  """
177
- logger.info("Consolidating final execution plan.")
178
- plan = state["execution_plan"]
179
- merged_apps: dict[str, SubTask] = {}
180
-
181
- for step in plan:
182
- app_id = step["app_id"]
183
- if app_id not in merged_apps:
184
- # Store the first occurrence of this app
185
- merged_apps[app_id] = step.copy()
186
- merged_apps[app_id]["tool_ids"] = set(step["tool_ids"])
187
- else:
188
- # If app already seen, just update its set of tool_ids
189
- merged_apps[app_id]["tool_ids"].update(step["tool_ids"])
116
+ task = state["original_task"]
117
+ queries = state["queries"]
118
+ retry_count = state.get("retry_count", 0)
119
+
120
+ if retry_count >= MAX_RETRIES:
121
+ logger.error("Max retries reached. Failing the planning process.")
122
+ return Command(
123
+ update={"messages": [AIMessage(content="I could not find any relevant tools after extensive searching.")]},
124
+ goto="handle_failure",
125
+ )
190
126
 
191
- # Convert the merged dictionary back to a list of SubTasks
192
- final_plan = []
193
- for app_id, step_data in merged_apps.items():
194
- step_data["tool_ids"] = sorted(list(step_data["tool_ids"]))
195
- final_plan.append(step_data)
127
+ logger.info(f"--- RETRY {retry_count + 1}/{MAX_RETRIES} ---")
128
+ logger.info("Performing a general tool search without app filters.")
129
+
130
+ general_search_tasks = [
131
+ registry.search_tools(query, distance_threshold=0.85) for query in queries
132
+ ]
133
+ tool_results = await asyncio.gather(*general_search_tasks)
134
+
135
+ unique_tools = {tool['id']: tool for tool_list in tool_results for tool in tool_list}
136
+ candidate_tools = list(unique_tools.values())
137
+
138
+ if not candidate_tools:
139
+ logger.error("General search (retry) also failed to find any tools.")
140
+ return Command(
141
+ update={
142
+ "messages": [AIMessage(content="I could not find any tools for your request, even with a broader search.")],
143
+ "retry_count": retry_count + 1,
144
+ },
145
+ goto="handle_failure",
146
+ )
147
+
148
+ logger.success(f"General search found {len(candidate_tools)} candidate tools.")
149
+ return Command(
150
+ update={"candidate_tools": candidate_tools, "retry_count": retry_count + 1},
151
+ goto="select_tools_for_plan",
152
+ )
196
153
 
197
- return {"execution_plan": final_plan}
154
+ async def _select_tools_for_plan(state: AgentState) -> Command:
155
+ """Selects the best tools from the candidates and builds the final execution plan."""
156
+ task = state["original_task"]
157
+ candidate_tools = state["candidate_tools"]
158
+ retry_count = state.get("retry_count", 0)
159
+ logger.info("Starting tool selection from candidate list.")
160
+
161
+ tool_candidates_str = "\n - ".join([f"{tool['id']}: {tool['description']}" for tool in candidate_tools])
162
+ prompt = TOOL_SELECTION_PROMPT.format(task=task, tool_candidates=tool_candidates_str)
163
+ response = await llm.with_structured_output(ToolSelection).ainvoke(prompt)
164
+ selected_tool_ids = response.tool_ids
165
+
166
+ if not selected_tool_ids:
167
+ if retry_count >= MAX_RETRIES:
168
+ logger.error("LLM did not select any tools, even after a retry. Failing.")
169
+ return Command(
170
+ update={"messages": [AIMessage(content="I found potential tools, but could not create a final plan.")]},
171
+ goto="handle_failure",
172
+ )
173
+ else:
174
+ logger.warning("LLM did not select any tools from the current candidate list. Triggering general search.")
175
+ return Command(goto="general_search_and_select")
198
176
 
199
- # --- Graph Definition ---
200
177
 
201
- workflow = StateGraph(AgentState)
178
+ logger.success(f"Selected {len(selected_tool_ids)} tools for the final plan: {selected_tool_ids}")
202
179
 
203
- workflow.add_node("decompose_task", _decompose_task)
204
- workflow.add_node("resolve_sub_tasks", _resolve_sub_tasks)
205
- workflow.add_node("consolidate_plan", _consolidate_plan) # NEW NODE
206
- workflow.add_node("handle_planning_failure", _handle_planning_failure)
180
+ final_plan = defaultdict(list)
181
+ for tool_id in selected_tool_ids:
182
+ if "__" in tool_id:
183
+ app_id, tool_name = tool_id.split("__", 1)
184
+ final_plan[app_id].append(tool_name)
207
185
 
208
- workflow.set_entry_point("decompose_task")
186
+ sorted_final_plan = {app_id: sorted(tools) for app_id, tools in final_plan.items()}
187
+ return Command(update={"execution_plan": sorted_final_plan}, goto=END)
209
188
 
210
- def should_continue(state: AgentState):
211
- if not state.get("sub_tasks"): # Resolution failed or succeeded
212
- if state.get("execution_plan"):
213
- return "consolidate_plan" # MODIFIED: Go to consolidate on success
214
- elif state["decomposition_attempts"] >= MAX_DECOMPOSITION_ATTEMPTS:
215
- return "handle_planning_failure"
216
- else:
217
- return "decompose_task" # Re-try decomposition
189
+ def _handle_planning_failure(state: AgentState) -> Command:
190
+ """Handles cases where tool search or selection fails by logging the final error message."""
191
+ if messages := state.get("messages"):
192
+ last_message = messages[-1].content
193
+ logger.error(f"Planning failed. Final message: {last_message}")
218
194
  else:
219
- return "resolve_sub_tasks"
195
+ logger.error("Planning failed with no specific message.")
196
+ return Command(goto=END)
220
197
 
221
- workflow.add_conditional_edges("decompose_task", lambda s: "resolve_sub_tasks")
222
- workflow.add_conditional_edges("resolve_sub_tasks", should_continue)
198
+ workflow = StateGraph(AgentState)
199
+ workflow.add_node("search_for_tools", _search_for_tools)
200
+ workflow.add_node("general_search_and_select", _general_search_and_select)
201
+ workflow.add_node("select_tools_for_plan", _select_tools_for_plan)
202
+ workflow.add_node("handle_failure", _handle_planning_failure)
223
203
 
224
- workflow.add_edge("consolidate_plan", END) # NEW EDGE
225
- workflow.add_edge("handle_planning_failure", END)
204
+ workflow.set_entry_point("search_for_tools")
226
205
 
227
- return workflow.compile()
206
+ return workflow.compile()
@@ -9,6 +9,8 @@ from rich.markdown import Markdown
9
9
  from rich.panel import Panel
10
10
  from rich.prompt import Prompt
11
11
  from rich.table import Table
12
+ from pydantic import ValidationError
13
+ from requests import JSONDecodeError
12
14
 
13
15
 
14
16
  class RichCLI:
@@ -143,3 +145,66 @@ def get_message_text(message: BaseMessage):
143
145
  logger.error(f"Error getting message text: {e}")
144
146
  logger.error(f"Message: {message}")
145
147
  raise e
148
+
149
+
150
+ def filter_retry_on(exc: Exception) -> bool:
151
+ import httpx
152
+ import requests
153
+
154
+ # Transient local/network issues and parsing hiccups
155
+ if isinstance(
156
+ exc,
157
+ (
158
+ TimeoutError,
159
+ ConnectionError,
160
+ JSONDecodeError,
161
+ ValidationError,
162
+ ),
163
+ ):
164
+ return True
165
+
166
+ # httpx transient request-layer errors
167
+ if isinstance(
168
+ exc,
169
+ (
170
+ httpx.TimeoutException,
171
+ httpx.ConnectError,
172
+ httpx.ReadError,
173
+ ),
174
+ ):
175
+ return True
176
+
177
+ if isinstance(exc, (requests.Timeout, requests.ConnectionError)):
178
+ return True
179
+
180
+ # HTTP status based retries: 408 (timeout), 429 (rate limit), and 5xx
181
+ if isinstance(exc, httpx.HTTPStatusError):
182
+ status = exc.response.status_code
183
+ return status == 408 or status == 429 or 500 <= status < 600
184
+ if isinstance(exc, requests.HTTPError):
185
+ if exc.response is None:
186
+ return True
187
+ status = exc.response.status_code
188
+ return status == 408 or status == 429 or 500 <= status < 600
189
+
190
+ if isinstance(
191
+ exc,
192
+ (
193
+ ValueError,
194
+ TypeError,
195
+ ArithmeticError,
196
+ ImportError,
197
+ LookupError,
198
+ NameError,
199
+ SyntaxError,
200
+ RuntimeError,
201
+ ReferenceError,
202
+ StopIteration,
203
+ StopAsyncIteration,
204
+ OSError,
205
+ ),
206
+ ):
207
+ return False
208
+
209
+ # Default: do not retry unknown exceptions
210
+ return False
@@ -1,10 +1,10 @@
1
1
  import os
2
2
  from typing import Any, Literal, TypedDict
3
3
 
4
- from dotenv import load_dotenv
5
4
  import httpx
6
- from universal_mcp.applications.application import BaseApplication
5
+ from dotenv import load_dotenv
7
6
  from markitdown import MarkItDown
7
+ from universal_mcp.applications.application import BaseApplication
8
8
 
9
9
  load_dotenv()
10
10
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp-agents
3
- Version: 0.1.13
3
+ Version: 0.1.14
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
@@ -1,23 +1,24 @@
1
1
  universal_mcp/agents/__init__.py,sha256=kM4mC6Pf6lmaaZF1volo7VtKgA8FDyzb1sNenpB7Ulk,1244
2
- universal_mcp/agents/base.py,sha256=le5vy02eXN15u8ntHWCu6Y-LgOmE_DA8tWmPOmLohAk,6908
2
+ universal_mcp/agents/base.py,sha256=xQ_zMHTvULaiE4LiE7IfhrLmlBtvJ5zVE9Rq2nJ4Hm4,6976
3
3
  universal_mcp/agents/cli.py,sha256=AG9e4iSX3GazT537573YrYT1wSaZYOr42rrYQ7xP3YA,1016
4
4
  universal_mcp/agents/hil.py,sha256=_5PCK6q0goGm8qylJq44aSp2MadP-yCPvhOJYKqWLMo,3808
5
5
  universal_mcp/agents/llm.py,sha256=hVRwjZs3MHl5_3BWedmurs2Jt1oZDfFX0Zj9F8KH7fk,1787
6
6
  universal_mcp/agents/react.py,sha256=8XQvJ0HLVgc-K0qn9Ml48WGcgUGuIKtL67HatlT6Da0,3334
7
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
8
+ universal_mcp/agents/utils.py,sha256=pZFAqH__csH-gRLlTOnuU8kNix3_t5eC3yIk926FWpQ,6898
9
+ universal_mcp/agents/bigtool/__init__.py,sha256=mZG8dsaCVyKlm82otxtiTA225GIFLUCUUYPEIPF24uw,2299
10
10
  universal_mcp/agents/bigtool/__main__.py,sha256=a15OUoqPR938x7eseWtxu0aLX7lRS3nu8k5Ks3giUY4,472
11
- universal_mcp/agents/bigtool/agent.py,sha256=anXjMgFWS95RYrnQsZJJxYq8HoCB6z9Poi-Uv5fhPis,266
11
+ universal_mcp/agents/bigtool/agent.py,sha256=FsWa7S00RHmzrLd2ms_aR4wJ09h_KFJ8DDzFnADKJOQ,251
12
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
13
+ universal_mcp/agents/bigtool/graph.py,sha256=SE1IavwrSciIWsb_miekEh3H46UJWpeIOkcp7CsRG_w,5764
14
+ universal_mcp/agents/bigtool/prompts.py,sha256=Joi5mCzZX63aM_6eBrMOKuNRHjTkceVIibSsGBGqhYE,2041
15
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
16
+ universal_mcp/agents/bigtool/tools.py,sha256=vlydQQnITS7OPaSzyILf3czrgO3_fEB-i7zItKcEWjU,6377
17
+ universal_mcp/agents/builder/__main__.py,sha256=mGw5xHK53qNGgs6q7s2PPPPWozWPDKXD2AZGQTqCDQc,10750
18
+ universal_mcp/agents/builder/builder.py,sha256=uhUOH6S7F2iWDhpNOpfzVUT1VB0MQnmcEkLPYJs65gY,8151
19
+ universal_mcp/agents/builder/helper.py,sha256=G6IoXB433zAwfOGhDXOIGsf_EC9bbOSGN8WPWGSzmrk,2700
20
+ universal_mcp/agents/builder/prompts.py,sha256=k0IfhWZ10zou3I2e3kczxf2zpY2AsPshjeY_h5oQ1KA,2673
21
+ universal_mcp/agents/builder/state.py,sha256=nJrH-ChSpqv-fsXhvP4UkouVdQw0ji376uFM0aJ91D0,921
21
22
  universal_mcp/agents/codeact/__init__.py,sha256=rLE8gvOo5H4YSr71DRq76b3RV3uuotxuAy_VnBVaVwk,60
22
23
  universal_mcp/agents/codeact/__main__.py,sha256=W2cHXRwH1dZG3ETIkMwUqA_d62K3IctHP-FDZWDjxdw,1067
23
24
  universal_mcp/agents/codeact/agent.py,sha256=sKZWokTHcuL68Y6SNyaaHe6_XkWxaIq36TrNmPJfQto,9762
@@ -28,16 +29,17 @@ universal_mcp/agents/codeact/state.py,sha256=WTPfpxDlGRnlr5tZuXMg_KU7GS7TZbnrIKs
28
29
  universal_mcp/agents/codeact/utils.py,sha256=JUbT_HYGS_D1BzmzoVpORIe7SGur1KgJguTZ_1tZ4JY,1918
29
30
  universal_mcp/agents/codeact0/__init__.py,sha256=rLE8gvOo5H4YSr71DRq76b3RV3uuotxuAy_VnBVaVwk,60
30
31
  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/agent.py,sha256=WmCHhhGN1q2Q1s14FN2kaNBEIVlYRoMpWJOqQEYjaBo,6538
32
33
  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
34
+ universal_mcp/agents/codeact0/langgraph_agent.py,sha256=8-Y3-OOEpY_XLcnjvFX-KyAQA5SzBjya3YBrrV9Qh_8,652
35
+ universal_mcp/agents/codeact0/llm_tool.py,sha256=9xgRylNZo3lya33L3iqS7yipJYaHCeUEEBCfjUp71vc,13821
36
+ universal_mcp/agents/codeact0/prompts.py,sha256=VT3v61JkAvZwjQoxwKR3QsYdkbyd_1E8bIm3oABNfMg,7512
35
37
  universal_mcp/agents/codeact0/sandbox.py,sha256=tkrhQoV7sAIT5rtd5kpNYEgDz4y82WSHKpMGE6bqthE,3108
36
38
  universal_mcp/agents/codeact0/state.py,sha256=kcoCoz-kd0Ukw1YNGDHNggixCK34KzMl2t6GztakSo4,412
37
- universal_mcp/agents/codeact0/utils.py,sha256=QIT4XIUln9o3H7MOmMen7i-dhDgvM8q0p--oy9Kbkss,15345
39
+ universal_mcp/agents/codeact0/utils.py,sha256=6yeUVsB2CkcrutHjfpMI3KsMJkVpFj3Piag6A2IG0Jc,14733
38
40
  universal_mcp/agents/codeact0/usecases/1-unsubscribe.yaml,sha256=DiChHW-mNOcaaiec7f_04_A0Xyf9a2ihzXiPA9-Fw1I,239
39
41
  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
42
+ universal_mcp/agents/codeact0/usecases/11-github.yaml,sha256=iu4-vlvopPct9vfAseElLXPUAWN5sMVri4oMbDYlA4s,672
41
43
  universal_mcp/agents/codeact0/usecases/2-reddit.yaml,sha256=-dstvFMjsuP9UQM3B_G1HBn7ImNIMxSCNbITpfwgwAY,2525
42
44
  universal_mcp/agents/codeact0/usecases/2.1-instructions.md,sha256=0gQBY_A3jT_lgKNiu6GdyEYupbX0Xz2unlCoTQwv-Es,5098
43
45
  universal_mcp/agents/codeact0/usecases/2.2-instructions.md,sha256=Cx-VkcC55MrgFxlMBMBCD83jEge_yZgBWKwtuK1OPFc,4458
@@ -53,11 +55,12 @@ universal_mcp/agents/planner/__main__.py,sha256=OfhTfYDZK_ZUfc8sX-Sa6TWk-dNqD2rl
53
55
  universal_mcp/agents/planner/graph.py,sha256=70hhIoEZOcYojpiyVSCedgYpnmxVP7aqdn8s6VBu-D4,3228
54
56
  universal_mcp/agents/planner/prompts.py,sha256=_JoHqiAvswtqCDu90AGUHmfsu8eWE1-_yI4LLn3pqMU,657
55
57
  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/agents/shared/__main__.py,sha256=u6ezelmT7poaMRr5AfKp3uXGOV7zEwj017j_iAeUcZs,1450
59
+ universal_mcp/agents/shared/prompts.py,sha256=-gNyfhrJFtPEG4YeoJRYKmkmRHsI8a5f5ssxk_AWrOI,3760
60
+ universal_mcp/agents/shared/tool_node.py,sha256=syydbbKFMpu9Gg45yGP-kpN5KvD2oSRXWtqo1YpD6gY,9038
58
61
  universal_mcp/applications/llm/__init__.py,sha256=xnpxq4Wl_pevvwtSUtEwcty8_d61ywO1V2YnEXyCREY,46
59
62
  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,,
63
+ universal_mcp/applications/ui/app.py,sha256=c7OkZsO2fRtndgAzAQbKu-1xXRuRp9Kjgml57YD2NR4,9459
64
+ universal_mcp_agents-0.1.14.dist-info/METADATA,sha256=6a7VjzkIfQdp6b4UfQVAMKFzg2qE8wszgkocVRgqNok,878
65
+ universal_mcp_agents-0.1.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
66
+ universal_mcp_agents-0.1.14.dist-info/RECORD,,