Code2Flow MCP Server
by kursk-ye
Verified
from mcp.server.fastmcp import FastMCP
import subprocess
import tempfile
import os
import base64
import json
from pathlib import Path
from typing import List, Optional, Dict, Any
# 创建MCP服务器
mcp = FastMCP("Code2Flow")
@mcp.tool()
def generate_call_graph(
source_paths: List[str],
output_path: Optional[str] = None,
language: Optional[str] = None,
exclude: Optional[List[str]] = None,
include: Optional[List[str]] = None
) -> str:
"""
生成代码调用图并返回PNG图像路径
参数:
- source_paths: 要分析的源代码文件或目录的路径列表
- output_path: 输出PNG文件的路径(可选,默认生成临时文件)
- language: 源代码语言(可选,自动检测)
- exclude: 要排除的文件或目录模式列表(可选)
- include: 要包含的文件或目录模式列表(可选)
返回:
- 生成的PNG图像的资源URI
"""
try:
# 如果没有指定输出路径,创建临时文件
if not output_path:
temp_dir = tempfile.mkdtemp()
output_path = os.path.join(temp_dir, "call_graph.png")
# 构建命令行参数
cmd = ["code2flow"]
# 添加语言参数
if language:
cmd.extend(["--language", language])
# 添加排除模式
if exclude:
for pattern in exclude:
cmd.extend(["--exclude", pattern])
# 添加包含模式
if include:
for pattern in include:
cmd.extend(["--include", pattern])
# 添加输出路径
cmd.extend(["--output", output_path])
# 添加源路径
cmd.extend(source_paths)
# 执行命令
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
# 注册生成的图像作为资源
resource_id = register_image_resource(output_path)
return f"成功生成调用图!资源ID: {resource_id}"
except subprocess.CalledProcessError as e:
print(f"生成调用图失败: {e.stderr}")
return f"生成调用图失败: {e.stderr}"
except Exception as e:
print(f"处理请求时发生错误: {str(e)}")
return f"处理请求时发生错误: {str(e)}"
@mcp.tool()
def check_code2flow_version() -> str:
"""
检查安装的code2flow版本信息
返回:
- code2flow的版本信息
"""
try:
result = subprocess.run(
["code2flow", "--version"],
capture_output=True,
text=True,
check=True
)
return f"已安装的code2flow版本: {result.stdout.strip()}"
except subprocess.CalledProcessError as e:
print(f"获取版本信息失败: {e.stderr}")
return f"获取版本信息失败: {e.stderr}"
except FileNotFoundError:
error_msg = "未找到code2flow命令,请确保已正确安装"
print(error_msg)
return error_msg
except Exception as e:
error_msg = f"处理请求时发生错误: {str(e)}"
print(error_msg)
return error_msg
@mcp.tool()
def analyze_code_complexity(
source_path: str,
language: Optional[str] = None
) -> Dict[str, Any]:
"""
分析源代码复杂度并返回结果
参数:
- source_path: 源代码文件或目录的路径
- language: 源代码语言(可选,自动检测)
返回:
- 复杂度分析结果
"""
try:
# 构建命令行参数
cmd = ["code2flow", "--analyze-only"]
if language:
cmd.extend(["--language", language])
cmd.append(source_path)
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
# 解析输出结果
analysis = {
"file_count": 0,
"function_count": 0,
"class_count": 0,
"raw_output": result.stdout
}
# 尝试从输出中提取一些基本信息
for line in result.stdout.splitlines():
if "Processing" in line and "source file(s)" in line:
try:
analysis["file_count"] = int(line.split("Processing")[1].split("source")[0].strip())
except:
pass
# 这里可以添加更多解析逻辑,但实际输出格式需要根据code2flow的实际输出进行调整
return analysis
except subprocess.CalledProcessError as e:
error_msg = f"分析失败: {e.stderr}"
print(error_msg)
return {"error": error_msg}
except FileNotFoundError:
error_msg = "未找到code2flow命令,请确保已正确安装"
print(error_msg)
return {"error": error_msg}
except Exception as e:
error_msg = f"处理请求时发生错误: {str(e)}"
print(error_msg)
return {"error": error_msg}
def register_image_resource(image_path: str) -> str:
"""注册图像文件作为资源并返回资源ID"""
# 为图像创建一个唯一ID
image_id = os.path.basename(image_path)
@mcp.resource(f"call-graph://{image_id}")
def get_image_resource():
"""获取生成的调用图PNG图像"""
with open(image_path, "rb") as f:
content = f.read()
return content, "image/png"
return f"call-graph://{image_id}"
# 添加帮助资源
@mcp.resource("help://code2flow")
def get_help() -> str:
"""获取code2flow的帮助文档"""
help_text = """
# Code2Flow MCP服务器
此服务器提供Code2Flow功能,用于生成代码调用图。
## 主要功能
- `generate_call_graph`: 生成代码调用图并返回PNG图像
- `check_code2flow_version`: 检查安装的code2flow版本信息
- `analyze_code_complexity`: 分析源代码复杂度
## 使用示例
```python
# 分析单个文件
result = await session.call_tool("generate_call_graph", {
"source_paths": ["path/to/file.py"]
})
# 分析整个目录
result = await session.call_tool("generate_call_graph", {
"source_paths": ["path/to/directory"],
"language": "python",
"exclude": ["**/test/**"]
})
# 检查版本
version_info = await session.call_tool("check_code2flow_version")
# 分析代码复杂度
complexity = await session.call_tool("analyze_code_complexity", {
"source_path": "path/to/analyze",
"language": "python"
})
```
"""
return help_text, "text/markdown"
# 添加支持的语言资源
@mcp.resource("languages://supported")
def get_supported_languages() -> str:
"""获取code2flow支持的编程语言列表"""
languages = {
"python": {
"name": "Python",
"extension": ".py",
"description": "Python编程语言"
},
"js": {
"name": "JavaScript",
"extension": ".js",
"description": "JavaScript编程语言"
},
"ruby": {
"name": "Ruby",
"extension": ".rb",
"description": "Ruby编程语言"
},
"php": {
"name": "PHP",
"extension": ".php",
"description": "PHP编程语言"
}
}
return json.dumps(languages, ensure_ascii=False, indent=2), "application/json"
if __name__ == "__main__":
mcp.run()