Files
PlantAI-rag-new/tests/paper/test_mcp_protocol.py
T
admin 91f6a1c242 feat: 新增论文撰写功能 paper-writer(独立 MCP 服务 + BFF 大脑 + 前端三卡)
- paper_writer_service(8097):FastAPI + MCP streamable-http 单端点,9 工具
  (propose_paper_outline/write_paper_section/get_paper_progress/search_refs/
  revise_paper_section/revise_paper/get_paper/complete_paper/cancel_paper),
  SQLite 三表(papers/paper_sections/citations,WAL),GB/T 7714 引用协议
- BFF:services/paper/{mcp_client,agent}.py(裸 JSON-RPC 客户端 + 规则状态机)、
  routers/paper.py、schemas.py 4 个 Paper*Block + ArtifactBlock 加 paper_id/citation_count、
  config.py 开关、main.py 条件注册、docgen.py 抽 insert_artifact 公共 helper
- 前端:intent.js paperIntent 纯函数、Composer docgen 分支 sub-route、sendPaper/轮询、
  PaperBriefCard/PaperOutlineCard/PaperProgressCard、DocPane cite-chip
- deploy/paper_restart.sh + toggle_paper.sh;compile_sdk.sh 白名单追加 paper 文件
- tests/paper 26 条(工具逻辑 + MCP 协议 + BFF 状态机 + 块 schema + 前端 intent 单测)
2026-09-30 22:29:22 +08:00

70 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""tests/paper/test_mcp_protocol.py —— MCP 协议层测试(list_tools / call_tool 往返)。
验证:9 个工具正确暴露、call_tool 返回可解析的 JSON(structuredContent 或 content 文本块)。
镜像 bioflow_service/tests/test_mcp_server.py 的 call_tool 集成模式。
"""
from __future__ import annotations
import json
from conftest import _run
from paper_writer_service.app.mcp import paper_mcp
EXPECTED_TOOLS = {
"propose_paper_outline", "write_paper_section", "get_paper_progress", "search_refs",
"revise_paper_section", "revise_paper", "get_paper", "complete_paper", "cancel_paper",
}
def _content_text(result):
"""从 CallToolResult 抽取文本内容(无 structuredContent 时)。"""
for block in getattr(result, "content", None) or []:
text = getattr(block, "text", None)
if text:
return json.loads(text)
return {}
def _call(name, args):
r = _run(paper_mcp.call_tool(name, args))
return getattr(r, "structuredContent", None) or _content_text(r)
def test_list_tools_exposes_nine():
tools = _run(paper_mcp.list_tools())
names = {getattr(t, "name", None) for t in tools}
assert EXPECTED_TOOLS == names
def test_call_tool_propose_outline_roundtrip(paper_db, fake_llm, fake_search):
fake_llm.responses.append({
"title": "水稻SPL基因家族研究进展",
"abstract_expect": "综述",
"sections": [{"section_id": "s1", "title": "引言", "key_points": [], "target_words": 1500}],
})
r = _call("propose_paper_outline", {"topic": "水稻SPL基因家族", "paper_type": "综述"})
assert r.get("ok") is True
assert r["paper_id"]
assert r["title"] == "水稻SPL基因家族研究进展"
def test_call_tool_not_found(paper_db):
r = _call("get_paper", {"paper_id": "nope"})
assert r.get("ok") is False
assert r.get("code") == "NOT_FOUND"
def test_call_tool_content_is_json_string(paper_db, fake_llm, fake_search):
"""验证 content[0].text 是合法 JSON 字符串(BFF 裸 JSON-RPC 客户端解析面)。"""
fake_llm.responses.append({
"title": "T", "abstract_expect": "A",
"sections": [{"section_id": "s1", "title": "引言", "key_points": [], "target_words": 1500}],
})
raw = _run(paper_mcp.call_tool("propose_paper_outline", {"topic": "水稻SPL"}))
blocks = getattr(raw, "content", None) or []
assert blocks, "CallToolResult 无 content 块"
text = getattr(blocks[0], "text", None)
assert text is not None
payload = json.loads(text) # 应为合法 JSON 字符串
assert payload.get("ok") is True