admission-feishu-mcp
by XuYui
README.md
# 录取数据飞书多维表格 MCP
这个 MCP 给 AstrBot 提供招生录取数据查询、聚合和跨表对比能力。机器人只拿到 MCP 处理后的统计结果,不需要把多维表格大表原始数据塞进上下文。
示例配置支持 5 个数据集:
- `admission_2026`:26录取数据表
- `admission_2021_2025`:21-25录取数据表
- `plan_2025`:25计划表
- `plan_2026`:26计划表
- `score_rank_2026_primary`:26一分一段表
核心原则:
- 字段来自飞书表格实时字段列表,字段写错会直接报错并给相近字段建议。
- 每张表有自己的字段别名映射,例如 `school` 在 26 录取表对应 `匹配中学`,在 21-25 录取表对应 `中学`。
- 聚合工具会扫描完飞书返回的所有匹配记录后再返回;如果飞书接口失败,不返回半截统计。
- 数值聚合用 Decimal 计算,结果以字符串返回,避免浮点误差。
- 默认不允许返回敏感字段;如内部使用可设置 `ADMISSION_MCP_ALLOW_SENSITIVE_FIELDS=true`。
- 所有工具都要求由 AstrBot 飞书事件上下文注入 `requester_id`,服务端据此检查显式读取、写入、运维和特定能力权限。
## 1. 配置
`.env` 至少需要:
~~~text
FEISHU_APP_ID=cli_xxx
FEISHU_APP_SECRET=xxx
FEISHU_APP_TOKEN=base_or_bitable_token
ADMISSION_MCP_ALLOW_SENSITIVE_FIELDS=false
~~~
先复制公开仓库中的示例文件,再填入你自己的飞书配置:
~~~bash
cp .env.example .env
cp config/datasets.example.json config/datasets.json
cp admission-permissions.example.json admission-permissions.json
~~~
上述三个运行时文件已被 `.gitignore` 排除,请勿提交真实凭据、表格 ID、人员姓名或 Open ID。
四张表的 `table_id`、`view_id` 和字段别名放在:
~~~text
config/datasets.json
~~~
如果配置文件不在默认位置,可以设置:
~~~text
ADMISSION_DATASETS_CONFIG=/path/to/datasets.json
~~~
权限由 `admission-permissions.json` 和 `admission-tool-permissions.json` 管理。系统仅保留三种招办角色,实际读、写和运维权限以人员配置中的显式开关为准,详见 [PERMISSIONS.md](PERMISSIONS.md)。
## 2. 启动 MCP
本服务使用 MCP stdio 方式:
~~~powershell
python -m pip install -r requirements.txt
python run_mcp.py
~~~
AstrBot 的 MCP 配置形态类似:
~~~json
{
"mcpServers": {
"admission-feishu": {
"command": "python3",
"args": ["/AstrBot/data/mcp/admission-feishu/run_mcp.py"],
"active": true
}
}
}
~~~
## 3. 工具
### admission_config_check
检查配置和数据集状态。`ping_feishu=true` 时会对每个数据集做最小只读查询。
### admission_schema
查看指定数据集的字段、别名、敏感字段和支持操作。
示例:
~~~json
{"requester_id": "ou_from_event_context", "dataset": "admission_2021_2025"}
~~~
### admission_send_student_photo
按姓名或学号精确查询 `admission_2026` 的学生记录,并将“个人照片”附件通过机器人私聊发送给当前查询人。图片只在内存中完成多维表格素材下载和飞书 IM 图片上传,不写入本地临时文件。姓名存在重名时不会发送,需改用学号。
示例:
~~~json
{
"requester_id": "ou_from_event_context",
"identifier": "202600001"
}
~~~
### admission_query
查询少量明细。`dataset` 留空时默认查 `admission_2026`,兼容旧用法。
示例:
~~~json
{
"requester_id": "ou_from_event_context",
"dataset": "admission_2021_2025",
"filters": [
{"field": "year", "op": "eq", "value": 2025},
{"field": "school", "op": "contains", "value": "南通中学"}
],
"fields": ["year", "province", "school", "major", "score", "rank"],
"limit": 20
}
~~~
### admission_aggregate
单表聚合,适合人数、分布、最高/最低/平均分、计划数。
示例:查 2025 年南通中学录取人数和分数统计:
~~~json
{
"requester_id": "ou_from_event_context",
"dataset": "admission_2021_2025",
"filters": [
{"field": "year", "op": "eq", "value": 2025},
{"field": "school", "op": "contains", "value": "南通中学"}
],
"metrics": [
{"name": "人数", "op": "count"},
{"name": "最低投档成绩", "op": "min", "field": "score"},
{"name": "最高投档成绩", "op": "max", "field": "score"},
{"name": "平均投档成绩", "op": "avg", "field": "score"}
]
}
~~~
计划表如果不传 `metrics`,默认按 `PlanNUM` 汇总为 `计划数`。
### admission_zodiac_distribution
按指定年份统计十二星座录取人数。星座由 MCP 在内部使用“出生月份”和“出生日”计算,只返回汇总人数和无效日期数量,不返回生日明细。可通过 `filters` 继续限定省份、中学、专业或书院。
~~~json
{
"requester_id": "ou_from_event_context",
"year": 2026,
"filters": [
{"field": "province", "op": "eq", "value": "江苏"}
]
}
~~~
### admission_score_rank
查询 2026 年某省某分数对应的一分一段位次区间。`province` 和 `score` 必填,`subject` 可选;科类名称按省份考试模式区分:新疆返回“文史/理工”,新高考省份返回“历史类/物理类”,综合改革省份返回“综合改革”。工具会按省份自动转换“文科”“理科”等简称。
~~~json
{
"requester_id": "ou_from_event_context",
"province": "江苏",
"score": 650,
"subject": "物理类"
}
~~~
### admission_college_rank
计算指定年份目标书院的平均投档成绩排名。MCP 会在内部使用全部书院数据完成排名,但只返回目标书院的平均分、名次、参评书院数和样本量,不返回其他书院的名称或统计值。调用者需要显式配置 `benchmark.college_rank` 能力。
### admission_multi_aggregate
一次执行多个聚合请求,适合同时取多个年份或计划/录取数据。
### admission_compare
跨表对比工具。每个 request 指定 `name`、`dataset`、`filters`;公共 `group_by` 和 `metrics` 会按各表别名自动映射。
示例:比较南通中学 2025 年和 2024 年录取情况:
~~~json
{
"requester_id": "ou_from_event_context",
"requests": [
{
"name": "2025",
"dataset": "admission_2021_2025",
"filters": [
{"field": "year", "op": "eq", "value": 2025},
{"field": "school", "op": "contains", "value": "南通中学"}
]
},
{
"name": "2024",
"dataset": "admission_2021_2025",
"filters": [
{"field": "year", "op": "eq", "value": 2024},
{"field": "school", "op": "contains", "value": "南通中学"}
]
}
],
"metrics": [
{"name": "人数", "op": "count"},
{"name": "最低投档成绩", "op": "min", "field": "score"},
{"name": "最高投档成绩", "op": "max", "field": "score"},
{"name": "平均投档成绩", "op": "avg", "field": "score"}
]
}
~~~
### admission_yearly_score_trend
年度分数趋势专用工具。适合“21-26 各年最低分/分数线分别是多少”这类问题;内部最多扫描 21-25 历史表一次、26 录取表一次,避免逐年多次调用。
示例:江苏省 2021-2026 年各年最低投档成绩:
~~~json
{
"requester_id": "ou_from_event_context",
"start_year": 2021,
"end_year": 2026,
"province": "江苏"
}
~~~
不要自动加 `score_gt` 或 `score_gte`;只有用户明确要求排除某类分数时才使用。
### admission_permission_check
查看当前请求者自身的身份、显式读写权限和 MCP 运维权限。
### admission_update_record
按 `record_id` 更新单条飞书记录。只有显式配置 `write_all: true` 的用户可调用,角色名称本身不授予写权限,且只能修改字段白名单中的字段。
## 4. 常用别名
录取表常用:
- `year`:录取年份
- `province`:省市名称
- `city`:地级市
- `school`:26 表为匹配中学,21-25 表为中学
- `major`:录取专业
- `major_category`:录取大类,仅 26 表有
- `subject`:科类名称
- `score`:投档成绩
- `rank`:省排位
计划表常用:
- `year`:YEAR 或 Year
- `province`:SYSSMC
- `major`:ZSZYMC
- `subject`:KLMC
- `plan_count`:PlanNUM
- `category`:JHLBMC
- `direction`:ZKFX
- `major_group`:XBZYDH
## 5. 验证
不连接飞书时先跑:
~~~powershell
python -m unittest discover -s tests -v
~~~
部署后建议调用:
~~~json
{"tool": "admission_config_check", "arguments": {"requester_id": "ou_from_event_context", "ping_feishu": true}}
~~~
再用一个人工已知答案的小范围问题核对,例如“2025 年南通中学录取人数”和多维表格界面筛选结果是否一致。
AstrBot 侧还需要启用 `plugins/admission_access_control`,确保每次招生 MCP 调用的 `requester_id` 都由飞书事件发送者 ID 强制覆盖。
## 6. 安全与许可证
仓库只提供代码和脱敏示例配置,不包含真实录取数据。部署前请阅读 [SECURITY.md](SECURITY.md),并始终从可信事件上下文覆盖 `requester_id`。
本项目采用 [MIT License](LICENSE)。
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues