| 9628 | } |
| 9629 | |
| 9630 | static char *handle_search_code(cbm_mcp_server_t *srv, const char *args) { |
| 9631 | char *pattern = cbm_mcp_get_string_arg(args, "pattern"); |
| 9632 | char *project = get_project_arg(args); |
| 9633 | char *file_pattern = cbm_mcp_get_string_arg(args, "file_pattern"); |
| 9634 | char *path_filter = cbm_mcp_get_string_arg(args, "path_filter"); |
| 9635 | char *mode_str = cbm_mcp_get_string_arg(args, "mode"); |
| 9636 | int limit = cbm_mcp_get_int_arg(args, "limit", MCP_DEFAULT_LIMIT); |
| 9637 | /* #1511: a negative limit flowed straight into the result cap and came back |
| 9638 | * as the reported count ("results: -5"), which reads to an agent as a real |
| 9639 | * answer rather than a rejected argument. The schema now declares |
| 9640 | * minimum:1, but a schema is a request to the client, never a guarantee to |
| 9641 | * the server — clamp here too. */ |
| 9642 | if (limit < 1) { |
| 9643 | limit = MCP_DEFAULT_LIMIT; |
| 9644 | } |
| 9645 | int context_lines = cbm_mcp_get_int_arg(args, "context", 0); |
| 9646 | bool use_regex = cbm_mcp_get_bool_arg(args, "regex"); |
| 9647 | uint64_t search_t0 = cbm_now_ms(); |
| 9648 | /* In literal (non-regex) mode a '|' is matched as a byte, not alternation — |
| 9649 | * a common silent 0-match trap; flagged in the result warnings (#282). */ |
| 9650 | bool pat_has_pipe = pattern && strchr(pattern, '|') != NULL; |
| 9651 | |
| 9652 | int mode = parse_search_mode(mode_str); |
| 9653 | free(mode_str); |
| 9654 | |
| 9655 | cbm_regex_t path_regex; |
| 9656 | bool has_path_filter = compile_path_filter(path_filter, &path_regex); |
| 9657 | free(path_filter); |
| 9658 | path_filter = NULL; |
| 9659 | |
| 9660 | if (!pattern) { |
| 9661 | free(project); |
| 9662 | free(file_pattern); |
| 9663 | return cbm_mcp_text_result("pattern is required", true); |
| 9664 | } |
| 9665 | |
| 9666 | /* Project is required */ |
| 9667 | if (!project) { |
| 9668 | free(pattern); |
| 9669 | free(file_pattern); |
| 9670 | char *_err = build_project_list_error("project is required"); |
| 9671 | char *_res = cbm_mcp_text_result(_err, true); |
| 9672 | free(_err); |
| 9673 | return _res; |
| 9674 | } |
| 9675 | |
| 9676 | char *root_path = get_project_root(srv, project); |
| 9677 | if (!root_path) { |
| 9678 | free(pattern); |
| 9679 | free(project); |
| 9680 | free(file_pattern); |
| 9681 | char *_err = build_project_list_error("project not found or not indexed"); |
| 9682 | char *_res = cbm_mcp_text_result(_err, true); |
| 9683 | free(_err); |
| 9684 | return _res; |
| 9685 | } |
| 9686 | |
| 9687 | if (!validate_search_args(root_path, file_pattern)) { |
no test coverage detected