Serena: Symbol-Level Code Understanding
Navigate and manipulate code at the symbol level using IDE-like semantic analysis powered by Language Server Protocol (LSP).
How You Can Access Serena
You may have Serena available in one or both of these ways:
Option 1: Direct MCP Tools (if configured by your orchestrator) Check your available tools for:
-
find_symbol , find_referencing_symbols
-
Symbol lookup
-
rename_symbol , replace_symbol_body
-
Refactoring
-
insert_after_symbol , insert_before_symbol
-
Precise insertions
-
onboarding , activate_project
-
Project understanding
-
write_memory , read_memory
-
Save context
-
And 25+ more LSP-powered tools
If you see these tools, use them directly - they provide full Serena capabilities!
Option 2: CLI Commands (always available via execute_command) You can run serena commands using:
execute_command("uvx --from git+https://github.com/oraios/serena serena <command>")
This skill focuses on CLI usage patterns. If you have direct MCP tools, prefer those for better integration.
Purpose
The serena skill provides access to Serena, a coding agent toolkit that transforms text-based LLMs into symbol-aware code agents. Unlike traditional text search (ripgrep) or structural search (ast-grep), Serena understands code semantics through LSP integration.
Key capabilities:
-
Symbol Discovery: Find classes, functions, variables, and types by name across 30+ languages
-
Reference Tracking: Discover all locations where a symbol is referenced or used
-
Precise Editing: Insert code at specific symbol locations with surgical precision
Serena operates at the symbol level rather than the text or syntax level, providing true IDE-like understanding of code structure, scope, and relationships.
When to Use This Skill
Use the serena skill when you need symbol-level code understanding:
Code Navigation:
-
Finding where a class, function, or variable is defined
-
Discovering all places where a symbol is used (call sites, imports, references)
-
Understanding code dependencies and relationships
-
Tracing execution flow through function calls
Code Understanding:
-
Analyzing impact of changes to a function or class
-
Understanding inheritance hierarchies and type relationships
-
Identifying dead code (symbols never referenced)
-
Mapping API usage patterns across a codebase
Code Refactoring:
-
Renaming symbols while tracking all usage locations
-
Adding methods or fields to specific classes
-
Inserting error handling after specific function calls
-
Modifying all call sites of a deprecated function
Choose serena over file-search (ripgrep/ast-grep) when:
-
You need to understand symbol semantics (not just text patterns)
-
You want to track references across files and modules
-
You need precise insertion points based on code structure
-
You're working with complex, multi-file codebases
Still use file-search when:
-
Searching for text patterns, comments, or strings
-
Finding todos, security issues, or documentation
-
You need faster, simpler pattern matching
-
Symbol-level precision isn't required
Language Support
Serena uses LSP servers for semantic analysis. Most common languages are supported out-of-the-box:
-
Python (pyright, jedi)
-
JavaScript/TypeScript (typescript-language-server)
-
Rust (rust-analyzer)
-
Go (gopls)
-
Java (jdtls)
-
C/C++ (clangd)
-
C#, Ruby, PHP, Kotlin, Swift, Scala, and 15+ more
The LSP servers provide symbol information for the language you're working with.
Core Operations
- Finding Symbols (find_symbol )
Locate where a symbol is defined in your codebase.
Note: All examples below use the short form serena <command> . The full command is:
uvx --from git+https://github.com/oraios/serena serena <command>
Find a class definition
execute_command("uvx --from git+https://github.com/oraios/serena serena find_symbol --name 'UserService' --type class")
Find a function definition
execute_command("uvx --from git+https://github.com/oraios/serena serena find_symbol --name 'authenticate' --type function")
Find a variable definition
execute_command("uvx --from git+https://github.com/oraios/serena serena find_symbol --name 'API_KEY' --type variable")
Use cases:
-
Locating the definition of a class before modifying it
-
Finding where a function is implemented
-
Understanding where constants are defined
-
Tracing type definitions in typed languages
Output format:
File: src/services/user_service.py Line: 42 Symbol: UserService (class) Context: class UserService(BaseService):
- Finding References (find_referencing_symbols )
Discover all locations where a symbol is used, imported, or referenced.
Find all usages of a class
execute_command("serena find_referencing_symbols --name 'UserService'")
Find all call sites of a function
execute_command("serena find_referencing_symbols --name 'authenticate'")
Find all reads/writes of a variable
execute_command("serena find_referencing_symbols --name 'API_KEY'")
Use cases:
-
Impact analysis before refactoring
-
Finding all call sites of a function
-
Tracking API usage across modules
-
Identifying unused symbols (zero references)
-
Understanding data flow and dependencies
Output format:
Found 12 references to 'authenticate':
-
src/api/routes.py:34 authenticate(user_credentials)
-
src/middleware/auth.py:18 from services import authenticate
-
tests/test_auth.py:56 mock_authenticate = Mock(spec=authenticate) ...
-
Precise Code Insertion (insert_after_symbol )
Insert code at specific symbol locations with surgical precision.
Add a method to a class
execute_command("""serena insert_after_symbol --name 'UserService' --type class --code ' def get_user_by_email(self, email: str) -> Optional[User]: return self.db.query(User).filter_by(email=email).first() '""")
Insert error handling after a function call
execute_command("""serena insert_after_symbol --name 'database_query' --code ' if result is None: raise DatabaseError("Query returned no results") '""")
Add a field to a dataclass
execute_command("""serena insert_after_symbol --name 'User' --type class --code ' email_verified: bool = False '""")
Use cases:
-
Adding methods to existing classes
-
Inserting validation or error handling
-
Adding fields to data structures
-
Injecting logging or monitoring code
-
Implementing missing functionality
Safety features:
-
Respects indentation and code formatting
-
Maintains syntactic validity
-
Positions code correctly within scope
-
Preserves existing code structure
Workflow Patterns
Pattern 1: Safe Refactoring
When changing a function signature or behavior:
Step 1: Find the function definition
serena find_symbol --name 'process_payment' --type function
Step 2: Find all call sites
serena find_referencing_symbols --name 'process_payment'
Step 3: Analyze impact (review output)
[Review all usage locations to understand impact]
Step 4: Make changes with confidence
[Update function and all call sites based on findings]
Pattern 2: Adding Functionality
When extending a class with new methods:
Step 1: Locate the class
serena find_symbol --name 'PaymentProcessor' --type class
Step 2: Verify no conflicts
serena find_symbol --name 'process_refund' --type function
Step 3: Insert new method
serena insert_after_symbol --name 'PaymentProcessor' --type class --code ' def process_refund(self, payment_id: str, amount: float) -> bool: # Implementation here pass '
Pattern 3: Understanding Dependencies
When analyzing code relationships:
Step 1: Find class definition
serena find_symbol --name 'DatabaseManager' --type class
Step 2: Find all usages
serena find_referencing_symbols --name 'DatabaseManager'
Step 3: For each usage, find what symbols use that code
[Repeat reference tracking to build dependency graph]
Pattern 4: Dead Code Detection
When identifying unused code:
Step 1: Find symbol definition
serena find_symbol --name 'legacy_auth_handler'
Step 2: Check references
serena find_referencing_symbols --name 'legacy_auth_handler'
Step 3: If zero references (except definition), mark for removal
[If output shows only the definition, symbol is unused]
Integration with file-search
Serena and file-search (ripgrep/ast-grep) are complementary tools. Use them together:
When to Combine Tools
Use ripgrep THEN serena:
1. Find potential matches with ripgrep (fast, broad)
rg "authenticate" --type py
2. Narrow to specific symbol with serena (precise)
serena find_symbol --name 'authenticate' --type function serena find_referencing_symbols --name 'authenticate'
Use serena THEN ripgrep:
1. Find symbol definition with serena
serena find_symbol --name 'UserService'
2. Search for related patterns with ripgrep
rg "UserService(" --type py # Find direct instantiations rg "class.*UserService" --type py # Find subclasses
Complementary Strengths
Task Best Tool Why
Find string literals ripgrep Text-based, fast
Find TODOs/comments ripgrep Text-based
Find symbol definition serena Symbol-aware
Find all references serena Semantic understanding
Find code patterns ast-grep Syntax-aware
Insert at symbol serena Precise positioning
Search across languages ripgrep Language-agnostic
Understand scope serena LSP semantic info
Best Practices
- Start with Symbol Discovery
Always locate the symbol definition first:
GOOD: Find definition, then references
serena find_symbol --name 'MyClass' serena find_referencing_symbols --name 'MyClass'
AVOID: Searching for references without confirming definition exists
- Use Specific Symbol Types
Narrow searches with --type when possible:
GOOD: Specific type reduces ambiguity
serena find_symbol --name 'User' --type class
LESS PRECISE: May match User function, User variable, etc.
serena find_symbol --name 'User'
- Verify Before Inserting
Always find the symbol before inserting code:
GOOD: Verify target exists first
serena find_symbol --name 'PaymentService' --type class
[Review output to confirm correct class]
serena insert_after_symbol --name 'PaymentService' --code '...'
RISKY: Inserting without verification
serena insert_after_symbol --name 'PaymentService' --code '...'
- Review Reference Counts
Check reference output for impact analysis:
Find references and assess impact
serena find_referencing_symbols --name 'deprecated_function'
If 50+ references, plan careful migration
If 0 references, safe to remove
- Combine with git diff
After insertions, verify changes:
serena insert_after_symbol --name 'MyClass' --code '...' git diff # Review actual changes before committing
Supported Languages
Serena supports 30+ languages through LSP integration:
Tier 1 (Fully tested):
- Python, JavaScript, TypeScript, Rust, Go, Java, C/C++
Tier 2 (Community tested):
- C#, Ruby, PHP, Kotlin, Swift, Scala
Tier 3 (Experimental):
- Haskell, Elixir, Clojure, Erlang, Julia, R, and more
For the complete list and setup instructions, see Serena language support docs.
Limitations
When NOT to Use Serena
Searching text/comments: Use ripgrep instead
WRONG TOOL: Serena doesn't search comments
serena find_symbol --name "TODO"
RIGHT TOOL: Use ripgrep for text
rg "TODO"
Generated code: LSP may not index auto-generated files
-
Use ripgrep for build artifacts, generated code
Very large codebases: Symbol indexing can be slow
-
Use ripgrep for initial broad searches
-
Use serena for precise follow-up
Dynamic languages without types: Limited semantic info
-
Python without type hints has reduced precision
-
JavaScript without TypeScript has fewer guarantees
Known Edge Cases
-
Ambiguous symbols: Multiple symbols with same name may require manual disambiguation
-
Macro-generated code: C/C++ macros may confuse LSP
-
Circular dependencies: May affect reference tracking accuracy
-
Incomplete projects: Missing dependencies can reduce LSP effectiveness
Performance Considerations
Token Efficiency
Serena is designed for token-efficient code navigation:
Traditional approach (inefficient)
execute_command("cat entire_file.py") # 1000+ tokens
[Search for symbol manually in output]
Serena approach (efficient)
serena find_symbol --name 'MyClass' # 50 tokens
[Get precise location immediately]
Speed Characteristics
-
Symbol lookup: Near-instant (LSP indexed)
-
Reference finding: Fast (O(log n) with indexing)
-
Code insertion: Instant (direct file modification)
Comparison with alternatives:
-
Ripgrep: Faster for text search (no semantic understanding)
-
AST-grep: Comparable speed (syntax vs semantic)
-
Serena: Slower initial startup (LSP indexing), faster precise queries
Troubleshooting
Symbol Not Found
If find_symbol returns no results:
Verify symbol exists: Use ripgrep to confirm
rg "class MyClass" --type py
Check language server: Ensure LSP is configured for the language
serena status # Check LSP server status
Try case variations: Symbol names are case-sensitive
serena find_symbol --name 'myclass' # Try different cases
Rebuild index: Force LSP to re-index
serena reindex # Rebuild symbol index
Too Many References
If find_referencing_symbols returns hundreds of results:
Use file-search first: Narrow scope with ripgrep
rg "MyClass" src/services/ # Limit to specific directory serena find_referencing_symbols --name 'MyClass' --path src/services/
Filter by reference type: Focus on specific usage patterns
Look for imports only
rg "from .* import.*MyClass" --type py
Prioritize recent changes: Check git history first
git log --all -p -S 'MyClass' --since="1 week ago"
Insertion Failures
If insert_after_symbol fails:
-
Verify symbol exists: Find it first
-
Check syntax: Ensure inserted code is valid
-
Review indentation: Match surrounding code style
-
Test incrementally: Insert small changes first
Resources
-
Serena GitHub
-
Serena Documentation
-
LSP Specification
-
Solid-LSP (Serena's foundation)