nextmv 1.1.3.dev1__tar.gz → 1.1.3.dev2__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 (281) hide show
  1. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/PKG-INFO +1 -1
  2. nextmv-1.1.3.dev2/nextmv/__about__.py +1 -0
  3. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/mcp/server.py +115 -7
  4. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cli/test_mcp.py +112 -2
  5. nextmv-1.1.3.dev1/nextmv/__about__.py +0 -1
  6. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/.gitignore +0 -0
  7. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/.python-version +0 -0
  8. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/LICENSE +0 -0
  9. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/README.md +0 -0
  10. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/__entrypoint__.py +0 -0
  11. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/__init__.py +0 -0
  12. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/_serialization.py +0 -0
  13. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/base_model.py +0 -0
  14. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/CONTRIBUTING.md +0 -0
  15. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/__init__.py +0 -0
  16. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/__init__.py +0 -0
  17. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/acceptance/__init__.py +0 -0
  18. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/acceptance/create.py +0 -0
  19. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/acceptance/delete.py +0 -0
  20. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/acceptance/get.py +0 -0
  21. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/acceptance/list.py +0 -0
  22. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/acceptance/update.py +0 -0
  23. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/account/__init__.py +0 -0
  24. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/account/create.py +0 -0
  25. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/account/delete.py +0 -0
  26. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/account/get.py +0 -0
  27. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/account/update.py +0 -0
  28. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/__init__.py +0 -0
  29. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/create.py +0 -0
  30. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/delete.py +0 -0
  31. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/exists.py +0 -0
  32. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/get.py +0 -0
  33. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/list.py +0 -0
  34. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/push.py +0 -0
  35. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/app/update.py +0 -0
  36. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/batch/__init__.py +0 -0
  37. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/batch/create.py +0 -0
  38. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/batch/delete.py +0 -0
  39. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/batch/get.py +0 -0
  40. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/batch/list.py +0 -0
  41. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/batch/metadata.py +0 -0
  42. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/batch/update.py +0 -0
  43. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/data/__init__.py +0 -0
  44. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/data/upload.py +0 -0
  45. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/ensemble/__init__.py +0 -0
  46. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/ensemble/create.py +0 -0
  47. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/ensemble/delete.py +0 -0
  48. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/ensemble/get.py +0 -0
  49. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/ensemble/list.py +0 -0
  50. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/ensemble/update.py +0 -0
  51. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/input_set/__init__.py +0 -0
  52. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/input_set/create.py +0 -0
  53. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/input_set/delete.py +0 -0
  54. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/input_set/get.py +0 -0
  55. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/input_set/list.py +0 -0
  56. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/input_set/update.py +0 -0
  57. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/instance/__init__.py +0 -0
  58. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/instance/create.py +0 -0
  59. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/instance/delete.py +0 -0
  60. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/instance/exists.py +0 -0
  61. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/instance/get.py +0 -0
  62. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/instance/list.py +0 -0
  63. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/instance/update.py +0 -0
  64. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/managed_input/__init__.py +0 -0
  65. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/managed_input/create.py +0 -0
  66. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/managed_input/delete.py +0 -0
  67. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/managed_input/get.py +0 -0
  68. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/managed_input/list.py +0 -0
  69. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/managed_input/update.py +0 -0
  70. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/__init__.py +0 -0
  71. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/app/__init__.py +0 -0
  72. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/app/create.py +0 -0
  73. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/app/get.py +0 -0
  74. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/app/list.py +0 -0
  75. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/app/update.py +0 -0
  76. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/subscription/__init__.py +0 -0
  77. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/subscription/create.py +0 -0
  78. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/subscription/delete.py +0 -0
  79. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/subscription/get.py +0 -0
  80. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/subscription/list.py +0 -0
  81. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/version/__init__.py +0 -0
  82. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/version/create.py +0 -0
  83. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/version/get.py +0 -0
  84. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/version/list.py +0 -0
  85. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/marketplace/version/update.py +0 -0
  86. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/__init__.py +0 -0
  87. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/cancel.py +0 -0
  88. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/create.py +0 -0
  89. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/get.py +0 -0
  90. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/input.py +0 -0
  91. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/list.py +0 -0
  92. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/logs.py +0 -0
  93. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/metadata.py +0 -0
  94. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/run/track.py +0 -0
  95. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/scenario/__init__.py +0 -0
  96. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/scenario/create.py +0 -0
  97. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/scenario/delete.py +0 -0
  98. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/scenario/get.py +0 -0
  99. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/scenario/list.py +0 -0
  100. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/scenario/metadata.py +0 -0
  101. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/scenario/update.py +0 -0
  102. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/secrets/__init__.py +0 -0
  103. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/secrets/create.py +0 -0
  104. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/secrets/delete.py +0 -0
  105. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/secrets/get.py +0 -0
  106. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/secrets/list.py +0 -0
  107. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/secrets/update.py +0 -0
  108. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/__init__.py +0 -0
  109. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/create.py +0 -0
  110. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/delete.py +0 -0
  111. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/get.py +0 -0
  112. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/list.py +0 -0
  113. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/metadata.py +0 -0
  114. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/start.py +0 -0
  115. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/stop.py +0 -0
  116. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/shadow/update.py +0 -0
  117. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/sso/__init__.py +0 -0
  118. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/sso/create.py +0 -0
  119. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/sso/delete.py +0 -0
  120. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/sso/disable.py +0 -0
  121. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/sso/enable.py +0 -0
  122. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/sso/get.py +0 -0
  123. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/sso/update.py +0 -0
  124. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/__init__.py +0 -0
  125. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/create.py +0 -0
  126. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/delete.py +0 -0
  127. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/get.py +0 -0
  128. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/list.py +0 -0
  129. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/metadata.py +0 -0
  130. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/start.py +0 -0
  131. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/stop.py +0 -0
  132. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/switchback/update.py +0 -0
  133. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/upload/__init__.py +0 -0
  134. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/upload/create.py +0 -0
  135. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/version/__init__.py +0 -0
  136. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/version/create.py +0 -0
  137. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/version/delete.py +0 -0
  138. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/version/exists.py +0 -0
  139. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/version/get.py +0 -0
  140. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/version/list.py +0 -0
  141. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/cloud/version/update.py +0 -0
  142. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/community/__init__.py +0 -0
  143. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/community/clone.py +0 -0
  144. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/community/list.py +0 -0
  145. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/configuration/__init__.py +0 -0
  146. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/configuration/config.py +0 -0
  147. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/configuration/create.py +0 -0
  148. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/configuration/delete.py +0 -0
  149. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/configuration/list.py +0 -0
  150. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/confirm.py +0 -0
  151. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/__init__.py +0 -0
  152. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/__init__.py +0 -0
  153. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/delete.py +0 -0
  154. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/get.py +0 -0
  155. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/list.py +0 -0
  156. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/register.py +0 -0
  157. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/registered.py +0 -0
  158. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/sync.py +0 -0
  159. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/app/update.py +0 -0
  160. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/__init__.py +0 -0
  161. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/create.py +0 -0
  162. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/get.py +0 -0
  163. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/input.py +0 -0
  164. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/list.py +0 -0
  165. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/logs.py +0 -0
  166. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/metadata.py +0 -0
  167. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/local/run/visuals.py +0 -0
  168. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/main.py +0 -0
  169. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/mcp/__init__.py +0 -0
  170. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/mcp/serve.py +0 -0
  171. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/message.py +0 -0
  172. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/options.py +0 -0
  173. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cli/version.py +0 -0
  174. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/__init__.py +0 -0
  175. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/acceptance_test.py +0 -0
  176. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/account.py +0 -0
  177. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/__init__.py +0 -0
  178. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_acceptance.py +0 -0
  179. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_batch_scenario.py +0 -0
  180. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_ensemble.py +0 -0
  181. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_input_set.py +0 -0
  182. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_instance.py +0 -0
  183. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_managed_input.py +0 -0
  184. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_run.py +0 -0
  185. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_secrets.py +0 -0
  186. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_shadow.py +0 -0
  187. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_switchback.py +0 -0
  188. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_utils.py +0 -0
  189. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/application/_version.py +0 -0
  190. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/assets.py +0 -0
  191. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/batch_experiment.py +0 -0
  192. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/client.py +0 -0
  193. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/community.py +0 -0
  194. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/ensemble.py +0 -0
  195. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/input_set.py +0 -0
  196. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/instance.py +0 -0
  197. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/integration.py +0 -0
  198. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/marketplace.py +0 -0
  199. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/package.py +0 -0
  200. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/scenario.py +0 -0
  201. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/secrets.py +0 -0
  202. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/shadow.py +0 -0
  203. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/sso.py +0 -0
  204. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/switchback.py +0 -0
  205. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/url.py +0 -0
  206. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/cloud/version.py +0 -0
  207. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/.gitignore +0 -0
  208. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/README.md +0 -0
  209. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/app.yaml +0 -0
  210. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/input.json +0 -0
  211. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/main.py +0 -0
  212. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/requirements.txt +0 -0
  213. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/src/__init__.py +0 -0
  214. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/default_app/src/visuals.py +0 -0
  215. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/deprecated.py +0 -0
  216. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/input.py +0 -0
  217. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/__init__.py +0 -0
  218. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/application.py +0 -0
  219. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/executor.py +0 -0
  220. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/geojson_handler.py +0 -0
  221. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/local.py +0 -0
  222. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/plotly_handler.py +0 -0
  223. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/registry.py +0 -0
  224. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/local/runner.py +0 -0
  225. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/logger.py +0 -0
  226. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/manifest.py +0 -0
  227. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/model.py +0 -0
  228. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/options.py +0 -0
  229. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/output.py +0 -0
  230. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/polling.py +0 -0
  231. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/run.py +0 -0
  232. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/safe.py +0 -0
  233. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/nextmv/status.py +0 -0
  234. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/pyproject.toml +0 -0
  235. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/README.md +0 -0
  236. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/__init__.py +0 -0
  237. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cli/__init__.py +0 -0
  238. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cli/test_configuration.py +0 -0
  239. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cli/test_main.py +0 -0
  240. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cli/test_version.py +0 -0
  241. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cloud/__init__.py +0 -0
  242. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cloud/app.yaml +0 -0
  243. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cloud/test_client.py +0 -0
  244. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cloud/test_instance.py +0 -0
  245. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cloud/test_package.py +0 -0
  246. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/cloud/test_scenario.py +0 -0
  247. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/integration/__init__.py +0 -0
  248. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/integration/cloud/__init__.py +0 -0
  249. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/integration/cloud/test_integration_cloud.py +0 -0
  250. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/integration/cloud/test_integration_marketplace.py +0 -0
  251. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/local/__init__.py +0 -0
  252. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/local/test_application.py +0 -0
  253. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/local/test_executor.py +0 -0
  254. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/local/test_registry.py +0 -0
  255. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/local/test_runner.py +0 -0
  256. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/__init__.py +0 -0
  257. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/options1.py +0 -0
  258. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/options2.py +0 -0
  259. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/options3.py +0 -0
  260. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/options4.py +0 -0
  261. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/options5.py +0 -0
  262. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/options6.py +0 -0
  263. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/scripts/options7.py +0 -0
  264. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_base_model.py +0 -0
  265. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_entrypoint/__init__.py +0 -0
  266. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_entrypoint/test_entrypoint.py +0 -0
  267. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_input.py +0 -0
  268. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_inputs/test_data.csv +0 -0
  269. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_inputs/test_data.json +0 -0
  270. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_inputs/test_data.txt +0 -0
  271. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_logger.py +0 -0
  272. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_manifest.py +0 -0
  273. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_model.py +0 -0
  274. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_options.py +0 -0
  275. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_output.py +0 -0
  276. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_polling.py +0 -0
  277. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_run.py +0 -0
  278. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_safe.py +0 -0
  279. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_serialization.py +0 -0
  280. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/tests/test_version.py +0 -0
  281. {nextmv-1.1.3.dev1 → nextmv-1.1.3.dev2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nextmv
3
- Version: 1.1.3.dev1
3
+ Version: 1.1.3.dev2
4
4
  Summary: The all-purpose Python SDK for Nextmv
5
5
  Project-URL: Homepage, https://www.nextmv.io
6
6
  Project-URL: Documentation, https://nextmv-py.docs.nextmv.io/en/latest/nextmv/
@@ -0,0 +1 @@
1
+ __version__ = "v1.1.3.dev2"
@@ -17,12 +17,32 @@ from nextmv.cloud import (
17
17
  )
18
18
  from nextmv.polling import default_polling_options
19
19
 
20
+ # Session-level profile. None means "default" (top-level config keys).
21
+ _current_profile: str | None = None
20
22
 
21
- def _get_client() -> Client:
22
- """Build a Cloud client from env var or CLI config."""
23
+
24
+ def _mask_key(key: str | None) -> str | None:
25
+ """Mask an API key, keeping only the last 4 characters visible."""
26
+
27
+ if not key:
28
+ return None
29
+ if len(key) <= 4:
30
+ return key
31
+ return "X" * (len(key) - 4) + key[-4:]
32
+
33
+
34
+ def _get_client(profile: str | None = None) -> Client:
35
+ """Build a Cloud client from env var or CLI config.
36
+
37
+ Args:
38
+ profile: Optional profile override. If not provided, uses the
39
+ session-level ``_current_profile``.
40
+ """
41
+
42
+ resolved = profile or _current_profile
23
43
 
24
44
  api_key = os.getenv("NEXTMV_API_KEY")
25
- if api_key:
45
+ if api_key and not resolved:
26
46
  endpoint = os.getenv("NEXTMV_ENDPOINT", "https://api.cloud.nextmv.io")
27
47
  if not endpoint.startswith("http"):
28
48
  endpoint = f"https://{endpoint}"
@@ -32,7 +52,9 @@ def _get_client() -> Client:
32
52
  try:
33
53
  from nextmv.cli.configuration.config import build_client
34
54
 
35
- return build_client()
55
+ # "default" means use top-level keys (profile=None in build_client).
56
+ p = None if resolved is None or resolved == "default" else resolved
57
+ return build_client(profile=p)
36
58
  except Exception as e:
37
59
  raise ValueError(
38
60
  "No Nextmv API key found. Either set the NEXTMV_API_KEY "
@@ -41,10 +63,10 @@ def _get_client() -> Client:
41
63
  ) from e
42
64
 
43
65
 
44
- def _get_app(app_id: str) -> Application:
66
+ def _get_app(app_id: str, profile: str | None = None) -> Application:
45
67
  """Build a cloud Application handle for the given ID."""
46
68
 
47
- client = _get_client()
69
+ client = _get_client(profile=profile)
48
70
  return Application(client=client, id=app_id)
49
71
 
50
72
 
@@ -69,6 +91,83 @@ def _save_to_file(data: Any, prefix: str) -> str:
69
91
  return f"Data saved to {path} — use file-reading tools to inspect the contents."
70
92
 
71
93
 
94
+ # ════════════════════════════════════════════════════════════════
95
+ # Cloud: Profile management
96
+ # ════════════════════════════════════════════════════════════════
97
+
98
+
99
+ def _register_profile_tools(mcp: FastMCP) -> None:
100
+ """Register profile management tools."""
101
+
102
+ @mcp.tool()
103
+ def cloud_list_profiles() -> list[dict[str, str | None]]:
104
+ """List available Nextmv Cloud profiles from ~/.nextmv/config.yaml.
105
+
106
+ Returns a list of profiles. Each profile has a name, endpoint,
107
+ and a masked API key (last 4 characters visible).
108
+ The "default" profile uses the top-level configuration keys.
109
+ """
110
+
111
+ from nextmv.cli.configuration.config import (
112
+ API_KEY_KEY,
113
+ ENDPOINT_KEY,
114
+ load_config,
115
+ non_profile_keys,
116
+ )
117
+
118
+ config = load_config()
119
+ profiles: list[dict[str, str | None]] = []
120
+
121
+ # Add the default profile (top-level keys).
122
+ if config:
123
+ profiles.append(
124
+ {
125
+ "name": "default",
126
+ "endpoint": config.get(ENDPOINT_KEY),
127
+ "api_key": _mask_key(config.get(API_KEY_KEY)),
128
+ }
129
+ )
130
+
131
+ # Add named profiles.
132
+ reserved = non_profile_keys()
133
+ for key, value in config.items():
134
+ if key in reserved:
135
+ continue
136
+ if isinstance(value, dict):
137
+ profiles.append(
138
+ {
139
+ "name": key,
140
+ "endpoint": value.get(ENDPOINT_KEY),
141
+ "api_key": _mask_key(value.get(API_KEY_KEY)),
142
+ }
143
+ )
144
+
145
+ return profiles
146
+
147
+ @mcp.tool()
148
+ def cloud_set_profile(profile: str) -> str:
149
+ """Set the active Nextmv Cloud profile for this session.
150
+
151
+ All subsequent cloud tool calls will use this profile's
152
+ credentials unless overridden. Use "default" to switch back
153
+ to the default profile.
154
+
155
+ Args:
156
+ profile: Profile name from ~/.nextmv/config.yaml, or
157
+ "default" for the top-level configuration.
158
+ """
159
+
160
+ global _current_profile
161
+ _current_profile = None if profile == "default" else profile
162
+ return f"Active profile set to \"{profile}\"."
163
+
164
+ @mcp.tool()
165
+ def cloud_get_profile() -> str:
166
+ """Get the currently active Nextmv Cloud profile name."""
167
+
168
+ return _current_profile or "default"
169
+
170
+
72
171
  # ════════════════════════════════════════════════════════════════
73
172
  # Cloud: App management
74
173
  # ════════════════════════════════════════════════════════════════
@@ -1790,11 +1889,20 @@ def create_server() -> FastMCP:
1790
1889
  "inputs, logs) are automatically saved to local temp files to "
1791
1890
  "keep the context window small — the tool will return the file "
1792
1891
  "path so you can selectively read what you need. Large inputs "
1793
- "can be passed directly as tool parameters without concern."
1892
+ "can be passed directly as tool parameters without concern.\n\n"
1893
+ "PROFILES: The server supports multiple profiles from "
1894
+ "~/.nextmv/config.yaml. Use cloud_list_profiles to see "
1895
+ "available profiles and cloud_set_profile to switch. The "
1896
+ "default profile is used unless changed. When the user asks "
1897
+ "to use a specific profile (e.g. \"list apps in my dev "
1898
+ "profile\"), call cloud_set_profile first. Always state which "
1899
+ "profile you are using when calling cloud tools, e.g. "
1900
+ "(profile: \"default\")."
1794
1901
  ),
