Skip to content

Commit 8e82f2c

Browse files
Shidfarclaude
andcommitted
fix: replace fixed output buffer with growable buffer in cross_project_links
The 58KB safety limit on a 64KB buffer caused the SQL result loop to exit early, truncating output when many cross-project links exist (e.g., 163 graphql links fill ~57KB, hiding all 142 pubsub links). Replace with a dynamically growing buffer (realloc 2x when needed) and a reserved header space to avoid the memmove headroom issue. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b7eabf7 commit 8e82f2c

1 file changed

Lines changed: 24 additions & 15 deletions

File tree

src/mcp/mcp.c

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3434,12 +3434,13 @@ static char *handle_cross_project_links(cbm_mcp_server_t *srv, const char *args)
34343434
sqlite3_bind_text(stmt, bind_idx++, identifier, -1, SQLITE_STATIC);
34353435
}
34363436

3437-
/* Format output */
3438-
enum { XL_BUF_SIZE = 65536 };
3439-
char *buf = malloc(XL_BUF_SIZE);
3437+
/* Format output — reserve 128 bytes at start for header (filled after loop) */
3438+
enum { XL_HDR_RESERVE = 128 };
3439+
int buf_cap = 65536;
3440+
char *buf = malloc((size_t)buf_cap);
34403441
if (!buf) { sqlite3_finalize(stmt); sqlite3_close(db);
34413442
return cbm_mcp_text_result("alloc failed", true); }
3442-
int pos = 0;
3443+
int pos = XL_HDR_RESERVE; /* start writing after header reservation */
34433444
int total = 0;
34443445
char cur_protocol[64] = {0};
34453446
int proto_count = 0;
@@ -3455,26 +3456,33 @@ static char *handle_cross_project_links(cbm_mcp_server_t *srv, const char *args)
34553456
const char *fcons = (const char *)sqlite3_column_text(stmt, MCP_COL_7);
34563457
double conf = sqlite3_column_double(stmt, 8);
34573458

3459+
/* Grow buffer if needed (each entry is ~300 bytes max) */
3460+
if (pos + 512 > buf_cap) {
3461+
int new_cap = buf_cap * 2;
3462+
char *new_buf = realloc(buf, (size_t)new_cap);
3463+
if (!new_buf) break; /* return what we have so far */
3464+
buf = new_buf;
3465+
buf_cap = new_cap;
3466+
}
3467+
34583468
/* Protocol header */
34593469
if (strcmp(cur_protocol, proto ? proto : "") != 0) {
34603470
if (proto_count > 0) {
3461-
pos += snprintf(buf + pos, XL_BUF_SIZE - (size_t)pos, "\n");
3471+
pos += snprintf(buf + pos, (size_t)(buf_cap - pos), "\n");
34623472
}
34633473
snprintf(cur_protocol, sizeof(cur_protocol), "%s", proto ? proto : "");
3464-
pos += snprintf(buf + pos, XL_BUF_SIZE - (size_t)pos, "## %s\n\n", proto);
3474+
pos += snprintf(buf + pos, (size_t)(buf_cap - pos), "## %s\n\n", proto);
34653475
proto_count++;
34663476
}
34673477

3468-
pos += snprintf(buf + pos, XL_BUF_SIZE - (size_t)pos,
3478+
pos += snprintf(buf + pos, (size_t)(buf_cap - pos),
34693479
"%s (confidence: %.2f)\n"
34703480
" producer: %s :: %s (%s)\n"
34713481
" consumer: %s :: %s (%s)\n\n",
34723482
ident ? ident : "", conf,
34733483
pprod ? pprod : "", qprod ? qprod : "", fprod ? fprod : "",
34743484
pcons ? pcons : "", qcons ? qcons : "", fcons ? fcons : "");
34753485
total++;
3476-
3477-
if (pos > 58000) break; /* safety limit — leave headroom for header prepend */
34783486
}
34793487

34803488
sqlite3_finalize(stmt);
@@ -3486,13 +3494,14 @@ static char *handle_cross_project_links(cbm_mcp_server_t *srv, const char *args)
34863494
"No cross-project links found. Index at least 2 projects first.", false);
34873495
}
34883496

3489-
/* Prepend summary */
3490-
char header[128];
3491-
snprintf(header, sizeof(header), "# Cross-Project Links (%d total)\n\n", total);
3492-
int hlen = (int)strlen(header);
3493-
if (pos + hlen >= XL_BUF_SIZE) pos = XL_BUF_SIZE - hlen - 1;
3494-
memmove(buf + hlen, buf, (size_t)pos + 1);
3497+
/* Fill header in the reserved space, then shift content to close the gap */
3498+
char header[XL_HDR_RESERVE];
3499+
int hlen = snprintf(header, sizeof(header), "# Cross-Project Links (%d total)\n\n", total);
3500+
int gap = XL_HDR_RESERVE - hlen;
3501+
memmove(buf + hlen, buf + XL_HDR_RESERVE, (size_t)(pos - XL_HDR_RESERVE) + 1);
34953502
memcpy(buf, header, (size_t)hlen);
3503+
pos -= gap;
3504+
buf[pos] = '\0';
34963505

34973506
char *result = cbm_mcp_text_result(buf, false);
34983507
free(buf);

0 commit comments

Comments
 (0)