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.
- {hud_python-0.5.30 → hud_python-0.5.32}/PKG-INFO +1 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/claude.py +11 -3
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/analyze.py +18 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/build.py +153 -87
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_analyze_module.py +40 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_build.py +281 -5
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/docker.py +79 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/mcp.py +22 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connection.py +3 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/mcp_config.py +23 -12
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_connection.py +29 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_connectors.py +43 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/context.py +40 -14
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/instrument.py +50 -6
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/bash.py +22 -9
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_bash.py +13 -20
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_bash_extended.py +28 -2
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/anthropic.py +69 -10
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/settings.py +5 -0
- hud_python-0.5.32/hud/tools/computer/tests/test_compression.py +164 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/types.py +2 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_version.py +1 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/version.py +1 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/pyproject.toml +1 -1
- {hud_python-0.5.30 → hud_python-0.5.32}/.gitignore +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/LICENSE +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/README.md +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/examples/README.md +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/__main__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/gateway.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/gemini.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/gemini_cua.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/grounded_openai.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/misc/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/misc/integration_test_agent.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/misc/response_agent.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/openai.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/openai_chat.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/operator.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/resolver.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/conftest.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_base_runtime.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_claude.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_gemini.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_grounded_openai_agent.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_integration_test_agent.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_openai.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_operator.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_resolver.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/tests/test_run_eval.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/agents/types.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/__main__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/cancel.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/harbor.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/tests/conftest.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/convert/tests/test_harbor.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/debug.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/deploy.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/dev.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/eval.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/dev.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/init.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/tasks.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/templates.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/flows/tests/test_dev.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/init.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/link.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/models.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/push.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/rft.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/rft_status.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_analyze.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_analyze_metadata.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_build_failure.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_build_module.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_init.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_main.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_more_wrappers.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_cli_root.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_convert.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_debug.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_debug_directory_mode.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_deploy.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_dev.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_eval.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_eval_bedrock.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_init.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_main_module.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_mcp_server.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_push.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_push_happy.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_push_wrapper.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/tests/test_utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/api.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/args.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/build_display.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/build_logs.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/config.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/context.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/env_check.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/environment.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/git.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/interactive.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/lockfile.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/logging.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/metadata.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/server.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/source_hash.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tasks.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_config.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_docker.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_docker_hints.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_env_check.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_environment.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_git.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_interactive_module.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_logging_utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_metadata.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_source_hash.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/tests/test_tasks.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/validation.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/version_check.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/cli/utils/viewer.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/loader.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/runner.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/tests/test_loader.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/tests/test_utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/datasets/utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/local.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/openai.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/connectors/remote.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/environment.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/adk.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/anthropic.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/gemini.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/langchain.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/llamaindex.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/integrations/openai.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/mock.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/router.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/scenarios.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_environment.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_integrations.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_local_connectors.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_scenarios.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_session_id.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/tests/test_tools.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/types.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/formats.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/schema.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/environment/utils/tool_wrappers.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/display.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/instrument.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/manager.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/parallel.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/task.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_context.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_eval.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_manager.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_parallel.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/tests/test_task.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/types.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/eval/utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/native/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/native/chat.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/patches/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/patches/mcp_patches.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/patches/warnings.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/py.typed +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/context.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/helper/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/low_level.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/router.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/server.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_add_tool.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_context.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_mcp_server_handlers.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_mcp_server_integration.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_mcp_server_more.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_prefix_naming.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_run_wrapper.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_server_extra.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/server/tests/test_sigterm_runner.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/chat.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/chat_service.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/reply_metadata.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/tests/test_chat.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/services/tests/test_chat_service.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/settings.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/exceptions.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/hints.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/requests.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/test_exceptions.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/test_hints.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/shared/tests/test_requests.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/exporter.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/test_eval_telemetry.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/test_exporter.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/telemetry/tests/test_instrument.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/agent.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/apply_patch.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/edit.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/gemini_edit.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/gemini_shell.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/session.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/shell.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_apply_patch.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_bash_integration.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_edit.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_gemini_tools.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/tests/test_shell.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/coding/utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/gemini.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/glm.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/hud.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/openai.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/qwen.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/test_computer.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/test_computer_actions.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/computer/tests/test_glm_computer.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/elicitation.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/pyautogui.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/tests/test_base_executor.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/tests/test_pyautogui_executor.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/executors/xdo.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/gemini.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/glob.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/grep.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/list.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/read.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_glob.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_grep.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_list.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/filesystem/tests/test_read.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/config.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/grounded_tool.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/grounder.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/grounding/tests/test_grounded_tool.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/code_execution.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/google_search.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/tool_search.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/url_context.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/web_fetch.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/hosted/web_search.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/jupyter.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/claude.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/gemini.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/session.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/test_claude.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/test_gemini.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/memory/tests/test_session.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/native_types.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/playwright.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/response.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/submit.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_agent_tool.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_base.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_elicitation.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_init.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_jupyter_tool.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_native_tool_e2e.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_native_types.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_playwright_tool.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_response.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_submit.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_tools.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_tools_init.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_types.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/tests/test_utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/tools/utils.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/types.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/env.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/hud_console.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/mcp.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/pretty_errors.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/strict_schema.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/__init__.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_init.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_pretty_errors.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tests/test_tool_shorthand.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/tool_shorthand.py +0 -0
- {hud_python-0.5.30 → hud_python-0.5.32}/hud/utils/types.py +0 -0
|
@@ -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(
|
|
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(
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
469
|
-
|
|
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
|
-
|
|
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
|
-
|
|
474
|
-
|
|
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
|
-
|
|
477
|
-
|
|
531
|
+
if verbose:
|
|
532
|
+
hud_console.info(f"Container started: {proc.stdout.strip()[:12]}")
|
|
478
533
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
-
|
|
484
|
-
|
|
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
|
-
|
|
544
|
+
from hud.cli.analyze import parse_docker_command
|
|
487
545
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
|
|
497
|
-
|
|
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
|
-
|
|
585
|
+
if isinstance(e, HudException):
|
|
586
|
+
raise
|
|
574
587
|
raise HudException from e
|
|
575
588
|
finally:
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
try:
|
|
589
|
+
if initialized and client is not None:
|
|
590
|
+
with contextlib.suppress(Exception):
|
|
579
591
|
await client.close()
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
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"
|