universal-mcp-agents 0.1.23rc1__py3-none-any.whl → 0.1.23rc3__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.

@@ -9,7 +9,7 @@ from universal_mcp.agents.simple import SimpleAgent
9
9
 
10
10
 
11
11
  def get_agent(
12
- agent_name: Literal["react", "simple", "builder", "bigtool", "codeact-script", "codeact-repl"],
12
+ agent_name: Literal["react", "simple", "builder", "bigtool", "codeact-repl"],
13
13
  ):
14
14
  if agent_name == "react":
15
15
  return ReactAgent
@@ -23,7 +23,7 @@ def get_agent(
23
23
  return CodeActPlaybookAgent
24
24
  else:
25
25
  raise ValueError(
26
- f"Unknown agent: {agent_name}. Possible values: react, simple, builder, bigtool, codeact-script, codeact-repl"
26
+ f"Unknown agent: {agent_name}. Possible values: react, simple, builder, bigtool, codeact-repl"
27
27
  )
28
28
 
29
29
 
@@ -66,9 +66,9 @@ class BaseAgent:
66
66
  ):
67
67
  if event == "messages" and isinstance(meta, (tuple, list)) and len(meta) == 2:
68
68
  payload, meta_dict = meta
69
- is_playbook = isinstance(meta_dict, dict) and meta_dict.get("langgraph_node") == "playbook"
69
+ is_agent_builder = isinstance(meta_dict, dict) and meta_dict.get("langgraph_node") == "agent_builder"
70
70
  additional_kwargs = getattr(payload, "additional_kwargs", {}) or {}
71
- if is_playbook and not additional_kwargs.get("stream"):
71
+ if is_agent_builder and not additional_kwargs.get("stream"):
72
72
  continue
73
73
  if isinstance(payload, AIMessageChunk):
74
74
  last_ai_chunk = payload
@@ -25,7 +25,7 @@ def run(name: str = "react"):
25
25
  client = AgentrClient()
26
26
  params = {
27
27
  "instructions": "You are a helpful assistant",
28
- "model": "anthropic/claude-sonnet-4-20250514",
28
+ "model": "azure/gpt-4.1",
29
29
  "registry": AgentrRegistry(client=client),
30
30
  "memory": MemorySaver(),
31
31
  }
@@ -13,15 +13,12 @@ async def main():
13
13
  agent = CodeActPlaybookAgent(
14
14
  name="CodeAct Agent",
15
15
  instructions="Be very concise in your answers.",
16
- model="anthropic:claude-4-sonnet-20250514",
17
- tools={"google_mail": ["list_messages"]},
16
+ model="azure/gpt-4.1",
18
17
  registry=AgentrRegistry(),
19
18
  memory=memory,
20
19
  )
21
20
  print("Starting agent...")
22
- result = await agent.invoke(
23
- user_input="Fetch unsubscribe links from my Gmail inbox for promo emails I have received in the last 7 days"
24
- )
21
+ result = await agent.invoke(user_input="load all the tools of reddit which can be used to search subreddit")
25
22
  print(messages_to_list(result["messages"]))
26
23
 
27
24
 
@@ -1,35 +1,36 @@
1
+ import copy
1
2
  import json
2
3
  import re
3
- from typing import Literal, cast
4
4
  import uuid
5
+ from typing import Literal, cast
5
6
 
7
+ from langchain_anthropic import ChatAnthropic
6
8
  from langchain_core.messages import AIMessage, ToolMessage
7
9
  from langchain_core.tools import StructuredTool
8
10
  from langgraph.checkpoint.base import BaseCheckpointSaver
9
11
  from langgraph.graph import START, StateGraph
10
12
  from langgraph.types import Command, RetryPolicy, StreamWriter
11
13
  from universal_mcp.tools.registry import ToolRegistry
12
- from universal_mcp.types import ToolConfig, ToolFormat
14
+ from universal_mcp.types import ToolFormat
13
15
 
14
16
  from universal_mcp.agents.base import BaseAgent
15
17
  from universal_mcp.agents.codeact0.llm_tool import smart_print
16
18
  from universal_mcp.agents.codeact0.prompts import (
17
- PLAYBOOK_GENERATING_PROMPT,
18
- PLAYBOOK_PLANNING_PROMPT,
19
- PLAYBOOK_META_PROMPT,
19
+ AGENT_BUILDER_GENERATING_PROMPT,
20
+ AGENT_BUILDER_META_PROMPT,
21
+ AGENT_BUILDER_PLANNING_PROMPT,
20
22
  create_default_prompt,
21
23
  )
22
24
  from universal_mcp.agents.codeact0.sandbox import eval_unsafe, execute_ipython_cell, handle_execute_ipython_cell
23
- from universal_mcp.agents.codeact0.state import CodeActState, PlaybookCode, PlaybookPlan, PlaybookMeta
25
+ from universal_mcp.agents.codeact0.state import CodeActState, AgentBuilderCode, AgentBuilderMeta, AgentBuilderPlan
24
26
  from universal_mcp.agents.codeact0.tools import (
25
27
  create_meta_tools,
26
- enter_playbook_mode,
28
+ enter_agent_builder_mode,
27
29
  get_valid_tools,
28
30
  )
29
- from universal_mcp.agents.codeact0.utils import add_tools
31
+ from universal_mcp.agents.codeact0.utils import build_anthropic_cache_message, get_connected_apps_string
30
32
  from universal_mcp.agents.llm import load_chat_model
31
33
  from universal_mcp.agents.utils import convert_tool_ids_to_dict, filter_retry_on, get_message_text
32
- from universal_mcp.agents.codeact0.utils import get_connected_apps_string
33
34
 
34
35
 
35
36
  class CodeActPlaybookAgent(BaseAgent):
@@ -40,7 +41,7 @@ class CodeActPlaybookAgent(BaseAgent):
40
41
  model: str,
41
42
  memory: BaseCheckpointSaver | None = None,
42
43
  registry: ToolRegistry | None = None,
43
- playbook_registry: object | None = None,
44
+ agent_builder_registry: object | None = None,
44
45
  sandbox_timeout: int = 20,
45
46
  **kwargs,
46
47
  ):
@@ -52,18 +53,19 @@ class CodeActPlaybookAgent(BaseAgent):
52
53
  **kwargs,
53
54
  )
54
55
  self.model_instance = load_chat_model(model)
55
- self.playbook_model_instance = load_chat_model("azure/gpt-4.1")
56
+ self.agent_builder_model_instance = load_chat_model("azure/gpt-4.1")
56
57
  self.registry = registry
