Understand symbols
This page covers the operations that answer identity questions about your Kotlin code. Each operation takes a position or a name and returns structured JSON describing exactly what the compiler knows about the symbol at that location. Together they let you resolve a symbol to its unique identity, browse the declaration tree of a file, discover symbols across the workspace by name, and enumerate every concrete implementation of an interface or abstract class.
Resolve a symbol
When you point kast at a byte offset in a Kotlin file, it doesn't grep
for a name. It resolves the exact declaration the compiler sees at that
position and returns three fields that uniquely identify the symbol:
fqName, kind, and location. Every other field — return type,
parameters, containing declaration — is context that builds on that
identity triple.
Position-based resolution is what makes kast different from text
matching. Two functions named process in different classes produce
two distinct fqName values. Overloads at the same call site resolve
to the correct overload based on the compiler's type analysis, not a
string match.
{
"symbol": {
"fqName": "com.example.OrderService.processOrder",
"kind": "FUNCTION",
"location": {
"filePath": "/app/src/.../OrderService.kt",
"startLine": 47,
"preview": "processOrder"
},
"returnType": "Order",
"parameters": [
{ "name": "cart", "type": "Cart" }
],
"containingDeclaration": "com.example.OrderService"
}
}
The --offset value is a zero-based byte offset into the file. You
can get it from your editor's cursor position or compute it from a
line and column. kast resolves through references, so pointing at a
call site returns the declaration the call resolves to, not the call
itself.
Outline a file
The outline command returns a nested declaration tree for a single
Kotlin file. Each node carries the same Symbol shape you see in a
resolve response, and child declarations nest inside their parent.
The tree excludes function parameters, anonymous elements, and local
declarations — it shows only the named declarations that form the
file's public and internal structure.
Use outline when you need a quick map of a file's contents without
reading the full source. Agents use it to decide which offset to pass
to resolve or references.
{
"symbols": [
{
"symbol": {
"fqName": "com.example.OrderService",
"kind": "CLASS",
"location": {
"filePath": "/app/src/.../OrderService.kt",
"startLine": 12,
"preview": "class OrderService"
}
},
"children": [
{
"symbol": {
"fqName": "com.example.OrderService.processOrder",
"kind": "FUNCTION",
"location": {
"filePath": "/app/src/.../OrderService.kt",
"startLine": 47,
"preview": "processOrder"
},
"returnType": "Order",
"parameters": [
{ "name": "cart", "type": "Cart" }
]
},
"children": []
},
{
"symbol": {
"fqName": "com.example.OrderService.orderRepository",
"kind": "PROPERTY",
"location": {
"filePath": "/app/src/.../OrderService.kt",
"startLine": 14,
"preview": "val orderRepository"
},
"type": "OrderRepository"
},
"children": []
}
]
}
]
}
The children array nests recursively. A top-level class contains its
member functions and properties, an inner class contains its own
members, and so on. Empty children means the declaration has no
nested named declarations.
Search for workspace symbols
The workspace-symbol command finds declarations across your entire
workspace by name. By default it runs a substring match against symbol
names. You can narrow results with --kind to filter by symbol kind,
or switch to --regex=true for regular expression patterns.
Use this when you know a name (or part of one) but don't know which
file contains it. Agents use it as a discovery step before calling
resolve on a specific match.
{
"symbols": [
{
"fqName": "com.example.OrderService",
"kind": "CLASS",
"location": {
"filePath": "/app/src/.../OrderService.kt",
"startLine": 12,
"preview": "class OrderService"
}
},
{
"fqName": "com.example.CartService",
"kind": "CLASS",
"location": {
"filePath": "/app/src/.../CartService.kt",
"startLine": 8,
"preview": "class CartService"
}
}
],
"page": {
"truncated": false
}
}
When results exceed maxResults, the page object reports
"truncated": true and includes a nextPageToken you can pass in a
follow-up request. Always check page.truncated before assuming you
have every match.
Find implementations
The implementations command takes a position on an interface or
abstract class and returns every concrete implementation in the
workspace. Each result carries its supertypes chain so you can see
the full inheritance path, and the exhaustive flag tells you whether
kast found every implementation within the result cap.
{
"declaration": {
"fqName": "sample.Greeter",
"kind": "INTERFACE"
},
"implementations": [
{
"fqName": "sample.LoudGreeter",
"kind": "CLASS",
"supertypes": ["sample.FriendlyGreeter"]
}
],
"exhaustive": true
}
When exhaustive is true, kast found every implementation within
the maxResults limit. When it's false, more implementations exist
than the cap allowed — increase maxResults or paginate to get the
full set. The supertypes array shows the immediate supertypes of
each implementation, which is useful for understanding intermediate
abstract classes or mixins in the inheritance chain.
Next steps
Now that you can identify symbols and browse the declaration landscape, move on to tracing how those symbols are used and changing them safely.
- Trace usage — find every reference to a symbol and walk call hierarchies with exhaustiveness proof.
- Refactor safely — plan and apply renames with hash-based conflict detection.