universal-mcp-agents 0.1.23rc2__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.

@@ -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
@@ -16,16 +16,16 @@ from universal_mcp.types import ToolFormat
16
16
  from universal_mcp.agents.base import BaseAgent
17
17
  from universal_mcp.agents.codeact0.llm_tool import smart_print
18
18
  from universal_mcp.agents.codeact0.prompts import (
19
- PLAYBOOK_GENERATING_PROMPT,
20
- PLAYBOOK_META_PROMPT,
21
- PLAYBOOK_PLANNING_PROMPT,
19
+ AGENT_BUILDER_GENERATING_PROMPT,
20
+ AGENT_BUILDER_META_PROMPT,
21
+ AGENT_BUILDER_PLANNING_PROMPT,
22
22
  create_default_prompt,
23
23
  )
24
24
  from universal_mcp.agents.codeact0.sandbox import eval_unsafe, execute_ipython_cell, handle_execute_ipython_cell
25
- from universal_mcp.agents.codeact0.state import CodeActState, PlaybookCode, PlaybookMeta, PlaybookPlan
25
+ from universal_mcp.agents.codeact0.state import CodeActState, AgentBuilderCode, AgentBuilderMeta, AgentBuilderPlan
26
26
  from universal_mcp.agents.codeact0.tools import (
27
27
  create_meta_tools,
28
- enter_playbook_mode,
28
+ enter_agent_builder_mode,
29
29
  get_valid_tools,
30
30
  )
31
31
  from universal_mcp.agents.codeact0.utils import build_anthropic_cache_message, get_connected_apps_string
@@ -41,7 +41,7 @@ class CodeActPlaybookAgent(BaseAgent):
41
41
  model: str,
42
42
  memory: BaseCheckpointSaver | None = None,
43
43
  registry: ToolRegistry | None = None,
44
- playbook_registry: object | None = None,
44
+ agent_builder_registry: object | None = None,
45
45
  sandbox_timeout: int = 20,
46
46
  **kwargs,
47
47
  ):
@@ -53,11 +53,11 @@ class CodeActPlaybookAgent(BaseAgent):
53
53
  **kwargs,
54
54
  )
55
55
  self.model_instance = load_chat_model(model)
56
- self.playbook_model_instance = load_chat_model("azure/gpt-4.1")
56
+ self.agent_builder_model_instance = load_chat_model("azure/gpt-4.1")
57
57
  self.registry = registry
58
- self.playbook_registry = playbook_registry
59
- self.playbook = playbook_registry.get_agent() if playbook_registry else None
60
- 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 {}
61
61
  self.eval_fn = eval_unsafe
62
62
  self.sandbox_timeout = sandbox_timeout
