fast-agent-mcp 0.2.23__tar.gz → 0.2.25__tar.gz

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 (192) hide show
  1. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/.gitignore +0 -2
  2. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/PKG-INFO +7 -3
  3. fast_agent_mcp-0.2.25/examples/azure-openai/fastagent.config.yaml +54 -0
  4. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/pyproject.toml +13 -3
  5. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/commands/check_config.py +61 -28
  6. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/config.py +17 -0
  7. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/context.py +2 -0
  8. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/event_progress.py +1 -0
  9. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/model_factory.py +2 -0
  10. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/provider_types.py +1 -0
  11. fast_agent_mcp-0.2.25/src/mcp_agent/llm/providers/augmented_llm_azure.py +137 -0
  12. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/augmented_llm_openai.py +8 -7
  13. fast_agent_mcp-0.2.25/src/mcp_agent/mcp/common.py +16 -0
  14. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/mcp_agent_client_session.py +41 -11
  15. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/mcp_aggregator.py +139 -14
  16. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/ui/console_display.py +28 -1
  17. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/LICENSE +0 -0
  18. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/README.md +0 -0
  19. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/data-analysis/analysis-campaign.py +0 -0
  20. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/data-analysis/analysis.py +0 -0
  21. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/data-analysis/fastagent.config.yaml +0 -0
  22. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
  23. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/mcp/state-transfer/agent_one.py +0 -0
  24. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/mcp/state-transfer/agent_two.py +0 -0
  25. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/mcp/state-transfer/fastagent.config.yaml +0 -0
  26. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/mcp/vision-examples/example1.py +0 -0
  27. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/mcp/vision-examples/example2.py +0 -0
  28. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/mcp/vision-examples/example3.py +0 -0
  29. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/mcp/vision-examples/fastagent.config.yaml +0 -0
  30. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/otel/agent.py +0 -0
  31. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/otel/agent2.py +0 -0
  32. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/otel/docker-compose.yaml +0 -0
  33. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/otel/fastagent.config.yaml +0 -0
  34. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/researcher/fastagent.config.yaml +0 -0
  35. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/researcher/researcher-eval.py +0 -0
  36. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/researcher/researcher-imp.py +0 -0
  37. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/researcher/researcher.py +0 -0
  38. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/tensorzero/README.md +0 -0
  39. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/tensorzero/agent.py +0 -0
  40. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/tensorzero/docker-compose.yml +0 -0
  41. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/tensorzero/fastagent.config.yaml +0 -0
  42. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/tensorzero/image_demo.py +0 -0
  43. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/tensorzero/mcp_server/mcp_server.py +0 -0
  44. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/tensorzero/simple_agent.py +0 -0
  45. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/chaining.py +0 -0
  46. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/evaluator.py +0 -0
  47. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/fastagent.config.yaml +0 -0
  48. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/graded_report.md +0 -0
  49. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/human_input.py +0 -0
  50. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/orchestrator.py +0 -0
  51. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/parallel.py +0 -0
  52. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/router.py +0 -0
  53. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/short_story.md +0 -0
  54. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/examples/workflows/short_story.txt +0 -0
  55. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/__init__.py +0 -0
  56. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/__init__.py +0 -0
  57. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/agent.py +0 -0
  58. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/base_agent.py +0 -0
  59. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/__init__.py +0 -0
  60. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/chain_agent.py +0 -0
  61. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/evaluator_optimizer.py +0 -0
  62. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/orchestrator_agent.py +0 -0
  63. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/orchestrator_models.py +0 -0
  64. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/orchestrator_prompts.py +0 -0
  65. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/parallel_agent.py +0 -0
  66. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/agents/workflow/router_agent.py +0 -0
  67. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/app.py +0 -0
  68. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/__init__.py +0 -0
  69. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/__main__.py +0 -0
  70. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/commands/go.py +0 -0
  71. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/commands/quickstart.py +0 -0
  72. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/commands/setup.py +0 -0
  73. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/commands/url_parser.py +0 -0
  74. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/main.py +0 -0
  75. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/cli/terminal.py +0 -0
  76. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/console.py +0 -0
  77. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/context_dependent.py +0 -0
  78. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/__init__.py +0 -0
  79. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/agent_app.py +0 -0
  80. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/agent_types.py +0 -0
  81. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/direct_decorators.py +0 -0
  82. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/direct_factory.py +0 -0
  83. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/enhanced_prompt.py +0 -0
  84. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/error_handling.py +0 -0
  85. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/exceptions.py +0 -0
  86. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/fastagent.py +0 -0
  87. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/interactive_prompt.py +0 -0
  88. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/mcp_content.py +0 -0
  89. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/prompt.py +0 -0
  90. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/request_params.py +0 -0
  91. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/core/validation.py +0 -0
  92. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/executor/__init__.py +0 -0
  93. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/executor/executor.py +0 -0
  94. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/executor/task_registry.py +0 -0
  95. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/executor/workflow_signal.py +0 -0
  96. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/human_input/__init__.py +0 -0
  97. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/human_input/handler.py +0 -0
  98. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/human_input/types.py +0 -0
  99. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/__init__.py +0 -0
  100. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/augmented_llm.py +0 -0
  101. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/augmented_llm_passthrough.py +0 -0
  102. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/augmented_llm_playback.py +0 -0
  103. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/memory.py +0 -0
  104. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/prompt_utils.py +0 -0
  105. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/provider_key_manager.py +0 -0
  106. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/__init__.py +0 -0
  107. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/anthropic_utils.py +0 -0
  108. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/augmented_llm_anthropic.py +0 -0
  109. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/augmented_llm_deepseek.py +0 -0
  110. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/augmented_llm_generic.py +0 -0
  111. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/augmented_llm_google.py +0 -0
  112. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/augmented_llm_openrouter.py +0 -0
  113. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/augmented_llm_tensorzero.py +0 -0
  114. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/multipart_converter_anthropic.py +0 -0
  115. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/multipart_converter_openai.py +0 -0
  116. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/multipart_converter_tensorzero.py +0 -0
  117. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/openai_multipart.py +0 -0
  118. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/openai_utils.py +0 -0
  119. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/sampling_converter_anthropic.py +0 -0
  120. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/providers/sampling_converter_openai.py +0 -0
  121. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/sampling_converter.py +0 -0
  122. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/llm/sampling_format_converter.py +0 -0
  123. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/logging/__init__.py +0 -0
  124. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/logging/events.py +0 -0
  125. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/logging/json_serializer.py +0 -0
  126. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/logging/listeners.py +0 -0
  127. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/logging/logger.py +0 -0
  128. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/logging/rich_progress.py +0 -0
  129. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/logging/transport.py +0 -0
  130. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/__init__.py +0 -0
  131. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/gen_client.py +0 -0
  132. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/helpers/__init__.py +0 -0
  133. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/helpers/content_helpers.py +0 -0
  134. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/interfaces.py +0 -0
  135. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/logger_textio.py +0 -0
  136. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/mcp_connection_manager.py +0 -0
  137. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/mime_utils.py +0 -0
  138. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompt_message_multipart.py +0 -0
  139. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompt_render.py +0 -0
  140. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompt_serialization.py +0 -0
  141. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompts/__init__.py +0 -0
  142. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompts/__main__.py +0 -0
  143. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompts/prompt_constants.py +0 -0
  144. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompts/prompt_helpers.py +0 -0
  145. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompts/prompt_load.py +0 -0
  146. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompts/prompt_server.py +0 -0
  147. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/prompts/prompt_template.py +0 -0
  148. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/resource_utils.py +0 -0
  149. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp/sampling.py +0 -0
  150. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp_server/__init__.py +0 -0
  151. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp_server/agent_server.py +0 -0
  152. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/mcp_server_registry.py +0 -0
  153. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/progress_display.py +0 -0
  154. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/data-analysis/analysis-campaign.py +0 -0
  155. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/data-analysis/analysis.py +0 -0
  156. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/data-analysis/fastagent.config.yaml +0 -0
  157. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +0 -0
  158. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/in_dev/agent_build.py +0 -0
  159. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/in_dev/css-LICENSE.txt +0 -0
  160. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/in_dev/slides.py +0 -0
  161. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/agent.py +0 -0
  162. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/fastagent.config.yaml +0 -0
  163. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/history_transfer.py +0 -0
  164. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/job.py +0 -0
  165. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/prompt_category.py +0 -0
  166. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/prompt_sizing.py +0 -0
  167. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/simple.txt +0 -0
  168. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/sizer.py +0 -0
  169. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/internal/social.py +0 -0
  170. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/mcp/state-transfer/agent_one.py +0 -0
  171. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/mcp/state-transfer/agent_two.py +0 -0
  172. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +0 -0
  173. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +0 -0
  174. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/prompting/__init__.py +0 -0
  175. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/prompting/agent.py +0 -0
  176. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/prompting/delimited_prompt.txt +0 -0
  177. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/prompting/fastagent.config.yaml +0 -0
  178. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/prompting/image_server.py +0 -0
  179. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/prompting/prompt1.txt +0 -0
  180. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/prompting/work_with_image.py +0 -0
  181. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/researcher/fastagent.config.yaml +0 -0
  182. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/researcher/researcher-eval.py +0 -0
  183. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/researcher/researcher-imp.py +0 -0
  184. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/researcher/researcher.py +0 -0
  185. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/chaining.py +0 -0
  186. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/evaluator.py +0 -0
  187. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/fastagent.config.yaml +0 -0
  188. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/human_input.py +0 -0
  189. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/orchestrator.py +0 -0
  190. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/parallel.py +0 -0
  191. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/router.py +0 -0
  192. {fast_agent_mcp-0.2.23 → fast_agent_mcp-0.2.25}/src/mcp_agent/resources/examples/workflows/short_story.txt +0 -0
