Files
sanguo_llmwiki/mcp_server/tools/wiki_lint.py
T
claude_dev dfd8421dc6 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>
2026-06-26 11:32:43 +08:00

165 lines
5.4 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.
"""
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": "检查级别"
}
}
}
}