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.

Files changed (45) hide show
  1. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/METADATA +2 -1
  2. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/RECORD +45 -40
  3. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/entry_points.txt +2 -2
  4. mcp_agent/agents/base_agent.py +111 -1
  5. mcp_agent/cli/__main__.py +29 -3
  6. mcp_agent/cli/commands/check_config.py +140 -81
  7. mcp_agent/cli/commands/go.py +151 -38
  8. mcp_agent/cli/commands/quickstart.py +6 -2
  9. mcp_agent/cli/commands/server_helpers.py +106 -0
  10. mcp_agent/cli/constants.py +25 -0
  11. mcp_agent/cli/main.py +1 -1
  12. mcp_agent/config.py +111 -44
  13. mcp_agent/core/agent_app.py +104 -15
  14. mcp_agent/core/agent_types.py +5 -1
  15. mcp_agent/core/direct_decorators.py +38 -0
  16. mcp_agent/core/direct_factory.py +18 -4
  17. mcp_agent/core/enhanced_prompt.py +173 -13
  18. mcp_agent/core/fastagent.py +4 -0
  19. mcp_agent/core/interactive_prompt.py +37 -37
  20. mcp_agent/core/usage_display.py +11 -1
  21. mcp_agent/core/validation.py +21 -2
  22. mcp_agent/human_input/elicitation_form.py +53 -21
  23. mcp_agent/llm/augmented_llm.py +28 -9
  24. mcp_agent/llm/augmented_llm_silent.py +48 -0
  25. mcp_agent/llm/model_database.py +20 -0
  26. mcp_agent/llm/model_factory.py +21 -0
  27. mcp_agent/llm/provider_key_manager.py +22 -8
  28. mcp_agent/llm/provider_types.py +20 -12
  29. mcp_agent/llm/providers/augmented_llm_anthropic.py +7 -2
  30. mcp_agent/llm/providers/augmented_llm_azure.py +7 -1
  31. mcp_agent/llm/providers/augmented_llm_bedrock.py +1787 -0
  32. mcp_agent/llm/providers/augmented_llm_google_native.py +4 -1
  33. mcp_agent/llm/providers/augmented_llm_openai.py +12 -3
  34. mcp_agent/llm/providers/augmented_llm_xai.py +38 -0
  35. mcp_agent/llm/usage_tracking.py +28 -3
  36. mcp_agent/logging/logger.py +7 -0
  37. mcp_agent/mcp/hf_auth.py +32 -4
  38. mcp_agent/mcp/mcp_agent_client_session.py +2 -0
  39. mcp_agent/mcp/mcp_aggregator.py +38 -44
  40. mcp_agent/mcp/sampling.py +15 -11
  41. mcp_agent/resources/examples/mcp/elicitations/forms_demo.py +0 -6
  42. mcp_agent/resources/examples/workflows/router.py +9 -0
  43. mcp_agent/ui/console_display.py +125 -13
  44. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/WHEEL +0 -0
  45. {fast_agent_mcp-0.2.40.dist-info → fast_agent_mcp-0.2.42.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,106 @@
1
+ """Helper functions for server configuration and naming."""
2
+
3
+ from typing import Any, Dict
4
+
5
+
6
+ def generate_server_name(identifier: str) -> str:
7
+ """Generate a clean server name from various identifiers.
8
+
9
+ Args:
10
+ identifier: Package name, file path, or other identifier
11
+
12
+ Returns:
13
+ Clean server name with only alphanumeric and underscore characters
14
+
15
+ Examples:
16
+ >>> generate_server_name("@modelcontextprotocol/server-filesystem")
17
+ 'server_filesystem'
18
+ >>> generate_server_name("./src/my-server.py")
19
+ 'src_my_server'
20
+ >>> generate_server_name("my-mcp-server")
21
+ 'my_mcp_server'
22
+ """
23
+
24
+ # Remove leading ./ if present
25
+ if identifier.startswith("./"):
26
+ identifier = identifier[2:]
27
+
28
+ # Handle npm package names with org prefix (only if no file extension)
29
+ has_file_ext = any(identifier.endswith(ext) for ext in [".py", ".js", ".ts"])
30
+ if "/" in identifier and not has_file_ext:
31
+ # This is likely an npm package, take the part after the last slash
32
+ identifier = identifier.split("/")[-1]
33
+
34
+ # Remove file extension for common script files
35
+ for ext in [".py", ".js", ".ts"]:
36
+ if identifier.endswith(ext):
37
+ identifier = identifier[: -len(ext)]
38
+ break
39
+
40
+ # Replace special characters with underscores
41
+ # Remove @ prefix if present
42
+ identifier = identifier.lstrip("@")
43
+
44
+ # Replace non-alphanumeric characters with underscores
45
+ server_name = ""
46
+ for char in identifier:
47
+ if char.isalnum():
48
+ server_name += char
49
+ else:
50
+ server_name += "_"
51
+
52
+ # Clean up multiple underscores
53
+ while "__" in server_name:
54
+ server_name = server_name.replace("__", "_")
55
+
56
+ # Remove leading/trailing underscores
57
+ server_name = server_name.strip("_")
58
+
59
+ return server_name
60
+
61
+
62
+ async def add_servers_to_config(fast_app: Any, servers: Dict[str, Dict[str, Any]]) -> None:
63
+ """Add server configurations to the FastAgent app config.
64
+
65
+ This function handles the repetitive initialization and configuration
66
+ of MCP servers, ensuring the app is initialized and the config
67
+ structure exists before adding servers.
68
+
69
+ Args:
70
+ fast_app: The FastAgent instance
71
+ servers: Dictionary of server configurations
72
+ """
73
+ if not servers:
74
+ return
75
+
76
+ from mcp_agent.config import MCPServerSettings, MCPSettings
77
+
78
+ # Initialize the app to ensure context is ready
79
+ await fast_app.app.initialize()
80
+
81
+ # Initialize mcp settings if needed
82
+ if not hasattr(fast_app.app.context.config, "mcp"):
83
+ fast_app.app.context.config.mcp = MCPSettings()
84
+
85
+ # Initialize servers dictionary if needed
86
+ if (
87
+ not hasattr(fast_app.app.context.config.mcp, "servers")
88
+ or fast_app.app.context.config.mcp.servers is None
89
+ ):
90
+ fast_app.app.context.config.mcp.servers = {}
91
+
92
+ # Add each server to the config
93
+ for server_name, server_config in servers.items():
94
+ # Build server settings based on transport type
95
+ server_settings = {"transport": server_config["transport"]}
96
+
97
+ # Add transport-specific settings
98
+ if server_config["transport"] == "stdio":
99
+ server_settings["command"] = server_config["command"]
100
+ server_settings["args"] = server_config["args"]
101
+ elif server_config["transport"] in ["http", "sse"]:
102
+ server_settings["url"] = server_config["url"]
103
+ if "headers" in server_config:
104
+ server_settings["headers"] = server_config["headers"]
105
+
106
+ fast_app.app.context.config.mcp.servers[server_name] = MCPServerSettings(**server_settings)
@@ -0,0 +1,25 @@
1
+ """Shared constants for CLI routing and commands."""
2
+
3
+ # Options that should automatically route to the 'go' command
4
+ GO_SPECIFIC_OPTIONS = {
5
+ "--npx",
6
+ "--uvx",
7
+ "--stdio",
8
+ "--url",
9
+ "--model",
10
+ "--models",
11
+ "--instruction",
12
+ "-i",
13
+ "--message",
14
+ "-m",
15
+ "--prompt-file",
16
+ "-p",
17
+ "--servers",
18
+ "--auth",
19
+ "--name",
20
+ "--config-path",
21
+ "-c",
22
+ }
23
+
24
+ # Known subcommands that should not trigger auto-routing
25
+ KNOWN_SUBCOMMANDS = {"go", "setup", "check", "bootstrap", "quickstart", "--help", "-h", "--version"}
mcp_agent/cli/main.py CHANGED
@@ -48,7 +48,7 @@ def show_welcome() -> None:
48
48
  console.print(table)
49
49
 
50
50
  console.print(
51
- "\n[italic]get started with:[/italic] [bold][cyan]fast-agent[/cyan][/bold] [green]setup[/green]"
51
+ "\n[italic]get started with:[/italic] [bold][cyan]fast-agent[/cyan][/bold] [green]setup[/green]. visit [cyan][link=https://fast-agent.ai]fast-agent.ai[/link][/cyan] for more information."
52
52
  )
53
53
 
54
54
 
mcp_agent/config.py CHANGED
@@ -6,7 +6,7 @@ for the application configuration.
6
6
  import os
7
7
  import re
8
8
  from pathlib import Path
9
- from typing import Any, Dict, List, Literal, Optional
9
+ from typing import Any, Dict, List, Literal, Optional, Tuple
10
10
 
11
11
  from pydantic import BaseModel, ConfigDict, field_validator
12
12
  from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -175,6 +175,17 @@ class GoogleSettings(BaseModel):
175
175
  model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
176
176
 
177
177
 
178
+ class XAISettings(BaseModel):
179
+ """
180
+ Settings for using xAI Grok models in the fast-agent application.
181
+ """
182
+
183
+ api_key: str | None = None
184
+ base_url: str | None = "https://api.x.ai/v1"
185
+
186
+ model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
187
+
188
+
178
189
  class GenericSettings(BaseModel):
179
190
  """
180
191
  Settings for using OpenAI models in the fast-agent application.
@@ -242,6 +253,20 @@ class TensorZeroSettings(BaseModel):
242
253
  model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
243
254
 
244
255
 
256
+ class BedrockSettings(BaseModel):
257
+ """
258
+ Settings for using AWS Bedrock models in the fast-agent application.
259
+ """
260
+
261
+ region: str | None = None
262
+ """AWS region for Bedrock service"""
263
+
264
+ profile: str | None = None
265
+ """AWS profile to use for authentication"""
266
+
267
+ model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
268
+
269
+
245
270
  class HuggingFaceSettings(BaseModel):
246
271
  """
247
272
  Settings for HuggingFace authentication (used for MCP connections).
@@ -296,6 +321,57 @@ class LoggerSettings(BaseModel):
296
321
  """Enable markup in console output. Disable for outputs that may conflict with rich console formatting"""
297
322
 
298
323
 
324
+ def find_fastagent_config_files(start_path: Path) -> Tuple[Optional[Path], Optional[Path]]:
325
+ """
326
+ Find FastAgent configuration files with standardized behavior.
327
+
328
+ Returns:
329
+ Tuple of (config_path, secrets_path) where either can be None if not found.
330
+
331
+ Strategy:
332
+ 1. Find config file recursively from start_path upward
333
+ 2. Prefer secrets file in same directory as config file
334
+ 3. If no secrets file next to config, search recursively from start_path
335
+ """
336
+ config_path = None
337
+ secrets_path = None
338
+
339
+ # First, find the config file with recursive search
340
+ current = start_path.resolve()
341
+ while current != current.parent:
342
+ potential_config = current / "fastagent.config.yaml"
343
+ if potential_config.exists():
344
+ config_path = potential_config
345
+ break
346
+ current = current.parent
347
+
348
+ # If config file found, prefer secrets file in the same directory
349
+ if config_path:
350
+ potential_secrets = config_path.parent / "fastagent.secrets.yaml"
351
+ if potential_secrets.exists():
352
+ secrets_path = potential_secrets
353
+ else:
354
+ # If no secrets file next to config, do recursive search from start
355
+ current = start_path.resolve()
356
+ while current != current.parent:
357
+ potential_secrets = current / "fastagent.secrets.yaml"
358
+ if potential_secrets.exists():
359
+ secrets_path = potential_secrets
360
+ break
361
+ current = current.parent
362
+ else:
363
+ # No config file found, just search for secrets file
364
+ current = start_path.resolve()
365
+ while current != current.parent:
366
+ potential_secrets = current / "fastagent.secrets.yaml"
367
+ if potential_secrets.exists():
368
+ secrets_path = potential_secrets
369
+ break
370
+ current = current.parent
371
+
372
+ return config_path, secrets_path
373
+
374
+
299
375
  class Settings(BaseSettings):
300
376
  """
301
377
  Settings class for the fast-agent application.
@@ -339,6 +415,9 @@ class Settings(BaseSettings):
339
415
  google: GoogleSettings | None = None
340
416
  """Settings for using DeepSeek models in the fast-agent application"""
341
417
 
418
+ xai: XAISettings | None = None
419
+ """Settings for using xAI Grok models in the fast-agent application"""
420
+
342
421
  openrouter: OpenRouterSettings | None = None
343
422
  """Settings for using OpenRouter models in the fast-agent application"""
344
423
 
@@ -354,6 +433,9 @@ class Settings(BaseSettings):
354
433
  aliyun: OpenAISettings | None = None
355
434
  """Settings for using Aliyun OpenAI Service in the fast-agent application"""
356
435
 
436
+ bedrock: BedrockSettings | None = None
437
+ """Settings for using AWS Bedrock models in the fast-agent application"""
438
+
357
439
  huggingface: HuggingFaceSettings | None = None
358
440
  """Settings for HuggingFace authentication (used for MCP connections)"""
359
441
 
@@ -445,51 +527,36 @@ def get_settings(config_path: str | None = None) -> Settings:
445
527
  resolved_path = Path.cwd() / config_file.name
446
528
  if resolved_path.exists():
447
529
  config_file = resolved_path
530
+
531
+ # When config path is explicitly provided, find secrets using standardized logic
532
+ secrets_file = None
533
+ if config_file.exists():
534
+ _, secrets_file = find_fastagent_config_files(config_file.parent)
448
535
  else:
449
- config_file = Settings.find_config()
536
+ # Use standardized discovery for both config and secrets
537
+ config_file, secrets_file = find_fastagent_config_files(Path.cwd())
450
538
 
451
539
  merged_settings = {}
452
540
 
453
- if config_file:
454
- if not config_file.exists():
455
- print(f"Warning: Specified config file does not exist: {config_file}")
456
- else:
457
- import yaml # pylint: disable=C0415
458
-
459
- # Load main config
460
- with open(config_file, "r", encoding="utf-8") as f:
461
- yaml_settings = yaml.safe_load(f) or {}
462
- # Resolve environment variables in the loaded YAML settings
463
- resolved_yaml_settings = resolve_env_vars(yaml_settings)
464
- merged_settings = resolved_yaml_settings
465
- # Look for secrets files recursively up the directory tree
466
- # but stop after finding the first one
467
- current_dir = config_file.parent
468
- found_secrets = False
469
- # Start with the absolute path of the config file\'s directory
470
- current_dir = config_file.parent.resolve()
471
-
472
- while current_dir != current_dir.parent and not found_secrets:
473
- for secrets_filename in [
474
- "fastagent.secrets.yaml",
475
- ]:
476
- secrets_file = current_dir / secrets_filename
477
- if secrets_file.exists():
478
- with open(secrets_file, "r", encoding="utf-8") as f:
479
- yaml_secrets = yaml.safe_load(f) or {}
480
- # Resolve environment variables in the loaded secrets YAML
481
- resolved_secrets_yaml = resolve_env_vars(yaml_secrets)
482
- merged_settings = deep_merge(merged_settings, resolved_secrets_yaml)
483
- found_secrets = True
484
- break
485
- if not found_secrets:
486
- # Get the absolute path of the parent directory
487
- current_dir = current_dir.parent.resolve()
488
-
489
- _settings = Settings(**merged_settings)
490
- return _settings
491
- else:
492
- pass
493
-
494
- _settings = Settings()
541
+ import yaml # pylint: disable=C0415
542
+
543
+ # Load main config if it exists
544
+ if config_file and config_file.exists():
545
+ with open(config_file, "r", encoding="utf-8") as f:
546
+ yaml_settings = yaml.safe_load(f) or {}
547
+ # Resolve environment variables in the loaded YAML settings
548
+ resolved_yaml_settings = resolve_env_vars(yaml_settings)
549
+ merged_settings = resolved_yaml_settings
550
+ elif config_file and not config_file.exists():
551
+ print(f"Warning: Specified config file does not exist: {config_file}")
552
+
553
+ # Load secrets file if found (regardless of whether config file exists)
554
+ if secrets_file and secrets_file.exists():
555
+ with open(secrets_file, "r", encoding="utf-8") as f:
556
+ yaml_secrets = yaml.safe_load(f) or {}
557
+ # Resolve environment variables in the loaded secrets YAML
558
+ resolved_secrets_yaml = resolve_env_vars(yaml_secrets)
559
+ merged_settings = deep_merge(merged_settings, resolved_secrets_yaml)
560
+
561
+ _settings = Settings(**merged_settings)
495
562
  return _settings
@@ -9,6 +9,7 @@ from mcp.types import PromptMessage
9
9
  from rich import print as rich_print
10
10
 
11
11
  from mcp_agent.agents.agent import Agent
12
+ from mcp_agent.core.agent_types import AgentType
12
13
  from mcp_agent.core.interactive_prompt import InteractivePrompt
13
14
  from mcp_agent.mcp.prompt_message_multipart import PromptMessageMultipart
14
15
  from mcp_agent.progress_display import progress_display
@@ -234,13 +235,14 @@ class AgentApp:
234
235
  """
235
236
  return await self.interactive(agent_name=agent_name, default_prompt=default_prompt)
236
237
 
237
- async def interactive(self, agent_name: str | None = None, default_prompt: str = "") -> str:
238
+ async def interactive(self, agent_name: str | None = None, default_prompt: str = "", pretty_print_parallel: bool = False) -> str:
238
239
  """
239
240
  Interactive prompt for sending messages with advanced features.
240
241
 
241
242
  Args:
242
243
  agent_name: Optional target agent name (uses default if not specified)
243
244
  default: Default message to use when user presses enter
245
+ pretty_print_parallel: Enable clean parallel results display for parallel agents
244
246
 
245
247
  Returns:
246
248
  The result of the interactive session
@@ -275,10 +277,18 @@ class AgentApp:
275
277
  # Define the wrapper for send function
276
278
  async def send_wrapper(message, agent_name):
277
279
  result = await self.send(message, agent_name)
278
-
280
+
281
+ # Show parallel results if enabled and this is a parallel agent
282
+ if pretty_print_parallel:
283
+ agent = self._agents.get(agent_name)
284
+ if agent and agent.agent_type == AgentType.PARALLEL:
285
+ from mcp_agent.ui.console_display import ConsoleDisplay
286
+ display = ConsoleDisplay(config=None)
287
+ display.show_parallel_results(agent)
288
+
279
289
  # Show usage info after each turn if progress display is enabled
280
290
  self._show_turn_usage(agent_name)
281
-
291
+
282
292
  return result
283
293
 
284
294
  # Start the prompt loop with the agent name (not the agent object)
@@ -293,32 +303,111 @@ class AgentApp:
293
303
  def _show_turn_usage(self, agent_name: str) -> None:
294
304
  """Show subtle usage information after each turn."""
295
305
  agent = self._agents.get(agent_name)
296
- if not agent or not agent.usage_accumulator:
306
+ if not agent:
297
307
  return
298
-
308
+
309
+ # Check if this is a parallel agent
310
+ if agent.agent_type == AgentType.PARALLEL:
311
+ self._show_parallel_agent_usage(agent)
312
+ else:
313
+ self._show_regular_agent_usage(agent)
314
+
315
+ def _show_regular_agent_usage(self, agent) -> None:
316
+ """Show usage for a regular (non-parallel) agent."""
317
+ usage_info = self._format_agent_usage(agent)
318
+ if usage_info:
319
+ with progress_display.paused():
320
+ rich_print(
321
+ f"[dim]Last turn: {usage_info['display_text']}[/dim]{usage_info['cache_suffix']}"
322
+ )
323
+
324
+ def _show_parallel_agent_usage(self, parallel_agent) -> None:
325
+ """Show usage for a parallel agent and its children."""
326
+ # Collect usage from all child agents
327
+ child_usage_data = []
328
+ total_input = 0
329
+ total_output = 0
330
+ total_tool_calls = 0
331
+
332
+ # Get usage from fan-out agents
333
+ if hasattr(parallel_agent, "fan_out_agents") and parallel_agent.fan_out_agents:
334
+ for child_agent in parallel_agent.fan_out_agents:
335
+ usage_info = self._format_agent_usage(child_agent)
336
+ if usage_info:
337
+ child_usage_data.append({**usage_info, "name": child_agent.name})
338
+ total_input += usage_info["input_tokens"]
339
+ total_output += usage_info["output_tokens"]
340
+ total_tool_calls += usage_info["tool_calls"]
341
+
342
+ # Get usage from fan-in agent
343
+ if hasattr(parallel_agent, "fan_in_agent") and parallel_agent.fan_in_agent:
344
+ usage_info = self._format_agent_usage(parallel_agent.fan_in_agent)
345
+ if usage_info:
346
+ child_usage_data.append({**usage_info, "name": parallel_agent.fan_in_agent.name})
347
+ total_input += usage_info["input_tokens"]
348
+ total_output += usage_info["output_tokens"]
349
+ total_tool_calls += usage_info["tool_calls"]
350
+
351
+ if not child_usage_data:
352
+ return
353
+
354
+ # Show aggregated usage for parallel agent (no context percentage)
355
+ with progress_display.paused():
356
+ tool_info = f", {total_tool_calls} tool calls" if total_tool_calls > 0 else ""
357
+ rich_print(
358
+ f"[dim]Last turn (parallel): {total_input:,} Input, {total_output:,} Output{tool_info}[/dim]"
359
+ )
360
+
361
+ # Show individual child agent usage
362
+ for i, usage_data in enumerate(child_usage_data):
363
+ is_last = i == len(child_usage_data) - 1
364
+ prefix = "└─" if is_last else "├─"
365
+ rich_print(
366
+ f"[dim] {prefix} {usage_data['name']}: {usage_data['display_text']}[/dim]{usage_data['cache_suffix']}"
367
+ )
368
+
369
+ def _format_agent_usage(self, agent) -> Optional[Dict]:
370
+ """Format usage information for a single agent."""
371
+ if not agent or not agent.usage_accumulator:
372
+ return None
373
+
299
374
  # Get the last turn's usage (if any)
300
375
  turns = agent.usage_accumulator.turns
301
376
  if not turns:
302
- return
303
-
377
+ return None
378
+
304
379
  last_turn = turns[-1]
305
380
  input_tokens = last_turn.display_input_tokens
306
381
  output_tokens = last_turn.output_tokens
307
-
382
+
308
383
  # Build cache indicators with bright colors
309
384
  cache_indicators = ""
310
385
  if last_turn.cache_usage.cache_write_tokens > 0:
311
386
  cache_indicators += "[bright_yellow]^[/bright_yellow]"
312
- if last_turn.cache_usage.cache_read_tokens > 0 or last_turn.cache_usage.cache_hit_tokens > 0:
387
+ if (
388
+ last_turn.cache_usage.cache_read_tokens > 0
389
+ or last_turn.cache_usage.cache_hit_tokens > 0
390
+ ):
313
391
  cache_indicators += "[bright_green]*[/bright_green]"
314
-
392
+
315
393
  # Build context percentage - get from accumulator, not individual turn
316
394
  context_info = ""
317
395
  context_percentage = agent.usage_accumulator.context_usage_percentage
318
396
  if context_percentage is not None:
319
397
  context_info = f" ({context_percentage:.1f}%)"
320
-
321
- # Show subtle usage line - pause progress display to ensure visibility
322
- with progress_display.paused():
323
- cache_suffix = f" {cache_indicators}" if cache_indicators else ""
324
- rich_print(f"[dim]Last turn: {input_tokens:,} Input, {output_tokens:,} Output{context_info}[/dim]{cache_suffix}")
398
+
399
+ # Build tool call info
400
+ tool_info = f", {last_turn.tool_calls} tool calls" if last_turn.tool_calls > 0 else ""
401
+
402
+ # Build display text
403
+ display_text = f"{input_tokens:,} Input, {output_tokens:,} Output{tool_info}{context_info}"
404
+ cache_suffix = f" {cache_indicators}" if cache_indicators else ""
405
+
406
+ return {
407
+ "input_tokens": input_tokens,
408
+ "output_tokens": output_tokens,
409
+ "tool_calls": last_turn.tool_calls,
410
+ "context_percentage": context_percentage,
411
+ "display_text": display_text,
412
+ "cache_suffix": cache_suffix,
413
+ }
@@ -4,7 +4,7 @@ Type definitions for agents and agent configurations.
4
4
 
5
5
  from dataclasses import dataclass, field
6
6
  from enum import Enum
7
- from typing import List
7
+ from typing import Dict, List, Optional
8
8
 
9
9
  from mcp.client.session import ElicitationFnT
10
10
 
@@ -31,6 +31,9 @@ class AgentConfig:
31
31
  name: str
32
32
  instruction: str = "You are a helpful agent."
33
33
  servers: List[str] = field(default_factory=list)
34
+ tools: Optional[Dict[str, List[str]]] = None
35
+ resources: Optional[Dict[str, List[str]]] = None
36
+ prompts: Optional[Dict[str, List[str]]] = None
34
37
  model: str | None = None
35
38
  use_history: bool = True
36
39
  default_request_params: RequestParams | None = None
@@ -38,6 +41,7 @@ class AgentConfig:
38
41
  agent_type: AgentType = AgentType.BASIC
39
42
  default: bool = False
40
43
  elicitation_handler: ElicitationFnT | None = None
44
+ api_key: str | None = None
41
45
 
42
46
  def __post_init__(self):
43
47
  """Ensure default_request_params exists with proper history setting"""