Skip to content

get_file_outline

Declarations under a path, grouped by file, with kind, signature and range. Use it for a package; one small file is cheaper to read.

ArgumentTypeDefaultMeaning
repositorystringnone, requiredThe repository name. Rejected with INVALID_ARGUMENT when empty or padded with whitespace, and with REPOSITORY_NOT_FOUND when the published graph does not hold it.
pathstringnone, requiredA repository-relative file, or a directory whose files are all wanted. An absolute path is rejected, and so is any .. segment; a trailing / is trimmed.
kindstringunsetKeeps only rows whose kind is exactly this string.
include_membersbooleanfalseAdds the rows that belong to the declaration above them: field, property, enum_member and variant. They are off by default because on a real file they are about half the payload.
limitinteger200Symbols per page. Must be between 1 and 500; anything else is rejected with INVALID_ARGUMENT.
cursorstringunsetThe opaque next_cursor of a previous page of the same query.
response_formatstringconciseconcise or detailed. detailed adds stable_key and canonical_identity, and restores the fully qualified signature; in the compact view it is also what puts a signature on a row at all. Any other value is rejected with INVALID_ARGUMENT.
viewstringcompactThe granularity of the answer, never a different answer. compact states the repository, the package and whatever every declaration shares once, and lists the declarations by file as name@start-end entries. full is the field-per-row shape. files answers only which files hold the page’s declarations and how many each holds. Any other value is rejected with INVALID_ARGUMENT.

This is how to read the shape of code without opening it: what is declared under a path, of what kind and on which lines.

By default the answer is the compact view. results is one object stating the repository, the path asked about, the package when the whole page sits in one — packages when it does not — and then whatever every declaration of the page shares: kind when they are all of one kind, exported when they are all visible or all not. files groups the declarations by file, and an entry of a group’s at is name@start, or name@start-end when the declaration spans more than one line, using the qualified name when the row has one. An entry becomes an array when the page could not hoist a column: the elements after the label are appended in a fixed order — the name, the kind, exported or unexported, the signature, the stable key, the canonical identity — skipping everything the header already states and everything concise withholds. There is no languages field: a path’s extension says the language.

With view: "full", results carries repository, path, packages, languages and files, each entry of files having a path and its symbols, and each symbol row carrying name, kind, signature, exported, start_line and end_line, plus qualified_name only when it differs from name.

Those rows are what the rest of the surface accepts: the repository, the path of the group and the qualified name are exactly the triple get_symbol, get_source, find_references and the traversal tools take, so the next call is built out of the answer you just got. Under response_format: "detailed" each row also carries its stable key. Measured over the private benchmark corpus, one directory outline went from 633 to 248 tokens with a single shared kind; a larger directory with no single shared kind or exported went from 3.667 to 3.184 once the second grouping tier below applied.

When kind and exported do not both hoist to the header, results.groups replaces files: each entry states its own kind and exported pair and holds the declarations that share it, grouped by file exactly like the flat page. See when a page groups for the mechanism shared by six tools, and the grouped example below for a captured page.

get_file_outline publishes no coverage counters at all. The four categories grade resolved relations and the confidence behind each one, and an outline returns the declarations of one repository, so none of them applies — total and returned already say how many declarations there are. The compact view omits the coverage key entirely; the full view still writes the four zeros, because that view writes every field by contract.

completeness states how far the page reaches, and the question names a place rather than a symbol, so no failed request for a name can bound it: the only failure that can is a scope of the repository asked for that the index could not read, named in invisible_scopes. There is no fallback pattern here — without a symbol there is nothing to grep for. An outline that lists nothing under a path is saying nothing is declared there, so the verdict is spent where the answer could be mistaken for that proof — a page with no rows, a truncated one, and every LOWER_BOUND — while a page of declarations claims no absence and carries no verdict at all. See the completeness verdict for the two values and the rest of the block.

{
"repository": "kivgraph",
"path": "internal/mcp/instructions.go"
}
{"snapshot_id":30,"total":2,"returned":2,"results":{"repository":"kivgraph","path":"internal/mcp/instructions.go","package":"github.com/Luqueee/kivgraph/internal/mcp","kind":"const","exported":false,"files":[{"file":"internal/mcp/instructions.go","at":["staleServerInstructions@36","serverInstructions@21-27"]}]}}

Both declarations are unexported constants of the same package, so the header carries the package, the kind and the visibility and each entry is the bare label: one line for staleServerInstructions, lines 21 to 27 for serverInstructions. Nothing was dropped — there was nothing left to say per row.

The same call in the full view, the field-per-row shape:

{
"repository": "kivgraph",
"path": "internal/mcp/instructions.go",
"view": "full"
}
{"snapshot_id":30,"snapshot_age_ms":9020,"total":2,"returned":2,"truncated":false,"next_cursor":null,"coverage":{"exact":0,"candidate":0,"unresolved_related":0,"package_level":0},"results":{"repository":"kivgraph","path":"internal/mcp/instructions.go","packages":["github.com/Luqueee/kivgraph/internal/mcp"],"languages":["go"],"files":[{"path":"internal/mcp/instructions.go","symbols":[{"name":"staleServerInstructions","kind":"const","signature":"untyped string","exported":false,"start_line":36,"end_line":36},{"name":"serverInstructions","kind":"const","signature":"untyped string","exported":false,"start_line":21,"end_line":27}]}]}}

