@@ -494,6 +494,44 @@ bool cbm_mcp_get_bool_arg(const char *args_json, const char *key) {
494494 return result ;
495495}
496496
497+ /* Extract a JSON string array. Returns heap-allocated array of heap strings.
498+ * Sets *out_count. Returns NULL only when the key is absent or not an array.
499+ * For an empty array [], returns a non-NULL pointer with *out_count = 0
500+ * so callers can distinguish "not provided" from "explicitly empty". */
501+ static char * * cbm_mcp_get_string_array_arg (const char * args_json , const char * key , int * out_count ) {
502+ * out_count = 0 ;
503+ yyjson_doc * doc = yyjson_read (args_json , strlen (args_json ), 0 );
504+ if (!doc ) {
505+ return NULL ;
506+ }
507+ yyjson_val * root = yyjson_doc_get_root (doc );
508+ yyjson_val * arr = yyjson_obj_get (root , key );
509+ if (!arr || !yyjson_is_arr (arr )) {
510+ yyjson_doc_free (doc );
511+ return NULL ;
512+ }
513+ int count = (int )yyjson_arr_size (arr );
514+ if (count == 0 ) {
515+ yyjson_doc_free (doc );
516+ return calloc (1 , sizeof (char * ));
517+ }
518+ char * * result = malloc ((size_t )count * sizeof (char * ));
519+ int n = 0 ;
520+ size_t idx , max ;
521+ yyjson_val * val ;
522+ yyjson_arr_foreach (arr , idx , max , val ) {
523+ if (yyjson_is_str (val )) {
524+ result [n ++ ] = heap_strdup (yyjson_get_str (val ));
525+ }
526+ }
527+ yyjson_doc_free (doc );
528+ if (n == 0 ) {
529+ return result ;
530+ }
531+ * out_count = n ;
532+ return result ;
533+ }
534+
497535/* ══════════════════════════════════════════════════════════════════
498536 * MCP SERVER
499537 * ══════════════════════════════════════════════════════════════════ */
@@ -940,6 +978,9 @@ static char *handle_search_graph(cbm_mcp_server_t *srv, const char *args) {
940978 char * label = cbm_mcp_get_string_arg (args , "label" );
941979 char * name_pattern = cbm_mcp_get_string_arg (args , "name_pattern" );
942980 char * file_pattern = cbm_mcp_get_string_arg (args , "file_pattern" );
981+ char * relationship = cbm_mcp_get_string_arg (args , "relationship" );
982+ bool exclude_entry_points = cbm_mcp_get_bool_arg (args , "exclude_entry_points" );
983+ bool include_connected = cbm_mcp_get_bool_arg (args , "include_connected" );
943984 int limit = cbm_mcp_get_int_arg (args , "limit" , 500000 );
944985 int offset = cbm_mcp_get_int_arg (args , "offset" , 0 );
945986 int min_degree = cbm_mcp_get_int_arg (args , "min_degree" , -1 );
@@ -950,6 +991,9 @@ static char *handle_search_graph(cbm_mcp_server_t *srv, const char *args) {
950991 .label = label ,
951992 .name_pattern = name_pattern ,
952993 .file_pattern = file_pattern ,
994+ .relationship = relationship ,
995+ .exclude_entry_points = exclude_entry_points ,
996+ .include_connected = include_connected ,
953997 .limit = limit ,
954998 .offset = offset ,
955999 .min_degree = min_degree ,
@@ -977,6 +1021,16 @@ static char *handle_search_graph(cbm_mcp_server_t *srv, const char *args) {
9771021 sr -> node .file_path ? sr -> node .file_path : "" );
9781022 yyjson_mut_obj_add_int (doc , item , "in_degree" , sr -> in_degree );
9791023 yyjson_mut_obj_add_int (doc , item , "out_degree" , sr -> out_degree );
1024+ /* Include connected node names if populated */
1025+ if (sr -> connected_count > 0 && sr -> connected_names ) {
1026+ yyjson_mut_val * conn = yyjson_mut_arr (doc );
1027+ for (int j = 0 ; j < sr -> connected_count ; j ++ ) {
1028+ if (sr -> connected_names [j ]) {
1029+ yyjson_mut_arr_add_strcpy (doc , conn , sr -> connected_names [j ]);
1030+ }
1031+ }
1032+ yyjson_mut_obj_add_val (doc , item , "connected_names" , conn );
1033+ }
9801034 yyjson_mut_arr_add_val (results , item );
9811035 }
9821036 yyjson_mut_obj_add_val (doc , root , "results" , results );
@@ -990,6 +1044,7 @@ static char *handle_search_graph(cbm_mcp_server_t *srv, const char *args) {
9901044 free (label );
9911045 free (name_pattern );
9921046 free (file_pattern );
1047+ free (relationship );
9931048
9941049 char * result = cbm_mcp_text_result (json , false);
9951050 free (json );
@@ -1225,9 +1280,17 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
12251280 char * direction = cbm_mcp_get_string_arg (args , "direction" );
12261281 int depth = cbm_mcp_get_int_arg (args , "depth" , 3 );
12271282
1283+ /* Extract edge_types array; fall back to {"CALLS"} if not provided */
1284+ int user_edge_type_count = 0 ;
1285+ char * * user_edge_types =
1286+ cbm_mcp_get_string_array_arg (args , "edge_types" , & user_edge_type_count );
1287+
12281288 if (!func_name ) {
12291289 free (project );
12301290 free (direction );
1291+ for (int i = 0 ; i < user_edge_type_count ; i ++ )
1292+ free (user_edge_types [i ]);
1293+ free (user_edge_types );
12311294 return cbm_mcp_text_result ("function_name is required" , true);
12321295 }
12331296 if (!store ) {
@@ -1237,6 +1300,9 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
12371300 free (func_name );
12381301 free (project );
12391302 free (direction );
1303+ for (int i = 0 ; i < user_edge_type_count ; i ++ )
1304+ free (user_edge_types [i ]);
1305+ free (user_edge_types );
12401306 return _res ;
12411307 }
12421308
@@ -1245,6 +1311,9 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
12451311 free (func_name );
12461312 free (project );
12471313 free (direction );
1314+ for (int i = 0 ; i < user_edge_type_count ; i ++ )
1315+ free (user_edge_types [i ]);
1316+ free (user_edge_types );
12481317 return not_indexed ;
12491318 }
12501319
@@ -1261,6 +1330,9 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
12611330 free (func_name );
12621331 free (project );
12631332 free (direction );
1333+ for (int i = 0 ; i < user_edge_type_count ; i ++ )
1334+ free (user_edge_types [i ]);
1335+ free (user_edge_types );
12641336 cbm_store_free_nodes (nodes , 0 );
12651337 return cbm_mcp_text_result ("{\"error\":\"function not found\"}" , true);
12661338 }
@@ -1272,8 +1344,16 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
12721344 yyjson_mut_obj_add_str (doc , root , "function" , func_name );
12731345 yyjson_mut_obj_add_str (doc , root , "direction" , direction );
12741346
1275- const char * edge_types [] = {"CALLS" };
1276- int edge_type_count = 1 ;
1347+ const char * default_edge_types [] = {"CALLS" };
1348+ const char * * edge_types ;
1349+ int edge_type_count ;
1350+ if (user_edge_types ) {
1351+ edge_types = (const char * * )user_edge_types ;
1352+ edge_type_count = user_edge_type_count ;
1353+ } else {
1354+ edge_types = default_edge_types ;
1355+ edge_type_count = 1 ;
1356+ }
12771357
12781358 /* Run BFS for each requested direction.
12791359 * IMPORTANT: yyjson_mut_obj_add_str borrows pointers — we must keep
@@ -1287,8 +1367,10 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
12871367 cbm_traverse_result_t tr_in = {0 };
12881368
12891369 if (do_outbound ) {
1290- cbm_store_bfs (store , nodes [0 ].id , "outbound" , edge_types , edge_type_count , depth , 100 ,
1291- & tr_out );
1370+ if (edge_type_count > 0 ) {
1371+ cbm_store_bfs (store , nodes [0 ].id , "outbound" , edge_types , edge_type_count , depth , 100 ,
1372+ & tr_out );
1373+ }
12921374
12931375 yyjson_mut_val * callees = yyjson_mut_arr (doc );
12941376 for (int i = 0 ; i < tr_out .visited_count ; i ++ ) {
@@ -1305,8 +1387,10 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
13051387 }
13061388
13071389 if (do_inbound ) {
1308- cbm_store_bfs (store , nodes [0 ].id , "inbound" , edge_types , edge_type_count , depth , 100 ,
1309- & tr_in );
1390+ if (edge_type_count > 0 ) {
1391+ cbm_store_bfs (store , nodes [0 ].id , "inbound" , edge_types , edge_type_count , depth , 100 ,
1392+ & tr_in );
1393+ }
13101394
13111395 yyjson_mut_val * callers = yyjson_mut_arr (doc );
13121396 for (int i = 0 ; i < tr_in .visited_count ; i ++ ) {
@@ -1338,6 +1422,9 @@ static char *handle_trace_call_path(cbm_mcp_server_t *srv, const char *args) {
13381422 free (func_name );
13391423 free (project );
13401424 free (direction );
1425+ for (int i = 0 ; i < user_edge_type_count ; i ++ )
1426+ free (user_edge_types [i ]);
1427+ free (user_edge_types );
13411428
13421429 char * result = cbm_mcp_text_result (json , false);
13431430 free (json );
0 commit comments