fast-agent-mcp 0.2.40__py3-none-any.whl → 0.2.42__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 fast-agent-mcp might be problematic. Click here for more details.
- {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/METADATA +2 -1
- {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/RECORD +45 -40
- {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/entry_points.txt +2 -2
- mcp_agent/agents/base_agent.py +111 -1
- mcp_agent/cli/__main__.py +29 -3
- mcp_agent/cli/commands/check_config.py +140 -81
- mcp_agent/cli/commands/go.py +151 -38
- mcp_agent/cli/commands/quickstart.py +6 -2
- mcp_agent/cli/commands/server_helpers.py +106 -0
- mcp_agent/cli/constants.py +25 -0
- mcp_agent/cli/main.py +1 -1
- mcp_agent/config.py +111 -44
- mcp_agent/core/agent_app.py +104 -15
- mcp_agent/core/agent_types.py +5 -1
- mcp_agent/core/direct_decorators.py +38 -0
- mcp_agent/core/direct_factory.py +18 -4
- mcp_agent/core/enhanced_prompt.py +173 -13
- mcp_agent/core/fastagent.py +4 -0
- mcp_agent/core/interactive_prompt.py +37 -37
- mcp_agent/core/usage_display.py +11 -1
- mcp_agent/core/validation.py +21 -2
- mcp_agent/human_input/elicitation_form.py +53 -21
- mcp_agent/llm/augmented_llm.py +28 -9
- mcp_agent/llm/augmented_llm_silent.py +48 -0
- mcp_agent/llm/model_database.py +20 -0
- mcp_agent/llm/model_factory.py +21 -0
- mcp_agent/llm/provider_key_manager.py +22 -8
- mcp_agent/llm/provider_types.py +20 -12
- mcp_agent/llm/providers/augmented_llm_anthropic.py +7 -2
- mcp_agent/llm/providers/augmented_llm_azure.py +7 -1
- mcp_agent/llm/providers/augmented_llm_bedrock.py +1787 -0
- mcp_agent/llm/providers/augmented_llm_google_native.py +4 -1
- mcp_agent/llm/providers/augmented_llm_openai.py +12 -3
- mcp_agent/llm/providers/augmented_llm_xai.py +38 -0
- mcp_agent/llm/usage_tracking.py +28 -3
- mcp_agent/logging/logger.py +7 -0
- mcp_agent/mcp/hf_auth.py +32 -4
- mcp_agent/mcp/mcp_agent_client_session.py +2 -0
- mcp_agent/mcp/mcp_aggregator.py +38 -44
- mcp_agent/mcp/sampling.py +15 -11
- mcp_agent/resources/examples/mcp/elicitations/forms_demo.py +0 -6
- mcp_agent/resources/examples/workflows/router.py +9 -0
- mcp_agent/ui/console_display.py +125 -13
- {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,6 +9,7 @@ from functools import wraps
|
|
|
9
9
|
from typing import (
|
|
10
10
|
Awaitable,
|
|
11
11
|
Callable,
|
|
12
|
+
Dict,
|
|
12
13
|
List,
|
|
13
14
|
Literal,
|
|
14
15
|
Optional,
|
|
@@ -93,6 +94,9 @@ def _decorator_impl(
|
|
|
93
94
|
request_params: RequestParams | None = None,
|
|
94
95
|
human_input: bool = False,
|
|
95
96
|
default: bool = False,
|
|
97
|
+
tools: Optional[Dict[str, List[str]]] = None,
|
|
98
|
+
resources: Optional[Dict[str, List[str]]] = None,
|
|
99
|
+
prompts: Optional[Dict[str, List[str]]] = None,
|
|
96
100
|
**extra_kwargs,
|
|
97
101
|
) -> Callable[[AgentCallable[P, R]], DecoratedAgentProtocol[P, R]]:
|
|
98
102
|
"""
|
|
@@ -133,11 +137,15 @@ def _decorator_impl(
|
|
|
133
137
|
name=name,
|
|
134
138
|
instruction=instruction,
|
|
135
139
|
servers=servers,
|
|
140
|
+
tools=tools,
|
|
141
|
+
resources=resources,
|
|
142
|
+
prompts=prompts,
|
|
136
143
|
model=model,
|
|
137
144
|
use_history=use_history,
|
|
138
145
|
human_input=human_input,
|
|
139
146
|
default=default,
|
|
140
147
|
elicitation_handler=extra_kwargs.get("elicitation_handler"),
|
|
148
|
+
api_key=extra_kwargs.get("api_key"),
|
|
141
149
|
)
|
|
142
150
|
|
|
143
151
|
# Update request params if provided
|
|
@@ -176,12 +184,16 @@ def agent(
|
|
|
176
184
|
*,
|
|
177
185
|
instruction: str = "You are a helpful agent.",
|
|
178
186
|
servers: List[str] = [],
|
|
187
|
+
tools: Optional[Dict[str, List[str]]] = None,
|
|
188
|
+
resources: Optional[Dict[str, List[str]]] = None,
|
|
189
|
+
prompts: Optional[Dict[str, List[str]]] = None,
|
|
179
190
|
model: Optional[str] = None,
|
|
180
191
|
use_history: bool = True,
|
|
181
192
|
request_params: RequestParams | None = None,
|
|
182
193
|
human_input: bool = False,
|
|
183
194
|
default: bool = False,
|
|
184
195
|
elicitation_handler: Optional[ElicitationFnT] = None,
|
|
196
|
+
api_key: str | None = None,
|
|
185
197
|
) -> Callable[[AgentCallable[P, R]], DecoratedAgentProtocol[P, R]]:
|
|
186
198
|
"""
|
|
187
199
|
Decorator to create and register a standard agent with type-safe signature.
|
|
@@ -191,12 +203,16 @@ def agent(
|
|
|
191
203
|
instruction_or_kwarg: Optional positional parameter for instruction
|
|
192
204
|
instruction: Base instruction for the agent (keyword arg)
|
|
193
205
|
servers: List of server names the agent should connect to
|
|
206
|
+
tools: Optional list of tool names or patterns to include
|
|
207
|
+
resources: Optional list of resource names or patterns to include
|
|
208
|
+
prompts: Optional list of prompt names or patterns to include
|
|
194
209
|
model: Model specification string
|
|
195
210
|
use_history: Whether to maintain conversation history
|
|
196
211
|
request_params: Additional request parameters for the LLM
|
|
197
212
|
human_input: Whether to enable human input capabilities
|
|
198
213
|
default: Whether to mark this as the default agent
|
|
199
214
|
elicitation_handler: Custom elicitation handler function (ElicitationFnT)
|
|
215
|
+
api_key: Optional API key for the LLM provider
|
|
200
216
|
|
|
201
217
|
Returns:
|
|
202
218
|
A decorator that registers the agent with proper type annotations
|
|
@@ -215,6 +231,10 @@ def agent(
|
|
|
215
231
|
human_input=human_input,
|
|
216
232
|
default=default,
|
|
217
233
|
elicitation_handler=elicitation_handler,
|
|
234
|
+
tools=tools,
|
|
235
|
+
resources=resources,
|
|
236
|
+
prompts=prompts,
|
|
237
|
+
api_key=api_key,
|
|
218
238
|
)
|
|
219
239
|
|
|
220
240
|
|
|
@@ -226,12 +246,16 @@ def custom(
|
|
|
226
246
|
*,
|
|
227
247
|
instruction: str = "You are a helpful agent.",
|
|
228
248
|
servers: List[str] = [],
|
|
249
|
+
tools: Optional[Dict[str, List[str]]] = None,
|
|
250
|
+
resources: Optional[Dict[str, List[str]]] = None,
|
|
251
|
+
prompts: Optional[Dict[str, List[str]]] = None,
|
|
229
252
|
model: Optional[str] = None,
|
|
230
253
|
use_history: bool = True,
|
|
231
254
|
request_params: RequestParams | None = None,
|
|
232
255
|
human_input: bool = False,
|
|
233
256
|
default: bool = False,
|
|
234
257
|
elicitation_handler: Optional[ElicitationFnT] = None,
|
|
258
|
+
api_key: str | None = None,
|
|
235
259
|
) -> Callable[[AgentCallable[P, R]], DecoratedAgentProtocol[P, R]]:
|
|
236
260
|
"""
|
|
237
261
|
Decorator to create and register a standard agent with type-safe signature.
|
|
@@ -265,6 +289,10 @@ def custom(
|
|
|
265
289
|
agent_class=cls,
|
|
266
290
|
default=default,
|
|
267
291
|
elicitation_handler=elicitation_handler,
|
|
292
|
+
api_key=api_key,
|
|
293
|
+
tools=tools,
|
|
294
|
+
resources=resources,
|
|
295
|
+
prompts=prompts,
|
|
268
296
|
)
|
|
269
297
|
|
|
270
298
|
|
|
@@ -288,6 +316,7 @@ def orchestrator(
|
|
|
288
316
|
plan_type: Literal["full", "iterative"] = "full",
|
|
289
317
|
plan_iterations: int = 5,
|
|
290
318
|
default: bool = False,
|
|
319
|
+
api_key: str | None = None,
|
|
291
320
|
) -> Callable[[AgentCallable[P, R]], DecoratedOrchestratorProtocol[P, R]]:
|
|
292
321
|
"""
|
|
293
322
|
Decorator to create and register an orchestrator agent with type-safe signature.
|
|
@@ -326,6 +355,7 @@ def orchestrator(
|
|
|
326
355
|
plan_type=plan_type,
|
|
327
356
|
plan_iterations=plan_iterations,
|
|
328
357
|
default=default,
|
|
358
|
+
api_key=api_key,
|
|
329
359
|
),
|
|
330
360
|
)
|
|
331
361
|
|
|
@@ -337,6 +367,9 @@ def router(
|
|
|
337
367
|
agents: List[str],
|
|
338
368
|
instruction: Optional[str] = None,
|
|
339
369
|
servers: List[str] = [],
|
|
370
|
+
tools: Optional[Dict[str, List[str]]] = None,
|
|
371
|
+
resources: Optional[Dict[str, List[str]]] = None,
|
|
372
|
+
prompts: Optional[Dict[str, List[str]]] = None,
|
|
340
373
|
model: Optional[str] = None,
|
|
341
374
|
use_history: bool = False,
|
|
342
375
|
request_params: RequestParams | None = None,
|
|
@@ -345,6 +378,7 @@ def router(
|
|
|
345
378
|
elicitation_handler: Optional[
|
|
346
379
|
ElicitationFnT
|
|
347
380
|
] = None, ## exclude from docs, decide whether allowable
|
|
381
|
+
api_key: str | None = None,
|
|
348
382
|
) -> Callable[[AgentCallable[P, R]], DecoratedRouterProtocol[P, R]]:
|
|
349
383
|
"""
|
|
350
384
|
Decorator to create and register a router agent with type-safe signature.
|
|
@@ -383,6 +417,10 @@ def router(
|
|
|
383
417
|
default=default,
|
|
384
418
|
router_agents=agents,
|
|
385
419
|
elicitation_handler=elicitation_handler,
|
|
420
|
+
api_key=api_key,
|
|
421
|
+
tools=tools,
|
|
422
|
+
prompts=prompts,
|
|
423
|
+
resources=resources,
|
|
386
424
|
),
|
|
387
425
|
)
|
|
388
426
|
|
mcp_agent/core/direct_factory.py
CHANGED
|
@@ -150,7 +150,11 @@ async def create_agents_by_type(
|
|
|
150
150
|
|
|
151
151
|
# Attach LLM to the agent
|
|
152
152
|
llm_factory = model_factory_func(model=config.model)
|
|
153
|
-
await agent.attach_llm(
|
|
153
|
+
await agent.attach_llm(
|
|
154
|
+
llm_factory,
|
|
155
|
+
request_params=config.default_request_params,
|
|
156
|
+
api_key=config.api_key
|
|
157
|
+
)
|
|
154
158
|
result_agents[name] = agent
|
|
155
159
|
|
|
156
160
|
elif agent_type == AgentType.CUSTOM:
|
|
@@ -165,7 +169,11 @@ async def create_agents_by_type(
|
|
|
165
169
|
|
|
166
170
|
# Attach LLM to the agent
|
|
167
171
|
llm_factory = model_factory_func(model=config.model)
|
|
168
|
-
await agent.attach_llm(
|
|
172
|
+
await agent.attach_llm(
|
|
173
|
+
llm_factory,
|
|
174
|
+
request_params=config.default_request_params,
|
|
175
|
+
api_key=config.api_key
|
|
176
|
+
)
|
|
169
177
|
result_agents[name] = agent
|
|
170
178
|
|
|
171
179
|
elif agent_type == AgentType.ORCHESTRATOR:
|
|
@@ -200,7 +208,9 @@ async def create_agents_by_type(
|
|
|
200
208
|
# Attach LLM to the orchestrator
|
|
201
209
|
llm_factory = model_factory_func(model=config.model)
|
|
202
210
|
await orchestrator.attach_llm(
|
|
203
|
-
llm_factory,
|
|
211
|
+
llm_factory,
|
|
212
|
+
request_params=config.default_request_params,
|
|
213
|
+
api_key=config.api_key
|
|
204
214
|
)
|
|
205
215
|
|
|
206
216
|
result_agents[name] = orchestrator
|
|
@@ -261,7 +271,11 @@ async def create_agents_by_type(
|
|
|
261
271
|
|
|
262
272
|
# Attach LLM to the router
|
|
263
273
|
llm_factory = model_factory_func(model=config.model)
|
|
264
|
-
await router.attach_llm(
|
|
274
|
+
await router.attach_llm(
|
|
275
|
+
llm_factory,
|
|
276
|
+
request_params=config.default_request_params,
|
|
277
|
+
api_key=config.api_key
|
|
278
|
+
)
|
|
265
279
|
result_agents[name] = router
|
|
266
280
|
|
|
267
281
|
elif agent_type == AgentType.CHAIN:
|
|
@@ -68,30 +68,189 @@ async def _display_agent_info_helper(agent_name: str, agent_provider: object) ->
|
|
|
68
68
|
len(tools_result.tools) if tools_result and hasattr(tools_result, "tools") else 0
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
+
resources_dict = await agent.list_resources()
|
|
72
|
+
resource_count = sum(len(resources) for resources in resources_dict.values()) if resources_dict else 0
|
|
73
|
+
|
|
71
74
|
prompts_dict = await agent.list_prompts()
|
|
72
75
|
prompt_count = sum(len(prompts) for prompts in prompts_dict.values()) if prompts_dict else 0
|
|
73
76
|
|
|
74
|
-
#
|
|
75
|
-
if
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
77
|
+
# Handle different agent types
|
|
78
|
+
if agent.agent_type == AgentType.PARALLEL:
|
|
79
|
+
# Count child agents for parallel agents
|
|
80
|
+
child_count = 0
|
|
81
|
+
if hasattr(agent, "fan_out_agents") and agent.fan_out_agents:
|
|
82
|
+
child_count += len(agent.fan_out_agents)
|
|
83
|
+
if hasattr(agent, "fan_in_agent") and agent.fan_in_agent:
|
|
84
|
+
child_count += 1
|
|
85
|
+
|
|
86
|
+
if child_count > 0:
|
|
87
|
+
child_word = "child agent" if child_count == 1 else "child agents"
|
|
88
|
+
rich_print(
|
|
89
|
+
f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {child_count:,}[dim] {child_word}[/dim]"
|
|
90
|
+
)
|
|
91
|
+
elif agent.agent_type == AgentType.ROUTER:
|
|
92
|
+
# Count child agents for router agents
|
|
93
|
+
child_count = 0
|
|
94
|
+
if hasattr(agent, "routing_agents") and agent.routing_agents:
|
|
95
|
+
child_count = len(agent.routing_agents)
|
|
96
|
+
elif hasattr(agent, "agents") and agent.agents:
|
|
97
|
+
child_count = len(agent.agents)
|
|
98
|
+
|
|
99
|
+
if child_count > 0:
|
|
100
|
+
child_word = "child agent" if child_count == 1 else "child agents"
|
|
101
|
+
rich_print(
|
|
102
|
+
f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {child_count:,}[dim] {child_word}[/dim]"
|
|
103
|
+
)
|
|
79
104
|
else:
|
|
105
|
+
# For regular agents, only display if they have MCP servers attached
|
|
106
|
+
if server_count > 0:
|
|
107
|
+
# Pluralization helpers
|
|
108
|
+
server_word = "Server" if server_count == 1 else "Servers"
|
|
109
|
+
tool_word = "tool" if tool_count == 1 else "tools"
|
|
110
|
+
resource_word = "resource" if resource_count == 1 else "resources"
|
|
111
|
+
prompt_word = "prompt" if prompt_count == 1 else "prompts"
|
|
112
|
+
|
|
113
|
+
rich_print(
|
|
114
|
+
f"[dim]Agent [/dim][blue]{agent_name}[/blue][dim]:[/dim] {server_count:,}[dim] MCP {server_word}, [/dim]{tool_count:,}[dim] {tool_word}, [/dim]{resource_count:,}[dim] {resource_word}, [/dim]{prompt_count:,}[dim] {prompt_word} available[/dim]"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
# Mark as shown
|
|
118
|
+
_agent_info_shown.add(agent_name)
|
|
119
|
+
|
|
120
|
+
except Exception:
|
|
121
|
+
# Silently ignore errors to not disrupt the user experience
|
|
122
|
+
pass
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
async def _display_all_agents_with_hierarchy(available_agents: List[str], agent_provider) -> None:
|
|
126
|
+
"""Display all agents with tree structure for workflow agents."""
|
|
127
|
+
# Track which agents are children to avoid displaying them twice
|
|
128
|
+
child_agents = set()
|
|
129
|
+
|
|
130
|
+
# First pass: identify all child agents
|
|
131
|
+
for agent_name in available_agents:
|
|
132
|
+
try:
|
|
133
|
+
if hasattr(agent_provider, "_agent"):
|
|
134
|
+
agent = agent_provider._agent(agent_name)
|
|
135
|
+
else:
|
|
136
|
+
agent = agent_provider
|
|
137
|
+
|
|
138
|
+
if agent.agent_type == AgentType.PARALLEL:
|
|
139
|
+
if hasattr(agent, "fan_out_agents") and agent.fan_out_agents:
|
|
140
|
+
for child_agent in agent.fan_out_agents:
|
|
141
|
+
child_agents.add(child_agent.name)
|
|
142
|
+
if hasattr(agent, "fan_in_agent") and agent.fan_in_agent:
|
|
143
|
+
child_agents.add(agent.fan_in_agent.name)
|
|
144
|
+
elif agent.agent_type == AgentType.ROUTER:
|
|
145
|
+
if hasattr(agent, "routing_agents") and agent.routing_agents:
|
|
146
|
+
for child_agent in agent.routing_agents:
|
|
147
|
+
child_agents.add(child_agent.name)
|
|
148
|
+
elif hasattr(agent, "agents") and agent.agents:
|
|
149
|
+
for child_agent in agent.agents:
|
|
150
|
+
child_agents.add(child_agent.name)
|
|
151
|
+
except Exception:
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
# Second pass: display agents (parents with children, standalone agents without children)
|
|
155
|
+
for agent_name in sorted(available_agents):
|
|
156
|
+
# Skip if this agent is a child of another agent
|
|
157
|
+
if agent_name in child_agents:
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
if hasattr(agent_provider, "_agent"):
|
|
162
|
+
agent = agent_provider._agent(agent_name)
|
|
163
|
+
else:
|
|
164
|
+
agent = agent_provider
|
|
165
|
+
|
|
166
|
+
# Display parent agent
|
|
167
|
+
await _display_agent_info_helper(agent_name, agent_provider)
|
|
168
|
+
|
|
169
|
+
# If it's a workflow agent, display its children
|
|
170
|
+
if agent.agent_type == AgentType.PARALLEL:
|
|
171
|
+
await _display_parallel_children(agent, agent_provider)
|
|
172
|
+
elif agent.agent_type == AgentType.ROUTER:
|
|
173
|
+
await _display_router_children(agent, agent_provider)
|
|
174
|
+
|
|
175
|
+
except Exception:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async def _display_parallel_children(parallel_agent, agent_provider) -> None:
|
|
180
|
+
"""Display child agents of a parallel agent in tree format."""
|
|
181
|
+
children = []
|
|
182
|
+
|
|
183
|
+
# Collect fan-out agents
|
|
184
|
+
if hasattr(parallel_agent, "fan_out_agents") and parallel_agent.fan_out_agents:
|
|
185
|
+
for child_agent in parallel_agent.fan_out_agents:
|
|
186
|
+
children.append(child_agent)
|
|
187
|
+
|
|
188
|
+
# Collect fan-in agent
|
|
189
|
+
if hasattr(parallel_agent, "fan_in_agent") and parallel_agent.fan_in_agent:
|
|
190
|
+
children.append(parallel_agent.fan_in_agent)
|
|
191
|
+
|
|
192
|
+
# Display children with tree formatting
|
|
193
|
+
for i, child_agent in enumerate(children):
|
|
194
|
+
is_last = i == len(children) - 1
|
|
195
|
+
prefix = "└─" if is_last else "├─"
|
|
196
|
+
await _display_child_agent_info(child_agent, prefix, agent_provider)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
async def _display_router_children(router_agent, agent_provider) -> None:
|
|
200
|
+
"""Display child agents of a router agent in tree format."""
|
|
201
|
+
children = []
|
|
202
|
+
|
|
203
|
+
# Collect routing agents
|
|
204
|
+
if hasattr(router_agent, "routing_agents") and router_agent.routing_agents:
|
|
205
|
+
children = router_agent.routing_agents
|
|
206
|
+
elif hasattr(router_agent, "agents") and router_agent.agents:
|
|
207
|
+
children = router_agent.agents
|
|
208
|
+
|
|
209
|
+
# Display children with tree formatting
|
|
210
|
+
for i, child_agent in enumerate(children):
|
|
211
|
+
is_last = i == len(children) - 1
|
|
212
|
+
prefix = "└─" if is_last else "├─"
|
|
213
|
+
await _display_child_agent_info(child_agent, prefix, agent_provider)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
async def _display_child_agent_info(child_agent, prefix: str, agent_provider) -> None:
|
|
217
|
+
"""Display info for a child agent with tree prefix."""
|
|
218
|
+
try:
|
|
219
|
+
# Get counts for child agent
|
|
220
|
+
servers = await child_agent.list_servers()
|
|
221
|
+
server_count = len(servers) if servers else 0
|
|
222
|
+
|
|
223
|
+
tools_result = await child_agent.list_tools()
|
|
224
|
+
tool_count = (
|
|
225
|
+
len(tools_result.tools) if tools_result and hasattr(tools_result, "tools") else 0
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
resources_dict = await child_agent.list_resources()
|
|
229
|
+
resource_count = sum(len(resources) for resources in resources_dict.values()) if resources_dict else 0
|
|
230
|
+
|
|
231
|
+
prompts_dict = await child_agent.list_prompts()
|
|
232
|
+
prompt_count = sum(len(prompts) for prompts in prompts_dict.values()) if prompts_dict else 0
|
|
233
|
+
|
|
234
|
+
# Only display if child has MCP servers
|
|
235
|
+
if server_count > 0:
|
|
80
236
|
# Pluralization helpers
|
|
81
237
|
server_word = "Server" if server_count == 1 else "Servers"
|
|
82
238
|
tool_word = "tool" if tool_count == 1 else "tools"
|
|
239
|
+
resource_word = "resource" if resource_count == 1 else "resources"
|
|
83
240
|
prompt_word = "prompt" if prompt_count == 1 else "prompts"
|
|
84
241
|
|
|
85
242
|
rich_print(
|
|
86
|
-
f"[dim]
|
|
243
|
+
f"[dim] {prefix} [/dim][blue]{child_agent.name}[/blue][dim]:[/dim] {server_count:,}[dim] MCP {server_word}, [/dim]{tool_count:,}[dim] {tool_word}, [/dim]{resource_count:,}[dim] {resource_word}, [/dim]{prompt_count:,}[dim] {prompt_word} available[/dim]"
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
# Show child even without MCP servers for context
|
|
247
|
+
rich_print(
|
|
248
|
+
f"[dim] {prefix} [/dim][blue]{child_agent.name}[/blue][dim]: No MCP Servers[/dim]"
|
|
87
249
|
)
|
|
88
|
-
|
|
89
|
-
# Mark as shown
|
|
90
|
-
_agent_info_shown.add(agent_name)
|
|
91
250
|
|
|
92
251
|
except Exception:
|
|
93
|
-
#
|
|
94
|
-
|
|
252
|
+
# Fallback: just show the name
|
|
253
|
+
rich_print(f"[dim] {prefix} [/dim][blue]{child_agent.name}[/blue]")
|
|
95
254
|
|
|
96
255
|
|
|
97
256
|
class AgentCompleter(Completer):
|
|
@@ -429,12 +588,13 @@ async def get_enhanced_input(
|
|
|
429
588
|
rich_print("[dim]Type /help for commands. Ctrl+T toggles multiline mode.[/dim]")
|
|
430
589
|
else:
|
|
431
590
|
rich_print(
|
|
432
|
-
"[dim]Type /
|
|
591
|
+
"[dim]Type '/' for commands, '@' to switch agent. Ctrl+T multiline, CTRL+E external editor.[/dim]\n"
|
|
433
592
|
)
|
|
434
593
|
|
|
435
594
|
# Display agent info right after help text if agent_provider is available
|
|
436
595
|
if agent_provider and not is_human_input:
|
|
437
|
-
|
|
596
|
+
# Display info for all available agents with tree structure for workflows
|
|
597
|
+
await _display_all_agents_with_hierarchy(available_agents, agent_provider)
|
|
438
598
|
|
|
439
599
|
rich_print()
|
|
440
600
|
help_message_shown = True
|
mcp_agent/core/fastagent.py
CHANGED
|
@@ -56,6 +56,7 @@ from mcp_agent.core.exceptions import (
|
|
|
56
56
|
)
|
|
57
57
|
from mcp_agent.core.usage_display import display_usage_report
|
|
58
58
|
from mcp_agent.core.validation import (
|
|
59
|
+
validate_provider_keys_post_creation,
|
|
59
60
|
validate_server_references,
|
|
60
61
|
validate_workflow_references,
|
|
61
62
|
)
|
|
@@ -313,6 +314,9 @@ class FastAgent:
|
|
|
313
314
|
self.agents,
|
|
314
315
|
model_factory_func,
|
|
315
316
|
)
|
|
317
|
+
|
|
318
|
+
# Validate API keys after agent creation
|
|
319
|
+
validate_provider_keys_post_creation(active_agents)
|
|
316
320
|
|
|
317
321
|
# Create a wrapper with all agents for simplified access
|
|
318
322
|
wrapper = AgentApp(active_agents)
|
|
@@ -53,6 +53,7 @@ class PromptProvider(Protocol):
|
|
|
53
53
|
async def apply_prompt(
|
|
54
54
|
self,
|
|
55
55
|
prompt_name: str,
|
|
56
|
+
prompt_title: Optional[str] = None,
|
|
56
57
|
arguments: Optional[Dict[str, str]] = None,
|
|
57
58
|
agent_name: Optional[str] = None,
|
|
58
59
|
**kwargs,
|
|
@@ -243,9 +244,10 @@ class InteractivePrompt:
|
|
|
243
244
|
"server": server_name,
|
|
244
245
|
"name": prompt.name,
|
|
245
246
|
"namespaced_name": f"{server_name}{SEP}{prompt.name}",
|
|
246
|
-
"
|
|
247
|
-
"
|
|
248
|
-
"
|
|
247
|
+
"title": prompt.title or None,
|
|
248
|
+
"description": prompt.description or "No description",
|
|
249
|
+
"arg_count": len(prompt.arguments or []),
|
|
250
|
+
"arguments": prompt.arguments or [],
|
|
249
251
|
}
|
|
250
252
|
)
|
|
251
253
|
elif isinstance(prompts_info, list) and prompts_info:
|
|
@@ -256,6 +258,7 @@ class InteractivePrompt:
|
|
|
256
258
|
"server": server_name,
|
|
257
259
|
"name": prompt["name"],
|
|
258
260
|
"namespaced_name": f"{server_name}{SEP}{prompt['name']}",
|
|
261
|
+
"title": prompt.get("title", None),
|
|
259
262
|
"description": prompt.get("description", "No description"),
|
|
260
263
|
"arg_count": len(prompt.get("arguments", [])),
|
|
261
264
|
"arguments": prompt.get("arguments", []),
|
|
@@ -263,17 +266,15 @@ class InteractivePrompt:
|
|
|
263
266
|
)
|
|
264
267
|
else:
|
|
265
268
|
# Handle Prompt objects from mcp.types
|
|
266
|
-
prompt_name = getattr(prompt, "name", str(prompt))
|
|
267
|
-
description = getattr(prompt, "description", "No description")
|
|
268
|
-
arguments = getattr(prompt, "arguments", [])
|
|
269
269
|
all_prompts.append(
|
|
270
270
|
{
|
|
271
271
|
"server": server_name,
|
|
272
|
-
"name":
|
|
273
|
-
"namespaced_name": f"{server_name}{SEP}{
|
|
274
|
-
"
|
|
275
|
-
"
|
|
276
|
-
"
|
|
272
|
+
"name": prompt.name,
|
|
273
|
+
"namespaced_name": f"{server_name}{SEP}{prompt.name}",
|
|
274
|
+
"title": prompt.title or None,
|
|
275
|
+
"description": prompt.description or "No description",
|
|
276
|
+
"arg_count": len(prompt.arguments or []),
|
|
277
|
+
"arguments": prompt.arguments or [],
|
|
277
278
|
}
|
|
278
279
|
)
|
|
279
280
|
|
|
@@ -314,6 +315,7 @@ class InteractivePrompt:
|
|
|
314
315
|
table.add_column("#", justify="right", style="cyan")
|
|
315
316
|
table.add_column("Server", style="green")
|
|
316
317
|
table.add_column("Prompt Name", style="bright_blue")
|
|
318
|
+
table.add_column("Title")
|
|
317
319
|
table.add_column("Description")
|
|
318
320
|
table.add_column("Args", justify="center")
|
|
319
321
|
|
|
@@ -323,6 +325,7 @@ class InteractivePrompt:
|
|
|
323
325
|
str(i + 1),
|
|
324
326
|
prompt["server"],
|
|
325
327
|
prompt["name"],
|
|
328
|
+
prompt["title"],
|
|
326
329
|
prompt["description"],
|
|
327
330
|
str(prompt["arg_count"]),
|
|
328
331
|
)
|
|
@@ -378,7 +381,7 @@ class InteractivePrompt:
|
|
|
378
381
|
continue
|
|
379
382
|
|
|
380
383
|
# Extract prompts
|
|
381
|
-
prompts = []
|
|
384
|
+
prompts: List[Prompt] = []
|
|
382
385
|
if hasattr(prompts_info, "prompts"):
|
|
383
386
|
prompts = prompts_info.prompts
|
|
384
387
|
elif isinstance(prompts_info, list):
|
|
@@ -387,8 +390,9 @@ class InteractivePrompt:
|
|
|
387
390
|
# Process each prompt
|
|
388
391
|
for prompt in prompts:
|
|
389
392
|
# Get basic prompt info
|
|
390
|
-
prompt_name =
|
|
391
|
-
|
|
393
|
+
prompt_name = prompt.name
|
|
394
|
+
prompt_title = prompt.title or None
|
|
395
|
+
prompt_description = prompt.description or "No description"
|
|
392
396
|
|
|
393
397
|
# Extract argument information
|
|
394
398
|
arg_names = []
|
|
@@ -397,23 +401,19 @@ class InteractivePrompt:
|
|
|
397
401
|
arg_descriptions = {}
|
|
398
402
|
|
|
399
403
|
# Get arguments list
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if getattr(arg, "required", False):
|
|
414
|
-
required_args.append(name)
|
|
415
|
-
else:
|
|
416
|
-
optional_args.append(name)
|
|
404
|
+
if prompt.arguments:
|
|
405
|
+
for arg in prompt.arguments:
|
|
406
|
+
arg_names.append(arg.name)
|
|
407
|
+
|
|
408
|
+
# Store description if available
|
|
409
|
+
if arg.description:
|
|
410
|
+
arg_descriptions[arg.name] = arg.description
|
|
411
|
+
|
|
412
|
+
# Check if required
|
|
413
|
+
if arg.required:
|
|
414
|
+
required_args.append(arg.name)
|
|
415
|
+
else:
|
|
416
|
+
optional_args.append(arg.name)
|
|
417
417
|
|
|
418
418
|
# Create namespaced version using the consistent separator
|
|
419
419
|
namespaced_name = f"{server_name}{SEP}{prompt_name}"
|
|
@@ -424,6 +424,7 @@ class InteractivePrompt:
|
|
|
424
424
|
"server": server_name,
|
|
425
425
|
"name": prompt_name,
|
|
426
426
|
"namespaced_name": namespaced_name,
|
|
427
|
+
"title": prompt_title,
|
|
427
428
|
"description": prompt_description,
|
|
428
429
|
"arg_count": len(arg_names),
|
|
429
430
|
"arg_names": arg_names,
|
|
@@ -486,6 +487,7 @@ class InteractivePrompt:
|
|
|
486
487
|
table.add_column("#", justify="right", style="cyan")
|
|
487
488
|
table.add_column("Server", style="green")
|
|
488
489
|
table.add_column("Prompt Name", style="bright_blue")
|
|
490
|
+
table.add_column("Title")
|
|
489
491
|
table.add_column("Description")
|
|
490
492
|
table.add_column("Args", justify="center")
|
|
491
493
|
|
|
@@ -508,6 +510,7 @@ class InteractivePrompt:
|
|
|
508
510
|
str(i + 1),
|
|
509
511
|
prompt["server"],
|
|
510
512
|
prompt["name"],
|
|
513
|
+
prompt["title"] or "No title",
|
|
511
514
|
prompt["description"] or "No description",
|
|
512
515
|
args_display,
|
|
513
516
|
)
|
|
@@ -669,6 +672,7 @@ class InteractivePrompt:
|
|
|
669
672
|
table = Table(title="Available MCP Tools")
|
|
670
673
|
table.add_column("#", justify="right", style="cyan")
|
|
671
674
|
table.add_column("Tool Name", style="bright_blue")
|
|
675
|
+
table.add_column("Title")
|
|
672
676
|
table.add_column("Description")
|
|
673
677
|
|
|
674
678
|
# Add tools to table
|
|
@@ -676,16 +680,12 @@ class InteractivePrompt:
|
|
|
676
680
|
table.add_row(
|
|
677
681
|
str(i + 1),
|
|
678
682
|
tool.name,
|
|
679
|
-
|
|
683
|
+
tool.title or "No title",
|
|
684
|
+
tool.description or "No description",
|
|
680
685
|
)
|
|
681
686
|
|
|
682
687
|
console.print(table)
|
|
683
688
|
|
|
684
|
-
# Add usage instructions
|
|
685
|
-
rich_print("\n[bold]Usage:[/bold]")
|
|
686
|
-
rich_print(" • Tools are automatically available in your conversation")
|
|
687
|
-
rich_print(" • Just ask the agent to use a tool by name or description")
|
|
688
|
-
|
|
689
689
|
except Exception as e:
|
|
690
690
|
import traceback
|
|
691
691
|
|