"""
模板管理工具
提供模板管理、自定义模板创建等功能。
"""
from typing import Dict, Any, List, Optional
from datetime import datetime
from services import TemplateService
def register_template_tools(mcp_server, template_service: TemplateService):
"""
注册模板管理相关工具
Args:
mcp_server: FastMCP服务器实例
template_service: 模板服务实例
"""
@mcp_server.tool()
async def folder_docs_list_templates(
template_type: str = "all"
) -> Dict[str, Any]:
"""
列出可用的模板
列出系统中所有可用的文档生成模板。
Args:
template_type: 模板类型过滤 (all, readme, mindmap, custom)
Returns:
可用模板列表,包含模板名称、类型、大小等信息
"""
try:
# 获取所有模板
all_templates = template_service.list_available_templates()
# 按类型过滤
if template_type.lower() == "readme":
filtered_templates = [t for t in all_templates if 'readme' in t.lower()]
elif template_type.lower() == "mindmap":
filtered_templates = [t for t in all_templates if 'mindmap' in t.lower() or 'mermaid' in t.lower()]
elif template_type.lower() == "custom":
filtered_templates = [t for t in all_templates if not any(prefix in t.lower() for prefix in ['readme', 'mindmap', 'mermaid'])]
else:
filtered_templates = all_templates
# 获取模板详细信息
template_details = []
for template_name in filtered_templates:
try:
info = template_service.get_template_info(template_name)
template_details.append(info)
except (RuntimeError, ValueError) as e:
# 如果获取详细信息失败,至少包含基本名称
template_details.append({
'name': template_name,
'error': str(e)
})
return {
"success": True,
"template_type": template_type,
"total_templates": len(all_templates),
"filtered_count": len(filtered_templates),
"templates": template_details,
"query_time": datetime.now().isoformat()
}
except (RuntimeError, ValueError) as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
@mcp_server.tool()
async def folder_docs_create_template(
template_name: str,
template_content: str,
template_type: str = "custom",
description: str = "",
overwrite: bool = False
) -> Dict[str, Any]:
"""
创建自定义模板
创建新的文档生成模板,支持Jinja2语法。
Args:
template_name: 模板名称(建议使用.j2后缀)
template_content: 模板内容(Jinja2语法)
template_type: 模板类型 (readme, mindmap, custom)
description: 模板描述
overwrite: 是否覆盖已存在的模板
Returns:
模板创建结果,包含验证信息和使用建议
"""
try:
# 验证模板语法
validation_result = await template_service.validate_template(template_content)
if not validation_result['valid']:
return {
"success": False,
"error": "模板语法验证失败",
"validation_errors": validation_result['errors'],
"template_name": template_name
}
# 创建模板
success = await template_service.create_template(template_name, template_content, overwrite)
if not success:
return {
"success": False,
"error": "创建模板失败",
"template_name": template_name
}
# 获取创建后的模板信息
template_info = template_service.get_template_info(template_name)
return {
"success": True,
"template_name": template_name,
"template_type": template_type,
"description": description,
"template_info": template_info,
"validation_warnings": validation_result.get('warnings', []),
"creation_time": datetime.now().isoformat(),
"usage_example": f"使用示例: folder_docs_generate_readme('{template_name}')"
}
except (RuntimeError, ValueError) as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
"template_name": template_name
}
@mcp_server.tool()
async def folder_docs_preview_template(
template_name: str,
sample_data: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
预览模板效果
使用示例数据预览模板的渲染效果。
Args:
template_name: 模板名称
sample_data: 示例数据(可选,使用默认示例数据)
Returns:
模板预览结果,包含渲染内容和使用的示例数据
"""
try:
# 检查模板是否存在
if not template_service.template_exists(template_name):
return {
"success": False,
"error": f"模板不存在: {template_name}",
"available_templates": template_service.list_available_templates()
}
# 使用默认示例数据或提供的数据
if sample_data is None:
sample_data = generate_sample_data()
# 渲染模板
rendered_content = await template_service.render_template(template_name, sample_data)
# 限制预览内容长度
max_preview_length = 2000
if len(rendered_content) > max_preview_length:
preview_content = rendered_content[:max_preview_length] + "\n... (内容已截断)"
truncated = True
else:
preview_content = rendered_content
truncated = False
return {
"success": True,
"template_name": template_name,
"preview_content": preview_content,
"truncated": truncated,
"used_sample_data": sample_data,
"preview_time": datetime.now().isoformat()
}
except (RuntimeError, ValueError) as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
"template_name": template_name
}
@mcp_server.tool()
async def folder_docs_validate_template(
template_content: str
) -> Dict[str, Any]:
"""
验证模板语法
验证Jinja2模板语法的正确性。
Args:
template_content: 要验证的模板内容
Returns:
模板验证结果,包含语法检查和建议
"""
try:
validation_result = await template_service.validate_template(template_content)
# 提取模板变量
import re
pattern = r'\{\{\s*([^}]+)\s*\}\}'
matches = re.findall(pattern, template_content)
variables = []
for match in matches:
# 清理变量名,移除过滤器和函数调用
variable = match.split('|')[0].strip()
# 移除属性访问,只保留主要变量名
variable = variable.split('.')[0].strip()
if variable and variable not in variables:
variables.append(variable)
return {
"success": True,
"valid": validation_result['valid'],
"errors": validation_result['errors'],
"warnings": validation_result['warnings'],
"extracted_variables": variables,
"suggestions": generate_template_suggestions(variables),
"validation_time": datetime.now().isoformat()
}
except (RuntimeError, ValueError) as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
@mcp_server.tool()
async def folder_docs_export_template(
template_name: str,
export_path: str,
export_format: str = "jinja2"
) -> Dict[str, Any]:
"""
导出模板
将模板导出到指定路径,支持多种格式。
Args:
template_name: 模板名称
export_path: 导出路径
export_format: 导出格式 (jinja2, html, markdown)
Returns:
模板导出结果
"""
try:
# 检查模板是否存在
if not template_service.template_exists(template_name):
return {
"success": False,
"error": f"模板不存在: {template_name}",
"available_templates": template_service.list_available_templates()
}
# 获取模板内容
template_content = template_service.get_template_content(template_name)
# 根据格式处理内容
if export_format.lower() == "html":
# 导出为HTML格式,添加基础样式
processed_content = convert_template_to_html(template_content)
elif export_format.lower() == "markdown":
# 导出为Markdown格式
processed_content = convert_template_to_markdown(template_content)
else:
# 默认导出原始Jinja2格式
processed_content = template_content
# 导出模板
success = await template_service.export_template(template_name, export_path)
if success:
return {
"success": True,
"template_name": template_name,
"export_path": export_path,
"export_format": export_format,
"content_size": len(processed_content),
"export_time": datetime.now().isoformat()
}
else:
return {
"success": False,
"error": "导出模板失败",
"template_name": template_name
}
except (RuntimeError, ValueError) as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
"template_name": template_name
}
@mcp_server.tool()
async def folder_docs_import_template(
import_path: str,
template_name: Optional[str] = None,
overwrite: bool = False
) -> Dict[str, Any]:
"""
导入模板
从指定路径导入模板文件。
Args:
import_path: 导入路径
template_name: 模板名称(可选,默认使用文件名)
overwrite: 是否覆盖已存在的模板
Returns:
模板导入结果,包含验证信息
"""
try:
# 导入模板
success = await template_service.import_template(import_path, template_name, overwrite)
if not success:
return {
"success": False,
"error": "导入模板失败",
"import_path": import_path
}
# 确定实际的模板名称
if not template_name:
from pathlib import Path
template_name = Path(import_path).name
if not template_name.endswith('.j2'):
template_name += '.j2'
# 验证导入的模板
template_content = template_service.get_template_content(template_name)
validation_result = await template_service.validate_template(template_content)
return {
"success": True,
"template_name": template_name,
"import_path": import_path,
"overwrite": overwrite,
"validation_valid": validation_result['valid'],
"validation_errors": validation_result.get('errors', []),
"validation_warnings": validation_result.get('warnings', []),
"import_time": datetime.now().isoformat()
}
except (RuntimeError, ValueError) as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__,
"import_path": import_path
}
def generate_sample_data() -> Dict[str, Any]:
"""生成示例数据用于模板预览"""
return {
"folder_name": "示例项目",
"folder_path": "/path/to/example/project",
"generation_time": datetime.now().isoformat(),
"statistics": {
"total_files": 15,
"total_folders": 5,
"total_size_human": "2.5 MB",
"code_files": 8,
"doc_files": 4,
"image_files": 3
},
"structure": {
"type": "folder",
"name": "示例项目",
"files": [
{"name": "README.md", "file_type": "documentation"},
{"name": "main.py", "file_type": "code"},
{"name": "config.json", "file_type": "config"}
],
"folders": [
{
"name": "src",
"files": [
{"name": "app.py", "file_type": "code"},
{"name": "utils.py", "file_type": "code"}
]
},
{
"name": "docs",
"files": [
{"name": "api.md", "file_type": "documentation"},
{"name": "guide.md", "file_type": "documentation"}
]
}
]
},
"complexity": {
"level": "medium",
"score": 6,
"description": "项目结构中等复杂度,需要适当文档"
},
"recommendations": [
"增加代码注释和API文档",
"建立代码规范和最佳实践指南"
]
}
def generate_template_suggestions(variables: List[str]) -> List[str]:
"""生成模板使用建议"""
suggestions = []
if not variables:
suggestions.append("模板没有使用任何变量,将输出静态内容")
return suggestions
# 检查常用变量
common_vars = ['folder_name', 'folder_path', 'statistics', 'structure', 'generation_time']
missing_vars = [var for var in common_vars if var not in variables]
if missing_vars:
suggestions.append(f"建议添加常用变量: {', '.join(missing_vars)}")
if 'statistics' in variables:
suggestions.append("可以使用 statistics.total_files 等属性访问具体统计信息")
if 'structure' in variables:
suggestions.append("可以使用 structure.items 遍历文件夹内容")
if 'generation_time' in variables:
suggestions.append("可以使用 |format_datetime 过滤器格式化时间")
return suggestions
def convert_template_to_html(template_content: str) -> str:
"""将Jinja2模板转换为HTML格式显示"""
html_content = [
"<!DOCTYPE html>",
"<html>",
"<head>",
" <meta charset='UTF-8'>",
" <title>模板预览</title>",
" <style>",
" body { font-family: Arial, sans-serif; margin: 20px; }",
" .template { background: #f5f5f5; padding: 15px; border-radius: 5px; }",
" .variable { color: #0066cc; font-weight: bold; }",
" .comment { color: #009900; font-style: italic; }",
" </style>",
"</head>",
"<body>",
" <h1>模板内容</h1>",
" <div class='template'>",
f" <pre>{template_content}</pre>",
" </div>",
"</body>",
"</html>"
]
return "\n".join(html_content)
def convert_template_to_markdown(template_content: str) -> str:
"""将Jinja2模板转换为Markdown格式显示"""
lines = ["# 模板内容", ""]
# 简单的语法高亮处理
for line in template_content.split('\n'):
if '{%' in line:
# 处理控制语句
formatted_line = f"```jinja2\n{line}\n```"
elif '{{' in line:
# 处理变量输出
formatted_line = line.replace('{{', '**`').replace('}}', '`**')
else:
formatted_line = line
lines.append(formatted_line)
return "\n".join(lines)