fast-agent-mcp 0.1.11__py3-none-any.whl → 0.1.13__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 (131) hide show
  1. {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/METADATA +1 -1
  2. fast_agent_mcp-0.1.13.dist-info/RECORD +164 -0
  3. mcp_agent/agents/agent.py +37 -102
  4. mcp_agent/app.py +16 -27
  5. mcp_agent/cli/commands/bootstrap.py +22 -52
  6. mcp_agent/cli/commands/config.py +4 -4
  7. mcp_agent/cli/commands/setup.py +11 -26
  8. mcp_agent/cli/main.py +6 -9
  9. mcp_agent/cli/terminal.py +2 -2
  10. mcp_agent/config.py +1 -5
  11. mcp_agent/context.py +13 -26
  12. mcp_agent/context_dependent.py +3 -7
  13. mcp_agent/core/agent_app.py +46 -122
  14. mcp_agent/core/agent_types.py +29 -2
  15. mcp_agent/core/agent_utils.py +3 -5
  16. mcp_agent/core/decorators.py +6 -14
  17. mcp_agent/core/enhanced_prompt.py +25 -52
  18. mcp_agent/core/error_handling.py +1 -1
  19. mcp_agent/core/exceptions.py +8 -8
  20. mcp_agent/core/factory.py +30 -72
  21. mcp_agent/core/fastagent.py +48 -88
  22. mcp_agent/core/mcp_content.py +10 -19
  23. mcp_agent/core/prompt.py +8 -15
  24. mcp_agent/core/proxies.py +34 -25
  25. mcp_agent/core/request_params.py +46 -0
  26. mcp_agent/core/types.py +6 -6
  27. mcp_agent/core/validation.py +16 -16
  28. mcp_agent/executor/decorator_registry.py +11 -23
  29. mcp_agent/executor/executor.py +8 -17
  30. mcp_agent/executor/task_registry.py +2 -4
  31. mcp_agent/executor/temporal.py +28 -74
  32. mcp_agent/executor/workflow.py +3 -5
  33. mcp_agent/executor/workflow_signal.py +17 -29
  34. mcp_agent/human_input/handler.py +4 -9
  35. mcp_agent/human_input/types.py +2 -3
  36. mcp_agent/logging/events.py +1 -5
  37. mcp_agent/logging/json_serializer.py +7 -6
  38. mcp_agent/logging/listeners.py +20 -23
  39. mcp_agent/logging/logger.py +15 -17
  40. mcp_agent/logging/rich_progress.py +10 -8
  41. mcp_agent/logging/tracing.py +4 -6
  42. mcp_agent/logging/transport.py +24 -24
  43. mcp_agent/mcp/gen_client.py +4 -12
  44. mcp_agent/mcp/interfaces.py +107 -88
  45. mcp_agent/mcp/mcp_agent_client_session.py +11 -19
  46. mcp_agent/mcp/mcp_agent_server.py +8 -10
  47. mcp_agent/mcp/mcp_aggregator.py +49 -122
  48. mcp_agent/mcp/mcp_connection_manager.py +16 -37
  49. mcp_agent/mcp/prompt_message_multipart.py +12 -18
  50. mcp_agent/mcp/prompt_serialization.py +13 -38
  51. mcp_agent/mcp/prompts/prompt_load.py +99 -0
  52. mcp_agent/mcp/prompts/prompt_server.py +21 -128
  53. mcp_agent/mcp/prompts/prompt_template.py +20 -42
  54. mcp_agent/mcp/resource_utils.py +8 -17
  55. mcp_agent/mcp/sampling.py +62 -64
  56. mcp_agent/mcp/stdio.py +11 -8
  57. mcp_agent/mcp_server/__init__.py +1 -1
  58. mcp_agent/mcp_server/agent_server.py +10 -17
  59. mcp_agent/mcp_server_registry.py +13 -35
  60. mcp_agent/resources/examples/data-analysis/analysis-campaign.py +1 -1
  61. mcp_agent/resources/examples/data-analysis/analysis.py +1 -1
  62. mcp_agent/resources/examples/data-analysis/slides.py +110 -0
  63. mcp_agent/resources/examples/internal/agent.py +2 -1
  64. mcp_agent/resources/examples/internal/job.py +2 -1
  65. mcp_agent/resources/examples/internal/prompt_category.py +1 -1
  66. mcp_agent/resources/examples/internal/prompt_sizing.py +3 -5
  67. mcp_agent/resources/examples/internal/sizer.py +2 -1
  68. mcp_agent/resources/examples/internal/social.py +2 -1
  69. mcp_agent/resources/examples/mcp_researcher/researcher-eval.py +1 -1
  70. mcp_agent/resources/examples/prompting/__init__.py +1 -1
  71. mcp_agent/resources/examples/prompting/agent.py +2 -1
  72. mcp_agent/resources/examples/prompting/image_server.py +5 -11
  73. mcp_agent/resources/examples/researcher/researcher-eval.py +1 -1
  74. mcp_agent/resources/examples/researcher/researcher-imp.py +3 -4
  75. mcp_agent/resources/examples/researcher/researcher.py +2 -1
  76. mcp_agent/resources/examples/workflows/agent_build.py +2 -1
  77. mcp_agent/resources/examples/workflows/chaining.py +2 -1
  78. mcp_agent/resources/examples/workflows/evaluator.py +2 -1
  79. mcp_agent/resources/examples/workflows/human_input.py +2 -1
  80. mcp_agent/resources/examples/workflows/orchestrator.py +2 -1
  81. mcp_agent/resources/examples/workflows/parallel.py +2 -1
  82. mcp_agent/resources/examples/workflows/router.py +2 -1
  83. mcp_agent/resources/examples/workflows/sse.py +1 -1
  84. mcp_agent/telemetry/usage_tracking.py +2 -1
  85. mcp_agent/ui/console_display.py +17 -41
  86. mcp_agent/workflows/embedding/embedding_base.py +1 -4
  87. mcp_agent/workflows/embedding/embedding_cohere.py +2 -2
  88. mcp_agent/workflows/embedding/embedding_openai.py +4 -13
  89. mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +23 -57
  90. mcp_agent/workflows/intent_classifier/intent_classifier_base.py +5 -8
  91. mcp_agent/workflows/intent_classifier/intent_classifier_embedding.py +7 -11
  92. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_cohere.py +4 -8
  93. mcp_agent/workflows/intent_classifier/intent_classifier_embedding_openai.py +4 -8
  94. mcp_agent/workflows/intent_classifier/intent_classifier_llm.py +11 -22
  95. mcp_agent/workflows/intent_classifier/intent_classifier_llm_anthropic.py +3 -3
  96. mcp_agent/workflows/intent_classifier/intent_classifier_llm_openai.py +4 -6
  97. mcp_agent/workflows/llm/anthropic_utils.py +8 -29
  98. mcp_agent/workflows/llm/augmented_llm.py +94 -332
  99. mcp_agent/workflows/llm/augmented_llm_anthropic.py +43 -76
  100. mcp_agent/workflows/llm/augmented_llm_openai.py +46 -100
  101. mcp_agent/workflows/llm/augmented_llm_passthrough.py +42 -20
  102. mcp_agent/workflows/llm/augmented_llm_playback.py +8 -6
  103. mcp_agent/workflows/llm/memory.py +103 -0
  104. mcp_agent/workflows/llm/model_factory.py +9 -21
  105. mcp_agent/workflows/llm/openai_utils.py +1 -1
  106. mcp_agent/workflows/llm/prompt_utils.py +39 -27
  107. mcp_agent/workflows/llm/providers/multipart_converter_anthropic.py +246 -184
  108. mcp_agent/workflows/llm/providers/multipart_converter_openai.py +212 -202
  109. mcp_agent/workflows/llm/providers/openai_multipart.py +19 -61
  110. mcp_agent/workflows/llm/providers/sampling_converter_anthropic.py +11 -212
  111. mcp_agent/workflows/llm/providers/sampling_converter_openai.py +13 -215
  112. mcp_agent/workflows/llm/sampling_converter.py +117 -0
  113. mcp_agent/workflows/llm/sampling_format_converter.py +12 -29
  114. mcp_agent/workflows/orchestrator/orchestrator.py +24 -67
  115. mcp_agent/workflows/orchestrator/orchestrator_models.py +14 -40
  116. mcp_agent/workflows/parallel/fan_in.py +17 -47
  117. mcp_agent/workflows/parallel/fan_out.py +6 -12
  118. mcp_agent/workflows/parallel/parallel_llm.py +9 -26
  119. mcp_agent/workflows/router/router_base.py +29 -59
  120. mcp_agent/workflows/router/router_embedding.py +11 -25
  121. mcp_agent/workflows/router/router_embedding_cohere.py +2 -2
  122. mcp_agent/workflows/router/router_embedding_openai.py +2 -2
  123. mcp_agent/workflows/router/router_llm.py +12 -28
  124. mcp_agent/workflows/swarm/swarm.py +20 -48
  125. mcp_agent/workflows/swarm/swarm_anthropic.py +2 -2
  126. mcp_agent/workflows/swarm/swarm_openai.py +2 -2
  127. fast_agent_mcp-0.1.11.dist-info/RECORD +0 -160
  128. mcp_agent/workflows/llm/llm_selector.py +0 -345
  129. {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/WHEEL +0 -0
  130. {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/entry_points.txt +0 -0
  131. {fast_agent_mcp-0.1.11.dist-info → fast_agent_mcp-0.1.13.dist-info}/licenses/LICENSE +0 -0
@@ -2,10 +2,11 @@
2
2
 
3
3
  import shutil
4
4
  from pathlib import Path
5
+
5
6
  import typer
6
7
  from rich.console import Console
7
- from rich.table import Table
8
8
  from rich.panel import Panel
9
+ from rich.table import Table
9
10
 
10
11
  app = typer.Typer(
11
12
  help="Create example applications",
@@ -49,9 +50,7 @@ EXAMPLE_TYPES = {
49
50
  }
50
51
 
51
52
 
52
- def copy_example_files(
53
- example_type: str, target_dir: Path, force: bool = False
54
- ) -> list[str]:
53
+ def copy_example_files(example_type: str, target_dir: Path, force: bool = False) -> list[str]:
55
54
  """Copy example files from resources to target directory."""
56
55
  created = []
57
56
 
@@ -72,12 +71,7 @@ def copy_example_files(
72
71
  console.print(f"Created mount-point directory: {mount_point_dir}")
73
72
 
74
73
  # Use the resources directory from the package
75
- source_dir = (
76
- Path(__file__).parent.parent.parent
77
- / "resources"
78
- / "examples"
79
- / ("workflows" if example_type == "workflow" else f"{example_type}")
80
- )
74
+ source_dir = Path(__file__).parent.parent.parent / "resources" / "examples" / ("workflows" if example_type == "workflow" else f"{example_type}")
81
75
 
82
76
  if not source_dir.exists():
83
77
  console.print(f"[red]Error: Source directory not found: {source_dir}[/red]")
@@ -116,9 +110,7 @@ def copy_example_files(
116
110
  continue
117
111
 
118
112
  if target.exists() and not force:
119
- console.print(
120
- f"[yellow]Skipping[/yellow] mount-point/{filename} (already exists)"
121
- )
113
+ console.print(f"[yellow]Skipping[/yellow] mount-point/{filename} (already exists)")
122
114
  continue
123
115
 
124
116
  shutil.copy2(source, target)
@@ -126,22 +118,18 @@ def copy_example_files(
126
118
  console.print(f"[green]Created[/green] mount-point/{filename}")
127
119
 
128
120
  except Exception as e:
129
- console.print(
130
- f"[red]Error copying mount-point/{filename}: {str(e)}[/red]"
131
- )
121
+ console.print(f"[red]Error copying mount-point/{filename}: {str(e)}[/red]")
132
122
 
133
123
  return created
134
124
 
135
125
 
136
- def show_overview():
126
+ def show_overview() -> None:
137
127
  """Display an overview of available examples in a nicely formatted table."""
138
128
  console.print("\n[bold cyan]fast-agent Example Applications[/bold cyan]")
139
129
  console.print("Build agents and compose workflows through practical examples\n")
140
130
 
141
131
  # Create a table for better organization
142
- table = Table(
143
- show_header=True, header_style="bold magenta", box=None, padding=(0, 2)
144
- )
132
+ table = Table(show_header=True, header_style="bold magenta", box=None, padding=(0, 2))
145
133
  table.add_column("Example")
146
134
  table.add_column("Description")
147
135
  table.add_column("Files")
@@ -149,9 +137,7 @@ def show_overview():
149
137
  for name, info in EXAMPLE_TYPES.items():
150
138
  files_list = "\n".join(f"• {f}" for f in info["files"])
151
139
  if "mount_point_files" in info:
152
- files_list += "\n[blue]mount-point:[/blue]\n" + "\n".join(
153
- f"• {f}" for f in info["mount_point_files"]
154
- )
140
+ files_list += "\n[blue]mount-point:[/blue]\n" + "\n".join(f"• {f}" for f in info["mount_point_files"])
155
141
  table.add_row(f"[green]{name}[/green]", info["description"], files_list)
156
142
 
157
143
  console.print(table)
@@ -178,10 +164,8 @@ def workflow(
178
164
  Path("."),
179
165
  help="Directory where workflow examples will be created",
180
166
  ),
181
- force: bool = typer.Option(
182
- False, "--force", "-f", help="Force overwrite existing files"
183
- ),
184
- ):
167
+ force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
168
+ ) -> None:
185
169
  """Create workflow pattern examples."""
186
170
  target_dir = directory.resolve()
187
171
  if not target_dir.exists():
@@ -198,10 +182,8 @@ def researcher(
198
182
  Path("."),
199
183
  help="Directory where researcher examples will be created (in 'researcher' subdirectory)",
200
184
  ),
201
- force: bool = typer.Option(
202
- False, "--force", "-f", help="Force overwrite existing files"
203
- ),
204
- ):
185
+ force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
186
+ ) -> None:
205
187
  """Create researcher pattern examples."""
206
188
  target_dir = directory.resolve()
207
189
  if not target_dir.exists():
@@ -218,10 +200,8 @@ def data_analysis(
218
200
  Path("."),
219
201
  help="Directory where data analysis examples will be created (creates 'data-analysis' subdirectory with mount-point)",
220
202
  ),
221
- force: bool = typer.Option(
222
- False, "--force", "-f", help="Force overwrite existing files"
223
- ),
224
- ):
203
+ force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
204
+ ) -> None:
225
205
  """Create data analysis examples with sample dataset."""
226
206
  target_dir = directory.resolve()
227
207
  if not target_dir.exists():
@@ -232,7 +212,7 @@ def data_analysis(
232
212
  _show_completion_message("data-analysis", created)
233
213
 
234
214
 
235
- def _show_completion_message(example_type: str, created: list[str]):
215
+ def _show_completion_message(example_type: str, created: list[str]) -> None:
236
216
  """Show completion message and next steps."""
237
217
  if created:
238
218
  console.print("\n[green]Setup completed successfully![/green]")
@@ -249,33 +229,23 @@ def _show_completion_message(example_type: str, created: list[str]):
249
229
  console.print(" - evaluator.py: Add evaluation capabilities")
250
230
  console.print(" - human_input.py: Incorporate human feedback")
251
231
  console.print("3. Run an example with: uv run <example>.py")
252
- console.print(
253
- "4. Try a different model with --model=<model>, or update the agent config"
254
- )
232
+ console.print("4. Try a different model with --model=<model>, or update the agent config")
255
233
 
256
234
  elif example_type == "researcher":
257
- console.print(
258
- "1. Set up the Brave MCP Server (get an API key from https://brave.com/search/api/)"
259
- )
235
+ console.print("1. Set up the Brave MCP Server (get an API key from https://brave.com/search/api/)")
260
236
  console.print("2. Try `uv run researcher.py` for the basic version")
261
- console.print(
262
- "3. Try `uv run researcher-eval.py` for the eval/optimize version"
263
- )
237
+ console.print("3. Try `uv run researcher-eval.py` for the eval/optimize version")
264
238
  elif example_type == "data-analysis":
265
- console.print(
266
- "1. Run uv `analysis.py` to perform data analysis and visualization"
267
- )
239
+ console.print("1. Run uv `analysis.py` to perform data analysis and visualization")
268
240
  console.print("2. The dataset is available in the mount-point directory:")
269
241
  console.print(" - mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv")
270
- console.print(
271
- "On Windows platforms, please edit the fastagent.config.yaml and adjust the volume mount point."
272
- )
242
+ console.print("On Windows platforms, please edit the fastagent.config.yaml and adjust the volume mount point.")
273
243
  else:
274
244
  console.print("\n[yellow]No files were created.[/yellow]")
275
245
 
276
246
 
277
247
  @app.callback(invoke_without_command=True)
278
- def main(ctx: typer.Context):
248
+ def main(ctx: typer.Context) -> None:
279
249
  """Create example applications and learn FastAgent patterns."""
280
250
  if ctx.invoked_subcommand is None:
281
251
  show_overview()
@@ -1,11 +1,11 @@
1
+ from typing import NoReturn
2
+
1
3
  import typer
2
4
 
3
5
  app = typer.Typer()
4
6
 
5
7
 
6
8
  @app.command()
7
- def show():
9
+ def show() -> NoReturn:
8
10
  """Show the configuration."""
9
- raise NotImplementedError(
10
- "The show configuration command has not been implemented yet"
11
- )
11
+ raise NotImplementedError("The show configuration command has not been implemented yet")
@@ -1,7 +1,8 @@
1
1
  from pathlib import Path
2
+
2
3
  import typer
3
- from rich.prompt import Confirm
4
4
  from rich.console import Console
5
+ from rich.prompt import Confirm
5
6
 
6
7
  app = typer.Typer()
7
8
  console = Console()
@@ -166,17 +167,13 @@ def init(
166
167
  "-c",
167
168
  help="Directory where configuration files will be created",
168
169
  ),
169
- force: bool = typer.Option(
170
- False, "--force", "-f", help="Force overwrite existing files"
171
- ),
172
- ):
170
+ force: bool = typer.Option(False, "--force", "-f", help="Force overwrite existing files"),
171
+ ) -> None:
173
172
  """Initialize a new FastAgent project with configuration files and example agent."""