57
- self.playbook_registry = playbook_registry
58
- self.playbook = playbook_registry.get_agent() if playbook_registry else None
59
- self.tools_config = self.playbook.tools if self.playbook else {}
58
+ self.agent_builder_registry = agent_builder_registry
59
+ self.agent = agent_builder_registry.get_agent() if agent_builder_registry else None
60
+ self.tools_config = self.agent.tools if self.agent else {}
60
61
  self.eval_fn = eval_unsafe
61
62
  self.sandbox_timeout = sandbox_timeout
62
- self.default_tools = {
63
+ self.default_tools_config = {
63
64
  "llm": ["generate_text", "classify_data", "extract_data", "call_llm"],
64
65
  }
65
- add_tools(self.tools_config, self.default_tools)
66
-
66
+ self.final_instructions = ""
67
+ self.tools_context = {}
68
+ self.exported_tools = []
67
69
 
68
70
  async def _build_graph(self):
69
71
  meta_tools = create_meta_tools(self.registry)
@@ -71,64 +73,86 @@ class CodeActPlaybookAgent(BaseAgent):
71
73
  self.additional_tools = [
72
74
  t if isinstance(t, StructuredTool) else StructuredTool.from_function(t) for t in additional_tools
73
75
  ]
76
+
74
77
  if self.tools_config:
75
- # Convert dict format to list format if needed
76
78
  if isinstance(self.tools_config, dict):
77
79
  self.tools_config = [
78
80
  f"{provider}__{tool}" for provider, tools in self.tools_config.items() for tool in tools
79
81
  ]
80
- if not self.registry:
81
- raise ValueError("Tools are configured but no registry is provided")
82
+ if not self.registry:
83
+ raise ValueError("Tools are configured but no registry is provided")
84
+ await self.registry.export_tools(self.tools_config, ToolFormat.LANGCHAIN)
85
+
86
+ await self.registry.export_tools(self.default_tools_config, ToolFormat.LANGCHAIN)
82
87
 
83
88
  async def call_model(state: CodeActState) -> Command[Literal["execute_tools"]]:
84
- messages = [{"role": "system", "content": self.final_instructions}] + state["messages"]
85
-
86
- # Run the model and potentially loop for reflection
87
- model_with_tools = self.model_instance.bind_tools(
88
- tools=[
89
- execute_ipython_cell,
90
- enter_playbook_mode,
91
- meta_tools["search_functions"],
92
- meta_tools["load_functions"],
93
- ],
94
- tool_choice="auto",
95
- )
89
+ """This node now only ever binds the four meta-tools to the LLM."""
90
+ messages = build_anthropic_cache_message(self.final_instructions) + state["messages"]
91
+
92
+ agent_facing_tools = [
93
+ execute_ipython_cell,
94
+ enter_agent_builder_mode,
95
+ meta_tools["search_functions"],
96
+ meta_tools["load_functions"],
97
+ ]
98
+
99
+ if isinstance(self.model_instance, ChatAnthropic):
100
+ model_with_tools = self.model_instance.bind_tools(
101
+ tools=agent_facing_tools,
102
+ tool_choice="auto",
103
+ cache_control={"type": "ephemeral", "ttl": "1h"},
104
+ )
105
+ if isinstance(messages[-1].content, str):
106
+ pass
107
+ else:
108
+ last = copy.deepcopy(messages[-1])
109
+ last.content[-1]["cache_control"] = {"type": "ephemeral", "ttl": "5m"}
110
+ messages[-1] = last
111
+ else:
112
+ model_with_tools = self.model_instance.bind_tools(
113
+ tools=agent_facing_tools,
114
+ tool_choice="auto",
115
+ )
116
+
96
117
  response = cast(AIMessage, model_with_tools.invoke(messages))
97
118
  if response.tool_calls:
98
119
  return Command(goto="execute_tools", update={"messages": [response]})
99
120
  else:
100
121
  return Command(update={"messages": [response], "model_with_tools": model_with_tools})
101
122
 
102
- async def execute_tools(state: CodeActState) -> Command[Literal["call_model", "playbook"]]:
123
+ async def execute_tools(state: CodeActState) -> Command[Literal["call_model", "agent_builder"]]:
103
124
  """Execute tool calls"""
104
125
  last_message = state["messages"][-1]
105
126
  tool_calls = last_message.tool_calls if isinstance(last_message, AIMessage) else []
106
127
 
107
128
  tool_messages = []
108
129
  new_tool_ids = []
130
+ tool_result = ""
109
131
  ask_user = False
110
132
  ai_msg = ""
111
- tool_result = ""
112
133
  effective_previous_add_context = state.get("add_context", {})
113
134
  effective_existing_context = state.get("context", {})
135
+ # logging.info(f"Initial new_tool_ids_for_context: {new_tool_ids_for_context}")
114
136
 
115
137
  for tool_call in tool_calls:
138
+ tool_name = tool_call["name"]
139
+ tool_args = tool_call["args"]
116
140
  try:
117
- if tool_call["name"] == "enter_playbook_mode":
141
+ if tool_name == "enter_agent_builder_mode":
118
142
  tool_message = ToolMessage(
119
- content=json.dumps("Entered Playbook Mode."),
143
+ content=json.dumps("Entered Agent Builder Mode."),
120
144
  name=tool_call["name"],
121
145
  tool_call_id=tool_call["id"],
122
146
  )
123
147
  return Command(
124
- goto="playbook",
125
- update={"playbook_mode": "planning", "messages": [tool_message]}, # Entered Playbook mode
148
+ goto="agent_builder",
149
+ update={"agent_builder_mode": "planning", "messages": [tool_message]}, # Entered Agent Builder mode
126
150
  )
127
- elif tool_call["name"] == "execute_ipython_cell":
151
+ elif tool_name == "execute_ipython_cell":
128
152
  code = tool_call["args"]["snippet"]
129
153
  output, new_context, new_add_context = await handle_execute_ipython_cell(
130
154
  code,
131
- self.tools_context,
155
+ self.tools_context, # Uses the dynamically updated context
132
156
  self.eval_fn,
133
157
  effective_previous_add_context,
134
158
  effective_existing_context,
@@ -136,23 +160,23 @@ class CodeActPlaybookAgent(BaseAgent):
136
160
  effective_existing_context = new_context
137
161
  effective_previous_add_context = new_add_context
138
162
  tool_result = output
139
- elif tool_call["name"] == "load_functions": # Handle load_functions separately
140
- valid_tools, unconnected_links = await get_valid_tools(
141
- tool_ids=tool_call["args"]["tool_ids"], registry=self.registry
142
- )
163
+ elif tool_name == "load_functions":
164
+ # The tool now does all the work of validation and formatting.
165
+ tool_result, new_context_for_sandbox, valid_tools, unconnected_links = await meta_tools["load_functions"].ainvoke(tool_args)
166
+ # We still need to update the sandbox context for `execute_ipython_cell`
143
167
  new_tool_ids.extend(valid_tools)
144
- # Create tool message response
145
- tool_result = f"Successfully loaded {len(valid_tools)} tools: {valid_tools}"
146
- links = "\n".join(unconnected_links)
147
- if links:
168
+ if new_tool_ids:
169
+ self.tools_context.update(new_context_for_sandbox)
170
+ if unconnected_links:
148
171
  ask_user = True
149
- ai_msg = f"Please login to the following app(s) using the following links and let me know in order to proceed:\n {links} "
150
- elif tool_call["name"] == "search_functions":
151
- tool_result = await meta_tools["search_functions"].ainvoke(tool_call["args"])
172
+ ai_msg = f"Please login to the following app(s) using the following links and let me know in order to proceed:\n {unconnected_links} "
173
+
174
+ elif tool_name == "search_functions":
175
+ tool_result = await meta_tools["search_functions"].ainvoke(tool_args)
152
176
  else:
153
177
  raise Exception(
154
178
  f"Unexpected tool call: {tool_call['name']}. "
155
- "tool calls must be one of 'enter_playbook_mode', 'execute_ipython_cell', 'load_functions', or 'search_functions'. For using functions, call them in code using 'execute_ipython_cell'."
179
+ "tool calls must be one of 'enter_agent_builder_mode', 'execute_ipython_cell', 'load_functions', or 'search_functions'. For using functions, call them in code using 'execute_ipython_cell'."
156
180
  )
157
181
  except Exception as e:
158
182
  tool_result = str(e)
@@ -163,13 +187,7 @@ class CodeActPlaybookAgent(BaseAgent):
163
187
  tool_call_id=tool_call["id"],
164
188
  )
165
189
  tool_messages.append(tool_message)
166
-
167
- if new_tool_ids:
168
- self.tools_config.extend(new_tool_ids)
169
- self.exported_tools = await self.registry.export_tools(new_tool_ids, ToolFormat.LANGCHAIN)
170
- self.final_instructions, self.tools_context = create_default_prompt(
171
- self.exported_tools, self.additional_tools, self.instructions, await get_connected_apps_string(self.registry), self.playbook
172
- )
190
+
173
191
  if ask_user:
174
192
  tool_messages.append(AIMessage(content=ai_msg))
175
193
  return Command(
@@ -191,27 +209,37 @@ class CodeActPlaybookAgent(BaseAgent):
191
209
  },
192
210
  )
