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.
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/PKG-INFO +2 -2
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/pyproject.toml +2 -2
- {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
- {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
- {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
- {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
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/uv.lock +1 -1
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/.github/workflows/python-ci.yml +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/.gitignore +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/Dockerfile +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/LICENSE +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/README.md +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/README_mcp_args.md +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/README_zh.md +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/examples/openapi_mcp_quickstart/server.py +0 -0
- {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
- {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
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/image/alibaba-cloud.png +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/image/qoder.svg +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/src/__init__.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {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
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/alibabacloud/test_exception.py +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/alibabacloud/test_utils.py +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/test_init.py +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/test_server.py +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/tools/test_api_tools.py +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/tools/test_cms_tools.py +0 -0
- {alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/tools/test_oos_tools.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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=
|
|
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
|
-
|
|
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
|
-
|
|
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) *
|
|
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
|
|
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
|
|
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
|
|
634
|
+
为多个实例打 tag
|
|
638
635
|
"""
|
|
639
636
|
remaining_instance_ids = instance_ids[1:]
|
|
640
637
|
if remaining_instance_ids:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/README_mcp_args.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/image/alibaba-cloud.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/test_init.py
RENAMED
|
File without changes
|
{alibaba_cloud_ops_mcp_server-0.9.14 → alibaba_cloud_ops_mcp_server-0.9.16}/tests/test_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|