hud-python 0.5.7__tar.gz → 0.5.9__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 (305) hide show
  1. {hud_python-0.5.7 → hud_python-0.5.9}/PKG-INFO +1 -1
  2. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/__init__.py +21 -3
  3. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/build.py +11 -2
  4. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/mcp_use.py +6 -1
  5. {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/loader.py +4 -8
  6. {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/tests/test_loader.py +14 -14
  7. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connection.py +28 -4
  8. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/mcp_config.py +29 -1
  9. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/environment.py +11 -4
  10. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_connectors.py +10 -23
  11. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_environment.py +248 -0
  12. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_local_connectors.py +81 -40
  13. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_task.py +107 -0
  14. {hud_python-0.5.7 → hud_python-0.5.9}/hud/patches/mcp_patches.py +64 -21
  15. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/agent.py +23 -16
  16. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_version.py +1 -1
  17. {hud_python-0.5.7 → hud_python-0.5.9}/hud/version.py +1 -1
  18. {hud_python-0.5.7 → hud_python-0.5.9}/pyproject.toml +1 -1
  19. {hud_python-0.5.7 → hud_python-0.5.9}/.gitignore +0 -0
  20. {hud_python-0.5.7 → hud_python-0.5.9}/LICENSE +0 -0
  21. {hud_python-0.5.7 → hud_python-0.5.9}/README.md +0 -0
  22. {hud_python-0.5.7 → hud_python-0.5.9}/examples/README.md +0 -0
  23. {hud_python-0.5.7 → hud_python-0.5.9}/hud/__init__.py +0 -0
  24. {hud_python-0.5.7 → hud_python-0.5.9}/hud/__main__.py +0 -0
  25. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/__init__.py +0 -0
  26. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/base.py +0 -0
  27. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/claude.py +0 -0
  28. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/gateway.py +0 -0
  29. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/gemini.py +0 -0
  30. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/gemini_cua.py +0 -0
  31. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/grounded_openai.py +0 -0
  32. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/misc/__init__.py +0 -0
  33. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/misc/integration_test_agent.py +0 -0
  34. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/misc/response_agent.py +0 -0
  35. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/openai.py +0 -0
  36. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/openai_chat.py +0 -0
  37. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/operator.py +0 -0
  38. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/resolver.py +0 -0
  39. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/__init__.py +0 -0
  40. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/conftest.py +0 -0
  41. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_base.py +0 -0
  42. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_base_runtime.py +0 -0
  43. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_claude.py +0 -0
  44. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_client.py +0 -0
  45. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_gemini.py +0 -0
  46. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
  47. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_openai.py +0 -0
  48. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_operator.py +0 -0
  49. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_resolver.py +0 -0
  50. {hud_python-0.5.7 → hud_python-0.5.9}/hud/agents/tests/test_run_eval.py +0 -0
  51. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/__main__.py +0 -0
  52. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/analyze.py +0 -0
  53. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/clone.py +0 -0
  54. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/debug.py +0 -0
  55. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/dev.py +0 -0
  56. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/eval.py +0 -0
  57. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/__init__.py +0 -0
  58. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/dev.py +0 -0
  59. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/init.py +0 -0
  60. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/tasks.py +0 -0
  61. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/templates.py +0 -0
  62. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/tests/__init__.py +0 -0
  63. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/flows/tests/test_dev.py +0 -0
  64. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/get.py +0 -0
  65. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/init.py +0 -0
  66. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/list_func.py +0 -0
  67. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/pull.py +0 -0
  68. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/push.py +0 -0
  69. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/remove.py +0 -0
  70. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/rft.py +0 -0
  71. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/rft_status.py +0 -0
  72. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/__init__.py +0 -0
  73. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_analyze.py +0 -0
  74. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_analyze_metadata.py +0 -0
  75. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_analyze_module.py +0 -0
  76. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_build.py +0 -0
  77. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_build_failure.py +0 -0
  78. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_build_module.py +0 -0
  79. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_init.py +0 -0
  80. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_main.py +0 -0
  81. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
  82. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cli_root.py +0 -0
  83. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_clone.py +0 -0
  84. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_convert.py +0 -0
  85. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_cursor.py +0 -0
  86. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_debug.py +0 -0
  87. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_dev.py +0 -0
  88. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_eval.py +0 -0
  89. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_eval_bedrock.py +0 -0
  90. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_init.py +0 -0
  91. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_list_func.py +0 -0
  92. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_main_module.py +0 -0
  93. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_mcp_server.py +0 -0
  94. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_pull.py +0 -0
  95. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_push.py +0 -0
  96. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_push_happy.py +0 -0
  97. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_push_wrapper.py +0 -0
  98. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_registry.py +0 -0
  99. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/tests/test_utils.py +0 -0
  100. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/__init__.py +0 -0
  101. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/celebrate.py +0 -0
  102. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/config.py +0 -0
  103. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/cursor.py +0 -0
  104. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/docker.py +0 -0
  105. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/env_check.py +0 -0
  106. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/environment.py +0 -0
  107. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/git.py +0 -0
  108. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/interactive.py +0 -0
  109. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/local_runner.py +0 -0
  110. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/logging.py +0 -0
  111. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/metadata.py +0 -0
  112. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/package_runner.py +0 -0
  113. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/registry.py +0 -0
  114. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/remote_runner.py +0 -0
  115. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/runner.py +0 -0
  116. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/server.py +0 -0
  117. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/source_hash.py +0 -0
  118. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tasks.py +0 -0
  119. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/__init__.py +0 -0
  120. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_config.py +0 -0
  121. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_docker.py +0 -0
  122. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_docker_hints.py +0 -0
  123. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_env_check.py +0 -0
  124. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_environment.py +0 -0
  125. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_git.py +0 -0
  126. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_interactive_module.py +0 -0
  127. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_local_runner.py +0 -0
  128. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_logging_utils.py +0 -0
  129. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_metadata.py +0 -0
  130. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_package_runner.py +0 -0
  131. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_registry_utils.py +0 -0
  132. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_remote_runner.py +0 -0
  133. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_runner_modules.py +0 -0
  134. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_source_hash.py +0 -0
  135. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/tests/test_tasks.py +0 -0
  136. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/version_check.py +0 -0
  137. {hud_python-0.5.7 → hud_python-0.5.9}/hud/cli/utils/viewer.py +0 -0
  138. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/README.md +0 -0
  139. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/__init__.py +0 -0
  140. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/base.py +0 -0
  141. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/environment.py +0 -0
  142. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/fastmcp.py +0 -0
  143. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/__init__.py +0 -0
  144. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_analyze_scenarios.py +0 -0
  145. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_client_integration.py +0 -0
  146. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_fastmcp.py +0 -0
  147. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_mcp_use_retry.py +0 -0
  148. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/tests/test_protocol.py +0 -0
  149. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/__init__.py +0 -0
  150. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/mcp_use_retry.py +0 -0
  151. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/retry.py +0 -0
  152. {hud_python-0.5.7 → hud_python-0.5.9}/hud/clients/utils/retry_transport.py +0 -0
  153. {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/__init__.py +0 -0
  154. {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/runner.py +0 -0
  155. {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/tests/__init__.py +0 -0
  156. {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/tests/test_utils.py +0 -0
  157. {hud_python-0.5.7 → hud_python-0.5.9}/hud/datasets/utils.py +0 -0
  158. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/__init__.py +0 -0
  159. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/__init__.py +0 -0
  160. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/base.py +0 -0
  161. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/local.py +0 -0
  162. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/openai.py +0 -0
  163. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/connectors/remote.py +0 -0
  164. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/__init__.py +0 -0
  165. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/adk.py +0 -0
  166. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/anthropic.py +0 -0
  167. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/gemini.py +0 -0
  168. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/langchain.py +0 -0
  169. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/llamaindex.py +0 -0
  170. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/integrations/openai.py +0 -0
  171. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/mock.py +0 -0
  172. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/router.py +0 -0
  173. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/scenarios.py +0 -0
  174. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/__init__.py +0 -0
  175. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_connection.py +0 -0
  176. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_integrations.py +0 -0
  177. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_scenarios.py +0 -0
  178. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/tests/test_tools.py +0 -0
  179. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/types.py +0 -0
  180. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/__init__.py +0 -0
  181. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/formats.py +0 -0
  182. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/schema.py +0 -0
  183. {hud_python-0.5.7 → hud_python-0.5.9}/hud/environment/utils/tool_wrappers.py +0 -0
  184. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/__init__.py +0 -0
  185. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/context.py +0 -0
  186. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/display.py +0 -0
  187. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/instrument.py +0 -0
  188. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/manager.py +0 -0
  189. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/parallel.py +0 -0
  190. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/task.py +0 -0
  191. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/__init__.py +0 -0
  192. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_context.py +0 -0
  193. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_eval.py +0 -0
  194. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_manager.py +0 -0
  195. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/tests/test_parallel.py +0 -0
  196. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/types.py +0 -0
  197. {hud_python-0.5.7 → hud_python-0.5.9}/hud/eval/utils.py +0 -0
  198. {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/__init__.py +0 -0
  199. {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/comparator.py +0 -0
  200. {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/tests/__init__.py +0 -0
  201. {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/tests/test_comparator.py +0 -0
  202. {hud_python-0.5.7 → hud_python-0.5.9}/hud/native/tests/test_native_init.py +0 -0
  203. {hud_python-0.5.7 → hud_python-0.5.9}/hud/patches/__init__.py +0 -0
  204. {hud_python-0.5.7 → hud_python-0.5.9}/hud/patches/warnings.py +0 -0
  205. {hud_python-0.5.7 → hud_python-0.5.9}/hud/py.typed +0 -0
  206. {hud_python-0.5.7 → hud_python-0.5.9}/hud/samples/__init__.py +0 -0
  207. {hud_python-0.5.7 → hud_python-0.5.9}/hud/samples/browser.py +0 -0
  208. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/__init__.py +0 -0
  209. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/context.py +0 -0
  210. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/helper/__init__.py +0 -0
  211. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/low_level.py +0 -0
  212. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/router.py +0 -0
  213. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/server.py +0 -0
  214. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/__init__.py +0 -0
  215. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_add_tool.py +0 -0
  216. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_context.py +0 -0
  217. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_mcp_server_handlers.py +0 -0
  218. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_mcp_server_integration.py +0 -0
  219. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_mcp_server_more.py +0 -0
  220. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_run_wrapper.py +0 -0
  221. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_server_extra.py +0 -0
  222. {hud_python-0.5.7 → hud_python-0.5.9}/hud/server/tests/test_sigterm_runner.py +0 -0
  223. {hud_python-0.5.7 → hud_python-0.5.9}/hud/settings.py +0 -0
  224. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/__init__.py +0 -0
  225. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/exceptions.py +0 -0
  226. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/hints.py +0 -0
  227. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/requests.py +0 -0
  228. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/__init__.py +0 -0
  229. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/test_exceptions.py +0 -0
  230. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/test_hints.py +0 -0
  231. {hud_python-0.5.7 → hud_python-0.5.9}/hud/shared/tests/test_requests.py +0 -0
  232. {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/__init__.py +0 -0
  233. {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/exporter.py +0 -0
  234. {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/instrument.py +0 -0
  235. {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/__init__.py +0 -0
  236. {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/test_eval_telemetry.py +0 -0
  237. {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/test_exporter.py +0 -0
  238. {hud_python-0.5.7 → hud_python-0.5.9}/hud/telemetry/tests/test_instrument.py +0 -0
  239. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/__init__.py +0 -0
  240. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/apply_patch.py +0 -0
  241. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/base.py +0 -0
  242. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/bash.py +0 -0
  243. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/__init__.py +0 -0
  244. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/anthropic.py +0 -0
  245. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/gemini.py +0 -0
  246. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/hud.py +0 -0
  247. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/openai.py +0 -0
  248. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/qwen.py +0 -0
  249. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/computer/settings.py +0 -0
  250. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/edit.py +0 -0
  251. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/__init__.py +0 -0
  252. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/base.py +0 -0
  253. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/pyautogui.py +0 -0
  254. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/tests/__init__.py +0 -0
  255. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/tests/test_base_executor.py +0 -0
  256. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
  257. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/executors/xdo.py +0 -0
  258. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/__init__.py +0 -0
  259. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/config.py +0 -0
  260. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/grounded_tool.py +0 -0
  261. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/grounder.py +0 -0
  262. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/tests/__init__.py +0 -0
  263. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
  264. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/jupyter.py +0 -0
  265. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/playwright.py +0 -0
  266. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/response.py +0 -0
  267. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/shell.py +0 -0
  268. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/submit.py +0 -0
  269. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/__init__.py +0 -0
  270. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_agent_tool.py +0 -0
  271. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_apply_patch.py +0 -0
  272. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_base.py +0 -0
  273. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_bash.py +0 -0
  274. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_bash_extended.py +0 -0
  275. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_computer.py +0 -0
  276. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_computer_actions.py +0 -0
  277. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_edit.py +0 -0
  278. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_init.py +0 -0
  279. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_jupyter_tool.py +0 -0
  280. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_playwright_tool.py +0 -0
  281. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_response.py +0 -0
  282. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_shell.py +0 -0
  283. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_submit.py +0 -0
  284. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_tools.py +0 -0
  285. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_tools_init.py +0 -0
  286. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_types.py +0 -0
  287. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/tests/test_utils.py +0 -0
  288. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/types.py +0 -0
  289. {hud_python-0.5.7 → hud_python-0.5.9}/hud/tools/utils.py +0 -0
  290. {hud_python-0.5.7 → hud_python-0.5.9}/hud/types.py +0 -0
  291. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/__init__.py +0 -0
  292. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/env.py +0 -0
  293. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/hud_console.py +0 -0
  294. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/mcp.py +0 -0
  295. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/pretty_errors.py +0 -0
  296. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/strict_schema.py +0 -0
  297. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/telemetry.py +0 -0
  298. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/__init__.py +0 -0
  299. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_init.py +0 -0
  300. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_mcp.py +0 -0
  301. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_pretty_errors.py +0 -0
  302. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_telemetry.py +0 -0
  303. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tests/test_tool_shorthand.py +0 -0
  304. {hud_python-0.5.7 → hud_python-0.5.9}/hud/utils/tool_shorthand.py +0 -0
  305. {hud_python-0.5.7 → hud_python-0.5.9}/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.7
3
+ Version: 0.5.9
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
@@ -757,7 +757,8 @@ def build(
757
757
  hud build environments/text_2048 -e API_KEY=secret
758
758
  hud build . --tag my-env:v1.0 -e VAR1=value1 -e VAR2=value2
759
759
  hud build . --no-cache # Force rebuild
760
- hud build . --remote-cache my-cache-repo # Use ECR remote cache (requires AWS_ACCOUNT_ID and AWS_DEFAULT_REGION)[/not dim]
760
+ hud build . --remote-cache my-cache-repo # Use ECR remote cache (requires AWS_ACCOUNT_ID and AWS_DEFAULT_REGION)
761
+ hud build . --build-arg NODE_ENV=production # Pass Docker build args[/not dim]
761
762
  """ # noqa: E501
762
763
  # Parse directory and extra arguments
763
764
  if params:
@@ -767,8 +768,9 @@ def build(
767
768
  directory = "."
768
769
  extra_args = []
769
770
 
770
- # Parse environment variables from extra args
771
+ # Parse environment variables and build args from extra args
771
772
  env_vars = {}
773
+ build_args = {}
772
774
  i = 0
773
775
  while i < len(extra_args):
774
776
  if extra_args[i] == "-e" and i + 1 < len(extra_args):
@@ -792,10 +794,26 @@ def build(
792
794
  key, value = env_arg.split("=", 1)
793
795
  env_vars[key] = value
794
796
  i += 2
797
+ elif extra_args[i] == "--build-arg" and i + 1 < len(extra_args):
798
+ # Parse --build-arg KEY=VALUE format
799
+ build_arg = extra_args[i + 1]
800
+ if "=" in build_arg:
801
+ key, value = build_arg.split("=", 1)
802
+ build_args[key] = value
803
+ i += 2
804
+ elif extra_args[i].startswith("--build-arg="):
805
+ # Parse --build-arg=KEY=VALUE format
806
+ build_arg = extra_args[i][12:] # Remove --build-arg=
807
+ if "=" in build_arg:
808
+ key, value = build_arg.split("=", 1)
809
+ build_args[key] = value
810
+ i += 1
795
811
  else:
796
812
  i += 1
797
813
 
798
- build_command(directory, tag, no_cache, verbose, env_vars, platform, remote_cache)
814
+ build_command(
815
+ directory, tag, no_cache, verbose, env_vars, platform, remote_cache, build_args or None
816
+ )
799
817
 
800
818
 
801
819
  @app.command()
@@ -651,10 +651,12 @@ def build_environment(
651
651
  env_vars: dict[str, str] | None = None,
652
652
  platform: str | None = None,
653
653
  remote_cache: str | None = None,
654
+ build_args: dict[str, str] | None = None,
654
655
  ) -> None:
655
656
  """Build a HUD environment and generate lock file."""
656
657
  hud_console = HUDConsole()
657
658
  env_vars = env_vars or {}
659
+ build_args = build_args or {}
658
660
  hud_console.header("HUD Environment Build")
659
661
 
660
662
  # Resolve directory
@@ -721,7 +723,7 @@ def build_environment(
721
723
  temp_tag,
722
724
  no_cache,
723
725
  verbose,
724
- build_args=None,
726
+ build_args=build_args or None,
725
727
  platform=platform,
726
728
  remote_cache=remote_cache,
727
729
  ):
@@ -1002,6 +1004,10 @@ def build_environment(
1002
1004
  if image_tag and image_tag not in [version_tag, latest_tag]:
1003
1005
  label_cmd.extend(["-t", image_tag])
1004
1006
 
1007
+ # Add build args to final image build (same as initial build)
1008
+ for key, value in build_args.items():
1009
+ label_cmd.extend(["--build-arg", f"{key}={value}"])
1010
+
1005
1011
  label_cmd.append(str(env_dir))
1006
1012
 
1007
1013
  # Run rebuild using Docker's native output formatting
@@ -1106,6 +1112,9 @@ def build_command(
1106
1112
  env_vars: dict[str, str] | None = None,
1107
1113
  platform: str | None = None,
1108
1114
  remote_cache: str | None = None,
1115
+ build_args: dict[str, str] | None = None,
1109
1116
  ) -> None:
1110
1117
  """Build a HUD environment and generate lock file."""
1111
- build_environment(directory, tag, no_cache, verbose, env_vars, platform, remote_cache)
1118
+ build_environment(
1119
+ directory, tag, no_cache, verbose, env_vars, platform, remote_cache, build_args
1120
+ )
@@ -64,9 +64,14 @@ class MCPUseHUDClient(BaseHUDClient):
64
64
  return
65
65
 
66
66
  # Use configurable timeout for SSE read operations to support long-running tool calls.
67
+ max_request_timeout = 840
67
68
  for server_cfg in mcp_config.values():
68
69
  if "sse_read_timeout" not in server_cfg:
69
- server_cfg["sse_read_timeout"] = settings.client_timeout
70
+ server_cfg["sse_read_timeout"] = (
71
+ min(settings.client_timeout, max_request_timeout)
72
+ if settings.client_timeout > 0
73
+ else max_request_timeout
74
+ )
70
75
 
71
76
  # If a server target matches HUD's MCP host and no auth is provided,
72
77
  # inject the HUD API key as a Bearer token to avoid OAuth browser flow.
@@ -14,6 +14,10 @@ import warnings
14
14
  from pathlib import Path
15
15
  from typing import TYPE_CHECKING, Any, overload
16
16
 
17
+ import httpx
18
+
19
+ from hud.settings import settings
20
+
17
21
  if TYPE_CHECKING:
18
22
  from hud.eval.task import Task
19
23
 
@@ -106,10 +110,6 @@ def _load_from_huggingface(dataset_name: str) -> list[Task]:
106
110
 
107
111
  def _load_raw_from_api(dataset_name: str) -> list[dict[str, Any]]:
108
112
  """Load raw task dicts from HUD API."""
109
- import httpx
110
-
111
- from hud.settings import settings
112
-
113
113
  headers = {}
114
114
  if settings.api_key:
115
115
  headers["Authorization"] = f"Bearer {settings.api_key}"
@@ -271,10 +271,6 @@ def save_tasks(
271
271
  TypeError: If any task is not a v5 Task object (must have 'scenario')
272
272
  ValueError: If API key is not set or save fails
273
273
  """
274
- import httpx
275
-
276
- from hud.settings import settings
277
-
278
274
  if not settings.api_key:
279
275
  raise ValueError("HUD_API_KEY is required to save tasks")
280
276
 
@@ -12,8 +12,8 @@ from hud.datasets.loader import load_tasks
12
12
  class TestLoadTasks:
13
13
  """Tests for load_tasks() function."""
14
14
 
15
- @patch("httpx.Client")
16
- @patch("hud.settings.settings")
15
+ @patch("hud.datasets.loader.httpx.Client")
16
+ @patch("hud.datasets.loader.settings")
17
17
  def test_load_tasks_success(
18
18
  self, mock_settings: MagicMock, mock_client_class: MagicMock
19
19
  ) -> None:
@@ -62,8 +62,8 @@ class TestLoadTasks:
62
62
  params={"all": "true"},
63
63
  )
64
64
 
65
- @patch("httpx.Client")
66
- @patch("hud.settings.settings")
65
+ @patch("hud.datasets.loader.httpx.Client")
66
+ @patch("hud.datasets.loader.settings")
67
67
  def test_load_tasks_single_task(
68
68
  self, mock_settings: MagicMock, mock_client_class: MagicMock
69
69
  ) -> None:
@@ -97,8 +97,8 @@ class TestLoadTasks:
97
97
  assert tasks[0].scenario == "checkout"
98
98
  assert tasks[0].id == "task-1"
99
99
 
100
- @patch("httpx.Client")
101
- @patch("hud.settings.settings")
100
+ @patch("hud.datasets.loader.httpx.Client")
101
+ @patch("hud.datasets.loader.settings")
102
102
  def test_load_tasks_no_api_key(
103
103
  self, mock_settings: MagicMock, mock_client_class: MagicMock
104
104
  ) -> None:
@@ -129,8 +129,8 @@ class TestLoadTasks:
129
129
  params={"all": "true"},
130
130
  )
131
131
 
132
- @patch("httpx.Client")
133
- @patch("hud.settings.settings")
132
+ @patch("hud.datasets.loader.httpx.Client")
133
+ @patch("hud.datasets.loader.settings")
134
134
  def test_load_tasks_http_error(
135
135
  self, mock_settings: MagicMock, mock_client_class: MagicMock
136
136
  ) -> None:
@@ -149,8 +149,8 @@ class TestLoadTasks:
149
149
  with pytest.raises(ValueError, match="Failed to load tasks"):
150
150
  load_tasks("test-org/test-dataset")
151
151
 
152
- @patch("httpx.Client")
153
- @patch("hud.settings.settings")
152
+ @patch("hud.datasets.loader.httpx.Client")
153
+ @patch("hud.datasets.loader.settings")
154
154
  def test_load_tasks_json_error(
155
155
  self, mock_settings: MagicMock, mock_client_class: MagicMock
156
156
  ) -> None:
@@ -171,8 +171,8 @@ class TestLoadTasks:
171
171
  with pytest.raises(ValueError, match="Failed to load tasks"):
172
172
  load_tasks("test-org/test-dataset")
173
173
 
174
- @patch("httpx.Client")
175
- @patch("hud.settings.settings")
174
+ @patch("hud.datasets.loader.httpx.Client")
175
+ @patch("hud.datasets.loader.settings")
176
176
  def test_load_tasks_empty(self, mock_settings: MagicMock, mock_client_class: MagicMock) -> None:
177
177
  """load_tasks() handles empty dataset."""
178
178
  mock_settings.hud_api_url = "https://api.hud.ai"
@@ -192,8 +192,8 @@ class TestLoadTasks:
192
192
 
193
193
  assert len(tasks) == 0
194
194
 
195
- @patch("httpx.Client")
196
- @patch("hud.settings.settings")
195
+ @patch("hud.datasets.loader.httpx.Client")
196
+ @patch("hud.datasets.loader.settings")
197
197
  def test_load_tasks_missing_fields(
198
198
  self, mock_settings: MagicMock, mock_client_class: MagicMock
199
199
  ) -> None:
@@ -120,8 +120,10 @@ class Connector:
120
120
  """
121
121
  from fastmcp.client import Client as FastMCPClient
122
122
 
123
- # Create fresh client from stored transport config
124
- self.client = FastMCPClient(transport=self._transport, auth=self._auth)
123
+ self.client = FastMCPClient(
124
+ transport=self._transport,
125
+ auth=self._auth,
126
+ )
125
127
  await self.client.__aenter__()
126
128
 
127
129
  async def disconnect(self) -> None:
@@ -198,10 +200,21 @@ class Connector:
198
200
 
199
201
  Always fetches fresh data from the server (no caching check).
200
202
  The result is cached for use by router.build_resources() via cached_resources property.
203
+
204
+ Note: resources/list is optional in the MCP spec. If the server doesn't
205
+ implement it, we return an empty list gracefully.
201
206
  """
202
207
  if self.client is None:
203
208
  raise RuntimeError("Not connected - call connect() first")
204
- self._resources_cache = await self.client.list_resources()
209
+ try:
210
+ self._resources_cache = await self.client.list_resources()
211
+ except Exception as e:
212
+ # Handle servers that don't implement resources/list (optional in MCP spec)
213
+ if "Method not found" in str(e):
214
+ logger.debug("Server %s does not support resources/list", self.name)
215
+ self._resources_cache = []
216
+ else:
217
+ raise
205
218
  return self._resources_cache
206
219
 
207
220
  async def list_prompts(self) -> list[mcp_types.Prompt]:
@@ -209,10 +222,21 @@ class Connector:
209
222
 
210
223
  Always fetches fresh data from the server (no caching check).
211
224
  The result is cached for use by router.build_prompts() via cached_prompts property.
225
+
226
+ Note: prompts/list is optional in the MCP spec. If the server doesn't
227
+ implement it, we return an empty list gracefully.
212
228
  """
213
229
  if self.client is None:
214
230
  raise RuntimeError("Not connected - call connect() first")
215
- self._prompts_cache = await self.client.list_prompts()
231
+ try:
232
+ self._prompts_cache = await self.client.list_prompts()
233
+ except Exception as e:
234
+ # Handle servers that don't implement prompts/list (optional in MCP spec)
235
+ if "Method not found" in str(e):
236
+ logger.debug("Server %s does not support prompts/list", self.name)
237
+ self._prompts_cache = []
238
+ else:
239
+ raise
216
240
  return self._prompts_cache
217
241
 
218
242
  async def read_resource(
@@ -50,6 +50,7 @@ class MCPConfigConnectorMixin(BaseConnectorMixin):
50
50
  ```
51
51
  """
52
52
  from hud.environment.connection import ConnectionType
53
+ from hud.settings import settings
53
54
 
54
55
  name = alias or next(iter(config.keys()), "mcp")
55
56
  server_config = next(iter(config.values()), {})
@@ -57,9 +58,20 @@ class MCPConfigConnectorMixin(BaseConnectorMixin):
57
58
  is_local = "command" in server_config or "args" in server_config
58
59
  conn_type = ConnectionType.LOCAL if is_local else ConnectionType.REMOTE
59
60
 
61
+ transport: Any = config
62
+ if not is_local and "url" in server_config:
63
+ max_request_timeout = 840
64
+ server_config.setdefault(
65
+ "sse_read_timeout",
66
+ min(settings.client_timeout, max_request_timeout)
67
+ if settings.client_timeout > 0
68
+ else max_request_timeout,
69
+ )
70
+ transport = _build_transport(server_config)
71
+
60
72
  return self._add_connection(
61
73
  name,
62
- config,
74
+ transport,
63
75
  connection_type=conn_type,
64
76
  prefix=prefix,
65
77
  include=include,
@@ -107,3 +119,19 @@ class MCPConfigConnectorMixin(BaseConnectorMixin):
107
119
  for server_name, server_config in mcp_config.items():
108
120
  self.connect_mcp({server_name: server_config}, alias=server_name, **kwargs)
109
121
  return self
122
+
123
+
124
+ def _build_transport(server_config: dict[str, Any]) -> Any:
125
+ from fastmcp.client.transports import SSETransport, StreamableHttpTransport
126
+ from fastmcp.mcp_config import infer_transport_type_from_url
127
+
128
+ url = server_config["url"]
129
+ transport_type = server_config.get("transport") or infer_transport_type_from_url(url)
130
+ transport_cls = SSETransport if transport_type == "sse" else StreamableHttpTransport
131
+
132
+ return transport_cls(
133
+ url=url,
134
+ headers=server_config.get("headers"),
135
+ auth=server_config.get("auth"),
136
+ sse_read_timeout=server_config.get("sse_read_timeout"),
137
+ )
@@ -189,18 +189,25 @@ class Environment(
189
189
  """Return tools in MCP format (base format).
190
190
 
191
191
  Applies agent-level include/exclude filtering if set.
192
+ Supports fnmatch-style wildcards (e.g., "*setup*", "browser_*").
192
193
  """
194
+ import fnmatch
195
+
193
196
  tools = self._router.tools
194
197
 
195
198
  # Apply agent-level filtering (from v4 allowed_tools/disallowed_tools)
196
199
  if self._agent_include is not None or self._agent_exclude is not None:
197
200
  filtered = []
198
201
  for tool in tools:
199
- # Include filter: None means include all
200
- if self._agent_include is not None and tool.name not in self._agent_include:
202
+ # Include filter: None means include all, check if matches any pattern
203
+ if self._agent_include is not None and not any(
204
+ fnmatch.fnmatch(tool.name, pattern) for pattern in self._agent_include
205
+ ):
201
206
  continue
202
- # Exclude filter
203
- if self._agent_exclude is not None and tool.name in self._agent_exclude:
207
+ # Exclude filter: skip if tool matches any exclude pattern
208
+ if self._agent_exclude is not None and any(
209
+ fnmatch.fnmatch(tool.name, pattern) for pattern in self._agent_exclude
210
+ ):
204
211
  continue
205
212
  filtered.append(tool)
206
213
  return filtered
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
- from unittest.mock import MagicMock, patch
6
+ from unittest.mock import patch
7
7
 
8
8
  from hud.environment.connection import ConnectionType, Connector
9
9
 
@@ -180,39 +180,26 @@ class TestRemoteConnectorMixin:
180
180
  conn = env._connections["example"]
181
181
  assert conn._auth == "Bearer my-token"
182
182
 
183
- @patch("httpx.Client")
184
- def test_connect_hub_fetches_config(self, mock_httpx_cls: MagicMock) -> None:
185
- """connect_hub fetches mcp_config from API."""
183
+ def test_connect_hub_creates_connection(self) -> None:
184
+ """connect_hub creates connection with correct config."""
186
185
  from hud.environment.connectors.remote import RemoteConnectorMixin
187
186
 
188
187
  class TestEnv(RemoteConnectorMixin):
189
188
  def __init__(self) -> None:
190
189
  self._connections: dict[str, Connector] = {}
190
+ self._hub_config: dict[str, Any] | None = None
191
191
 
192
192
  def mount(self, server: Any, *, prefix: str | None = None) -> None:
193
193
  pass
194
194
 
195
- # Mock httpx response
196
- mock_response = MagicMock()
197
- mock_response.json.return_value = {
198
- "mcp_config": {
199
- "browser": {"url": "https://mcp.hud.ai/browser"},
200
- }
201
- }
202
- mock_response.raise_for_status = MagicMock()
203
-
204
- mock_client = MagicMock()
205
- mock_client.get.return_value = mock_response
206
- mock_client.__enter__ = MagicMock(return_value=mock_client)
207
- mock_client.__exit__ = MagicMock(return_value=None)
208
- mock_httpx_cls.return_value = mock_client
209
-
210
195
  env = TestEnv()
211
196
  with patch("hud.settings.settings") as mock_settings:
212
- mock_settings.hud_api_url = "https://api.hud.so"
213
- mock_settings.api_key = "test-key"
197
+ mock_settings.hud_mcp_url = "https://mcp.hud.ai"
198
+ mock_settings.client_timeout = 300 # Used in connect_mcp for sse_read_timeout
214
199
 
215
- env.connect_hub("hud/browser")
200
+ env.connect_hub("browser")
216
201
 
217
- # connect_hub creates a connection named "hud" (the server name)
202
+ # connect_hub creates a connection named "hud" (from mcp_config key)
218
203
  assert "hud" in env._connections
204
+ # Verify hub config is stored for serialization
205
+ assert env._hub_config == {"name": "browser"}
@@ -343,3 +343,251 @@ class TestEnvironmentMCPProtocol:
343
343
  assert hasattr(env, "_env_call_tool")
344
344
  assert callable(env._env_list_tools)
345
345
  assert callable(env._env_call_tool)
346
+
347
+
348
+ class TestEnvironmentToolFiltering:
349
+ """Tests for agent-level tool filtering with wildcard support (v4 backwards compat)."""
350
+
351
+ @pytest.mark.asyncio
352
+ async def test_as_tools_no_filter(self) -> None:
353
+ """as_tools returns all tools when no filter is set."""
354
+ from hud.environment import Environment
355
+
356
+ env = Environment("test")
357
+
358
+ @env.tool()
359
+ def tool_a() -> str:
360
+ """Tool A."""
361
+ return "a"
362
+
363
+ @env.tool()
364
+ def tool_b() -> str:
365
+ """Tool B."""
366
+ return "b"
367
+
368
+ await env._build_routing()
369
+
370
+ tools = env.as_tools()
371
+ tool_names = [t.name for t in tools]
372
+
373
+ assert "tool_a" in tool_names
374
+ assert "tool_b" in tool_names
375
+
376
+ @pytest.mark.asyncio
377
+ async def test_as_tools_exact_include(self) -> None:
378
+ """as_tools filters with exact include list."""
379
+ from hud.environment import Environment
380
+
381
+ env = Environment("test")
382
+
383
+ @env.tool()
384
+ def tool_a() -> str:
385
+ """Tool A."""
386
+ return "a"
387
+
388
+ @env.tool()
389
+ def tool_b() -> str:
390
+ """Tool B."""
391
+ return "b"
392
+
393
+ env._agent_include = ["tool_a"]
394
+ await env._build_routing()
395
+
396
+ tools = env.as_tools()
397
+ tool_names = [t.name for t in tools]
398
+
399
+ assert "tool_a" in tool_names
400
+ assert "tool_b" not in tool_names
401
+
402
+ @pytest.mark.asyncio
403
+ async def test_as_tools_exact_exclude(self) -> None:
404
+ """as_tools filters with exact exclude list."""
405
+ from hud.environment import Environment
406
+
407
+ env = Environment("test")
408
+
409
+ @env.tool()
410
+ def tool_a() -> str:
411
+ """Tool A."""
412
+ return "a"
413
+
414
+ @env.tool()
415
+ def tool_b() -> str:
416
+ """Tool B."""
417
+ return "b"
418
+
419
+ env._agent_exclude = ["tool_a"]
420
+ await env._build_routing()
421
+
422
+ tools = env.as_tools()
423
+ tool_names = [t.name for t in tools]
424
+
425
+ assert "tool_a" not in tool_names
426
+ assert "tool_b" in tool_names
427
+
428
+ @pytest.mark.asyncio
429
+ async def test_as_tools_wildcard_exclude_prefix(self) -> None:
430
+ """as_tools filters with wildcard prefix pattern (e.g., 'setup_*')."""
431
+ from hud.environment import Environment
432
+
433
+ env = Environment("test")
434
+
435
+ @env.tool()
436
+ def setup_database() -> str:
437
+ """Setup tool."""
438
+ return "setup"
439
+
440
+ @env.tool()
441
+ def setup_user() -> str:
442
+ """Another setup tool."""
443
+ return "setup"
444
+
445
+ @env.tool()
446
+ def run_query() -> str:
447
+ """Regular tool."""
448
+ return "query"
449
+
450
+ env._agent_exclude = ["setup_*"]
451
+ await env._build_routing()
452
+
453
+ tools = env.as_tools()
454
+ tool_names = [t.name for t in tools]
455
+
456
+ assert "setup_database" not in tool_names
457
+ assert "setup_user" not in tool_names
458
+ assert "run_query" in tool_names
459
+
460
+ @pytest.mark.asyncio
461
+ async def test_as_tools_wildcard_exclude_contains(self) -> None:
462
+ """as_tools filters with wildcard contains pattern (e.g., '*setup*')."""
463
+ from hud.environment import Environment
464
+
465
+ env = Environment("test")
466
+
467
+ @env.tool()
468
+ def hud_setup() -> str:
469
+ """Contains setup."""
470
+ return "setup"
471
+
472
+ @env.tool()
473
+ def setup_env() -> str:
474
+ """Starts with setup."""
475
+ return "setup"
476
+
477
+ @env.tool()
478
+ def my_setup_tool() -> str:
479
+ """Contains setup in middle."""
480
+ return "setup"
481
+
482
+ @env.tool()
483
+ def run_query() -> str:
484
+ """No setup in name."""
485
+ return "query"
486
+
487
+ env._agent_exclude = ["*setup*"]
488
+ await env._build_routing()
489
+
490
+ tools = env.as_tools()
491
+ tool_names = [t.name for t in tools]
492
+
493
+ assert "hud_setup" not in tool_names
494
+ assert "setup_env" not in tool_names
495
+ assert "my_setup_tool" not in tool_names
496
+ assert "run_query" in tool_names
497
+
498
+ @pytest.mark.asyncio
499
+ async def test_as_tools_multiple_wildcard_patterns(self) -> None:
500
+ """as_tools filters with multiple wildcard patterns."""
501
+ from hud.environment import Environment
502
+
503
+ env = Environment("test")
504
+
505
+ @env.tool()
506
+ def setup_db() -> str:
507
+ """Setup tool."""
508
+ return "setup"
509
+
510
+ @env.tool()
511
+ def evaluate_result() -> str:
512
+ """Evaluate tool."""
513
+ return "evaluate"
514
+
515
+ @env.tool()
516
+ def checkout_branch() -> str:
517
+ """Checkout tool."""
518
+ return "checkout"
519
+
520
+ @env.tool()
521
+ def run_query() -> str:
522
+ """Regular tool."""
523
+ return "query"
524
+
525
+ env._agent_exclude = ["*setup*", "*evaluate*", "checkout_branch"]
526
+ await env._build_routing()
527
+
528
+ tools = env.as_tools()
529
+ tool_names = [t.name for t in tools]
530
+
531
+ assert "setup_db" not in tool_names
532
+ assert "evaluate_result" not in tool_names
533
+ assert "checkout_branch" not in tool_names
534
+ assert "run_query" in tool_names
535
+
536
+ @pytest.mark.asyncio
537
+ async def test_as_tools_wildcard_include_all(self) -> None:
538
+ """as_tools with ['*'] include pattern matches all tools."""
539
+ from hud.environment import Environment
540
+
541
+ env = Environment("test")
542
+
543
+ @env.tool()
544
+ def tool_a() -> str:
545
+ """Tool A."""
546
+ return "a"
547
+
548
+ @env.tool()
549
+ def tool_b() -> str:
550
+ """Tool B."""
551
+ return "b"
552
+
553
+ env._agent_include = ["*"]
554
+ await env._build_routing()
555
+
556
+ tools = env.as_tools()
557
+ tool_names = [t.name for t in tools]
558
+
559
+ assert "tool_a" in tool_names
560
+ assert "tool_b" in tool_names
561
+
562
+ @pytest.mark.asyncio
563
+ async def test_as_tools_include_and_exclude_combined(self) -> None:
564
+ """as_tools applies both include and exclude filters."""
565
+ from hud.environment import Environment
566
+
567
+ env = Environment("test")
568
+
569
+ @env.tool()
570
+ def browser_navigate() -> str:
571
+ """Browser tool."""
572
+ return "nav"
573
+
574
+ @env.tool()
575
+ def browser_setup() -> str:
576
+ """Browser setup - should be excluded."""
577
+ return "setup"
578
+
579
+ @env.tool()
580
+ def file_read() -> str:
581
+ """File tool - not included."""
582
+ return "read"
583
+
584
+ env._agent_include = ["browser_*"]
585
+ env._agent_exclude = ["*setup*"]
586
+ await env._build_routing()
587
+
588
+ tools = env.as_tools()
589
+ tool_names = [t.name for t in tools]
590
+
591
+ assert "browser_navigate" in tool_names
592
+ assert "browser_setup" not in tool_names # Excluded by *setup*
593
+ assert "file_read" not in tool_names # Not included by browser_*