When the question is which files declare anything, view: "files" answers with the file list and a count each, and nothing about what is declared:

{
"repository": "kivgraph",
"path": "internal/mcp/instructions.go",
"view": "files"
}
{"snapshot_id":30,"total":2,"returned":2,"results":{"repository":"kivgraph","path":"internal/mcp/instructions.go","files":[{"file":"internal/mcp/instructions.go","declarations":2}]}}

It is the shape of the question over a directory, where the answer is a dozen files rather than one. declarations counts what this page holds for the file, so on a truncated page it is the page’s count and not the file’s total.

These responses come from snapshot 30 of two repositories, kivgraph and mole.

A directory whose declarations do not share one kind or one exported:

{
"repository": "mole",
"path": "internal/admin"
}
{
"snapshot_id": 1,
"total": 32,
"returned": 19,
"results": {
"repository": "mole",
"path": "internal/admin",
"package": "github.com/Luqueee/mole/internal/admin",
"groups": [
{
"kind": "method",
"exported": true,
"files": [{ "file": "internal/admin/admin.go", "at": [
"Stats.OnConnect@30-33", "Server.WithPortController@89-92",
"PortController.RemoveDiscover@74", "Server.Handler@95-104",
"Server.WithPorts@81-84", "Stats.OnDialFail@41-43",
"Stats.OnDisconnect@36-38", "PortController.AddDiscover@73"
] }]
},
{
"kind": "method",
"exported": false,
"files": [{ "file": "internal/admin/admin.go", "at": [
"Server.handleStatus@106-129", "Server.handlePortAdd@150-169",
"Server.handleHealth@131-134", "Server.handlePortDelete@175-188"
] }]
},
{
"kind": "func",
"exported": true,
"files": [{ "file": "internal/admin/admin.go", "at": ["New@54-56", "NewStats@25-27"] }]
},
{
"kind": "type",
"exported": true,
"files": [{ "file": "internal/admin/admin.go", "at": ["PortController@72-75", "Server@58-63", "Stats@16-22"] }]
},
{
"kind": "type",
"exported": false,
"files": [{ "file": "internal/admin/admin.go", "at": ["portRequest@139-141", "snapshot@45-50"] }]
}
]
}
}

Five groups over one file: repository, path and package cover the whole page and stay in the header as always, but kind and exported each take four or more values between them, so neither hoists and every group states its own pair. Nothing here needed a column beyond the pair — no group mixed two signatures or two visibilities inside itself — so every entry is still the bare name@start-end label; a group with a residual disagreement of its own would turn an entry into an array exactly as the flat page does.

limit defaults to 200 and cannot exceed 500. A value outside 1 to 500 is rejected rather than clamped.

truncated is true and next_cursor is a token whenever the page did not exhaust total; the compact view omits both when it did. The cursor is an opaque, checksummed base64url token about 31 characters long — a binary body, where the previous version spelled 314 characters of base64-wrapped JSON — and it is bound to the snapshot identifier, to the sorting version stable-key-v1 and to the query identity: the tool name, repository, path, kind and include_members. Change any of those and the token no longer matches, which fails as CURSOR_INVALID, and so does a token that was edited, truncated, re-encoded or decodes with trailing bytes after its checksum. A token minted by a server of the previous cursor version fails closed the same way: the body declares version 2 and version 1 is never reinterpreted under the new layout. A token issued against an older generation fails as CURSOR_SNAPSHOT_EXPIRED. All of them mean the pagination has to restart. view is not part of the identity, so one cursor can continue the outline in another view.

kind and include_members filter the page after it is taken, so total counts every symbol under the path and returned counts what survived the filter. A page can therefore return fewer rows than limit and still be truncated.

A path the graph does not know is an error naming both the repository and the path, SYMBOL_NOT_FOUND, never an empty page. An empty page would read as “nothing is declared here”, which is a different and more misleading answer.

guidance is never emitted by this tool. Groups follow the order the page first mentions each file, and packages is sorted, so two calls over the same page of the same snapshot produce byte-identical responses.

response_format accepts concise and detailed. Concise drops the stable key, which on a 155-declaration file was half the tokens, and prints the signature the way the declaring source reads it, with the symbol’s own package path removed. Types from other packages keep their path. detailed restores the key, the full signature and canonical_identity. In the compact view the signature is on a row only under detailed: it is the largest field a row can carry, and a reader choosing between declarations is choosing between names.

One small file is cheaper to read than to outline: the outline is a second round trip and it gives you names where the file gives you the code. get_file_outline earns its cost on a directory or a package, where it replaces a dozen reads with one answer, and when you need the ranges before deciding which body to fetch with get_source.