1795
1902
  )
1796
1903
 
1797
1904
  # Cloud tools
1905
+ _register_profile_tools(mcp)
1798
1906
  _register_app_tools(mcp)
1799
1907
  _register_run_tools(mcp)
1800
1908
  _register_version_tools(mcp)
@@ -71,6 +71,15 @@ class TestMCPServerTools(unittest.TestCase):
71
71
  server = create_server()
72
72
  self.assertEqual(server.name, "nextmv")
73
73
 
74
+ def test_server_has_all_profile_tools(self):
75
+ """Test that profile tools are registered."""
76
+ from nextmv.cli.mcp.server import create_server
77
+
78
+ server = create_server()
79
+ tool_names = list(server._tool_manager._tools.keys())
80
+ for name in ["cloud_list_profiles", "cloud_set_profile", "cloud_get_profile"]:
81
+ self.assertIn(name, tool_names, f"Missing tool: {name}")
82
+
74
83
  def test_server_has_all_cloud_app_tools(self):
75
84
  """Test that cloud app tools are registered."""
76
85
  from nextmv.cli.mcp.server import create_server
@@ -238,8 +247,8 @@ class TestMCPServerTools(unittest.TestCase):
238
247
 
239
248
  server = create_server()
240
249
  tool_names = list(server._tool_manager._tools.keys())
