| 3877 | } |
| 3878 | |
| 3879 | static char *handle_query_graph(cbm_mcp_server_t *srv, const char *args) { |
| 3880 | char *query = cbm_mcp_get_string_arg(args, "query"); |
| 3881 | char *project = get_project_arg(args); |
| 3882 | cbm_store_t *store = resolve_store(srv, project); |
| 3883 | int max_rows = cbm_mcp_get_int_arg(args, "max_rows", 0); |
| 3884 | |
| 3885 | /* graph="missed" (#963): run the SAME cypher against the derived |
| 3886 | * miss-graph view (shadow project "<project>::missed") instead of the |
| 3887 | * code graph — file structure of not-fully-indexed files only. */ |
| 3888 | char *graph_arg = cbm_mcp_get_string_arg(args, "graph"); |
| 3889 | bool missed_graph = graph_arg && strcmp(graph_arg, "missed") == 0; |
| 3890 | free(graph_arg); |
| 3891 | |
| 3892 | if (!query) { |
| 3893 | free(project); |
| 3894 | return cbm_mcp_text_result("query is required", true); |
| 3895 | } |
| 3896 | if (missed_graph && !project) { |
| 3897 | free(query); |
| 3898 | return cbm_mcp_text_result("project is required when graph=\"missed\"", true); |
| 3899 | } |
| 3900 | if (!store) { |
| 3901 | char *_err = build_project_list_error("project not found or not indexed"); |
| 3902 | char *_res = cbm_mcp_text_result(_err, true); |
| 3903 | free(_err); |
| 3904 | free(project); |
| 3905 | free(query); |
| 3906 | return _res; |
| 3907 | } |
| 3908 | |
| 3909 | char *not_indexed = verify_project_indexed(store, project); |
| 3910 | if (not_indexed) { |
| 3911 | free(project); |
| 3912 | free(query); |
| 3913 | return not_indexed; |
| 3914 | } |
| 3915 | |
| 3916 | char covproj[CBM_SZ_512]; |
| 3917 | const char *cypher_project = project; |
| 3918 | if (missed_graph) { |
| 3919 | cbm_store_coverage_shadow_project(covproj, sizeof(covproj), project); |
| 3920 | cypher_project = covproj; |
| 3921 | } |
| 3922 | |
| 3923 | cbm_cypher_result_t result = {0}; |
| 3924 | int rc = cbm_cypher_execute(store, query, cypher_project, max_rows, &result); |
| 3925 | |
| 3926 | if (rc < 0) { |
| 3927 | char *err_msg = result.error ? result.error : "query execution failed"; |
| 3928 | char *resp = cbm_mcp_text_result(err_msg, true); |
| 3929 | cbm_cypher_result_free(&result); |
| 3930 | free(query); |
| 3931 | free(project); |
| 3932 | return resp; |
| 3933 | } |
| 3934 | |
| 3935 | /* Response encoding: TOON table by default (the columns double as the |
| 3936 | * table header); format:"json" restores the legacy columns/rows arrays. */ |
no test coverage detected