193
211
 
194
- def playbook(state: CodeActState, writer: StreamWriter) -> Command[Literal["call_model"]]:
195
- playbook_mode = state.get("playbook_mode")
196
- if playbook_mode == "planning":
212
+ def agent_builder(state: CodeActState, writer: StreamWriter) -> Command[Literal["call_model"]]:
213
+ agent_builder_mode = state.get("agent_builder_mode")
214
+ if agent_builder_mode == "planning":
197
215
  plan_id = str(uuid.uuid4())
198
- writer({
199
- "type": "custom",
200
- id: plan_id,
201
- "name": "planning",
202
- "data": {"update": bool(self.playbook)}
203
- })
204
- planning_instructions = self.instructions + PLAYBOOK_PLANNING_PROMPT
216
+ writer({"type": "custom", id: plan_id, "name": "planning", "data": {"update": bool(self.agent)}})
217
+ planning_instructions = self.instructions + AGENT_BUILDER_PLANNING_PROMPT
205
218
  messages = [{"role": "system", "content": planning_instructions}] + state["messages"]
206
219
 
207
- model_with_structured_output = self.playbook_model_instance.with_structured_output(PlaybookPlan)
220
+ model_with_structured_output = self.agent_builder_model_instance.with_structured_output(AgentBuilderPlan)
208
221
  response = model_with_structured_output.invoke(messages)
209
- plan = cast(PlaybookPlan, response)
210
-
222
+ plan = cast(AgentBuilderPlan, response)
223
+
211
224
  writer({"type": "custom", id: plan_id, "name": "planning", "data": {"plan": plan.steps}})
212
- return Command(update={"messages": [AIMessage(content=json.dumps(plan.dict()), additional_kwargs={"type": "planning", "plan": plan.steps, "update": bool(self.playbook)})], "playbook_mode": "confirming", "plan": plan.steps})
225
+ return Command(
226
+ update={
227
+ "messages": [
228
+ AIMessage(
229
+ content=json.dumps(plan.model_dump()),
230
+ additional_kwargs={
231
+ "type": "planning",
232
+ "plan": plan.steps,
233
+ "update": bool(self.agent),
234
+ },
235
+ )
236
+ ],
237
+ "agent_builder_mode": "confirming",
238
+ "plan": plan.steps,
239
+ }
240
+ )
213
241
 
214
- elif playbook_mode == "confirming":
242
+ elif agent_builder_mode == "confirming":
215
243
  # Deterministic routing based on three exact button inputs from UI
216
244
  user_text = ""
217
245
  for m in reversed(state["messages"]):
@@ -227,139 +255,152 @@ class CodeActPlaybookAgent(BaseAgent):
227
255
  if t == "yes, this is great":
228
256
  self.meta_id = str(uuid.uuid4())
229
257
  name, description = None, None
230
- if self.playbook:
258
+ if self.agent:
231
259
  # Update flow: use existing name/description and do not re-generate
