Summary
When neighbors or find returns a list of method Symbols as NodeRef objects, the agent has no way to know which of those methods declare clients, producers, or expose routes without calling describe(id=...) on each one individually. The NodeRef schema carries only structural identity fields:
class NodeRef(BaseModel):
id: str
kind: Literal["symbol", "route", "client", "producer"]
fqn: str
symbol_kind: str | None = None
microservice: str | None = None
module: str | None = None
role: str | None = None
There is no capabilities, outbound_edge_kinds, or similar field that would signal "this method also declares an HTTP client" or "this method exposes a route."
Problem
Consider a typical agent workflow:
search("payment processing") → gets a class FQN
neighbors([class_id], 'out', ['DECLARES']) → gets 15 method NodeRefs
- Agent now must decide which methods are interesting for cross-service call tracing
At step 3, the agent has no signal. It must either:
- Call
describe on all 15 methods (expensive, 15 tool calls)
- Guess based on method names (unreliable)
- Go back to the class-level
describe and use the edge_summary rollup keys (requires the agent to have called describe first, which it may not have done)
If the agent missed the class-level describe call (e.g., it arrived at the class via find or neighbors from another class), it will never learn that one of the methods declares an HTTP client.
Proposed fix
Add a lightweight edge-presence signal to NodeRef for symbol-kind nodes. The field should indicate which interesting outbound edge types exist on the node, without requiring a graph re-query at NodeRef construction time.
Candidate shapes (pick one):
Option A — capabilities field (reuse existing graph data):
class NodeRef(BaseModel):
...
capabilities: list[str] | None = None # e.g. ["HTTP_CLIENT", "MESSAGE_PRODUCER", "MESSAGE_LISTENER"]
This already exists on the Symbol node in Kuzu. NodeRef just doesn't surface it.
Option B — outbound_edges set (computed at query time):
class NodeRef(BaseModel):
...
has_outbound: list[str] | None = None # e.g. ["DECLARES_CLIENT", "EXPOSES"]
This requires an extra query per node, which may be too expensive for batch results.
Option A is preferred because capabilities is already stored on each Symbol row and can be projected without additional queries. The indexer already tags methods with capabilities like HTTP_CLIENT, MESSAGE_PRODUCER, MESSAGE_LISTENER, SCHEDULED_TASK, etc. (see ast_java.py capability inference). Surfacing this in NodeRef gives the agent enough signal to prioritize which methods to describe in detail.
Scope
- Add
capabilities (or equivalent) to NodeRef for symbol-kind nodes
- Populate it from the existing graph data in
_node_ref_from_row
- Update
NodeRef schema description in server.py
- No re-index required (data already exists in the graph)
- This is a schema change to the MCP tool output surface
What this does NOT do
- Does not add
edge_summary to NodeRef (that belongs on NodeRecord in describe)
- Does not add per-row hints (that is a separate design axis)
- Does not change the graph schema or require re-indexing
- Does not affect
NodeRef for route/client/producer kinds (those already carry identifying fields like client_kind, producer_kind, framework)
References
mcp_v2.py lines 261–268 (NodeRef definition)
mcp_v2.py lines 524–555 (_node_ref_from_row)
ast_java.py — capability inference (_METHOD_ANN_TO_CAPABILITY, _TYPE_ANN_TO_CAPABILITY, etc.)
java_ontology.py — VALID_CAPABILITIES set
Summary
When
neighborsorfindreturns a list of method Symbols asNodeRefobjects, the agent has no way to know which of those methods declare clients, producers, or expose routes without callingdescribe(id=...)on each one individually. TheNodeRefschema carries only structural identity fields:There is no
capabilities,outbound_edge_kinds, or similar field that would signal "this method also declares an HTTP client" or "this method exposes a route."Problem
Consider a typical agent workflow:
search("payment processing")→ gets a class FQNneighbors([class_id], 'out', ['DECLARES'])→ gets 15 methodNodeRefsAt step 3, the agent has no signal. It must either:
describeon all 15 methods (expensive, 15 tool calls)describeand use theedge_summaryrollup keys (requires the agent to have calleddescribefirst, which it may not have done)If the agent missed the class-level
describecall (e.g., it arrived at the class viafindorneighborsfrom another class), it will never learn that one of the methods declares an HTTP client.Proposed fix
Add a lightweight edge-presence signal to
NodeReffor symbol-kind nodes. The field should indicate which interesting outbound edge types exist on the node, without requiring a graph re-query atNodeRefconstruction time.Candidate shapes (pick one):
Option A —
capabilitiesfield (reuse existing graph data):This already exists on the
Symbolnode in Kuzu.NodeRefjust doesn't surface it.Option B —
outbound_edgesset (computed at query time):This requires an extra query per node, which may be too expensive for batch results.
Option A is preferred because
capabilitiesis already stored on each Symbol row and can be projected without additional queries. The indexer already tags methods with capabilities likeHTTP_CLIENT,MESSAGE_PRODUCER,MESSAGE_LISTENER,SCHEDULED_TASK, etc. (seeast_java.pycapability inference). Surfacing this inNodeRefgives the agent enough signal to prioritize which methods todescribein detail.Scope
capabilities(or equivalent) toNodeReffor symbol-kind nodes_node_ref_from_rowNodeRefschema description inserver.pyWhat this does NOT do
edge_summarytoNodeRef(that belongs onNodeRecordindescribe)NodeReffor route/client/producer kinds (those already carry identifying fields likeclient_kind,producer_kind,framework)References
mcp_v2.pylines 261–268 (NodeRefdefinition)mcp_v2.pylines 524–555 (_node_ref_from_row)ast_java.py— capability inference (_METHOD_ANN_TO_CAPABILITY,_TYPE_ANN_TO_CAPABILITY, etc.)java_ontology.py—VALID_CAPABILITIESset