Skip to content

Commit 5a8e22b

Browse files
authored
Fix MCP compact search metadata (#4402) (#4482)
1 parent f859e74 commit 5a8e22b

4 files changed

Lines changed: 42 additions & 8 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
category: fixed
3+
issues:
4+
- 4402
5+
affected:
6+
- src/CodeIndex/Mcp/McpToolHandlers.QueryTools.cs
7+
- src/CodeIndex/Mcp/McpToolHandlers.Instructions.cs
8+
---
9+
10+
## English
11+
12+
- **MCP compact search now reports actual match lines and only emits a cursor when more results exist (#4402)** — compact locations now agree with full search results, and terminal pages no longer expose a misleading `next_cursor` when `truncated` and `more_available` are false.
13+
14+
## 日本語
15+
16+
- **MCP compact search が実際の一致行を返し、続きがある場合だけ cursor を出力するようになりました (#4402)** — compact の位置情報が full search の結果と一致し、`truncated``more_available` が false の終端ページでは誤解を招く `next_cursor` を返さなくなりました。

src/CodeIndex/Mcp/McpToolHandlers.Instructions.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,12 @@ private static void AddFreshnessHint(JsonObject payload, DbReader reader)
142142
payload["freshness_degraded_reason"] = freshness.FreshnessDegradedReason;
143143
}
144144

145-
private static void AddSearchStabilityMetadata(JsonObject payload, DbReader reader, SearchCursor? cursor, IReadOnlyList<SearchResult> results)
145+
private static void AddSearchStabilityMetadata(
146+
JsonObject payload,
147+
DbReader reader,
148+
SearchCursor? cursor,
149+
IReadOnlyList<SearchResult> results,
150+
bool moreAvailable = false)
146151
{
147152
var freshness = reader.GetFreshnessHint();
148153
payload["result_stable_at"] = freshness.IndexedAt.HasValue
@@ -152,7 +157,7 @@ private static void AddSearchStabilityMetadata(JsonObject payload, DbReader read
152157
if (!freshness.FreshnessAvailable && freshness.FreshnessDegradedReason != null)
153158
payload["freshness_degraded_reason"] = freshness.FreshnessDegradedReason;
154159

155-
if (results.Count > 0)
160+
if (moreAvailable && results.Count > 0)
156161
payload["next_cursor"] = FormatSearchCursor(results[^1]);
157162
}
158163

src/CodeIndex/Mcp/McpToolHandlers.QueryTools.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,14 @@ private JsonNode ExecuteSearch(JsonNode? id, JsonNode? args)
188188
["excludeTests"] = excludeTests,
189189
["results"] = ToJsonArray(compactResults)
190190
};
191-
AddSearchStabilityMetadata(structured, reader, cursor, results);
191+
AddSearchStabilityMetadata(structured, reader, cursor, results, truncated);
192192
AddResultEnvelope(structured, results.Count, truncated ? null : results.Count, truncated);
193193
if (format == "compact")
194-
ApplyCompactResults(structured, results, result => result.Path, result => result.StartLine);
194+
ApplyCompactResults(
195+
structured,
196+
compactResults,
197+
result => result.Path,
198+
result => result.MatchLines.Count > 0 ? result.MatchLines[0] : result.ChunkStartLine);
195199
var topResult = results[0];
196200
AddNextStepSuggestion(
197201
structured,

tests/CodeIndex.Tests/McpServerToolsCallTests.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,28 @@ namespace CodeIndex.Tests;
2222
public partial class McpServerTests
2323
{
2424
[Fact]
25-
public void ToolsCall_SearchFormatCompactEmitsFileLineOnly_Issue1642()
25+
public void ToolsCall_SearchFormatCompactEmitsMatchLineAndNoTerminalCursor_Issues1642And4402()
2626
{
27+
InsertIndexedFile(
28+
"src/compact-line.cs",
29+
"csharp",
30+
"class CompactLine\n{\n void Needle4402() { }\n}\n");
2731
var request = JsonNode.Parse(
28-
"""{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search","arguments":{"query":"Run","format":"compact"}}}""")!;
32+
"""{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search","arguments":{"query":"Needle4402","format":"compact","limit":1}}}""")!;
2933

3034
var response = _server.HandleMessage(request)!;
3135
var structured = response["result"]!["structuredContent"]!;
3236
var row = Assert.Single(structured["results"]!.AsArray());
3337

3438
Assert.Equal("compact", structured["format"]!.GetValue<string>());
35-
Assert.Equal("src/app.cs", row!["file"]!.GetValue<string>());
36-
Assert.Equal(1, row["line"]!.GetValue<int>());
39+
Assert.Equal("src/compact-line.cs", row!["file"]!.GetValue<string>());
40+
Assert.Equal(3, row["line"]!.GetValue<int>());
3741
Assert.Null(row["snippet"]);
42+
Assert.Equal(1, structured["count"]!.GetValue<int>());
43+
Assert.Equal(1, structured["total"]!.GetValue<int>());
44+
Assert.False(structured["truncated"]!.GetValue<bool>());
45+
Assert.False(structured["more_available"]!.GetValue<bool>());
46+
Assert.Null(structured["next_cursor"]);
3847
}
3948

4049
[Fact]

0 commit comments

Comments
 (0)