63
63
  self.default_tools_config = {
@@ -91,7 +91,7 @@ class CodeActPlaybookAgent(BaseAgent):
91
91
 
92
92
  agent_facing_tools = [
93
93
  execute_ipython_cell,
94
- enter_playbook_mode,
94
+ enter_agent_builder_mode,
95
95
  meta_tools["search_functions"],
96
96
  meta_tools["load_functions"],
97
97
  ]
@@ -120,7 +120,7 @@ class CodeActPlaybookAgent(BaseAgent):
120
120
  else:
121
121
  return Command(update={"messages": [response], "model_with_tools": model_with_tools})
122
122
 
123
- async def execute_tools(state: CodeActState) -> Command[Literal["call_model", "playbook"]]:
123
+ async def execute_tools(state: CodeActState) -> Command[Literal["call_model", "agent_builder"]]:
124
124
  """Execute tool calls"""
125
125
  last_message = state["messages"][-1]
126
126
  tool_calls = last_message.tool_calls if isinstance(last_message, AIMessage) else []
@@ -128,6 +128,8 @@ class CodeActPlaybookAgent(BaseAgent):
128
128
  tool_messages = []
129
129
  new_tool_ids = []
130
130
  tool_result = ""
131
+ ask_user = False
132
+ ai_msg = ""
131
133
  effective_previous_add_context = state.get("add_context", {})
132
134
  effective_existing_context = state.get("context", {})
133
135
  # logging.info(f"Initial new_tool_ids_for_context: {new_tool_ids_for_context}")
@@ -136,15 +138,15 @@ class CodeActPlaybookAgent(BaseAgent):
136
138
  tool_name = tool_call["name"]
137
139
  tool_args = tool_call["args"]
138
140
  try:
139
- if tool_name == "enter_playbook_mode":
141
+ if tool_name == "enter_agent_builder_mode":
140
142
  tool_message = ToolMessage(
141
- content=json.dumps("Entered Playbook Mode."),
143
+ content=json.dumps("Entered Agent Builder Mode."),
142
144
  name=tool_call["name"],
143
145
  tool_call_id=tool_call["id"],
144
146
  )
145
147
  return Command(
146
- goto="playbook",
147
- 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
148
150
  )
149
151
  elif tool_name == "execute_ipython_cell":
150
152
  code = tool_call["args"]["snippet"]
@@ -160,24 +162,21 @@ class CodeActPlaybookAgent(BaseAgent):
160
162
  tool_result = output
161
163
  elif tool_name == "load_functions":
162
164
  # The tool now does all the work of validation and formatting.
163
- tool_result = await meta_tools["load_functions"].ainvoke(tool_args)
164
-
165
+ tool_result, new_context_for_sandbox, valid_tools, unconnected_links = await meta_tools["load_functions"].ainvoke(tool_args)
165
166
  # We still need to update the sandbox context for `execute_ipython_cell`
166
- valid_tools, _ = await get_valid_tools(tool_ids=tool_args["tool_ids"], registry=self.registry)
167
167
  new_tool_ids.extend(valid_tools)
168
168
  if new_tool_ids:
169
- newly_exported = await self.registry.export_tools(new_tool_ids, ToolFormat.LANGCHAIN)
170
- _, new_context_for_sandbox = create_default_prompt(
171
- newly_exported, [], "", "", None
172
- ) # is_initial_prompt is False by default
173
169
  self.tools_context.update(new_context_for_sandbox)
170
+ if unconnected_links:
171
+ ask_user = True
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} "
174
173
 
175
174
  elif tool_name == "search_functions":
176
175
  tool_result = await meta_tools["search_functions"].ainvoke(tool_args)
177
176
  else:
178
177
  raise Exception(
179
178
  f"Unexpected tool call: {tool_call['name']}. "
180
- "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'."
181
180
  )
182
181
  except Exception as e:
183
182
  tool_result = str(e)
@@ -188,6 +187,17 @@ class CodeActPlaybookAgent(BaseAgent):
188
187
  tool_call_id=tool_call["id"],
189
188
  )
190
189
  tool_messages.append(tool_message)
190
+
191
+ if ask_user:
192
+ tool_messages.append(AIMessage(content=ai_msg))
193
+ return Command(
194
+ update={
195
+ "messages": tool_messages,
196
+ "selected_tool_ids": new_tool_ids,
197
+ "context": effective_existing_context,
198
+ "add_context": effective_previous_add_context,
199
+ }
200
+ )
191
201
 
192
202
  return Command(
193
203
  goto="call_model",
@@ -199,17 +209,17 @@ class CodeActPlaybookAgent(BaseAgent):
199
209
  },
200
210
  )
201
211
 
202
- def playbook(state: CodeActState, writer: StreamWriter) -> Command[Literal["call_model"]]:
203
- playbook_mode = state.get("playbook_mode")
204
- 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":
205
215
  plan_id = str(uuid.uuid4())
206
- writer({"type": "custom", id: plan_id, "name": "planning", "data": {"update": bool(self.playbook)}})
207
- 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
208
218
  messages = [{"role": "system", "content": planning_instructions}] + state["messages"]
209
219
 
210
- 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)
211
221
  response = model_with_structured_output.invoke(messages)
212
- plan = cast(PlaybookPlan, response)
222
+ plan = cast(AgentBuilderPlan, response)
213
223
 
214
224
  writer({"type": "custom", id: plan_id, "name": "planning", "data": {"plan": plan.steps}})
