feat(§03): 实现 MCP Server 核心 - Storage/Service/Tool/MCP Protocol Layers

**Storage Layer:**
- Database 类(aiosqlite + WAL + 并发保护)
- WikiPage 数据模型
- fix_dirty_states 恢复机制
- FTS5 全文搜索支持
- 完整的 SQLite 表结构

**Service Layer:**
- CacheService(LRU 缓存 + TTL + 大小限制)
- QueryService(查询服务)
- ParserService(Markdown 解析)
- IndexerService(索引服务)
- GraphService(链接图服务)

**Tool Layer (8 个 MCP Tools):**
- wiki_query - FTS5 全文搜索 + 标签过滤
- memory_bridge - 按工具来源浏览
- wiki_status - 索引状态
- wiki_lint - 健康审计
- cross_linker - 缺失链接发现
- tag_taxonomy - 标签一致性
- wiki_synthesize - 跨概念综合分析
- daily_update - 日常维护 + hot.md 生成

**MCP Protocol Layer:**
- MCP 协议解析和封装
- 工具注册和路由
- 错误处理和日志
- stdio 模式支持

**配置和部署:**
- requirements.txt
- config.example.yaml
- ecosystem.config.cjs (PM2)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-26 11:32:43 +08:00
parent 67a53d82de
commit dfd8421dc6
22 changed files with 2561 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
"""
Tool Layer - wiki_synthesize 工具
发现跨概念的综合分析机会,生成 synthesis 页面。
参考设计文档:第 2.2 节
"""
import logging
from typing import Dict, Any, List
from ..services import QueryService, GraphService
logger = logging.getLogger(__name__)
class WikiSynthesizeTool:
"""wiki_synthesize 工具实现"""
def __init__(self, query_service: QueryService, graph_service: GraphService):
self.query_service = query_service
self.graph_service = graph_service
async def handle(self, concepts: List[str] = None, threshold: float = 0.3) -> Dict[str, Any]:
"""
发现跨概念的综合分析机会
Args:
concepts: 概念列表(空表示自动发现)
threshold: 相关性阈值
Returns:
{
"synthesis": [
{
"concept_a": "SQLite",
"concept_b": "Concurrency",
"common_references": ["page1", "page2"],
"strength": 0.8,
"suggestion": "Consider creating synthesis page: SQLite & Concurrency"
}
],
"total": 5
}
"""
synthesis = []
# 如果未提供概念,自动发现高相关性概念
if not concepts:
# 获取高度连接的页面作为候选
highly_connected = await self.graph_service.get_highly_connected_pages(threshold=10)
concepts = [item["title"] for item in highly_connected[:10]]
# 分析概念间的关联
for i, concept_a in enumerate(concepts):
for concept_b in concepts[i + 1:]:
# 查找同时引用两个概念的页面
pages_a = await self.query_service.search(concept_a, limit=50)
pages_b = await self.query_service.search(concept_b, limit=50)
paths_a = {p.path for p in pages_a}
paths_b = {p.path for p in pages_b}
common = paths_a & paths_b
if len(common) >= 2: # 至少 2 个共同引用
strength = len(common) / max(len(paths_a), len(paths_b))
if strength >= threshold:
synthesis.append({
"concept_a": concept_a,
"concept_b": concept_b,
"common_references": sorted(list(common)),
"strength": round(strength, 2),
"suggestion": f"Consider creating synthesis page: {concept_a} & {concept_b}"
})
# 按关联强度排序
synthesis.sort(key=lambda x: x["strength"], reverse=True)
return {
"synthesis": synthesis[:20],
"total": len(synthesis)
}
def get_schema(self) -> dict:
"""返回 MCP Tool schema"""
return {
"name": "wiki_synthesize",
"description": "发现跨概念的综合分析机会,生成 synthesis 页面",
"inputSchema": {
"type": "object",
"properties": {
"concepts": {
"type": "array",
"items": {"type": "string"},
"description": "概念列表(空表示自动发现)"
},
"threshold": {
"type": "number",
"default": 0.3,
"description": "相关性阈值"
}
}
}
}