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:
@@ -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": "检查级别"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user