| name | java-codebase-explore | ||||||
|---|---|---|---|---|---|---|---|
| title | Explore a Java microservices codebase with the java-codebase-rag MCP | ||||||
| description | Use when exploring an unfamiliar Java microservices estate indexed by the java-codebase-rag MCP. Activates on "explore this codebase", "help me understand this system", "map the call graph", "plan a change to service", "onboard onto this code", "write a propose doc for redesign". Teaches exploration *strategy* — when to call MCP vs fall back to rg/file reads/CLI, how to read staleness and confidence, and a catalogue of named missions (understand / plan / onboard / trace / propose / debug) with stopping rules. Complements but does not require docs/AGENT-GUIDE.md. | ||||||
| when_to_load |
|
||||||
| when_not_to_load |
|
This skill is java-codebase-explore: use it when the user’s intent matches the activation phrases in the YAML metadata above (explore the estate, understand the system, map call paths, plan a change, onboard, draft a propose, or debug a symptom). It is the strategy guide (when to use MCP versus other tools, how to sequence exploration, stopping rules, and anti-capabilities). docs/AGENT-GUIDE.md remains the operating manual for exact JSON argument shapes, recovery moves, and slash-style aliases—open or link it when you need those details; this document does not duplicate them. Cross-links below use GitHub blob/master URLs (not ../ paths) so they still resolve when this skill is installed from the Perplexity zip without a full repo checkout.
Before any mission, establish whether the project is indexed and whether graph and vector layers are aligned.
- Run
java-codebase-rag meta(with the same--source-root/--index-dir/ env as the operator setup). Parse the JSON payload: ontology version, counts,edge_counts, paths, and anysuccess: falseoutcome (seedocs/JAVA-CODEBASE-RAG-CLI.mdformetasemantics and exit codes). - Unindexed or missing graph: if there is no usable index or
metaindicates failure, do not pretend the MCP can see the repo. Stop MCP-driven missions untiljava-codebase-rag init(orreprocesswhere appropriate) has been run per the CLI guide; meanwhile userg, README, and build files to orient. - Lance vs Kuzu after
increment:incrementrefreshes the CocoIndex / Lance side without rebuilding Kuzu. Expect stale graph navigation untilreprocess(full Lance reprocess + full Kuzu rebuild). Treatmeta(and lastreprocess/ provenance fields there) as the source of truth for how fresh the graph is; if graph and source disagree, prefer the open file and report staleness.
On any new estate, enumerate before you search-hunt:
find(kind="route", …)— list HTTP seams (paths, methods, microservices).find(kind="client", …)— list outbound clients (Feign,RestTemplate, Kafka, etc.) and targets.- Optionally
find(kind="symbol", filter={"role":"CONTROLLER"})(or equivalentNodeFilter) to anchor web entrypoints.
Identifier-shaped strings (FQN, sym: / route: / client: id, route path, client target): start with resolve, then describe(id=…). Use search / find for discovery when you do not have a concrete identifier yet — not as the primary chain for identifier disambiguation.
You cannot reason reliably about cross-service behaviour until these surfaces exist in your working mental model (or you have consciously fallen back to non-MCP discovery).
When it applies: The user asks how a feature, flow, or implementation detail works (for example “how does X work?” or “explain Y in service Z”).
Goal: A coherent story: entrypoints (routes/controllers), core symbols, and principal callees/callers, with explicit gaps where the graph is silent.
Opening move: java-codebase-rag meta if not yet done this session, then search with a tight query or find when you already know a symbol/route id.
Sequence:
- From
searchhits orfind, pick the best anchor id;describe(id)for full node +edge_summary. - Walk one hop at a time with
neighbors: e.g. outboundCALLS/EXPOSES/ cross-serviceHTTP_CALLS/ASYNC_CALLSas the question requires; inboundCALLS/INJECTSto see who drives a method. - If the feature spans services, pivot through
find(kind="client", …)and matching routes on the callee side rather than deep blindsearchloops.
Stopping rule: You can name the main controller/handler, the core service methods, and the outbound/inbound seams relevant to the question; you have called out anything unresolved (dynamic dispatch, unindexed paths) per anti-capabilities.
Fallbacks: If search is empty or vague, use rg on distinctive strings, read files directly, then return to find/describe with ids you discovered.
When it applies: The user will modify code and needs blast radius, touched routes, or dependency direction (for example renames, API changes, or refactors).
Goal: Bounded sets: callers, callees, injected collaborators, exposed routes, and cross-service consumers/producers touched by the change.
Opening move: find(kind="symbol", …) to resolve the target symbol or search if the qualified name is unknown—then describe.
Sequence:
neighbors(direction="in", edge_types=["CALLS","INJECTS",…])from the target method(s); repeat one hop at a time until diminishing returns.- For API or route changes:
find(kind="route", filter={…})on the owning microservice;neighborswithEXPOSES/HTTP_CALLS/ASYNC_CALLSas needed. - For PR-shaped work, use
java-codebase-rag analyze-pr(CLI only) with a diff when the user has a branch or patch—seedocs/JAVA-CODEBASE-RAG-CLI.md.
Stopping rule: You can list concrete symbols/routes/clients at risk and say what is not knowable from static graph alone (reflection, unindexed services).
Fallbacks: rg for string literals and annotations; git log / git blame for ownership and recency; README / pom.xml / build.gradle for module boundaries.
When it applies: The user is new to a service and wants orientation (for example “I’m new to billing-service; orient me.”).
Goal: A map of that service’s outward HTTP API, inbound dependencies (other services calling it), and outbound clients it uses to call others.
Opening move: find(kind="route", filter={"microservice":"<service>"}) then find(kind="client", filter={"microservice":"<service>"}) (adjust filter keys to match live NodeFilter / tool schema in the MCP client).
Sequence:
- Cluster routes by path prefix;
describeon representativeroute:ids. - For each major route,
neighbors(direction="in", edge_types=["EXPOSES"])to land on handler symbols; for inboundHTTP_CALLS, expect Client callers (thenDECLARES_CLIENTinbound to the declaring method);ASYNC_CALLSinbound still lands on Symbol callers until PR-C. - Use
find(kind="client", …)with the same microservice filter to list outbound integration points; follow outboundHTTP_CALLSfrom each Client (orASYNC_CALLSfrom methods until Producer lands).
Stopping rule: You can summarize how traffic enters the service, what modules/controllers own key paths, and what external systems it calls—without claiming tests, runtime config, or unindexed siblings exist in MCP.
Fallbacks: If the service does not appear in meta / find, assume unindexed or wrong root—verify with meta, then rg + README + adjacent services’ find results; do not assert absence from an empty MCP result alone.
When it applies: The user asks what happens when a route is invoked or how control moves from an entrypoint to clients/async boundaries (for example “POST /orders …” or “from controller A to client B”).
Goal: A directed chain (or tree) of nodes linked by EXPOSES, CALLS, and resolver-backed HTTP_CALLS / ASYNC_CALLS, with explicit notes on confidence and dead ends.
Opening move: find(kind="route", filter={path/http_method/microservice…}) to resolve the route: id.
Sequence:
neighbors(direction="in", edge_types=["EXPOSES"])onto the handling symbol; walkCALLSoutbound method-by-method.- When a method makes outbound HTTP,
neighbors(..., out, ["DECLARES_CLIENT"])then outboundHTTP_CALLSfrom each Client id; for async (pre-PR-C),ASYNC_CALLSmay still be direct from the method Symbol. - Stop at leaves, framework boundaries, or unresolved edges; read
edge.attrs(attrs.confidence,attrs.strategy,attrs.match) and report low-confidence segments as resolver gaps, not as facts.
Stopping rule: You reach a stable leaf (external IO, message publish, clear terminal layer) or you document every unresolved hop with a concrete next non-MCP check.
Fallbacks: rg for string-built URLs; git log on suspect handlers; runtime logs are out of MCP scope—say so.
When it applies: The user is about to author a design/propose document and needs scoped evidence from the codebase (for example a cross-cutting redesign).
Goal: An evidence inventory: affected routes/clients/symbols, dependency direction, and known unknowns suitable for a propose’s “scope / risks / alternatives” sections.
Opening move: java-codebase-rag meta, then find(kind="client", …) or find(kind="route", …) scoped to the hypothesis (e.g. all feign_method clients if replacing Feign).
Sequence:
- Enumerate candidates with
find;describerepresentatives. - Use
neighborsto measure fan-in / fan-out and cross-service edges. - Capture staleness (
incrementvsreprocess) and confidence (attrs.*) limitations explicitly for the propose’s risk section.
Stopping rule: You have a bullet list of graph-backed facts vs gaps, enough that a propose author can write without further blind exploration.
Fallbacks: Repo propose/ and plans/ examples via file reads; java-codebase-rag analyze-pr when there is an existing diff to ground scope.
When it applies: The user has a symptom (error string, latency on a path, unexpected behaviour) and wants likely code loci.
Goal: Short ranked hypotheses tied to symbols/routes, each with a suggested verification step (read file, log line, metric, or reindex).
Opening move: If tied to an HTTP path, find(kind="route", …) for that path/method/service; otherwise search on distinctive error tokens then describe/neighbors.
Sequence:
- From the route/handler,
neighborsforHTTP_CALLS,ASYNC_CALLS,CALLSas the symptom suggests (sync slowness vs fan-out vs messaging). - Read
edge.attrs.confidence/attrs.strategy/attrs.matchon cross-service edges before blaming a downstream. git log/git blameon the hot handler or client when the graph looks stale or incomplete.
Stopping rule: You can point to a small set of files/methods worth inspecting and you have separated MCP-backed claims from speculation.
Fallbacks: rg across logs strings and config; read application*.yml, feature flags, and build/CI files; java-codebase-rag CLI help for commands (analyze-pr, diagnose-ignore) when the question is operational—see CLI doc, not MCP tools.
Use non-MCP work first-class, not as a last resort:
| Layer | Rule of thumb |
|---|---|
rg / grep |
Fastest for exact strings, stack traces, config keys, test code (tests are not indexed). |
| File reads | Single-file truth for logic the graph has not ingested or when increment left Kuzu stale. |
git log / git blame |
History, ownership, “when did this regress”—never MCP. |
README, pom.xml, build.gradle, Docker, workflows |
Build/run/deploy and module structure—never MCP. |
java-codebase-rag CLI |
Lifecycle (init / increment / reprocess), meta, tables, analyze-pr, diagnose-ignore—per docs/JAVA-CODEBASE-RAG-CLI.md. |
If answering the question would require runtime truth (live traffic, actual DB rows, dynamic classpath), say so and stop relying on MCP.
If you find yourself surprised by an empty result, a missing edge, or a stale fact, read this section first. The MCP is a static graph + vector index over indexed Java production code. The following are out of frame:
- Test files. Not indexed. Use
rgfor test discovery. - Build, deploy, runtime story. Not indexed. Read
README.md,pom.xml,build.gradle,Dockerfile,docker-compose.yml,.github/workflows/. - When something changed. Use
git log/git blame. - Reflection and dynamic dispatch.
CALLSedges resolve static method calls; reflective invocations, SPI lookups, and dynamic proxies are invisible. Treat the resolved caller set as a lower bound, not a complete set. - Unindexed services. A service that was never
init'd does not exist from MCP's point of view. Verify withjava-codebase-rag metabefore claiming absence. - Cross-service edge completeness.
HTTP_CALLS/ASYNC_CALLSdepend on the resolver matching a client invocation to a route. Lowconfidenceinedge.attrs(attrs.confidence,attrs.strategy,attrs.match) is a resolver gap signal, not a hallucination. Report it as such. - Stale graph after
increment.incrementupdates Lance but not Kuzu. A graph older than the source tree is normal mid-development. Check the lastreprocesstime viameta; when in doubt, the open file wins.
When MCP and the open file disagree, the file wins. Report the disagreement as evidence of staleness, not as a contradiction.
- Wire fields: Cross-service and resolver-heavy edges carry
edge.attrs(same map surfaced asattrsin payloads). Treatattrs.confidence,attrs.strategy, andattrs.matchas structured hints: low confidence means “resolver could not pin this cleanly,” not “definitely false.” - MCP vs editor: If the open buffer contradicts graph edges (deleted method, renamed class), trust the file and treat MCP as stale until
reprocess(or at least acknowledge incremental lag afterincrement). - Operational check: Use
java-codebase-rag metato compare index health, ontology version (currently 14 in this repo’s README), and recency signals—then decide whether to re-runreprocessbefore continuing a mission.
- Fishing with
search. Repeated broadsearchwithoutfind/describeanchors burns context and hides the real seam structure. - Unbounded graph walks. Hopping
neighborswithout a stopping rule; you must terminate with a stated evidence bar from the mission template. - Empty result = proof of absence. Could be unindexed, wrong table, stale Kuzu, or not in Java production scope—verify per What this MCP cannot see before claiming “does not exist.”
- Ignoring
attrs.confidence/attrs.strategy/attrs.match. Especially onHTTP_CALLS/ASYNC_CALLS, low confidence is a gap signal, not noise to omit from the answer. - Using MCP for CLI-only or repo-meta questions.
analyze-prshape, exit codes, ignore diagnostics → CLI doc +--help, notsearch. - Treating the graph as runtime telemetry. Throughput, live queues, and prod-only config are out of scope—say so and switch tools.
- Over-trusting post-
incrementnavigation. If users ranincrementwithoutreprocess, expect stale Kuzu; prefermeta, file reads, or queuereprocessbefore high-stakes conclusions. - Asking the user for JSON shapes that already ship in MCP tool descriptions and
docs/AGENT-GUIDE.md—read those surfaces instead of inventing parameters.
Five MCP tools:
resolve(identifier, hint_kind)— identifier-shaped lookup (one/many/none).search(query, table, hybrid, limit, filter)— fuzzy locate.find(kind, filter, limit)— structured listing;filteris required.describe(id)— full node +edge_summary.neighbors(ids, direction, edge_types, limit, offset, filter)— one hop;directionandedge_typesare required.
Shorthand: MCP calls are always named JSON arguments per the live tool schema, not positional Python. The lines above are mnemonic; for pagination and optional filters, match the tool surface (e.g. neighbors exposes limit, then offset, then optional filter).
Three node kinds: symbol, route, client. Ids carry a prefix
(sym:, route: / r:, client: / c:).
Ten edge types:
| Group | Edges |
|---|---|
| Type wiring | EXTENDS, IMPLEMENTS, INJECTS |
| Containment | DECLARES, DECLARES_CLIENT |
| Method overrides | OVERRIDES |
| Method calls | CALLS |
| Service boundary | EXPOSES |
| Cross-service | HTTP_CALLS (Client→Route), ASYNC_CALLS (Symbol→Route until Producer) |
For exact argument shapes, recovery playbook, and slash aliases see
docs/AGENT-GUIDE.md in the java-codebase-rag repo.