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
+164
View File
@@ -0,0 +1,164 @@
"""
Tool Layer - wiki_lint 工具
审计 wiki 健康(格式、链接、frontmatter 规范)。
参考设计文档:第 2.2 节
"""
import logging
from typing import Dict, Any, List
from ..services import ParserService, QueryService, GraphService
logger = logging.getLogger(__name__)
class WikiLintTool:
"""wiki_lint 工具实现"""
def __init__(self, parser: ParserService, query_service: QueryService, graph_service: GraphService):
self.parser = parser
self.query_service = query_service
self.graph_service = graph_service
async def handle(self, path: str = "", level: str = "basic") -> Dict[str, Any]:
"""
审计 wiki 健康
Args:
path: 指定路径(空字符串表示全部)
level: 检查级别(basic/strict
Returns:
{
"issues": [
{
"path": "practices/example.md",
"type": "missing_title",
"message": "Missing title",
"severity": "warning"
}
],
"fixes": [
{
"path": "practices/example.md",
"action": "add_title",
"suggestion": "# Example Page"
}
],
"summary": {
"total_issues": 5,
"critical": 0,
"warning": 5,
"info": 0
}
}
"""
issues = []
fixes = []
# 获取所有页面或指定页面
if path:
pages = [await self.query_service.get_page(path)]
else:
pages = await self.query_service.db.get_all_pages()
summary = {"critical": 0, "warning": 0, "info": 0}
for page in pages:
# 读取内容
try:
from pathlib import Path
vault_path = Path(self.query_service.db.path).parent.parent / "wiki-vault"
full_path = vault_path / page.path
with open(full_path, 'r', encoding='utf-8') as f:
content = f.read()
except Exception as e:
issues.append({
"path": page.path,
"type": "file_error",
"message": f"Failed to read file: {e}",
"severity": "critical"
})
summary["critical"] += 1
continue
# 验证页面
validation_issues = self.parser.validate_page(page.path, content)
for issue in validation_issues:
severity = "warning" if "Missing" in issue else "info"
issues.append({
"path": page.path,
"type": issue.lower().replace(" ", "_"),
"message": issue,
"severity": severity
})
summary[severity] += 1
# 生成修复建议
if "Missing title" in issue:
title = self.parser.extract_title(content)
if not title:
title = page.path.replace(".md", "").replace("-", " ").replace("_", " ").title()
fixes.append({
"path": page.path,
"action": "add_title",
"suggestion": f"# {title}"
})
# strict 模式额外检查
if level == "strict":
# 检查孤立页面
orphans = await self.graph_service.find_orphans()
for orphan in orphans:
issues.append({
"path": orphan,
"type": "orphan",
"message": "No backlinks found",
"severity": "info"
})
summary["info"] += 1
# 检查缺失链接
missing_links = await self.graph_service.find_missing_links()
for source, target in missing_links[:10]:
issues.append({
"path": source,
"type": "broken_link",
"message": f"Link to non-existent page: {target}",
"severity": "warning"
})
summary["warning"] += 1
return {
"issues": issues,
"fixes": fixes,
"summary": {
"total_issues": len(issues),
**summary
}
}
def get_schema(self) -> dict:
"""返回 MCP Tool schema"""
return {
"name": "wiki_lint",
"description": "审计 wiki 健康(格式、链接、frontmatter 规范)",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"default": "",
"description": "指定路径(空字符串表示全部)"
},
"level": {
"type": "string",
"default": "basic",
"enum": ["basic", "strict"],
"description": "检查级别"
}
}
}
}