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.
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/METADATA +121 -21
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/RECORD +27 -25
- mcp_agent/cli/__main__.py +3 -0
- mcp_agent/cli/commands/bootstrap.py +1 -1
- mcp_agent/cli/commands/setup.py +4 -1
- mcp_agent/cli/main.py +13 -3
- mcp_agent/config.py +19 -11
- mcp_agent/core/agent_app.py +1 -1
- mcp_agent/core/enhanced_prompt.py +13 -5
- mcp_agent/core/fastagent.py +87 -49
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +188 -0
- mcp_agent/resources/examples/data-analysis/analysis.py +26 -0
- mcp_agent/resources/examples/workflows/evaluator.py +3 -3
- mcp_agent/resources/examples/workflows/orchestrator.py +1 -1
- mcp_agent/resources/examples/workflows/parallel.py +0 -4
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +229 -91
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +16 -2
- mcp_agent/workflows/llm/augmented_llm_openai.py +13 -1
- mcp_agent/workflows/llm/prompt_utils.py +137 -0
- mcp_agent/workflows/orchestrator/orchestrator.py +252 -50
- mcp_agent/workflows/orchestrator/orchestrator_models.py +81 -9
- mcp_agent/workflows/orchestrator/orchestrator_prompts.py +112 -42
- mcp_agent/workflows/router/router_base.py +113 -21
- mcp_agent/workflows/router/router_llm.py +19 -5
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
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
|
-
|
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
|
-
|
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"
|
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"
|
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
|
-
|
80
|
+
<fastagent:data>
|
81
|
+
<fastagent:objective>
|
82
|
+
{objective}
|
83
|
+
</fastagent:objective>
|
71
84
|
|
72
|
-
|
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
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
{agents}
|
93
|
+
<fastagent:status>
|
94
|
+
{plan_status}
|
95
|
+
</fastagent:status>
|
96
|
+
</fastagent:data>
|
84
97
|
|
85
|
-
|
86
|
-
|
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"
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
{
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
261
|
-
|
262
|
-
|
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
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
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
|
-
|
276
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
File without changes
|
File without changes
|
File without changes
|