Skip to content

Commit 91d1a23

Browse files
authored
Merge pull request DeusData#1043 from WarGloom/agent/fix-mcp-structured-content
fix(mcp): return structured compact results
2 parents 6c5795b + c43fabd commit 91d1a23

2 files changed

Lines changed: 20 additions & 5 deletions

File tree

‎src/mcp/mcp.c‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,30 @@ char *cbm_mcp_text_result(const char *text, bool is_error) {
261261
yyjson_mut_arr_add_val(content, item);
262262
yyjson_mut_obj_add_val(doc, root, "content", content);
263263

264-
if (!is_error && text) {
264+
bool has_structured_content = false;
265+
if (text) {
265266
yyjson_doc *structured_doc = yyjson_read(text, strlen(text), 0);
266267
if (structured_doc) {
267268
yyjson_val *structured_root = yyjson_doc_get_root(structured_doc);
268269
if (yyjson_is_obj(structured_root)) {
269270
yyjson_mut_val *structured = yyjson_val_mut_copy(doc, structured_root);
270-
yyjson_mut_obj_add_val(doc, root, "structuredContent", structured);
271+
if (structured) {
272+
yyjson_mut_obj_add_val(doc, root, "structuredContent", structured);
273+
has_structured_content = true;
274+
}
271275
}
272276
yyjson_doc_free(structured_doc);
273277
}
274278
}
279+
if (!has_structured_content) {
280+
/* Every advertised MCP tool has an object outputSchema, so even compact
281+
* TOON/plain-text and error results must carry a conforming structured
282+
* object. Keep the text Content block above for model visibility and
283+
* backwards compatibility. */
284+
yyjson_mut_val *structured = yyjson_mut_obj(doc);
285+
yyjson_mut_obj_add_str(doc, structured, is_error ? "error" : "text", text ? text : "");
286+
yyjson_mut_obj_add_val(doc, root, "structuredContent", structured);
287+
}
275288
yyjson_mut_obj_add_bool(doc, root, "isError", is_error);
276289

277290
char *out = yy_doc_to_str(doc);

‎tests/test_mcp.c‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -567,10 +567,10 @@ TEST(mcp_text_result) {
567567
PASS();
568568
}
569569

570-
TEST(mcp_text_result_skips_structured_content_for_plain_text) {
570+
TEST(mcp_text_result_wraps_plain_text_as_structured_content) {
571571
char *json = cbm_mcp_text_result("plain text", false);
572572
ASSERT_NOT_NULL(json);
573-
ASSERT_NULL(strstr(json, "\"structuredContent\""));
573+
ASSERT_NOT_NULL(strstr(json, "\"structuredContent\":{\"text\":\"plain text\"}"));
574574
ASSERT_NOT_NULL(strstr(json, "\"isError\":false"));
575575
free(json);
576576
PASS();
@@ -589,6 +589,7 @@ TEST(mcp_cancel_matches_request_id) {
589589
TEST(mcp_text_result_error) {
590590
char *json = cbm_mcp_text_result("something failed", true);
591591
ASSERT_NOT_NULL(json);
592+
ASSERT_NOT_NULL(strstr(json, "\"structuredContent\":{\"error\":\"something failed\"}"));
592593
ASSERT_NOT_NULL(strstr(json, "\"isError\":true"));
593594
ASSERT_NOT_NULL(strstr(json, "something failed"));
594595
free(json);
@@ -1106,6 +1107,7 @@ TEST(tool_search_graph_includes_node_properties) {
11061107
"\"arguments\":{\"project\":\"test-project\",\"label\":\"Function\","
11071108
"\"name_pattern\":\"HandleRequest\",\"limit\":5}}}");
11081109
ASSERT_NOT_NULL(resp);
1110+
ASSERT_NOT_NULL(strstr(resp, "\"structuredContent\":{\"text\":"));
11091111
char *inner = extract_text_content(resp);
11101112
ASSERT_NOT_NULL(inner);
11111113
ASSERT_NOT_NULL(strstr(inner, "results[")); /* TOON table header */
@@ -6349,7 +6351,7 @@ SUITE(mcp) {
63496351
RUN_TEST(mcp_ingest_traces_items_disallow_additional_properties_issue731);
63506352
RUN_TEST(mcp_get_architecture_aspects_schema_enum_pr560);
63516353
RUN_TEST(mcp_text_result);
6352-
RUN_TEST(mcp_text_result_skips_structured_content_for_plain_text);
6354+
RUN_TEST(mcp_text_result_wraps_plain_text_as_structured_content);
63536355
RUN_TEST(mcp_cancel_matches_request_id);
63546356
RUN_TEST(mcp_text_result_error);
63556357

0 commit comments

Comments
 (0)