Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 27 additions & 30 deletions src/mcp/mcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3082,6 +3082,27 @@ static int sg_parse_fields(const char *args, const char *out[], int max_out, yyj
return n;
}

/* Append a property as one compact-output cell. Compound values stay one
* column by using their compact JSON representation; the cell emitter quotes
* and escapes that representation as needed. */
static void sg_toon_property_cell(cbm_sb_t *sb, yyjson_val *v) {
if (v && yyjson_is_str(v)) {
cbm_tree_cell_str(sb, yyjson_get_str(v), false);
} else if (v && yyjson_is_bool(v)) {
cbm_tree_cell_bool(sb, yyjson_get_bool(v), false);
} else if (v && yyjson_is_int(v)) {
cbm_tree_cell_int(sb, yyjson_get_int(v), false);
} else if (v && yyjson_is_real(v)) {
cbm_tree_cell_real(sb, yyjson_get_real(v), false);
} else if (v && !yyjson_is_null(v)) {
char *json = yyjson_val_write(v, 0, NULL);
cbm_tree_cell_str(sb, json ? json : "", false);
free(json);
} else {
cbm_tree_cell_str(sb, "", false);
}
}

/* Append one row's extra-field cells, pulled from the node's properties. */
static void sg_toon_extra_cells(cbm_sb_t *sb, const char *props_json, const char *const *fields,
int nfields) {
Expand All @@ -3090,17 +3111,7 @@ static void sg_toon_extra_cells(cbm_sb_t *sb, const char *props_json, const char
yyjson_val *pr = pd ? yyjson_doc_get_root(pd) : NULL;
for (int f = 0; f < nfields; f++) {
yyjson_val *v = (pr && yyjson_is_obj(pr)) ? yyjson_obj_get(pr, fields[f]) : NULL;
if (v && yyjson_is_str(v)) {
cbm_tree_cell_str(sb, yyjson_get_str(v), false);
} else if (v && yyjson_is_bool(v)) {
cbm_tree_cell_bool(sb, yyjson_get_bool(v), false);
} else if (v && yyjson_is_int(v)) {
cbm_tree_cell_int(sb, yyjson_get_int(v), false);
} else if (v && yyjson_is_real(v)) {
cbm_tree_cell_real(sb, yyjson_get_real(v), false);
} else {
cbm_tree_cell_str(sb, "", false);
}
sg_toon_property_cell(sb, v);
}
if (pd) {
yyjson_doc_free(pd);
Expand Down Expand Up @@ -3232,17 +3243,7 @@ static void emit_search_results_tree(cbm_sb_t *sb, cbm_search_output_t *out, int
yyjson_val *pr = pd ? yyjson_doc_get_root(pd) : NULL;
for (int f = 0; f < nfields; f++) {
yyjson_val *v = (pr && yyjson_is_obj(pr)) ? yyjson_obj_get(pr, fields[f]) : NULL;
if (v && yyjson_is_str(v)) {
cbm_tree_cell_str(sb, yyjson_get_str(v), false);
} else if (v && yyjson_is_int(v)) {
cbm_tree_cell_int(sb, yyjson_get_int(v), false);
} else if (v && yyjson_is_real(v)) {
cbm_tree_cell_real(sb, yyjson_get_real(v), false);
} else if (v && yyjson_is_bool(v)) {
cbm_tree_cell_bool(sb, yyjson_get_bool(v), false);
} else {
cbm_tree_cell_str(sb, "", false); /* emits "-" */
}
sg_toon_property_cell(sb, v);
}
if (pd) {
yyjson_doc_free(pd);
Expand Down Expand Up @@ -3324,14 +3325,10 @@ static void emit_search_results_tree_json(yyjson_mut_doc *doc, yyjson_mut_val *r
yyjson_val *pr = pd ? yyjson_doc_get_root(pd) : NULL;
for (int f = 0; f < nfields; f++) {
yyjson_val *v = (pr && yyjson_is_obj(pr)) ? yyjson_obj_get(pr, fields[f]) : NULL;
if (v && yyjson_is_str(v)) {
yyjson_mut_arr_add_strcpy(doc, row, yyjson_get_str(v));
} else if (v && yyjson_is_int(v)) {
yyjson_mut_arr_add_int(doc, row, yyjson_get_int(v));
} else if (v && yyjson_is_real(v)) {
yyjson_mut_arr_add_real(doc, row, yyjson_get_real(v));
} else if (v && yyjson_is_bool(v)) {
yyjson_mut_arr_add_bool(doc, row, yyjson_get_bool(v));
yyjson_mut_val *copy =
(v && !yyjson_is_null(v)) ? yyjson_val_mut_copy(doc, v) : NULL;
if (copy) {
yyjson_mut_arr_add_val(row, copy);
} else {
yyjson_mut_arr_add_null(doc, row);
}
Expand Down
26 changes: 23 additions & 3 deletions tests/test_mcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1793,15 +1793,32 @@ TEST(tool_search_graph_includes_node_properties) {
free(inner);
free(resp);

/* List-valued fields are compact JSON in text output, rather than an
* empty placeholder. */
resp = cbm_mcp_server_handle(
srv, "{\"jsonrpc\":\"2.0\",\"id\":431,\"method\":\"tools/call\","
"\"params\":{\"name\":\"search_graph\","
"\"arguments\":{\"project\":\"test-project\",\"label\":\"Function\","
"\"name_pattern\":\"HandleRequest\",\"fields\":[\"base_classes\"],"
"\"limit\":5}}}");
ASSERT_NOT_NULL(resp);
inner = extract_text_content(resp);
ASSERT_NOT_NULL(inner);
ASSERT_NOT_NULL(strstr(inner, "base_classes"));
ASSERT_NOT_NULL(strstr(inner, "HandlerBase"));
ASSERT_NOT_NULL(strstr(inner, "Audited"));
free(inner);
free(resp);

/* format:"json" = json-stringified tree: same grouped model, column-
* ordered row arrays — never per-row key envelopes or property blobs.
* fields adds columns there too. */
* fields adds columns there too and preserves compound JSON types. */
resp = cbm_mcp_server_handle(
srv, "{\"jsonrpc\":\"2.0\",\"id\":44,\"method\":\"tools/call\","
"\"params\":{\"name\":\"search_graph\","
"\"arguments\":{\"project\":\"test-project\",\"label\":\"Function\","
"\"name_pattern\":\"HandleRequest\",\"format\":\"json\","
"\"fields\":[\"signature\"],\"limit\":5}}}");
"\"fields\":[\"signature\",\"base_classes\"],\"limit\":5}}}");
ASSERT_NOT_NULL(resp);
inner = extract_text_content(resp);
ASSERT_NOT_NULL(inner);
Expand All @@ -1810,6 +1827,8 @@ TEST(tool_search_graph_includes_node_properties) {
ASSERT_NOT_NULL(strstr(inner, "\"rows\""));
ASSERT_NOT_NULL(strstr(inner, "\"signature\"")); /* requested column */
ASSERT_NOT_NULL(strstr(inner, "func HandleRequest")); /* its value */
ASSERT_NOT_NULL(strstr(inner, "\"base_classes\""));
ASSERT_NOT_NULL(strstr(inner, "[\"HandlerBase\",\"Audited\"]"));
ASSERT_NULL(strstr(inner, "is_exported")); /* blob never spills */
free(inner);
free(resp);
Expand Down Expand Up @@ -6162,7 +6181,8 @@ static cbm_mcp_server_t *setup_snippet_server(char *tmp_dir, size_t tmp_sz) {
n_hr.end_line = 5;
n_hr.properties_json = "{\"signature\":\"func HandleRequest() error\","
"\"return_type\":\"error\","
"\"is_exported\":true}";
"\"is_exported\":true,"
"\"base_classes\":[\"HandlerBase\",\"Audited\"]}";
int64_t id_hr = cbm_store_upsert_node(st, &n_hr);

cbm_node_t n_po = {0};
Expand Down
Loading