You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
8.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
name: educoder-mcp
description: 提供 Educoder 用户与课堂相关查询能力,当前支持用户基本信息、课堂列表、课堂作业列表、作业学生列表、课堂考试列表、考试学生列表查询。
---
## MCP 服务地址
- 线上地址:`http://47.98.32.66:48000/mcp`
- 本地开发:`http://localhost:8001/mcp`
> 使用 `streamable-http` 模式连接,不使用 SSE。
## 接入方式
- 客户端应使用 `streamable-http` MCP 客户端
- 使用 OpenClaw 安装本 skill 时MCP 服务地址直接填写线上地址:`http://47.98.32.66:48000/mcp`
- 不要使用 `sse_client`
- 本地开发端口默认是 `8001`,线上端口使用 `48000`
- MCP 路径默认是 `/mcp`
## 前置配置
接入 OpenClaw 时,需要让用户提供 `Authorization`,并在 MCP 客户端建立连接时放到请求头里。
### 单用户本地调试
本地单用户调试时,可以临时使用环境变量保存:
```bash
export EDUCODER_AUTHORIZATION="xxxxx"
```
> 这种方式只适合本地单用户调试,不适合多用户线上场景。
MCP 客户端连接时应注入:
```python
headers = {
"Authorization": os.getenv("EDUCODER_AUTHORIZATION", "").strip()
}
```
然后在创建 MCP 连接时透传:
```python
async with streamable_http_client(self.server_url, headers=headers or None) as (read, write, _):
yield read, write
```
> `Authorization` 走 MCP 连接层请求头,不放在工具参数里。
### 多用户隔离
多用户场景下,不要把 `Authorization` 放在服务进程级环境变量里,否则所有用户会共用同一份认证信息。
正确做法是:
1. 让每个用户单独输入自己的 `Authorization`
2. 调用端把这个值绑定到当前用户会话
3. 创建当前用户专属的 MCPClient 实例
4. 在 MCP 建连时,把当前用户自己的 `Authorization` 放到请求头里
示例:
```python
client = MCPClient(
server_url=tool_server_url,
authorization=current_user_authorization,
)
```
然后在建连时动态透传:
```python
headers = {}
if self.authorization:
headers["Authorization"] = self.authorization
async with streamable_http_client(self.server_url, headers=headers or None) as (read, write, _):
yield read, write
```
> 核心原则:`Authorization` 必须按用户动态传递,不能做成所有人共享的全局配置。
### OpenClaw 接入建议
如果使用 OpenClaw推荐按下面的方式接入
1. 安装 skill 时MCP 服务地址填写:`http://47.98.32.66:48000/mcp`
2. 用户在界面上输入自己的 `Authorization`
3. OpenClaw 后端把该值保存到当前用户自己的会话上下文
4. 当前用户发起工具调用时,后端读取这个用户自己的 `Authorization`
5. 用这个值创建当前用户专属的 MCPClient
6. MCPClient 建连时把 `Authorization` 放进 MCP headers
参考流程:
```python
tool_server_url = "http://47.98.32.66:48000/mcp"
current_user_authorization = session_store.get(current_user_id, "educoder_authorization")
client = MCPClient(
server_url=tool_server_url,
authorization=current_user_authorization,
)
tool_result = await client.call_tool(tool_name, tool_arguments)
```
MCPClient 示例:
```python
class MCPClient:
def __init__(self, server_url: str, authorization: str | None = None):
self.server_url = server_url
self.authorization = authorization
def _build_headers(self) -> dict[str, str]:
headers = {}
if self.authorization:
headers["Authorization"] = self.authorization
return headers
```
不推荐的做法:
-`Authorization` 写进服务端 `.env`
-`Authorization` 写成所有用户共享的全局变量
- 让多个用户复用同一个 MCPClient 实例
## 触发场景
### 意图 → 工具 速查表
| 用户意图 | 调用工具 |
|----------|---------|
| 查询当前用户基本信息 | `educoder_user_info` |
| 查询当前用户的课堂列表 | `educoder_user_course_list` |
| 查询某个课堂中的作业列表 | `educoder_course_homeworks` |
| 查询某次作业下的学生列表 | `educoder_homework_student_works` |
| 查询某个课堂中的考试列表 | `educoder_course_exercises` |
| 查询某场考试下的学生列表 | `educoder_exercise_users` |
### 不要触发的情况
- 与课堂、作业、考试数据查询无关的问题
- 需要新增、修改、删除数据的写操作
- 纯闲聊场景
### 组合调用示例
> 用户说:“帮我查一下我有哪些课堂,再看看某个课堂里的作业和考试”
依次调用:
1. `educoder_user_info`
2. `educoder_user_course_list`
3. `educoder_course_homeworks`
4. `educoder_course_exercises`
> 用户说:“帮我看一下这次作业和这场考试的学生列表”
依次调用:
1. `educoder_homework_student_works`
2. `educoder_exercise_users`
---
## 工具详细说明
### 全局规则
- 当前工具都是查询类工具,没有写操作
- 调用时必须传入完整的结构化 JSON 参数对象
- 参数名必须严格匹配工具定义
- 当前查询逻辑还是占位实现,后续会接外部 HTTP 接口
- `course_id``exercise_id` 当前按字符串传入
- 客户端连接 MCP 服务时,请在 transport headers 中传 `Authorization`
---
### 1. `educoder_user_info`
获取当前用户基本信息。
**参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `school` | 否 | 可选的 school 查询参数 |
**典型问法:**
- “查询当前用户信息”
- “我是谁”
- “获取我的基本资料”
---
### 2. `educoder_user_course_list`
获取当前用户课堂列表。
**参数:** 无
**典型问法:**
- “查询我有哪些课堂”
- “我的课堂列表”
- “获取当前用户的课堂信息”
**返回字段:**
- `status`
- `message`
- `data`
- `size`
---
### 3. `educoder_course_homeworks`
获取课堂中的作业列表。
**参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `course_id` | 是 | 课堂 ID |
**典型问法:**
- “查询课堂中的作业列表”
- “这个课堂有哪些作业”
- “帮我看一下课堂作业”
---
### 4. `educoder_homework_student_works`
获取某次作业下的学生列表。
**参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `exercise_id` | 是 | 作业 ID |
**典型问法:**
- “查询某次作业下的学生列表”
- “这次作业有哪些学生提交了”
- “帮我看一下作业学生情况”
---
### 5. `educoder_course_exercises`
获取课堂中的考试列表。
**参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `course_id` | 是 | 课堂 ID |
**典型问法:**
- “查询课堂中的考试列表”
- “这个课堂有哪些考试”
- “帮我看一下课堂考试”
---
### 6. `educoder_exercise_users`
获取某场考试下的学生列表。
**参数:**
| 参数 | 必填 | 说明 |
|------|------|------|
| `exercise_id` | 是 | 考试 ID |
**典型问法:**
- “查询某场考试下的学生列表”
- “这场考试有哪些学生”
- “帮我看一下考试参与学生”
---
## 客户端调用示例
```python
import httpx
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
async def main():
async with httpx.AsyncClient(headers={"Authorization": "demo-token"}) as http_client:
async with streamable_http_client("http://47.98.32.66:48000/mcp", http_client=http_client) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print([tool.name for tool in tools.tools])
result = await session.call_tool("educoder_user_info", {})
print(result)
```
## 注意事项
- 当前服务使用 `streamable-http`,不是 SSE
- 如果客户端使用了 `sse_client`,将无法正常调用当前服务
- `Authorization` 应通过 MCP 连接层 headers 传入,不放在工具参数里
- 当前工具返回空 `data` 数组属于预期行为,因为外部接口尚未接入
- 后续新增或调整工具时,需要同步更新本文件