174
173
 
175
174
  config_path = Path(config_dir).resolve()
176
175
  if not config_path.exists():
177
- should_create = Confirm.ask(
178
- f"Directory {config_path} does not exist. Create it?", default=True
179
- )
176
+ should_create = Confirm.ask(f"Directory {config_path} does not exist. Create it?", default=True)
180
177
  if should_create:
181
178
  config_path.mkdir(parents=True)
182
179
  else:
@@ -198,38 +195,26 @@ def init(
198
195
 
199
196
  # Create configuration files
200
197
  created = []
201
- if create_file(
202
- config_path / "fastagent.config.yaml", FASTAGENT_CONFIG_TEMPLATE, force
203
- ):
198
+ if create_file(config_path / "fastagent.config.yaml", FASTAGENT_CONFIG_TEMPLATE, force):
204
199
  created.append("fastagent.yaml")
205
200
 
206
- if create_file(
207
- config_path / "fastagent.secrets.yaml", FASTAGENT_SECRETS_TEMPLATE, force
208
- ):
201
+ if create_file(config_path / "fastagent.secrets.yaml", FASTAGENT_SECRETS_TEMPLATE, force):
209
202
  created.append("fastagent.secrets.yaml")
210
203
 
211
204
  if create_file(config_path / "agent.py", AGENT_EXAMPLE_TEMPLATE, force):
212
205
  created.append("agent.py")
213
206
 
214
207
  # Only create .gitignore if none exists in parent directories
215
- if needs_gitignore and create_file(
216
- config_path / ".gitignore", GITIGNORE_TEMPLATE, force
217
- ):
208
+ if needs_gitignore and create_file(config_path / ".gitignore", GITIGNORE_TEMPLATE, force):
218
209
  created.append(".gitignore")
219
210
 
220
211
  if created:
221
212
  console.print("\n[green]Setup completed successfully![/green]")
222
213
  if "fastagent.secrets.yaml" in created:
223
214
  console.print("\n[yellow]Important:[/yellow] Remember to:")
224
- console.print(
225
- "1. Add your API keys to fastagent-secrets.yaml or set OPENAI_API_KEY and ANTHROPIC_API_KEY environment variables"
226
- )
227
- console.print(
228
- "2. Keep fastagent.secrets.yaml secure and never commit it to version control"
229
- )
230
- console.print(
231
- "3. Update fastagent.config.yaml to set a default model (currently system default is 'haiku')"
232
- )
215
+ console.print("1. Add your API keys to fastagent-secrets.yaml or set OPENAI_API_KEY and ANTHROPIC_API_KEY environment variables")
216
+ console.print("2. Keep fastagent.secrets.yaml secure and never commit it to version control")
217
+ console.print("3. Update fastagent.config.yaml to set a default model (currently system default is 'haiku')")
233
218
  console.print("\nTo get started, run:")
234
219
  console.print(" uv run agent.py")
235
220
  else:
mcp_agent/cli/main.py CHANGED
@@ -3,8 +3,9 @@
3
3
  import typer
4
4
  from rich.console import Console
5
5
  from rich.table import Table
6
+
7
+ from mcp_agent.cli.commands import bootstrap, setup
6
8
  from mcp_agent.cli.terminal import Application
7
- from mcp_agent.cli.commands import setup, bootstrap
8
9
 
9
10
  app = typer.Typer(
10
11
  help="MCP Agent CLI - Build effective agents using Model Context Protocol",
@@ -20,7 +21,7 @@ application = Application()
20
21
  console = Console()
21
22
 
22
23
 
23
- def show_welcome():
24
+ def show_welcome() -> None:
24
25
  """Show a welcome message with available commands."""
25
26
  from importlib.metadata import version
26
27
 
@@ -38,9 +39,7 @@ def show_welcome():
38
39
  table.add_column("Description")
39
40
 
40
41
  table.add_row("setup", "Set up a new agent project with configuration files")
41
- table.add_row(
42
- "bootstrap", "Create example applications (workflow, researcher, etc.)"
43
- )
42
+ table.add_row("bootstrap", "Create example applications (workflow, researcher, etc.)")
44
43
  # table.add_row("config", "Manage agent configuration settings")
45
44
 
46
45
  console.print(table)
@@ -62,10 +61,8 @@ def main(
62
61
  ctx: typer.Context,
63
62
  verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose mode"),
64
63
  quiet: bool = typer.Option(False, "--quiet", "-q", help="Disable output"),
65
- color: bool = typer.Option(
66
- True, "--color/--no-color", help="Enable/disable color output"
67
- ),
68
- ):
64
+ color: bool = typer.Option(True, "--color/--no-color", help="Enable/disable color output"),
65
+ ) -> None:
69
66
  """FastAgent CLI - Build effective agents using Model Context Protocol (MCP).
70
67
 
71
68
  Use --help with any command for detailed usage information.
mcp_agent/cli/terminal.py CHANGED
@@ -2,7 +2,7 @@ from mcp_agent.console import console, error_console
2
2
 
3
3
 
4
4
  class Application:
5
- def __init__(self, verbosity: int = 0, enable_color: bool = True):
5
+ def __init__(self, verbosity: int = 0, enable_color: bool = True) -> None:
6
6
  self.verbosity = verbosity
7
7
  # Use the central console instances, respecting color setting
8
8
  if not enable_color:
@@ -13,7 +13,7 @@ class Application:
13
13
  self.console = console
14
14
  self.error_console = error_console
15
15
 
16
- def log(self, message: str, level: str = "info"):
16
+ def log(self, message: str, level: str = "info") -> None:
17
17
  if level == "info" or (level == "debug" and self.verbosity > 0):
18
18
  if level == "error":
19
19
  self.error_console.print(f"[{level.upper()}] {message}")
mcp_agent/config.py CHANGED
@@ -295,11 +295,7 @@ def get_settings(config_path: str | None = None) -> Settings:
295
295
  """Recursively merge two dictionaries, preserving nested structures."""
296
296
  merged = base.copy()
297
297
  for key, value in update.items():
298
- if (
299
- key in merged
300
- and isinstance(merged[key], dict)
301
- and isinstance(value, dict)
302
- ):
298
+ if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
303
299
  merged[key] = deep_merge(merged[key], value)
304
300
  else:
305
301
  merged[key] = value
mcp_agent/context.py CHANGED
@@ -4,41 +4,33 @@ A central context object to store global state that is shared across the applica
4
4
 
5
5
  import asyncio
6
6
  import concurrent.futures
7
- from typing import Any, Optional, Union, TYPE_CHECKING
8
-
9
- from pydantic import BaseModel, ConfigDict
7
+ from typing import TYPE_CHECKING, Any, Optional, Union
10
8
 
11
9
  from mcp import ServerSession
12
-
13
10
  from opentelemetry import trace
11
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
14
12
  from opentelemetry.propagate import set_global_textmap
15
13
  from opentelemetry.sdk.resources import Resource
16
14
  from opentelemetry.sdk.trace import TracerProvider
17
15
  from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
18
16
  from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
19
- from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
17
+ from pydantic import BaseModel, ConfigDict
20
18
 
21
- from mcp_agent.config import get_settings
22
- from mcp_agent.config import Settings
23
- from mcp_agent.executor.executor import Executor
19
+ from mcp_agent.config import Settings, get_settings
24
20
  from mcp_agent.executor.decorator_registry import (
25
21
  DecoratorRegistry,
26
22
  register_asyncio_decorators,
27
23
  )
24
+ from mcp_agent.executor.executor import AsyncioExecutor, Executor
28
25
  from mcp_agent.executor.task_registry import ActivityRegistry
29
- from mcp_agent.executor.executor import AsyncioExecutor
30
-
31
26
  from mcp_agent.logging.events import EventFilter
32
- from mcp_agent.logging.logger import LoggingConfig
27
+ from mcp_agent.logging.logger import LoggingConfig, get_logger
33
28
  from mcp_agent.logging.transport import create_transport
34
29
  from mcp_agent.mcp_server_registry import ServerRegistry
35
- from mcp_agent.workflows.llm.llm_selector import ModelSelector
36
- from mcp_agent.logging.logger import get_logger
37
-
38
30
 
39
31
  if TYPE_CHECKING:
40
- from mcp_agent.human_input.types import HumanInputCallback
41
32
  from mcp_agent.executor.workflow_signal import SignalWaitCallback
33
+ from mcp_agent.human_input.types import HumanInputCallback
42
34
  else:
43
35
  # Runtime placeholders for the types
44
36
  HumanInputCallback = Any
@@ -58,7 +50,6 @@ class Context(BaseModel):
58
50
  human_input_handler: Optional[HumanInputCallback] = None
59
51
  signal_notification: Optional[SignalWaitCallback] = None
60
52
  upstream_session: Optional[ServerSession] = None # TODO: saqadri - figure this out
61
- model_selector: Optional[ModelSelector] = None
62
53
 
63
54
  # Registries
64
55
  server_registry: Optional[ServerRegistry] = None
@@ -73,7 +64,7 @@ class Context(BaseModel):
73
64
  )
74
65
 
75
66
 
76
- async def configure_otel(config: "Settings"):
67
+ async def configure_otel(config: "Settings") -> None:
77
68
  """
78
69
  Configure OpenTelemetry based on the application config.
79
70
  """
@@ -114,9 +105,7 @@ async def configure_otel(config: "Settings"):
114
105
  tracer_provider.add_span_processor(BatchSpanProcessor(exporter))
115
106
 
116
107
  if config.otel.console_debug:
117
- tracer_provider.add_span_processor(
118
- BatchSpanProcessor(ConsoleSpanExporter())
119
- )
108
+ tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
120
109
  else:
121
110
  # Default to console exporter in development
122
111
  tracer_provider.add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))
@@ -125,7 +114,7 @@ async def configure_otel(config: "Settings"):
125
114
  trace.set_tracer_provider(tracer_provider)
126
115
 
127
116
 
128
- async def configure_logger(config: "Settings"):
117
+ async def configure_logger(config: "Settings") -> None:
129
118
  """
130
119
  Configure logging and tracing based on the application config.
131
120
  """
@@ -141,7 +130,7 @@ async def configure_logger(config: "Settings"):
141
130
  )
142
131
 
143
132
 
144
- async def configure_usage_telemetry(_config: "Settings"):
133
+ async def configure_usage_telemetry(_config: "Settings") -> None:
145
134
  """
146
135
  Configure usage telemetry based on the application config.
147
136
  TODO: saqadri - implement usage tracking
@@ -167,9 +156,7 @@ async def configure_executor(config: "Settings"):
167
156
  return executor
168
157
 
169
158
 
170
- async def initialize_context(
171
- config: Optional[Union["Settings", str]] = None, store_globally: bool = False
172
- ):
159
+ async def initialize_context(config: Optional[Union["Settings", str]] = None, store_globally: bool = False):
173
160
  """
174
161
  Initialize the global application context.
175
162
  """
@@ -204,7 +191,7 @@ async def initialize_context(
204
191
  return context
205
192
 
206
193
 
207
- async def cleanup_context():
194
+ async def cleanup_context() -> None:
208
195
  """
209
196
  Cleanup the global application context.
210
197
  """
@@ -1,6 +1,5 @@
1
1
  from contextlib import contextmanager
2
- from typing import Optional, TYPE_CHECKING
3
-
2
+ from typing import TYPE_CHECKING, Any, Optional
4
3
 
5
4
  if TYPE_CHECKING:
6
5
  from mcp_agent.context import Context
@@ -12,7 +11,7 @@ class ContextDependent:
12
11
  Provides both global fallback and instance-specific context support.
13
12
  """
14
13
 
15
- def __init__(self, context: Optional["Context"] = None, **kwargs):
14
+ def __init__(self, context: Optional["Context"] = None, **kwargs: dict[str, Any]) -> None:
16
15
  self._context = context
17
16
  super().__init__(**kwargs)
18
17
 
@@ -32,10 +31,7 @@ class ContextDependent:
32
31
 
33
32
  return get_current_context()
34
33
  except Exception as e:
35
- raise RuntimeError(
36
- f"No context available for {self.__class__.__name__}. "
37
- "Either initialize MCPApp first or pass context explicitly."
38
- ) from e
34
+ raise RuntimeError(f"No context available for {self.__class__.__name__}. Either initialize MCPApp first or pass context explicitly.") from e
39
35
 
40
36
  @contextmanager
41
37
  def use_context(self, context: "Context"):