@@ -197,7 +197,5 @@ tests/integration/prompt-state/history.json
197
197
  !tests/integration/api/fastagent.secrets.yaml
198
198
  fastagent.jsonl
199
199
 
200
- # JetBrains IDEs
201
- .idea/
202
200
  tests/e2e/smoke/base/weather_location.txt
203
201
  tests/integration/roots/fastagent.jsonl
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-agent-mcp
3
- Version: 0.2.23
3
+ Version: 0.2.25
4
4
  Summary: Define, Prompt and Test MCP enabled Agents and Workflows
5
5
  Author-email: Shaun Smith <fastagent@llmindset.co.uk>
6
6
  License: Apache License
@@ -212,13 +212,15 @@ Requires-Python: >=3.10
212
212
  Requires-Dist: a2a-types>=0.1.0
213
213
  Requires-Dist: aiohttp>=3.11.13
214
214
  Requires-Dist: anthropic>=0.49.0
215
+ Requires-Dist: azure-identity>=1.14.0
215
216
  Requires-Dist: fastapi>=0.115.6
216
217
  Requires-Dist: mcp>=1.8.0
217
218
  Requires-Dist: openai>=1.63.2
218
219
  Requires-Dist: opentelemetry-distro>=0.50b0
219
220
  Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.29.0
