hud-python 0.5.30__tar.gz → 0.5.32__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 (335) hide show
  1. {hud_python-0.5.30 → hud_python-0.5.32}/PKG-INFO +1 -1
  2. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/claude.py +11 -3
  3. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/analyze.py +18 -1
  4. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/build.py +153 -87
  5. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_analyze_module.py +40 -0
  6. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_build.py +281 -5
  7. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/docker.py +79 -1
  8. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/mcp.py +22 -0
  9. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connection.py +3 -0
  10. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/mcp_config.py +23 -12
  11. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_connection.py +29 -0
  12. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_connectors.py +43 -1
  13. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/context.py +40 -14
  14. {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/instrument.py +50 -6
  15. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/bash.py +22 -9
  16. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_bash.py +13 -20
  17. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_bash_extended.py +28 -2
  18. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/anthropic.py +69 -10
  19. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/settings.py +5 -0
  20. hud_python-0.5.32/hud/tools/computer/tests/test_compression.py +164 -0
  21. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/types.py +2 -1
  22. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_version.py +1 -1
  23. {hud_python-0.5.30 → hud_python-0.5.32}/hud/version.py +1 -1
  24. {hud_python-0.5.30 → hud_python-0.5.32}/pyproject.toml +1 -1
  25. {hud_python-0.5.30 → hud_python-0.5.32}/.gitignore +0 -0
  26. {hud_python-0.5.30 → hud_python-0.5.32}/LICENSE +0 -0
  27. {hud_python-0.5.30 → hud_python-0.5.32}/README.md +0 -0
  28. {hud_python-0.5.30 → hud_python-0.5.32}/examples/README.md +0 -0
  29. {hud_python-0.5.30 → hud_python-0.5.32}/hud/__init__.py +0 -0
  30. {hud_python-0.5.30 → hud_python-0.5.32}/hud/__main__.py +0 -0
  31. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/__init__.py +0 -0
  32. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/base.py +0 -0
  33. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/gateway.py +0 -0
  34. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/gemini.py +0 -0
  35. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/gemini_cua.py +0 -0
  36. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/grounded_openai.py +0 -0
  37. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/misc/__init__.py +0 -0
  38. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/misc/integration_test_agent.py +0 -0
  39. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/misc/response_agent.py +0 -0
  40. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/openai.py +0 -0
  41. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/openai_chat.py +0 -0
  42. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/operator.py +0 -0
  43. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/resolver.py +0 -0
  44. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/__init__.py +0 -0
  45. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/conftest.py +0 -0
  46. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_base.py +0 -0
  47. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_base_runtime.py +0 -0
  48. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_claude.py +0 -0
  49. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_gemini.py +0 -0
  50. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
  51. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_integration_test_agent.py +0 -0
  52. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_openai.py +0 -0
  53. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_operator.py +0 -0
  54. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_resolver.py +0 -0
  55. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_run_eval.py +0 -0
  56. {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/types.py +0 -0
  57. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/__init__.py +0 -0
  58. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/__main__.py +0 -0
  59. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/cancel.py +0 -0
  60. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/__init__.py +0 -0
  61. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/base.py +0 -0
  62. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/harbor.py +0 -0
  63. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/tests/__init__.py +0 -0
  64. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/tests/conftest.py +0 -0
  65. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/tests/test_harbor.py +0 -0
  66. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/debug.py +0 -0
  67. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/deploy.py +0 -0
  68. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/dev.py +0 -0
  69. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/eval.py +0 -0
  70. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/__init__.py +0 -0
  71. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/dev.py +0 -0
  72. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/init.py +0 -0
  73. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/tasks.py +0 -0
  74. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/templates.py +0 -0
  75. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/tests/__init__.py +0 -0
  76. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/tests/test_dev.py +0 -0
  77. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/init.py +0 -0
  78. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/link.py +0 -0
  79. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/models.py +0 -0
  80. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/push.py +0 -0
  81. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/rft.py +0 -0
  82. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/rft_status.py +0 -0
  83. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/__init__.py +0 -0
  84. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_analyze.py +0 -0
  85. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_analyze_metadata.py +0 -0
  86. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_build_failure.py +0 -0
  87. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_build_module.py +0 -0
  88. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_init.py +0 -0
  89. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_main.py +0 -0
  90. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
  91. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_root.py +0 -0
  92. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_convert.py +0 -0
  93. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_debug.py +0 -0
  94. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_debug_directory_mode.py +0 -0
  95. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_deploy.py +0 -0
  96. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_dev.py +0 -0
  97. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_eval.py +0 -0
  98. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_eval_bedrock.py +0 -0
  99. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_init.py +0 -0
  100. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_main_module.py +0 -0
  101. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_mcp_server.py +0 -0
  102. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_push.py +0 -0
  103. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_push_happy.py +0 -0
  104. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_push_wrapper.py +0 -0
  105. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_utils.py +0 -0
  106. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/__init__.py +0 -0
  107. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/api.py +0 -0
  108. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/args.py +0 -0
  109. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/build_display.py +0 -0
  110. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/build_logs.py +0 -0
  111. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/config.py +0 -0
  112. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/context.py +0 -0
  113. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/env_check.py +0 -0
  114. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/environment.py +0 -0
  115. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/git.py +0 -0
  116. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/interactive.py +0 -0
  117. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/lockfile.py +0 -0
  118. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/logging.py +0 -0
  119. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/metadata.py +0 -0
  120. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/server.py +0 -0
  121. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/source_hash.py +0 -0
  122. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tasks.py +0 -0
  123. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/__init__.py +0 -0
  124. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_config.py +0 -0
  125. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_docker.py +0 -0
  126. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_docker_hints.py +0 -0
  127. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_env_check.py +0 -0
  128. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_environment.py +0 -0
  129. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_git.py +0 -0
  130. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_interactive_module.py +0 -0
  131. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_logging_utils.py +0 -0
  132. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_metadata.py +0 -0
  133. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_source_hash.py +0 -0
  134. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_tasks.py +0 -0
  135. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/validation.py +0 -0
  136. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/version_check.py +0 -0
  137. {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/viewer.py +0 -0
  138. {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/__init__.py +0 -0
  139. {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/loader.py +0 -0
  140. {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/runner.py +0 -0
  141. {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/tests/__init__.py +0 -0
  142. {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/tests/test_loader.py +0 -0
  143. {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/tests/test_utils.py +0 -0
  144. {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/utils.py +0 -0
  145. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/__init__.py +0 -0
  146. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/__init__.py +0 -0
  147. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/base.py +0 -0
  148. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/local.py +0 -0
  149. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/openai.py +0 -0
  150. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/remote.py +0 -0
  151. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/environment.py +0 -0
  152. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/__init__.py +0 -0
  153. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/adk.py +0 -0
  154. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/anthropic.py +0 -0
  155. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/gemini.py +0 -0
  156. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/langchain.py +0 -0
  157. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/llamaindex.py +0 -0
  158. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/openai.py +0 -0
  159. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/mock.py +0 -0
  160. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/router.py +0 -0
  161. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/scenarios.py +0 -0
  162. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/__init__.py +0 -0
  163. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_environment.py +0 -0
  164. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_integrations.py +0 -0
  165. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_local_connectors.py +0 -0
  166. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_scenarios.py +0 -0
  167. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_session_id.py +0 -0
  168. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_tools.py +0 -0
  169. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/types.py +0 -0
  170. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/__init__.py +0 -0
  171. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/formats.py +0 -0
  172. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/schema.py +0 -0
  173. {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/tool_wrappers.py +0 -0
  174. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/__init__.py +0 -0
  175. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/display.py +0 -0
  176. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/instrument.py +0 -0
  177. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/manager.py +0 -0
  178. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/parallel.py +0 -0
  179. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/task.py +0 -0
  180. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/__init__.py +0 -0
  181. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_context.py +0 -0
  182. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_eval.py +0 -0
  183. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_manager.py +0 -0
  184. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_parallel.py +0 -0
  185. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_task.py +0 -0
  186. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/types.py +0 -0
  187. {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/utils.py +0 -0
  188. {hud_python-0.5.30 → hud_python-0.5.32}/hud/native/__init__.py +0 -0
  189. {hud_python-0.5.30 → hud_python-0.5.32}/hud/native/chat.py +0 -0
  190. {hud_python-0.5.30 → hud_python-0.5.32}/hud/patches/__init__.py +0 -0
  191. {hud_python-0.5.30 → hud_python-0.5.32}/hud/patches/mcp_patches.py +0 -0
  192. {hud_python-0.5.30 → hud_python-0.5.32}/hud/patches/warnings.py +0 -0
  193. {hud_python-0.5.30 → hud_python-0.5.32}/hud/py.typed +0 -0
  194. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/__init__.py +0 -0
  195. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/context.py +0 -0
  196. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/helper/__init__.py +0 -0
  197. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/low_level.py +0 -0
  198. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/router.py +0 -0
  199. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/server.py +0 -0
  200. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/__init__.py +0 -0
  201. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_add_tool.py +0 -0
  202. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_context.py +0 -0
  203. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_mcp_server_handlers.py +0 -0
  204. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_mcp_server_integration.py +0 -0
  205. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_mcp_server_more.py +0 -0
  206. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_prefix_naming.py +0 -0
  207. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_run_wrapper.py +0 -0
  208. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_server_extra.py +0 -0
  209. {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_sigterm_runner.py +0 -0
  210. {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/__init__.py +0 -0
  211. {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/chat.py +0 -0
  212. {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/chat_service.py +0 -0
  213. {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/reply_metadata.py +0 -0
  214. {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/tests/__init__.py +0 -0
  215. {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/tests/test_chat.py +0 -0
  216. {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/tests/test_chat_service.py +0 -0
  217. {hud_python-0.5.30 → hud_python-0.5.32}/hud/settings.py +0 -0
  218. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/__init__.py +0 -0
  219. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/exceptions.py +0 -0
  220. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/hints.py +0 -0
  221. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/requests.py +0 -0
  222. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/__init__.py +0 -0
  223. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/test_exceptions.py +0 -0
  224. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/test_hints.py +0 -0
  225. {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/test_requests.py +0 -0
  226. {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/__init__.py +0 -0
  227. {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/exporter.py +0 -0
  228. {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/__init__.py +0 -0
  229. {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/test_eval_telemetry.py +0 -0
  230. {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/test_exporter.py +0 -0
  231. {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/test_instrument.py +0 -0
  232. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/__init__.py +0 -0
  233. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/agent.py +0 -0
  234. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/base.py +0 -0
  235. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/__init__.py +0 -0
  236. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/apply_patch.py +0 -0
  237. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/edit.py +0 -0
  238. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/gemini_edit.py +0 -0
  239. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/gemini_shell.py +0 -0
  240. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/session.py +0 -0
  241. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/shell.py +0 -0
  242. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/__init__.py +0 -0
  243. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_apply_patch.py +0 -0
  244. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_bash_integration.py +0 -0
  245. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_edit.py +0 -0
  246. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_gemini_tools.py +0 -0
  247. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_shell.py +0 -0
  248. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/utils.py +0 -0
  249. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/__init__.py +0 -0
  250. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/gemini.py +0 -0
  251. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/glm.py +0 -0
  252. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/hud.py +0 -0
  253. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/openai.py +0 -0
  254. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/qwen.py +0 -0
  255. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/__init__.py +0 -0
  256. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/test_computer.py +0 -0
  257. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/test_computer_actions.py +0 -0
  258. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/test_glm_computer.py +0 -0
  259. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/elicitation.py +0 -0
  260. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/__init__.py +0 -0
  261. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/base.py +0 -0
  262. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/pyautogui.py +0 -0
  263. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/tests/__init__.py +0 -0
  264. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/tests/test_base_executor.py +0 -0
  265. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
  266. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/xdo.py +0 -0
  267. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/__init__.py +0 -0
  268. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/base.py +0 -0
  269. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/gemini.py +0 -0
  270. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/glob.py +0 -0
  271. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/grep.py +0 -0
  272. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/list.py +0 -0
  273. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/read.py +0 -0
  274. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/__init__.py +0 -0
  275. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_glob.py +0 -0
  276. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_grep.py +0 -0
  277. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_list.py +0 -0
  278. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_read.py +0 -0
  279. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/__init__.py +0 -0
  280. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/config.py +0 -0
  281. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/grounded_tool.py +0 -0
  282. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/grounder.py +0 -0
  283. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/tests/__init__.py +0 -0
  284. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
  285. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/__init__.py +0 -0
  286. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/base.py +0 -0
  287. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/code_execution.py +0 -0
  288. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/google_search.py +0 -0
  289. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/tool_search.py +0 -0
  290. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/url_context.py +0 -0
  291. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/web_fetch.py +0 -0
  292. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/web_search.py +0 -0
  293. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/jupyter.py +0 -0
  294. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/__init__.py +0 -0
  295. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/base.py +0 -0
  296. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/claude.py +0 -0
  297. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/gemini.py +0 -0
  298. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/session.py +0 -0
  299. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/__init__.py +0 -0
  300. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/test_claude.py +0 -0
  301. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/test_gemini.py +0 -0
  302. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/test_session.py +0 -0
  303. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/native_types.py +0 -0
  304. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/playwright.py +0 -0
  305. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/response.py +0 -0
  306. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/submit.py +0 -0
  307. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/__init__.py +0 -0
  308. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_agent_tool.py +0 -0
  309. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_base.py +0 -0
  310. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_elicitation.py +0 -0
  311. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_init.py +0 -0
  312. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_jupyter_tool.py +0 -0
  313. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_native_tool_e2e.py +0 -0
  314. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_native_types.py +0 -0
  315. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_playwright_tool.py +0 -0
  316. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_response.py +0 -0
  317. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_submit.py +0 -0
  318. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_tools.py +0 -0
  319. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_tools_init.py +0 -0
  320. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_types.py +0 -0
  321. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_utils.py +0 -0
  322. {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/utils.py +0 -0
  323. {hud_python-0.5.30 → hud_python-0.5.32}/hud/types.py +0 -0
  324. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/__init__.py +0 -0
  325. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/env.py +0 -0
  326. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/hud_console.py +0 -0
  327. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/mcp.py +0 -0
  328. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/pretty_errors.py +0 -0
  329. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/strict_schema.py +0 -0
  330. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/__init__.py +0 -0
  331. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_init.py +0 -0
  332. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_pretty_errors.py +0 -0
  333. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_tool_shorthand.py +0 -0
  334. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tool_shorthand.py +0 -0
  335. {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hud-python
3
- Version: 0.5.30
3
+ Version: 0.5.32
4
4
  Summary: SDK for the HUD platform.
5
5
  Project-URL: Homepage, https://github.com/hud-evals/hud-python
6
6
  Project-URL: Bug Tracker, https://github.com/hud-evals/hud-python/issues
@@ -445,7 +445,9 @@ class ClaudeAgent(MCPAgent):
445
445
  text_document_block(content.text, title=tool_call.name)
446
446
  )
447
447
  elif isinstance(content, types.ImageContent):
448
- claude_blocks.append(base64_to_content_block(content.data))
448
+ claude_blocks.append(
449
+ base64_to_content_block(content.data, content.mimeType)
450
+ )
449
451
  elif isinstance(content, types.EmbeddedResource):
450
452
  resource = content.resource
451
453
  if (
@@ -683,13 +685,19 @@ class ClaudeAgent(MCPAgent):
683
685
  return messages_cached
684
686
 
685
687
 
686
- def base64_to_content_block(base64: str) -> BetaImageBlockParam:
688
+ def base64_to_content_block(
689
+ base64: str,
690
+ media_type: str = "image/png",
691
+ ) -> BetaImageBlockParam:
687
692
  """Convert base64 image to Claude content block."""
688
693
  return BetaImageBlockParam(
689
694
  type="image",
690
695
  source=BetaBase64ImageSourceParam(
691
696
  type="base64",
692
- media_type="image/png",
697
+ media_type=cast(
698
+ "Literal['image/jpeg', 'image/png', 'image/gif', 'image/webp']",
699
+ media_type,
700
+ ),
693
701
  data=base64,
694
702
  ),
695
703
  )
@@ -439,6 +439,22 @@ async def analyze_environment_from_mcp_config(
439
439
  await _analyze_with_config(mcp_config, output_format, verbose)
440
440
 
441
441
 
442
+ def _prepare_mcp_config(mcp_config: dict[str, Any]) -> dict[str, Any]:
443
+ """Inject ``auth: None`` into URL-based server entries.
444
+
445
+ FastMCPClient attempts OAuth discovery on servers that expose a ``url``
446
+ field. For local / dev servers this causes hangs or connection errors.
447
+ Setting ``auth`` to ``None`` disables the discovery probe.
448
+ """
449
+ patched: dict[str, Any] = {}
450
+ for key, value in mcp_config.items():
451
+ if isinstance(value, dict) and "url" in value and "auth" not in value:
452
+ patched[key] = {**value, "auth": None}
453
+ else:
454
+ patched[key] = value
455
+ return patched
456
+
457
+
442
458
  async def _analyze_with_config(
443
459
  mcp_config: dict[str, Any], output_format: str, verbose: bool
444
460
  ) -> None:
@@ -455,7 +471,8 @@ async def _analyze_with_config(
455
471
 
456
472
  from hud.cli.utils.mcp import analyze_environment as mcp_analyze
457
473
 
458
- client = FastMCPClient(transport=mcp_config)
474
+ config = _prepare_mcp_config(mcp_config)
475
+ client = FastMCPClient(transport=config)
459
476
 
460
477
  try:
461
478
  await client.__aenter__()
@@ -461,107 +461,119 @@ def collect_runtime_metadata(image: str, *, verbose: bool = False) -> dict[str,
461
461
  async def analyze_mcp_environment(
462
462
  image: str, verbose: bool = False, env_vars: dict[str, str] | None = None
463
463
  ) -> dict[str, Any]:
464
- """Analyze an MCP environment to extract metadata."""
464
+ """Analyze an MCP environment to extract metadata.
465
+
466
+ Supports both stdio (default) and HTTP transport. The transport is
467
+ auto-detected from the image's CMD directive.
468
+ """
469
+ from fastmcp import Client as FastMCPClient
470
+
471
+ from hud.cli.utils.docker import (
472
+ DEFAULT_HTTP_PORT,
473
+ build_env_flags,
474
+ detect_transport,
475
+ stop_container,
476
+ )
477
+ from hud.cli.utils.mcp import analyze_environment
478
+
465
479
  hud_console = HUDConsole()
466
480
  env_vars = env_vars or {}
481
+ transport_mode, container_port = detect_transport(image)
482
+ is_http = transport_mode == "http"
483
+ container_name: str | None = None
484
+ initialized = False
485
+ client: Any = None
467
486
 
468
- # Build Docker command to run the image, injecting any provided env vars
469
- from hud.cli.utils.docker import build_env_flags
487
+ try:
488
+ # --- transport-specific setup ---
489
+ if is_http:
490
+ from hud.cli.utils.logging import find_free_port
491
+ from hud.cli.utils.mcp import wait_for_http_server
492
+
493
+ port = container_port or DEFAULT_HTTP_PORT
494
+ host_port = find_free_port(port)
495
+ if host_port is None:
496
+ from hud.shared.exceptions import HudException
497
+
498
+ raise HudException(f"No free port found starting from {port}")
499
+
500
+ container_name = f"hud-build-analyze-{os.getpid()}"
501
+ docker_cmd = [
502
+ "docker",
503
+ "run",
504
+ "-d",
505
+ "--rm",
506
+ "--name",
507
+ container_name,
508
+ "-p",
509
+ f"{host_port}:{port}",
510
+ *build_env_flags(env_vars),
511
+ image,
512
+ ]
513
+ hud_console.dim_info("Command:", " ".join(docker_cmd))
514
+ hud_console.info(f"HTTP transport detected — mapping port {host_port}:{port}")
470
515
 
471
- docker_cmd = ["docker", "run", "--rm", "-i", *build_env_flags(env_vars), image]
516
+ try:
517
+ proc = await asyncio.to_thread(
518
+ subprocess.run,
519
+ docker_cmd,
520
+ capture_output=True,
521
+ text=True,
522
+ check=True,
523
+ timeout=30,
524
+ )
525
+ except subprocess.CalledProcessError as e:
526
+ from hud.shared.exceptions import HudException
472
527
 
473
- # Show full docker command being used for analysis
474
- hud_console.dim_info("Command:", " ".join(docker_cmd))
528
+ hud_console.error(f"Failed to start container: {e.stderr.strip()}")
529
+ raise HudException("Failed to start Docker container for HTTP analysis") from e
475
530
 
476
- # Create MCP config consistently with analyze helpers
477
- from hud.cli.analyze import parse_docker_command
531
+ if verbose:
532
+ hud_console.info(f"Container started: {proc.stdout.strip()[:12]}")
478
533
 
479
- mcp_config = parse_docker_command(docker_cmd)
480
- # Extract server name for display (first key in mcp_config)
481
- server_name = next(iter(mcp_config.keys()), None)
534
+ server_url = f"http://localhost:{host_port}/mcp"
535
+ if verbose:
536
+ hud_console.info(f"Waiting for server at {server_url} ...")
482
537
 
483
- # Initialize client and measure timing
484
- from fastmcp import Client as FastMCPClient
538
+ mcp_config: dict[str, Any] = {"hud": {"url": server_url, "auth": None}}
539
+ server_name = "hud"
540
+ else:
541
+ docker_cmd = ["docker", "run", "--rm", "-i", *build_env_flags(env_vars), image]
542
+ hud_console.dim_info("Command:", " ".join(docker_cmd))
485
543
 
486
- from hud.cli.utils.mcp import analyze_environment
544
+ from hud.cli.analyze import parse_docker_command
487
545
 
488
- start_time = time.time()
489
- client = FastMCPClient(transport=mcp_config)
490
- initialized = False
546
+ mcp_config = parse_docker_command(docker_cmd)
547
+ server_name = next(iter(mcp_config.keys()), None)
548
+
549
+ # --- shared: connect, analyze, build result ---
550
+ start_time = time.time()
551
+ client = FastMCPClient(transport=mcp_config)
491
552
 
492
- try:
493
553
  if verbose:
494
554
  hud_console.info("Initializing MCP client...")
495
555
 
496
- # Add timeout to fail fast instead of hanging (60 seconds)
497
- await asyncio.wait_for(client.__aenter__(), timeout=60.0)
556
+ if is_http:
557
+ await wait_for_http_server( # type: ignore[possibly-undefined]
558
+ server_url, timeout_seconds=60.0
559
+ )
560
+ await asyncio.wait_for(client.__aenter__(), timeout=60.0)
561
+ else:
562
+ await asyncio.wait_for(client.__aenter__(), timeout=60.0)
563
+
498
564
  initialized = True
499
565
  initialize_ms = int((time.time() - start_time) * 1000)
500
566
 
501
- # Delegate to standard analysis helper
502
567
  full_analysis = await analyze_environment(client, verbose, server_name=server_name)
503
-
504
- # Normalize and enrich with internalTools if a hub map is present
505
- tools_list = full_analysis.get("tools", [])
506
- hub_map = full_analysis.get("hub_tools", {}) or full_analysis.get("hubTools", {})
507
-
508
- normalized_tools: list[dict[str, Any]] = []
509
- internal_total = 0
510
- for t in tools_list:
511
- # Extract core fields (support object or dict forms)
512
- if hasattr(t, "name"):
513
- name = getattr(t, "name", None)
514
- description = getattr(t, "description", None)
515
- input_schema = getattr(t, "inputSchema", None)
516
- existing_internal = getattr(t, "internalTools", None)
517
- else:
518
- name = t.get("name")
519
- description = t.get("description")
520
- # accept either inputSchema or input_schema
521
- input_schema = t.get("inputSchema") or t.get("input_schema")
522
- # accept either internalTools or internal_tools
523
- existing_internal = t.get("internalTools") or t.get("internal_tools")
524
-
525
- tool_entry: dict[str, Any] = {"name": name}
526
- if description:
527
- tool_entry["description"] = description
528
- if input_schema:
529
- tool_entry["inputSchema"] = input_schema
530
-
531
- # Merge internal tools: preserve any existing declaration and add hub_map[name]
532
- merged_internal: list[str] = []
533
- if isinstance(existing_internal, list):
534
- merged_internal.extend([str(x) for x in existing_internal])
535
- if isinstance(hub_map, dict) and name in hub_map and isinstance(hub_map[name], list):
536
- merged_internal.extend([str(x) for x in hub_map[name]])
537
- if merged_internal:
538
- # Deduplicate while preserving order
539
- merged_internal = list(dict.fromkeys(merged_internal))
540
- tool_entry["internalTools"] = merged_internal
541
- internal_total += len(merged_internal)
542
-
543
- normalized_tools.append(tool_entry)
544
-
545
- result = {
546
- "initializeMs": initialize_ms,
547
- "toolCount": len(tools_list),
548
- "internalToolCount": internal_total,
549
- "tools": normalized_tools,
550
- "success": True,
551
- }
552
- if hub_map:
553
- result["hub_tools"] = hub_map
554
- # Include prompts and resources from analysis
555
- if full_analysis.get("prompts"):
556
- result["prompts"] = full_analysis["prompts"]
557
- if full_analysis.get("resources"):
558
- result["resources"] = full_analysis["resources"]
559
- if "scenarios" in full_analysis:
560
- result["scenarios"] = full_analysis["scenarios"]
561
- return result
568
+ return _build_analysis_result(full_analysis, initialize_ms)
562
569
  except TimeoutError:
563
570
  from hud.shared.exceptions import HudException
564
571
 
572
+ if is_http:
573
+ hud_console.error("MCP server did not become ready/initialize within 60 seconds")
574
+ if container_name:
575
+ hud_console.info("Check container logs: docker logs " + container_name)
576
+ raise HudException("MCP server HTTP readiness timeout") from None
565
577
  hud_console.error("MCP server initialization timed out after 60 seconds")
566
578
  hud_console.info(
567
579
  "The server likely crashed during startup - check stderr logs with 'hud debug'"
@@ -570,16 +582,70 @@ async def analyze_mcp_environment(
570
582
  except Exception as e:
571
583
  from hud.shared.exceptions import HudException
572
584
 
573
- # Convert to HudException for better error messages and hints
585
+ if isinstance(e, HudException):
586
+ raise
574
587
  raise HudException from e
575
588
  finally:
576
- # Only shutdown if we successfully initialized
577
- if initialized and client.is_connected():
578
- try:
589
+ if initialized and client is not None:
590
+ with contextlib.suppress(Exception):
579
591
  await client.close()
580
- except Exception:
581
- # Ignore shutdown errors
582
- hud_console.warning("Failed to shutdown MCP client")
592
+ if container_name:
593
+ stop_container(container_name)
594
+
595
+
596
+ def _build_analysis_result(full_analysis: dict[str, Any], initialize_ms: int) -> dict[str, Any]:
597
+ """Normalize the raw analysis dict into the build-lock result format."""
598
+ tools_list = full_analysis.get("tools", [])
599
+ hub_map = full_analysis.get("hub_tools", {}) or full_analysis.get("hubTools", {})
600
+
601
+ normalized_tools: list[dict[str, Any]] = []
602
+ internal_total = 0
603
+ for t in tools_list:
604
+ if hasattr(t, "name"):
605
+ name = getattr(t, "name", None)
606
+ description = getattr(t, "description", None)
607
+ input_schema = getattr(t, "inputSchema", None)
608
+ existing_internal = getattr(t, "internalTools", None)
609
+ else:
610
+ name = t.get("name")
611
+ description = t.get("description")
612
+ input_schema = t.get("inputSchema") or t.get("input_schema")
613
+ existing_internal = t.get("internalTools") or t.get("internal_tools")
614
+
615
+ tool_entry: dict[str, Any] = {"name": name}
616
+ if description:
617
+ tool_entry["description"] = description
618
+ if input_schema:
619
+ tool_entry["inputSchema"] = input_schema
620
+
621
+ merged_internal: list[str] = []
622
+ if isinstance(existing_internal, list):
623
+ merged_internal.extend([str(x) for x in existing_internal])
624
+ if isinstance(hub_map, dict) and name in hub_map and isinstance(hub_map[name], list):
625
+ merged_internal.extend([str(x) for x in hub_map[name]])
626
+ if merged_internal:
627
+ merged_internal = list(dict.fromkeys(merged_internal))
628
+ tool_entry["internalTools"] = merged_internal
629
+ internal_total += len(merged_internal)
630
+
631
+ normalized_tools.append(tool_entry)
632
+
633
+ result: dict[str, Any] = {
634
+ "initializeMs": initialize_ms,
635
+ "toolCount": len(tools_list),
636
+ "internalToolCount": internal_total,
637
+ "tools": normalized_tools,
638
+ "success": True,
639
+ }
640
+ if hub_map:
641
+ result["hub_tools"] = hub_map
642
+ if full_analysis.get("prompts"):
643
+ result["prompts"] = full_analysis["prompts"]
644
+ if full_analysis.get("resources"):
645
+ result["resources"] = full_analysis["resources"]
646
+ if "scenarios" in full_analysis:
647
+ result["scenarios"] = full_analysis["scenarios"]
648
+ return result
583
649
 
584
650
 
585
651
  def build_docker_image(
@@ -6,6 +6,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
6
6
  import pytest
7
7
 
8
8
  from hud.cli.analyze import (
9
+ _prepare_mcp_config,
9
10
  analyze_environment,
10
11
  analyze_environment_from_config,
11
12
  analyze_environment_from_mcp_config,
@@ -125,3 +126,42 @@ async def test_analyze_environment_from_mcp_config(MockClient, mock_mcp_analyze)
125
126
  mcp_config = {"local": {"command": "docker", "args": ["run", "img"]}}
126
127
  await analyze_environment_from_mcp_config(mcp_config, output_format="json", verbose=False)
127
128
  assert client.__aenter__.awaited and client.close.awaited
129
+
130
+
131
+ @patch("hud.cli.utils.mcp.analyze_environment")
132
+ @patch("fastmcp.Client")
133
+ async def test_analyze_environment_from_mcp_config_http(MockClient, mock_mcp_analyze):
134
+ """HTTP transport (hud dev) should inject auth=None to skip OAuth discovery."""
135
+ client = MagicMock()
136
+ client.__aenter__ = AsyncMock(return_value=client)
137
+ client.is_connected = MagicMock(return_value=True)
138
+ client.close = AsyncMock()
139
+ MockClient.return_value = client
140
+ mock_mcp_analyze.return_value = {"tools": [], "resources": []}
141
+
142
+ mcp_config = {"hud": {"url": "http://localhost:8000/mcp"}}
143
+ await analyze_environment_from_mcp_config(mcp_config, output_format="json", verbose=False)
144
+ assert client.__aenter__.awaited and client.close.awaited
145
+ # Verify that _prepare_mcp_config injected auth=None
146
+ call_kwargs = MockClient.call_args
147
+ transport_arg = call_kwargs.kwargs.get("transport") or call_kwargs.args[0]
148
+ assert transport_arg["hud"]["auth"] is None
149
+
150
+
151
+ def test_prepare_mcp_config_injects_auth_for_url():
152
+ """URL-based entries get auth=None; stdio entries are left alone."""
153
+ cfg = {
154
+ "hud": {"url": "http://localhost:8000/mcp"},
155
+ "local": {"command": "docker", "args": ["run", "img"]},
156
+ }
157
+ result = _prepare_mcp_config(cfg)
158
+ assert result["hud"]["auth"] is None
159
+ assert result["hud"]["url"] == "http://localhost:8000/mcp"
160
+ assert "auth" not in result["local"]
161
+
162
+
163
+ def test_prepare_mcp_config_preserves_explicit_auth():
164
+ """If auth is already set, don't overwrite it."""
165
+ cfg = {"hud": {"url": "http://localhost:8000/mcp", "auth": "bearer-token"}}
166
+ result = _prepare_mcp_config(cfg)
167
+ assert result["hud"]["auth"] == "bearer-token"