alibaba-cloud-ops-mcp-server 0.7.8__py3-none-any.whl → 0.8.0__py3-none-any.whl

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.
File without changes
@@ -75,6 +75,10 @@ class ApiMetaClient:
75
75
  # 获取POP平台API META参考文档:https://api.aliyun.com/openmeta/guide
76
76
  version = cls.get_service_version(service)
77
77
  service_standard, api_standard = cls.get_standard_service_and_api(service, api, version)
78
+ if service_standard is None:
79
+ raise Exception(f'InvalidServiceName: Please check the Service ({service}) you provide.')
80
+ if api_standard is None:
81
+ raise Exception(f'InvalidAPIName: Please check the Service ({service}) and the API ({api}) you provide.')
78
82
  data = cls.get_response_from_pop_api(cls.GET_API_INFO, service_standard, api_standard, version)
79
83
  return data, version
80
84
 
@@ -1,176 +1,13 @@
1
- # server.py
2
- import os
3
- from mcp.server.fastmcp import FastMCP, Context
4
- from pydantic import Field
1
+ from mcp.server.fastmcp import FastMCP
5
2
  import click
6
3
  import logging
7
4
 
8
- import inspect
9
- import types
10
- from dataclasses import make_dataclass, field
11
- from alibabacloud_tea_openapi import models as open_api_models
12
- from alibabacloud_tea_util import models as util_models
13
- from alibabacloud_tea_openapi.client import Client as OpenApiClient
14
- from alibabacloud_openapi_util.client import Client as OpenApiUtilClient
15
- from alibaba_cloud_ops_mcp_server.api_meta_client import ApiMetaClient
16
5
  from alibaba_cloud_ops_mcp_server.config import config
17
-
18
- from alibaba_cloud_ops_mcp_server import oos_tools
19
- from alibaba_cloud_ops_mcp_server import cms_tools
20
- from alibaba_cloud_ops_mcp_server import oss_tools
6
+ from alibaba_cloud_ops_mcp_server.tools import cms_tools, oos_tools, oss_tools, api_tools
21
7
 
22
8
  logger = logging.getLogger(__name__)
23
9
 
24
10
 
