mcp-server-sms 0.1.0__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.
- mcp_server_sms-0.1.0/.env.example +4 -0
- mcp_server_sms-0.1.0/.gitignore +8 -0
- mcp_server_sms-0.1.0/LICENSE +202 -0
- mcp_server_sms-0.1.0/PKG-INFO +110 -0
- mcp_server_sms-0.1.0/README.md +89 -0
- mcp_server_sms-0.1.0/README_zh.md +116 -0
- mcp_server_sms-0.1.0/pyproject.toml +38 -0
- mcp_server_sms-0.1.0/src/mcp_server_sms/__init__.py +3 -0
- mcp_server_sms-0.1.0/src/mcp_server_sms/auth.py +46 -0
- mcp_server_sms-0.1.0/src/mcp_server_sms/client.py +309 -0
- mcp_server_sms-0.1.0/src/mcp_server_sms/models.py +132 -0
- mcp_server_sms-0.1.0/src/mcp_server_sms/server.py +555 -0
- mcp_server_sms-0.1.0/tests/conftest.py +147 -0
- mcp_server_sms-0.1.0/tests/test_client.py +207 -0
- mcp_server_sms-0.1.0/tests/test_stdio.py +29 -0
- mcp_server_sms-0.1.0/tests/test_tools.py +278 -0
- mcp_server_sms-0.1.0/tests/test_transport.py +105 -0
- mcp_server_sms-0.1.0/uv.lock +1553 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but not
|
|
33
|
+
limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mcp-server-sms
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Stateless MCP tools for Volcengine SMS APIs
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: httpx<1,>=0.28
|
|
9
|
+
Requires-Dist: mcp[cli]<3,>=2.2
|
|
10
|
+
Requires-Dist: pydantic<3,>=2.11
|
|
11
|
+
Requires-Dist: starlette<2,>=0.47
|
|
12
|
+
Requires-Dist: uvicorn<1,>=0.35
|
|
13
|
+
Requires-Dist: volcengine<2,>=1.0.191
|
|
14
|
+
Provides-Extra: test
|
|
15
|
+
Requires-Dist: build>=1; extra == 'test'
|
|
16
|
+
Requires-Dist: pytest-asyncio>=1; extra == 'test'
|
|
17
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
18
|
+
Requires-Dist: ruff<1,>=0.16; extra == 'test'
|
|
19
|
+
Requires-Dist: twine>=6; extra == 'test'
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# Volcengine SMS MCP Server
|
|
23
|
+
|
|
24
|
+
[简体中文](README_zh.md)
|
|
25
|
+
|
|
26
|
+
An independent adapter for Volcengine SMS APIs. Each tool receives its complete API input, makes one upstream request and returns the result. It supports MCP **2026-07-28**, stdio and **stateless Streamable HTTP**.
|
|
27
|
+
|
|
28
|
+
There is no database, business cache, qualification draft, stored preview, generic execution ticket, local file service or UI. It does not load or depend on a Skill. The client owns interaction and user confirmation; the SMS service owns business validation, applications, tasks and delivery records.
|
|
29
|
+
|
|
30
|
+
## Tools
|
|
31
|
+
|
|
32
|
+
| Capability | Tools |
|
|
33
|
+
| --- | --- |
|
|
34
|
+
| Account and message groups | `list_message_groups`, `get_message_group`, `get_account_information` |
|
|
35
|
+
| Qualifications | `get_qualification_requirements`, `list_qualifications`, `recognize_qualification_document`, `check_qualification_enterprise`, `check_qualification_person`, `apply_qualification` |
|
|
36
|
+
| Mobile verification | `send_verification_code`, `verify_code` |
|
|
37
|
+
| Signatures and templates | `list_signatures`, `apply_signature`, `list_templates`, `list_second_templates`, `apply_template` |
|
|
38
|
+
| Sending and reports | `send_sms`, `list_send_logs`, `get_send_statistics` |
|
|
39
|
+
| Ordinary batches | `get_batch_upload_url`, `get_batch_csv_template`, `create_batch_task`, `get_batch_task`, `list_batch_tasks`, `start_batch_task`, `cancel_batch_task` |
|
|
40
|
+
|
|
41
|
+
There are **26 tools**. Ordinary batches cover domestic notification and marketing SMS. Special notification batches, OTP batches and international SMS are outside this server's scope.
|
|
42
|
+
|
|
43
|
+
Tools preserve the upstream result structure and IDs. They do not select a template, rewrite content, deduplicate recipients, synthesize qualification-check tickets or decide whether a human approved an operation. A `ticket` returned by a qualification check is an upstream business receipt, not MCP state; the caller provides it when submitting the complete application. Client configuration must provide the application/scene/template values required by mobile verification APIs.
|
|
44
|
+
|
|
45
|
+
Qualification images must already be available through the existing SMS material channel. OCR and application tools accept its file references; there is no MCP-owned `fileId`, filesystem access or upload endpoint. The client must arrange a private material channel and handle OCR results and personal information appropriately. For batches, `get_batch_upload_url` returns the upstream signed upload URL and `file` key: upload the CSV directly, then pass that key as `fileUrl` to `create_batch_task`. Creating a task does not start it.
|
|
46
|
+
|
|
47
|
+
## Install and configure
|
|
48
|
+
|
|
49
|
+
Python 3.11 or later is required. From source:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
uv sync --extra test
|
|
53
|
+
uv run mcp-server-sms --transport stdio
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
For stdio, supply `VOLCENGINE_ACCESS_KEY`, `VOLCENGINE_SECRET_KEY` and optionally `VOLCENGINE_SESSION_TOKEN` in the client process environment. No local subject, encryption key or database URL is required. Do not place credentials in prompts or source control.
|
|
57
|
+
|
|
58
|
+
After version 0.1.0 has been published:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uvx --from 'mcp-server-sms==0.1.0' mcp-server-sms --transport stdio
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Development clients can use an explicit wheel path before publication.
|
|
65
|
+
|
|
66
|
+
## Streamable HTTP
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
mcp-server-sms --transport streamable-http --host 0.0.0.0 --port 8000 \
|
|
70
|
+
--public-url https://sms-mcp.example.com
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Connect to `/mcp`. The deployment supplies HTTPS and access policy. Every HTTP request carries `Authorization: Bearer <Base64 JSON>` with the following credential fields:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{"AccessKeyId":"<access-key>","SecretAccessKey":"<secret-key>","SessionToken":"<optional-session-token>"}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This follows the repository's credential-passing convention; it is **not an OAuth access token** and Base64 is not encryption. The client or trusted gateway must construct the header outside model context. HTTP never falls back to process credentials. Each SMS call uses these credentials for official V4 signing, and SMS/IAM enforces resource access. The server has no OAuth login flow or persistent caller identity.
|
|
80
|
+
|
|
81
|
+
`--public-url` adds the deployment's HTTPS origin to DNS-rebinding protection. Without it, only local HTTP hosts are accepted. Requests do not require a protocol session or shared process state. Replicas can serve requests independently.
|
|
82
|
+
|
|
83
|
+
The HTTP connection pool is owned by the MCP server lifespan and closed at shutdown. Credentials and signed headers remain per-request, and response cookies are not retained between callers. Connection reuse does not introduce business state or a database.
|
|
84
|
+
|
|
85
|
+
## Outcomes and confirmation
|
|
86
|
+
|
|
87
|
+
- The client must obtain user authorization before calling tools with side effects. Tool annotations describe behavior; they do not prove user consent.
|
|
88
|
+
- SMS API acceptance and delivery are separate. Use the returned Message ID with `list_send_logs`; an empty result does not prove failure.
|
|
89
|
+
- Application creation is not audit approval. Query qualification, signature or template state separately.
|
|
90
|
+
- No API calls are automatically retried. A timeout or unconfirmed write returns `outcome_unknown`; do not blindly repeat it. This adapter does not provide durable request deduplication or an exactly-once guarantee.
|
|
91
|
+
- Unstructured HTTP 400/401/403/404/405/413/415/422/429 responses are explicit rejections. Other unstructured statuses, server errors, and malformed or incomplete successful write responses remain uncertain. API-response errors include `http_status` without echoing the response body.
|
|
92
|
+
- The upload API's top-level `url` is returned byte-for-byte, including its signed credential parameters. Other fields still undergo credential redaction. Treat the complete URL as a temporary capability; do not redact it before upload or expose it in logs.
|
|
93
|
+
- A successful API call can contain a failed business check, such as an invalid verification code. Inspect the upstream `status` value.
|
|
94
|
+
- The API adapter does not log request bodies or credentials. Upstream error text is not echoed, and authentication fields are removed from results; ordinary business content is preserved. Clients and deployment platforms must also configure their own logging and private-data handling.
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uv sync --extra test
|
|
100
|
+
uv run ruff check src tests
|
|
101
|
+
uv run pytest -q
|
|
102
|
+
uv build
|
|
103
|
+
uv run python -m twine check dist/*
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Tests cover direct API calls, request serialization, credential isolation, uncertain writes, business error responses, file-response decoding and the stateless protocol. They do not submit real qualifications or send real SMS. Actual write tests require separate customer authorization.
|
|
107
|
+
|
|
108
|
+
API fields are checked against the [official SMS SDK](https://github.com/volcengine/volcengine-python-sdk/tree/3b116781c5b8bb648f215b29587f2ec74d9e5d02/volcenginesdkvolcsms). V4 signing uses the [official Python SDK](https://github.com/volcengine/volc-sdk-python). Qualification extension APIs use the existing SMS service contract; SDK coverage and account availability must be verified for the deployment.
|
|
109
|
+
|
|
110
|
+
The repository's package workflow builds PRs and publishes new versions after merge, with maintainer PyPI permissions. Package publication does not deploy a cloud service. Licensed under Apache-2.0.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Volcengine SMS MCP Server
|
|
2
|
+
|
|
3
|
+
[简体中文](README_zh.md)
|
|
4
|
+
|
|
5
|
+
An independent adapter for Volcengine SMS APIs. Each tool receives its complete API input, makes one upstream request and returns the result. It supports MCP **2026-07-28**, stdio and **stateless Streamable HTTP**.
|
|
6
|
+
|
|
7
|
+
There is no database, business cache, qualification draft, stored preview, generic execution ticket, local file service or UI. It does not load or depend on a Skill. The client owns interaction and user confirmation; the SMS service owns business validation, applications, tasks and delivery records.
|
|
8
|
+
|
|
9
|
+
## Tools
|
|
10
|
+
|
|
11
|
+
| Capability | Tools |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| Account and message groups | `list_message_groups`, `get_message_group`, `get_account_information` |
|
|
14
|
+
| Qualifications | `get_qualification_requirements`, `list_qualifications`, `recognize_qualification_document`, `check_qualification_enterprise`, `check_qualification_person`, `apply_qualification` |
|
|
15
|
+
| Mobile verification | `send_verification_code`, `verify_code` |
|
|
16
|
+
| Signatures and templates | `list_signatures`, `apply_signature`, `list_templates`, `list_second_templates`, `apply_template` |
|
|
17
|
+
| Sending and reports | `send_sms`, `list_send_logs`, `get_send_statistics` |
|
|
18
|
+
| Ordinary batches | `get_batch_upload_url`, `get_batch_csv_template`, `create_batch_task`, `get_batch_task`, `list_batch_tasks`, `start_batch_task`, `cancel_batch_task` |
|
|
19
|
+
|
|
20
|
+
There are **26 tools**. Ordinary batches cover domestic notification and marketing SMS. Special notification batches, OTP batches and international SMS are outside this server's scope.
|
|
21
|
+
|
|
22
|
+
Tools preserve the upstream result structure and IDs. They do not select a template, rewrite content, deduplicate recipients, synthesize qualification-check tickets or decide whether a human approved an operation. A `ticket` returned by a qualification check is an upstream business receipt, not MCP state; the caller provides it when submitting the complete application. Client configuration must provide the application/scene/template values required by mobile verification APIs.
|
|
23
|
+
|
|
24
|
+
Qualification images must already be available through the existing SMS material channel. OCR and application tools accept its file references; there is no MCP-owned `fileId`, filesystem access or upload endpoint. The client must arrange a private material channel and handle OCR results and personal information appropriately. For batches, `get_batch_upload_url` returns the upstream signed upload URL and `file` key: upload the CSV directly, then pass that key as `fileUrl` to `create_batch_task`. Creating a task does not start it.
|
|
25
|
+
|
|
26
|
+
## Install and configure
|
|
27
|
+
|
|
28
|
+
Python 3.11 or later is required. From source:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv sync --extra test
|
|
32
|
+
uv run mcp-server-sms --transport stdio
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For stdio, supply `VOLCENGINE_ACCESS_KEY`, `VOLCENGINE_SECRET_KEY` and optionally `VOLCENGINE_SESSION_TOKEN` in the client process environment. No local subject, encryption key or database URL is required. Do not place credentials in prompts or source control.
|
|
36
|
+
|
|
37
|
+
After version 0.1.0 has been published:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uvx --from 'mcp-server-sms==0.1.0' mcp-server-sms --transport stdio
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Development clients can use an explicit wheel path before publication.
|
|
44
|
+
|
|
45
|
+
## Streamable HTTP
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
mcp-server-sms --transport streamable-http --host 0.0.0.0 --port 8000 \
|
|
49
|
+
--public-url https://sms-mcp.example.com
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Connect to `/mcp`. The deployment supplies HTTPS and access policy. Every HTTP request carries `Authorization: Bearer <Base64 JSON>` with the following credential fields:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{"AccessKeyId":"<access-key>","SecretAccessKey":"<secret-key>","SessionToken":"<optional-session-token>"}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This follows the repository's credential-passing convention; it is **not an OAuth access token** and Base64 is not encryption. The client or trusted gateway must construct the header outside model context. HTTP never falls back to process credentials. Each SMS call uses these credentials for official V4 signing, and SMS/IAM enforces resource access. The server has no OAuth login flow or persistent caller identity.
|
|
59
|
+
|
|
60
|
+
`--public-url` adds the deployment's HTTPS origin to DNS-rebinding protection. Without it, only local HTTP hosts are accepted. Requests do not require a protocol session or shared process state. Replicas can serve requests independently.
|
|
61
|
+
|
|
62
|
+
The HTTP connection pool is owned by the MCP server lifespan and closed at shutdown. Credentials and signed headers remain per-request, and response cookies are not retained between callers. Connection reuse does not introduce business state or a database.
|
|
63
|
+
|
|
64
|
+
## Outcomes and confirmation
|
|
65
|
+
|
|
66
|
+
- The client must obtain user authorization before calling tools with side effects. Tool annotations describe behavior; they do not prove user consent.
|
|
67
|
+
- SMS API acceptance and delivery are separate. Use the returned Message ID with `list_send_logs`; an empty result does not prove failure.
|
|
68
|
+
- Application creation is not audit approval. Query qualification, signature or template state separately.
|
|
69
|
+
- No API calls are automatically retried. A timeout or unconfirmed write returns `outcome_unknown`; do not blindly repeat it. This adapter does not provide durable request deduplication or an exactly-once guarantee.
|
|
70
|
+
- Unstructured HTTP 400/401/403/404/405/413/415/422/429 responses are explicit rejections. Other unstructured statuses, server errors, and malformed or incomplete successful write responses remain uncertain. API-response errors include `http_status` without echoing the response body.
|
|
71
|
+
- The upload API's top-level `url` is returned byte-for-byte, including its signed credential parameters. Other fields still undergo credential redaction. Treat the complete URL as a temporary capability; do not redact it before upload or expose it in logs.
|
|
72
|
+
- A successful API call can contain a failed business check, such as an invalid verification code. Inspect the upstream `status` value.
|
|
73
|
+
- The API adapter does not log request bodies or credentials. Upstream error text is not echoed, and authentication fields are removed from results; ordinary business content is preserved. Clients and deployment platforms must also configure their own logging and private-data handling.
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
uv sync --extra test
|
|
79
|
+
uv run ruff check src tests
|
|
80
|
+
uv run pytest -q
|
|
81
|
+
uv build
|
|
82
|
+
uv run python -m twine check dist/*
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Tests cover direct API calls, request serialization, credential isolation, uncertain writes, business error responses, file-response decoding and the stateless protocol. They do not submit real qualifications or send real SMS. Actual write tests require separate customer authorization.
|
|
86
|
+
|
|
87
|
+
API fields are checked against the [official SMS SDK](https://github.com/volcengine/volcengine-python-sdk/tree/3b116781c5b8bb648f215b29587f2ec74d9e5d02/volcenginesdkvolcsms). V4 signing uses the [official Python SDK](https://github.com/volcengine/volc-sdk-python). Qualification extension APIs use the existing SMS service contract; SDK coverage and account availability must be verified for the deployment.
|
|
88
|
+
|
|
89
|
+
The repository's package workflow builds PRs and publishes new versions after merge, with maintainer PyPI permissions. Package publication does not deploy a cloud service. Licensed under Apache-2.0.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# 火山引擎短信 MCP Server
|
|
2
|
+
|
|
3
|
+
[English](README.md)
|
|
4
|
+
|
|
5
|
+
独立的短信 API 适配服务,支持 **MCP 2026-07-28**、stdio 和 **Stateless Streamable HTTP**。每个工具接收完整参数,调用一次对应短信接口并返回结果。
|
|
6
|
+
|
|
7
|
+
不依赖 Skill,不保存业务草稿或预览,不提供页面、本地文件服务及通用 `prepare/execute` 流程;不需要 SQLite、PostgreSQL、Redis 或业务加密密钥。客户端负责交互和用户确认,现有短信服务负责业务校验、申请、任务与发送记录。
|
|
8
|
+
|
|
9
|
+
## 工具与接口
|
|
10
|
+
|
|
11
|
+
| 工具 | 短信 Action |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `list_message_groups` | `ListSubAccountForAgent` |
|
|
14
|
+
| `get_message_group` | `GetSubAccountDetail` |
|
|
15
|
+
| `get_account_information` | `ListAllSmsProduct` |
|
|
16
|
+
| `get_qualification_requirements` | `GetAccountIdentRankForAgent` |
|
|
17
|
+
| `list_qualifications` | `GetSignatureIdentificationList` |
|
|
18
|
+
| `recognize_qualification_document` | `GetOCRLicenseForAgent` |
|
|
19
|
+
| `check_qualification_enterprise` | `ThreeElementEnterpriseCheckForAgent` |
|
|
20
|
+
| `check_qualification_person` | `ThreeElementPersonCheckForAgent` |
|
|
21
|
+
| `send_verification_code` | `SendSmsVerifyCodeByMobile` |
|
|
22
|
+
| `verify_code` | `CheckSmsVerifyCodeByMobile` |
|
|
23
|
+
| `apply_qualification` | `ApplySignatureIdentificationForAgent` |
|
|
24
|
+
| `list_signatures` | `ListSignatureForAgent` |
|
|
25
|
+
| `apply_signature` | `ApplySmsSignatureV2` |
|
|
26
|
+
| `list_templates` | `ListSmsTemplateForAgent` |
|
|
27
|
+
| `list_second_templates` | `ListSecondTemplate` |
|
|
28
|
+
| `apply_template` | `ApplySmsTemplateV2` |
|
|
29
|
+
| `send_sms` | `SendSmsForAgent` |
|
|
30
|
+
| `list_send_logs` | `ListSmsSendLogForAgent` |
|
|
31
|
+
| `get_send_statistics` | `ListTotalSendCountStatForAgent` |
|
|
32
|
+
| `get_batch_upload_url` | `GetUploadTosURL` |
|
|
33
|
+
| `get_batch_csv_template` | `TemplateUploadDemo` |
|
|
34
|
+
| `create_batch_task` | `SetBatchTask` |
|
|
35
|
+
| `get_batch_task` | `GetBatchTaskDetail` |
|
|
36
|
+
| `list_batch_tasks` | `GetBatchTaskList` |
|
|
37
|
+
| `start_batch_task` | `ConsentBatchTask` |
|
|
38
|
+
| `cancel_batch_task` | `DeleteBatchTask` |
|
|
39
|
+
|
|
40
|
+
共 26 个工具。普通群发仅覆盖国内通知和营销短信,不包含特殊通知群发、验证码群发或国际短信。服务开通由用户在[短信控制台](https://console.volcengine.com/sms)阅读协议并完成;`RE:0001` 表示未开通。
|
|
41
|
+
|
|
42
|
+
工具不替换签名、不匹配或改写模板、不去重号码,也不生成跳过校验票据。已有模板 ID 直接交给发送接口校验,不因查询列表缺少某类模板就自行禁止发送。
|
|
43
|
+
|
|
44
|
+
## 资质与文件
|
|
45
|
+
|
|
46
|
+
资质申请接收完整企业、经办人、责任人、可选法人信息、材料引用和上游校验票据。`ticket` 是短信校验接口返回的业务凭据,由客户端保管并在申请时传回,不是 MCP 的草稿 ID。上游要求、票据有效性和审核结果由短信服务判断。
|
|
47
|
+
|
|
48
|
+
验证码接口所需的应用 ID、场景、模板和通道来自已有业务配置,MCP 不硬编码某个内部应用或自动选择。客户端应通过私密渠道提供个人材料和验证码;OCR 返回识别字段,由客户核对后决定提交内容。
|
|
49
|
+
|
|
50
|
+
资质材料应先通过现有短信材料渠道上传,OCR 和申请工具接收该渠道的文件引用。MCP 不读取本机路径、不下载任意 URL、不托管文件,也不提供额外的 `/files` 接口。实际客户端须具备可用的私密材料渠道,普通聊天附件不会自动转换成短信材料引用。
|
|
51
|
+
|
|
52
|
+
群发 CSV:调用 `get_batch_upload_url`,客户端使用短期签名 URL 直接上传原文件,再把返回的 `file` 对象 Key 作为 `fileUrl` 传入 `create_batch_task`。创建与启动是短信业务本身的两个操作,必须分别调用。
|
|
53
|
+
|
|
54
|
+
## 本地运行
|
|
55
|
+
|
|
56
|
+
需要 Python 3.11 或以上版本。配置 `VOLCENGINE_ACCESS_KEY`、`VOLCENGINE_SECRET_KEY` 及可选 `VOLCENGINE_SESSION_TOKEN`,再运行:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uv sync --extra test
|
|
60
|
+
uv run mcp-server-sms --transport stdio
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
凭据放在客户端进程环境中,不放入工具参数、聊天或仓库。无需配置数据库、流程有效期、本地身份或材料目录。
|
|
64
|
+
|
|
65
|
+
0.1.0 发布后可使用固定版本:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uvx --from 'mcp-server-sms==0.1.0' mcp-server-sms --transport stdio
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
发布前可以从源码或指定 wheel 安装。
|
|
72
|
+
|
|
73
|
+
## 云端运行
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
mcp-server-sms --transport streamable-http --host 0.0.0.0 --port 8000 \
|
|
77
|
+
--public-url https://sms-mcp.example.com
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
客户端连接 `/mcp`。部署平台提供 HTTPS 和访问策略,每个请求通过 `Authorization: Bearer <Base64 JSON>` 传递短信凭据:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{"AccessKeyId":"<access-key>","SecretAccessKey":"<secret-key>","SessionToken":"<optional-session-token>"}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
这沿用仓库已有的凭据传递方式,**不是 OAuth access token,Base64 也不是加密**。由客户端或可信网关在模型之外构造请求头,不得写入访问日志。每次短信请求独立执行官方 V4 签名,由短信/IAM 校验权限;HTTP 缺少凭据时不会使用进程默认账号。
|
|
87
|
+
|
|
88
|
+
不创建 OAuth 登录应用,不保存用户身份。`--public-url` 用于配置 HTTP 域名校验;未提供时仅允许本机域名。无需协议会话或共享存储,多副本可以独立处理请求。云端平台的实际请求头与材料渠道仍需联调确认。
|
|
89
|
+
|
|
90
|
+
HTTP 连接池随 MCP 服务启动和关闭,凭据和签名头仍逐请求生成,不保留不同调用者之间的响应 Cookie。连接复用不引入业务状态或数据库。
|
|
91
|
+
|
|
92
|
+
## 执行语义
|
|
93
|
+
|
|
94
|
+
- 写工具直接产生业务操作,客户端必须先取得用户授权;工具注解不是授权证明。
|
|
95
|
+
- 发送接口成功只表示受理,需按 Message ID 查询送达回执。空列表不是失败证据。
|
|
96
|
+
- 资质、签名和模板申请成功不等于审核通过,分别查询其状态。
|
|
97
|
+
- 不自动重试任何 API 调用。写请求超时或响应不足以确认结果时返回 `outcome_unknown`,不能盲目重发;不承诺跨请求去重或“恰好一次”。
|
|
98
|
+
- 非结构化的 HTTP 400/401/403/404/405/413/415/422/429 响应按明确拒绝处理;其他非结构化状态、服务端异常和损坏或缺少关键结果的成功响应,仍保留未知写结果。API 响应错误携带 `http_status`,不回显响应正文。
|
|
99
|
+
- 上传接口顶层 `url` 保持完整原文,包括参与签名的凭证参数;其他字段仍按规则脱敏。完整 URL 是短期授权信息,不要修改后上传,也不要写入日志。
|
|
100
|
+
- 接口调用成功也可能返回校验未通过,例如验证码的 `status` 非零;保留上游字段,调用方应检查业务结果。
|
|
101
|
+
- 错误保留公开错误码和 RequestId,不回显请求正文、验证码、凭据或原始服务端错误文本;正常业务数据不做无声改写。
|
|
102
|
+
|
|
103
|
+
## 验证和发布
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
uv run ruff check src tests
|
|
107
|
+
uv run pytest -q
|
|
108
|
+
uv build
|
|
109
|
+
uv run python -m twine check dist/*
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
测试覆盖直接调用、参数映射、调用者凭据隔离、未知写结果、业务错误、CSV 响应及无状态协议;不会真的发送短信或提交申请。实际写操作验收需要客户单独授权,旧实现的测试结果不能代表本版已完成全流程验收。
|
|
113
|
+
|
|
114
|
+
请求字段参照[官方短信 SDK](https://github.com/volcengine/volcengine-python-sdk/tree/3b116781c5b8bb648f215b29587f2ec74d9e5d02/volcenginesdkvolcsms),请求签名使用[官方 Python SDK](https://github.com/volcengine/volc-sdk-python)。资质扩展 Action 按现有短信服务契约接入,需确认部署账号的接口可用性。
|
|
115
|
+
|
|
116
|
+
包版本为 0.1.0,尚未发布。复用仓库现有 PR 构建检查和合入后的 PyPI 发布流程,发布权限由维护者提供;包发布不等于云端部署。许可证为 Apache-2.0。
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mcp-server-sms"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Stateless MCP tools for Volcengine SMS APIs"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = {text = "Apache-2.0"}
|
|
8
|
+
dependencies = [
|
|
9
|
+
"mcp[cli]>=2.2,<3",
|
|
10
|
+
"pydantic>=2.11,<3",
|
|
11
|
+
"httpx>=0.28,<1",
|
|
12
|
+
"starlette>=0.47,<2",
|
|
13
|
+
"uvicorn>=0.35,<1",
|
|
14
|
+
"volcengine>=1.0.191,<2",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
mcp-server-sms = "mcp_server_sms.server:main"
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
test = ["pytest>=8", "pytest-asyncio>=1", "build>=1", "twine>=6", "ruff>=0.16,<1"]
|
|
22
|
+
|
|
23
|
+
[build-system]
|
|
24
|
+
requires = ["hatchling"]
|
|
25
|
+
build-backend = "hatchling.build"
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.wheel]
|
|
28
|
+
packages = ["src/mcp_server_sms"]
|
|
29
|
+
|
|
30
|
+
[tool.pytest.ini_options]
|
|
31
|
+
asyncio_mode = "auto"
|
|
32
|
+
testpaths = ["tests"]
|
|
33
|
+
|
|
34
|
+
[tool.ruff]
|
|
35
|
+
line-length = 100
|
|
36
|
+
|
|
37
|
+
[tool.ruff.lint]
|
|
38
|
+
select = ["E4", "E7", "E9", "F", "I"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Per-call credentials. HTTP never falls back to the server's environment."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import binascii
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AuthenticationError(ValueError):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True, repr=False)
|
|
16
|
+
class Credentials:
|
|
17
|
+
access_key: str
|
|
18
|
+
secret_key: str
|
|
19
|
+
session_token: str = ""
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_headers(cls, headers: Mapping[str, str] | None):
|
|
23
|
+
if headers is None:
|
|
24
|
+
values = {
|
|
25
|
+
"AccessKeyId": os.getenv("VOLCENGINE_ACCESS_KEY"),
|
|
26
|
+
"SecretAccessKey": os.getenv("VOLCENGINE_SECRET_KEY"),
|
|
27
|
+
"SessionToken": os.getenv("VOLCENGINE_SESSION_TOKEN", ""),
|
|
28
|
+
}
|
|
29
|
+
else:
|
|
30
|
+
authorization = headers.get("authorization", "")
|
|
31
|
+
scheme, separator, encoded = authorization.partition(" ")
|
|
32
|
+
if not separator or scheme.lower() != "bearer" or len(encoded) > 16384:
|
|
33
|
+
raise AuthenticationError("需要 Authorization: Bearer <Base64 JSON 短信凭据>")
|
|
34
|
+
try:
|
|
35
|
+
values = json.loads(base64.b64decode(encoded, validate=True))
|
|
36
|
+
except (ValueError, UnicodeError, binascii.Error):
|
|
37
|
+
raise AuthenticationError("短信调用凭据格式无效") from None
|
|
38
|
+
if not isinstance(values, dict) or any(
|
|
39
|
+
not isinstance(values.get(key), str) or not values[key]
|
|
40
|
+
for key in ("AccessKeyId", "SecretAccessKey")
|
|
41
|
+
):
|
|
42
|
+
raise AuthenticationError("需要完整的短信 AccessKeyId 和 SecretAccessKey")
|
|
43
|
+
token = values.get("SessionToken", "")
|
|
44
|
+
if not isinstance(token, str):
|
|
45
|
+
raise AuthenticationError("SessionToken 必须是字符串")
|
|
46
|
+
return cls(values["AccessKeyId"], values["SecretAccessKey"], token)
|