内容
# ohneben's LearnWorlds MCP
[](https://github.com/ohneben/Learnworlds-MCP/actions/workflows/ci.yml)
[](https://github.com/ohneben/Learnworlds-MCP/actions/workflows/docker-publish.yml)
[](./LICENSE.md)
使用 AI 助手(如 **Claude**、**Cursor** 等)以纯语言运行您的 [LearnWorlds](https://www.learnworlds.com/) 学校。
此 [模型上下文协议](https://modelcontextprotocol.io) 服务器暴露了 **LearnWorlds 公共 API** — 所有 **94 个端点**,直接从 OpenAPI 规范生成到 MCP 工具中。每个工具都经过 **安全分类**(🟢 只读 / 🟡 写入 / 🔴 销毁),以便您的助手在调用之前知道操作的作用。
## 为什么您需要它
一些 MCP 服务器只是转发 API。此服务器旨在 **安全地交给 LLM** 并 **易于实际运行**:
| 您获得的功能 | 原因 |
| --- | --- |
| **所有 94 个端点,按规范驱动** | 完整涵盖课程、用户、注册、付款、订阅、优惠券、证书、席位、社区和报告 — 没有精选或遗漏。 |
| **每个工具都经过安全分类** 🟢 / 🟡 / 🔴 | 每个工具描述顶部的横幅告诉模型它到底是读取、创建、更新还是删除 — 在执行前。 |
| **机器可读的 MCP 注释** (`readOnlyHint`、`destructiveHint`) | 尊重注释的主机(包括 Claude)可以自动信任读取并在销毁之前要求确认。 |
| **自动重试和退避** | 暂时的 `429` / `5xx` 响应会重试并带有抖动的指数退避,同时遵守服务器的 `Retry-After` 头。 |
| **内置速率限制** | 在 LearnWorlds 的 **30 请求 / 10 秒** 限制下自我限制,以防止突发请求导致 `429` 错误。 |
| **每个请求的超时** | 挂起的上游调用会中止并重试,而不是冻结服务器。 |
| **两种传输方式:stdio 和 Streamable HTTP** | 在 Claude Desktop 上本地使用,或运行一个始终在线的服务器,任意数量的 MCP 客户端都可以通过 HTTP 访问。 |
| **Docker + docker-compose,健康检查,自动重启** | 开箱即用的生产风格部署:`docker compose up` 然后保持运行。 |
| **可选的 bearer-token 身份验证** 在 HTTP 端点上 | 在服务器可访问范围超出 localhost 之前,将其置于共享密钥之下。 |
| **您的密钥永远不会到达模型** | 凭证存储在服务器的环境中,并在每个请求上注入 — 助手只会看到工具输入和 API 响应。 |
| **直接替换规范更新** | LearnWorlds 发布了新的 YAML?替换一个文件并重建 — 新的端点会自动成为新的工具,无需代码更改。 |
## 它是如何比较的
在编写本文时,这似乎是唯一专门的 LearnWorlds MCP 服务器。
您也可以将一个通用的 OpenAPI→MCP 包装器指向规范 — 但这样会失去以下功能:
| 功能 | **此项目** | 通用 OpenAPI→MCP 包装器\* |
| --- | :---: | :---: |
| 所有 94 个 LearnWorlds 端点作为工具 | ✅ | ✅ |
| 每个工具的安全类别 + 横幅 | ✅ | ❌ |
| `readOnlyHint` / `destructiveHint` MCP 注释 | ✅ | ➖ |
| `$ref` 解引用 + 递归安全模式 | ✅ | ➖ |
| `429` / `5xx` 自动重试(遵守 `Retry-After`) | ✅ | ❌ |
| 客户端速率限制(保持在 30 请求 / 10 秒) | ✅ | ❌ |
| 每个请求的超时和中止 | ✅ | ➖ |
| `stdio` 传输 | ✅ | ✅ |
| **Streamable-HTTP 传输** | ✅ | ➖ |
| **Docker + docker-compose**,健康检查,自动重启 | ✅ | ❌ |
| **可选的 bearer-token 身份验证** 在端点上 | ✅ | ❌ |
| 服务器端注入凭证,永不发送给模型 | ✅ | ➖ |
| 许可证 | MIT | 各不相同 |
<sub>\* 通用 OpenAPI→MCP 包装器将任何 Swagger/OpenAPI 规范转换为 MCP 工具。它们可以访问相同的端点,但所有操作都被视为相同 — 没有安全类别、没有弹性、没有部署故事,也没有针对实时学校数据的防护措施。 "➖" = 因工具而异 / 不保证。 2026 年 7 月快照。</sub>
## 您可以使用它做什么
连接后,询问您的助手:
- “列出本月注册的前 10 名用户。”
- “为 jane@example.com 创建一个用户,并将她加入 ‘Pro’ 捆绑包。”
- “显示本月的付款和总收入。”
- “哪些用户尚未完成 ‘Onboarding’ 课程?”
- “为年度订阅计划创建一个 20% 折扣的优惠券。”
- “提取我们前 5 名课程的完成分析。”
工具会自动从 API 生成并分为 🟢 只读、🟡 写入和 🔴 销毁 — 以便表现良好的主机可以区别对待每个组。
## 它是如何工作的
```
Claude / Cursor / 任意 MCP 客户端 ──MCP──► 此服务器 ──HTTPS──► LearnWorlds API(您的学校)
```
服务器解析捆绑的 OpenAPI 规范为 MCP 工具(解析 `$ref`s 并防止递归模式),为每个工具标记其安全类别,并在每个传出请求上注入您的 bearer 令牌和 `Lw-Client` 头。您的凭证存储在服务器的环境中 — 模型永远不会看到或处理它们。
## 要求
- 一个 **具有 API 访问权限的 LearnWorlds 学校** — 一个 **访问令牌** 和一个 **客户端 ID**(管理员 → 设置 → 集成 → 开发者),以及您的学校的 API 基本 URL。参见 [获取您的 API 凭证](#获取您的-api-凭证)。
- **Docker**(适用于快速启动的 Docker Desktop macOS/Windows)— 或 **Node.js ≥ 18** 以 [从源代码运行](#从源代码运行-stdio-无-docker)。
## 快速启动(Docker)
**1. 添加您的凭证。** 复制示例配置并填写:
```bash
cp .env.example .env
# 编辑 .env → 设置 LEARNWORLDS_BASE_URL、LEARNWORLDS_API_TOKEN、LEARNWORLDS_CLIENT_ID
# → 将 MCP_SHARED_TOKEN 设置为一个长随机字符串(如果在 localhost 之外可访问)
```
**2. 启动服务器:**
```bash
docker compose up -d --build
```
捆绑的 `docker-compose.yml` 仅绑定到 `127.0.0.1:8765`,因此服务器可以从您的机器访问,但不能从网络访问。
**3. 确认它正在运行:**
```bash
curl -s http://localhost:8765/health
# → {"status":"ok","server":"learnworlds-mcp","tools":94}
```
**4. 连接您的 MCP 客户端。** MCP 端点是 `http://localhost:8765/mcp`。
- **Claude Desktop** — 添加一个 **自定义连接器**(设置 → 连接器)指向该 URL,或使用 [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) 在本地桥接。 在您的配置中添加以下内容,然后完全退出并重新打开应用程序:
```json
{
"mcpServers": {
"learnworlds": {
"command": "npx",
"args": [
"mcp-remote",
"http://localhost:8765/mcp",
"--header", "Authorization: Bearer YOUR_MCP_SHARED_TOKEN"
]
}
}
}
```
(如果您将 `MCP_SHARED_TOKEN` 留空,则可以省略 `--header` 行。)
- **Claude Code** — 一个命令:
```bash
claude mcp add --transport http learnworlds http://localhost:8765/mcp
```
- **Claude Cowork** — 与 Claude Code 共享 MCP 配置,因此上述命令也使工具在那里可用。
### 更喜欢预构建的镜像?
每次推送到 `main` 都会将一个现成的镜像发布到 GitHub 容器注册表中,因此您可以完全跳过本地构建:
```bash
docker run -d --name learnworlds-mcp -p 127.0.0.1:8765:8765 --env-file .env \
ghcr.io/ohneben/learnworlds-mcp:latest
```
## 获取您的 API 凭证
1. 登录到您的 LearnWorlds 学校管理员。
2. 进入 **设置 → 集成 → 开发者**(API 屏幕)。
3. 复制您的 **访问令牌** → `LEARNWORLDS_API_TOKEN`,和您的 **客户端 ID** → `LEARNWORLDS_CLIENT_ID`。
4. 将 `LEARNWORLDS_BASE_URL` 设置为您的学校的 API 基本 URL:`https://<your-school>.learnworlds.com/admin/api`。如果您的学校使用自定义域,请使用该主机(例如 `https://academy.example.com/admin/api`)。
将这三个放入 `.env`。服务器在每个请求上注入它们,因此您的助手永远不会看到它们。
## 配置
所有内容都设置在 `.env` 中(从 `.env.example` 复制):
| 变量 | 必需 | 默认值 | 描述 |
|---|---|---|---|
| `LEARNWORLDS_BASE_URL` | ✅ | — | 您学校的 API 基本 URL |
| `LEARNWORLDS_API_TOKEN` | ✅ | — | Bearer 访问令牌 |
| `LEARNWORLDS_CLIENT_ID` | ✅ | — | 作为 `Lw-Client` 头发送 |
| `MCP_TRANSPORT` | — | `stdio` | `stdio` 或 `http`(Docker 镜像默认为 `http`) |
| `PORT` | — | `8765` | HTTP 监听端口 |
| `HOST` | — | `0.0.0.0` | HTTP 绑定地址 |
| `MCP_HTTP_PATH` | — | `/mcp` | HTTP MCP 路由 |
| `MCP_SHARED_TOKEN` | — | _(关闭)_ | 需要 `/mcp` 上的 `Authorization: Bearer <token>` |
| `LEARNWORLDS_MAX_REQUESTS` | — | `25` | 客户端请求每窗口(`0` 禁用限制) |
| `LEARNWORLDS_RATE_WINDOW_MS` | — | `10000` | 速率限制窗口(毫秒) |
| `LEARNWORLDS_MAX_RETRIES` | — | `3` | `429` / `5xx` / 网络错误重试 |
| `LEARNWORLDS_TIMEOUT_MS` | — | `30000` | 每个尝试的请求超时 |
| `LEARNWORLDS_OPENAPI_PATH` | — | _(捆绑)_ | 加载不同的 OpenAPI YAML |
更改 `.env` 后,使用 `docker compose up -d --force-recreate` 重启。
## 工具安全类别
每个工具的描述以以下横幅之一开头,并带有匹配的 [MCP 注释](https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations):
| 横幅 | 计数 | `readOnlyHint` | `destructiveHint` | 含义 |
|---|:---:|:---:|:---:|---|
| 🟢 **只读** | 59 | `true` | `false` | 仅获取数据。安全。 |
| 🟡 **写入 · 创建数据** | — | `false` | `false` | `POST` — 创建记录(非幂等;可能会重复)。 |
| 🟡 **写入 · 更新数据** | — | `false` | `false` | `PUT` — 修改现有记录(幂等)。 |
| 🔴 **销毁 · 删除** | 8 | `false` | `true` | `DELETE` — 删除记录。确认后。 |
🟡 写入工具共 **27** 个(17 个创建 + 10 个更新)。尊重注释的主机(包括 Claude)可以为 `destructiveHint` 工具要求确认,并自动信任 `readOnlyHint` 工具。
> 运行 `npm run list-tools`(无需凭证)以打印完整的目录和每个类别的计数。
<details>
<summary><strong>按区域划分的覆盖率(读取 / 写入 / 删除)</strong></summary>
| 区域 | 🟢 读取 | 🟡 写入 | 🔴 删除 |
|---|:---:|:---:|:---:|
| 用户 | 10 | 7 | 1 |
| 联盟 | 7 | 1 | 0 |
| 社区 | 6 | 3 | 2 |
| 课程 | 5 | 3 | 0 |
| 促销(优惠券) | 4 | 3 | 0 |
| 报告 | 4 | 0 | 0 |
| 付款 | 3 | 0 | 0 |
| 多席位 | 3 | 3 | 2 |
| 用户组 | 3 | 3 | 2 |
| 捆绑包 | 2 | 0 | 0 |
| 评估 | 2 | 1 | 0 |
| 订阅计划 | 2 | 0 | 0 |
| 证书 | 1 | 1 | 1 |
| 用户订阅 | 1 | 0 | 0 |
| 用户角色 | 1 | 0 | 0 |
| 分期付款 | 1 | 0 | 0 |
| 线索 | 1 | 0 | 0 |
| 日历 | 1 | 0 | 0 |
| 事件日志 | 1 | 0 | 0 |
| 异步操作 | 1 | 0 | 0 |
| 更新用户进度 | 0 | 2 | 0 |
| **总数** | **59** | **27** | **8** |
</details>
## 从源代码运行(stdio,无 Docker)
更喜欢经典的 stdio 模式用于 Claude Desktop?在本地构建:
```bash
npm install
npm run build
```
然后在 `claude_desktop_config.json` 中指向编译后的入口点:
```json
{
"mcpServers": {
"learnworlds": {
"command": "node",
"args": ["/ABSOLUTE/PATH/Learnworlds-MCP/dist/index.js"],
"env": {
"MCP_TRANSPORT": "stdio",
"LEARNWORLDS_BASE_URL": "https://your-school.learnworlds.com/admin/api",
"LEARNWORLDS_API_TOKEN": "your-access-token",
"LEARNWORLDS_CLIENT_ID": "your-client-id"
}
}
}
}
```
## 保持规范更新
捆绑的 `spec/learnworlds-openapi.yaml` 是工具的真实来源。要刷新到较新的 API 版本,请将其替换(或将 `LEARNWORLDS_OPENAPI_PATH` 指向它)并重建:
```bash
docker compose up -d --build # Docker
# 或
npm run build # 从源代码
```
新路径会自动成为新工具 — 无需代码更改。
## 开发
```bash
npm install
npm run build # 编译 TypeScript → dist/
npm test # 运行 Vitest 套件
npm run list-tools # 打印分类的工具目录(无需凭证)
```
CI 构建并测试每次推送到 Node 20 和 22;推送到 `main` 还会发布 Docker 镜像到 GitHub 容器注册表。
## 注释和约定
- **传输方式**:`MCP_TRANSPORT=stdio`(默认)用于本地启动器;`MCP_TRANSPORT=http` 用于始终在线的 Streamable-HTTP 服务器,Docker 镜像运行。
- **分页**:大多数 `get` 工具接受 `page`(和端点特定的过滤器);LearnWorlds 对列表响应分页(通常每页 20–50 项)。
- **速率限制**:LearnWorlds 允许 30 请求 / 10 秒;服务器在 `LEARNWORLDS_MAX_REQUESTS` 每 `LEARNWORLDS_RATE_WINDOW_MS`(默认为 25 / 10 秒)并重试任何收到的 `429`。
## 安全
- 您的 API 凭证仅存储在 `.env` 中,该文件被 git 忽略。 **永远不要提交真实的密钥。** 如果令牌泄漏,请在 **LearnWorlds 管理员 → 设置 → 集成 → 开发者(API)** 中旋转它。
- HTTP 端点默认未认证(在 localhost 上没问题)。要将其公开到您的机器之外,请设置 `MCP_SHARED_TOKEN` 并将其作为 `Authorization: Bearer <token>` 头发送 — 理想情况下在 TLS 后。
参见 [SECURITY.md](./SECURITY.md) 了解完整的政策以及如何报告漏洞。
## 致谢和许可证
[LearnWorlds](https://www.learnworlds.com/) 的非官方社区集成;不属于 LearnWorlds 关联或支持。构建在 [模型上下文协议](https://modelcontextprotocol.io) 上。 [MIT 许可证](./LICENSE.md)。