25
- type_map = {
26
- 'string': str,
27
- 'integer': int,
28
- 'boolean': bool,
29
- 'array': list,
30
- 'object': dict,
31
- 'number': float
32
- }
33
-
34
-
35
- def create_client(service: str, region_id: str) -> OpenApiClient:
36
- config = open_api_models.Config(
37
- access_key_id=os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'],
38
- access_key_secret=os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'],
39
- user_agent='alibaba-cloud-ops-mcp-server',
40
- )
41
- if isinstance(service, str):
42
- service = service.lower()
43
- config.endpoint = f'{service}.{region_id}.aliyuncs.com'
44
- return OpenApiClient(config)
45
-
46
-
47
- def tools_api_call(service: str, api: str, parameters: dict, ctx: Context):
48
- service = service.lower()
49
- api_meta, _ = ApiMetaClient.get_api_meta(service, api)
50
- version = ApiMetaClient.get_service_version(service)
51
- method = 'POST' if api_meta.get('methods', [])[0] == 'post' else 'GET'
52
- path = api_meta.get('path', '/')
53
- style = ApiMetaClient.get_service_style(service)
54
- req = open_api_models.OpenApiRequest(
55
- query=OpenApiUtilClient.query(parameters)
56
- )
57
- params = open_api_models.Params(
58
- action=api,
59
- version=version,
60
- protocol='HTTPS',
61
- pathname=path,
62
- method=method,
63
- auth_type='AK',
64
- style=style,
65
- req_body_type='formData',
66
- body_type='json'
67
- )
68
- client = create_client(service, parameters.get('RegionId', 'cn-hangzhou'))
69
- runtime = util_models.RuntimeOptions()
70
- return client.call_api(params, req, runtime)
71
-
72
-
73
- def create_parameter_schema(fields: dict):
74
- return make_dataclass("ParameterSchema", [(name, type_, value) for name, (type_, value) in fields.items()])
75
-
76
-
77
- def create_function_schemas(service, api, api_meta):
78
- schemas = {}
79
- schemas[api] = {}
80
- parameters = api_meta['parameters']
81
- for parameter in parameters:
82
- name = parameter.get('name')
83
- # TODO 目前忽略了带'.'的参数
84
- if '.' in name:
85
- continue
86
- schema = parameter.get('schema', '')
87
- description = schema.get('description', '')
88
- example = schema.get('example', '')
89
- type_ = schema.get('type', '')
90
- description = f'{description} 请注意,提供参数要严格按照参数的类型和参数示例的提示,如果提到参数为String,且为一个 JSON 数组字符串,应在数组内使用单引号包裹对应的参数以避免转义问题,并在最外侧用双引号包裹以确保其是字符串,否则可能会导致参数解析错误。参数类型: {type_},参数示例:{example}'
91
- required = schema.get('required', False)
92
- python_type = type_map.get(type_, str)
93
- field_info = (
94
- python_type,
95
- field(
96
- default=None,
97
- metadata={'description': description, 'required': required}
98
- )
99
- )
100
- schemas[api][name] = field_info
101
- if 'RegionId' not in schemas[api]:
102
- schemas[api]['RegionId'] = (
103
- str,
104
- field(
105
- default=None,
106
- metadata={'description': '地域ID', 'required': False}
107
- )
108
- )
109
- return schemas
110
-
111
-
112
- def create_tool_function_with_signature(service: str, function_name: str, fields: dict, description: str):
113
- """
114
- Dynamically creates a lambda function with a custom signature based on the provided fields.
115
- """
116
- parameters = []
117
- annotations = {}
118
- defaults = {}
119
-
120
- for name, (type_, field_info) in fields.items():
121
- field_description = field_info.metadata.get('description', '')
122
- is_required = field_info.metadata.get('required', False)
123
- default_value = field_info.default if not is_required else ...
124
-
125
- field_default = Field(default=default_value, description=field_description)
126
- parameters.append(inspect.Parameter(
127
- name=name,
128
- kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
129
- default=field_default,
130
- annotation=type_
131
- ))
132
- annotations[name] = type_
133
- defaults[name] = field_default
134
-
135
- signature = inspect.Signature(parameters)
136
-
137
- def func_code(*args, **kwargs):
138
- bound_args = signature.bind(*args, **kwargs)
139
- bound_args.apply_defaults()
140
-
141
- return tools_api_call(
142
- service=service,
143
- api=function_name,
144
- parameters=bound_args.arguments,
145
- ctx=None
146
- )
147
-
148
- func = types.FunctionType(
149
- func_code.__code__,
150
- globals(),
151
- function_name,
152
- None,
153
- func_code.__closure__
154
- )
155
- func.__signature__ = signature
156
- func.__annotations__ = annotations
157
- func.__defaults__ = tuple(defaults.values())
158
- func.__doc__ = description
159
-
160
- return func
161
-
162
-
163
- def create_and_decorate_tool(mcp: FastMCP, service: str, api: str):
164
- """Create a tool function for a Lambda function."""
165
- api_meta, _ = ApiMetaClient.get_api_meta(service, api)
166
- fields = create_function_schemas(service, api, api_meta).get(api, {})
167
- description = api_meta.get('summary', '')
168
- dynamic_lambda = create_tool_function_with_signature(service, api, fields, description)
169
- decorated_function = mcp.tool(name=api)(dynamic_lambda)
170
-
171
- return decorated_function
172
-
173
-
174
11
  @click.command()
