mariusei/file-scanner-mcp

GitHub: mariusei/file-scanner-mcp

Scantool 是一个基于 tree-sitter 的代码结构分析 MCP 服务器,帮助 AI 助手以更少的 token 精准理解代码库结构。

Stars: 3 | Forks: 0

# Scantool: 用于 Claude 的代码分析 MCP Server [![PyPI version](https://badge.fury.io/py/scantool.svg)](https://pypi.org/project/scantool/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) MCP server 为 AI agent 提供代码库的**结构** —— 类、函数、调用图、导入、热点函数,所有这些都带有精确的行号 —— 而不是原始的文件转储。适用于 **Claude Code**、**Claude Desktop**、**Cursor**、**VS Code** 以及任何 **Model Context Protocol** 客户端。通过 **tree-sitter** 支持 20 多种语言 —— 并通过相同的视角处理代码*和*文档(Markdown、HTML、CSS、SQL、配置文件),这是纯代码工具无法做到的。 **实际测量出的优势(而非空头声称):** ``` "Where is the cache invalidated?" scantool 378 tokens / 1 call grep 9,370 tokens / 4 calls -> 25x less pytest skipif-caching bug scantool solved in 3 calls grep gave up after 13,450 tokens ``` 在真实的 agent 任务中,scantool agent 以 **88% 的事实覆盖率 vs 73%** 击败了仅使用 grep 的 agent —— 锚定更准确,错误文件更少。诚实的范围评估:grep 在普通的字面查找和顶级概览方面仍然占优。Scantool 测量了**两个**维度,并且也会报告损失(`experiments/benchmark/`)。 **零基础设施**:无需构建索引,无需 API 密钥,无需向量数据库,无需下载模型。只需指向一个目录,它就会按需进行扫描。 ## 快速开始 **需要 [uv](https://docs.astral.sh/uv/)**(提供 `uvx` 命令)。如果你还没有安装它,请先安装 —— 没有它,scantool 将无法静默启动: ``` # macOS / Linux / WSL curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### Claude Code ``` # 在所有项目中可用(推荐) claude mcp add --scope user scantool -- uvx scantool # 或仅用于当前项目 claude mcp add scantool -- uvx scantool ``` 重启 Claude Code 即可开始使用。 ### Claude Desktop 添加到配置文件中(macOS 上位于 `~/Library/Application Support/Claude/claude_desktop_config.json`): ``` { "mcpServers": { "scantool": { "command": "uvx", "args": ["scantool"] } } } ``` 配置完成后重启 Claude Desktop。 ### Cursor 添加到 `~/.cursor/mcp.json`(全局)或 `.cursor/mcp.json`(单个项目): ``` { "mcpServers": { "scantool": { "command": "uvx", "args": ["scantool"] } } } ``` ### Windsurf 添加到 `~/.codeium/windsurf/mcp_config.json`: ``` { "mcpServers": { "scantool": { "command": "uvx", "args": ["scantool"] } } } ``` ### VS Code (Copilot agent 模式) 添加到工作区的 `.vscode/mcp.json`: ``` { "servers": { "scantool": { "command": "uvx", "args": ["scantool"] } } } ``` ### Cline 在 Cline 面板中:MCP Servers 图标 → *Configure* 标签页 → *Configure MCP Servers*,然后添加与上述相同的 `mcpServers` 条目。(Cline CLI 会读取 `~/.cline/mcp.json`。) ### 故障排除:找不到 `uvx` `uvx` 随 [uv](https://docs.astral.sh/uv/)(Python 包管理器)一起提供。请先安装它: ``` # macOS / Linux / WSL curl -LsSf https://astral.sh/uv/install.sh | sh # Windows (PowerShell) powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" ``` **安装 uv 后,请重启你的终端**(或打开一个新终端),以便 `uvx` 加入你的 PATH。然后重新运行上方的设置命令。 如果重启终端后仍未找到 `uvx`,请手动将其添加到你的 PATH: ``` # Linux / WSL - 添加到 ~/.bashrc 或 ~/.zshrc: export PATH="$HOME/.local/bin:$PATH" # macOS - 通常开箱即用,但如果不可以: export PATH="$HOME/.local/bin:$PATH" ``` ### 替代方案:从源码安装 ``` git clone https://github.com/mariusei/file-scanner-mcp.git cd file-scanner-mcp uv sync # Claude Code claude mcp add --transport stdio scantool -- uv run --directory /path/to/file-scanner-mcp scantool # Claude Desktop # 使用命令:"uv", args: ["run", "--directory", "/path/to/file-scanner-mcp", "scantool"] ``` ### 与团队共享 (.mcp.json) 在你的项目根目录中添加一个 `.mcp.json` 文件,以便与团队共享配置: ``` { "mcpServers": { "scantool": { "command": "uvx", "args": ["scantool"] } } } ``` Claude Code 会在首次使用时提示团队成员进行批准。 ## 功能 ### 多语言支持 Python, JavaScript, TypeScript, Rust, Go, C/C++, Java, PHP, C#, Ruby, Zig, Swift, SQL (PostgreSQL, MySQL, SQLite), HTML, CSS, SCSS, Markdown, Plain Text, Images ### 结构提取 - 类、方法、函数、导入 - 带有类型注解的函数签名 - 装饰器和属性 - Docstring 和 JSDoc 注释 - 精确的行号(起止范围) ### 分析工具 - **preview_directory**: 智能代码库分析,包含入口点、导入图、调用图和热点函数 (5-10秒) - **scan_file**: 详细的文件结构,包含签名和元数据;`focus=` 逐字读取指定的一个函数/类/代码段及其父级上下文 - **scan_directory**: 紧凑的目录树,内联显示函数/类名 - **search_structures**: 按类型、名称模式、装饰器或复杂度进行过滤 - **list_directories**: 目录树(仅包含文件夹) - **find_divergence**: 审查目录中的同级差异 —— 即破坏了其同级所遵循的调用模式的函数(调用 X 的同级也调用了 Y,但该函数没有);这仅作为审查提示,并非已验证的 bug;如果代码库一致,则不会有输出。相同的段落也会内联显示在 `scan_diff`(更改的代码)和 `preview_directory`(深度)中 ### 输出格式 - 带有框线字符的树状格式 - 用于编程的 JSON 格式 - 可配置的显示选项 ## 用法 ### preview_directory - 代码分析(主要工具) 分析代码库结构,包括入口点、导入图、调用图和热点函数。 ``` preview_directory( directory=".", depth="deep", # "quick", "normal", or "deep" (default: "deep") max_files=10000, # Safety limit (default: 10000) max_entries=20, # Entries per section (default: 20) respect_gitignore=True # Honor .gitignore (default: True) ) ``` **深度级别:** - `"quick"`: 仅元数据 (0.5s) - 文件数、大小、类型 - `"normal"`: 架构分析 (2-5s) - 导入、入口点、集群 - `"deep"`: 完整分析 (5-10s) - 包含热点函数和调用图(默认) **输出示例 (depth="deep"):** ``` project/ --- ENTRY POINTS --- main.py:main() @1 backend/application.py:Flask app @15 frontend/index.ts:export default --- CORE FILES (by centrality) --- backend/database.py: imports 0, used by 15 files backend/auth.py: imports 1, used by 8 files shared/utils.py: imports 2, used by 12 files --- ARCHITECTURE --- Entry Points: 25 files Core Logic: 68 files Plugins: 15 files Tests: 42 files --- HOT FUNCTIONS (most called) --- get_database() (function): called by 41, calls 1 @backend/database.py authenticate() (function): called by 23, calls 5 @backend/auth.py validate_input() (function): called by 15, calls 2 @shared/utils.py Analysis: 486 files in 4.82s (layer1+layer2) ``` **使用场景:** - 首次探索代码库 - 了解多模态项目(前端/后端/数据库) - 查找关键函数(热点) - 识别入口点 ### scan_file - 详细的文件分析 ``` scan_file( file_path="path/to/file.py", focus=None, # Read ONE node verbatim by name ("query", # "DatabaseManager.query", a markdown heading) # instead of guessing line ranges — see below show_signatures=True, # Include function signatures with types show_decorators=True, # Include @decorator annotations show_docstrings=True, # Include first line of docstrings show_complexity=False, # Show complexity metrics condense=True, # Condensed skeletons (set False for verbatim lines) budget=None, # Approx token cap for skeletons — least salient # functions degrade first, output stays predictable output_format="tree" # "tree" or "json" ) ``` **输出示例:** ``` example.py (1-57) - file-info: 1.4KB modified: 2 hours ago - imports: import statements (3-5) - class: DatabaseManager (8-26) "Manages database connections and queries." - method: __init__ (self, connection_string: str) (11-13) - method: connect (self) (15-17) "Establish database connection." - method: query (self, sql: str) -> list (24-26) "Execute a SQL query." return self.cursor.execute(sql).fetchall() - function: main () (53-57) "Main entry point." ``` 函数还会将其实现显示为压缩的方法 骨架:即没有行号的伪代码行,其中带有条件的控制流、 调用和返回都会被保留,而简单的语句会被折叠为 `…` (逐字读取的行始终带有 `N |` 行号 —— 这就是你区分 它们的方法)。骨架分为两个层级:最突出的函数(根据 entropy、 uniqueness 和 centrality)获得全深度,其他所有函数获得 浅层的 depth-2 大纲 —— 这是根据测量得出的最佳 token 事实覆盖率。 标记是纯 ASCII 字符,因为框线字形每个会消耗 2-3 个 BPE token。 传入 `condense=False` 可获取带有行号的摘录(仅限顶层)。 压缩会根据语言进行适配:命令式语言(Python、TypeScript、 Go、Rust、Java 等)默认获取折叠骨架,声明式语言(CSS、 SQL、HTML)保留其内容,仅丢弃空格、注释和结束 标点符号,而文本/配置保持原样 —— 如果没有安全折叠的余地, 则原样显示原始摘录。 #### focus= — 读取步骤 在扫描或搜索定位到节点后,传入 `focus=` 即可逐字 读取该函数/类/方法/标题 —— 而无需为 Read/cat/sed 猜测行 范围: ``` scan_file(file_path="example.py", focus="DatabaseManager.query") ``` ``` focus: DatabaseManager.query @24-26 example.py (3-57) - import statements @3 - DatabaseManager @8 # Manages database connections and queries. - __init__ (self, connection_string: str) @11 - connect (self) @15 # Establish database connection. - disconnect (self) @19 # Close database connection. - query (self, sql: str) -> list @24 # Execute a SQL query. 24 | def query(self, sql: str) -> list: 25 | """Execute a SQL query.""" 26 | return [] - UserService @29 # Handles user-related operations. - validate_email (email: str) -> bool @48 # Validate email format. - main () @53 # Main entry point. ``` 文件的其余部分保持为 depth-1 骨架,因此该节点会连同 其父级上下文一起返回。名称分三个层级进行解析:精确匹配、限定 路径(`ClassA.method`,也适用于 Markdown 标题),然后是 不区分大小写的子字符串;模棱两可的名称将返回合格的 候选列表,而不是进行猜测。根据真实 agent 任务的测量结果 (`experiments/benchmark/M2C.md`):在读取 token 数量比猜测 cat/sed 行范围少 75% 的情况下,获得了同等的答案质量。 ### scan_file_content - 直接分析内容 无需文件路径即可扫描内容。适用于远程文件、API 或内存中的内容。 ``` scan_file_content( content="def hello(): pass\n\nclass MyClass:\n pass", filename="example.py", # Extension determines parser show_signatures=True, show_decorators=True, show_docstrings=True, show_complexity=False, output_format="tree" ) ``` ### scan_directory - 紧凑概览 显示目录树,内联包含类/函数名。 ``` scan_directory( directory="./src", pattern="**/*", # Glob pattern max_files=None, # File limit respect_gitignore=True, # Honor .gitignore exclude_patterns=None, # Additional exclusions output_format="tree" # "tree" or "json" ) ``` **输出示例:** ``` src/ (22 files, 15 classes, 127 functions, 89 methods) ├─ languages/ │ ├─ python.py (1-329) [11.9KB, 2 hours ago] - PythonLanguage │ ├─ typescript.py (1-505) [18.9KB, 1 day ago] - TypeScriptLanguage │ └─ rust.py (1-481) [17.6KB, 3 days ago] - RustLanguage ├─ scanner.py (1-232) [8.8KB, 5 mins ago] - FileScanner └─ server.py (1-735) [27.2KB, just now] - scan_file, scan_directory, ... ``` **Pattern 示例:** ``` # 特定文件类型 scan_directory("./src", pattern="**/*.py") # 多种类型 scan_directory("./src", pattern="**/*.{py,ts,js}") # 浅层扫描(深入 1 层) scan_directory(".", pattern="*/*") # 排除目录 scan_directory(".", exclude_patterns=["tests/**", "docs/**"]) ``` ### search_structures - 查找和过滤 ``` # 查找测试函数 search_structures( directory="./tests", type_filter="function", name_pattern="^test_" ) # 查找以 "Manager" 结尾的类 search_structures( directory="./src", type_filter="class", name_pattern=".*Manager$" ) # 查找带有 @staticmethod 的函数 search_structures( directory="./src", has_decorator="@staticmethod" ) # 查找复杂函数(>100 行) search_structures( directory="./src", type_filter="function", min_complexity=100 ) ``` ### list_directories - 文件夹结构 显示不含文件的目录树。 ``` list_directories( directory=".", max_depth=3, # Maximum depth (default: 3) respect_gitignore=True # Honor .gitignore (default: True) ) ``` **输出示例:** ``` /Users/user/project/ ├─ src/ │ ├─ components/ │ ├─ services/ │ └─ utils/ ├─ tests/ │ ├─ unit/ │ └─ integration/ └─ docs/ ``` ## 输出契约 默认的输出格式就是 API:LLM agent 会直接 不加甄别地消费 scantool 的输出,因此格式偏移 就是消费者中的行为偏移(在 `experiments/benchmark/M2B.md` 中测量)。由此带来两个结果: - **默认值即测量出的最优解 —— 参数是逃生舱。** 每一个默认值(双层压缩、显著性选择、骨架 深度、按语言区分的紧凑与逐字模式)都有 `experiments/condensation/`、`experiments/entropy_metrics/` 和 `experiments/benchmark/` 中的测量数据作为支撑。只有在特定情况 需要时才覆盖它们,而不是作为风格偏好。 - **默认格式被 golden 测试冻结**(`tests/test_golden.py`, 快照位于 `tests/golden/`)。刻意的格式更改需要 刻意的快照更新(`UPDATE_GOLDEN=1 uv run pytest tests/test_golden.py`);意外更改会导致 CI 失败。依赖 环境的部分(文件大小/mtime、git churn、delta 内存)位于 冻结层之外。同级差异是代码的纯函数,因此它 也被冻结(`tests/golden/consensus.txt`,测试 fixture 位于 `tests/golden/consensus_fixture/`)。 ## 支持的语言 | 扩展名 | 语言 | 提取的元素 | |-----------|----------|-------------------| | `.py`, `.pyw` | Python | 类、方法、函数、导入、装饰器、docstring | | `.js`, `.jsx`, `.mjs`, `.cjs` | JavaScript | 类、方法、函数、导入、JSDoc 注释 | | `.ts`, `.tsx`, `.mts`, `.cts` | TypeScript | 类、方法、函数、导入、类型注解、JSDoc | | `.rs` | Rust | 结构体、枚举、trait、impl 块、函数、use 语句 | | `.go` | Go | 类型、结构体、接口、函数、方法、导入 | | `.c`, `.h` | C | 函数、结构体、枚举、include | | `.cpp`, `.hpp`, `.cc`, `.hh` | C++ | 类、函数、命名空间、模板、include | | `.java` | Java | 类、方法、接口、枚举、注解、导入 | | `.php` | PHP | 类、方法、函数、trait、接口、命名空间 | | `.cs` | C# | 类、方法、属性、结构体、枚举、命名空间 | | `.rb` | Ruby | 模块、类、方法、单例方法 | | `.zig` | Zig | 函数、结构体、枚举、联合体、测试 | | `.swift` | Swift | 类、结构体、枚举、协议、函数、扩展 | | `.sql` | SQL | 表、视图、函数、存储过程、索引、列 | | `.html` | HTML | 文档结构、元素、属性 | | `.css` | CSS | 选择器、属性、媒体查询 | | `.scss` | SCSS | 选择器、mixin、变量、嵌套 | | `.md` | Markdown | 标题 (h1-h6)、具有层级结构的代码块 | | `.txt` | Plain Text | 章节、段落 | | `.png`, `.jpg`, `.gif`, `.webp` | Images | 格式、尺寸、颜色、内容类型 | 所有文件都会自动包含元数据(大小、修改日期、权限)。 ## 使用场景 ### 代码导航 - 不熟悉代码库的结构概览 - 了解文件组织结构 - 使用精确的行范围进行导航 ### 重构 - 识别类和函数边界以进行安全拆分 - 查找特定模式的实现 - 定位复杂度超过阈值的函数 ### 代码审查 - 生成结构差异 - 查找带有特定装饰器的函数 - 识别测试覆盖盲区 - 同级差异:找出打破了其跨存储库同级所遵循的调用模式的已更改函数(这很可能是回归 —— 需通过阅读来判定) ### 文档 - 自动生成带行号的目录 - 提取 API 签名 - 将结构化数据提供给分析工具(JSON 输出) ### AI 代码辅助 - 主要的探索工具(取代 ls/grep/find 工作流) - 为 LLM 上下文窗口智能拆分大文件 - 提取具有精确边界的代码段 - 跨代码库搜索模式 - 减少 token 使用:首先获取结构,仅在需要时读取内容 ## 架构 ``` scantool/ ├── server.py # FastMCP server (stdio + HTTP entry points) ├── scanner.py # Core scanning logic using tree-sitter ├── formatter.py # Tree formatting with box-drawing characters ├── code_map.py # Architecture analysis (Layer 1 + 2) ├── call_graph.py # Hot functions, centrality analysis ├── preview.py # Quick directory preview └── languages/ # Unified language system (one file per language) ├── base.py # BaseLanguage - all languages inherit from this ├── models.py # StructureNode, CallInfo, ImportInfo, etc. ├── python.py # PythonLanguage ├── typescript.py ├── rust.py └── ... # 20+ languages ``` ## HTTP 传输(高级) 适用于 stdio 无法工作的环境,或者需要在多个客户端之间共享服务器的情况: ``` # 启动 HTTP server uvx --from scantool scantool-http # 默认监听端口 8080(设置 PORT 环境变量来更改) # 将 Claude Code 连接到它 claude mcp add --transport http scantool http://127.0.0.1:8080/mcp ``` 注意:必须单独启动保持 HTTP 服务器运行。对于大多数用户来说,stdio 传输(默认)更简单,也是推荐的方式。 ## 测试 ``` # 运行所有测试 uv run pytest # 运行特定测试 uv run pytest tests/languages/ uv run pytest tests/python/ uv run pytest tests/typescript/ # 运行 coverage uv run pytest --cov=src/scantool # 运行 verbose 输出 uv run pytest -v ``` ## 贡献 有关添加语言支持的详细信息,请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。 ## 许可证 MIT License - 详情请参阅 [LICENSE](LICENSE) 文件。 ## 依赖项 - [FastMCP](https://github.com/jlowin/fastmcp) - MCP server 框架 - [tree-sitter](https://tree-sitter.github.io/) - 解析库 - [uv](https://github.com/astral-sh/uv) - Python 包安装器 ## 已知的限制 ### MCP 工具响应大小限制 Claude Desktop 对 MCP 工具响应强制执行 25,000 个 token 的限制。Claude Code 具有可配置的限制(设置 `MAX_MCP_OUTPUT_TOKENS` 环境变量以进行调整)。 **内置缓解措施:** - `scan_directory()` 使用紧凑的内联格式 - 默认遵守 `.gitignore`(排除 node_modules、.venv 等) - 显示带有相对时间戳的文件元数据 **手动控制:** - 使用 `pattern` 限制范围:`"**/*.py"` vs `"*/*"`(浅层) - 使用 `max_files` 限制处理的文件数量 - 使用 `exclude_patterns` 进行额外排除 - 扫描特定的子目录而不是整个代码库 **对于大型代码库:** ``` # 扫描特定区域 scan_directory("./src", pattern="**/*.py") scan_directory("./tests", pattern="**/*.py") ``` ### Agent 委托 使用 Claude Code 时,要求“探索代码库”可能会委托给无法访问 MCP 工具的 Explore agent。请明确指示:“使用 scantool 扫描代码库”以确保直接使用 MCP 工具。 ## 支持 - [GitHub Issues](https://github.com/mariusei/file-scanner-mcp/issues) - [GitHub Discussions](https://github.com/mariusei/file-scanner-mcp/discussions)
标签:AI辅助编程, Claude, CVE检测, MCP Server, SOC Prime, Tree-sitter, 云安全监控, 代码分析, 凭证管理, 开发工具, 逆向工具, 静态分析