自有期刊文献数据库的 MCP 封装:七个工具与一个资源
本服务把自有 MySQL 数据库 journals 封装成 MCP(Model Context Protocol)工具集,供 Claude Desktop 等支持 MCP 的客户端直接调用。库内现有文献 90,170 篇、期刊 1,027 种,覆盖 1950 至 2026 年,全部为中文生物与农林类期刊文献。
工具的组织方式沿用 NCBI PubMed E-utilities 的分层结构:检索类工具返回命中总数与标识符列表,取详情类工具按标识符批量返回记录,词表类工具负责把自由词对齐到库内用词,统计类工具给出库规模与分面分布。输出契约也保持一致:成功时返回业务字段,失败时统一返回带错误码的结构化错误对象;批量请求中缺失的标识符逐条回报,不因个别记录缺失导致整体失败。数据全部来自本地库,运行期间不访问任何外部接口。
表1 工具与 E-utilities 端点的对应关系
| 工具 | 对应端点 | 返回内容 |
|---|---|---|
| journals_search_articles | ESearch | 命中总数、文献 ID 列表、摘要级条目、查询回显 |
| journals_fetch_articles | EFetch / ESummary | 完整文献记录,附期刊信息与最新指标 |
| journals_search_journals | ESearch | 期刊总数、期刊 ID 列表、期刊元数据与指标 |
| journals_fetch_journal | EFetch + ESummary | 期刊档案:元数据、历年指标、近期期次、近期文献 |
| journals_lookup_keywords | MeSH 词表查询 | 词条 ID、名称、类型 |
| journals_count_articles | ESearch 聚合视图 | 按期刊、年份、学科、PDF 状态分组的计数 |
| journals_database_info | EInfo | 各表行数、年份范围、PDF 覆盖率、更新时间 |
表2 服务可做与不可做的事项
| 可以做 | 做不到或需要留意 |
|---|---|
| 按标题、关键词、摘要、作者做模糊检索,可按期刊、ISSN、年份、PDF 状态过滤 | 不支持布尔运算(AND / OR / NOT)、字段限定语法与截词符,检索语义为单一关键词串联 |
| 按 ID 或 DOI 批量取文献详情,单次最多 50 个标识符,缺失项逐条回报 | 文献无页码、文献类型、语言、基金与参考文献字段,无法筛"仅综述"或统计被引 |
| 按刊名、ISSN、学科、地区、SCI / CSCD / 北大核心标记、影响因子区间筛选期刊 | 期刊元数据覆盖不均:ISSN 覆盖 39.7%,学科 27.7%,影响因子 3.9% |
| 统计选题分布、年份趋势、PDF 下载率等分面指标 | 关键词尚无同义词与上下位关系,不能做受控词扩展检索 |
表3 运行环境与组件版本
| 项目 | 版本或取值 |
|---|---|
| 操作系统 | Windows(本地运行,可直接使用 PowerShell) |
| Python | 3.12.6,解释器路径 E:\Python\python.exe;项目内使用 .venv 虚拟环境 |
| MCP SDK | mcp 2.3.0(使用 mcp.server.mcpserver.MCPServer) |
| 数据库驱动 | pymysql 1.2.3 |
| 配置加载 | python-dotenv 1.2.4 |
| HTTP 传输 | uvicorn 0.54.0 |
| 数据模型 | pydantic 2.14.1 |
| 数据库 | MySQL 5.7,库名 journals,字符集 utf8mb4 |
表4 主要文件与职责
| 路径 | 职责 |
|---|---|
| server.py | MCP 入口:注册 7 个工具与 1 个资源,按传输类型分派 stdio 或 HTTP |
| config.py | 读取 .env,提供数据库连接参数与分页上限等配置 |
| services/db.py | 只读数据访问层:单连接复用、断线重连、错误码翻译 |
| tools/_sql.py | 公共 SQL 片段:LIKE 转义、分页钳制、年份表达式、过滤条件组装 |
| tools/articles.py | 文献检索与批量取详情 |
| tools/journals.py | 期刊检索与期刊档案 |
| tools/keywords.py | 关键词词表检索 |
| tools/info.py | 库统计与分面计数 |
| .env | 数据库连接与传输配置(含凭据,勿外传) |
| scripts/ | 连通性检查、结构自检、冒烟测试、协议测试、数据体检脚本 |
| docs/ | 本手册 |
项目虚拟环境已创建并安装完毕,正常使用无需重复安装。若需在新机器上重建,用真实 Python 创建虚拟环境后安装依赖清单即可;注意不要使用系统 PATH 上的沙箱运行时 Python 建环境。
E:\Python\python.exe -m venv .venv .venv\Scripts\python.exe -m pip install -r requirements.txt
requirements.txt 中声明的依赖为 mcp>=2.0、pymysql>=1.1、python-dotenv>=1.0、uvicorn>=0.30。
配置集中写在项目根目录的 .env 文件中,随进程启动一次性载入;修改后需要重启服务才会生效。同名的 .env.example 是模板,包含全部配置项与默认值说明,可据此复制出新配置。数据库凭据只保存在该文件中,不写入客户端配置,也不出现在日志与工具返回里。
表5 配置项含义与默认值
| 配置项 | 含义 | 默认值 | 是否必填 |
|---|---|---|---|
| DB_HOST | 数据库主机 | 127.0.0.1 | 是 |
| DB_PORT | 数据库端口,0 视为未填写 | 0 | 是 |
| DB_NAME | 数据库名 | journals | 是 |
| DB_USER | 数据库账号,建议使用只读账号 | 空 | 是 |
| DB_PASSWORD | 数据库密码 | 空 | 是 |
| DB_CHARSET | 连接字符集 | utf8mb4 | 否 |
| DB_CONNECT_TIMEOUT | 连接超时(秒) | 10 | 否 |
| DB_READ_TIMEOUT | 读超时(秒) | 30 | 否 |
| DB_DEFAULT_LIMIT | 未指定 limit 时的默认条数 | 20 | 否 |
| DB_MAX_LIMIT | 单次返回条数上限 | 100 | 否 |
| DB_MAX_IDS | 批量取详情的标识符上限 | 50 | 否 |
| MCP_TRANSPORT_TYPE | 传输类型:stdio 或 http | stdio | 否 |
| MCP_HTTP_PORT | HTTP 传输监听端口 | 3010 | 否 |
DB_PORT、DB_USER、DB_PASSWORD 三项任一缺失时,服务仍可正常启动并完成工具注册,但每次工具调用都会返回 CONFIG_MISSING 错误对象,提示缺少的具体项。这种设计让配置缺失不表现为进程崩溃,客户端能拿到明确的修复指引。
{
"error": {
"code": "CONFIG_MISSING",
"message": "缺少数据库连接配置:DB_PORT、DB_USER、DB_PASSWORD。请在项目根目录 .env 中填写后重启服务。"
}
}
连接建立后立即执行 SET SESSION TRANSACTION READ ONLY,使当前会话处于只读事务状态;查询入口另有一道语句检查,只放行以 SELECT 开头的语句,其他语句直接返回 DB_READ_ONLY 错误。所有用户输入均以参数化方式传入 SQL,模糊匹配中的通配符会被转义,避免拼接注入。对外暴露的配置摘要只包含主机、端口、库名、账号与"密码是否已设置",不输出密码本身。
stdio 是默认传输方式,适合由客户端自动拉起进程,无需常驻端口。
cd E:\PythonProjects\MCP\pubmed_mcp_server .venv\Scripts\python.exe server.py
启动后进程等待客户端在标准输入输出上发起 MCP 握手,不打印额外日志,避免污染协议流。
需要多个客户端共用或跨机器访问时改用 HTTP 传输。注意传输名必须是 streamable-http,写成 http 会被 SDK 拒绝。
$env:MCP_TRANSPORT_TYPE = "http" .venv\Scripts\python.exe server.py # 默认监听 3010 端口,可用 MCP_HTTP_PORT 覆盖
编辑 %APPDATA%\Claude\claude_desktop_config.json,在 mcpServers 中加入本项目。command 指向虚拟环境解释器,args 指向 server.py 的绝对路径;凭据由 .env 提供,无需写进该文件。
{
"mcpServers": {
"journals": {
"command": "E:\\PythonProjects\\MCP\\pubmed_mcp_server\\.venv\\Scripts\\python.exe",
"args": ["E:\\PythonProjects\\MCP\\pubmed_mcp_server\\server.py"]
}
}
}
保存后完全退出并重启 Claude Desktop,客户端会在启动时拉起该进程并列出 7 个工具。
不依赖客户端界面也能验证链路:协议测试脚本会在 stdio 上真实起服、列出工具、调用一个工具并读取资源。输出中出现 7 个工具名与 MCP PROTOCOL TEST OK 即表示协议层正常。
.venv\Scripts\python.exe scripts\mcp_client_test.py
以下七节逐一说明每个工具。参数表的"默认"列取自代码签名,未标注必填的参数均可省略;返回示例为真实库返回的节选,长文本与未列出的字段用省略号略去。
按关键词检索文献,是使用频率最高的入口。q 会在标题、关键词、摘要、作者四列做模糊匹配;其余参数用于收窄范围。返回命中总数与 ID 列表,可用 offset 与 limit 翻页。
表6 journals_search_articles 参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| q | string | 空 | 关键词,匹配标题、关键词、摘要、作者 |
| journal | string | 空 | 期刊名模糊匹配 |
| issn | string | 空 | ISSN 或 eISSN,可带连字符 |
| year_from / year_to | string | 空 | 出版年份区间,取 publication 前 4 位 |
| has_pdf | bool | false | 仅返回已下载 PDF 的文献 |
| sort | string | relevance | relevance、newest、oldest、click 四选一 |
| limit | int | 20 | 返回条数,上限 100 |
| offset | int | 0 | 跳过条数,用于翻页 |
返回字段包括 total(命中总数)、offset 与 limit(实际生效的分页参数)、returned(本次条数)、ids(文献 ID 列表)、items(摘要级条目)、query_echo(查询回显)。每个条目含 id、title、journal_name、year、publication、volume、issue、issn、doi、authors、keywords、url、has_pdf、clicks(total、site、cnki、journal 四个点击量)与 date_in。其中 authors 超过 200 字符会被截断并追加省略号。
以下为 q=仿生、limit=5 的真实返回节选:
{
"total": 50, "offset": 0, "limit": 5, "returned": 5,
"ids": [63689, 62963, 61777, 29866, 11043],
"items": [
{"id": 63689, "title": "昆虫与仿生学浅淡", "journal_name": "应用昆虫学报",
"year": 2000, "publication": "2000年第3期", "volume": null, "issue": null,
"issn": "2095-1353", "doi": "", "has_pdf": false, ...}
],
"query_echo": {"q": "仿生", "sort": "relevance",
"matched_fields": "title, keywords, abstract, authors"}
}
排序规则说明:relevance 优先标题命中,其次关键词命中,再按总点击量与 ID 倒序;click 按总点击量倒序;newest 与 oldest 按文献 ID 倒序与正序,可用于快速浏览最新入库记录。未传 q 时 relevance 退化为按 ID 倒序。
按标识符批量取完整记录。ids 与 dois 至少提供其一,两者可同时使用,结果取并集。找不到的标识符不会中断请求,而是列在 not_found 中逐条回报。
表7 journals_fetch_articles 参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| ids | int[] | 空 | 文献 ID 列表,单次最多 50 个 |
| dois | string[] | 空 | DOI 列表,大小写不敏感,单次最多 50 个 |
| include_abstract | bool | true | 是否返回摘要全文 |
返回 requested(请求数)、found_count(命中数)、not_found(缺失标识符列表)与 items。每个条目在摘要字段之外附带 journal(期刊元数据对象)与 metrics(该刊最新一期指标对象),期刊或指标不存在时为 null。
以下为同时请求 3 个 ID 的真实返回结构:
{"requested": 3, "found_count": 3, "not_found": [],
"items": [{"id": 61777,
"title": "蝉类昆虫翅表疏水性研究为仿生超疏水材料研制提供新思路",
"journal_name": "应用昆虫学报", "publication": "2009年第46卷第6期",
"doi": "", "keywords": "", "authors": "", "abstract": "", ...}]}
该示例中的摘要、作者、关键词为空串,属库内原始数据缺失,不是接口丢弃。若把 include_abstract 设为 false,可减少返回体积,适合只需要书目信息的场景。
检索期刊,用于拿到期刊 ID 后再调用 journals_fetch_journal,或直接按学科、收录标记、影响因子筛选目标刊。q 同时匹配中文刊名、英文刊名与主刊名三列。
表8 journals_search_journals 参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| q | string | 空 | 刊名关键词,匹配 name、name_cn、name_en |
| issn | string | 空 | ISSN 或 eISSN |
| category | string | 空 | 学科模糊匹配 |
| region | string | 空 | 地区模糊匹配 |
| sci / cscd / bdhx | bool 或空 | 空 | 收录标记:true 筛"是",false 筛"否与空",不传不过滤 |
| min_factors / max_factors | float | 空 | 综合影响因子区间,分别对应大于等于与小于等于 |
| limit / offset | int | 20 / 0 | 分页参数,limit 上限 100 |
返回 total、offset、limit、returned、ids、items 与 query_echo。items 直接给出期刊表的元数据字段,并附加 metrics。排序固定为综合影响因子降序、ID 升序,无影响因子的期刊排在后面。
以下为 sci=true、limit=5 的真实返回:
{"total": 8, "ids": [23, 30, 28, 29, 32],
"items": [{"id": 23, "name": "生物化学与生物物理进展",
"name_cn": "生物化学与生物物理进展",
"name_en": "Progress in Biochemistry and Biophysics",
"issn": "1000-3282", "eissn": null, ...}], ...}
库内收录标记以中文"是"与"否"存储,另有大量空值。传 true 时只返回标记为"是"的期刊,传 false 时返回标记为"否"或空白的期刊。这也是为什么 sci=true 的命中数只有 8 条。
按期刊 ID 取完整档案,一次调用即可看到期刊的家底:元数据、历年指标、最近五个期次与近期文献。
表9 journals_fetch_journal 参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| journal_id | int | 必填 | 期刊 ID,可由 journals_search_journals 获得 |
| recent_limit | int | 10 | 近期文献条数,上限 100 |
返回四部分:journal(期刊元数据)、metrics(历年指标数组,最多为库内全部年份)、issues(最近五个期次)、articles(total 该刊文献总数与 recent 近期文献列表)。期刊 ID 不存在时返回 NOT_FOUND,并在 hint 中提示先检索拿 ID。
以下为 journal_id=8 的真实返回要点:
{"journal": {"id": 8, "name": "应用昆虫学报", "issn": "2095-1353",
"sci": "否", "z_factors": 0.82, ...},
"metrics": [ ... 1 条 ],
"issues": [ ... 5 个期次 ],
"articles": {"total": 9902, "recent": [ ... 10 条 ]}}
该刊在库内有 9,902 篇文献,是收录量最大的期刊。若某刊的 articles.total 为 0,说明该刊的文献尚未入库(例如生态学报在 journal_detail 中没有任何记录),并非查询错误。
检索关键词词表,用于把自由词对齐到库内用词,或在写作前确认某个概念在库内的标准说法。词表共 46,259 条有效词条(另有 1 条空名称记录已被过滤)。
表10 journals_lookup_keywords 参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| term | string | 空 | 词条名称模糊匹配 |
| type | string | 空 | 词条类型过滤,如动物名称、物质名、地名、植物名称 |
| limit / offset | int | 20 / 0 | 分页参数,limit 上限 100 |
返回 total、offset、limit、returned、items 与 query_echo;items 每项含 id、name、type。排序为精确名称优先,其次按名称长度升序、ID 升序,因此越短的词条越靠前,便于快速定位目标词。
以下为不带 term 的真实返回节选:
{"total": 46259,
"items": [{"id": 1441, "name": "乳", "type": ""},
{"id": 1486, "name": "二", "type": ""},
{"id": 2210, "name": "人", "type": ""}, ...],
"query_echo": {"term": "", "type": ""}}
不带 term 时会按长度优先返回大量单字词条,实际使用建议始终传入 term。词表 62.1% 的记录未标注类型,type 过滤会因此漏掉这部分词条。
按维度做分面计数,用于回答"某主题在哪些刊发得多""逐年发文量如何变化"这类统计问题。过滤条件与 journals_search_articles 完全共用,口径一致。
表11 journals_count_articles 参数
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
| facet | string | journal | 分组维度:journal、year、category、pdf |
| q / journal / issn | string | 空 | 与检索工具相同的过滤条件 |
| year_from / year_to | string | 空 | 年份区间 |
| has_pdf | bool 或空 | 空 | 是否已下载 PDF |
| limit | int | 20 | 返回的分组个数,上限 100 |
返回 facet、total_hits(过滤后的总命中数)、buckets(分组键与计数)与 query_echo。facet 取值非法时返回 BAD_REQUEST。year 分面按年份倒序,未知年份归入"(未知)";pdf 分面的键为"已下载 PDF"与"未下载 PDF"。
以下为 facet=journal、limit=5 的真实返回:
{"facet": "journal", "total_hits": 90170,
"buckets": [{"key": "应用昆虫学报", "count": 9902},
{"key": "动物学杂志", "count": 7782},
{"key": "昆虫学报", "count": 7286},
{"key": "野生动物学报", "count": 6213},
{"key": "四川动物", "count": 6007}]}
分面计数与年份过滤共用同一套年份表达式,实测 2024 年分面计数为 2,042,与 year_from=2024、year_to=2024 的检索 total 完全相同,两张表可以互相校验。
返回库的整体画像,适合在会话开始时先调用一次,让模型知道数据规模与覆盖范围,避免提出库内无法回答的问题。该工具不接受参数。
表12 journals_database_info 返回字段
| 字段 | 含义 |
|---|---|
| database | 库名,固定为 journals |
| tables | 五张表的行数:journal、journal_detail、journal_issue、keywords、index |
| years | 年份范围 min 与 max,以及 records_without_year 无法解析年份的条数 |
| articles | 文献总数 total 与已下载 PDF 数 with_pdf |
| journals_with_sci_flag | 收录标记为"是"的 SCI 期刊数 |
| last_updated | 文献表最近更新时间 |
| field_guide | 主键与标识符字段指引 |
| note | 只读访问与年份来源说明 |
以下为真实返回节选:
{"database": "journals",
"tables": {"journal": 1027, "journal_detail": 90170, "journal_issue": 2982,
"keywords": 46260, "index": 24},
"years": {"min": 1950, "max": 2026, "records_without_year": 523},
"articles": {"total": 90170, "with_pdf": 25671},
"journals_with_sci_flag": 8, "last_updated": "2026-11-05 ..."}
除工具之外,服务还注册了一个 MCP 资源,URI 为 journals://database/info。内容与 journals_database_info 同源,以 JSON 字符串形式返回,适合客户端把它挂载为常驻上下文,避免每轮都调用一次工具。
资源不承担查询职责,因此不需要参数;数据库不可用时同样返回带 CONFIG_MISSING 或数据库错误码的 JSON,客户端按错误对象处理即可。
所有失败都以 {"error": {"code", "message", "detail", "hint"}} 的形式返回,detail 与 hint 仅在可用时出现。code 是稳定标识,可直接用于分支判断;message 是中文说明,足以指导人工修复。
表13 错误码一览
| 错误码 | 触发场景 | 处理建议 |
|---|---|---|
| CONFIG_MISSING | DB_PORT、DB_USER 或 DB_PASSWORD 未填 | 按提示补齐 .env 后重启服务 |
| DB_AUTH_FAILED | 账号或密码错误(MySQL 1045) | 核对 .env 中的账号密码,注意区分大小写与空格 |
| DB_ACCESS_DENIED | 账号无权访问该库(1044) | 为该账号授予 journals 库的 SELECT 权限 |
| DB_UNREACHABLE | 端口不通或主机不可达(2003、2005) | 检查主机、端口、防火墙与网络可达性 |
| DB_UNKNOWN_DATABASE | DB_NAME 指向的库不存在(1049) | 确认库名拼写,或在服务器上确认库已创建 |
| DB_TABLE_MISSING | 表不存在(1146) | 按 db 目录下的建表脚本导入结构 |
| DB_COLUMN_MISSING | 字段不存在(1054) | 表结构与代码不一致,核对字段名(如 DOI 为大写列名) |
| DB_SYNTAX_ERROR | SQL 语法错误(1064) | 通常由异常输入触发,记录 query_echo 复现 |
| DB_TIMEOUT | 锁等待超时(1205) | 稍后重试;该库为只读,通常不会长时间持锁 |
| DB_READ_ONLY | 尝试执行非 SELECT 语句 | 本服务只读,写操作需另行处理 |
| DB_ERROR | 其他数据库错误 | 查看 detail 中的原始错误信息 |
| NOT_FOUND | 按 ID 取期刊不存在 | 先用 journals_search_journals 检索拿 ID |
| BAD_REQUEST | 参数非法,如 facet 取值错误、标识符为空 | 按 message 修正参数 |
| LIMIT_EXCEEDED | 批量标识符超过 50 个 | 分批调用,每批不超过 50 个 |
本章的数字全部来自对真实库的只读统计(scripts/field_coverage.py、scripts/data_probe.py、scripts/smoke_test.py),用于说明哪些字段可以放心使用、哪些需要容错。
表14 真实库规模(2026 年 11 月统计)
| 对象 | 数量 | 说明 |
|---|---|---|
| 文献 journal_detail | 90,170 | 覆盖 1950 至 2026 年,最近入库 2026-09-15 |
| 期刊 journal | 1,027 | SCI 标记为是 8 种,CSCD 34 种,北大核心 24 种 |
| 期次 journal_issue | 2,982 | 覆盖 46,010 篇文献,其余 44,160 篇无期次归属 |
| 关键词 keywords | 46,260 | 其中 1 条空名称已过滤,有效词条 46,259 |
| 指标 index | 24 | 仅 24 种期刊有指标,JIF 非空 7 条 |
| PDF 覆盖 | 25,671 | 占文献总量 28.5% |
表15 文献字段非空率(n = 90,170)
| 字段 | 非空数 | 覆盖率 |
|---|---|---|
| title | 90,169 | 100.0% |
| journal_name | 90,170 | 100.0% |
| issn | 90,090 | 99.9% |
| publication | 89,670 | 99.4% |
| authors | 87,871 | 97.5% |
| abstract | 84,428 | 93.6% |
| keywords | 62,015 | 68.8% |
| DOI | 32,684 | 36.2% |
| is_pdf | 25,671 | 28.5% |
| url | 18,943 | 21.0% |
摘要平均长度 406.6 字符,最长 5,008 字符;作者字段是单串文本,平均长度 23.1 字符。DOI 去重后为 32,645 条,存在 39 条重复,按 DOI 批量取详情时同一 DOI 可能命中多条记录,调用方需按实际业务取舍。
表16 期刊字段非空率(n = 1,027)
| 字段 | 非空数 | 覆盖率 |
|---|---|---|
| name | 1,027 | 100.0% |
| website | 1,020 | 99.3% |
| issn | 408 | 39.7% |
| category | 284 | 27.7% |
| eissn | 265 | 25.8% |
| name_cn | 141 | 13.7% |
| sponsor | 103 | 10.0% |
| z_factors | 40 | 3.9% |
| region | 24 | 2.3% |
期刊元数据覆盖不均,按 ISSN 检索会漏掉六成期刊,按学科或影响因子筛选的样本量也有限。若检索目标是中文期刊,用刊名比用 ISSN 更可靠。
文献表本身没有年份列,服务统一从 publication 字段取前 4 位作为年份,例如「2022年57卷第6期」解析为 2022。该字段非空 89,670 条,其中 89,647 条能解析出年份,剩余 523 条因字段为空或格式异常没有年份,不会出现在任何年份区间结果中。
publication 存在前导空格(如「 2023年第30卷第1期」),服务在解析前先去除空格,因此这类记录也能正确归年。年份上限 2026 是提前上架的预出版记录,共约 394 条,不是错误数据。卷号与期号来自期次联表,只有具备期次归属的 46,010 篇文献才有值。
部分记录的作者、摘要、关键词、DOI 为空串或 null,接口原样返回,不做补全。词表中有 188 个单字词条(如"乳""二""人"),属于原始数据特征。期次表 volume 字段仅 78.0% 有值。这些情况在使用前都应做空值判断,不要默认字段一定存在内容。
检索基于 SQL LIKE 的模糊匹配,多个条件之间为"与"关系,通配符由服务转义,用户输入中的百分号与下划线不会变成通配符。这种实现不区分词形,也不支持布尔逻辑与字段限定,适合中文关键词的快速定位;在 9 万行规模下响应时间为毫秒级,若数据量继续增长,可增加全文索引改为 MATCH 查询。
scripts 目录下的六个脚本覆盖从连通性到数据体检的完整链路,均可重复执行。其中结构自检脚本在本地临时库中运行,不会触碰业务库。
表17 自检与体检脚本
| 脚本 | 作用 | 运行前提 |
|---|---|---|
| check_db.py | 连通性、五张表与关键字段校验 | .env 已填写 |
| schema_selftest.py | 导入 db 目录建表脚本到本地临时库,跑全部工具 SQL,30 项断言,结束自动删库 | 本地 MySQL(root/root) |
| smoke_test.py | 对真实库跑 7 个工具,含年份口径一致性断言 | .env 已填写 |
| mcp_client_test.py | stdio 起服、列工具、调工具、读资源,验证协议层 | 无需数据库 |
| data_probe.py | 只读体检:收录标记分布、期次覆盖、年份解析率、空词条 | .env 已填写 |
| field_coverage.py | 只读体检:字段非空率、摘要长度、DOI 重复 | .env 已填写 |
.venv\Scripts\python.exe scripts\check_db.py .venv\Scripts\python.exe scripts\schema_selftest.py .venv\Scripts\python.exe scripts\smoke_test.py .venv\Scripts\python.exe scripts\mcp_client_test.py
当前状态:连通性检查 5 张表全部通过;结构自检 30 项断言全部通过;真实库冒烟测试通过;协议测试 7 个工具注册完整、资源可读。
先在本机直接测试端口:Test-NetConnection 指定主机与端口,若不通,检查服务端是否只监听内网地址、防火墙是否放行该端口、是否需要先接入内网或隧道。端口通了仍报错时,再核对库名与账号。
多为账号密码前后带空格,或复制时带了引号。.env 中的值不加引号,等号两侧不要留空格。若确认无误仍失败,用同一账号在其他客户端连接验证,排除账号权限或密码策略问题。
该条文献的 publication 为空或不是以四位年份开头,共 523 条属于此类。这类记录不参与年份过滤,可用 journals_database_info 的 records_without_year 字段核对总量。
说明该刊的文献尚未入库。服务在文献表按 journal_id 查不到记录时,会自动改用刊名再查一次,两次都为空才返回 0。例如生态学报在期刊表中存在,但文献表中没有任何记录。
指标表只有 24 行,仅覆盖 24 种期刊。其余期刊的 metrics 返回 null 属正常结果,不代表查询失败,业务逻辑需容忍该字段为空。
单次返回上限为 100 条,超过会被钳制。需要更多数据时用 offset 分页循环取,或先用 journals_count_articles 拿到总量与分布,再决定只取哪些分组的明细,避免一次拉全量。
检索为整串模糊匹配,不含分词。把长概念拆成较短的核心词分别检索,或用词表中的规范词再查,命中率通常更高;也可同时用刊名与年份收窄范围后按 click 排序,优先看高点击文献。
表18 版本记录
| 版本 | 日期 | 主要内容 |
|---|---|---|
| 0.1.0 | 2026-11-05 | 首个数据库版:7 个工具与 1 个资源,只读访问 journals 库;年份改用 publication 前 4 位解析;收录标记按中文是/否判定 |
数据侧建议优先补齐期刊的 ISSN、学科与影响因子,这三项决定期刊检索的可用性,且总量只有 1,027 条,回填成本可控。若年份过滤成为高频查询,可为文献表增加冗余年份列并建索引,把解析成本前移到入库阶段。文献的页码、文献类型与参考文献需要重新采集才能补齐,短期不宜承诺。
代码侧改动后按顺序执行结构自检、真实库冒烟与协议测试三步即可回归;新增工具时保持同一套输出契约,错误一律走 error 对象,批量请求保留逐条缺失回报。