Skip to content

MCP server

OpenUnderstand ships an MCP server, so an assistant can analyse a Java project and ask structural questions about it without shelling out or knowing the database schema.

pip install "openunderstand[mcp]"

Register it

Any MCP client works. A client's configuration looks like this:

{
  "mcpServers": {
    "openunderstand": { "command": "openunderstand-mcp" }
  }
}

Run it directly to check it starts: openunderstand-mcp. It speaks over stdio, so it will sit waiting for a client -- that is correct behaviour.

Tools

Tool Does
analyze(source_dir, database="") Parse a Java project and open the result
open_database(path) Open an existing .udb
update(paths, source_root="") Re-analyse changed files, without a full rebuild
list_entities(kind="", limit=100) Entities, optionally filtered by kind
entity_references(longname, reference_kind="", limit=100) References scoped to an entity
entity_metrics(longname, metrics=None) Metric values; omit metrics for all
list_kinds(kind_filter="", references=False) The kind vocabulary

analyze or open_database has to come first -- the others operate on whichever database is open.

After editing source, call update rather than analyze again. It re-analyses only the files named and the files that depend on them, which is about ten times quicker on a mid-sized project -- one file of the 85-file org.json benchmark takes 1.2s against 14s for the whole thing. Each file's previous contribution is deleted first, so a renamed method does not linger under both names, and the result is what a rebuild of the same source would have written. source_root defaults to the root the open database recorded when it was built, so it is usually not needed. A path that no longer exists is purged and not re-analysed, which is how you record a deleted file.

Resources

Reference data the client can read without spending a tool call.

URI Contents
openunderstand://kinds/entity the 237 entity kind names
openunderstand://kinds/reference the 106 reference kind names
openunderstand://metrics metric names this database answers
openunderstand://database what is open, and how many entities

The kind resources matter more than they look. Every filter argument in this server is a kind string, and a wrong filter returns an empty list rather than an error -- so an assistant guessing at names fails silently. Reading the vocabulary first removes the guess.

Prompts

Prompt Does
review_class(longname) size, complexity, what it declares and couples to
complexity_hotspots(limit=10) the most complex methods, ranked
trace_callers(longname) the call graph into a method

Each names the tools it needs, so the model does not have to rediscover the sequence. They also tell it what the numbers do not mean -- trace_callers says an empty result means "none found", not "none exist": this analysis reproduces 90 to 98% of Understand's references at the exact position, so an empty result is usually but not always complete.

kind and reference_kind take Understand's filter grammar: tokens are ANDed, ~ excludes, , ors. So "Class ~Unknown", "Method ~Static" and "Class,Interface" all work.

Example exchange

analyze(source_dir="~/projects/myapp/src")
  → {"database": ".../src/src.udb", "files_analyzed": 128, "entities": 3114}

list_entities(kind="Class ~Unknown", limit=3)
  → com.myapp.Server        Java Class Type Public Member
    com.myapp.Router        Java Class Type Public Member
    com.myapp.Handler       Java Class Type Default Member

entity_metrics(longname="com.myapp.Server",
               metrics=["CountLine", "CountDeclMethod", "SumCyclomatic"])
  → {"CountLine": 214, "CountDeclMethod": 12, "SumCyclomatic": 31}

Notes

The analysis layer prints progress and pass failures to stdout, and stdout is the MCP transport -- anything written there corrupts the protocol. Every tool runs its work with stdout and stderr captured for that reason.

Accuracy is whatever Parity reports. The tools are a faithful view of the database; they do not paper over what the analysis missed.