241
- # Should have 65+ tools covering all CLI commands.
242
- self.assertGreaterEqual(len(tool_names), 65, f"Only {len(tool_names)} tools registered")
250
+ # Should have 68+ tools covering all CLI commands.
251
+ self.assertGreaterEqual(len(tool_names), 68, f"Only {len(tool_names)} tools registered")
243
252
 
244
253
  @patch("nextmv.cli.mcp.server._get_client")
245
254
  def test_cloud_list_apps_calls_sdk(self, mock_get_client):
@@ -510,3 +519,104 @@ class TestSaveToFile(unittest.TestCase):
510
519
  # Second scenario: explicit type + list config.
511
520
  self.assertEqual(scenarios[1].scenario_input.scenario_input_data, "other-set")
512
521
  self.assertEqual(scenarios[1].configuration[0].values, ["10", "30"])
522
+
523
+
524
+ class TestProfiles(unittest.TestCase):
525
+ """Tests for profile management tools."""
526
+
527
+ def test_mask_key(self):
528
+ """Test that _mask_key masks all but the last 4 characters."""
529
+ from nextmv.cli.mcp.server import _mask_key
530
+
531
+ self.assertIsNone(_mask_key(None))
532
+ self.assertEqual(_mask_key("abcd"), "abcd")
533
+ self.assertEqual(_mask_key("abcde12345"), "XXXXXX2345")
534
+
535
+ def test_set_and_get_profile(self):
536
+ """Test that cloud_set_profile and cloud_get_profile work together."""
537
+ import nextmv.cli.mcp.server as srv
538
+ from nextmv.cli.mcp.server import create_server
539
+
540
+ server = create_server()
541
+ set_tool = server._tool_manager._tools["cloud_set_profile"]
542
+ get_tool = server._tool_manager._tools["cloud_get_profile"]
543
+
544
+ # Default profile.
545
+ result = asyncio.run(get_tool.run({}))
546
+ text = json.loads(result[0].text) if hasattr(result[0], "text") else str(result)
547
+ self.assertIn("default", str(text))
548
+
549
+ # Switch to a named profile.
550
+ asyncio.run(set_tool.run({"profile": "staging"}))
551
+ self.assertEqual(srv._current_profile, "staging")
552
+
553
+ result = asyncio.run(get_tool.run({}))
554
+ text = json.loads(result[0].text) if hasattr(result[0], "text") else str(result)
555
+ self.assertIn("staging", str(text))
556
+
557
+ # Switch back to default.
558
+ asyncio.run(set_tool.run({"profile": "default"}))
559
+ self.assertIsNone(srv._current_profile)
560
+
561
+ def test_list_profiles(self):
562
+ """Test that cloud_list_profiles reads config correctly."""
563
+ from nextmv.cli.mcp.server import create_server
564
+
565
+ mock_config = {
566
+ "apikey": "my-secret-key-1234",
567
+ "endpoint": "api.cloud.nextmv.io",
568
+ "staging": {
569
+ "apikey": "stg-key-5678",
570
+ "endpoint": "staging.api.nextmv.io",
571
+ },
572
+ }
573
+
574
+ with patch(
575
+ "nextmv.cli.configuration.config.load_config",
576
+ return_value=mock_config,
577
+ ):
578
+ server = create_server()
579
+ tool = server._tool_manager._tools["cloud_list_profiles"]
580
+ result = asyncio.run(tool.run({}))
581
+ profiles = json.loads(result[0].text) if hasattr(result[0], "text") else result
582
+ self.assertEqual(len(profiles), 2)
583
+ self.assertEqual(profiles[0]["name"], "default")
584
+ self.assertEqual(profiles[0]["endpoint"], "api.cloud.nextmv.io")
585
+ self.assertIn("1234", profiles[0]["api_key"])
586
+ self.assertNotIn("my-secret", profiles[0]["api_key"])
587
+ self.assertEqual(profiles[1]["name"], "staging")
588
+ self.assertIn("5678", profiles[1]["api_key"])
589
+
590
+ @patch.dict("os.environ", {}, clear=True)
591
+ @patch("nextmv.cli.configuration.config.build_client")
592
+ def test_get_client_uses_profile(self, mock_build_client):
593
+ """Test that _get_client passes the profile to build_client."""
594
+ import nextmv.cli.mcp.server as srv
595
+ from nextmv.cli.mcp.server import _get_client
596
+
597
+ mock_build_client.return_value = MagicMock()
598
+
599
+ # Explicit profile override.
600
+ _get_client(profile="staging")
601
+ mock_build_client.assert_called_with(profile="staging")
602
+
603
+ # Session-level profile.
604
+ srv._current_profile = "prod"
605
+ _get_client()
606
+ mock_build_client.assert_called_with(profile="prod")
607
+
608
+ # Reset.
609
+ srv._current_profile = None
610
+
611
+ @patch.dict("os.environ", {"NEXTMV_API_KEY": "env-key"}, clear=False)
612
+ def test_get_client_env_skipped_when_profile_set(self):
613
+ """Test that env var is skipped when a profile is active."""
614
+ import nextmv.cli.mcp.server as srv
615
+ from nextmv.cli.mcp.server import _get_client
616
+
617
+ with patch("nextmv.cli.configuration.config.build_client") as mock_build:
618
+ mock_build.return_value = MagicMock()
619
+ srv._current_profile = "staging"
620
+ _get_client()
621
+ mock_build.assert_called_with(profile="staging")
622
+ srv._current_profile = None
@@ -1 +0,0 @@
1
- __version__ = "v1.1.3.dev1"
File without changes
File without changes
File without changes
File without changes