xpander-sdk 2.0.403__tar.gz → 2.0.404__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.
- {xpander_sdk-2.0.403/src/xpander_sdk.egg-info → xpander_sdk-2.0.404}/PKG-INFO +1 -1
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/setup.py +1 -1
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/__init__.py +1 -1
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/agno.py +82 -4
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/sub_modules/dynamic_tools.py +109 -14
- xpander_sdk-2.0.404/src/xpander_sdk/modules/tools_repository/sub_modules/mcp_tool_proxy.py +60 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/tools_repository_module.py +18 -2
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404/src/xpander_sdk.egg-info}/PKG-INFO +1 -1
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/SOURCES.txt +1 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/LICENSE +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/README.md +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/pyproject.toml +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/setup.cfg +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/consts/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/consts/api_routes.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/action_ledger.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/compact_retry_result.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/completion_evidence.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/constants.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/context_optimizer.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/encryption.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/error_patterns.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/finalize_mode.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/chunking.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/recent_actions.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/secrets.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/tool_result.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/xml_safety.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/mixins/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/mixins/map_reduce.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/multimodal_error_patterns.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/prompts.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/search.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/workspace_cache.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/module_base.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/state.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/xpander_api_client.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/exceptions/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/exceptions/module_exception.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/action_ledger.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/activity.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/compactization.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/configuration.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/context_status.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/deep_planning.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/events.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/frameworks.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/generic.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/notifications.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/orchestrations.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/shared.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/user.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/agents_module.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/agent.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/agent_list.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/knowledge_bases.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/sub_modules/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/sub_modules/agent.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/utils/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/utils/generic.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/backend_module.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/decorators/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/decorators/on_auth_event.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/events_registry.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/_anthropic_cache.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/_bedrock_cache.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/dispatch.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/extra_headers.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/mcp_connect.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/mcp_oauth.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/tool_call_events.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_boot.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_shutdown.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_task.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_tool.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/events_module.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/deployments.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/events.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/streaming_server.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/generic.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/git_init.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/knowledge_bases_module.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/models/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/models/knowledge_bases.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/sub_modules/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base_document_item.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/utils/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/task.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/tasks_list.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/sub_modules/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/sub_modules/task.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/tasks_module.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/files.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/media.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/model_capabilities.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/decorators/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/decorators/register_tool.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/models/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/models/mcp.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/models/tool_invocation_result.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/sub_modules/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/sub_modules/tool.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/generic.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/local_tools.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/schemas.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/workspace_payload.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/agents/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/cache.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/db/__init__.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/db/engine_registry.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/env.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/event_loop.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/generic.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/tools.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/dependency_links.txt +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/requires.txt +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/top_level.txt +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_action_ledger_dedup.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_agent_instructions_render.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_aget_db_engine_registry.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_anthropic_prompt_cache.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_auto_context_management_toggle.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_bedrock_prompt_cache.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_boot_shutdown_handlers.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_child_startup_perf.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_chunked.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_perf.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_recent_actions.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_status_events.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_unwrap.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_window.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_retrieve_reoffload.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_search.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_window_detection.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_cross_subexec_decrypt.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_engine_registry.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_headroom_l0.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_layer_1_cache_integration.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_connect_probe.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_oauth_events.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_parallel_connect.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_preflight_tail.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_parent_update_deep_planning.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_planning_tools_visibility.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_reasoning_inline_gateway.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_reasoning_plan_task_id.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_skills_instructions.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_spawn_bg.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_tool_call_events.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_weak_model_reasoning.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_workspace_cache.py +0 -0
- {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_workspace_tools_toggle.py +0 -0
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/agno.py
RENAMED
|
@@ -105,6 +105,9 @@ from xpander_sdk.modules.tools_repository.models.mcp import (
|
|
|
105
105
|
from xpander_sdk.modules.tools_repository.models.tool_invocation_result import (
|
|
106
106
|
ToolInvocationResult,
|
|
107
107
|
)
|
|
108
|
+
from xpander_sdk.modules.tools_repository.sub_modules.mcp_tool_proxy import (
|
|
109
|
+
build_mcp_proxies,
|
|
110
|
+
)
|
|
108
111
|
from xpander_sdk.modules.tools_repository.sub_modules.tool import Tool
|
|
109
112
|
from xpander_sdk.modules.tools_repository.utils.schemas import build_model_from_schema
|
|
110
113
|
from xpander_sdk.modules.tools_repository.utils.workspace_payload import (
|
|
@@ -909,6 +912,13 @@ async def build_agent_args(
|
|
|
909
912
|
)
|
|
910
913
|
args["tools"] = resolved_tools
|
|
911
914
|
|
|
915
|
+
# Dynamic mode: MCP toolkits are SDK-owned (kept out of args["tools"] so the LLM
|
|
916
|
+
# never sees them). Surface them here so the worker closes their sessions post-run
|
|
917
|
+
# — the agno-owned close path only iterates args["tools"].
|
|
918
|
+
args["_xpander_hidden_mcp_toolkits"] = list(
|
|
919
|
+
getattr(xpander_agent.tools, "_dynamic_mcp_toolkits", []) or []
|
|
920
|
+
)
|
|
921
|
+
|
|
912
922
|
# Tell the agent about any MCP tools skipped this run (no user / unreachable) so it
|
|
913
923
|
# can explain the missing capability to the user instead of silently lacking it.
|
|
914
924
|
if _skipped_mcp_notes:
|
|
@@ -1000,6 +1010,14 @@ async def build_agent_args(
|
|
|
1000
1010
|
if members and len(members) != 0:
|
|
1001
1011
|
for member in members:
|
|
1002
1012
|
member["model"] = model
|
|
1013
|
+
# A member's SDK-owned MCP toolkits (dynamic mode) must be hoisted to
|
|
1014
|
+
# the top-level args so the worker closes them, and the key removed
|
|
1015
|
+
# before AgnoAgent/AgnoTeam(**member) — their __init__ rejects it.
|
|
1016
|
+
member_hidden = member.pop("_xpander_hidden_mcp_toolkits", None)
|
|
1017
|
+
if member_hidden:
|
|
1018
|
+
args.setdefault("_xpander_hidden_mcp_toolkits", []).extend(
|
|
1019
|
+
member_hidden
|
|
1020
|
+
)
|
|
1003
1021
|
|
|
1004
1022
|
args.update(
|
|
1005
1023
|
{
|
|
@@ -3290,6 +3308,17 @@ async def _resolve_agent_tools(
|
|
|
3290
3308
|
# Skipped-MCP notes surfaced to the agent (so it can tell the user a capability
|
|
3291
3309
|
# is unavailable) — caller passes a list to receive them; else collect locally.
|
|
3292
3310
|
notes = skipped_notes if skipped_notes is not None else []
|
|
3311
|
+
|
|
3312
|
+
# Dynamic mode collapses MCP tools into the same meta-tool disclosure as the
|
|
3313
|
+
# xpander catalog: the SDK connects each server here, hides its tools from the
|
|
3314
|
+
# LLM, and exposes them via xp_search/get/execute. Per-run reset because the
|
|
3315
|
+
# repo can be reused across tasks in a long-lived worker.
|
|
3316
|
+
use_dynamic = bool(getattr(agent, "use_dynamic_tools", False))
|
|
3317
|
+
if use_dynamic:
|
|
3318
|
+
agent.tools._dynamic_mcp_proxies.clear()
|
|
3319
|
+
agent.tools._dynamic_mcp_toolkits.clear()
|
|
3320
|
+
agent.tools._dynamic_inspected.clear()
|
|
3321
|
+
|
|
3293
3322
|
mcp_servers = agent.mcp_servers
|
|
3294
3323
|
|
|
3295
3324
|
# combine task mcps and agent mcps
|
|
@@ -3314,6 +3343,51 @@ async def _resolve_agent_tools(
|
|
|
3314
3343
|
|
|
3315
3344
|
is_xpander_cloud = getenv("IS_XPANDER_CLOUD", "false") == "true"
|
|
3316
3345
|
|
|
3346
|
+
async def _finalize_toolkit(
|
|
3347
|
+
toolkit: MCPTools, server_name: str, server_url: Optional[str] = None
|
|
3348
|
+
) -> Optional[MCPTools]:
|
|
3349
|
+
"""Non-dynamic: return the toolkit for agno to own (today's behavior).
|
|
3350
|
+
Dynamic: connect + enumerate now, register proxies, keep the toolkit out of
|
|
3351
|
+
the agent's tools (hidden from the LLM), and return None. The SDK owns the
|
|
3352
|
+
session; the worker closes it post-run."""
|
|
3353
|
+
if not use_dynamic:
|
|
3354
|
+
return toolkit
|
|
3355
|
+
try:
|
|
3356
|
+
await toolkit.connect()
|
|
3357
|
+
except (Exception, BaseException) as exc:
|
|
3358
|
+
logger.warning(
|
|
3359
|
+
f"[dynamic-mcp] connect failed for '{server_name}': "
|
|
3360
|
+
f"{type(exc).__name__}: {exc}"
|
|
3361
|
+
)
|
|
3362
|
+
try:
|
|
3363
|
+
await toolkit.close()
|
|
3364
|
+
except (Exception, BaseException):
|
|
3365
|
+
pass
|
|
3366
|
+
notes.append(
|
|
3367
|
+
f"{server_name}: temporarily unavailable (could not connect this run)."
|
|
3368
|
+
)
|
|
3369
|
+
return None
|
|
3370
|
+
if not getattr(toolkit, "initialized", False) or not toolkit.functions:
|
|
3371
|
+
try:
|
|
3372
|
+
await toolkit.close()
|
|
3373
|
+
except (Exception, BaseException):
|
|
3374
|
+
pass
|
|
3375
|
+
if not getattr(toolkit, "initialized", False):
|
|
3376
|
+
notes.append(
|
|
3377
|
+
f"{server_name}: temporarily unavailable (could not connect this run)."
|
|
3378
|
+
)
|
|
3379
|
+
return None
|
|
3380
|
+
proxies = build_mcp_proxies(
|
|
3381
|
+
toolkit, server_name=server_name, server_url=server_url
|
|
3382
|
+
)
|
|
3383
|
+
agent.tools._dynamic_mcp_proxies.extend(proxies)
|
|
3384
|
+
agent.tools._dynamic_mcp_toolkits.append(toolkit)
|
|
3385
|
+
logger.info(
|
|
3386
|
+
f"[dynamic-mcp] collapsed {len(proxies)} tools from '{server_name}' "
|
|
3387
|
+
f"behind the dynamic meta-tools"
|
|
3388
|
+
)
|
|
3389
|
+
return None
|
|
3390
|
+
|
|
3317
3391
|
# Build one MCPTools per server. Remote servers each run a preflight probe
|
|
3318
3392
|
# (~100-800ms); gathering them concurrently makes the wall-time the slowest
|
|
3319
3393
|
# probe instead of their sum. Returns None for servers we deliberately skip.
|
|
@@ -3332,7 +3406,7 @@ async def _resolve_agent_tools(
|
|
|
3332
3406
|
return None
|
|
3333
3407
|
|
|
3334
3408
|
command_parts = shlex.split(mcp.command)
|
|
3335
|
-
return (
|
|
3409
|
+
return await _finalize_toolkit(
|
|
3336
3410
|
MCPTools(
|
|
3337
3411
|
transport=transport,
|
|
3338
3412
|
server_params=StdioServerParameters(
|
|
@@ -3343,7 +3417,9 @@ async def _resolve_agent_tools(
|
|
|
3343
3417
|
include_tools=mcp.allowed_tools or None,
|
|
3344
3418
|
timeout_seconds=120,
|
|
3345
3419
|
tool_name_prefix="mcp_tool",
|
|
3346
|
-
)
|
|
3420
|
+
),
|
|
3421
|
+
mcp.name or mcp.command,
|
|
3422
|
+
None,
|
|
3347
3423
|
)
|
|
3348
3424
|
elif mcp.url:
|
|
3349
3425
|
params_cls = (
|
|
@@ -3489,7 +3565,7 @@ async def _resolve_agent_tools(
|
|
|
3489
3565
|
)
|
|
3490
3566
|
return None
|
|
3491
3567
|
|
|
3492
|
-
return (
|
|
3568
|
+
return await _finalize_toolkit(
|
|
3493
3569
|
MCPTools(
|
|
3494
3570
|
transport=transport,
|
|
3495
3571
|
server_params=params_cls(
|
|
@@ -3501,7 +3577,9 @@ async def _resolve_agent_tools(
|
|
|
3501
3577
|
include_tools=mcp.allowed_tools or None,
|
|
3502
3578
|
timeout_seconds=120,
|
|
3503
3579
|
tool_name_prefix="mcp_tool",
|
|
3504
|
-
)
|
|
3580
|
+
),
|
|
3581
|
+
mcp.name or mcp.url,
|
|
3582
|
+
mcp.url,
|
|
3505
3583
|
)
|
|
3506
3584
|
|
|
3507
3585
|
return None
|
|
@@ -24,6 +24,7 @@ hints and concrete examples.
|
|
|
24
24
|
|
|
25
25
|
import json
|
|
26
26
|
import re
|
|
27
|
+
import uuid
|
|
27
28
|
from inspect import Parameter, Signature
|
|
28
29
|
from typing import Any, Callable, Dict, List
|
|
29
30
|
|
|
@@ -68,6 +69,11 @@ META_TOOL_NAMES = (
|
|
|
68
69
|
|
|
69
70
|
def is_always_loaded(tool: Any) -> bool:
|
|
70
71
|
"""Return True if the tool must stay fully loaded (never hidden)."""
|
|
72
|
+
# MCP proxies carry an "mcp_tool_*" id but ARE the thing we hide behind the
|
|
73
|
+
# meta-tools, so they must never count as always-loaded (unlike repo.list
|
|
74
|
+
# tools whose "mcp"/"xp" prefix genuinely means always-loaded).
|
|
75
|
+
if getattr(tool, "is_mcp_proxy", False):
|
|
76
|
+
return False
|
|
71
77
|
tid = tool.id or ""
|
|
72
78
|
tname = tool.name or ""
|
|
73
79
|
return tid.startswith(DYNAMIC_TOOL_PREFIXES) or tname.startswith(
|
|
@@ -134,6 +140,21 @@ def _xml_escape_text(text: str) -> str:
|
|
|
134
140
|
return (text or "").replace("&", "&").replace("<", "<").replace(">", ">")
|
|
135
141
|
|
|
136
142
|
|
|
143
|
+
def _mcp_attrs(tool: Any) -> str:
|
|
144
|
+
"""XML attributes naming an MCP proxy's source server (server + url), so the
|
|
145
|
+
model knows which MCP server a tool comes from. Empty for xpander tools."""
|
|
146
|
+
if not getattr(tool, "is_mcp_proxy", False):
|
|
147
|
+
return ""
|
|
148
|
+
parts = []
|
|
149
|
+
server_name = getattr(tool, "server_name", None)
|
|
150
|
+
server_url = getattr(tool, "server_url", None)
|
|
151
|
+
if server_name:
|
|
152
|
+
parts.append(f' server="{_xml_escape(str(server_name))}"')
|
|
153
|
+
if server_url:
|
|
154
|
+
parts.append(f' url="{_xml_escape(str(server_url))}"')
|
|
155
|
+
return "".join(parts)
|
|
156
|
+
|
|
157
|
+
|
|
137
158
|
def _truncate(text: str, limit: int = TRUNCATED_DESC_LEN) -> str:
|
|
138
159
|
collapsed = " ".join((text or "").split())
|
|
139
160
|
if len(collapsed) <= limit:
|
|
@@ -202,11 +223,16 @@ def render_tool(tool: Any, full_schema: bool = False) -> str:
|
|
|
202
223
|
display name."""
|
|
203
224
|
name = _xml_escape(tool.id)
|
|
204
225
|
desc = _truncate(tool.description or tool.name or tool.id, limit=300)
|
|
205
|
-
parts = [f'<tool name="{name}">', f" <desc>{desc}</desc>"]
|
|
226
|
+
parts = [f'<tool name="{name}"{_mcp_attrs(tool)}>', f" <desc>{desc}</desc>"]
|
|
206
227
|
|
|
207
228
|
if full_schema:
|
|
208
229
|
try:
|
|
209
|
-
|
|
230
|
+
if getattr(tool, "is_mcp_proxy", False):
|
|
231
|
+
# MCP tools carry a raw JSON-schema dict (no pydantic class) and
|
|
232
|
+
# take FLAT args — the example falls out of the raw inputSchema.
|
|
233
|
+
schema_json = tool.raw_json_schema or {"type": "object", "properties": {}}
|
|
234
|
+
else:
|
|
235
|
+
schema_json = cached_tool_json_schema(tool.schema, "serialization")
|
|
210
236
|
# Show the actual xp_execute_tool call shape so the model passes the
|
|
211
237
|
# correct nested envelope (body_params/headers) on the first try.
|
|
212
238
|
example = {"name": tool.id, "arguments": _example_arguments(schema_json)}
|
|
@@ -283,9 +309,10 @@ _EXECUTE_DESC = (
|
|
|
283
309
|
"Run a tool by id with its arguments. "
|
|
284
310
|
"<when>ONLY after xp_get_tool for this exact tool — execution is REFUSED until you have "
|
|
285
311
|
"read its schema, since you cannot know its arguments otherwise.</when> "
|
|
286
|
-
"`arguments` is the tool's own input object —
|
|
287
|
-
'shows (e.g. {"body_params": {...}, "headers": {...}}),
|
|
288
|
-
"
|
|
312
|
+
"`arguments` is the tool's own input object — match the schema exactly. Most tools want the "
|
|
313
|
+
'FULL nested envelope the schema shows (e.g. {"body_params": {...}, "headers": {...}}), NOT '
|
|
314
|
+
"flattened fields; but MCP tools (name starts with `mcp_tool_`) take FLAT args as their schema "
|
|
315
|
+
"shows, with no body_params wrapper. `name` is the tool id. Wrap arguments in a `payload` object."
|
|
289
316
|
)
|
|
290
317
|
|
|
291
318
|
|
|
@@ -310,12 +337,12 @@ def _list_impl(repo: Any, data: Dict[str, Any], inspected: set) -> str:
|
|
|
310
337
|
),
|
|
311
338
|
)
|
|
312
339
|
|
|
313
|
-
tools = hidden_tools(repo.
|
|
340
|
+
tools = hidden_tools(repo.dynamic_catalog)
|
|
314
341
|
total = len(tools)
|
|
315
342
|
page = tools[cursor : cursor + limit]
|
|
316
343
|
|
|
317
344
|
entries = "\n".join(
|
|
318
|
-
f' <tool name="{_xml_escape(t.id)}">{_truncate(t.description or "")}</tool>'
|
|
345
|
+
f' <tool name="{_xml_escape(t.id)}"{_mcp_attrs(t)}>{_truncate(t.description or "")}</tool>'
|
|
319
346
|
for t in page
|
|
320
347
|
)
|
|
321
348
|
next_cursor = cursor + limit
|
|
@@ -345,7 +372,7 @@ def _search_impl(repo: Any, data: Dict[str, Any], inspected: set) -> str:
|
|
|
345
372
|
"</no_matches>"
|
|
346
373
|
)
|
|
347
374
|
|
|
348
|
-
tools = hidden_tools(repo.
|
|
375
|
+
tools = hidden_tools(repo.dynamic_catalog)
|
|
349
376
|
scored = [(score_tool(query, t), t) for t in tools]
|
|
350
377
|
scored = [(s, t) for s, t in scored if s > 0]
|
|
351
378
|
scored.sort(key=lambda x: x[0], reverse=True)
|
|
@@ -411,6 +438,12 @@ async def _execute_impl(repo: Any, data: Dict[str, Any], inspected: set) -> Any:
|
|
|
411
438
|
agent = getattr(state, "agent", None)
|
|
412
439
|
task = getattr(state, "task", None)
|
|
413
440
|
|
|
441
|
+
# MCP proxies dispatch through their live agno session (not the xpander HTTP
|
|
442
|
+
# path), take flat args (no workspace_path/body_params envelope), and the
|
|
443
|
+
# meta-tool path bypasses the agno hook — so report activity ourselves.
|
|
444
|
+
if getattr(tool, "is_mcp_proxy", False):
|
|
445
|
+
return await _execute_mcp_proxy(tool, arguments, task)
|
|
446
|
+
|
|
414
447
|
# Parity with the agno-hook path: resolve workspace_path offloads here too,
|
|
415
448
|
# otherwise schema validation rejects the empty inline body_params.
|
|
416
449
|
if has_workspace_path(arguments):
|
|
@@ -438,6 +471,66 @@ async def _execute_impl(repo: Any, data: Dict[str, Any], inspected: set) -> Any:
|
|
|
438
471
|
)
|
|
439
472
|
|
|
440
473
|
|
|
474
|
+
async def _execute_mcp_proxy(tool: Any, arguments: Dict[str, Any], task: Any) -> Any:
|
|
475
|
+
"""Run an MCP proxy via its live agno session, surfacing the real MCP tool in
|
|
476
|
+
the activity log (the meta-tool path bypasses the agno hook, so nothing else
|
|
477
|
+
reports it). Reporting is best-effort and never blocks the tool result."""
|
|
478
|
+
report_request = report_result = None
|
|
479
|
+
if task is not None and getattr(task, "id", None):
|
|
480
|
+
try:
|
|
481
|
+
from xpander_sdk.modules.backend.utils.tool_call_events import (
|
|
482
|
+
report_tool_call_request,
|
|
483
|
+
report_tool_call_result,
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
report_request, report_result = (
|
|
487
|
+
report_tool_call_request,
|
|
488
|
+
report_tool_call_result,
|
|
489
|
+
)
|
|
490
|
+
except Exception:
|
|
491
|
+
pass
|
|
492
|
+
|
|
493
|
+
request_id = uuid.uuid4().hex
|
|
494
|
+
if report_request:
|
|
495
|
+
try:
|
|
496
|
+
await report_request(
|
|
497
|
+
task=task,
|
|
498
|
+
request_id=request_id,
|
|
499
|
+
operation_id=tool.id,
|
|
500
|
+
tool_name=tool.id,
|
|
501
|
+
payload=arguments,
|
|
502
|
+
)
|
|
503
|
+
except Exception:
|
|
504
|
+
pass
|
|
505
|
+
|
|
506
|
+
is_error = False
|
|
507
|
+
try:
|
|
508
|
+
result = await tool.ainvoke(arguments)
|
|
509
|
+
except Exception as exc:
|
|
510
|
+
is_error = True
|
|
511
|
+
result = f"Error executing MCP tool '{tool.id}': {exc}"
|
|
512
|
+
else:
|
|
513
|
+
# agno reports MCP failures in-band as content prefixed "Error from MCP tool".
|
|
514
|
+
if isinstance(result, str) and result.lstrip().startswith("Error from MCP tool"):
|
|
515
|
+
is_error = True
|
|
516
|
+
|
|
517
|
+
if report_result:
|
|
518
|
+
try:
|
|
519
|
+
await report_result(
|
|
520
|
+
task=task,
|
|
521
|
+
request_id=request_id,
|
|
522
|
+
operation_id=tool.id,
|
|
523
|
+
tool_name=tool.id,
|
|
524
|
+
payload=arguments,
|
|
525
|
+
result=result,
|
|
526
|
+
is_error=is_error,
|
|
527
|
+
)
|
|
528
|
+
except Exception:
|
|
529
|
+
pass
|
|
530
|
+
|
|
531
|
+
return result
|
|
532
|
+
|
|
533
|
+
|
|
441
534
|
# --- Meta-tool callable assembly -------------------------------------------- #
|
|
442
535
|
|
|
443
536
|
|
|
@@ -544,14 +637,14 @@ def build_dynamic_tools_hint(repo: Any) -> str:
|
|
|
544
637
|
"""The <dynamic_tools> system-prompt block: workflow, rules, inline catalog.
|
|
545
638
|
|
|
546
639
|
Returns "" when there are no hidden tools (nothing to disclose)."""
|
|
547
|
-
tools = hidden_tools(repo.
|
|
640
|
+
tools = hidden_tools(repo.dynamic_catalog)
|
|
548
641
|
total = len(tools)
|
|
549
642
|
if total == 0:
|
|
550
643
|
return ""
|
|
551
644
|
|
|
552
645
|
shown = tools[:HINT_CATALOG_CAP]
|
|
553
646
|
catalog = "\n".join(
|
|
554
|
-
f' <tool name="{_xml_escape(t.id)}">{_truncate(t.description or "")}</tool>'
|
|
647
|
+
f' <tool name="{_xml_escape(t.id)}"{_mcp_attrs(t)}>{_truncate(t.description or "")}</tool>'
|
|
555
648
|
for t in shown
|
|
556
649
|
)
|
|
557
650
|
overflow = ""
|
|
@@ -561,7 +654,7 @@ def build_dynamic_tools_hint(repo: Any) -> str:
|
|
|
561
654
|
return f"""
|
|
562
655
|
|
|
563
656
|
<dynamic_tools>
|
|
564
|
-
Your full tool library ({total} tools) is NOT loaded into context. Only xp*/
|
|
657
|
+
Your full tool library ({total} tools) is NOT loaded into context. Only xp*/knowledge tools are directly callable; everything else — including MCP server tools (name starts with `mcp_tool_`) — is reached through the meta-tools below.
|
|
565
658
|
<workflow>
|
|
566
659
|
1. xp_search_tools(query) — find candidates by intent (or xp_list_tools to browse).
|
|
567
660
|
2. xp_get_tool(name) — read the chosen tool's schema + a ready-to-copy execute example. REQUIRED before step 3.
|
|
@@ -570,9 +663,11 @@ Your full tool library ({total} tools) is NOT loaded into context. Only xp*/mcp*
|
|
|
570
663
|
<rules>
|
|
571
664
|
- Search FIRST. Never assume a tool is missing because it isn't loaded — search for it.
|
|
572
665
|
- `name` is the tool id shown in search/list/catalog — use it verbatim for xp_get_tool and xp_execute_tool.
|
|
573
|
-
- ALWAYS xp_get_tool before xp_execute_tool. Never guess a tool's arguments —
|
|
574
|
-
|
|
575
|
-
|
|
666
|
+
- ALWAYS xp_get_tool before xp_execute_tool. Never guess a tool's arguments — match the schema exactly:
|
|
667
|
+
most tools want the FULL nested envelope (e.g. {{"body_params": {{...}}, "headers": {{...}}}}), but MCP
|
|
668
|
+
tools (`mcp_tool_*`) take FLAT args with no body_params wrapper — copy the shape their schema shows.
|
|
669
|
+
- xp*/knowledge tools are already available directly; no search needed for those. MCP tools are NOT
|
|
670
|
+
directly loaded — search/get them like any other hidden tool.
|
|
576
671
|
</rules>
|
|
577
672
|
<catalog>
|
|
578
673
|
{catalog}{overflow}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Adapter that lets an MCP server tool ride the dynamic-tools mechanism.
|
|
2
|
+
|
|
3
|
+
When ``agent.use_dynamic_tools`` is on, MCP servers are connected at build time
|
|
4
|
+
and each of their tools is wrapped as an ``MCPToolProxy`` instead of being handed
|
|
5
|
+
to agno directly. The proxy exposes the same small surface the dynamic meta-tools
|
|
6
|
+
already read off an xpander ``Tool`` (``id``/``name``/``description`` plus a schema)
|
|
7
|
+
so ``xp_list_tools``/``xp_search_tools``/``xp_get_tool``/``xp_execute_tool`` can
|
|
8
|
+
disclose and run MCP tools with no special-casing beyond the raw-schema render.
|
|
9
|
+
|
|
10
|
+
An MCP tool's schema is a raw JSON-schema dict (``Function.parameters``), not a
|
|
11
|
+
pydantic model, and it takes flat kwargs (no ``payload``/``body_params`` envelope);
|
|
12
|
+
both differences are handled where the proxy is consumed.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import Any, Dict, List, Optional
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MCPToolProxy:
|
|
19
|
+
"""Dynamic-catalog entry backed by one live agno MCP ``Function``."""
|
|
20
|
+
|
|
21
|
+
# Marker read by the dynamic-tools code to branch on MCP vs xpander tools.
|
|
22
|
+
is_mcp_proxy: bool = True
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
agno_function: Any,
|
|
27
|
+
server_name: Optional[str] = None,
|
|
28
|
+
server_url: Optional[str] = None,
|
|
29
|
+
):
|
|
30
|
+
# ``Function.name`` is already prefixed ("mcp_tool_<tool>") and unique, so
|
|
31
|
+
# it doubles as the id the model passes to xp_get_tool / xp_execute_tool.
|
|
32
|
+
self.id: str = agno_function.name
|
|
33
|
+
self.name: str = agno_function.name
|
|
34
|
+
self.description: str = agno_function.description or agno_function.name
|
|
35
|
+
# Raw MCP inputSchema; rendered directly (not via a pydantic model class).
|
|
36
|
+
self.raw_json_schema: Dict[str, Any] = agno_function.parameters or {}
|
|
37
|
+
# Source server identity — rendered into the catalog XML so the agent knows
|
|
38
|
+
# which MCP server a tool belongs to (url is None for local/stdio servers).
|
|
39
|
+
self.server_name: Optional[str] = server_name
|
|
40
|
+
self.server_url: Optional[str] = server_url
|
|
41
|
+
self._agno_function = agno_function
|
|
42
|
+
|
|
43
|
+
async def ainvoke(self, arguments: Optional[Dict[str, Any]] = None) -> Any:
|
|
44
|
+
"""Run the MCP tool via the live session; return its text content."""
|
|
45
|
+
result = await self._agno_function.entrypoint(**(arguments or {}))
|
|
46
|
+
# agno wraps output as a ToolResult (content str, optional images); MCP
|
|
47
|
+
# failures come back as content prefixed "Error from MCP tool ...".
|
|
48
|
+
return getattr(result, "content", result)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def build_mcp_proxies(
|
|
52
|
+
mcp_tool: Any,
|
|
53
|
+
server_name: Optional[str] = None,
|
|
54
|
+
server_url: Optional[str] = None,
|
|
55
|
+
) -> List[MCPToolProxy]:
|
|
56
|
+
"""Wrap every tool of a connected agno ``MCPTools`` toolkit as a proxy."""
|
|
57
|
+
return [
|
|
58
|
+
MCPToolProxy(fn, server_name=server_name, server_url=server_url)
|
|
59
|
+
for fn in (mcp_tool.functions or {}).values()
|
|
60
|
+
]
|
|
@@ -68,6 +68,16 @@ class ToolsRepository(XPanderSharedModel):
|
|
|
68
68
|
# re-materialization of the non-cached `functions` property within a run.
|
|
69
69
|
_dynamic_inspected: set = PrivateAttr(default_factory=set)
|
|
70
70
|
|
|
71
|
+
# MCP server tools collapsed into the dynamic catalog for this run (populated
|
|
72
|
+
# by the agno framework layer when use_dynamic_tools is on). The meta-tools
|
|
73
|
+
# list/search/get/execute over these alongside repo.list.
|
|
74
|
+
_dynamic_mcp_proxies: List[Any] = PrivateAttr(default_factory=list)
|
|
75
|
+
|
|
76
|
+
# The live agno MCPTools toolkits backing the proxies above. The SDK owns
|
|
77
|
+
# their session (they are kept out of the agno agent's tools so the LLM never
|
|
78
|
+
# sees them); the worker closes them post-run via args["_xpander_hidden_mcp_toolkits"].
|
|
79
|
+
_dynamic_mcp_toolkits: List[Any] = PrivateAttr(default_factory=list)
|
|
80
|
+
|
|
71
81
|
@classmethod
|
|
72
82
|
def register_tool(cls, tool: Tool):
|
|
73
83
|
"""
|
|
@@ -105,6 +115,12 @@ class ToolsRepository(XPanderSharedModel):
|
|
|
105
115
|
|
|
106
116
|
return tools
|
|
107
117
|
|
|
118
|
+
@property
|
|
119
|
+
def dynamic_catalog(self) -> List[Any]:
|
|
120
|
+
"""All targets the dynamic meta-tools operate over: xpander tools plus any
|
|
121
|
+
MCP proxies collapsed into the catalog for this run."""
|
|
122
|
+
return self.list + self._dynamic_mcp_proxies
|
|
123
|
+
|
|
108
124
|
def get_tool_by_id(self, tool_id: str):
|
|
109
125
|
"""
|
|
110
126
|
Retrieve a tool by its unique identifier.
|
|
@@ -115,7 +131,7 @@ class ToolsRepository(XPanderSharedModel):
|
|
|
115
131
|
Returns:
|
|
116
132
|
Tool: The tool corresponding to the given ID.
|
|
117
133
|
"""
|
|
118
|
-
return next((tool for tool in self.
|
|
134
|
+
return next((tool for tool in self.dynamic_catalog if tool.id == tool_id), None)
|
|
119
135
|
|
|
120
136
|
def get_tool_by_name(self, tool_name: str):
|
|
121
137
|
"""
|
|
@@ -127,7 +143,7 @@ class ToolsRepository(XPanderSharedModel):
|
|
|
127
143
|
Returns:
|
|
128
144
|
Tool: The tool corresponding to the given ID.
|
|
129
145
|
"""
|
|
130
|
-
return next((tool for tool in self.
|
|
146
|
+
return next((tool for tool in self.dynamic_catalog if tool.name == tool_name), None)
|
|
131
147
|
|
|
132
148
|
def should_sync_local_tools(self):
|
|
133
149
|
"""
|
|
@@ -119,6 +119,7 @@ src/xpander_sdk/modules/tools_repository/models/mcp.py
|
|
|
119
119
|
src/xpander_sdk/modules/tools_repository/models/tool_invocation_result.py
|
|
120
120
|
src/xpander_sdk/modules/tools_repository/sub_modules/__init__.py
|
|
121
121
|
src/xpander_sdk/modules/tools_repository/sub_modules/dynamic_tools.py
|
|
122
|
+
src/xpander_sdk/modules/tools_repository/sub_modules/mcp_tool_proxy.py
|
|
122
123
|
src/xpander_sdk/modules/tools_repository/sub_modules/tool.py
|
|
123
124
|
src/xpander_sdk/modules/tools_repository/utils/__init__.py
|
|
124
125
|
src/xpander_sdk/modules/tools_repository/utils/generic.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/__init__.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/action_ledger.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/encryption.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/error_patterns.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/finalize_mode.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/prompts.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/agent_list.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/knowledge_bases.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/sub_modules/__init__.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/sub_modules/agent.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/backend_module.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/decorators/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/events_registry.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/dispatch.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/__init__.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/extra_headers.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/mcp_connect.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/mcp_oauth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/__init__.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_boot.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_shutdown.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_task.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_tool.py
RENAMED
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/__init__.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/deployments.py
RENAMED
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/streaming_server.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/git_init.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/tasks_list.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/sub_modules/__init__.py
RENAMED
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/sub_modules/task.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/models/mcp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|