215
225
  return Command(
@@ -220,16 +230,16 @@ class CodeActPlaybookAgent(BaseAgent):
220
230
  additional_kwargs={
221
231
  "type": "planning",
222
232
  "plan": plan.steps,
223
- "update": bool(self.playbook),
233
+ "update": bool(self.agent),
224
234
  },
225
235
  )
226
236
  ],
227
- "playbook_mode": "confirming",
237
+ "agent_builder_mode": "confirming",
228
238
  "plan": plan.steps,
229
239
  }
230
240
  )
231
241
 
232
- elif playbook_mode == "confirming":
242
+ elif agent_builder_mode == "confirming":
233
243
  # Deterministic routing based on three exact button inputs from UI
234
244
  user_text = ""
235
245
  for m in reversed(state["messages"]):
@@ -245,10 +255,10 @@ class CodeActPlaybookAgent(BaseAgent):
245
255
  if t == "yes, this is great":
246
256
  self.meta_id = str(uuid.uuid4())
247
257
  name, description = None, None
248
- if self.playbook:
258
+ if self.agent:
249
259
  # Update flow: use existing name/description and do not re-generate
250
- name = getattr(self.playbook, "name", None)
251
- description = getattr(self.playbook, "description", None)
260
+ name = getattr(self.agent, "name", None)
261
+ description = getattr(self.agent, "description", None)
252
262
  writer(
253
263
  {
254
264
  "type": "custom",
@@ -264,12 +274,12 @@ class CodeActPlaybookAgent(BaseAgent):
264
274
  else:
265
275
  writer({"type": "custom", id: self.meta_id, "name": "generating", "data": {"update": False}})
266
276
 
267
- meta_instructions = self.instructions + PLAYBOOK_META_PROMPT
277
+ meta_instructions = self.instructions + AGENT_BUILDER_META_PROMPT
268
278
  messages = [{"role": "system", "content": meta_instructions}] + state["messages"]
269
279
 
270
- 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)
271
281
  meta_response = model_with_structured_output.invoke(messages)
272
- meta = cast(PlaybookMeta, meta_response)
282
+ meta = cast(AgentBuilderMeta, meta_response)
273
283
  name, description = meta.name, meta.description
274
284
 
275
285
  # Emit intermediary UI update with created name/description
@@ -283,11 +293,11 @@ class CodeActPlaybookAgent(BaseAgent):
283
293
  )
284
294
 
285
295
  return Command(
286
- goto="playbook",
296
+ goto="agent_builder",
287
297
  update={
288
- "playbook_mode": "generating",
289
- "playbook_name": name,
290
- "playbook_description": description,
298
+ "agent_builder_mode": "generating",
299
+ "agent_name": name,
300
+ "agent_description": description,
291
301
  },
292
302
  )
293
303
  if t == "i would like to modify the plan":
@@ -295,52 +305,51 @@ class CodeActPlaybookAgent(BaseAgent):
295
305
  content="What would you like to change about the plan? Let me know and I'll update the plan accordingly.",
296
306
  additional_kwargs={"stream": "true"},
297
307
  )
298
- return Command(update={"playbook_mode": "planning", "messages": [prompt_ai]})
308
+ return Command(update={"agent_builder_mode": "planning", "messages": [prompt_ai]})
299
309
  if t == "let's do something else":
300
- return Command(goto="call_model", update={"playbook_mode": "inactive"})
310
+ return Command(goto="call_model", update={"agent_builder_mode": "inactive"})
301
311
 
302
312
  # Fallback safe default
303
- return Command(goto="call_model", update={"playbook_mode": "inactive"})
313
+ return Command(goto="call_model", update={"agent_builder_mode": "inactive"})
304
314
 
305
- elif playbook_mode == "generating":
306
- generating_instructions = self.instructions + PLAYBOOK_GENERATING_PROMPT
315
+ elif agent_builder_mode == "generating":
316
+ generating_instructions = self.instructions + AGENT_BUILDER_GENERATING_PROMPT
307
317
  messages = [{"role": "system", "content": generating_instructions}] + state["messages"]