220
- Requires-Dist: opentelemetry-instrumentation-anthropic>=0.39.3
221
- Requires-Dist: opentelemetry-instrumentation-openai>=0.39.3
221
+ Requires-Dist: opentelemetry-instrumentation-anthropic>=0.39.3; python_version >= '3.10' and python_version < '4.0'
222
+ Requires-Dist: opentelemetry-instrumentation-mcp>=0.40.3; python_version >= '3.10' and python_version < '4.0'
223
+ Requires-Dist: opentelemetry-instrumentation-openai>=0.39.3; python_version >= '3.10' and python_version < '4.0'
222
224
  Requires-Dist: prompt-toolkit>=3.0.50
223
225
  Requires-Dist: pydantic-settings>=2.7.0
224
226
  Requires-Dist: pydantic>=2.10.4
@@ -226,6 +228,8 @@ Requires-Dist: pyyaml>=6.0.2
226
228
  Requires-Dist: rich>=13.9.4
227
229
  Requires-Dist: tensorzero>=2025.4.7
228
230
  Requires-Dist: typer>=0.15.1
231
+ Provides-Extra: azure
232
+ Requires-Dist: azure-identity>=1.14.0; extra == 'azure'
229
233
  Provides-Extra: dev
230
234
  Requires-Dist: anthropic>=0.42.0; extra == 'dev'
231
235
  Requires-Dist: pre-commit>=4.0.1; extra == 'dev'
@@ -0,0 +1,54 @@
1
+ # NOTE: Since version X.X, the Azure OpenAI integration in FastAgent reuses the OpenAI logic.
2
+ # Authentication (API Key or DefaultAzureCredential) and connectivity are managed automatically.
3
+ # You only need to configure the 'azure' section as indicated below; the system will select the appropriate method.
4
+ #
5
+ # Example configuration for Azure OpenAI in fast-agent
6
+ #
7
+ # There are three supported authentication/configuration modes for Azure OpenAI:
8
+ #
9
+ # 1. Using 'resource_name' and 'api_key' (recommended for most users)
10
+ # 2. Using 'base_url' and 'api_key' (for custom endpoints or sovereign clouds)
11
+ # 3. Using 'base_url' and DefaultAzureCredential (for managed identity, Azure CLI, etc.)
12
+ #
13
+ # Use ONLY one of the parameters: 'resource_name' or 'base_url'.
14
+ # - If you define 'base_url', it will be used directly as the endpoint and 'resource_name' will be ignored.
15
+ # - If you define 'resource_name' (and not 'base_url'), the endpoint will be constructed automatically.
16
+ # - If both are missing, the configuration is invalid.
17
+ #
18
+ # Do not include both at the same time.
19
+
20
+ # --- OPTION 1: Using resource_name and api_key (recommended for standard Azure) ---
21
+ default_model: "azure.my-deployment"
22
+
23
+ azure:
24
+ api_key: "YOUR_AZURE_OPENAI_API_KEY"
25
+ resource_name: "your-resource-name"
26
+ azure_deployment: "my-deployment"
27
+ api_version: "2023-05-15"
28
+ # Do not include base_url if you use resource_name
29
+
30
+ # --- OPTION 2: Using base_url and api_key (for custom endpoints or sovereign clouds) ---
31
+ # default_model: "azure.my-deployment"
32
+ #
33
+ # azure:
34
+ # api_key: "YOUR_AZURE_OPENAI_API_KEY"
35
+ # base_url: "https://your-resource-name.openai.azure.com/"
36
+ # azure_deployment: "my-deployment"
37
+ # api_version: "2023-05-15"
38
+ # # Do not include resource_name if you use base_url
39
+
40
+ # --- OPTION 3: Using base_url and DefaultAzureCredential (for managed identity, Azure CLI, etc.) ---
41
+ # Requires the 'azure-identity' package to be installed.
42
+ # No api_key or resource_name should be present in this mode.
43
+ # base_url is required and must be the full endpoint URL.
44
+ #
45
+ # default_model: "azure.my-deployment"
46
+ #
47
+ # azure:
48
+ # use_default_azure_credential: true
49
+ # base_url: "https://your-resource-name.openai.azure.com/"
50
+ # azure_deployment: "my-deployment"
51
+ # api_version: "2023-05-15"
52
+ # # Do not include api_key or resource_name in this mode
53
+
54
+ # You can add other providers or settings as needed.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fast-agent-mcp"
3
- version = "0.2.23"
3
+ version = "0.2.25"
4
4
  description = "Define, Prompt and Test MCP enabled Agents and Workflows"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -25,18 +25,24 @@ dependencies = [
25
25
  "typer>=0.15.1",
26
26
  "anthropic>=0.49.0",
27
27
  "openai>=1.63.2",
28
+ "azure-identity>=1.14.0",
28
29
  "prompt-toolkit>=3.0.50",
29
30
  "aiohttp>=3.11.13",
30
31
  "a2a-types>=0.1.0",
31
- "opentelemetry-instrumentation-openai>=0.39.3",
32
- "opentelemetry-instrumentation-anthropic>=0.39.3",
32
+ "opentelemetry-instrumentation-openai>=0.39.3; python_version >= '3.10' and python_version < '4.0'",
33
+ "opentelemetry-instrumentation-anthropic>=0.39.3; python_version >= '3.10' and python_version < '4.0'",
33
34
  "tensorzero>=2025.4.7",
35
+ "opentelemetry-instrumentation-mcp>=0.40.3; python_version >= '3.10' and python_version < '4.0'",
34
36
  ]
