Gograph: a Go CLI that parses your codebase for AI agents
Gograph is a local repository indexer for Go projects. It parses your code with the standard AST packages, writes a compact .gograph/graph.json, and then lets humans or AI coding agents ask targeted questions about packages, symbols, calls, routes, SQL, tests, config reads, and code quality.
The README frames it as context infrastructure for coding agents, which is a useful way to think about it. Instead of asking an agent to skim a whole repository and hope it notices the important paths, gograph gives it focused commands like source, callers, context, hotspot, deps, and changes. The implementation is also a good Go example because most of the value comes from ordinary standard-library parsing plus a deliberately simple graph model.
The build step is deliberately boring
The main build path is in internal/cli/cli.go. BuildGraph walks the target repo with internal/scanner/ignore.go, skips directories like .git, .gograph, vendor, node_modules, dist, and build, and ignores generated Go files by suffix or by checking the first few lines for Code generated.
For every remaining .go file it calls parser.ParseFile from internal/parser/parser.go. That detail matters: gograph is not trying to run or compile the target project in the default mode. It parses files one at a time using go/parser.ParseFile, records what it can, and keeps going if one file fails. That makes it useful during active development when the repository might not build.
The extracted data lands in a single graph.Graph struct from internal/graph/graph.go. The model is plain and inspectable: packages, files, symbols, imports, calls, dependencies, HTTP routes, SQL queries, error strings, concurrency operations, tests, interface implementations, and mutation edges. The CLI then writes JSON plus a set of markdown reports under .gograph/.
That simplicity is the point. The generated graph is cheap to inspect, easy to diff, and good enough for navigation tasks where an agent needs to know where to look next.
What the parser actually records
internal/parser/parser.go extracts more than a list of functions. For top-level declarations it records:
- structs, including fields, tags, and embedded types
- interfaces, including method signatures
- vars and consts
- functions and methods, including receiver, signature, line range, doc comment, and arity
Then it walks function bodies for call edges and practical signals. There are small extractors for os.Getenv and viper.Get* config reads, SQL-looking strings passed to methods such as Query, QueryRow, and Exec, custom errors and panics, test-to-production call edges from Test* and Benchmark* functions, and assignments that look like field or global mutations.
The concurrency map is a nice example of pragmatic AST work. It records go statements, channel sends, calls ending in .Lock, .RLock, .Unlock, .RUnlock, .Add, .Wait, and .Do. It is heuristic rather than type-checked, but that is often exactly what a coding agent needs: a fast warning that this part of the code involves goroutines, locks, WaitGroups, or sync.Once.
There is also route extraction for common HTTP method calls such as GET, POST, Handle, and HandleFunc when the first argument is a string literal. Again, it is not pretending to be a framework-aware compiler plugin. It is surfacing navigational clues from syntax.
Default mode versus precise mode
Gograph has two correctness levels, and the distinction is worth copying if you build your own developer tools.
The default gograph build . path is AST-only. It tolerates incomplete code and uses best-effort matching. That is useful in the middle of a refactor or when an agent is working on a half-edited tree.
If you run:
gograph build . --precise
then internal/precise/precise.go runs a slower enrichment pass. It loads packages with golang.org/x/tools/go/packages, builds SSA with ssautil.AllPackages, uses types.Implements for stronger interface satisfaction results, and adds call edges from class hierarchy analysis via golang.org/x/tools/go/callgraph/cha.
That split is a sensible tradeoff. The fast path gives agents context when the repo is messy. The precise path is there before a risky refactor, when you care more about interface implementers and dynamic dispatch than raw speed.
The search commands are the product
Once .gograph/graph.json exists, the query commands avoid re-parsing the whole repository for every question. A few of the more agent-friendly commands are:
gograph source "ValidateToken"
gograph callers "ValidateToken" --no-tests
gograph callees "InitServer" --no-tests
gograph context "ValidateToken"
gograph hotspot --top 20
gograph deps "internal/auth" --transitive
gograph changes
gograph path "CreateUser" "ExecContext"
context is especially useful. In internal/search/context.go, it bundles the node metadata, source, callers, callees, and tests for a symbol. That replaces the usual loop of grep, open file, search callers, search tests, then open more files. For an LLM-driven coding agent, fewer broad file reads usually means less irrelevant context and fewer wrong assumptions.
The code-quality commands are built on the same graph. internal/search/complexity.go parses function bodies on demand and counts branch-inducing AST nodes for a McCabe-style complexity score. coupling calculates package fan-in and fan-out. hotspot ranks by incoming calls. orphans does reachability from entry points rather than simply reporting zero incoming calls.
None of this requires embeddings, a hosted service, or sending source code anywhere.
MCP support is a thin wrapper, not a second system
The MCP server lives in internal/mcp/server.go and uses mark3labs/mcp-go. It runs over stdio:
gograph mcp .
Each MCP tool calls back into the same internal/search functions used by the CLI. The server also attempts to rebuild the graph before handling tool calls, so an agent gets fresh-ish repository context without a separate daemon or SaaS backend.
The current MCP tool set includes the basics you would expect: query, focus, callers, callees, implementers, fields, source, orphans, impact, routes, imports, SQL, errors, embeds, public API, constructors, schema, globals, and mocks. That is enough for an editor agent to ask precise questions instead of wandering through the tree.
Why this is a Go-shaped tool
Gograph works because Go makes this kind of tool fairly approachable. go/parser, go/ast, and go/token give you reliable syntax trees and source positions without building a compiler frontend yourself. When you need stronger answers, go/types, go/packages, and SSA are available as a second layer.
The repository layout mirrors that progression:
internal/scanner/decides which files are worth parsinginternal/parser/turns files into graph nodes and edgesinternal/graph/defines the JSON-friendly data modelinternal/search/implements the user-facing questionsinternal/precise/adds type-checked enrichmentinternal/mcp/exposes selected searches to coding agentsinternal/report/writes markdown summaries for humans and agents
That separation keeps the project easy to read. The parser does extraction, the graph stays dumb, and the searches encode the product behaviour.
Trying it
Install it with Homebrew or Go:
brew install ozgurcd/tap/gograph
# or
go install github.com/ozgurcd/gograph@latest
Then build a graph for a Go project:
gograph build .
For deeper analysis on a compiling repository:
gograph build . --precise
Then query the generated graph:
gograph context "Run"
gograph complexity
gograph concurrency
gograph envs
gograph tests "BuildGraph"
If you are building Go developer tools, gograph is worth reading for its restraint. It does not try to solve semantic code understanding in one jump. It starts with syntax, records the signals that are cheap and useful, and exposes them through commands that match how agents actually investigate repositories.