308
318
 
309
- model_with_structured_output = self.playbook_model_instance.with_structured_output(PlaybookCode)
319
+ model_with_structured_output = self.agent_builder_model_instance.with_structured_output(AgentBuilderCode)
310
320
  response = model_with_structured_output.invoke(messages)
311
- func_code = cast(PlaybookCode, response).code
321
+ func_code = cast(AgentBuilderCode, response).code
312
322
 
313
323
  # Extract function name (handle both regular and async functions)
314
324
  match = re.search(r"^\s*(?:async\s+)?def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(", func_code, re.MULTILINE)
315
325
  if match:
316
326
  function_name = match.group(1)
317
327
  else:
318
- function_name = "generated_playbook"
328
+ function_name = "generated_agent"
319
329
 
320
330
  # Use generated metadata if available
321
- final_name = state.get("playbook_.pyname") or function_name
322
- 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}"
323
333
 
324
334
  # Save or update an Agent using the helper registry
325
335
  try:
326
- if not self.playbook_registry:
327
- raise ValueError("Playbook registry is not configured")
336
+ if not self.agent_builder_registry:
337
+ raise ValueError("AgentBuilder registry is not configured")
328
338
 
329
339
  # Build instructions payload embedding the plan and function code
330
340
  instructions_payload = {
331
- "playbookPlan": state["plan"],
332
- "playbookScript": func_code,
341
+ "plan": state["plan"],
342
+ "script": func_code,
333
343
  }
334
344
 
335
345
  # Convert tool ids list to dict
336
346
  tool_dict = convert_tool_ids_to_dict(state["selected_tool_ids"])
337
347
 
338
- res = self.playbook_registry.upsert_agent(
348
+ res = self.agent_builder_registry.upsert_agent(
339
349
  name=final_name,
340
350
  description=final_description,
341
351
  instructions=instructions_payload,
342
352
  tools=tool_dict,
343
- visibility="private",
344
353
  )
345
354
  except Exception as e:
346
355
  raise e
@@ -352,7 +361,7 @@ class CodeActPlaybookAgent(BaseAgent):
352
361
  "name": "generating",
353
362
  "data": {
354
363
  "id": str(res.id),
355
- "update": bool(self.playbook),
364
+ "update": bool(self.agent),
356
365
  "name": final_name,
357
366
  "description": final_description,
358
367
  },
@@ -363,16 +372,16 @@ class CodeActPlaybookAgent(BaseAgent):
363
372
  additional_kwargs={
364
373
  "type": "generating",
365
374
  "id": str(res.id),
366
- "update": bool(self.playbook),
375
+ "update": bool(self.agent),
367
376
  "name": final_name,
368
377
  "description": final_description,
369
378
  },
370
379
  )
371
380
 
372
- return Command(update={"messages": [mock_assistant_message], "playbook_mode": "normal"})
381
+ return Command(update={"messages": [mock_assistant_message], "agent_builder_mode": "normal"})
373
382
 
374
- async def route_entry(state: CodeActState) -> Literal["call_model", "playbook"]:
375
- """Route to either normal mode or playbook creation"""
383
+ async def route_entry(state: CodeActState) -> Literal["call_model", "agent_builder"]:
384
+ """Route to either normal mode or agent builder creation"""
376
385
  all_tools = await self.registry.export_tools(state["selected_tool_ids"], ToolFormat.LANGCHAIN)
377
386
  # print(all_tools)
378
387
 
@@ -382,16 +391,16 @@ class CodeActPlaybookAgent(BaseAgent):
382
391
  self.additional_tools,
383
392
  self.instructions,
384
393
  await get_connected_apps_string(self.registry),
385
- self.playbook,
394
+ self.agent,
386
395
  is_initial_prompt=True,
387
396
  )
388
- if state.get("playbook_mode") in ["planning", "confirming", "generating"]:
389
- return "playbook"
397
+ if state.get("agent_builder_mode") in ["planning", "confirming", "generating"]:
398
+ return "agent_builder"
390
399
  return "call_model"
391
400
 
