A Language Server Protocol (LSP) server with built-in MCP server for Smali — the bytecode language of Android apps.
- Go to Definition — classes, methods, fields, labels and resource ids
- Find References — cross-file xrefs with inheritance-aware polymorphic matching; string literals too
- Hover — signatures, types, class info, reference counts, registers (
p1is which parameter), the method's frame, string literals, resource ids with their values, opcode docs - Inlay hints — resource names or values after
0x7f…andandroid.Rids, the decimal value after other hex literals - Call Hierarchy — incoming/outgoing call graphs for any method
- Type Hierarchy — supertypes/subtypes navigation for classes
- Code Lens — reference counts on classes, methods and fields; subclass and implementation counts
- Completion — class names (ranked), members including inherited ones, opcodes
- Rename — rename methods, fields, and labels across workspace
- Document Symbols — class outline with methods and fields
- Workspace Symbols — fuzzy search across the entire codebase
- Diagnostics — syntax errors, undefined or duplicate labels, undefined classes in the app's own packages, and what the assembler would reject after a hand edit
- Implementations, document highlight, folding — overrides and implementors, occurrences of a symbol or register in its file, foldable blocks
- Label Navigation — goto/if-*/switch targets,
.catchblocks and switch tables within methods - Every Dalvik instruction parses — 224 opcodes, including
invoke-custom,invoke-polymorphicand method handles
Supported requests
| Request | Notes |
|---|---|
initialize / initialized / shutdown / exit |
workspace folders or rootUri; indexing after initialized, with progress |
textDocument/didOpen didChange didClose |
full sync; the buffer while open, the disk version after close |
textDocument/publishDiagnostics |
syntax errors at once, undefined classes once indexing has finished |
textDocument/definition hover references implementation documentHighlight |
references follow overrides and calls through subclasses |
textDocument/foldingRange |
methods, annotations, switch tables, array data |
textDocument/documentSymbol · workspace/symbol |
outline; fuzzy search, best 500 |
textDocument/codeLens + codeLens/resolve |
counts resolved lazily |
textDocument/inlayHint |
resource names and values, decimal values |
textDocument/completion |
trigger characters L, >, / |
textDocument/prepareRename + rename |
methods, fields, labels |
textDocument/prepareCallHierarchy + callHierarchy/incomingCalls outgoingCalls |
|
textDocument/prepareTypeHierarchy + typeHierarchy/supertypes subtypes |
|
workspace/didChangeWatchedFiles |
**/*.smali and res/values/public.xml, registered dynamically |
workspace/didChangeWorkspaceFolders |
added folders are indexed, removed ones dropped |
$/cancelRequest |
honoured by symbol search and reference lists |
Built-in MCP server for full semantic understanding of decompiled APKs by AI agents — various tools covering indexing, navigation, search, call graphs, and cross-references.
Available tools
| Tool | Description |
|---|---|
smali_index |
Index a directory of smali files |
smali_find_definition |
Go to definition, by position or by class and member name |
smali_search_symbols |
Fuzzy search across classes, methods, fields |
smali_get_stats |
Index statistics (class/method/field/string counts) |
smali_find_references |
Find all references to a symbol, by position or by name (bounded) |
smali_hover |
Get hover info (signatures, types, class details) |
smali_diagnostics |
Compute diagnostics for a file |
smali_document_symbols |
Get document outline (classes, methods, fields) |
smali_search_strings |
Search const-string literals by substring |
smali_call_graph |
Incoming/outgoing call graph for a method |
smali_xref_summary |
Full cross-reference report (subclasses, implementors, callers, field accessors) |
smali_type_hierarchy |
Supertypes and subtypes for a class (inheritance chain) |
smali_read_method |
One method's source, numbered, resource ids annotated |
smali_list_package |
Classes under a package prefix and its sub-packages |
The server communicates over stdio in standard LSP protocol — no daemon, no port, just stdio:
java -jar build/libs/smali-lsp-<version>.jar lspConfigure your editor's LSP client to launch this command for .smali files. The server indexes the workspace on startup and watches it for changes. Warnings go to stderr; --verbose and --log-file <path> give more.
VS Code (via APKLab)
APKLab integrates smali-lsp automatically. Install APKLab -> Run update tools command and the LSP starts when you open a .smali file.
Alternatively, you can set apklab.smaliLspPath to your smali-lsp-<version>.jar.
Neovim (via nvim-lspconfig)
local lspconfig = require('lspconfig')
local configs = require('lspconfig.configs')
if not configs.smali_lsp then
configs.smali_lsp = {
default_config = {
cmd = { 'java', '-jar', '/path/to/smali-lsp-<version>.jar', 'lsp' },
filetypes = { 'smali' },
root_dir = function(fname)
return lspconfig.util.root_pattern('AndroidManifest.xml', 'apktool.yml', '.git')(fname)
end,
},
}
end
lspconfig.smali_lsp.setup {}Helix
# ~/.config/helix/languages.toml
[[language]]
name = "smali"
scope = "source.smali"
file-types = ["smali"]
language-servers = ["smali-lsp"]
[language-server.smali-lsp]
command = "java"
args = ["-jar", "/path/to/smali-lsp-<version>.jar", "lsp"]Emacs (eglot)
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'(smali-mode . ("java" "-jar" "/path/to/smali-lsp-<version>.jar" "lsp"))))java -jar build/libs/smali-lsp-<version>.jar mcpRuns as an MCP server over stdio, exposing smali analysis tools to AI agents (Claude, Cursor, etc.).
Add to your MCP config (.vscode/mcp.json,claude_desktop_config.json, Cursor settings, etc.):
{
"mcpServers": {
"smali-mcp": {
"command": "java",
"args": ["-jar", "/path/to/smali-lsp-<version>.jar", "mcp"]
}
}
}Measured on three apktool projects on a 12-core Apple Silicon laptop, JDK 21:
| Mastodon (2,517 files) | ProtonMail (19,637 files) | 76 K-file app | |
|---|---|---|---|
| Index the workspace (cold) | 0.8 s | 1.6 s | 6 s |
| Heap after indexing | — | 178 MB | 659 MB |
| Re-index one file on edit, p95 | — | 0.2 ms | 0.6 ms |
| Workspace symbol search | — | 4–14 ms | 19–25 ms |
Definition, hover, references, rename and code lens are index lookups, well under a millisecond. All 98,686 files of the three apps parse without errors.
Requires Java 17+; the Gradle wrapper (9.8) downloads itself.
./gradlew shadowJarOutput: build/libs/smali-lsp-<version>.jar. ./gradlew installApklab copies it to ~/.apklab/smali-lsp-dev.jar for APKLab.
SmaliTextParser (hand-written) → SmaliParser → WorkspaceIndex → LSP/MCP Providers
↑
WorkspaceScanner (parallel)
- Parser: hand-written lexer and statement parser, 0.04 ms per file, token spans in the AST
- Index: files by canonical URI, class hierarchy sets, packed usage lists retired per file
- Providers: resolve the caret from the AST and answer from the index; one writer thread, requests wait for earlier edits
- Scanner: one coroutine worker per core
See DESIGN.md for the measured costs and the reasoning.
Run the categories separately; each test fork holds a parsed app in memory:
./gradlew test --tests "*.unit.*"
./gradlew test --tests "*.regression.*"
./gradlew test --tests "*.integration.*"The integration category uses apktool projects under test-data/ when present (public app releases work; the directory is gitignored) and skips those checks otherwise.
Copyright (C) 2026 Surendrajat
This project is licensed under the GNU General Public License v3.0.
- @psygate for https://github.com/psygate/smali-antlr4-grammar, the grammar the parser's operand table was derived from