lumio-admin-mcp 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.
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cyetstar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.5
2
+ Name: lumio-admin-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP server for Lumio admin APIs (诗文 / 词库 / 字库) via Bot Token
5
+ Project-URL: Homepage, https://github.com/cyetstar/lumio
6
+ Project-URL: Repository, https://github.com/cyetstar/lumio
7
+ Project-URL: Documentation, https://github.com/cyetstar/lumio/tree/main/mcp/lumio-admin
8
+ Project-URL: Issues, https://github.com/cyetstar/lumio/issues
9
+ Author: cyetstar
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: admin,bot,lumio,mcp
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: mcp>=1.9.0
25
+ Requires-Dist: pydantic>=2.0
26
+ Description-Content-Type: text/markdown
27
+
28
+ # lumio-admin-mcp
29
+
30
+ MCP server (stdio) that exposes Lumio **诗文库 / 词库 / 字库** admin APIs to hosts such as **Cursor** and **Grok Bot**, authenticated with a long-lived Bot Token.
31
+
32
+ **PyPI:** [`lumio-admin-mcp`](https://pypi.org/project/lumio-admin-mcp/)
33
+ **源码:** [`mcp/lumio-admin`](https://github.com/cyetstar/lumio/tree/main/mcp/lumio-admin)
34
+
35
+ **设计规格:**
36
+
37
+ - 一期诗文:`docs/superpowers/specs/2026-10-22-lumio-classic-bot-mcp-design.md`
38
+ - 二期词库/字库:`docs/superpowers/specs/2026-10-23-lumio-mcp-words-hanzi.md`
39
+
40
+ ## Requirements
41
+
42
+ - Python 3.11+
43
+ - [uv](https://docs.astral.sh/uv/)(推荐用 `uvx` 直接跑)
44
+ - 可达的 Lumio API(`LUMIO_API_BASE_URL`)
45
+ - Bot Token(scopes:`classic` / `words` / `hanzi`)
46
+
47
+ ## 1. Create a Bot Token
48
+
49
+ 在 monorepo 的 `backend` 目录:
50
+
51
+ ```bash
52
+ cd backend && ../.venv/bin/python -m scripts.create_bot_token --name grok-bot
53
+ ```
54
+
55
+ 默认 scopes:`classic,words,hanzi`。仅诗文时可:
56
+
57
+ ```bash
58
+ ../.venv/bin/python -m scripts.create_bot_token --name classic-only --scopes classic
59
+ ```
60
+
61
+ stdout 仅打印一次:
62
+
63
+ ```text
64
+ BOT_TOKEN=lumio_bot_…
65
+ ```
66
+
67
+ ## 2. Cursor / Grok Bot MCP(推荐:PyPI + uvx)
68
+
69
+ 发布到 PyPI 后,无需本地 checkout:
70
+
71
+ ```json
72
+ {
73
+ "mcpServers": {
74
+ "lumio-admin": {
75
+ "command": "uvx",
76
+ "args": ["lumio-admin-mcp"],
77
+ "env": {
78
+ "LUMIO_API_BASE_URL": "https://your-lumio-host",
79
+ "LUMIO_BOT_TOKEN": "lumio_bot_…"
80
+ }
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ 固定版本示例:`"args": ["lumio-admin-mcp==0.1.0"]`。
87
+
88
+ ### 本地开发(未发布 / 改源码时)
89
+
90
+ ```json
91
+ {
92
+ "mcpServers": {
93
+ "lumio-admin": {
94
+ "command": "uv",
95
+ "args": [
96
+ "--directory",
97
+ "/ABS/PATH/mcp/lumio-admin",
98
+ "run",
99
+ "lumio-admin-mcp"
100
+ ],
101
+ "env": {
102
+ "LUMIO_API_BASE_URL": "http://127.0.0.1:7061",
103
+ "LUMIO_BOT_TOKEN": "lumio_bot_…"
104
+ }
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## 3. Publish to PyPI(维护者)
111
+
112
+ 1. 在 [PyPI](https://pypi.org) 创建账号,Account settings → API tokens → 生成 token(`pypi-…`)。
113
+ 2. 在本目录构建并发布:
114
+
115
+ ```bash
116
+ cd mcp/lumio-admin
117
+ uv build
118
+ uv publish
119
+ ```
120
+
121
+ `uv publish` 会提示输入用户名(可用 `__token__`)与 password(粘贴 API token)。也可用环境变量:
122
+
123
+ ```bash
124
+ export UV_PUBLISH_TOKEN=pypi-…
125
+ uv publish
126
+ ```
127
+
128
+ 发版前请 bump `pyproject.toml` 的 `version`,再 `uv build && uv publish`。
129
+
130
+ ## 4. Local install / debug
131
+
132
+ ```bash
133
+ cd mcp/lumio-admin
134
+ uv sync
135
+ export LUMIO_API_BASE_URL=http://127.0.0.1:7061
136
+ export LUMIO_BOT_TOKEN=lumio_bot_…
137
+ uv run lumio-admin-mcp
138
+ ```
139
+
140
+ stdio:等待 MCP host;缺环境变量时 stderr 提示并以 code 1 退出。
141
+
142
+ ## 5. Example prompts
143
+
144
+ - 「列出 online 古诗」
145
+ - 「给静夜思加注释 床=井栏边 并上线」
146
+ - 「在采实每日背诵词库新建分组 Unit 3-2,批量写入这些短语」
147
+ - 「列出字库,往某某字库的某分组批量加汉字」
148
+
149
+ ## 6. Tools
150
+
151
+ ### 诗文库(classic)
152
+
153
+ | Tool | HTTP | Notes |
154
+ |------|------|--------|
155
+ | `list_works` | GET `/api/admin/classic/works` | `kind`, `q`, `status`, `page`, `page_size` |
156
+ | `get_work` | GET `.../works/{id}` | |
157
+ | `create_work` | POST `.../works` | may set `status=online` |
158
+ | `update_work` | PATCH `.../works/{id}` | |
159
+ | `delete_work` | DELETE `.../works/{id}` | single work only |
160
+ | `list_glosses` | GET `.../works/{id}/glosses` | |
161
+ | `create_gloss` | POST `.../works/{id}/glosses` | |
162
+ | `update_gloss` | PATCH `.../glosses/{id}` | |
163
+ | `delete_gloss` | DELETE `.../glosses/{id}` | |
164
+
165
+ ### 词库(words)
166
+
167
+ | Tool | HTTP | Notes |
168
+ |------|------|--------|
169
+ | `list_word_libraries` | GET `/api/admin/word-libraries` | |
170
+ | `list_word_groups` | GET `.../{library_id}/groups` | |
171
+ | `create_word_group` | POST `.../{library_id}/groups` | 不新建词库 |
172
+ | `batch_add_words` | POST `.../groups/{group_id}/words/batch` | |
173
+
174
+ ### 字库(hanzi)
175
+
176
+ | Tool | HTTP | Notes |
177
+ |------|------|--------|
178
+ | `list_hanzi_libraries` | GET `/api/admin/hanzi-libraries` | |
179
+ | `list_hanzi_groups` | GET `.../{library_id}/groups` | |
180
+ | `create_hanzi_group` | POST `.../{library_id}/groups` | 不新建字库 |
181
+ | `batch_add_hanzis` | POST `.../groups/{group_id}/hanzis/batch` | |
182
+
183
+ ## 7. Security
184
+
185
+ - **Never commit** `LUMIO_BOT_TOKEN` / PyPI token。
186
+ - Bot Token 按 `scopes` 限制路径前缀。
187
+ - 吊销:`cd backend && ../.venv/bin/python -m scripts.revoke_bot_token --id <id>`
@@ -0,0 +1,160 @@
1
+ # lumio-admin-mcp
2
+
3
+ MCP server (stdio) that exposes Lumio **诗文库 / 词库 / 字库** admin APIs to hosts such as **Cursor** and **Grok Bot**, authenticated with a long-lived Bot Token.
4
+
5
+ **PyPI:** [`lumio-admin-mcp`](https://pypi.org/project/lumio-admin-mcp/)
6
+ **源码:** [`mcp/lumio-admin`](https://github.com/cyetstar/lumio/tree/main/mcp/lumio-admin)
7
+
8
+ **设计规格:**
9
+
10
+ - 一期诗文:`docs/superpowers/specs/2026-10-22-lumio-classic-bot-mcp-design.md`
11
+ - 二期词库/字库:`docs/superpowers/specs/2026-10-23-lumio-mcp-words-hanzi.md`
12
+
13
+ ## Requirements
14
+
15
+ - Python 3.11+
16
+ - [uv](https://docs.astral.sh/uv/)(推荐用 `uvx` 直接跑)
17
+ - 可达的 Lumio API(`LUMIO_API_BASE_URL`)
18
+ - Bot Token(scopes:`classic` / `words` / `hanzi`)
19
+
20
+ ## 1. Create a Bot Token
21
+
22
+ 在 monorepo 的 `backend` 目录:
23
+
24
+ ```bash
25
+ cd backend && ../.venv/bin/python -m scripts.create_bot_token --name grok-bot
26
+ ```
27
+
28
+ 默认 scopes:`classic,words,hanzi`。仅诗文时可:
29
+
30
+ ```bash
31
+ ../.venv/bin/python -m scripts.create_bot_token --name classic-only --scopes classic
32
+ ```
33
+
34
+ stdout 仅打印一次:
35
+
36
+ ```text
37
+ BOT_TOKEN=lumio_bot_…
38
+ ```
39
+
40
+ ## 2. Cursor / Grok Bot MCP(推荐:PyPI + uvx)
41
+
42
+ 发布到 PyPI 后,无需本地 checkout:
43
+
44
+ ```json
45
+ {
46
+ "mcpServers": {
47
+ "lumio-admin": {
48
+ "command": "uvx",
49
+ "args": ["lumio-admin-mcp"],
50
+ "env": {
51
+ "LUMIO_API_BASE_URL": "https://your-lumio-host",
52
+ "LUMIO_BOT_TOKEN": "lumio_bot_…"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ 固定版本示例:`"args": ["lumio-admin-mcp==0.1.0"]`。
60
+
61
+ ### 本地开发(未发布 / 改源码时)
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "lumio-admin": {
67
+ "command": "uv",
68
+ "args": [
69
+ "--directory",
70
+ "/ABS/PATH/mcp/lumio-admin",
71
+ "run",
72
+ "lumio-admin-mcp"
73
+ ],
74
+ "env": {
75
+ "LUMIO_API_BASE_URL": "http://127.0.0.1:7061",
76
+ "LUMIO_BOT_TOKEN": "lumio_bot_…"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## 3. Publish to PyPI(维护者)
84
+
85
+ 1. 在 [PyPI](https://pypi.org) 创建账号,Account settings → API tokens → 生成 token(`pypi-…`)。
86
+ 2. 在本目录构建并发布:
87
+
88
+ ```bash
89
+ cd mcp/lumio-admin
90
+ uv build
91
+ uv publish
92
+ ```
93
+
94
+ `uv publish` 会提示输入用户名(可用 `__token__`)与 password(粘贴 API token)。也可用环境变量:
95
+
96
+ ```bash
97
+ export UV_PUBLISH_TOKEN=pypi-…
98
+ uv publish
99
+ ```
100
+
101
+ 发版前请 bump `pyproject.toml` 的 `version`,再 `uv build && uv publish`。
102
+
103
+ ## 4. Local install / debug
104
+
105
+ ```bash
106
+ cd mcp/lumio-admin
107
+ uv sync
108
+ export LUMIO_API_BASE_URL=http://127.0.0.1:7061
109
+ export LUMIO_BOT_TOKEN=lumio_bot_…
110
+ uv run lumio-admin-mcp
111
+ ```
112
+
113
+ stdio:等待 MCP host;缺环境变量时 stderr 提示并以 code 1 退出。
114
+
115
+ ## 5. Example prompts
116
+
117
+ - 「列出 online 古诗」
118
+ - 「给静夜思加注释 床=井栏边 并上线」
119
+ - 「在采实每日背诵词库新建分组 Unit 3-2,批量写入这些短语」
120
+ - 「列出字库,往某某字库的某分组批量加汉字」
121
+
122
+ ## 6. Tools
123
+
124
+ ### 诗文库(classic)
125
+
126
+ | Tool | HTTP | Notes |
127
+ |------|------|--------|
128
+ | `list_works` | GET `/api/admin/classic/works` | `kind`, `q`, `status`, `page`, `page_size` |
129
+ | `get_work` | GET `.../works/{id}` | |
130
+ | `create_work` | POST `.../works` | may set `status=online` |
131
+ | `update_work` | PATCH `.../works/{id}` | |
132
+ | `delete_work` | DELETE `.../works/{id}` | single work only |
133
+ | `list_glosses` | GET `.../works/{id}/glosses` | |
134
+ | `create_gloss` | POST `.../works/{id}/glosses` | |
135
+ | `update_gloss` | PATCH `.../glosses/{id}` | |
136
+ | `delete_gloss` | DELETE `.../glosses/{id}` | |
137
+
138
+ ### 词库(words)
139
+
140
+ | Tool | HTTP | Notes |
141
+ |------|------|--------|
142
+ | `list_word_libraries` | GET `/api/admin/word-libraries` | |
143
+ | `list_word_groups` | GET `.../{library_id}/groups` | |
144
+ | `create_word_group` | POST `.../{library_id}/groups` | 不新建词库 |
145
+ | `batch_add_words` | POST `.../groups/{group_id}/words/batch` | |
146
+
147
+ ### 字库(hanzi)
148
+
149
+ | Tool | HTTP | Notes |
150
+ |------|------|--------|
151
+ | `list_hanzi_libraries` | GET `/api/admin/hanzi-libraries` | |
152
+ | `list_hanzi_groups` | GET `.../{library_id}/groups` | |
153
+ | `create_hanzi_group` | POST `.../{library_id}/groups` | 不新建字库 |
154
+ | `batch_add_hanzis` | POST `.../groups/{group_id}/hanzis/batch` | |
155
+
156
+ ## 7. Security
157
+
158
+ - **Never commit** `LUMIO_BOT_TOKEN` / PyPI token。
159
+ - Bot Token 按 `scopes` 限制路径前缀。
160
+ - 吊销:`cd backend && ../.venv/bin/python -m scripts.revoke_bot_token --id <id>`
@@ -0,0 +1,41 @@
1
+ [project]
2
+ name = "lumio-admin-mcp"
3
+ version = "0.1.0"
4
+ description = "MCP server for Lumio admin APIs (诗文 / 词库 / 字库) via Bot Token"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ license = "MIT"
8
+ authors = [{ name = "cyetstar" }]
9
+ keywords = ["mcp", "lumio", "admin", "bot"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Environment :: Console",
13
+ "Intended Audience :: Developers",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Topic :: Software Development :: Libraries",
20
+ ]
21
+ dependencies = [
22
+ "mcp>=1.9.0",
23
+ "httpx>=0.27.0",
24
+ "pydantic>=2.0",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://github.com/cyetstar/lumio"
29
+ Repository = "https://github.com/cyetstar/lumio"
30
+ Documentation = "https://github.com/cyetstar/lumio/tree/main/mcp/lumio-admin"
31
+ Issues = "https://github.com/cyetstar/lumio/issues"
32
+
33
+ [project.scripts]
34
+ lumio-admin-mcp = "lumio_admin_mcp.server:main"
35
+
36
+ [build-system]
37
+ requires = ["hatchling"]
38
+ build-backend = "hatchling.build"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/lumio_admin_mcp"]
@@ -0,0 +1,3 @@
1
+ """Lumio admin MCP server — Bot Token HTTP bridge for Cursor / Grok Bot."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ """Allow `python -m lumio_admin_mcp`."""
2
+
3
+ from lumio_admin_mcp.server import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,63 @@
1
+ """HTTP client for Lumio admin APIs used by MCP (classic / words / hanzi)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import httpx
8
+
9
+ CLASSIC_API_PREFIX = "/api/admin/classic"
10
+ WORDS_API_PREFIX = "/api/admin/word-libraries"
11
+ HANZI_API_PREFIX = "/api/admin/hanzi-libraries"
12
+
13
+ # Back-compat alias for tests and callers that imported API_PREFIX.
14
+ API_PREFIX = CLASSIC_API_PREFIX
15
+
16
+
17
+ class ClassicApiError(Exception):
18
+ def __init__(self, status_code: int, detail: object):
19
+ self.status_code = status_code
20
+ self.detail = detail
21
+ super().__init__(f"{status_code}: {detail}")
22
+
23
+
24
+ class ClassicApiClient:
25
+ def __init__(self, base_url: str, token: str):
26
+ self._base = base_url.rstrip("/")
27
+ self._token = token
28
+
29
+ def _full_path(self, path: str, *, prefix: str = CLASSIC_API_PREFIX) -> str:
30
+ if path.startswith("/api/admin/"):
31
+ return path
32
+ if path in ("", "/"):
33
+ return prefix
34
+ if not path.startswith("/"):
35
+ path = f"/{path}"
36
+ return f"{prefix}{path}"
37
+
38
+ async def request(
39
+ self,
40
+ method: str,
41
+ path: str,
42
+ *,
43
+ json: Any | None = None,
44
+ params: dict[str, Any] | None = None,
45
+ prefix: str = CLASSIC_API_PREFIX,
46
+ ) -> Any:
47
+ async with httpx.AsyncClient(base_url=self._base, timeout=60.0) as client:
48
+ r = await client.request(
49
+ method,
50
+ self._full_path(path, prefix=prefix),
51
+ headers={"Authorization": f"Bearer {self._token}"},
52
+ json=json,
53
+ params=params,
54
+ )
55
+ if r.status_code >= 400:
56
+ try:
57
+ detail = r.json().get("detail", r.text)
58
+ except Exception:
59
+ detail = r.text
60
+ raise ClassicApiError(r.status_code, detail)
61
+ if r.status_code == 204 or not r.content:
62
+ return None
63
+ return r.json()