392
401
  agent = StateGraph(state_schema=CodeActState)
393
402
  agent.add_node(call_model, retry_policy=RetryPolicy(max_attempts=3, retry_on=filter_retry_on))
394
- agent.add_node(playbook)
403
+ agent.add_node(agent_builder)
395
404
  agent.add_node(execute_tools)
396
405
  agent.add_conditional_edges(START, route_entry)
397
406
  return agent.compile(checkpointer=self.memory)
@@ -63,16 +63,16 @@ Rules:
63
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.
64
64
  """
65
65
 
66
- 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.
67
67
 
68
68
  TASK: Analyze the conversation history and code execution to create a step-by-step plan for a reusable function.
69
69
  Do not include the searching and loading of tools. Assume that the tools have already been loaded.
70
70
  The plan is a sequence of steps.
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 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.
72
72
 
73
73
  Your plan should:
74
74
  1. Identify the key steps in the workflow
75
- 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 ``
76
76
  3. Keep the logic generic and reusable
77
77
  4. Be clear and concise
78
78
 
@@ -90,26 +90,26 @@ Now create a plan based on the conversation history. Do not include any other te
90
90
  """
91
91
 
92
92
 
93
- 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.
94
94
  Your response must be ONLY the Python code for the function.
95
95
  Do not include any other text, markdown, or explanations in your response.
96
96
  Your response should start with `def` or `async def`.
97
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.
98
- 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.
99
99
  """
100
100
 
101
101
 
102
- PLAYBOOK_META_PROMPT = """
103
- 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.
104
104
 
105
- 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.
106
106
 
107
107
  INPUTS:
108
108
  - Conversation context and plan steps will be provided in prior messages
109
109
 
110
110
  REQUIREMENTS:
111
111
  1. Name: 3-6 words, Title Case, no punctuation except hyphens if needed
112
- 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
113
113
 
114
114
  OUTPUT: Return ONLY a JSON object with exactly these keys:
