@@ -99,38 +99,115 @@ def find_code(
9999 project_folder : str = Field (description = "The absolute path to the project folder. It must be absolute path." ),
100100 pattern : str = Field (description = "The ast-grep pattern to search for. Note, the pattern must have valid AST structure." ),
101101 language : str = Field (description = "The language of the query" , default = "" ),
102- ) -> List [dict [str , Any ]]:
102+ max_results : Optional [int ] = Field (default = None , description = "Maximum results to return" ),
103+ output_format : str = Field (default = "text" , description = "'text' or 'json'" ),
104+ ) -> str | List [dict [str , Any ]]:
103105 """
104106 Find code in a project folder that matches the given ast-grep pattern.
105107 Pattern is good for simple and single-AST node result.
106108 For more complex usage, please use YAML by `find_code_by_rule`.
107109
108- Internally calls: ast-grep run --pattern <pattern> --json <project_folder>
110+ Internally calls: ast-grep run --pattern <pattern> [--json] <project_folder>
111+
112+ Output formats:
113+ - text (default): Simple file:line:content format, ~75% fewer tokens
114+ - json: Full match objects with metadata
115+
116+ Example usage:
117+ find_code(pattern="class $NAME", max_results=20) # Returns text format
118+ find_code(pattern="class $NAME", output_format="json") # Returns JSON with metadata
109119 """
110- args = ["--pattern" , pattern , "--json" ]
120+ if output_format not in ["text" , "json" ]:
121+ raise ValueError (f"Invalid output_format: { output_format } . Must be 'text' or 'json'." )
122+
123+ args = ["--pattern" , pattern ]
111124 if language :
112125 args .extend (["--lang" , language ])
113- args .append (project_folder )
114- result = run_ast_grep ("run" , args )
115- return json .loads (result .stdout )
126+
127+ if output_format == "json" :
128+ # JSON format - return structured data
129+ result = run_ast_grep ("run" , args + ["--json" , project_folder ])
130+ matches = json .loads (result .stdout .strip () or "[]" )
131+ # Limit results if max_results is specified
132+ if max_results is not None and len (matches ) > max_results :
133+ matches = matches [:max_results ]
134+ return matches
135+ else :
136+ # Text format - return plain text output
137+ result = run_ast_grep ("run" , args + [project_folder ])
138+ output = result .stdout .strip ()
139+ if not output :
140+ output = "No matches found"
141+ else :
142+ # Apply max_results limit if specified
143+ lines = output .split ('\n ' )
144+ non_empty_lines = [line for line in lines if line .strip ()]
145+ if max_results is not None and len (non_empty_lines ) > max_results :
146+ # Limit the results
147+ non_empty_lines = non_empty_lines [:max_results ]
148+ output = '\n ' .join (non_empty_lines )
149+ header = f"Found { len (non_empty_lines )} matches (limited to { max_results } ):\n "
150+ else :
151+ header = f"Found { len (non_empty_lines )} matches:\n "
152+ output = header + output
153+ return output
116154
117155@mcp .tool ()
118156def find_code_by_rule (
119157 project_folder : str = Field (description = "The absolute path to the project folder. It must be absolute path." ),
120158 yaml : str = Field (description = "The ast-grep YAML rule to search. It must have id, language, rule fields." ),
121- ) -> List [dict [str , Any ]]:
159+ max_results : Optional [int ] = Field (default = None , description = "Maximum results to return" ),
160+ output_format : str = Field (default = "text" , description = "'text' or 'json'" ),
161+ ) -> str | List [dict [str , Any ]]:
122162 """
123163 Find code using ast-grep's YAML rule in a project folder.
124164 YAML rule is more powerful than simple pattern and can perform complex search like find AST inside/having another AST.
125165 It is a more advanced search tool than the simple `find_code`.
126166
127167 Tip: When using relational rules (inside/has), add `stopBy: end` to ensure complete traversal.
128-
129- Internally calls: ast-grep scan --inline-rules <yaml> --json <project_folder>
168+
169+ Internally calls: ast-grep scan --inline-rules <yaml> [--json] <project_folder>
170+
171+ Output formats:
172+ - text (default): Simple file:line:content format, ~75% fewer tokens
173+ - json: Full match objects with metadata
174+
175+ Example usage:
176+ find_code_by_rule(yaml="id: x\\ nlanguage: python\\ nrule: {pattern: 'class $NAME'}", max_results=20)
177+ find_code_by_rule(yaml="...", output_format="json") # For full metadata
130178 """
131- args = ["--inline-rules" , yaml , "--json" , project_folder ]
132- result = run_ast_grep ("scan" , args )
133- return json .loads (result .stdout )
179+ if output_format not in ["text" , "json" ]:
180+ raise ValueError (f"Invalid output_format: { output_format } . Must be 'text' or 'json'." )
181+
182+ args = ["--inline-rules" , yaml ]
183+
184+ if output_format == "json" :
185+ # JSON format - return structured data
186+ result = run_ast_grep ("scan" , args + ["--json" , project_folder ])
187+ matches = json .loads (result .stdout .strip () or "[]" )
188+ # Limit results if max_results is specified
189+ if max_results is not None and len (matches ) > max_results :
190+ matches = matches [:max_results ]
191+ return matches
192+ else :
193+ # Text format - return plain text output
194+ result = run_ast_grep ("scan" , args + [project_folder ])
195+ output = result .stdout .strip ()
196+ if not output :
197+ output = "No matches found"
198+ else :
199+ # Apply max_results limit if specified
200+ lines = output .split ('\n ' )
201+ non_empty_lines = [line for line in lines if line .strip ()]
202+ if max_results is not None and len (non_empty_lines ) > max_results :
203+ # Limit the results
204+ non_empty_lines = non_empty_lines [:max_results ]
205+ output = '\n ' .join (non_empty_lines )
206+ header = f"Found { len (non_empty_lines )} matches (limited to { max_results } ):\n "
207+ else :
208+ header = f"Found { len (non_empty_lines )} matches:\n "
209+ output = header + output
210+ return output
134211
135212def run_command (args : List [str ], input_text : Optional [str ] = None ) -> subprocess .CompletedProcess :
136213 try :
0 commit comments