--- name: educoder-skill 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` token。调用端每次建立 MCP 连接时,都从环境变量 `EDUCODER_AUTHORIZATION` 读取 token,并放到请求头里。 ### Authorization 配置 在运行 OpenClaw 或 MCP 客户端的进程环境中配置: ```bash export EDUCODER_AUTHORIZATION="xxxxx" ``` MCP 客户端每次连接时应读取环境变量并注入: ```python headers = { "Authorization": os.getenv("EDUCODER_AUTHORIZATION", "").strip() } ``` `Authorization` 的 header 值直接传 token 原文,不要拼接 `Bearer ` 前缀。例如: ```http Authorization: xxxxx ``` 不要传成: ```http Authorization: Bearer xxxxx ``` 然后在创建 MCP 连接时透传: ```python async with streamable_http_client(self.server_url, headers=headers or None) as (read, write, _): yield read, write ``` > `Authorization` 走 MCP 连接层请求头,不放在工具参数里。 ### 多会话共享 Token 多个会话共用同一个 `EDUCODER_AUTHORIZATION`。不要让用户在每个会话里单独输入 token,也不要把 token 放到工具参数里。 正确做法是: 1. 在 OpenClaw 运行环境中配置 `EDUCODER_AUTHORIZATION` 2. 每次创建 MCPClient 或建立 MCP 连接时,从 `os.getenv("EDUCODER_AUTHORIZATION")` 读取最新值 3. 把读取到的 token 原文放进 MCP transport headers 的 `Authorization`,不要添加 `Bearer ` 前缀 4. 多个会话可以分别创建 MCPClient 实例,但认证 token 都来自同一个环境变量 ### 401 Token 过期处理 如果调用 MCP 工具或后端接口时返回 `401 Unauthorized`,说明当前 `EDUCODER_AUTHORIZATION` 已过期或无效。 处理流程: 1. 停止继续使用当前 token 重试 2. 提示用户重新输入新的 `Authorization` 3. 将用户输入的新 token 更新到环境变量 `EDUCODER_AUTHORIZATION` 4. 重新创建 MCP 连接,并从环境变量读取最新 token 5. 使用新 token 重新发起本次工具调用 示例提示: ```text 当前 Educoder 授权已过期,请重新输入 Authorization token。 ``` 更新环境变量示例: ```bash export EDUCODER_AUTHORIZATION="new-token" ``` > 更新 token 后必须重新建立 MCP 连接,不能复用已经携带旧 headers 的连接。 ### OpenClaw 接入建议 如果使用 OpenClaw,推荐按下面的方式接入: 1. 安装 skill 时,MCP 服务地址填写:`http://47.98.32.66:48000/mcp` 2. 在 OpenClaw 运行环境中配置 `EDUCODER_AUTHORIZATION` 3. 当前用户发起工具调用时,后端创建 MCPClient 4. MCPClient 建连时从环境变量读取 `EDUCODER_AUTHORIZATION` 5. MCPClient 把读取到的 token 原文放进 MCP headers,不要添加 `Bearer ` 前缀 6. 如果调用返回 `401 Unauthorized`,提示用户重新输入 token,更新 `EDUCODER_AUTHORIZATION` 后重新建连并重试 参考流程: ```python import os tool_server_url = "http://47.98.32.66:48000/mcp" client = MCPClient( server_url=tool_server_url, ) tool_result = await client.call_tool(tool_name, tool_arguments) ``` MCPClient 示例: ```python class MCPClient: def __init__(self, server_url: str): self.server_url = server_url def _build_headers(self) -> dict[str, str]: headers = {} authorization = os.getenv("EDUCODER_AUTHORIZATION", "").strip() if authorization: headers["Authorization"] = authorization return headers ``` 不推荐的做法: - 让用户在每个会话里重复输入 `Authorization` - 把 `Authorization` 放到工具参数里 - 给 `Authorization` 自动拼接 `Bearer ` 前缀 - 在进程启动后只读取一次 token,导致环境变量更新后无法生效 ## 触发场景 ### 意图 → 工具 速查表 | 用户意图 | 调用工具 | |----------|---------| | 查询当前用户基本信息 | `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 服务时,请从环境变量 `EDUCODER_AUTHORIZATION` 读取 token 原文,并在 transport headers 中传 `Authorization`,不要添加 `Bearer ` 前缀 --- ### 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 os import httpx from mcp import ClientSession from mcp.client.streamable_http import streamable_http_client async def main(): headers = { "Authorization": os.getenv("EDUCODER_AUTHORIZATION", "").strip() } async with httpx.AsyncClient(headers=headers) 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` 应从环境变量 `EDUCODER_AUTHORIZATION` 读取 token 原文,通过 MCP 连接层 headers 传入,不放在工具参数里,也不要添加 `Bearer ` 前缀 - 调用接口返回 `401 Unauthorized` 时,视为 token 过期或无效,应要求用户重新输入 token,并更新到环境变量 `EDUCODER_AUTHORIZATION` - 当前工具返回空 `data` 数组属于预期行为,因为外部接口尚未接入 - 后续新增或调整工具时,需要同步更新本文件