alibaba-cloud-ops-mcp-server 0.9.14__tar.gz → 0.9.16__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 (44) hide show
  1. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/PKG-INFO +2 -2
  2. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/pyproject.toml +2 -2
  3. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/alibabacloud/utils.py +3 -6
  4. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/server.py +3 -1
  5. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/api_tools.py +2 -5
  6. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/application_management_tools.py +10 -13
  7. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/uv.lock +1 -1
  8. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/.github/workflows/python-ci.yml +0 -0
  9. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/.gitignore +0 -0
  10. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/Dockerfile +0 -0
  11. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/LICENSE +0 -0
  12. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/README.md +0 -0
  13. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/README_mcp_args.md +0 -0
  14. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/README_zh.md +0 -0
  15. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/examples/openapi_mcp_quickstart/server.py +0 -0
  16. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/image/Alibaba-Cloud-Ops-MCP-User-Group-en.png +0 -0
  17. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/image/Alibaba-Cloud-Ops-MCP-User-Group-zh.png +0 -0
  18. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/image/alibaba-cloud.png +0 -0
  19. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/image/qoder.svg +0 -0
  20. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/__init__.py +0 -0
  21. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/__init__.py +0 -0
  22. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/__main__.py +0 -0
  23. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/alibabacloud/__init__.py +0 -0
  24. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/alibabacloud/api_meta_client.py +0 -0
  25. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/alibabacloud/exception.py +0 -0
  26. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/alibabacloud/static/PROMPT_UNDERSTANDING.md +0 -0
  27. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/alibabacloud/static/__init__.py +0 -0
  28. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/config.py +0 -0
  29. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/settings.py +0 -0
  30. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/__init__.py +0 -0
  31. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/cms_tools.py +0 -0
  32. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/common_api_tools.py +0 -0
  33. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/local_tools.py +0 -0
  34. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/oos_tools.py +0 -0
  35. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/alibaba_cloud_ops_mcp_server/tools/oss_tools.py +0 -0
  36. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/alibabacloud/test_api_meta_client.py +0 -0
  37. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/alibabacloud/test_exception.py +0 -0
  38. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/alibabacloud/test_utils.py +0 -0
  39. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/test_init.py +0 -0
  40. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/test_server.py +0 -0
  41. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/tools/test_api_tools.py +0 -0
  42. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/tools/test_cms_tools.py +0 -0
  43. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/tools/test_oos_tools.py +0 -0
  44. {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/tools/test_oss_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alibaba-cloud-ops-mcp-server
3
- Version: 0.9.14
3
+ Version: 0.9.16
4
4
  Summary: A MCP server for Alibaba Cloud
5
5
  Author-email: Zheng Dayu <dayu.zdy@alibaba-inc.com>, Zhao Shuaibo <zhaoshuaibo.zsb@alibaba-inc.com>
6
6
  License-File: LICENSE
@@ -9,7 +9,7 @@ Requires-Dist: alibabacloud-cms20190101>=3.1.4
9
9
  Requires-Dist: alibabacloud-credentials>=1.0.3
10
10
  Requires-Dist: alibabacloud-ecs20140526>=6.1.0
11
11
  Requires-Dist: alibabacloud-oos20190601>=3.5.0
12
- Requires-Dist: alibabacloud-oss-v2>=1.1.0
12
+ Requires-Dist: alibabacloud-oss-v2>=1.2.0
13
13
  Requires-Dist: click>=8.1.8
14
14
  Requires-Dist: fastmcp==2.8.0
15
15
  Requires-Dist: pydantic==2.11.3
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "alibaba-cloud-ops-mcp-server"
3
- version = "0.9.14"
3
+ version = "0.9.16"
4
4
  description = "A MCP server for Alibaba Cloud"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -12,7 +12,7 @@ dependencies = [
12
12
  "alibabacloud-cms20190101>=3.1.4",
13
13
  "alibabacloud-ecs20140526>=6.1.0",
14
14
  "alibabacloud-oos20190601>=3.5.0",
15
- "alibabacloud_oss_v2>=1.1.0",
15
+ "alibabacloud_oss_v2>=1.2.0",
16
16
  "alibabacloud-credentials>=1.0.3",
17
17
  "click>=8.1.8",
18
18
  "fastmcp==2.8.0",
@@ -192,7 +192,7 @@ def find_bucket_by_tag(client: oss.Client, tag_key: str, tag_value: str) -> Opti
192
192
  return buckets[0] if buckets else None
193
193
 
194
194
 
195
- def get_or_create_bucket_for_code_deploy(application_name: str, region_id: str) -> str:
195
+ def get_or_create_bucket_for_code_deploy(application_name: str) -> str:
196
196
  """
197
197
  Obtain or create an OSS bucket for code deployment
198
198
 
@@ -206,17 +206,14 @@ def get_or_create_bucket_for_code_deploy(application_name: str, region_id: str)
206
206
  """
207
207
  tag_key = 'app_management'
208
208
  tag_value = 'code_deploy'
209
- client = create_oss_client(region_id=region_id)
209
+ client = create_oss_client(region_id='cn-hangzhou')
210
210
 
211
211
  found_bucket = find_bucket_by_tag(client, tag_key, tag_value)
212
212
  if found_bucket:
213
213
  logger.info(f"[code_deploy] Found existing bucket by tag: {found_bucket}")
214
214
  return found_bucket
215
215
 
216
- safe_app_name = application_name.lower().replace('_', '-').replace(' ', '-')
217
-
218
- safe_app_name = ''.join(c if c.isalnum() or c == '-' else '' for c in safe_app_name)
219
- bucket_name = f'app-{safe_app_name}-code-deploy-{str(uuid.uuid4())[:8]}'
216
+ bucket_name = f'code-deploy-{str(uuid.uuid4())[:8]}'
220
217
 
221
218
  try:
222
219
  client.put_bucket(oss.PutBucketRequest(
@@ -190,7 +190,9 @@ def main(transport: str, port: int, host: str, services: str, headers_credential
190
190
  for tool in oos_tools.tools:
191
191
  mcp.tool(tool)
192
192
  for tool in application_management_tools.tools:
193
- mcp.tool(tool)
193
+ # Skip ECS_DescribeInstances in normal mode
194
+ if tool.__name__ != 'ECS_DescribeInstances':
195
+ mcp.tool(tool)
194
196
  for tool in cms_tools.tools:
195
197
  mcp.tool(tool)
196
198
  for tool in oss_tools.tools:
@@ -150,19 +150,16 @@ def _tools_api_call(service: str, api: str, parameters: dict, ctx: Context):
150
150
  except UnretryableException as e:
151
151
  last_exception = e
152
152
  error_msg = str(e)
153
- # 检查错误消息是否包含 [Errno 9] Bad file descriptor
154
153
  has_bad_fd = '[Errno 9] Bad file descriptor' in error_msg
155
154
 
156
155
  if has_bad_fd and attempt < max_retries - 1:
157
- wait_time = (attempt + 1) * 1 # 递增等待时间:1秒、2秒
156
+ wait_time = (attempt + 1) * 0.5
158
157
  logger.warning(f'[_tools_api_call] UnretryableException with [Errno 9] Bad file descriptor (attempt {attempt + 1}/{max_retries}), retrying after {wait_time}s: {e}')
159
158
  time.sleep(wait_time)
160
159
  else:
161
- # 如果不是可重试的错误,或者已经重试了3次,直接抛出异常
162
160
  logger.error(f'Call API Error: {e}')
163
161
  raise e
164
-
165
- # 如果所有重试都失败了,抛出最后一次的异常
162
+
166
163
  if last_exception:
167
164
  logger.error(f'[_tools_api_call] All retries failed, raising last exception: {last_exception}')
168
165
  raise last_exception
@@ -47,13 +47,9 @@ def OOS_CodeDeploy(
47
47
  name: str = Field(description='name of the application'),
48
48
  deploy_region_id: str = Field(description='Region ID for deployment'),
49
49
  application_group_name: str = Field(description='name of the application group'),
50
- region_id_oss: str = Field(description='OSS region ID'),
51
50
  object_name: str = Field(description='OSS object name'),
52
51
  file_path: str = Field(description='Local file path to upload. If the file is not in '
53
52
  '.code_deploy/release directory, it will be copied there.'),
54
- is_internal_oss: bool = Field(description='Whether to download OSS files through internal network. Note: '
55
- 'If you choose internal network download, you must ensure that '
56
- 'the ECS to be deployed and OSS are in the same region.'),
57
53
  application_start: str = Field(
58
54
  description='Application start command script. IMPORTANT: If the uploaded artifact '
59
55
  'is a tar archive or compressed package (e.g., .tar, .tar.gz, .zip), '
@@ -93,13 +89,11 @@ def OOS_CodeDeploy(
93
89
  - 记录文件路径,留待后续CodeDeploy使用
94
90
 
95
91
  步骤 3:调用此工具进行部署
96
- - 此工具会依次调用:CreateApplication(如果不存在)、CreateApplicationGroup(如果不存在)、
97
- TagResources(可选,如果是已有资源需要打 tag 导入应用分组)、DeployApplicationGroup
98
92
 
99
93
  重要提示:
100
94
  1. 启动脚本(application_start)必须与上传的产物对应。如果产物是压缩包(tar、tar.gz、zip等),
101
95
  需要先解压并进入对应目录后再执行启动命令。
102
- 2. 示例:如果上传的是 app.tar.gz,启动脚本应该类似,一般压缩包就在当前目录下,直接解压即可:
96
+ 2. 示例:如果上传的是 app.tar.gz,启动脚本应该类似,一般压缩包就在当前目录下,直接解压即可,不要盲目cd,使用前务必确认对应文件和路径存在:
103
97
  "tar -xzf app.tar.gz && ./start.sh"
104
98
  或者如果解压后是Java应用:
105
99
  "tar -xzf app.tar.gz && java -jar app.jar"
@@ -157,7 +151,7 @@ def OOS_CodeDeploy(
157
151
  logger.info(f"[code_deploy] Using file directory as project path: {file_path_resolved.parent}")
158
152
 
159
153
  # Check ECS instance ID
160
- if not instance_ids or len(instance_ids) == 0:
154
+ if not instance_ids:
161
155
  ecs_purchase_link = f'https://ecs-buy.aliyun.com/ecs#/custom/prepay/{deploy_region_id}?orderSource=buyWizard-console-list'
162
156
  security_group_link = f'https://ecs.console.aliyun.com/securityGroup?regionId={deploy_region_id}'
163
157
  port_info = f'port {port}' if port else 'application port'
@@ -242,7 +236,8 @@ def OOS_CodeDeploy(
242
236
  file_path = str(release_path_resolved)
243
237
  else:
244
238
  logger.info(f"[code_deploy] File already in release directory: {file_path}")
245
-
239
+ region_id_oss = 'cn-hangzhou'
240
+ is_internal_oss = True if deploy_region_id.lower() == 'cn-hangzhou' else False
246
241
  # Log input parameters
247
242
  logger.info(f"[code_deploy] Input parameters: name={name}, deploy_region_id={deploy_region_id}, "
248
243
  f"application_group_name={application_group_name}, instance_ids={instance_ids}, "
@@ -250,7 +245,7 @@ def OOS_CodeDeploy(
250
245
  f"is_internal_oss={is_internal_oss}, port={port}")
251
246
 
252
247
  # Upload file to OSS
253
- bucket_name = get_or_create_bucket_for_code_deploy(name, region_id_oss)
248
+ bucket_name = get_or_create_bucket_for_code_deploy(name)
254
249
  logger.info(f"[code_deploy] Auto selected/created bucket: {bucket_name}")
255
250
 
256
251
  put_object_resp = oss_tools.OSS_PutObject(
@@ -314,6 +309,8 @@ def OOS_CodeDeploy(
314
309
  'security_group_link': security_group_link,
315
310
  'port': port,
316
311
  'deploy_region_id': deploy_region_id,
312
+ 'bucket_name': bucket_name,
313
+ 'oss_bucket_link': f'https://oss.console.aliyun.com/bucket/oss-cn-hangzhou/{bucket_name}/object',
317
314
  'security_group_instructions': f'''
318
315
  ## Deployment Successful!
319
316
 
@@ -551,7 +548,7 @@ def _check_ecs_instances_exist(deploy_region_id: str, instance_ids: list) -> Tup
551
548
  response = _describe_instances_with_retry(deploy_region_id, describe_instances_request)
552
549
 
553
550
  existing_instance_ids = set()
554
- if response.body and response.body.instances:
551
+ if response.body and response.body.instances and response.body.instances.instance:
555
552
  for instance in response.body.instances.instance:
556
553
  if instance.instance_id:
557
554
  existing_instance_ids.add(instance.instance_id)
@@ -582,7 +579,7 @@ def _check_instance_has_tag(deploy_region_id: str, instance_id: str, tag_key: st
582
579
  response = _describe_instances_with_retry(deploy_region_id, describe_instances_request)
583
580
  if response.body and response.body.instances and response.body.instances.instance:
584
581
  instance = response.body.instances.instance[0]
585
- if instance.tags and instance.tags.tag:
582
+ if instance.tags and instance.tags.tag is not None:
586
583
  for tag in instance.tags.tag:
587
584
  if tag.tag_key == tag_key and tag.tag_value == tag_value:
588
585
  logger.info(f"[_check_instance_has_tag] Instance {instance_id} already has tag {tag_key}={tag_value}")
@@ -634,7 +631,7 @@ def _ensure_instances_tagged(deploy_region_id: str, name: str, application_group
634
631
 
635
632
  def _tag_multiple_instances(deploy_region_id, name, application_group_name, instance_ids):
636
633
  """
637
- 为多个实例打 tag(已废弃,使用 _ensure_instances_tagged 代替)
634
+ 为多个实例打 tag
638
635
  """
639
636
  remaining_instance_ids = instance_ids[1:]
640
637
  if remaining_instance_ids:
@@ -116,7 +116,7 @@ wheels = [
116
116
 
117
117
  [[package]]
118
118
  name = "alibaba-cloud-ops-mcp-server"
119
- version = "0.9.13"
119
+ version = "0.9.15"
120
120
  source = { editable = "." }
121
121
  dependencies = [
122
122
  { name = "alibabacloud-cms20190101" },