66import httpx
77from fastmcp import Context
88from fastmcp .exceptions import ToolError
9+ from loguru import logger
910
1011from core import CodeAliveContext , get_api_key_from_context , log_api_request , log_api_response
1112from utils import handle_api_error
@@ -40,10 +41,29 @@ async def get_artifact_relationships(
4041 """
4142 Retrieve relationship groups for a single artifact by profile.
4243
43- Use this tool to explore an artifact's call graph, inheritance hierarchy,
44- or references. This is a drill-down tool — use it AFTER `semantic_search`,
45- `grep_search`, legacy `codebase_search`, or `fetch_artifacts` when you need
46- to understand how an artifact relates to others in the codebase.
44+ Use this tool to expand the relationship graph around one known artifact:
45+ call graph edges, inheritance hierarchy, or references.
46+
47+ Important usage rules:
48+ - This is a graph expansion tool, not a search tool. The `identifier`
49+ must be an exact artifact identifier returned by `semantic_search`,
50+ `grep_search`, legacy `codebase_search`, or `fetch_artifacts`.
51+ - Do not pass a repository name, file path, class name, method name, or
52+ guessed symbol name unless it is the full identifier from a prior
53+ tool result.
54+ - If `found=false` or the backend returns a not-found/inaccessible
55+ error, get a fresh identifier with `semantic_search`, `grep_search`,
56+ `codebase_search`, or `fetch_artifacts` before retrying. Repeating
57+ the same guessed identifier usually repeats the same failure.
58+ - Relationships are primarily available for symbol artifacts such as
59+ functions, methods, classes, and interfaces. Plain files and prose
60+ documents can legitimately have no relationship graph.
61+ - The response contains relationship metadata and short summaries, not
62+ full source code. Use `fetch_artifacts` on returned identifiers when
63+ exact source content is needed.
64+ - If any relationship group has `truncated=true`, increase
65+ `max_count_per_type` up to 1000 or narrow the investigation with a
66+ more specific `profile`.
4767
4868 Args:
4969 identifier: Fully qualified artifact identifier from search or fetch results.
@@ -68,17 +88,32 @@ async def get_artifact_relationships(
6888 When the artifact is not found or inaccessible:
6989 {"sourceIdentifier":"...","profile":"callsOnly","found":false}
7090 """
91+ tool_arguments = {
92+ "identifier" : identifier ,
93+ "profile" : profile ,
94+ "max_count_per_type" : max_count_per_type ,
95+ }
96+
7197 if not identifier :
98+ logger .bind (tool = _TOOL_NAME , tool_arguments = tool_arguments ).warning (
99+ "Tool validation failed: artifact identifier is required"
100+ )
72101 raise ToolError (f"[{ _TOOL_NAME } ] Artifact identifier is required." )
73102
74103 if not (1 <= max_count_per_type <= 1000 ):
104+ logger .bind (tool = _TOOL_NAME , tool_arguments = tool_arguments ).warning (
105+ "Tool validation failed: max_count_per_type is out of range"
106+ )
75107 raise ToolError (f"[{ _TOOL_NAME } ] max_count_per_type must be between 1 and 1000." )
76108
77109 # Literal type handles most validation via Pydantic, but direct callers
78110 # (e.g. unit tests) can still pass invalid values — keep as fallback.
79111 api_profile = PROFILE_MAP .get (profile )
80112 if api_profile is None :
81113 supported = ", " .join (PROFILE_MAP .keys ())
114+ logger .bind (tool = _TOOL_NAME , tool_arguments = tool_arguments ).warning (
115+ "Tool validation failed: unsupported relationship profile"
116+ )
82117 raise ToolError (f'[{ _TOOL_NAME } ] Unsupported profile "{ profile } ". Use one of: { supported } ' )
83118
84119 context : CodeAliveContext = ctx .request_context .lifespan_context
@@ -98,7 +133,7 @@ async def get_artifact_relationships(
98133 "maxCountPerType" : max_count_per_type ,
99134 }
100135
101- await ctx .info (f"Fetching { profile } relationships for artifact" )
136+ await ctx .debug (f"Fetching { profile } relationships for artifact" )
102137
103138 full_url = urljoin (context .base_url , "/api/search/artifact-relationships" )
104139 request_id = log_api_request ("POST" , full_url , headers , body = body )
@@ -113,6 +148,12 @@ async def get_artifact_relationships(
113148 return _build_relationships_dict (response .json ())
114149
115150 except (httpx .HTTPStatusError , Exception ) as e :
151+ logger .bind (
152+ tool = _TOOL_NAME ,
153+ tool_arguments = tool_arguments ,
154+ error_type = type (e ).__name__ ,
155+ error = str (e ),
156+ ).warning ("Tool call failed while fetching artifact relationships" )
116157 await handle_api_error (
117158 ctx , e , "get artifact relationships" , method = _TOOL_NAME ,
118159 recovery_hints = {
0 commit comments