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.
Files changed (174) hide show
  1. {xpander_sdk-2.0.403/src/xpander_sdk.egg-info → xpander_sdk-2.0.404}/PKG-INFO +1 -1
  2. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/setup.py +1 -1
  3. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/__init__.py +1 -1
  4. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/agno.py +82 -4
  5. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/sub_modules/dynamic_tools.py +109 -14
  6. xpander_sdk-2.0.404/src/xpander_sdk/modules/tools_repository/sub_modules/mcp_tool_proxy.py +60 -0
  7. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/tools_repository_module.py +18 -2
  8. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404/src/xpander_sdk.egg-info}/PKG-INFO +1 -1
  9. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/SOURCES.txt +1 -0
  10. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/LICENSE +0 -0
  11. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/README.md +0 -0
  12. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/pyproject.toml +0 -0
  13. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/setup.cfg +0 -0
  14. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/consts/__init__.py +0 -0
  15. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/consts/api_routes.py +0 -0
  16. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/__init__.py +0 -0
  17. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/__init__.py +0 -0
  18. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/action_ledger.py +0 -0
  19. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/compact_retry_result.py +0 -0
  20. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/completion_evidence.py +0 -0
  21. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/constants.py +0 -0
  22. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/context_optimizer.py +0 -0
  23. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/encryption.py +0 -0
  24. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/error_patterns.py +0 -0
  25. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/finalize_mode.py +0 -0
  26. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/__init__.py +0 -0
  27. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/chunking.py +0 -0
  28. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/recent_actions.py +0 -0
  29. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/secrets.py +0 -0
  30. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/tool_result.py +0 -0
  31. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/helpers/xml_safety.py +0 -0
  32. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/mixins/__init__.py +0 -0
  33. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/mixins/map_reduce.py +0 -0
  34. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/multimodal_error_patterns.py +0 -0
  35. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/prompts.py +0 -0
  36. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/search.py +0 -0
  37. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/context_optimizer/workspace_cache.py +0 -0
  38. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/module_base.py +0 -0
  39. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/state.py +0 -0
  40. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/core/xpander_api_client.py +0 -0
  41. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/exceptions/__init__.py +0 -0
  42. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/exceptions/module_exception.py +0 -0
  43. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/__init__.py +0 -0
  44. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/action_ledger.py +0 -0
  45. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/activity.py +0 -0
  46. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/compactization.py +0 -0
  47. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/configuration.py +0 -0
  48. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/context_status.py +0 -0
  49. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/deep_planning.py +0 -0
  50. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/events.py +0 -0
  51. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/frameworks.py +0 -0
  52. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/generic.py +0 -0
  53. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/notifications.py +0 -0
  54. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/orchestrations.py +0 -0
  55. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/shared.py +0 -0
  56. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/models/user.py +0 -0
  57. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/__init__.py +0 -0
  58. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/__init__.py +0 -0
  59. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/agents_module.py +0 -0
  60. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/__init__.py +0 -0
  61. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/agent.py +0 -0
  62. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/agent_list.py +0 -0
  63. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/models/knowledge_bases.py +0 -0
  64. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/sub_modules/__init__.py +0 -0
  65. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/sub_modules/agent.py +0 -0
  66. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/utils/__init__.py +0 -0
  67. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/agents/utils/generic.py +0 -0
  68. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/__init__.py +0 -0
  69. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/backend_module.py +0 -0
  70. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/decorators/__init__.py +0 -0
  71. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/decorators/on_auth_event.py +0 -0
  72. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/events_registry.py +0 -0
  73. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/__init__.py +0 -0
  74. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/_anthropic_cache.py +0 -0
  75. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/_bedrock_cache.py +0 -0
  76. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/frameworks/dispatch.py +0 -0
  77. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/__init__.py +0 -0
  78. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/extra_headers.py +0 -0
  79. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/mcp_connect.py +0 -0
  80. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/mcp_oauth.py +0 -0
  81. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/backend/utils/tool_call_events.py +0 -0
  82. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/__init__.py +0 -0
  83. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/__init__.py +0 -0
  84. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_boot.py +0 -0
  85. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_shutdown.py +0 -0
  86. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_task.py +0 -0
  87. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/decorators/on_tool.py +0 -0
  88. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/events_module.py +0 -0
  89. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/__init__.py +0 -0
  90. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/deployments.py +0 -0
  91. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/models/events.py +0 -0
  92. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/streaming_server.py +0 -0
  93. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/__init__.py +0 -0
  94. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/generic.py +0 -0
  95. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/events/utils/git_init.py +0 -0
  96. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/__init__.py +0 -0
  97. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/knowledge_bases_module.py +0 -0
  98. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/models/__init__.py +0 -0
  99. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/models/knowledge_bases.py +0 -0
  100. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/sub_modules/__init__.py +0 -0
  101. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base.py +0 -0
  102. {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
  103. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/knowledge_bases/utils/__init__.py +0 -0
  104. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/__init__.py +0 -0
  105. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/__init__.py +0 -0
  106. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/task.py +0 -0
  107. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/models/tasks_list.py +0 -0
  108. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/sub_modules/__init__.py +0 -0
  109. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/sub_modules/task.py +0 -0
  110. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/tasks_module.py +0 -0
  111. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/__init__.py +0 -0
  112. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/files.py +0 -0
  113. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/media.py +0 -0
  114. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tasks/utils/model_capabilities.py +0 -0
  115. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/__init__.py +0 -0
  116. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/decorators/__init__.py +0 -0
  117. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/decorators/register_tool.py +0 -0
  118. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/models/__init__.py +0 -0
  119. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/models/mcp.py +0 -0
  120. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/models/tool_invocation_result.py +0 -0
  121. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/sub_modules/__init__.py +0 -0
  122. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/sub_modules/tool.py +0 -0
  123. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/__init__.py +0 -0
  124. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/generic.py +0 -0
  125. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/local_tools.py +0 -0
  126. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/schemas.py +0 -0
  127. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/modules/tools_repository/utils/workspace_payload.py +0 -0
  128. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/__init__.py +0 -0
  129. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/agents/__init__.py +0 -0
  130. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/cache.py +0 -0
  131. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/db/__init__.py +0 -0
  132. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/db/engine_registry.py +0 -0
  133. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/env.py +0 -0
  134. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/event_loop.py +0 -0
  135. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/generic.py +0 -0
  136. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk/utils/tools.py +0 -0
  137. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/dependency_links.txt +0 -0
  138. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/requires.txt +0 -0
  139. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/src/xpander_sdk.egg-info/top_level.txt +0 -0
  140. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_action_ledger_dedup.py +0 -0
  141. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_agent_instructions_render.py +0 -0
  142. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_aget_db_engine_registry.py +0 -0
  143. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_anthropic_prompt_cache.py +0 -0
  144. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_auto_context_management_toggle.py +0 -0
  145. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_bedrock_prompt_cache.py +0 -0
  146. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_boot_shutdown_handlers.py +0 -0
  147. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_child_startup_perf.py +0 -0
  148. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_chunked.py +0 -0
  149. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_perf.py +0 -0
  150. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_recent_actions.py +0 -0
  151. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_status_events.py +0 -0
  152. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_unwrap.py +0 -0
  153. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_optimizer_window.py +0 -0
  154. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_retrieve_reoffload.py +0 -0
  155. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_search.py +0 -0
  156. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_context_window_detection.py +0 -0
  157. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_cross_subexec_decrypt.py +0 -0
  158. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_engine_registry.py +0 -0
  159. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_headroom_l0.py +0 -0
  160. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_layer_1_cache_integration.py +0 -0
  161. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_connect_probe.py +0 -0
  162. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_oauth_events.py +0 -0
  163. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_parallel_connect.py +0 -0
  164. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_mcp_preflight_tail.py +0 -0
  165. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_parent_update_deep_planning.py +0 -0
  166. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_planning_tools_visibility.py +0 -0
  167. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_reasoning_inline_gateway.py +0 -0
  168. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_reasoning_plan_task_id.py +0 -0
  169. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_skills_instructions.py +0 -0
  170. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_spawn_bg.py +0 -0
  171. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_tool_call_events.py +0 -0
  172. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_weak_model_reasoning.py +0 -0
  173. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_workspace_cache.py +0 -0
  174. {xpander_sdk-2.0.403 → xpander_sdk-2.0.404}/tests/test_workspace_tools_toggle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xpander-sdk
3
- Version: 2.0.403
3
+ Version: 2.0.404
4
4
  Summary: xpander.ai Backend-as-a-service for AI Agents - SDK
5
5
  Home-page: https://www.xpander.ai
6
6
  Author: xpanderAI
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="xpander-sdk",
8
- version="2.0.403",
8
+ version="2.0.404",
9
9
  author="xpanderAI",
10
10
  author_email="dev@xpander.ai",
11
11
  description="xpander.ai Backend-as-a-service for AI Agents - SDK",
@@ -15,7 +15,7 @@ Main Components:
15
15
  For more information, visit: https://xpander.ai
16
16
  """
17
17
 
18
- __version__ = "2.0.403"
18
+ __version__ = "2.0.404"
19
19
 
20
20
  # Backend-related imports
21
21
  from .modules.backend.backend_module import Backend
@@ -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("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
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
- schema_json = cached_tool_json_schema(tool.schema, "serialization")
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 — pass the FULL nested envelope the schema "
287
- 'shows (e.g. {"body_params": {...}, "headers": {...}}), not flattened fields. '
288
- "`name` is the tool id. Wrap arguments in a `payload` object."
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.list)
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.list)
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.list)
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*/mcp*/knowledge tools are directly callable; everything else is reached through the meta-tools below.
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 — pass the FULL nested
574
- envelope the schema shows (e.g. {{"body_params": {{...}}, "headers": {{...}}}}), not flattened fields.
575
- - xp*/mcp*/knowledge tools are already available directly; no search needed for those.
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.list if tool.id == tool_id), None)
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.list if tool.name == tool_name), None)
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
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xpander-sdk
3
- Version: 2.0.403
3
+ Version: 2.0.404
4
4
  Summary: xpander.ai Backend-as-a-service for AI Agents - SDK
5
5
  Home-page: https://www.xpander.ai
6
6
  Author: xpanderAI
@@ -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