35
37
 
36
38
  [project.optional-dependencies]
37
39
  openai = [
38
40
  "openai>=1.58.1",
39
41
  ]
42
+ # For Azure OpenAI with DefaultAzureCredential support, install with: pip install fast-agent-mcp[azure]
43
+ azure = [
44
+ "azure-identity>=1.14.0"
45
+ ]
40
46
  dev = [
41
47
  "anthropic>=0.42.0",
42
48
  "pre-commit>=4.0.1",
@@ -56,6 +62,10 @@ build-backend = "hatchling.build"
56
62
  [tool.hatch.build.targets.wheel]
57
63
  packages = ["src/mcp_agent"]
58
64
 
65
+ [[tool.poetry.packages]]
66
+ include = "mcp_agent"
67
+ from = "src"
68
+
59
69
  [tool.hatch.build]
60
70
  include = [
61
71
  "src/mcp_agent/**/*.py",
@@ -92,42 +92,73 @@ def get_secrets_summary(secrets_path: Optional[Path]) -> dict:
92
92
  return result
93
93
 
94
94
 
95
- def check_api_keys(secrets_summary: dict) -> dict:
96
- """Check if API keys are configured in secrets file or environment."""
95
+ def check_api_keys(secrets_summary: dict, config_summary: dict) -> dict:
96
+ """Check if API keys are configured in secrets file or environment, including Azure DefaultAzureCredential.
97
+ Now also checks Azure config in main config file for retrocompatibility.
98
+ """
97
99
  import os
98
100
 
99
- # Initialize results dict using Provider enum values
100
101
  results = {
101
- provider.value: {"env": None, "config": None}
102
+ provider.value: {"env": "", "config": ""}
102
103
  for provider in Provider
103
104
  if provider != Provider.FAST_AGENT
104
- } # Include GENERIC but exclude FAST_AGENT
105
+ }
105
106
 
106
107
  # Get secrets if available
107
108
  secrets = secrets_summary.get("secrets", {})
108
109
  secrets_status = secrets_summary.get("status", "not_found")
110
+ # Get config if available
111
+ config = config_summary if config_summary.get("status") == "parsed" else {}
112
+ config_azure = {}
113
+ if config and "azure" in config.get("config", {}):
114
+ config_azure = config["config"]["azure"]
109
115
 
110
- # Check both environment variables and config file for each provider
111
116
  for provider_value in results:
112
- # Check environment variables using ProviderKeyManager
113
- env_key_name = ProviderKeyManager.get_env_key_name(provider_value)
114
- env_key_value = os.environ.get(env_key_name)
115
- if env_key_value:
116
- # Store the last 5 characters if key is long enough
117
- if len(env_key_value) > 5:
118
- results[provider_value]["env"] = f"...{env_key_value[-5:]}"
119
- else:
120
- results[provider_value]["env"] = "...***"
121
-
122
- # Check secrets file if it was parsed successfully
123
- if secrets_status == "parsed":
124
- config_key = ProviderKeyManager.get_config_file_key(provider_value, secrets)
125
- if config_key and config_key != API_KEY_HINT_TEXT:
126
- # Store the last 5 characters if key is long enough
127
- if len(config_key) > 5:
128
- results[provider_value]["config"] = f"...{config_key[-5:]}"
117
+ # Special handling for Azure: support api_key and DefaultAzureCredential
118
+ if provider_value == "azure":
119
+ # Prefer secrets if present, else fallback to config
120
+ azure_cfg = {}
121
+ if secrets_status == "parsed" and "azure" in secrets:
122
+ azure_cfg = secrets.get("azure", {})
123
+ elif config_azure:
124
+ azure_cfg = config_azure
125
+
126
+ use_default_cred = azure_cfg.get("use_default_azure_credential", False)
127
+ base_url = azure_cfg.get("base_url")
128
+ api_key = azure_cfg.get("api_key")
129
+ # DefaultAzureCredential mode
130
+ if use_default_cred and base_url:
131
+ results[provider_value]["config"] = "DefaultAzureCredential"
132
+ # API key mode (retrocompatible)
133
+ if api_key and api_key != API_KEY_HINT_TEXT:
134
+ if len(api_key) > 5:
135
+ if results[provider_value]["config"]:
136
+ results[provider_value]["config"] += " + api_key"
137
+ else:
138
+ results[provider_value]["config"] = f"...{api_key[-5:]}"
129
139
  else:
130
- results[provider_value]["config"] = "...***"
140
+ if results[provider_value]["config"]:
141
+ results[provider_value]["config"] += " + api_key"
142
+ else:
143
+ results[provider_value]["config"] = "...***"
144
+ else:
145
+ # Check environment variables using ProviderKeyManager
146
+ env_key_name = ProviderKeyManager.get_env_key_name(provider_value)
147
+ env_key_value = os.environ.get(env_key_name)
148
+ if env_key_value:
149
+ if len(env_key_value) > 5:
150
+ results[provider_value]["env"] = f"...{env_key_value[-5:]}"
151
+ else:
152
+ results[provider_value]["env"] = "...***"
153
+
154
+ # Check secrets file if it was parsed successfully
155
+ if secrets_status == "parsed":
156
+ config_key = ProviderKeyManager.get_config_file_key(provider_value, secrets)
157
+ if config_key and config_key != API_KEY_HINT_TEXT:
158
+ if len(config_key) > 5:
159
+ results[provider_value]["config"] = f"...{config_key[-5:]}"
160
+ else:
161
+ results[provider_value]["config"] = "...***"
131
162
 
132
163
  return results
133
164
 
@@ -235,7 +266,7 @@ def show_check_summary() -> None:
235
266
  system_info = get_system_info()
236
267
  config_summary = get_config_summary(config_files["config"])
237
268
  secrets_summary = get_secrets_summary(config_files["secrets"])
238
- api_keys = check_api_keys(secrets_summary)
269
+ api_keys = check_api_keys(secrets_summary, config_summary)
239
270
  fastagent_version = get_fastagent_version()
240
271
 
241
272
  # System info panel
@@ -341,8 +372,10 @@ def show_check_summary() -> None:
341
372
 
342
373
  keys_table.add_row(provider.capitalize(), env_status, config_status, active)
343
374
 
344
- console.print(Panel(keys_table, title="API Keys", border_style="blue"))
345
-
375
+ # Print the API Keys panel (fix: this was missing)
376
+ keys_panel = Panel(keys_table, title="API Keys", border_style="blue", subtitle_align="left")
377
+ console.print(keys_panel)
378
+
346
379
  # MCP Servers panel (shown after API Keys)
347
380
  if config_summary.get("status") == "parsed":
348
381
  mcp_servers = config_summary.get("mcp_servers", [])
@@ -447,4 +480,4 @@ def show(
447
480
  def main(ctx: typer.Context) -> None:
448
481
  """Check and diagnose FastAgent configuration."""
449
482
  if ctx.invoked_subcommand is None:
450
- show_check_summary()
483
+ show_check_summary()
@@ -179,6 +179,20 @@ class OpenRouterSettings(BaseModel):
179
179
  model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
180
180
 
181
181
 
182
+ class AzureSettings(BaseModel):
183
+ """
184
+ Settings for using Azure OpenAI Service in the fast-agent application.
185
+ """
186
+
187
+ api_key: str | None = None
188
+ resource_name: str | None = None
189
+ azure_deployment: str | None = None
190
+ api_version: str | None = None
191
+ base_url: str | None = None # Optional, can be constructed from resource_name
192
+
193
+ model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
194
+
195
+
182
196
  class OpenTelemetrySettings(BaseModel):
183
197
  """
184
198
  OTEL settings for the fast-agent application.
@@ -302,6 +316,9 @@ class Settings(BaseSettings):
302
316
  tensorzero: Optional[TensorZeroSettings] = None
303
317
  """Settings for using TensorZero inference gateway"""
304
318
 
319
+ azure: AzureSettings | None = None
320
+ """Settings for using Azure OpenAI Service in the fast-agent application"""
321
+
305
322
  logger: LoggerSettings | None = LoggerSettings()
306
323
  """Logger settings for the fast-agent application"""
307
324
 
@@ -11,6 +11,7 @@ from mcp import ServerSession
11
11
  from opentelemetry import trace
12
12
  from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
13
13
  from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
14
+ from opentelemetry.instrumentation.mcp import McpInstrumentor
14
15
  from opentelemetry.instrumentation.openai import OpenAIInstrumentor
15
16
  from opentelemetry.propagate import set_global_textmap
16
17
  from opentelemetry.sdk.resources import Resource
@@ -111,6 +112,7 @@ async def configure_otel(config: "Settings") -> None:
111
112
  trace.set_tracer_provider(tracer_provider)
112
113
  AnthropicInstrumentor().instrument()
113
114
  OpenAIInstrumentor().instrument()
115
+ McpInstrumentor().instrument()
114
116
 
115
117
 
116
118
  async def configure_logger(config: "Settings") -> None:
@@ -19,6 +19,7 @@ class ProgressAction(str, Enum):
19
19
  PLANNING = "Planning"
20
20
  READY = "Ready"
21
21
  CALLING_TOOL = "Calling Tool"
22
+ UPDATED = "Updated"
22
23
  FINISHED = "Finished"
23
24
  SHUTDOWN = "Shutdown"
24
25
  AGGREGATOR_INITIALIZED = "Running"
@@ -10,6 +10,7 @@ from mcp_agent.llm.augmented_llm_passthrough import PassthroughLLM
10
10
  from mcp_agent.llm.augmented_llm_playback import PlaybackLLM
11
11
  from mcp_agent.llm.provider_types import Provider
12
12
  from mcp_agent.llm.providers.augmented_llm_anthropic import AnthropicAugmentedLLM
13
+ from mcp_agent.llm.providers.augmented_llm_azure import AzureOpenAIAugmentedLLM
13
14
  from mcp_agent.llm.providers.augmented_llm_deepseek import DeepSeekAugmentedLLM
14
15
  from mcp_agent.llm.providers.augmented_llm_generic import GenericAugmentedLLM
15
16
  from mcp_agent.llm.providers.augmented_llm_google import GoogleAugmentedLLM
@@ -113,6 +114,7 @@ class ModelFactory:
113
114
  Provider.GOOGLE: GoogleAugmentedLLM, # type: ignore
114
115
  Provider.OPENROUTER: OpenRouterAugmentedLLM,
115
116
  Provider.TENSORZERO: TensorZeroAugmentedLLM,
117
+ Provider.AZURE: AzureOpenAIAugmentedLLM,
116
118
  }
117
119
 
118
120
  # Mapping of special model names to their specific LLM classes
@@ -16,3 +16,4 @@ class Provider(Enum):
16
16
  GENERIC = "generic"
17
17
  OPENROUTER = "openrouter"
18
18
  TENSORZERO = "tensorzero" # For TensorZero Gateway
19
+ AZURE = "azure" # Azure OpenAI Service
@@ -0,0 +1,137 @@
1
+ from openai import AuthenticationError, AzureOpenAI, OpenAI
2
+
3
+ from mcp_agent.core.exceptions import ProviderKeyError
4
+ from mcp_agent.llm.provider_types import Provider
5
+ from mcp_agent.llm.providers.augmented_llm_openai import OpenAIAugmentedLLM
6
+
7
+ try:
8
+ from azure.identity import DefaultAzureCredential
9
+ except ImportError:
10
+ DefaultAzureCredential = None
11
+
12
+
13
+ def _extract_resource_name(url: str) -> str | None:
14
+ from urllib.parse import urlparse
15
+
16
+ host = urlparse(url).hostname or ""
17
+ suffix = ".openai.azure.com"
18
+ return host.replace(suffix, "") if host.endswith(suffix) else None
19
+
20
+
21
+ DEFAULT_AZURE_API_VERSION = "2023-05-15"
22
+
23
+
24
+ class AzureOpenAIAugmentedLLM(OpenAIAugmentedLLM):
25
+ """
26
+ Azure OpenAI implementation extending OpenAIAugmentedLLM.
27
+ Handles both API Key and DefaultAzureCredential authentication.
28
+ """
29
+
30
+ def __init__(self, provider: Provider = Provider.AZURE, *args, **kwargs):
31
+ # Set provider to AZURE, pass through to base
32
+ super().__init__(provider=provider, *args, **kwargs)
33
+
34
+ # Context/config extraction
35
+ context = getattr(self, "context", None)
36
+ config = getattr(context, "config", None) if context else None
37
+ azure_cfg = getattr(config, "azure", None) if config else None
38
+
39
+ if azure_cfg is None:
40
+ raise ProviderKeyError(
41
+ "Missing Azure configuration",
42
+ "Azure provider requires configuration section 'azure' in your config file.",
43
+ )
44
+
45
+ self.use_default_cred = getattr(azure_cfg, "use_default_azure_credential", False)
46
+ default_request_params = getattr(self, "default_request_params", None)
47
+ self.deployment_name = getattr(default_request_params, "model", None) or getattr(
48
+ azure_cfg, "azure_deployment", None
49
+ )
50
+ self.api_version = getattr(azure_cfg, "api_version", None) or DEFAULT_AZURE_API_VERSION
51
+
52
+ if self.use_default_cred:
53
+ self.base_url = getattr(azure_cfg, "base_url", None)
54
+ if not self.base_url:
55
+ raise ProviderKeyError(
56
+ "Missing Azure endpoint",
57
+ "When using 'use_default_azure_credential', 'base_url' is required in azure config.",
58
+ )
59
+ if DefaultAzureCredential is None:
60
+ raise ProviderKeyError(
61
+ "azure-identity not installed",
62
+ "You must install 'azure-identity' to use DefaultAzureCredential authentication.",
63
+ )
64
+ self.credential = DefaultAzureCredential()
65
+
66
+ def get_azure_token():
67
+ token = self.credential.get_token("https://cognitiveservices.azure.com/.default")
68
+ return token.token
69
+
70
+ self.get_azure_token = get_azure_token
71
+ else:
72
+ self.api_key = getattr(azure_cfg, "api_key", None)
73
+ self.resource_name = getattr(azure_cfg, "resource_name", None)
74
+ self.base_url = getattr(azure_cfg, "base_url", None) or (
75
+ f"https://{self.resource_name}.openai.azure.com/" if self.resource_name else None
76
+ )
77
+ if not self.api_key:
78
+ raise ProviderKeyError(
79
+ "Missing Azure OpenAI credentials",
80
+ "Field 'api_key' is required in azure config.",
81
+ )
82
+ if not (self.resource_name or self.base_url):
83
+ raise ProviderKeyError(
84
+ "Missing Azure endpoint",
85
+ "Provide either 'resource_name' or 'base_url' under azure config.",
86
+ )
87
+ if not self.deployment_name:
88
+ raise ProviderKeyError(
89
+ "Missing deployment name",
90
+ "Set 'azure_deployment' in config or pass model=<deployment>.",
91
+ )
92
+ # If resource_name was missing, try to extract it from base_url
93
+ if not self.resource_name and self.base_url:
94
+ self.resource_name = _extract_resource_name(self.base_url)
95
+
96
+ def _openai_client(self) -> OpenAI:
97
+ """
98
+ Returns an AzureOpenAI client, handling both API Key and DefaultAzureCredential.
99
+ """
100
+ try:
101
+ if self.use_default_cred:
102
+ if self.base_url is None:
103
+ raise ProviderKeyError(
104
+ "Missing Azure endpoint",
105
+ "azure_endpoint (base_url) is None at client creation time.",
106
+ )
107
+ return AzureOpenAI(
108
+ azure_ad_token_provider=self.get_azure_token,
109
+ azure_endpoint=self.base_url,
110
+ api_version=self.api_version,
111
+ azure_deployment=self.deployment_name,
112
+ )
113
+ else:
114
+ if self.base_url is None:
115
+ raise ProviderKeyError(
116
+ "Missing Azure endpoint",
117
+ "azure_endpoint (base_url) is None at client creation time.",
118
+ )
119
+ return AzureOpenAI(
120
+ api_key=self.api_key,
121
+ azure_endpoint=self.base_url,
122
+ api_version=self.api_version,
123
+ azure_deployment=self.deployment_name,
124
+ )
125
+ except AuthenticationError as e:
126
+ if self.use_default_cred:
127
+ raise ProviderKeyError(
128
+ "Invalid Azure AD credentials",
129
+ "The configured Azure AD credentials were rejected.\n"
130
+ "Please check your Azure identity setup.",
131
+ ) from e
132
+ else:
133
+ raise ProviderKeyError(
134
+ "Invalid Azure OpenAI API key",
135
+ "The configured Azure OpenAI API key was rejected.\n"
136
+ "Please check that your API key is valid and not expired.",
137
+ ) from e
@@ -78,7 +78,7 @@ class OpenAIAugmentedLLM(AugmentedLLM[ChatCompletionMessageParam, ChatCompletion
78
78
  self._reasoning_effort = self.context.config.openai.reasoning_effort
79
79
 
80
80
  # Determine if we're using a reasoning model
81
- # TODO -- move this to model capabiltities, add o4.
81
+ # TODO -- move this to model capabilities, add o4.
82
82
  chosen_model = self.default_request_params.model if self.default_request_params else None
83
83
  self._reasoning = chosen_model and (
84
84
  chosen_model.startswith("o3") or chosen_model.startswith("o1")
@@ -274,7 +274,9 @@ class OpenAIAugmentedLLM(AugmentedLLM[ChatCompletionMessageParam, ChatCompletion
274
274
  # Calculate new conversation messages (excluding prompts)
275
275
  new_messages = messages[len(prompt_messages) :]
276
276
 
277
- # Update conversation history
277
+ if system_prompt:
278
+ new_messages = new_messages[1:]
279
+
278
280
  self.history.set(new_messages)
279
281
 
280
282
  self._log_chat_finished(model=self.default_request_params.model)
@@ -323,7 +325,7 @@ class OpenAIAugmentedLLM(AugmentedLLM[ChatCompletionMessageParam, ChatCompletion
323
325
  return result
324
326
 
325
327
  def _prepare_api_request(
326
- self, messages, tools, request_params: RequestParams
328
+ self, messages, tools: List[ChatCompletionToolParam] | None, request_params: RequestParams
327
329
  ) -> dict[str, str]:
328
330
  # Create base arguments dictionary
329
331
 
@@ -343,9 +345,8 @@ class OpenAIAugmentedLLM(AugmentedLLM[ChatCompletionMessageParam, ChatCompletion
343
345
  )
344
346
  else:
345
347
  base_args["max_tokens"] = request_params.maxTokens
346
-
347
- if tools:
348
- base_args["parallel_tool_calls"] = request_params.parallel_tool_calls
348
+ if tools:
349
+ base_args["parallel_tool_calls"] = request_params.parallel_tool_calls
349
350
 
350
351
  arguments: Dict[str, str] = self.prepare_provider_arguments(
351
352
  base_args, request_params, self.OPENAI_EXCLUDE_FIELDS.union(self.BASE_EXCLUDE_FIELDS)
@@ -354,7 +355,7 @@ class OpenAIAugmentedLLM(AugmentedLLM[ChatCompletionMessageParam, ChatCompletion
354
355
 
355
356
  def adjust_schema(self, inputSchema: Dict) -> Dict:
356
357
  # return inputSchema
357
- if not Provider.OPENAI == self.provider:
358
+ if self.provider not in [Provider.OPENAI, Provider.AZURE]:
358
359
  return inputSchema
359
360
 
360
361
  if "properties" in inputSchema:
@@ -0,0 +1,16 @@
1
+ """
2
+ Common constants and utilities shared between modules to avoid circular imports.
3
+ """
4
+
5
+ # Constants
6
+ SEP = "-"
7
+
8
+
9
+ def create_namespaced_name(server_name: str, resource_name: str) -> str:
10
+ """Create a namespaced resource name from server and resource names"""
11
+ return f"{server_name}{SEP}{resource_name}"
12
+
13
+
14
+ def is_namespaced_name(name: str) -> bool:
15
+ """Check if a name is already namespaced"""
16
+ return SEP in name
@@ -6,21 +6,16 @@ It adds logging and supports sampling requests.
6
6
  from datetime import timedelta
7
7
  from typing import TYPE_CHECKING, Optional
8
8
 
9
- from mcp import ClientSession
9
+ from mcp import ClientSession, ServerNotification
10
10
  from mcp.shared.session import (
11
- ReceiveNotificationT,
12
11
  ReceiveResultT,
13
12
  RequestId,
14
13
  SendNotificationT,
15
14
  SendRequestT,
16
15
  SendResultT,
17
16
  )
18
- from mcp.types import (
19
- ErrorData,
20
- ListRootsResult,
21
- Root,
22
- )
23
- from pydantic import AnyUrl
17
+ from mcp.types import ErrorData, ListRootsResult, Root, ToolListChangedNotification
18
+ from pydantic import FileUrl
24
19
 
25
20
  from mcp_agent.context_dependent import ContextDependent
26
21
  from mcp_agent.logging.logger import get_logger
@@ -45,7 +40,7 @@ async def list_roots(ctx: ClientSession) -> ListRootsResult:
45
40
  ):
46
41
  roots = [
47
42
  Root(
48
- uri=AnyUrl(
43
+ uri=FileUrl(
49
44
  root.server_uri_alias or root.uri,
50
45
  ),
51
46
  name=root.name,
@@ -67,6 +62,11 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
67
62
  """
68
63
 
69
64
  def __init__(self, *args, **kwargs) -> None:
65
+ # Extract server_name if provided in kwargs
66
+ self.session_server_name = kwargs.pop("server_name", None)
67
+ # Extract the notification callbacks if provided
68
+ self._tool_list_changed_callback = kwargs.pop("tool_list_changed_callback", None)
69
+
70
70
  super().__init__(*args, **kwargs, list_roots_callback=list_roots, sampling_callback=sample)
71
71
  self.server_config: Optional[MCPServerSettings] = None
72
72
 
@@ -104,7 +104,7 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
104
104
  )
105
105
  return await super()._send_response(request_id, response)
106
106
 
107
- async def _received_notification(self, notification: ReceiveNotificationT) -> None:
107
+ async def _received_notification(self, notification: ServerNotification) -> None:
108
108
  """
109
109
  Can be overridden by subclasses to handle a notification without needing
110
110
  to listen on the message stream.
@@ -113,7 +113,37 @@ class MCPAgentClientSession(ClientSession, ContextDependent):
113
113
  "_received_notification: notification=",
114
114
  data=notification.model_dump(),
115
115
  )
116
- return await super()._received_notification(notification)
116
+
117
+ # Call parent notification handler first
118
+ await super()._received_notification(notification)
119
+
120
+ # Then process our specific notification types
121
+ match notification.root:
122
+ case ToolListChangedNotification():
123
+ # Simple notification handling - just call the callback if it exists
124
+ if self._tool_list_changed_callback and self.session_server_name:
125
+ logger.info(
126
+ f"Tool list changed for server '{self.session_server_name}', triggering callback"
127
+ )
128
+ # Use asyncio.create_task to prevent blocking the notification handler
129
+ import asyncio
130
+ asyncio.create_task(self._handle_tool_list_change_callback(self.session_server_name))
131
+ else:
132
+ logger.debug(
133
+ f"Tool list changed for server '{self.session_server_name}' but no callback registered"
134
+ )
135
+
136
+ return None
137
+
138
+ async def _handle_tool_list_change_callback(self, server_name: str) -> None:
139
+ """
140
+ Helper method to handle tool list change callback in a separate task
141
+ to prevent blocking the notification handler
142
+ """
143
+ try:
144
+ await self._tool_list_changed_callback(server_name)
145
+ except Exception as e:
146
+ logger.error(f"Error in tool list changed callback: {e}")
117
147
 
118
148
  async def send_progress_notification(
119
149
  self, progress_token: str | int, progress: float, total: float | None = None