openhands-sdk 1.49.1__tar.gz → 1.49.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/PKG-INFO +2 -2
  2. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/impl/local_conversation.py +6 -23
  3. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/config.py +18 -0
  4. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/utils.py +56 -0
  5. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/__init__.py +2 -0
  6. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/__init__.py +7 -12
  7. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/agent_plugins.py +37 -17
  8. openhands_sdk-1.49.2/openhands/sdk/plugin/format/agent_plugins_mcp.py +371 -0
  9. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/base.py +56 -12
  10. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/claude_code.py +9 -3
  11. openhands_sdk-1.49.2/openhands/sdk/plugin/format/schemas/mcp-1.0.0.schema.json +120 -0
  12. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/loader.py +2 -9
  13. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/skill.py +7 -2
  14. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/utils.py +57 -8
  15. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/load.py +8 -2
  16. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/path.py +14 -0
  17. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/PKG-INFO +2 -2
  18. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/SOURCES.txt +4 -0
  19. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/requires.txt +1 -1
  20. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/pyproject.toml +4 -2
  21. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/__init__.py +0 -0
  22. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/__init__.py +0 -0
  23. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_agent.py +0 -0
  24. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_file_credentials.py +0 -0
  25. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_models.py +0 -0
  26. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/acp_tracing.py +0 -0
  27. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/agent.py +0 -0
  28. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/base.py +0 -0
  29. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/critic_mixin.py +0 -0
  30. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/parallel_executor.py +0 -0
  31. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/response_dispatch.py +0 -0
  32. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/stream_context.py +0 -0
  33. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/agent/utils.py +0 -0
  34. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/banner.py +0 -0
  35. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/__init__.py +0 -0
  36. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/agent_context.py +0 -0
  37. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/__init__.py +0 -0
  38. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/base.py +0 -0
  39. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/llm_summarizing_condenser.py +0 -0
  40. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/no_op_condenser.py +0 -0
  41. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/pipeline_condenser.py +0 -0
  42. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/prompts/summarizing_prompt.j2 +0 -0
  43. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/condenser/utils.py +0 -0
  44. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/memory.py +0 -0
  45. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/__init__.py +0 -0
  46. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/presets.py +0 -0
  47. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/prompt.py +0 -0
  48. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/registry.py +0 -0
  49. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/section.py +0 -0
  50. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/__init__.py +0 -0
  51. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/dynamic.py +0 -0
  52. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/planning.py +0 -0
  53. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/sections/static.py +0 -0
  54. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/templates/ask_agent_template.j2 +0 -0
  55. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/prompts/templates/skill_knowledge_info.j2 +0 -0
  56. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/__init__.py +0 -0
  57. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/manipulation_indices.py +0 -0
  58. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/__init__.py +0 -0
  59. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/base.py +0 -0
  60. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/batch_atomicity.py +0 -0
  61. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/observation_uniqueness.py +0 -0
  62. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/tool_call_matching.py +0 -0
  63. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/properties/tool_loop_atomicity.py +0 -0
  64. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/context/view/view.py +0 -0
  65. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/__init__.py +0 -0
  66. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/base.py +0 -0
  67. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/cancellation.py +0 -0
  68. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/conversation.py +0 -0
  69. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/conversation_stats.py +0 -0
  70. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/event_store.py +0 -0
  71. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/events_list_base.py +0 -0
  72. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/exceptions.py +0 -0
  73. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/fifo_lock.py +0 -0
  74. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/__init__.py +0 -0
  75. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/controller.py +0 -0
  76. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/judge.py +0 -0
  77. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/prompts.py +0 -0
  78. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/goal/runner.py +0 -0
  79. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/impl/__init__.py +0 -0
  80. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/impl/remote_conversation.py +0 -0
  81. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/persistence_const.py +0 -0
  82. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/request.py +0 -0
  83. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/resource_lock_manager.py +0 -0
  84. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/response_utils.py +0 -0
  85. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/secret_registry.py +0 -0
  86. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/state.py +0 -0
  87. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/stuck_detector.py +0 -0
  88. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/title_utils.py +0 -0
  89. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/types.py +0 -0
  90. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/visualizer/__init__.py +0 -0
  91. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/visualizer/base.py +0 -0
  92. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/conversation/visualizer/default.py +0 -0
  93. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/credential.py +0 -0
  94. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/__init__.py +0 -0
  95. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/base.py +0 -0
  96. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/__init__.py +0 -0
  97. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/agent_finished.py +0 -0
  98. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/__init__.py +0 -0
  99. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/chat_template.py +0 -0
  100. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/client.py +0 -0
  101. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/critic.py +0 -0
  102. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/api/taxonomy.py +0 -0
  103. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/empty_patch.py +0 -0
  104. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/impl/pass_critic.py +0 -0
  105. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/critic/result.py +0 -0
  106. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/__init__.py +0 -0
  107. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/acp_tool_call.py +0 -0
  108. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/base.py +0 -0
  109. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/condenser.py +0 -0
  110. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/conversation_error.py +0 -0
  111. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/conversation_state.py +0 -0
  112. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/error_classification.py +0 -0
  113. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/hook_execution.py +0 -0
  114. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_completion_log.py +0 -0
  115. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/__init__.py +0 -0
  116. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/action.py +0 -0
  117. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/message.py +0 -0
  118. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/observation.py +0 -0
  119. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/reasoning_utils.py +0 -0
  120. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/llm_convertible/system.py +0 -0
  121. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/resume_transcript.py +0 -0
  122. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/streaming_delta.py +0 -0
  123. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/token.py +0 -0
  124. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/types.py +0 -0
  125. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/event/user_action.py +0 -0
  126. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/__init__.py +0 -0
  127. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/fetch.py +0 -0
  128. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/__init__.py +0 -0
  129. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/info.py +0 -0
  130. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/interface.py +0 -0
  131. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/manager.py +0 -0
  132. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/metadata.py +0 -0
  133. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/extensions/installation/utils.py +0 -0
  134. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/cached_repo.py +0 -0
  135. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/exceptions.py +0 -0
  136. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/git_changes.py +0 -0
  137. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/git_commits.py +0 -0
  138. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/git_diff.py +0 -0
  139. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/models.py +0 -0
  140. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/git/utils.py +0 -0
  141. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/__init__.py +0 -0
  142. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/config.py +0 -0
  143. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/conversation_hooks.py +0 -0
  144. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/executor.py +0 -0
  145. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/manager.py +0 -0
  146. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/hooks/types.py +0 -0
  147. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/__init__.py +0 -0
  148. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/base.py +0 -0
  149. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/cache.py +0 -0
  150. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/local.py +0 -0
  151. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/io/memory.py +0 -0
  152. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/__init__.py +0 -0
  153. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/auth/__init__.py +0 -0
  154. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/auth/credentials.py +0 -0
  155. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/auth/openai.py +0 -0
  156. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/cleanup_profile.py +0 -0
  157. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/__init__.py +0 -0
  158. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/classifier.py +0 -0
  159. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/mapping.py +0 -0
  160. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/exceptions/types.py +0 -0
  161. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/fallback_strategy.py +0 -0
  162. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm.py +0 -0
  163. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm_profile_store.py +0 -0
  164. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm_registry.py +0 -0
  165. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/llm_response.py +0 -0
  166. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/message.py +0 -0
  167. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/mixins/fn_call_converter.py +0 -0
  168. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/mixins/fn_call_examples.py +0 -0
  169. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/mixins/non_native_fc.py +0 -0
  170. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/__init__.py +0 -0
  171. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/chat_options.py +0 -0
  172. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/common.py +0 -0
  173. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/options/responses_options.py +0 -0
  174. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/provider_connection_store.py +0 -0
  175. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/__init__.py +0 -0
  176. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/base.py +0 -0
  177. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/impl/multimodal.py +0 -0
  178. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/router/impl/random.py +0 -0
  179. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/streaming.py +0 -0
  180. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/image_inline.py +0 -0
  181. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/image_resize.py +0 -0
  182. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/litellm_provider.py +0 -0
  183. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/metrics.py +0 -0
  184. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/model_features.py +0 -0
  185. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/model_info.py +0 -0
  186. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/model_prompt_spec.py +0 -0
  187. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/openhands_provider.py +0 -0
  188. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/providers/__init__.py +0 -0
  189. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/providers/openrouter.py +0 -0
  190. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/responses_serialization.py +0 -0
  191. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/retry_mixin.py +0 -0
  192. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/runtime_metadata.py +0 -0
  193. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/telemetry.py +0 -0
  194. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/unverified_models.py +0 -0
  195. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/verified_models.py +0 -0
  196. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/llm/utils/vertex_preflight.py +0 -0
  197. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/logger/__init__.py +0 -0
  198. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/logger/logger.py +0 -0
  199. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/logger/rolling.py +0 -0
  200. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/__init__.py +0 -0
  201. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/registration.py +0 -0
  202. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/registry.py +0 -0
  203. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/marketplace/types.py +0 -0
  204. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/__init__.py +0 -0
  205. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/client.py +0 -0
  206. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/definition.py +0 -0
  207. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/exceptions.py +0 -0
  208. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/mcp/tool.py +0 -0
  209. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/observability/__init__.py +0 -0
  210. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/observability/laminar.py +0 -0
  211. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/observability/utils.py +0 -0
  212. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/discovery.py +0 -0
  213. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/fetch.py +0 -0
  214. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/format/schemas/plugin-1.0.0.schema.json +0 -0
  215. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/installed.py +0 -0
  216. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/plugin.py +0 -0
  217. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/source.py +0 -0
  218. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/plugin/types.py +0 -0
  219. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/__init__.py +0 -0
  220. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/agent_profile.py +0 -0
  221. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/agent_profile_store.py +0 -0
  222. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/profile_refs.py +0 -0
  223. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/resolver.py +0 -0
  224. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/profiles/seed.py +0 -0
  225. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/py.typed +0 -0
  226. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/secret/__init__.py +0 -0
  227. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/secret/secrets.py +0 -0
  228. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/__init__.py +0 -0
  229. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/_shell_ast.py +0 -0
  230. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/analyzer.py +0 -0
  231. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/confirmation_policy.py +0 -0
  232. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/__init__.py +0 -0
  233. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/pattern.py +0 -0
  234. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/policy_rails.py +0 -0
  235. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/shell_semantics.py +0 -0
  236. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/defense_in_depth/utils.py +0 -0
  237. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/ensemble.py +0 -0
  238. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/grayswan/__init__.py +0 -0
  239. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/grayswan/analyzer.py +0 -0
  240. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/grayswan/utils.py +0 -0
  241. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/llm_analyzer.py +0 -0
  242. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/risk.py +0 -0
  243. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/shell_parser.py +0 -0
  244. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/toolshield_helpers.py +0 -0
  245. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/security/toolshield_llm_analyzer.py +0 -0
  246. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/__init__.py +0 -0
  247. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/acp_install_catalog.py +0 -0
  248. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/acp_providers.py +0 -0
  249. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/api_models.py +0 -0
  250. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/metadata.py +0 -0
  251. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/settings/model.py +0 -0
  252. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/__init__.py +0 -0
  253. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/exceptions.py +0 -0
  254. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/execute.py +0 -0
  255. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/fetch.py +0 -0
  256. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/installed.py +0 -0
  257. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/trigger.py +0 -0
  258. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/skills/types.py +0 -0
  259. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/__init__.py +0 -0
  260. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/registry.py +0 -0
  261. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/subagent/schema.py +0 -0
  262. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/testing/__init__.py +0 -0
  263. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/testing/test_llm.py +0 -0
  264. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/__init__.py +0 -0
  265. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/__init__.py +0 -0
  266. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/finish.py +0 -0
  267. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/invoke_skill.py +0 -0
  268. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/switch_llm.py +0 -0
  269. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/think.py +0 -0
  270. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/builtins/vision_inspect.py +0 -0
  271. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/client_tool.py +0 -0
  272. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/defaults.py +0 -0
  273. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/registry.py +0 -0
  274. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/schema.py +0 -0
  275. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/spec.py +0 -0
  276. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/tool/tool.py +0 -0
  277. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/__init__.py +0 -0
  278. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/async_executor.py +0 -0
  279. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/async_utils.py +0 -0
  280. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/cipher.py +0 -0
  281. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/command.py +0 -0
  282. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/datetime.py +0 -0
  283. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/deprecation.py +0 -0
  284. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/files.py +0 -0
  285. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/github.py +0 -0
  286. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/json.py +0 -0
  287. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/models.py +0 -0
  288. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/paging.py +0 -0
  289. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/pydantic_secrets.py +0 -0
  290. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/redact.py +0 -0
  291. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/truncate.py +0 -0
  292. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/utils/visualize.py +0 -0
  293. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/__init__.py +0 -0
  294. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/base.py +0 -0
  295. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/local.py +0 -0
  296. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/models.py +0 -0
  297. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/__init__.py +0 -0
  298. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/async_remote_workspace.py +0 -0
  299. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/base.py +0 -0
  300. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/remote/remote_workspace_mixin.py +0 -0
  301. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/repo.py +0 -0
  302. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands/sdk/workspace/workspace.py +0 -0
  303. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/dependency_links.txt +0 -0
  304. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/openhands_sdk.egg-info/top_level.txt +0 -0
  305. {openhands_sdk-1.49.1 → openhands_sdk-1.49.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openhands-sdk
3
- Version: 1.49.1
3
+ Version: 1.49.2
4
4
  Summary: OpenHands SDK - Core functionality for building AI agents
5
5
  Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
6
6
  Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
@@ -10,7 +10,7 @@ Requires-Python: >=3.12
10
10
  Requires-Dist: agent-client-protocol<0.11.0,>=0.10.1
11
11
  Requires-Dist: deprecation>=2.1.0
12
12
  Requires-Dist: fakeredis[lua]>=2.32.1
13
- Requires-Dist: fastmcp>=3.2.0
13
+ Requires-Dist: fastmcp<4,>=3.2.0
14
14
  Requires-Dist: filelock>=3.20.1
15
15
  Requires-Dist: httpx[socks]>=0.27.0
16
16
  Requires-Dist: jsonschema>=4.23.0
@@ -63,8 +63,6 @@ from openhands.sdk.marketplace.registry import MarketplaceRegistry
63
63
  from openhands.sdk.mcp.client import MCPClient
64
64
  from openhands.sdk.mcp.config import (
65
65
  MCPServer,
66
- coerce_mcp_config,
67
- dump_mcp_config,
68
66
  enabled_mcp_servers,
69
67
  )
70
68
  from openhands.sdk.mcp.tool import MCPToolDefinition
@@ -100,7 +98,7 @@ from openhands.sdk.skills import (
100
98
  merge_skills_by_name,
101
99
  )
102
100
  from openhands.sdk.skills.utils import (
103
- expand_mcp_variables,
101
+ expand_mcp_servers,
104
102
  expand_variable_references,
105
103
  )
106
104
  from openhands.sdk.subagent import (
@@ -1205,16 +1203,13 @@ class LocalConversation(BaseConversation):
1205
1203
  # - Variables with defaults that don't have secrets fall back to their defaults
1206
1204
  # - This is the ONLY place where defaults are applied (plugin loading preserves
1207
1205
  # placeholders with expand_defaults=False to avoid double-expansion)
1206
+ # Agent Plugins servers are left literal (see expand_mcp_servers).
1208
1207
  if merged_mcp:
1209
1208
  # Pass the registry's lookup method as a callback - secrets are retrieved
1210
1209
  # lazily, one at a time, only when actually referenced in the config
1211
- expanded_mcp = expand_mcp_variables(
1212
- {"mcpServers": dump_mcp_config(merged_mcp)},
1213
- {},
1214
- get_secret=self._state.secret_registry.get_secret_value,
1215
- expand_defaults=True,
1210
+ merged_mcp = expand_mcp_servers(
1211
+ merged_mcp, self._state.secret_registry.get_secret_value
1216
1212
  )
1217
- merged_mcp = coerce_mcp_config(expanded_mcp["mcpServers"])
1218
1213
  logger.debug("Expanded MCP config variables")
1219
1214
 
1220
1215
  # Update agent with merged content only if something changed.
@@ -1447,25 +1442,13 @@ class LocalConversation(BaseConversation):
1447
1442
  get_secret = self._state.secret_registry.get_secret_value
1448
1443
  runtime_plugin_mcp: dict[str, MCPServer] = {}
1449
1444
  if plugin.mcp_config:
1450
- expanded_plugin_mcp = expand_mcp_variables(
1451
- {"mcpServers": dump_mcp_config(plugin.mcp_config)},
1452
- {},
1453
- get_secret=get_secret,
1454
- expand_defaults=True,
1455
- )
1456
- runtime_plugin_mcp = coerce_mcp_config(expanded_plugin_mcp["mcpServers"])
1445
+ runtime_plugin_mcp = expand_mcp_servers(plugin.mcp_config, get_secret)
1457
1446
  merged_context = plugin.add_skills_to(self.agent.agent_context)
1458
1447
  merged_mcp = plugin.add_mcp_config_to(
1459
1448
  dict(self.agent.mcp_config) if self.agent.mcp_config else {}
1460
1449
  )
1461
1450
  if merged_mcp:
1462
- expanded_mcp = expand_mcp_variables(
1463
- {"mcpServers": dump_mcp_config(merged_mcp)},
1464
- {},
1465
- get_secret=get_secret,
1466
- expand_defaults=True,
1467
- )
1468
- merged_mcp = coerce_mcp_config(expanded_mcp["mcpServers"])
1451
+ merged_mcp = expand_mcp_servers(merged_mcp, get_secret)
1469
1452
  runtime_mcp_tools = (
1470
1453
  self._runtime_mcp_tools(
1471
1454
  runtime_plugin_mcp,
@@ -14,6 +14,7 @@ from pydantic import (
14
14
  Field,
15
15
  GetCoreSchemaHandler,
16
16
  GetJsonSchemaHandler,
17
+ PrivateAttr,
17
18
  SecretStr,
18
19
  SerializationInfo,
19
20
  TypeAdapter,
@@ -520,6 +521,9 @@ class MCPServer(_MCPBaseModel):
520
521
  "ACP subprocess."
521
522
  ),
522
523
  )
524
+ # Never a field: data from outside must not be able to exempt a server from
525
+ # expansion. Only code sets it, via ``as_literal()``.
526
+ _literal_values: bool = PrivateAttr(default=False)
523
527
 
524
528
  @field_validator("env", "headers", mode="after")
525
529
  @classmethod
@@ -554,6 +558,20 @@ class MCPServer(_MCPBaseModel):
554
558
  )
555
559
  return self
556
560
 
561
+ @property
562
+ def literal_values(self) -> bool:
563
+ """True if built by :meth:`as_literal`: never ``${VAR}``-expanded.
564
+
565
+ In memory only; input cannot set it and dumps do not write it.
566
+ """
567
+ return self._literal_values
568
+
569
+ def as_literal(self) -> MCPServer:
570
+ """Return a copy exempt from ``${VAR}`` expansion, for package servers."""
571
+ server = self.model_copy()
572
+ server._literal_values = True
573
+ return server
574
+
557
575
  @property
558
576
  def effective_transport(self) -> MCPTransport | None:
559
577
  return self.transport
@@ -6,12 +6,15 @@ import logging
6
6
  from collections.abc import Callable, Mapping, Sequence
7
7
  from typing import Protocol
8
8
 
9
+ import httpx
9
10
  import mcp.types
10
11
  from fastmcp.client.auth import OAuth
11
12
  from fastmcp.client.logging import LogMessage
12
13
  from fastmcp.client.messages import MessageHandler
14
+ from fastmcp.client.transports import StreamableHttpTransport
13
15
  from fastmcp.mcp_config import MCPConfig as FastMCPConfig, RemoteMCPServer
14
16
  from key_value.aio.protocols import AsyncKeyValue
17
+ from mcp.shared._httpx_utils import create_mcp_http_client
15
18
 
16
19
  from openhands.sdk.logger import get_logger
17
20
  from openhands.sdk.mcp.client import MCPClient, ToolsReconciledCallback
@@ -114,6 +117,52 @@ def _oauth_auth_from_authentication_config(
114
117
  )
115
118
 
116
119
 
120
+ class _PackageRemoteMCPServer(RemoteMCPServer):
121
+ """A package-declared remote server whose headers stay on its own origin.
122
+
123
+ Agent Plugins §7.2.1: configured headers must not follow a redirect to a
124
+ different origin. httpx strips only ``Authorization`` there, so this
125
+ server's HTTP client drops every configured header name from any request
126
+ that leaves the configured origin.
127
+ """
128
+
129
+ def to_transport(self): # type: ignore[override]
130
+ transport = super().to_transport()
131
+ if isinstance(transport, StreamableHttpTransport) and self.headers:
132
+ transport.httpx_client_factory = _origin_bound_client_factory(
133
+ self.url, tuple(self.headers)
134
+ )
135
+ return transport
136
+
137
+
138
+ def _origin_bound_client_factory(url: str, header_names: tuple[str, ...]):
139
+ origin = _origin(httpx.URL(url))
140
+
141
+ async def drop_headers_off_origin(request: httpx.Request) -> None:
142
+ # Request hooks run on every redirect hop, after httpx has copied the
143
+ # previous request's headers onto the next one.
144
+ if _origin(request.url) != origin:
145
+ for name in header_names:
146
+ request.headers.pop(name, None)
147
+
148
+ def factory(
149
+ headers: dict[str, str] | None = None,
150
+ timeout: httpx.Timeout | None = None,
151
+ auth: httpx.Auth | None = None,
152
+ **_: object,
153
+ ) -> httpx.AsyncClient:
154
+ client = create_mcp_http_client(headers=headers, timeout=timeout, auth=auth)
155
+ client.event_hooks = {"request": [drop_headers_off_origin], "response": []}
156
+ return client
157
+
158
+ return factory
159
+
160
+
161
+ def _origin(url: httpx.URL) -> tuple[str, str, int | None]:
162
+ default_port = {"http": 80, "https": 443}.get(url.scheme)
163
+ return url.scheme, url.host, url.port or default_port
164
+
165
+
117
166
  def _prepare_mcp_config(
118
167
  mcp_config: dict[str, MCPServer],
119
168
  *,
@@ -123,6 +172,13 @@ def _prepare_mcp_config(
123
172
  """Validate MCP config and apply explicit OpenHands runtime auth metadata."""
124
173
  prepared = FastMCPConfig.model_validate(to_fastmcp_mcp_config(mcp_config))
125
174
 
175
+ for server_name, server_spec in mcp_config.items():
176
+ server = prepared.mcpServers.get(server_name)
177
+ if server_spec.literal_values and isinstance(server, RemoteMCPServer):
178
+ prepared.mcpServers[server_name] = _PackageRemoteMCPServer.model_validate(
179
+ server.model_dump()
180
+ )
181
+
126
182
  for server_name, server_spec in mcp_config.items():
127
183
  auth = server_spec.auth
128
184
  if not isinstance(auth, MCPOAuthAuthCredential):
@@ -27,6 +27,7 @@ from openhands.sdk.plugin.format import (
27
27
  PluginFormat,
28
28
  detect_format,
29
29
  )
30
+ from openhands.sdk.plugin.format.agent_plugins_mcp import get_plugin_data_dir
30
31
  from openhands.sdk.plugin.installed import (
31
32
  InstalledPluginInfo,
32
33
  disable_plugin,
@@ -92,6 +93,7 @@ __all__ = [
92
93
  "load_installed_plugins",
93
94
  "get_installed_plugins_dir",
94
95
  "get_installed_plugin",
96
+ "get_plugin_data_dir",
95
97
  "enable_plugin",
96
98
  "disable_plugin",
97
99
  "update_plugin",
@@ -12,19 +12,16 @@ Module layout:
12
12
  - ``base`` — the abstract :class:`PluginFormat` contract plus the shared
13
13
  discovery logic (skills discovery, final assembly).
14
14
  - ``claude_code`` — the concrete :class:`ClaudeCodePluginFormat` strategy.
15
- - ``agent_plugins`` — the :class:`AgentPluginsFormat` strategy, not registered;
16
- it also owns our ``dev.openhands`` client-extension namespace.
15
+ - ``agent_plugins`` — the :class:`AgentPluginsFormat` strategy, which also owns
16
+ our ``dev.openhands`` client-extension namespace, and ``agent_plugins_mcp``,
17
+ its ``mcp.json`` loader.
17
18
  - this package ``__init__`` — the format registry (``_FORMATS``) and the
18
19
  :func:`detect_format` dispatcher.
19
20
 
20
21
  :func:`detect_format` returns the first format in ``_FORMATS`` whose
21
- :meth:`PluginFormat.detect` returns True. Claude Code is the only *registered*
22
- format today and its ``detect`` accepts any directory, so it is the universal
23
- fallback.
24
-
25
- :class:`AgentPluginsFormat` is held out of ``_FORMATS`` until its ``mcp.json``
26
- loader lands (#4405): registering it earlier would claim any directory with a
27
- root ``plugin.json`` and load it with zero MCP servers.
22
+ :meth:`PluginFormat.detect` returns True. Agent Plugins goes first because it is
23
+ specific — a root ``plugin.json`` — and Claude Code last because its ``detect``
24
+ accepts any directory, making it the universal fallback.
28
25
 
29
26
  How to add a new plugin format
30
27
  ------------------------------
@@ -62,9 +59,7 @@ logger = get_logger(__name__)
62
59
 
63
60
  # Registered formats, in detection-precedence order. Higher-precedence formats
64
61
  # come first; the Claude Code format is last because it accepts any directory.
65
- # AgentPluginsFormat is intentionally absent until its mcp.json loader lands
66
- # (see the module docstring); it belongs ahead of ClaudeCodePluginFormat.
67
- _FORMATS: list[type[PluginFormat]] = [ClaudeCodePluginFormat]
62
+ _FORMATS: list[type[PluginFormat]] = [AgentPluginsFormat, ClaudeCodePluginFormat]
68
63
 
69
64
 
70
65
  def detect_format(plugin_dir: Path) -> PluginFormat:
@@ -8,7 +8,6 @@ See the ``openhands.sdk.plugin.format`` package docstring for the design.
8
8
  """
9
9
 
10
10
  import json
11
- from functools import cache
12
11
  from pathlib import Path
13
12
  from typing import Any, ClassVar, Final
14
13
 
@@ -18,14 +17,20 @@ from jsonschema.exceptions import ValidationError as JSONSchemaValidationError
18
17
  from openhands.sdk.hooks import HookConfig
19
18
  from openhands.sdk.logger import get_logger
20
19
  from openhands.sdk.mcp.config import MCPServer
20
+ from openhands.sdk.plugin.format.agent_plugins_mcp import (
21
+ get_plugin_data_dir,
22
+ load_mcp_servers,
23
+ )
21
24
  from openhands.sdk.plugin.format.base import (
22
25
  PluginFormat,
26
+ _load_schema,
23
27
  _read_command_definitions,
24
28
  _read_hooks_config,
25
29
  )
26
30
  from openhands.sdk.plugin.types import CommandDefinition, PluginManifest
27
31
  from openhands.sdk.subagent.load import load_agents_from_dir
28
32
  from openhands.sdk.subagent.schema import AgentDefinition
33
+ from openhands.sdk.utils.path import resolves_within
29
34
 
30
35
 
31
36
  logger = get_logger(__name__)
@@ -39,7 +44,6 @@ MANIFEST_FILE: Final[str] = "plugin.json"
39
44
  #: components (§8.2).
40
45
  EXTENSION_NAMESPACE: Final[str] = "dev.openhands"
41
46
 
42
- _SCHEMAS_DIR: Final[Path] = Path(__file__).parent / "schemas"
43
47
 
44
48
  #: The only manifest ``$schema`` we support, and its vendored file. Agent
45
49
  #: Plugins also publishes an ``mcp.schema.json``, but that one belongs to the
@@ -51,12 +55,6 @@ MANIFEST_SCHEMA_URL: Final[str] = (
51
55
  _MANIFEST_SCHEMA_FILE: Final[str] = "plugin-1.0.0.schema.json"
52
56
 
53
57
 
54
- @cache
55
- def _load_schema(filename: str) -> dict[str, Any]:
56
- """Read a vendored schema. Cached; never fetched over the network."""
57
- return json.loads((_SCHEMAS_DIR / filename).read_text(encoding="utf-8"))
58
-
59
-
60
58
  class AgentPluginsFormat(PluginFormat):
61
59
  """The Agent Plugins v1.0.0 portable package layout.
62
60
 
@@ -79,13 +77,17 @@ class AgentPluginsFormat(PluginFormat):
79
77
  moving those directories rather than rewriting them. Namespaces other than
80
78
  ours are ignored without validating their contents.
81
79
 
82
- ``mcp.json`` is a follow-up under #4405, so its loader still returns empty.
83
-
84
- Not registered in ``_FORMATS`` yet; see the package docstring.
80
+ Args:
81
+ plugin_data_root: Parent of the per-plugin ``PLUGIN_DATA`` directories.
82
+ Defaults to the user's plugin data directory; injectable for tests
83
+ and for callers that keep plugin state somewhere else.
85
84
  """
86
85
 
87
86
  name: ClassVar[str] = "agent-plugins"
88
87
 
88
+ def __init__(self, *, plugin_data_root: Path | None = None) -> None:
89
+ self._plugin_data_root = plugin_data_root
90
+
89
91
  @classmethod
90
92
  def detect(cls, plugin_dir: Path) -> bool:
91
93
  # Presence is the whole rule: a broken manifest (bad JSON, missing or
@@ -111,6 +113,10 @@ class AgentPluginsFormat(PluginFormat):
111
113
  ValueError: On any fatal manifest violation.
112
114
  """
113
115
  manifest_path = plugin_dir / MANIFEST_FILE
116
+ if not resolves_within(manifest_path, plugin_dir):
117
+ raise ValueError(
118
+ f"Manifest {manifest_path} resolves outside the plugin root"
119
+ )
114
120
  try:
115
121
  # utf-8-sig: tolerate a leading BOM, which RFC 8259 lets us ignore.
116
122
  data = json.loads(manifest_path.read_text(encoding="utf-8-sig"))
@@ -161,21 +167,35 @@ class AgentPluginsFormat(PluginFormat):
161
167
  data | _extension_manifest_fields(data, manifest_path)
162
168
  )
163
169
 
164
- def load_mcp_config(self, plugin_dir: Path) -> dict[str, MCPServer]: # noqa: ARG002
165
- """Not read yet: root ``mcp.json`` is a follow-up."""
166
- return {}
170
+ def load_mcp_config(self, plugin_dir: Path) -> dict[str, MCPServer]:
171
+ """Load MCP servers from the root ``mcp.json`` (no leading dot).
172
+
173
+ Unlike the Claude Code format, expansion is complete here: Agent Plugins
174
+ defines exactly two placeholders and forbids every other kind, so there
175
+ is nothing left to expand once per-conversation secrets arrive.
176
+ """
177
+ plugin_root = plugin_dir.resolve()
178
+ return load_mcp_servers(
179
+ plugin_dir,
180
+ plugin_root=plugin_root,
181
+ plugin_data=get_plugin_data_dir(
182
+ plugin_root, data_root=self._plugin_data_root
183
+ ),
184
+ )
167
185
 
168
186
  def load_hooks(self, plugin_dir: Path) -> HookConfig | None:
169
187
  """Load hooks from ``dev.openhands/hooks/hooks.json``."""
170
- return _read_hooks_config(plugin_dir / EXTENSION_NAMESPACE)
188
+ return _read_hooks_config(plugin_dir / EXTENSION_NAMESPACE, plugin_dir)
171
189
 
172
190
  def load_agents(self, plugin_dir: Path) -> list[AgentDefinition]:
173
191
  """Load agent definitions from ``dev.openhands/agents/``."""
174
- return load_agents_from_dir(plugin_dir / EXTENSION_NAMESPACE / "agents")
192
+ return load_agents_from_dir(
193
+ plugin_dir / EXTENSION_NAMESPACE / "agents", root=plugin_dir
194
+ )
175
195
 
176
196
  def load_commands(self, plugin_dir: Path) -> list[CommandDefinition]:
177
197
  """Load command definitions from ``dev.openhands/commands/``."""
178
- return _read_command_definitions(plugin_dir / EXTENSION_NAMESPACE)
198
+ return _read_command_definitions(plugin_dir / EXTENSION_NAMESPACE, plugin_dir)
179
199
 
180
200
 
181
201
  def _extension_manifest_fields(