115
115
  {
@@ -137,7 +137,7 @@ def create_default_prompt(
137
137
  additional_tools: Sequence[StructuredTool],
138
138
  base_prompt: str | None = None,
139
139
  apps_string: str | None = None,
140
- playbook: object | None = None,
140
+ agent: object | None = None,
141
141
  is_initial_prompt: bool = False,
142
142
  ):
143
143
  if is_initial_prompt:
@@ -191,14 +191,14 @@ def create_default_prompt(
191
191
  f"\n\nUse the following information/instructions while completing your tasks:\n\n{base_prompt}"
192
192
  )
193
193
 
194
- # Append existing playbook (plan + code) if provided
194
+ # Append existing agent (plan + code) if provided
195
195
  try:
196
- if playbook and hasattr(playbook, "instructions"):
197
- pb = playbook.instructions or {}
198
- plan = pb.get("playbookPlan")
199
- code = pb.get("playbookScript")
196
+ if agent and hasattr(agent, "instructions"):
197
+ pb = agent.instructions or {}
198
+ plan = pb.get("plan")
199
+ code = pb.get("script")
200
200
  if plan or code:
201
- system_prompt += "\n\nExisting Playbook Provided:\n"
201
+ system_prompt += "\n\nExisting Agent Provided:\n"
202
202
  if plan:
203
203
  if isinstance(plan, list):
204
204
  plan_block = "\n".join(f"- {str(s)}" for s in plan)
@@ -4,16 +4,16 @@ 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."""
@@ -11,8 +11,8 @@ from universal_mcp.types import ToolFormat
11
11
  from universal_mcp.agents.codeact0.prompts import create_default_prompt
12
12
 
13
13
 
14
- def enter_playbook_mode():
15
- """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."""
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."""
16
16
  return
17
17
 
18
18
 
@@ -212,21 +212,18 @@ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
212
212
  return "No tool IDs provided to load."
213
213
 
214
214
  # Step 1: Validate which tools are usable and get login links for others.
215
- valid_tool_ids, unconnected_links = await get_valid_tools(tool_ids=tool_ids, registry=tool_registry)
215
+ valid_tools, unconnected_links = await get_valid_tools(tool_ids=tool_ids, registry=tool_registry)
216
216
 
217
- if not valid_tool_ids:
217
+ if not valid_tools:
218
218
  return "Error: None of the provided tool IDs could be validated or loaded."
219
219
 
220
220
  # Step 2: Export the schemas of the valid tools.
221
- try:
222
- # Create a temporary, clean registry to export only the requested tools
223
- temp_registry = AgentrRegistry()
224
- exported_tools = await temp_registry.export_tools(valid_tool_ids, ToolFormat.LANGCHAIN)
225
- except Exception as e:
226
- return f"Error exporting tools: {e}"
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
+
227
224
 
228
225
  # Step 3: Build the informational string for the agent.
229
- tool_definitions, _ = create_default_prompt(exported_tools, [], is_initial_prompt=False)
226
+ tool_definitions, new_tools_context = create_default_prompt(exported_tools, [], is_initial_prompt=False)
230
227
 
231
228
  result_parts = [
232
229
  f"Successfully loaded {len(exported_tools)} functions. They are now available for use inside `execute_ipython_cell`:",
@@ -234,15 +231,9 @@ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
234
231
  ]
235
232
 
236
233
  response_string = "\n\n".join(result_parts)
234
+ unconnected_links = "\n".join(unconnected_links)
237
235
 
238
- # Append login links if any apps were not connected
239
- if unconnected_links:
240
- links = "\n".join(unconnected_links)
241
- response_string += (
242
- f"\n\nPlease ask the user to log in to the following app(s) to use their full functionality:\n{links}"
243
- )
244
-
245
- return response_string
236
+ return response_string, new_tools_context, valid_tools, unconnected_links
246
237
 
247
238
  @tool
248
239
  async def web_search(query: str) -> dict:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: universal-mcp-agents
3
- Version: 0.1.23rc2
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
@@ -1,5 +1,5 @@
1
1
  universal_mcp/agents/__init__.py,sha256=bW7WJopR6YZSLxghLf8nhohhHPWzm0wdGoZlmKDAcZ4,1078
2
- universal_mcp/agents/base.py,sha256=CEnY8y2as_XR311t9v2iqd4DOCSyhpOPOBDcZKNJMpc,7378
2
+ universal_mcp/agents/base.py,sha256=sSC217rac89dEheeIRZoMSU_nk9UfSfQw-0E1BjNSIc,7393
3
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
@@ -22,14 +22,14 @@ universal_mcp/agents/builder/prompts.py,sha256=8Xs6uzTUHguDRngVMLak3lkXFkk2VV_uQ
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
24
  universal_mcp/agents/codeact0/__main__.py,sha256=EHW9ePVePEemGI5yMUBc2Mp_JlrP6Apk1liab1y2Rd8,782
25
- universal_mcp/agents/codeact0/agent.py,sha256=9YwcCeRE_bSl77JG54SOJjQxqZxCd_ZfdBJmgb1N0RA,18818
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=qFcGDsISqW3iUNpW4yNDCH_vDwx75QS0acQsWI0Ul2w,11666
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=ESlxz68bwudasuL8jCI7GhweTqgLbYQqZM0mPkE06hQ,1938
32
- universal_mcp/agents/codeact0/tools.py,sha256=bCIJDNkyE_6SULoU8LGvupOrRrvaa9JZVMPMTdsGDEs,15017
31
+ universal_mcp/agents/codeact0/state.py,sha256=241G-ZDIs4dqm_RnfLZuZOBr7llJOh4dQoDc0aiRcPo,1947
32
+ universal_mcp/agents/codeact0/tools.py,sha256=ZvAqi2vwarTlPizVdu9cZIAoujtBTF4OWnd69rdIIyA,14711
33
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
@@ -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.23rc2.dist-info/METADATA,sha256=v2vW8cUeI8cn6XY502FcEhBaYVeLpXu1n2D9FpUmJ4Y,881
43
- universal_mcp_agents-0.1.23rc2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
- universal_mcp_agents-0.1.23rc2.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,,