|
|
|
|
@ -18,21 +18,35 @@ description: 提供 Educoder 用户与课堂相关查询能力,当前支持用
|
|
|
|
|
- 本地开发端口默认是 `8001`,线上端口使用 `48000`
|
|
|
|
|
- MCP 路径默认是 `/mcp`
|
|
|
|
|
|
|
|
|
|
## 安装完成提示
|
|
|
|
|
|
|
|
|
|
安装完成后,面向用户只介绍当前 skill 提供的功能,不展示 MCP 地址、配置文件路径、token 保存方式、headers 规则等接入细节。
|
|
|
|
|
|
|
|
|
|
推荐展示文案:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
Educoder skill 已安装完成。当前支持查询:
|
|
|
|
|
- 当前用户基本信息
|
|
|
|
|
- 当前用户的课堂列表
|
|
|
|
|
- 课堂中的作业列表
|
|
|
|
|
- 某次作业下的学生列表
|
|
|
|
|
- 课堂中的考试列表
|
|
|
|
|
- 某场考试下的学生列表
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 前置配置
|
|
|
|
|
|
|
|
|
|
接入 OpenClaw 时,多个会话使用同一个 `Authorization` token。第一次安装 skill 时,把 MCP 服务地址和 token 一起写到固定的 OpenClaw MCP 配置文件 `.openclaw/educoder-skill/.mcp.json` 中;后续调用 MCP 时,也只从 `.openclaw/educoder-skill/.mcp.json` 读取 token 并放到请求头里。
|
|
|
|
|
接入 OpenClaw 时,使用单用户 `Authorization` token。第一次安装 skill 时,把 MCP 服务地址和 token 一起写到固定的 OpenClaw MCP 配置文件;后续调用 MCP 时,从该配置文件读取 token 并放到请求头里。
|
|
|
|
|
|
|
|
|
|
### Authorization 配置
|
|
|
|
|
|
|
|
|
|
安装 skill 时要求把用户输入 Educoder `Authorization` token 写入 MCP 配置文件 `.openclaw/educoder-skill/.mcp.json`。
|
|
|
|
|
|
|
|
|
|
配置文件路径必须固定为:
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
.openclaw/educoder-skill/.mcp.json
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
不要写入 `.openclaw/mcp.json` 或 `.openclaw/config/mcp.json`,否则新开会话时可能读取不到配置。
|
|
|
|
|
安装 skill 时写入 MCP 服务地址,并将用户输入的`EDUCODER_AUTHORIZATION` token写入该配置文件。
|
|
|
|
|
|
|
|
|
|
MCP 配置示例:
|
|
|
|
|
|
|
|
|
|
@ -82,9 +96,9 @@ async with streamable_http_client(self.server_url, headers=headers or None) as (
|
|
|
|
|
|
|
|
|
|
> `Authorization` 走 MCP 连接层请求头,不放在工具参数里。
|
|
|
|
|
|
|
|
|
|
### 多会话共享 Token
|
|
|
|
|
### 单用户 Token
|
|
|
|
|
|
|
|
|
|
多个会话共用 `.openclaw/educoder-skill/.mcp.json` 中的同一个 `Authorization` token。安装后不需要每个会话重复输入 token;每次调用 MCP 时读取配置里的 `headers.Authorization` 即可。
|
|
|
|
|
当前 skill 使用单用户 token。安装后不需要每个会话重复输入 token;每次调用 MCP 时读取配置里的 `headers.Authorization` 即可。
|
|
|
|
|
|
|
|
|
|
### 401 Token 过期处理
|
|
|
|
|
|
|
|
|
|
@ -94,7 +108,7 @@ async with streamable_http_client(self.server_url, headers=headers or None) as (
|
|
|
|
|
|
|
|
|
|
1. 停止继续使用当前 token 重试
|
|
|
|
|
2. 提示用户重新输入新的 `Authorization`
|
|
|
|
|
3. 将用户输入的新 token 写入 `.openclaw/educoder-skill/.mcp.json`
|
|
|
|
|
3. 将用户输入的新 token 写入 MCP 配置文件中的 `headers.Authorization`
|
|
|
|
|
4. 重新创建 MCP 连接,并从 MCP 配置读取最新 token
|
|
|
|
|
5. 使用新 token 重新发起本次工具调用
|
|
|
|
|
|
|
|
|
|
@ -112,7 +126,7 @@ async with streamable_http_client(self.server_url, headers=headers or None) as (
|
|
|
|
|
|
|
|
|
|
1. 安装 skill 时,MCP 服务地址填写:`http://47.98.32.66:48000/mcp`
|
|
|
|
|
2. 安装 skill 时,要求用户输入 Educoder `Authorization` token
|
|
|
|
|
3. OpenClaw 将 MCP 地址和 token 写入 `.openclaw/educoder-skill/.mcp.json`,其中 token 放到 `headers.Authorization`
|
|
|
|
|
3. OpenClaw 将 MCP 地址和 token 写入 MCP 配置文件,其中 token 放到 `headers.Authorization`
|
|
|
|
|
4. 当前用户发起工具调用时,后端创建 MCPClient
|
|
|
|
|
5. MCPClient 建连时从 MCP 配置读取 token
|
|
|
|
|
6. MCPClient 把读取到的 token 原文放进 MCP headers,不要添加 `Bearer ` 前缀
|
|
|
|
|
@ -124,8 +138,9 @@ async with streamable_http_client(self.server_url, headers=headers or None) as (
|
|
|
|
|
tool_server_url = "http://47.98.32.66:48000/mcp"
|
|
|
|
|
|
|
|
|
|
mcp_config = openclaw_mcp_config.get("educoder-mcp")
|
|
|
|
|
token = mcp_config["headers"]["Authorization"]
|
|
|
|
|
|
|
|
|
|
client = MCPClient(mcp_config=mcp_config)
|
|
|
|
|
client = MCPClient(mcp_config=mcp_config, authorization=token)
|
|
|
|
|
|
|
|
|
|
tool_result = await client.call_tool(tool_name, tool_arguments)
|
|
|
|
|
```
|
|
|
|
|
@ -134,15 +149,14 @@ MCPClient 示例:
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
class MCPClient:
|
|
|
|
|
def __init__(self, mcp_config: dict):
|
|
|
|
|
def __init__(self, mcp_config: dict, authorization: str):
|
|
|
|
|
self.server_url = mcp_config["url"]
|
|
|
|
|
self.mcp_config = mcp_config
|
|
|
|
|
self.authorization = authorization
|
|
|
|
|
|
|
|
|
|
def _build_headers(self) -> dict[str, str]:
|
|
|
|
|
headers = {}
|
|
|
|
|
authorization = self.mcp_config.get("headers", {}).get("Authorization", "").strip()
|
|
|
|
|
if authorization:
|
|
|
|
|
headers["Authorization"] = authorization
|
|
|
|
|
if self.authorization:
|
|
|
|
|
headers["Authorization"] = self.authorization
|
|
|
|
|
return headers
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
@ -152,7 +166,7 @@ class MCPClient:
|
|
|
|
|
- 把 `Authorization` 放到工具参数里
|
|
|
|
|
- 给 `Authorization` 自动拼接 `Bearer ` 前缀
|
|
|
|
|
- 把 token 写入 OpenClaw `.env` 后要求用户重启才能生效
|
|
|
|
|
- 每个会话都重复要求用户输入 token
|
|
|
|
|
- 把 token 写到其他位置,导致新会话读取不到 `headers.Authorization`
|
|
|
|
|
|
|
|
|
|
## 触发场景
|
|
|
|
|
|
|
|
|
|
@ -318,7 +332,7 @@ from mcp.client.streamable_http import streamable_http_client
|
|
|
|
|
|
|
|
|
|
async def main(openclaw_mcp_config):
|
|
|
|
|
mcp_config = openclaw_mcp_config.get("educoder-mcp")
|
|
|
|
|
token = mcp_config.get("headers", {}).get("Authorization", "").strip()
|
|
|
|
|
token = mcp_config["headers"]["Authorization"]
|
|
|
|
|
|
|
|
|
|
headers = {
|
|
|
|
|
"Authorization": token
|
|
|
|
|
@ -340,9 +354,8 @@ async def main(openclaw_mcp_config):
|
|
|
|
|
|
|
|
|
|
- 当前服务使用 `streamable-http`,不是 SSE
|
|
|
|
|
- 如果客户端使用了 `sse_client`,将无法正常调用当前服务
|
|
|
|
|
- `Authorization` 应从 OpenClaw MCP 配置读取 token 原文,通过 MCP 连接层 headers 传入,不放在工具参数里,也不要添加 `Bearer ` 前缀
|
|
|
|
|
- 调用接口返回 `401 Unauthorized` 时,视为 token 过期或无效,应要求用户重新输入 token,并更新 `.openclaw/educoder-skill/.mcp.json` 中的 `headers.Authorization`
|
|
|
|
|
- 新开会话直接从 `.openclaw/educoder-skill/.mcp.json` 读取 `headers.Authorization`
|
|
|
|
|
- 调用接口返回 `401 Unauthorized` 时,视为 token 过期或无效,应要求用户重新输入 token,并更新 MCP 配置文件中的 `headers.Authorization`
|
|
|
|
|
- 新开会话直接从 MCP 配置文件读取 `headers.Authorization`
|
|
|
|
|
- MCP 配置文件路径固定为 `.openclaw/educoder-skill/.mcp.json`,不要写入或读取 `.openclaw/mcp.json`、`.openclaw/config/mcp.json`
|
|
|
|
|
- 当前工具返回空 `data` 数组属于预期行为,因为外部接口尚未接入
|
|
|
|
|
- 后续新增或调整工具时,需要同步更新本文件
|
|
|
|
|
|