232
- name = getattr(self.playbook, "name", None)
233
- description = getattr(self.playbook, "description", None)
234
- writer({
235
- "type": "custom",
236
- id: self.meta_id,
237
- "name": "generating",
238
- "data": {
239
- "update": True,
240
- "name": name,
241
- "description": description,
260
+ name = getattr(self.agent, "name", None)
261
+ description = getattr(self.agent, "description", None)
262
+ writer(
263
+ {
264
+ "type": "custom",
265
+ id: self.meta_id,
266
+ "name": "generating",
267
+ "data": {
268
+ "update": True,
269
+ "name": name,
270
+ "description": description,
271
+ },
242
272
  }
243
- })
273
+ )
244
274
  else:
245
- writer({
246
- "type": "custom",
247
- id: self.meta_id,
248
- "name": "generating",
249
- "data": {"update": False}
250
- })
251
-
252
- meta_instructions = self.instructions + PLAYBOOK_META_PROMPT
275
+ writer({"type": "custom", id: self.meta_id, "name": "generating", "data": {"update": False}})
276
+
277
+ meta_instructions = self.instructions + AGENT_BUILDER_META_PROMPT
253
278
  messages = [{"role": "system", "content": meta_instructions}] + state["messages"]
254
279
 
255
- model_with_structured_output = self.playbook_model_instance.with_structured_output(PlaybookMeta)
280
+ model_with_structured_output = self.agent_builder_model_instance.with_structured_output(AgentBuilderMeta)
256
281
  meta_response = model_with_structured_output.invoke(messages)
257
- meta = cast(PlaybookMeta, meta_response)
282
+ meta = cast(AgentBuilderMeta, meta_response)
258
283
  name, description = meta.name, meta.description
259
284
 
260
285
  # Emit intermediary UI update with created name/description
261
- writer({
262
- "type": "custom",
263
- id: self.meta_id,
264
- "name": "generating",
265
- "data": {"update": False, "name": name, "description": description}
266
- })
267
-
268
- return Command(goto="playbook", update={"playbook_mode": "generating", "playbook_name": name, "playbook_description": description})
286
+ writer(
287
+ {
288
+ "type": "custom",
289
+ id: self.meta_id,
290
+ "name": "generating",
291
+ "data": {"update": False, "name": name, "description": description},
292
+ }
293
+ )
294
+
295
+ return Command(
296
+ goto="agent_builder",
297
+ update={
298
+ "agent_builder_mode": "generating",
299
+ "agent_name": name,
300
+ "agent_description": description,
301
+ },
302
+ )
269
303
  if t == "i would like to modify the plan":
270
- prompt_ai = AIMessage(content="What would you like to change about the plan? Let me know and I'll update the plan accordingly.", additional_kwargs={"stream": "true"})
271
- return Command(update={"playbook_mode": "planning", "messages": [prompt_ai]})
304
+ prompt_ai = AIMessage(
305
+ content="What would you like to change about the plan? Let me know and I'll update the plan accordingly.",
306
+ additional_kwargs={"stream": "true"},
307
+ )
308
+ return Command(update={"agent_builder_mode": "planning", "messages": [prompt_ai]})
272
309
  if t == "let's do something else":
273
- return Command(goto="call_model", update={"playbook_mode": "inactive"})
310
+ return Command(goto="call_model", update={"agent_builder_mode": "inactive"})
274
311
 
275
312
  # Fallback safe default
276
- return Command(goto="call_model", update={"playbook_mode": "inactive"})
313
+ return Command(goto="call_model", update={"agent_builder_mode": "inactive"})
277
314
 
278
- elif playbook_mode == "generating":
279
- generating_instructions = self.instructions + PLAYBOOK_GENERATING_PROMPT
315
+ elif agent_builder_mode == "generating":
316
+ generating_instructions = self.instructions + AGENT_BUILDER_GENERATING_PROMPT
280
317
  messages = [{"role": "system", "content": generating_instructions}] + state["messages"]
281
-
282
- model_with_structured_output = self.playbook_model_instance.with_structured_output(PlaybookCode)
318
+
319
+ model_with_structured_output = self.agent_builder_model_instance.with_structured_output(AgentBuilderCode)
283
320
  response = model_with_structured_output.invoke(messages)
284
- func_code = cast(PlaybookCode, response).code
321
+ func_code = cast(AgentBuilderCode, response).code
285
322
 
286
323
  # Extract function name (handle both regular and async functions)
287
324
  match = re.search(r"^\s*(?:async\s+)?def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(", func_code, re.MULTILINE)
288
325
  if match:
289
326
  function_name = match.group(1)
290
327
  else:
291
- function_name = "generated_playbook"
328
+ function_name = "generated_agent"
292
329
 
293
330
  # Use generated metadata if available
294
- final_name = state.get("playbook_name") or function_name
295
- final_description = state.get("playbook_description") or f"Generated playbook: {function_name}"
331
+ final_name = state.get("agent_name") or function_name
332
+ final_description = state.get("agent_description") or f"Generated agent: {function_name}"
296
333
 
297
334
  # Save or update an Agent using the helper registry
298
335
  try:
299
- if not self.playbook_registry:
300
- raise ValueError("Playbook registry is not configured")
336
+ if not self.agent_builder_registry:
337
+ raise ValueError("AgentBuilder registry is not configured")
301
338
 
302
339
  # Build instructions payload embedding the plan and function code
303
340
  instructions_payload = {
304
- "playbookPlan": state["plan"],
305
- "playbookScript": func_code,
341
+ "plan": state["plan"],
342
+ "script": func_code,
306
343
  }
307
344
 
308
345
  # Convert tool ids list to dict
309
346
  tool_dict = convert_tool_ids_to_dict(state["selected_tool_ids"])
310
347
 
311
- res = self.playbook_registry.upsert_agent(
348
+ res = self.agent_builder_registry.upsert_agent(
312
349
  name=final_name,
313
350
  description=final_description,
314
351
  instructions=instructions_payload,
315
352
  tools=tool_dict,
316
- visibility="private",
317
353
  )
318
354
  except Exception as e:
319
355
  raise e
320
356
 
321
- writer({
322
- "type": "custom",
323
- id: self.meta_id,
324
- "name": "generating",
325
- "data": {
326
- "id": str(res.id),
327
- "update": bool(self.playbook),
328
- "name": final_name,
329
- "description": final_description,
357
+ writer(
358
+ {
359
+ "type": "custom",
360
+ id: self.meta_id,
361
+ "name": "generating",
362
+ "data": {
363
+ "id": str(res.id),
364
+ "update": bool(self.agent),
365
+ "name": final_name,
366
+ "description": final_description,
367
+ },
330
368
  }
331
- })
369
+ )
332
370
  mock_assistant_message = AIMessage(
333
- content=json.dumps(response.dict()),
371
+ content=json.dumps(response.model_dump()),
334
372
  additional_kwargs={
335
373
  "type": "generating",
336
374
  "id": str(res.id),
337
- "update": bool(self.playbook),
375
+ "update": bool(self.agent),
338
376
  "name": final_name,
339
377
  "description": final_description,
340
378
  },
341
379
  )
342
380
 
343
- return Command(
344
- update={"messages": [mock_assistant_message], "playbook_mode": "normal"}
345
- )
381
+ return Command(update={"messages": [mock_assistant_message], "agent_builder_mode": "normal"})
382
+
383
+ async def route_entry(state: CodeActState) -> Literal["call_model", "agent_builder"]:
384
+ """Route to either normal mode or agent builder creation"""
385
+ all_tools = await self.registry.export_tools(state["selected_tool_ids"], ToolFormat.LANGCHAIN)
386
+ # print(all_tools)
346
387
 
347
- async def route_entry(state: CodeActState) -> Literal["call_model", "playbook"]:
348
- """Route to either normal mode or playbook creation"""
349
- self.exported_tools = []
350
- self.tools_config.extend(state.get("selected_tool_ids", []))
351
- self.exported_tools = await self.registry.export_tools(self.tools_config, ToolFormat.LANGCHAIN)
388
+ # Create the initial system prompt and tools_context in one go
352
389
  self.final_instructions, self.tools_context = create_default_prompt(
353
- self.exported_tools, self.additional_tools, self.instructions, await get_connected_apps_string(self.registry), self.playbook
390
+ all_tools,
391
+ self.additional_tools,
392
+ self.instructions,
393
+ await get_connected_apps_string(self.registry),
394
+ self.agent,
395
+ is_initial_prompt=True,
354
396
  )
355
- if state.get("playbook_mode") in ["planning", "confirming", "generating"]:
356
- return "playbook"
397
+ if state.get("agent_builder_mode") in ["planning", "confirming", "generating"]:
398
+ return "agent_builder"
357
399
  return "call_model"
358
400
 
359
401
  agent = StateGraph(state_schema=CodeActState)
360
402
  agent.add_node(call_model, retry_policy=RetryPolicy(max_attempts=3, retry_on=filter_retry_on))
361
- agent.add_node(playbook)
403
+ agent.add_node(agent_builder)
362
404
  agent.add_node(execute_tools)
363
405
  agent.add_conditional_edges(START, route_entry)
364
- # agent.add_edge(START, "call_model")
365
406
  return agent.compile(checkpointer=self.memory)
@@ -3,6 +3,7 @@ import re
3
3
  from collections.abc import Sequence
4
4
 
5
5
  from langchain_core.tools import StructuredTool
6
+
6
7
  from universal_mcp.agents.codeact0.utils import schema_to_signature
7
8
 
8
9
  uneditable_prompt = """
@@ -62,16 +63,16 @@ Rules:
62
63
  - Your final response should contain the complete answer to the user's request in a clear, well-formatted manner that directly addresses what they asked for.
63
64
  """
64
65
 
65
- PLAYBOOK_PLANNING_PROMPT = """Now, you are tasked with creating a reusable playbook from the user's previous workflow.
66
+ AGENT_BUILDER_PLANNING_PROMPT = """Now, you are tasked with creating a reusable agent from the user's previous workflow.
66
67
 
67
68
  TASK: Analyze the conversation history and code execution to create a step-by-step plan for a reusable function.
68
69
  Do not include the searching and loading of tools. Assume that the tools have already been loaded.
69
70
  The plan is a sequence of steps.
70
- You must output a JSON object with a single key "steps", which is a list of strings. Each string is a step in the playbook.
71
+ You must output a JSON object with a single key "steps", which is a list of strings. Each string is a step in the agent.
71
72
 
72
73
  Your plan should:
73
74
  1. Identify the key steps in the workflow
74
- 2. Mark user-specific variables that should become the main playbook function parameters using `variable_name` syntax. Intermediate variables should not be highlighted using ``
75
+ 2. Mark user-specific variables that should become the main agent function parameters using `variable_name` syntax. Intermediate variables should not be highlighted using ``
75
76
  3. Keep the logic generic and reusable
76
77
  4. Be clear and concise
77
78
 
@@ -89,26 +90,26 @@ Now create a plan based on the conversation history. Do not include any other te
89
90
  """
90
91
 
91
92
 
92
- PLAYBOOK_GENERATING_PROMPT = """Now, you are tasked with generating the playbook function.
93
+ AGENT_BUILDER_GENERATING_PROMPT = """Now, you are tasked with generating the agent function.
93
94
  Your response must be ONLY the Python code for the function.
94
95
  Do not include any other text, markdown, or explanations in your response.
95
96
  Your response should start with `def` or `async def`.
96
97
  The function should be a single, complete piece of code that can be executed independently, based on previously executed code snippets that executed correctly.
97
- The parameters of the function should be the same as the final confirmed playbook plan.
98
+ The parameters of the function should be the same as the final confirmed agent plan.
98
99
  """
99
100
 
100
101
 
101
- PLAYBOOK_META_PROMPT = """
102
- You are preparing metadata for a reusable playbook based on the confirmed step-by-step plan.
102
+ AGENT_BUILDER_META_PROMPT = """
103
+ You are preparing metadata for a reusable agent based on the confirmed step-by-step plan.
103
104
 
104
- TASK: Create a concise, human-friendly name and a short description for the playbook.
105
+ TASK: Create a concise, human-friendly name and a short description for the agent.
105
106
 
106
107
  INPUTS:
107
108
  - Conversation context and plan steps will be provided in prior messages
108
109
 
109
110
  REQUIREMENTS:
110
111
  1. Name: 3-6 words, Title Case, no punctuation except hyphens if needed
111
- 2. Description: Single sentence, <= 140 characters, clearly states what the playbook does
112
+ 2. Description: Single sentence, <= 140 characters, clearly states what the agent does
112
113
 
113
114
  OUTPUT: Return ONLY a JSON object with exactly these keys:
114
115
  {
@@ -136,14 +137,21 @@ def create_default_prompt(
136
137
  additional_tools: Sequence[StructuredTool],
137
138
  base_prompt: str | None = None,
138
139
  apps_string: str | None = None,
139
- playbook: object | None = None,
140
+ agent: object | None = None,
141
+ is_initial_prompt: bool = False,
140
142
  ):
141
- system_prompt = uneditable_prompt.strip()
142
- if apps_string:
143
- system_prompt += f"\n\n**Connected external applications (These apps have been logged into by the user):**\n{apps_string}\n\n Use `search_functions` to search for functions you can perform using the above. You can also discover more applications using the `search_functions` tool to find additional tools and integrations, if required.\n"
144
- system_prompt += "\n\nIn addition to the Python Standard Library, you can use the following external functions:\n"
145
-
143
+ if is_initial_prompt:
144
+ system_prompt = uneditable_prompt.strip()
145
+ if apps_string:
146
+ system_prompt += f"\n\n**Connected external applications (These apps have been logged into by the user):**\n{apps_string}\n\n Use `search_functions` to search for functions you can perform using the above. You can also discover more applications using the `search_functions` tool to find additional tools and integrations, if required.\n"
147
+ system_prompt += (
148
+ "\n\nIn addition to the Python Standard Library, you can use the following external functions:\n"
149
+ )
150
+ else:
151
+ system_prompt = ""
152
+
146
153
  tools_context = {}
154
+ tool_definitions = []
147
155
 
148
156
  for tool in tools:
149
157
  if hasattr(tool, "func") and tool.func is not None:
@@ -152,10 +160,11 @@ def create_default_prompt(
152
160
  elif hasattr(tool, "coroutine") and tool.coroutine is not None:
153
161
  tool_callable = tool.coroutine
154
162
  is_async = True
155
- system_prompt += f'''{"async " if is_async else ""}{schema_to_signature(tool.args, tool.name)}:
163
+ tool_definitions.append(
164
+ f'''{"async " if is_async else ""}{schema_to_signature(tool.args, tool.name)}:
156
165
  """{tool.description}"""
157
- ...
158
- '''
166
+ ...'''
167
+ )
159
168
  safe_name = make_safe_function_name(tool.name)
160
169
  tools_context[safe_name] = tool_callable
161
170
 
@@ -166,34 +175,40 @@ def create_default_prompt(
166
175
  elif hasattr(tool, "coroutine") and tool.coroutine is not None:
167
176
  tool_callable = tool.coroutine
168
177
  is_async = True
169
- system_prompt += f'''{"async " if is_async else ""}def {tool.name} {str(inspect.signature(tool_callable))}:
178
+ tool_definitions.append(
179
+ f'''{"async " if is_async else ""}def {tool.name} {str(inspect.signature(tool_callable))}:
170
180
  """{tool.description}"""
171
- ...
172
- '''
181
+ ...'''
182
+ )
173
183
  safe_name = make_safe_function_name(tool.name)
174
184
  tools_context[safe_name] = tool_callable
175
185
 
176
- if base_prompt and base_prompt.strip():
177
- system_prompt += f"Your goal is to perform the following task:\n\n{base_prompt}"
178
-
179
- # Append existing playbook (plan + code) if provided
180
- try:
181
- if playbook and hasattr(playbook, "instructions"):
182
- pb = playbook.instructions or {}
183
- plan = pb.get("playbookPlan")
184
- code = pb.get("playbookScript")
185
- if plan or code:
186
- system_prompt += "\n\nExisting Playbook Provided:\n"
187
- if plan:
188
- if isinstance(plan, list):
189
- plan_block = "\n".join(f"- {str(s)}" for s in plan)
190
- else:
191
- plan_block = str(plan)
192
- system_prompt += f"Plan Steps:\n{plan_block}\n"
193
- if code:
194
- system_prompt += f"\nScript:\n```python\n{str(code)}\n```\n"
195
- except Exception:
196
- # Silently ignore formatting issues
197
- pass
186
+ system_prompt += "\n".join(tool_definitions)
187
+
188
+ if is_initial_prompt:
189
+ if base_prompt and base_prompt.strip():
190
+ system_prompt += (
191
+ f"\n\nUse the following information/instructions while completing your tasks:\n\n{base_prompt}"
192
+ )
193
+
194
+ # Append existing agent (plan + code) if provided
195
+ try:
196
+ if agent and hasattr(agent, "instructions"):
197
+ pb = agent.instructions or {}
198
+ plan = pb.get("plan")
199
+ code = pb.get("script")
200
+ if plan or code:
201
+ system_prompt += "\n\nExisting Agent Provided:\n"
202
+ if plan:
203
+ if isinstance(plan, list):
204
+ plan_block = "\n".join(f"- {str(s)}" for s in plan)
205
+ else:
206
+ plan_block = str(plan)
207
+ system_prompt += f"Plan Steps:\n{plan_block}\n"
208
+ if code:
209
+ system_prompt += f"\nScript:\n```python\n{str(code)}\n```\n"
210
+ except Exception:
211
+ # Silently ignore formatting issues
212
+ pass
198
213
 
199
214
  return system_prompt, tools_context
@@ -1,19 +1,19 @@
1
- from typing import Annotated, Any, List
1
+ from typing import Annotated, Any
2
2
 
3
3
  from langgraph.prebuilt.chat_agent_executor import AgentState
4
4
  from pydantic import BaseModel, Field
5
5
 
6
6
 
7
- class PlaybookPlan(BaseModel):
8
- steps: List[str] = Field(description="The steps of the playbook.")
7
+ class AgentBuilderPlan(BaseModel):
8
+ steps: list[str] = Field(description="The steps of the agent.")
9
9
 
10
10
 
11
- class PlaybookCode(BaseModel):
12
- code: str = Field(description="The Python code for the playbook.")
11
+ class AgentBuilderCode(BaseModel):
12
+ code: str = Field(description="The Python code for the agent.")
13
13
 
14
14
 
15
- class PlaybookMeta(BaseModel):
16
- name: str = Field(description="Concise, title-cased playbook name (3-6 words).")
15
+ class AgentBuilderMeta(BaseModel):
16
+ name: str = Field(description="Concise, title-cased agent name (3-6 words).")
17
17
  description: str = Field(description="Short, one-sentence description (<= 140 chars).")
18
18
 
19
19
 
@@ -46,13 +46,13 @@ class CodeActState(AgentState):
46
46
  """Dictionary containing the execution context with available tools and variables."""
47
47
  add_context: dict[str, Any]
48
48
  """Dictionary containing the additional context (functions, classes, imports) to be added to the execution context."""
49
- playbook_mode: str | None
50
- """State for the playbook agent."""
49
+ agent_builder_mode: str | None
50
+ """State for the agent builder agent."""
51
51
  selected_tool_ids: Annotated[list[str], _enqueue]
52
52
  """Queue for tools exported from registry"""
53
53
  plan: list[str] | None
54
- """Plan for the playbook agent."""
55
- playbook_name: str | None
56
- """Generated playbook name after confirmation."""
57
- playbook_description: str | None
54
+ """Plan for the agent builder agent."""
55
+ agent_name: str | None
56
+ """Generated agent name after confirmation."""
57
+ agent_description: str | None
58
58
  """Generated short description after confirmation."""
@@ -4,16 +4,18 @@ from collections import defaultdict
4
4
  from typing import Annotated, Any
5
5
 
6
6
  from langchain_core.tools import tool
7
- from loguru import logger
8
7
  from pydantic import Field
9
8
  from universal_mcp.agentr.registry import AgentrRegistry
10
9
  from universal_mcp.types import ToolFormat
11
10
 
11
+ from universal_mcp.agents.codeact0.prompts import create_default_prompt
12
12
 
13
- def enter_playbook_mode():
14
- """Call this function to enter playbook mode. Playbook mode is when the user wants to store a repeated task as a script with some inputs for the future."""
13
+
14
+ def enter_agent_builder_mode():
15
+ """Call this function to enter agent builder mode. Agent builder mode is when the user wants to store a repeated task as a script with some inputs for the future."""
15
16
  return
16
17
 
18
+
17
19
  def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
18
20
  """Create the meta tools for searching and loading tools"""
19
21
 
@@ -99,7 +101,9 @@ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
99
101
  prioritized_app_id_list = [canonical_app_id]
100
102
  else:
101
103
  # 1. Perform an initial broad search for tools.
102
- initial_tool_search_tasks = [registry.search_tools(query=q, distance_threshold=THRESHOLD) for q in queries]
104
+ initial_tool_search_tasks = [
105
+ registry.search_tools(query=q, distance_threshold=THRESHOLD) for q in queries
106
+ ]
103
107
  initial_tool_results = await asyncio.gather(*initial_tool_search_tasks)
104
108
 
105
109
  # 2. Search for relevant apps.
@@ -192,15 +196,44 @@ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
192
196
 
193
197
  @tool
194
198
  async def load_functions(tool_ids: list[str]) -> str:
195
- """Load specific functions by their IDs for use in subsequent steps.
199
+ """
200
+ Loads specified functions and returns their Python signatures and docstrings.
201
+ This makes the functions available for use inside the 'execute_ipython_cell' tool.
202
+ The agent MUST use the returned information to understand how to call the functions correctly.
196
203
 
197
204
  Args:
198
- tool_ids: Function ids in the form 'app__function'. Example: 'google_mail__send_email'
205
+ tool_ids: A list of function IDs in the format 'app__function'. Example: ['google_mail__send_email']
199
206
 
200
207
  Returns:
201
- Confirmation message about loaded functions
208
+ A string containing the signatures and docstrings of the successfully loaded functions,
209
+ ready for the agent to use in its code.
202
210
  """
203
- return f"Successfully loaded {len(tool_ids)} functions: {tool_ids}"
211
+ if not tool_ids:
212
+ return "No tool IDs provided to load."
213
+
214
+ # Step 1: Validate which tools are usable and get login links for others.
215
+ valid_tools, unconnected_links = await get_valid_tools(tool_ids=tool_ids, registry=tool_registry)
216
+
217
+ if not valid_tools:
218
+ return "Error: None of the provided tool IDs could be validated or loaded."
219
+
220
+ # Step 2: Export the schemas of the valid tools.
221
+ all_exported_tools = await tool_registry.export_tools(valid_tools, ToolFormat.LANGCHAIN)
222
+ exported_tools = [tool for tool in all_exported_tools if tool.name in valid_tools]
223
+
224
+
225
+ # Step 3: Build the informational string for the agent.
226
+ tool_definitions, new_tools_context = create_default_prompt(exported_tools, [], is_initial_prompt=False)
227
+
228
+ result_parts = [
229
+ f"Successfully loaded {len(exported_tools)} functions. They are now available for use inside `execute_ipython_cell`:",
230
+ tool_definitions,
231
+ ]
232
+
233
+ response_string = "\n\n".join(result_parts)
234
+ unconnected_links = "\n".join(unconnected_links)
235
+
236
+ return response_string, new_tools_context, valid_tools, unconnected_links
204
237
 
205
238
  @tool
206
239
  async def web_search(query: str) -> dict:
@@ -10,6 +10,26 @@ from universal_mcp.types import ToolConfig
10
10
  MAX_CHARS = 5000
11
11
 
12
12
 
13
+ def build_anthropic_cache_message(text: str, role: str = "system", ttl: str = "1h") -> list[dict[str, Any]]:
14
+ """Build a complete Anthropic cache messages array from text.
15
+
16
+ Returns a list with a single cache message whose content is the
17
+ cached Anthropic content array with ephemeral cache control and TTL.
18
+ """
19
+ return [
20
+ {
21
+ "role": role,
22
+ "content": [
23
+ {
24
+ "type": "text",
25
+ "text": text,
26
+ "cache_control": {"type": "ephemeral", "ttl": ttl},
27
+ }
28
+ ],
29
+ }
30
+ ]
31
+
32
+
13
33
  def add_tools(tool_config: ToolConfig, tools_to_add: ToolConfig):
14
34
  for app_id, new_tools in tools_to_add.items():
15
35
  all_tools = tool_config.get(app_id, []) + new_tools
@@ -375,6 +395,7 @@ def schema_to_signature(schema: dict, func_name: str = "my_function") -> str:
375
395
  param_str = ",\n ".join(params)
376
396
  return f"def {func_name}(\n {param_str},\n):"
377
397
 
398
+
378
399
  def smart_truncate(
379
400
  output: str, max_chars_full: int = 2000, max_lines_headtail: int = 20, summary_threshold: int = 10000
380
401
  ) -> str:
@@ -413,21 +434,21 @@ async def get_connected_apps_string(registry) -> str:
413
434
  """Get a formatted string of connected applications from the registry."""
414
435
  if not registry:
415
436
  return ""
416
-
437
+
417
438
  try:
418
439
  # Get connected apps from registry
419
440
  connections = await registry.list_connected_apps()
420
441
  if not connections:
421
442
  return "No applications are currently connected."
422
-
443
+
423
444
  # Extract app names from connections
424
445
  connected_app_ids = {connection["app_id"] for connection in connections}
425
-
446
+
426
447
  # Format the apps list
427
448
  apps_list = []
428
449
  for app_id in connected_app_ids:
429
450
  apps_list.append(f"- {app_id}")
430
-
451
+
431
452
  return "\n".join(apps_list)
432
453
  except Exception:
433
- return "Unable to retrieve connected applications."
454
+ return "Unable to retrieve connected applications."
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp-agents
3
- Version: 0.1.23rc1
3
+ Version: 0.1.23rc3
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
@@ -12,8 +12,8 @@ Requires-Dist: langchain-google-genai>=2.1.10
12
12
  Requires-Dist: langchain-openai>=0.3.32
13
13
  Requires-Dist: langgraph>=0.6.6
14
14
  Requires-Dist: typer>=0.17.4
15
- Requires-Dist: universal-mcp-applications>=0.1.24
16
- Requires-Dist: universal-mcp>=0.1.24rc25
15
+ Requires-Dist: universal-mcp-applications>=0.1.25
16
+ Requires-Dist: universal-mcp>=0.1.24rc26
17
17
  Provides-Extra: dev
18
18
  Requires-Dist: pre-commit; extra == 'dev'
19
19
  Requires-Dist: ruff; extra == 'dev'
@@ -1,6 +1,6 @@
1
- universal_mcp/agents/__init__.py,sha256=Rh6vKqpwuZ2joC9nzLFHUI1G7jzbwwC3p0mgpH5qRgo,1112
2
- universal_mcp/agents/base.py,sha256=CEnY8y2as_XR311t9v2iqd4DOCSyhpOPOBDcZKNJMpc,7378
3
- universal_mcp/agents/cli.py,sha256=bXdpgxsOMjclm1STHJgx10ocX9EebQ11DrxH0p6KMZk,943
1
+ universal_mcp/agents/__init__.py,sha256=bW7WJopR6YZSLxghLf8nhohhHPWzm0wdGoZlmKDAcZ4,1078
2
+ universal_mcp/agents/base.py,sha256=sSC217rac89dEheeIRZoMSU_nk9UfSfQw-0E1BjNSIc,7393
3
+ universal_mcp/agents/cli.py,sha256=9CG7majpWUz7C6t0d8xr-Sg2ZPKBuQdykTbYS6KIZ3A,922
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
@@ -21,16 +21,16 @@ universal_mcp/agents/builder/helper.py,sha256=8igR1b3Gy_N2u3WxHYKIWzvw7F5BMnfpO2
21
21
  universal_mcp/agents/builder/prompts.py,sha256=8Xs6uzTUHguDRngVMLak3lkXFkk2VV_uQXaDllzP5cI,4670
22
22
  universal_mcp/agents/builder/state.py,sha256=7DeWllxfN-yD6cd9wJ3KIgjO8TctkJvVjAbZT8W_zqk,922
23
23
  universal_mcp/agents/codeact0/__init__.py,sha256=8-fvUo1Sm6dURGI-lW-X3Kd78LqySYbb5NMkNJ4NDwg,76
24
- universal_mcp/agents/codeact0/__main__.py,sha256=_7qSz97YnRgYJTESkALS5_eBIGHiMjA5rhr3IAeBvVo,896
25
- universal_mcp/agents/codeact0/agent.py,sha256=--cAjQ05VntXga2ofpnL9oFXAk6ANZ2SftwtLkQgy00,17844
24
+ universal_mcp/agents/codeact0/__main__.py,sha256=EHW9ePVePEemGI5yMUBc2Mp_JlrP6Apk1liab1y2Rd8,782
25
+ universal_mcp/agents/codeact0/agent.py,sha256=LzCshVduxT8weYbZ2mOyL1-rp4o-8H1ODoZAaONAV6o,19322
26
26
  universal_mcp/agents/codeact0/config.py,sha256=H-1woj_nhSDwf15F63WYn723y4qlRefXzGxuH81uYF0,2215
27
27
  universal_mcp/agents/codeact0/langgraph_agent.py,sha256=8nz2wq-LexImx-l1y9_f81fK72IQetnCeljwgnduNGY,420
28
28
  universal_mcp/agents/codeact0/llm_tool.py,sha256=-pAz04OrbZ_dJ2ueysT1qZd02DrbLY4EbU0tiuF_UNU,798
29
- universal_mcp/agents/codeact0/prompts.py,sha256=Gk6WTx2X7IOlbC3wYtGwKWDoeUtgpkMeiyfhj6LEq2I,11221
29
+ universal_mcp/agents/codeact0/prompts.py,sha256=PEBPHvw4yItNVMAaaVUfNVhp53iijxs283Ln6xhew-4,11623
30
30
  universal_mcp/agents/codeact0/sandbox.py,sha256=Xw4tbUV_6haYIZZvteJi6lIYsW6ni_3DCRCOkslTKgM,4459
31
- universal_mcp/agents/codeact0/state.py,sha256=co3BZBuMIt1FP2qzgsbsLkyKbddCG1ieKyAw9TAskSU,1944
32
- universal_mcp/agents/codeact0/tools.py,sha256=1EBStJQQQCuNeWOqt0VP-XFiT0fE4oCR9nvUiwzRhe4,13164
33
- universal_mcp/agents/codeact0/utils.py,sha256=PgisAxmqYIzimc4lFA1nV-R7gxkICIrtO1q0FQZ-UoY,17580
31
+ universal_mcp/agents/codeact0/state.py,sha256=241G-ZDIs4dqm_RnfLZuZOBr7llJOh4dQoDc0aiRcPo,1947
32
+ universal_mcp/agents/codeact0/tools.py,sha256=ZvAqi2vwarTlPizVdu9cZIAoujtBTF4OWnd69rdIIyA,14711
33
+ universal_mcp/agents/codeact0/utils.py,sha256=Gvft0W0Sg1qlFWm8ciX14yssCa8y3x037lql92yGsBQ,18164
34
34
  universal_mcp/agents/shared/__main__.py,sha256=XxH5qGDpgFWfq7fwQfgKULXGiUgeTp_YKfcxftuVZq8,1452
35
35
  universal_mcp/agents/shared/prompts.py,sha256=yjP3zbbuKi87qCj21qwTTicz8TqtkKgnyGSeEjMu3ho,3761
36
36
  universal_mcp/agents/shared/tool_node.py,sha256=DC9F-Ri28Pam0u3sXWNODVgmj9PtAEUb5qP1qOoGgfs,9169
@@ -39,6 +39,6 @@ universal_mcp/applications/filesystem/app.py,sha256=0TRjjm8YnslVRSmfkXI7qQOAlqWl
39
39
  universal_mcp/applications/llm/__init__.py,sha256=_XGRxN3O1--ZS5joAsPf8IlI9Qa6negsJrwJ5VJXno0,46
40
40
  universal_mcp/applications/llm/app.py,sha256=g9mK-luOLUshZzBGyQZMOHBeCSXmh2kCKir40YnsGUo,12727
41
41
  universal_mcp/applications/ui/app.py,sha256=c7OkZsO2fRtndgAzAQbKu-1xXRuRp9Kjgml57YD2NR4,9459
42
- universal_mcp_agents-0.1.23rc1.dist-info/METADATA,sha256=Fa-ACWH1oawoVDBAbp2iSSDGSCRovD2rWP9e9lUhcYE,881
43
- universal_mcp_agents-0.1.23rc1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
- universal_mcp_agents-0.1.23rc1.dist-info/RECORD,,
42
+ universal_mcp_agents-0.1.23rc3.dist-info/METADATA,sha256=-Bv-20WOl1vY3WTMTmZe0eUShg6RG7vcmPt9HuUFXnU,881
43
+ universal_mcp_agents-0.1.23rc3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
+ universal_mcp_agents-0.1.23rc3.dist-info/RECORD,,