Skip to content

Commit 04c8a4e

Browse files
refactor(mcp): bound minimal context top summaries
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent c80d962 commit 04c8a4e

2 files changed

Lines changed: 81 additions & 42 deletions

File tree

internal/mcp/handler_context.go

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package mcp
44
import (
55
"context"
66
"fmt"
7-
"sort"
87
"strings"
98

109
"github.com/mark3labs/mcp-go/mcp"
@@ -30,6 +29,7 @@ type fileCount struct {
3029
// commCount holds aggregated membership counts per community.
3130
// @intent transport GROUP BY community_id results into MCP response shaping.
3231
type commCount struct {
32+
Label string
3333
CommunityID uint
3434
Count int
3535
}
@@ -44,6 +44,7 @@ type minimalContextCommInfo struct {
4444
// flowCount holds aggregated membership counts per flow.
4545
// @intent transport GROUP BY flow_id results into MCP response shaping.
4646
type flowCount struct {
47+
Name string
4748
FlowID uint
4849
Count int
4950
}
@@ -175,60 +176,37 @@ func (h *handlers) getMinimalContext(ctx context.Context, request mcp.CallToolRe
175176
Joins("JOIN communities ON communities.id = community_memberships.community_id").
176177
Where("communities.namespace = ?", ns)
177178
if err := commCountQ.
178-
Select("community_id, COUNT(*) as count").
179+
Select("community_id, communities.label as label, COUNT(*) as count").
179180
Group("community_id").
181+
Group("communities.label").
182+
Order("count DESC").
183+
Order("community_id ASC").
184+
Limit(3).
180185
Scan(&ccRows).Error; err != nil {
181186
return "", trace.Wrap(err, "group community memberships")
182187
}
183-
ccMap := make(map[uint]int, len(ccRows))
184-
for _, r := range ccRows {
185-
ccMap[r.CommunityID] = r.Count
186-
}
187-
188-
var communities []model.Community
189-
communityQ := h.deps.DB.WithContext(ctx).Where("namespace = ?", ns)
190-
if err := communityQ.Find(&communities).Error; err != nil {
191-
return "", trace.Wrap(err, "find communities")
192-
}
193-
commInfos := make([]minimalContextCommInfo, len(communities))
194-
for i, c := range communities {
195-
commInfos[i] = minimalContextCommInfo{Label: c.Label, NodeCount: ccMap[c.ID]}
196-
}
197-
sort.Slice(commInfos, func(i, j int) bool {
198-
return commInfos[i].NodeCount > commInfos[j].NodeCount
199-
})
200-
if len(commInfos) > 3 {
201-
commInfos = commInfos[:3]
188+
commInfos := make([]minimalContextCommInfo, len(ccRows))
189+
for i, r := range ccRows {
190+
commInfos[i] = minimalContextCommInfo{Label: r.Label, NodeCount: r.Count}
202191
}
203192
var fcRows []flowCount
204193
flowCountQ := h.deps.DB.WithContext(ctx).
205194
Model(&model.FlowMembership{}).
206-
Where("namespace = ?", ns)
195+
Joins("JOIN flows ON flows.id = flow_memberships.flow_id").
196+
Where("flow_memberships.namespace = ?", ns)
207197
if err := flowCountQ.
208-
Select("flow_id, COUNT(*) as count").
198+
Select("flow_id, flows.name as name, COUNT(*) as count").
209199
Group("flow_id").
200+
Group("flows.name").
201+
Order("count DESC").
202+
Order("flow_id ASC").
203+
Limit(3).
210204
Scan(&fcRows).Error; err != nil {
211205
return "", trace.Wrap(err, "group flow memberships")
212206
}
213-
fcMap := make(map[uint]int, len(fcRows))
214-
for _, r := range fcRows {
215-
fcMap[r.FlowID] = r.Count
216-
}
217-
218-
var flowList []model.Flow
219-
flowQ := h.deps.DB.WithContext(ctx).Where("namespace = ?", ns)
220-
if err := flowQ.Find(&flowList).Error; err != nil {
221-
return "", trace.Wrap(err, "find flows")
222-
}
223-
flowInfos := make([]minimalContextFlowInfo, len(flowList))
224-
for i, f := range flowList {
225-
flowInfos[i] = minimalContextFlowInfo{Name: f.Name, NodeCount: fcMap[f.ID]}
226-
}
227-
sort.Slice(flowInfos, func(i, j int) bool {
228-
return flowInfos[i].NodeCount > flowInfos[j].NodeCount
229-
})
230-
if len(flowInfos) > 3 {
231-
flowInfos = flowInfos[:3]
207+
flowInfos := make([]minimalContextFlowInfo, len(fcRows))
208+
for i, r := range fcRows {
209+
flowInfos[i] = minimalContextFlowInfo{Name: r.Name, NodeCount: r.Count}
232210
}
233211

234212
suggestedTools := suggestTools(task)

internal/mcp/handler_context_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,67 @@ func TestGetMinimalContext_CommunitiesAndFlows(t *testing.T) {
214214
if firstFlow["node_count"].(float64) != 8 {
215215
t.Errorf("first flow node_count = %v, want 8", firstFlow["node_count"])
216216
}
217+
if comms[1].(map[string]any)["label"] != "internal/api" || comms[2].(map[string]any)["label"] != "internal/db" {
218+
t.Fatalf("unexpected community ordering: %v", comms)
219+
}
220+
if fl[1].(map[string]any)["name"] != "checkout_flow" || fl[2].(map[string]any)["name"] != "signup_flow" {
221+
t.Fatalf("unexpected flow ordering: %v", fl)
222+
}
223+
}
224+
225+
func TestGetMinimalContext_CommunitiesAndFlows_StableTieBreakByID(t *testing.T) {
226+
deps := setupTestDeps(t)
227+
228+
for i := 1; i <= 6; i++ {
229+
deps.DB.Create(&model.Node{
230+
QualifiedName: fmt.Sprintf("pkg.Tie%d", i),
231+
Kind: model.NodeKindFunction,
232+
Name: fmt.Sprintf("Tie%d", i),
233+
FilePath: "tie.go",
234+
StartLine: i,
235+
EndLine: i,
236+
Language: "go",
237+
})
238+
}
239+
240+
var communities []model.Community
241+
for _, label := range []string{"comm-a", "comm-b", "comm-c", "comm-d"} {
242+
comm := model.Community{Label: label, Key: label, Strategy: "directory"}
243+
deps.DB.Create(&comm)
244+
communities = append(communities, comm)
245+
}
246+
for i, comm := range communities {
247+
deps.DB.Create(&model.CommunityMembership{CommunityID: comm.ID, NodeID: uint(i + 1)})
248+
}
249+
250+
var flows []model.Flow
251+
for _, name := range []string{"flow-a", "flow-b", "flow-c", "flow-d"} {
252+
flow := model.Flow{Name: name}
253+
deps.DB.Create(&flow)
254+
flows = append(flows, flow)
255+
}
256+
for i, flow := range flows {
257+
deps.DB.Create(&model.FlowMembership{FlowID: flow.ID, NodeID: uint(i + 1), Ordinal: 0})
258+
}
259+
260+
result := callTool(t, deps, "get_minimal_context", map[string]any{})
261+
if result.IsError {
262+
t.Fatalf("returned error: %s", getTextContent(result))
263+
}
264+
265+
var data map[string]any
266+
if err := json.Unmarshal([]byte(getTextContent(result)), &data); err != nil {
267+
t.Fatalf("invalid JSON: %v", err)
268+
}
269+
270+
comms := data["top_communities"].([]any)
271+
if got := []string{comms[0].(map[string]any)["label"].(string), comms[1].(map[string]any)["label"].(string), comms[2].(map[string]any)["label"].(string)}; fmt.Sprint(got) != fmt.Sprint([]string{"comm-a", "comm-b", "comm-c"}) {
272+
t.Fatalf("unexpected tied community order: %v", got)
273+
}
274+
fl := data["top_flows"].([]any)
275+
if got := []string{fl[0].(map[string]any)["name"].(string), fl[1].(map[string]any)["name"].(string), fl[2].(map[string]any)["name"].(string)}; fmt.Sprint(got) != fmt.Sprint([]string{"flow-a", "flow-b", "flow-c"}) {
276+
t.Fatalf("unexpected tied flow order: %v", got)
277+
}
217278
}
218279

219280
func TestGetMinimalContext_RespectsFlowNamespace(t *testing.T) {

0 commit comments

Comments
 (0)