MCP tools for cross-file PHP code analysis powered by Tree-sitter. Index an entire project, then query dependency trees, call chains, symbol usages, and inheritance hierarchies.
-
Index your project:
{"tool": "code_index", "project": "my-app"} -
Query the graph:
{"tool": "code_usages", "symbol": "OrderService"} {"tool": "code_deps", "file": "src/Services/OrderService.php"} {"tool": "code_callers", "symbol": "OrderService::processPayment"}
| Tool | Description |
|---|---|
code_index |
Index a PHP project (required before other tools) |
code_deps |
File/namespace dependency tree (imports + imported by) |
code_usages |
Find all references to a symbol (like IDE "Find References") |
code_callers |
Who calls this function/method? (upward chain) |
code_callees |
What does this function call? (downward chain) |
code_hierarchy |
Inheritance / implementation tree |
code_impacted |
Impact analysis: what files change if this changes |
code_graph |
Visual graph export (Mermaid format) |
MCP Tools (function.lua) Index Process (process.lua)
┌────────────┐ ┌──────────────────────┐
│ code_index │──── "index" ────▶│ │
│ code_deps │──── "query:deps"▶│ code_graph_index │
│ code_usages│──── "query:..." ▶│ │
│ ... │◀─── reply ───────│ Holds: │
└────────────┘ │ - Symbol table │
│ - Reference graph │
│ - File imports │
│ - Derived indexes │
└──────────────────────┘
Tools are stateless function.lua entries. The index is a long-running process.lua actor registered as code_graph_index. Communication uses Wippy message passing with request/reply pattern.
Definitions extracted:
- Classes, interfaces, traits, enums (with namespace)
- Methods (with visibility, static flag, parent class)
- Functions
- Class constants
References extracted (13 kinds):
import— use statementsinstantiation—new ClassName()static_call/static_method_call—Class::method()static_access—Class::CONSTmethod_call—$obj->method()type_hint— parameter, return, property typesextends/implements/trait_useinstanceof/catch_typefunction_call— plain function calls
PHP use statements are fully resolved:
- Absolute names (
\App\Foo) → strip leading\ - Imported names → resolved via
usestatements - Aliased names → resolved via
use ... as ... - Unqualified names → prepended with current namespace
- Built-in types (
string,int,self, etc.) → filtered out
butschster/mcp-server— MCP tool discoverytreesittermodule — Tree-sitter PHP parsing (built into Wippy)
code-graph/
├── _index.yaml # Registry: tools, process, libraries
├── wippy.yaml # Module metadata
├── php_extractor.lua # PHP Tree-sitter queries & extraction
├── resolver_lib.lua # PHP namespace resolution
├── graph_lib.lua # Graph data structures & indexes
├── index_lib.lua # Indexing orchestration (scan/parse/build)
├── query_lib.lua # Query execution (usages, deps, callers...)
├── format_lib.lua # Output formatting (ASCII trees, Mermaid)
├── tool_lib.lua # Shared request/reply helper for tools
├── index_process.lua # Long-running index actor
├── code_index.lua # Tool: trigger indexing
├── code_deps.lua # Tool: dependency tree
├── code_usages.lua # Tool: find references
├── code_callers.lua # Tool: caller chain
├── code_callees.lua # Tool: callee chain
├── code_hierarchy.lua # Tool: inheritance tree
├── code_impacted.lua # Tool: impact analysis
└── code_graph_viz.lua # Tool: Mermaid graph export
Tree-sitter is a syntactic parser. It cannot resolve:
- Dynamic dispatch (
$handler->$method()) - Magic methods, service container resolution
- Laravel facades,
app()helper - Variable type inference (except
$this) - Closures as callbacks
For ~80-90% of typical PHP code with type hints and explicit use statements, the analysis is accurate.