fast-agent-mcp 0.0.15__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/METADATA +121 -21
  2. {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/RECORD +27 -25
  3. mcp_agent/cli/__main__.py +3 -0
  4. mcp_agent/cli/commands/bootstrap.py +1 -1
  5. mcp_agent/cli/commands/setup.py +4 -1
  6. mcp_agent/cli/main.py +13 -3
  7. mcp_agent/config.py +19 -11
  8. mcp_agent/core/agent_app.py +1 -1
  9. mcp_agent/core/enhanced_prompt.py +13 -5
  10. mcp_agent/core/fastagent.py +87 -49
  11. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +188 -0
  12. mcp_agent/resources/examples/data-analysis/analysis.py +26 -0
  13. mcp_agent/resources/examples/workflows/evaluator.py +3 -3
  14. mcp_agent/resources/examples/workflows/orchestrator.py +1 -1
  15. mcp_agent/resources/examples/workflows/parallel.py +0 -4
  16. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +229 -91
  17. mcp_agent/workflows/llm/augmented_llm_anthropic.py +16 -2
  18. mcp_agent/workflows/llm/augmented_llm_openai.py +13 -1
  19. mcp_agent/workflows/llm/prompt_utils.py +137 -0
  20. mcp_agent/workflows/orchestrator/orchestrator.py +252 -50
  21. mcp_agent/workflows/orchestrator/orchestrator_models.py +81 -9
  22. mcp_agent/workflows/orchestrator/orchestrator_prompts.py +112 -42
  23. mcp_agent/workflows/router/router_base.py +113 -21
  24. mcp_agent/workflows/router/router_llm.py +19 -5
  25. {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/WHEEL +0 -0
  26. {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/entry_points.txt +0 -0
  27. {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -10,29 +10,34 @@ PLAN_RESULT_TEMPLATE = """Plan Objective: {plan_objective}
10
10
  Progress So Far (steps completed):
11
11
  {steps_str}
12
12
 
13
- Plan Current Status: {plan_status}
14
- Plan Current Result: {plan_result}"""
13
+ Result: {plan_result}"""
15
14
 
16
15
  FULL_PLAN_PROMPT_TEMPLATE = """You are tasked with orchestrating a plan to complete an objective.
17
16
  You can analyze results from the previous steps already executed to decide if the objective is complete.
18
- Your plan must be structured in sequential steps, with each step containing independent parallel subtasks.
19
17
 
20
- Objective: {objective}
18
+ <fastagent:data>
19
+ <fastagent:objective>
20
+ {objective}
21
+ </fastagent:objective>
22
+
23
+ <fastagent:available-agents>
24
+ {agents}
25
+ </fastagent:available-agents>
21
26
 
27
+ <fastagent:progress>
22
28
  {plan_result}
29
+ </fastagent:progress>
30
+
31
+ <fastagent:status>
32
+ {plan_status}
33
+ </fastagent:status>
34
+ </fastagent:data>
23
35
 
36
+ Your plan must be structured in sequential steps, with each step containing independent parallel subtasks.
24
37
  If the previous results achieve the objective, return is_complete=True.
25
38
  Otherwise, generate remaining steps needed.
26
39
 
27
- You have access to the following MCP Servers (which are collections of tools/functions),
28
- and Agents (which are collections of servers):
29
-
30
- Agents:
31
- {agents}
32
-
33
- IMPORTANT: You can ONLY use the agents listed above. Do not invent or reference agents that are not in the list.
34
- The plan will fail if you reference agents that are not available.
35
-
40
+ <fastagent:instruction>
36
41
  Generate a plan with all remaining steps needed.
37
42
  Steps are sequential, but each Step can have parallel subtasks.
38
43
  For each Step, specify a description of the step and independent subtasks that can run in parallel.
@@ -40,6 +45,9 @@ For each subtask specify:
40
45
  1. Clear description of the task that an LLM can execute
41
46
  2. Name of 1 Agent from the available agents list above
42
47
 
48
+ CRITICAL: You MUST ONLY use agent names that are EXACTLY as they appear in <fastagent:available-agents> above.
49
+ Do NOT invent new agents. Do NOT modify agent names. The plan will FAIL if you use an agent that doesn't exist.
50
+
43
51
  Return your response in the following JSON structure:
44
52
  {{
45
53
  "steps": [
@@ -48,11 +56,11 @@ Return your response in the following JSON structure:
48
56
  "tasks": [
49
57
  {{
50
58
  "description": "Description of task 1",
51
- "agent": "agent_name" # For AgentTask - MUST be one of the available agents
59
+ "agent": "agent_name" // agent MUST be exactly one of the agent names listed above
52
60
  }},
53
61
  {{
54
62
  "description": "Description of task 2",
55
- "agent": "agent_name2" # MUST be one of the available agents
63
+ "agent": "agent_name2" // agent MUST be exactly one of the agent names listed above
56
64
  }}
57
65
  ]
58
66
  }}
@@ -61,58 +69,120 @@ Return your response in the following JSON structure:
61
69
  }}
62
70
 
63
71
  You must respond with valid JSON only, with no triple backticks. No markdown formatting.
64
- No extra text. Do not wrap in ```json code fences."""
72
+ No extra text. Do not wrap in ```json code fences.
73
+ </fastagent:instruction>
74
+ """
65
75
 
66
76
  ITERATIVE_PLAN_PROMPT_TEMPLATE = """You are tasked with determining only the next step in a plan
67
77
  needed to complete an objective. You must analyze the current state and progress from previous steps
68
78
  to decide what to do next.
69
79
 
70
- A Step must be sequential in the plan, but can have independent parallel subtasks. Only return a single Step.
80
+ <fastagent:data>
81
+ <fastagent:objective>
82
+ {objective}
83
+ </fastagent:objective>
71
84
 
72
- Objective: {objective}
85
+ <fastagent:available-agents>
86
+ {agents}
87
+ </fastagent:available-agents>
73
88
 
89
+ <fastagent:progress>
74
90
  {plan_result}
75
-
76
- If the previous results achieve the objective, return is_complete=True.
77
- Otherwise, generate the next Step.
91
+ </fastagent:progress>
78
92
 
79
- You have access to the following MCP Servers (which are collections of tools/functions),
80
- and Agents (which are collections of servers):
81
-
82
- Agents:
83
- {agents}
93
+ <fastagent:status>
94
+ {plan_status}
95
+ </fastagent:status>
96
+ </fastagent:data>
84
97
 
85
- IMPORTANT: You can ONLY use the agents listed above. Do not invent or reference agents that are not in the list.
86
- The plan will fail if you reference agents that are not available.
98
+ A Step must be sequential in the plan, but can have independent parallel subtasks. Only return a single Step.
99
+ If the previous results achieve the objective, return is_complete=True.
100
+ Otherwise, generate the next Step.
87
101
 
102
+ <fastagent:instruction>
88
103
  Generate the next step, by specifying a description of the step and independent subtasks that can run in parallel:
89
104
  For each subtask specify:
90
105
  1. Clear description of the task that an LLM can execute
91
106
  2. Name of 1 Agent from the available agents list above
92
107
 
108
+ CRITICAL: You MUST ONLY use agent names that are EXACTLY as they appear in <fastagent:available-agents> above.
109
+ Do NOT invent new agents. Do NOT modify agent names. The plan will FAIL if you use an agent that doesn't exist.
110
+
93
111
  Return your response in the following JSON structure:
94
112
  {{
95
113
  "description": "Description of step 1",
96
114
  "tasks": [
97
115
  {{
98
116
  "description": "Description of task 1",
99
- "agent": "agent_name" # For AgentTask - MUST be one of the available agents
117
+ "agent": "agent_name" // agent MUST be exactly one of the agent names listed above
100
118
  }}
101
119
  ],
102
120
  "is_complete": false
103
121
  }}
104
122
 
105
123
  You must respond with valid JSON only, with no triple backticks. No markdown formatting.
106
- No extra text. Do not wrap in ```json code fences."""
107
-
108
- TASK_PROMPT_TEMPLATE = """You are part of a larger workflow to achieve the objective: {objective}.
109
- Your job is to accomplish only the following task: {task}.
110
-
111
- Results so far that may provide helpful context:
112
- {context}"""
113
-
114
- SYNTHESIZE_STEP_PROMPT_TEMPLATE = """Synthesize the results of these parallel tasks into a cohesive result:
115
- {step_result}"""
116
-
117
- SYNTHESIZE_PLAN_PROMPT_TEMPLATE = """Synthesize the results of executing all steps in the plan into a cohesive result:
118
- {plan_result}"""
124
+ No extra text. Do not wrap in ```json code fences.
125
+ </fastagent:instruction>
126
+ """
127
+
128
+ TASK_PROMPT_TEMPLATE = """You are part of a larger workflow to achieve an objective.
129
+
130
+ <fastagent:data>
131
+ <fastagent:objective>
132
+ {objective}
133
+ </fastagent:objective>
134
+
135
+ <fastagent:task>
136
+ {task}
137
+ </fastagent:task>
138
+
139
+ <fastagent:context>
140
+ {context}
141
+ </fastagent:context>
142
+ </fastagent:data>
143
+
144
+ <fastagent:instruction>
145
+ Your job is to accomplish only the task specified above.
146
+ Use the context from previous steps to inform your approach.
147
+ The context contains structured XML with the results from previous steps - pay close attention to:
148
+ - The objective in <fastagent:objective>
149
+ - Previous step results in <fastagent:steps>
150
+ - Task results and their attribution in <fastagent:task-result>
151
+
152
+ Provide a direct, focused response that addresses the task.
153
+ </fastagent:instruction>
154
+ """
155
+
156
+ SYNTHESIZE_STEP_PROMPT_TEMPLATE = """You need to synthesize the results of parallel tasks into a cohesive result.
157
+
158
+ <fastagent:data>
159
+ <fastagent:step-results>
160
+ {step_result}
161
+ </fastagent:step-results>
162
+ </fastagent:data>
163
+
164
+ <fastagent:instruction>
165
+ Analyze the results from all tasks in this step.
166
+ Each task was executed by a specific agent (finder, writer, etc.)
167
+ Consider the expertise of each agent when weighing their results.
168
+ Combine the information into a coherent, unified response.
169
+ Focus on key insights and important outcomes.
170
+ Resolve any conflicting information if present.
171
+ </fastagent:instruction>
172
+ """
173
+
174
+ SYNTHESIZE_PLAN_PROMPT_TEMPLATE = """You need to synthesize the results of all completed plan steps into a final response.
175
+
176
+ <fastagent:data>
177
+ <fastagent:plan-results>
178
+ {plan_result}
179
+ </fastagent:plan-results>
180
+ </fastagent:data>
181
+
182
+ <fastagent:instruction>
183
+ Create a comprehensive final response that addresses the original objective.
184
+ Integrate all the information gathered across all plan steps.
185
+ Provide a clear, complete answer that achieves the objective.
186
+ Focus on delivering value through your synthesis, not just summarizing.
187
+ </fastagent:instruction>
188
+ """
@@ -200,6 +200,7 @@ class Router(ABC, ContextDependent):
200
200
  category=server_name,
201
201
  name=server_config.name if server_config else server_name,
202
202
  description=server_config.description,
203
+ tools=[], # Empty list to avoid validation errors
203
204
  )
204
205
 
205
206
  def get_agent_category(self, agent: Agent) -> AgentRouterCategory:
@@ -207,14 +208,17 @@ class Router(ABC, ContextDependent):
207
208
  agent.instruction({}) if callable(agent.instruction) else agent.instruction
208
209
  )
209
210
 
211
+ # Just get server categories without attempting to access tools
212
+ # This is a simpler approach that avoids potential issues with uninitialized agents
213
+ server_categories = [
214
+ self.get_server_category(server_name) for server_name in agent.server_names
215
+ ]
216
+
210
217
  return AgentRouterCategory(
211
218
  category=agent,
212
219
  name=agent.name,
213
220
  description=agent_description,
214
- servers=[
215
- self.get_server_category(server_name)
216
- for server_name in agent.server_names
217
- ],
221
+ servers=server_categories,
218
222
  )
219
223
 
220
224
  def get_function_category(self, function: Callable) -> RouterCategory:
@@ -231,9 +235,6 @@ class Router(ABC, ContextDependent):
231
235
  ) -> str:
232
236
  """Format a category into a readable string."""
233
237
 
234
- index_str = f"{index}. " if index is not None else " "
235
- category_str = ""
236
-
237
238
  if isinstance(category, ServerRouterCategory):
238
239
  category_str = self._format_server_category(category)
239
240
  elif isinstance(category, AgentRouterCategory):
@@ -241,36 +242,127 @@ class Router(ABC, ContextDependent):
241
242
  else:
242
243
  category_str = self._format_function_category(category)
243
244
 
244
- return f"{index_str}{category_str}"
245
+ return category_str
245
246
 
246
247
  def _format_tools(self, tools: List[FastTool]) -> str:
247
248
  """Format a list of tools into a readable string."""
248
249
  if not tools:
249
- return "No tool information provided."
250
+ # Return a note about tools within XML tags to maintain structure
251
+ return '<fastagent:tool name="info">No tool information available</fastagent:tool>'
250
252
 
251
253
  tool_descriptions = []
252
254
  for tool in tools:
253
- desc = f"- {tool.name}: {tool.description}"
255
+ # Access tool name and description safely
256
+ tool_name = getattr(tool, "name", "unnamed-tool")
257
+ tool_description = getattr(tool, "description", "No description available")
258
+ desc = f'<fastagent:tool name="{tool_name}">{tool_description}</fastagent:tool>'
254
259
  tool_descriptions.append(desc)
255
260
 
256
261
  return "\n".join(tool_descriptions)
257
262
 
258
263
  def _format_server_category(self, category: ServerRouterCategory) -> str:
259
264
  """Format a server category into a readable string."""
260
- description = category.description or "No description provided"
261
- tools = self._format_tools(category.tools)
262
- return f"Server Category: {category.name}\nDescription: {description}\nTools in server:\n{tools}"
265
+ # Check if we have any content (description or tools)
266
+ has_description = bool(category.description)
267
+ has_tools = bool(category.tools)
268
+
269
+ # If no content at all, use self-closing tag
270
+ if not has_description and not has_tools:
271
+ return f'<fastagent:server-category name="{category.name}" />'
272
+
273
+ # Otherwise, build the content
274
+ description_section = ""
275
+ if has_description:
276
+ description_section = f"\n<fastagent:description>{category.description}</fastagent:description>"
277
+
278
+ # Add tools section if we have tool information
279
+ if has_tools:
280
+ tools = self._format_tools(category.tools)
281
+ return f"""<fastagent:server-category name="{category.name}">{description_section}
282
+ <fastagent:tools>
283
+ {tools}
284
+ </fastagent:tools>
285
+ </fastagent:server-category>"""
286
+ else:
287
+ # Just description, no tools
288
+ return f"""<fastagent:server-category name="{category.name}">{description_section}
289
+ </fastagent:server-category>"""
263
290
 
264
291
  def _format_agent_category(self, category: AgentRouterCategory) -> str:
265
292
  """Format an agent category into a readable string."""
266
- description = category.description or "No description provided"
267
- servers = "\n".join(
268
- [f"- {server.name} ({server.description})" for server in category.servers]
269
- )
270
-
271
- return f"Agent Category: {category.name}\nDescription: {description}\nServers in agent:\n{servers}"
293
+ # Check if we have any content (description or servers)
294
+ has_description = bool(category.description)
295
+ has_servers = bool(category.servers)
296
+
297
+ # If no content at all, use self-closing tag
298
+ if not has_description and not has_servers:
299
+ return f'<fastagent:agent-category name="{category.name}" />'
300
+
301
+ # Build description section if needed
302
+ description_section = ""
303
+ if has_description:
304
+ description_section = f"\n<fastagent:description>{category.description}</fastagent:description>"
305
+
306
+ # Handle the case with no servers
307
+ if not has_servers:
308
+ return f"""<fastagent:agent-category name="{category.name}">{description_section}
309
+ </fastagent:agent-category>"""
310
+
311
+ # Format servers with proper XML tags and include their tools
312
+ server_sections = []
313
+ for server in category.servers:
314
+ # Check if this server has any content
315
+ has_server_description = bool(server.description)
316
+ has_server_tools = bool(server.tools)
317
+
318
+ # Use self-closing tag if server has no content
319
+ if not has_server_description and not has_server_tools:
320
+ server_section = f'<fastagent:server name="{server.name}" />'
321
+ server_sections.append(server_section)
322
+ continue
323
+
324
+ # Build server description if needed
325
+ server_desc_section = ""
326
+ if has_server_description:
327
+ server_desc_section = f"\n<fastagent:description>{server.description}</fastagent:description>"
328
+
329
+ # Format server tools if available
330
+ if has_server_tools:
331
+ tool_items = []
332
+ for tool in server.tools:
333
+ tool_desc = tool.description if tool.description else ""
334
+ tool_items.append(
335
+ f'<fastagent:tool name="{tool.name}">{tool_desc}</fastagent:tool>'
336
+ )
337
+
338
+ tools_section = f"\n<fastagent:tools>\n{chr(10).join(tool_items)}\n</fastagent:tools>"
339
+ server_section = f"""<fastagent:server name="{server.name}">{server_desc_section}{tools_section}
340
+ </fastagent:server>"""
341
+ else:
342
+ # Just description, no tools
343
+ server_section = f"""<fastagent:server name="{server.name}">{server_desc_section}
344
+ </fastagent:server>"""
345
+
346
+ server_sections.append(server_section)
347
+
348
+ servers = "\n".join(server_sections)
349
+
350
+ return f"""<fastagent:agent-category name="{category.name}">{description_section}
351
+ <fastagent:servers>
352
+ {servers}
353
+ </fastagent:servers>
354
+ </fastagent:agent-category>"""
272
355
 
273
356
  def _format_function_category(self, category: RouterCategory) -> str:
274
357
  """Format a function category into a readable string."""
275
- description = category.description or "No description provided"
276
- return f"Function Category: {category.name}\nDescription: {description}"
358
+ # Check if we have a description
359
+ has_description = bool(category.description)
360
+
361
+ # If no description, use self-closing tag
362
+ if not has_description:
363
+ return f'<fastagent:function-category name="{category.name}" />'
364
+
365
+ # Include description
366
+ return f"""<fastagent:function-category name="{category.name}">
367
+ <fastagent:description>{category.description}</fastagent:description>
368
+ </fastagent:function-category>"""
@@ -17,17 +17,23 @@ logger = get_logger(__name__)
17
17
  DEFAULT_ROUTING_INSTRUCTION = """
18
18
  You are a highly accurate request router that directs incoming requests to the most appropriate category.
19
19
  A category is a specialized destination, such as a Function, an MCP Server (a collection of tools/functions), or an Agent (a collection of servers).
20
- Below are the available routing categories, each with their capabilities and descriptions:
21
20
 
21
+ <fastagent:data>
22
+ <fastagent:categories>
22
23
  {context}
24
+ </fastagent:categories>
23
25
 
24
- Your task is to analyze the following request and determine the most appropriate categories from the options above. Consider:
26
+ <fastagent:request>
27
+ {request}
28
+ </fastagent:request>
29
+ </fastagent:data>
30
+
31
+ Your task is to analyze the request and determine the most appropriate categories from the options above. Consider:
25
32
  - The specific capabilities and tools each destination offers
26
33
  - How well the request matches the category's description
27
34
  - Whether the request might benefit from multiple categories (up to {top_k})
28
35
 
29
- Request: {request}
30
-
36
+ <fastagent:instruction>
31
37
  Respond in JSON format:
32
38
  {{
33
39
  "categories": [
@@ -41,13 +47,21 @@ Respond in JSON format:
41
47
 
42
48
  Only include categories that are truly relevant. You may return fewer than {top_k} if appropriate.
43
49
  If none of the categories are relevant, return an empty list.
50
+ </fastagent:instruction>
44
51
  """
45
52
 
46
53
  ROUTING_SYSTEM_INSTRUCTION = """
47
54
  You are a highly accurate request router that directs incoming requests to the most appropriate category.
48
55
  A category is a specialized destination, such as a Function, an MCP Server (a collection of tools/functions), or an Agent (a collection of servers).
49
- You will be provided with a request and a list of categories to choose from.
56
+
57
+ You will analyze requests and choose the most appropriate categories based on their capabilities and descriptions.
50
58
  You can choose one or more categories, or choose none if no category is appropriate.
59
+
60
+ Follow these guidelines:
61
+ - Carefully match the request's needs with category capabilities
62
+ - Consider which tools or servers would best address the request
63
+ - If multiple categories could help, select all relevant ones
64
+ - Only include truly relevant categories, not tangentially related ones
51
65
  """
52
66
 
53
67