175
12
  @click.option(
176
13
  "--transport",
@@ -187,9 +24,7 @@ def main(transport: str):
187
24
  mcp.add_tool(tool)
188
25
  for tool in oss_tools.tools:
189
26
  mcp.add_tool(tool)
190
- for service_code, apis in config.items():
191
- for api_name in apis:
192
- create_and_decorate_tool(mcp, service_code, api_name)
27
+ api_tools.create_api_tools(mcp, config)
193
28
 
194
29
  # Initialize and run the server
195
30
  logger.debug(f'mcp server is running on {transport} mode.')
File without changes
@@ -0,0 +1,167 @@
1
+ import os
2
+ from mcp.server.fastmcp import FastMCP, Context
3
+ from pydantic import Field
4
+ import logging
5
+
6
+ import inspect
7
+ import types
8
+ from dataclasses import make_dataclass, field
9
+ from alibabacloud_tea_openapi import models as open_api_models
10
+ from alibabacloud_tea_util import models as util_models
11
+ from alibabacloud_tea_openapi.client import Client as OpenApiClient
12
+ from alibabacloud_openapi_util.client import Client as OpenApiUtilClient
13
+ from alibaba_cloud_ops_mcp_server.alibabacloud.api_meta_client import ApiMetaClient
14
+
15
+
16
+ type_map = {
17
+ 'string': str,
18
+ 'integer': int,
19
+ 'boolean': bool,
20
+ 'array': list,
21
+ 'object': dict,
22
+ 'number': float
23
+ }
24
+
25
+
26
+ def create_client(service: str, region_id: str) -> OpenApiClient:
27
+ config = open_api_models.Config(
28
+ access_key_id=os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'],
29
+ access_key_secret=os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'],
30
+ user_agent='alibaba-cloud-ops-mcp-server',
31
+ )
32
+ if isinstance(service, str):
33
+ service = service.lower()
34
+ config.endpoint = f'{service}.{region_id}.aliyuncs.com'
35
+ return OpenApiClient(config)
36
+
37
+
38
+ def _tools_api_call(service: str, api: str, parameters: dict, ctx: Context):
39
+ service = service.lower()
40
+ api_meta, _ = ApiMetaClient.get_api_meta(service, api)
41
+ version = ApiMetaClient.get_service_version(service)
42
+ method = 'POST' if api_meta.get('methods', [])[0] == 'post' else 'GET'
43
+ path = api_meta.get('path', '/')
44
+ style = ApiMetaClient.get_service_style(service)
45
+ req = open_api_models.OpenApiRequest(
46
+ query=OpenApiUtilClient.query(parameters)
47
+ )
48
+ params = open_api_models.Params(
49
+ action=api,
50
+ version=version,
51
+ protocol='HTTPS',
52
+ pathname=path,
53
+ method=method,
54
+ auth_type='AK',
55
+ style=style,
56
+ req_body_type='formData',
57
+ body_type='json'
58
+ )
59
+ client = create_client(service, parameters.get('RegionId', 'cn-hangzhou'))
60
+ runtime = util_models.RuntimeOptions()
61
+ return client.call_api(params, req, runtime)
62
+
63
+
64
+ def _create_parameter_schema(fields: dict):
65
+ return make_dataclass("ParameterSchema", [(name, type_, value) for name, (type_, value) in fields.items()])
66
+
67
+
68
+ def _create_function_schemas(service, api, api_meta):
69
+ schemas = {}
70
+ schemas[api] = {}
71
+ parameters = api_meta.get('parameters', [])
72
+ for parameter in parameters:
73
+ name = parameter.get('name')
74
+ # TODO 目前忽略了带'.'的参数
75
+ if '.' in name:
76
+ continue
77
+ schema = parameter.get('schema', '')
78
+ description = schema.get('description', '')
79
+ example = schema.get('example', '')
80
+ type_ = schema.get('type', '')
81
+ description = f'{description} 请注意,提供参数要严格按照参数的类型和参数示例的提示,如果提到参数为String,且为一个 JSON 数组字符串,应在数组内使用单引号包裹对应的参数以避免转义问题,并在最外侧用双引号包裹以确保其是字符串,否则可能会导致参数解析错误。参数类型: {type_},参数示例:{example}'
82
+ required = schema.get('required', False)
83
+ python_type = type_map.get(type_, str)
84
+ field_info = (
85
+ python_type,
86
+ field(
87
+ default=None,
88
+ metadata={'description': description, 'required': required}
89
+ )
90
+ )
91
+ schemas[api][name] = field_info
92
+ if 'RegionId' not in schemas[api]:
93
+ schemas[api]['RegionId'] = (
94
+ str,
95
+ field(
96
+ default=None,
97
+ metadata={'description': '地域ID', 'required': False}
98
+ )
99
+ )
100
+ return schemas
101
+
102
+
103
+ def _create_tool_function_with_signature(service: str, function_name: str, fields: dict, description: str):
104
+ """
105
+ Dynamically creates a lambda function with a custom signature based on the provided fields.
106
+ """
107
+ parameters = []
108
+ annotations = {}
109
+ defaults = {}
110
+
111
+ for name, (type_, field_info) in fields.items():
112
+ field_description = field_info.metadata.get('description', '')
113
+ is_required = field_info.metadata.get('required', False)
114
+ default_value = field_info.default if not is_required else ...
115
+
116
+ field_default = Field(default=default_value, description=field_description)
117
+ parameters.append(inspect.Parameter(
118
+ name=name,
119
+ kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
120
+ default=field_default,
121
+ annotation=type_
122
+ ))
123
+ annotations[name] = type_
124
+ defaults[name] = field_default
125
+
126
+ signature = inspect.Signature(parameters)
127
+
128
+ def func_code(*args, **kwargs):
129
+ bound_args = signature.bind(*args, **kwargs)
130
+ bound_args.apply_defaults()
131
+
132
+ return _tools_api_call(
133
+ service=service,
134
+ api=function_name,
135
+ parameters=bound_args.arguments,
136
+ ctx=None
137
+ )
138
+
139
+ func = types.FunctionType(
140
+ func_code.__code__,
141
+ globals(),
142
+ function_name,
143
+ None,
144
+ func_code.__closure__
145
+ )
146
+ func.__signature__ = signature
147
+ func.__annotations__ = annotations
148
+ func.__defaults__ = tuple(defaults.values())
149
+ func.__doc__ = description
150
+
151
+ return func
152
+
153
+
154
+ def _create_and_decorate_tool(mcp: FastMCP, service: str, api: str):
155
+ """Create a tool function for an AlibabaCloud openapi."""
156
+ api_meta, _ = ApiMetaClient.get_api_meta(service, api)
157
+ fields = _create_function_schemas(service, api, api_meta).get(api, {})
158
+ description = api_meta.get('summary', '')
159
+ dynamic_lambda = _create_tool_function_with_signature(service, api, fields, description)
160
+ decorated_function = mcp.tool(name=api)(dynamic_lambda)
161
+
162
+ return decorated_function
163
+
164
+ def create_api_tools(mcp: FastMCP, config:dict):
165
+ for service_code, apis in config.items():
166
+ for api_name in apis:
167
+ _create_and_decorate_tool(mcp, service_code, api_name)
@@ -7,8 +7,6 @@ import time
7
7
  from alibabacloud_oos20190601.client import Client as oos20190601Client
8
8
  from alibabacloud_tea_openapi import models as open_api_models
9
9
  from alibabacloud_oos20190601 import models as oos_20190601_models
10
- from alibabacloud_tea_util import models as util_models
11
- from alibabacloud_tea_util.client import Client as UtilClient
12
10
 
13
11
 
14
12
  END_STATUSES = ['Success', 'Failed', 'Cancelled']
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alibaba-cloud-ops-mcp-server
3
- Version: 0.7.8
3
+ Version: 0.8.0
4
4
  Summary: A MCP server for Alibaba Cloud
5
5
  Author-email: Zheng Dayu <dayu.zdy@alibaba-inc.com>
6
6
  License-File: LICENSE
7
- Requires-Python: >=3.11
7
+ Requires-Python: >=3.10
8
8
  Requires-Dist: alibabacloud-cms20190101>=3.1.4
9
9
  Requires-Dist: alibabacloud-ecs20140526>=6.1.0
10
10
  Requires-Dist: alibabacloud-oos20190601>=3.4.1
@@ -14,7 +14,13 @@ Requires-Dist: mcp[cli]>=1.6.0
14
14
  Description-Content-Type: text/markdown
15
15
 
16
16
  # alibaba-cloud-ops-mcp-server
17
+
17
18
  [中文版本](./README_zh.md)
19
+
20
+ <a href="https://glama.ai/mcp/servers/@aliyun/alibabacloud-core-mcp-server">
21
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@aliyun/alibabacloud-core-mcp-server/badge" alt="AlibabaCloud Server MCP server" />
22
+ </a>
23
+
18
24
  ## Prepare
19
25
 
20
26
  Install [uv](https://github.com/astral-sh/uv)
@@ -94,4 +100,4 @@ If you have any questions, please join the [Alibaba Cloud Ops MCP discussion gro
94
100
 
95
101
  ## Know More
96
102
 
97
- - [Alibaba Cloud MCP Server is ready to use out of the box!!](https://developer.aliyun.com/article/1661348)
103
+ - [Alibaba Cloud MCP Server is ready to use out of the box!!](https://developer.aliyun.com/article/1661348)
@@ -0,0 +1,15 @@
1
+ alibaba_cloud_ops_mcp_server/__init__.py,sha256=BaluUNyRz8Qw-X7Y0ywDezwbkqiSvWlSYn2452XeGcA,213
2
+ alibaba_cloud_ops_mcp_server/config.py,sha256=Nq6AT8bqSVa6zu9xjInaSjFcxA-GC7MLlCsV1q53yO0,514
3
+ alibaba_cloud_ops_mcp_server/server.py,sha256=nwn04rpm68GI3UJ_hQ0E3pny86dJ47Lw1LncQiBIfW8,896
4
+ alibaba_cloud_ops_mcp_server/alibabacloud/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ alibaba_cloud_ops_mcp_server/alibabacloud/api_meta_client.py,sha256=inOmW1no1YsR6_ezOZfgJtzGKo5nRTDufkf2qUTuNOs,7605
6
+ alibaba_cloud_ops_mcp_server/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ alibaba_cloud_ops_mcp_server/tools/api_tools.py,sha256=VvAhlUEqvVj_ccwG17Vmen3QAW6fEP44k94SGPLYDXM,5936
8
+ alibaba_cloud_ops_mcp_server/tools/cms_tools.py,sha256=5IhPtkHSwqLsy7D3lGznovQ94e-XBy6HBE7qLDMoYaQ,4464
9
+ alibaba_cloud_ops_mcp_server/tools/oos_tools.py,sha256=lgjkdM18pi56VQUpK0U37-VfXpvk94N-iKisR33E2SM,9987
10
+ alibaba_cloud_ops_mcp_server/tools/oss_tools.py,sha256=6d_vacAJllwkr74bsWgle8yMzqb_Xr1CPAIr_htFP-I,4451
11
+ alibaba_cloud_ops_mcp_server-0.8.0.dist-info/METADATA,sha256=3ia0azdNXXZK5nPih34ve-c0U9e2jxy6yXdLffB-A4U,4259
12
+ alibaba_cloud_ops_mcp_server-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ alibaba_cloud_ops_mcp_server-0.8.0.dist-info/entry_points.txt,sha256=ESGAWXKEp184forhs7VzTD4P1AUdZz6vCW6hRUKITGw,83
14
+ alibaba_cloud_ops_mcp_server-0.8.0.dist-info/licenses/LICENSE,sha256=gQgVkp2ttRCjodiPpXZZR-d7JnrYIYNiHk1YDUYgpa4,11331
15
+ alibaba_cloud_ops_mcp_server-0.8.0.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- alibaba_cloud_ops_mcp_server/__init__.py,sha256=BaluUNyRz8Qw-X7Y0ywDezwbkqiSvWlSYn2452XeGcA,213
2
- alibaba_cloud_ops_mcp_server/api_meta_client.py,sha256=pxJztmkmcqqI3djECziAIMc3ZxsS8crqSc6AbneOX2I,7315
3
- alibaba_cloud_ops_mcp_server/cms_tools.py,sha256=5IhPtkHSwqLsy7D3lGznovQ94e-XBy6HBE7qLDMoYaQ,4464
4
- alibaba_cloud_ops_mcp_server/config.py,sha256=Nq6AT8bqSVa6zu9xjInaSjFcxA-GC7MLlCsV1q53yO0,514
5
- alibaba_cloud_ops_mcp_server/oos_tools.py,sha256=NDu18_R3SqB-Fv1SpnljV0b2Py6G9HXcoqvbV0MWL7Y,10105
6
- alibaba_cloud_ops_mcp_server/oss_tools.py,sha256=6d_vacAJllwkr74bsWgle8yMzqb_Xr1CPAIr_htFP-I,4451
7
- alibaba_cloud_ops_mcp_server/server.py,sha256=9qk122yEAZgzJ9iBTBns90McguU4pQFIgjOPD1CzH8c,6723
8
- alibaba_cloud_ops_mcp_server-0.7.8.dist-info/METADATA,sha256=e-yNpnuyrKUk1GkiRyxsWMqe6kEICuQBUfelilLJsrM,4023
9
- alibaba_cloud_ops_mcp_server-0.7.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- alibaba_cloud_ops_mcp_server-0.7.8.dist-info/entry_points.txt,sha256=ESGAWXKEp184forhs7VzTD4P1AUdZz6vCW6hRUKITGw,83
11
- alibaba_cloud_ops_mcp_server-0.7.8.dist-info/licenses/LICENSE,sha256=gQgVkp2ttRCjodiPpXZZR-d7JnrYIYNiHk1YDUYgpa4,11331
12
- alibaba_cloud_ops_mcp_server-0.7.8.dist-info/RECORD,,