Skip to content

Commit f9e68c8

Browse files
fix(features): share registration resolution
Seed feature state once for direct registration so tools, resources, and prompts use one consistent snapshot. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e4a1ca6-53f7-4158-af22-35d2448d0b13
1 parent 5db8653 commit f9e68c8

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

pkg/inventory/registry.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ func (r *Inventory) RegisterPrompts(ctx context.Context, s *mcp.Server) {
332332
// RegisterAll registers all available tools, resources, and prompts with the server.
333333
// The context is used for feature flag evaluation.
334334
func (r *Inventory) RegisterAll(ctx context.Context, s *mcp.Server, deps any, middleware ...ToolHandlerMiddleware) {
335+
ctx = r.WithResolvedFeatures(ctx)
335336
r.RegisterTools(ctx, s, deps, middleware...)
336337
r.RegisterResourceTemplates(ctx, s, deps)
337338
r.RegisterPrompts(ctx, s)

pkg/inventory/registry_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,53 @@ func TestFeatureFlagPrompts(t *testing.T) {
12211221
}
12221222
}
12231223

1224+
func TestRegisterAllSharesFeatureResolution(t *testing.T) {
1225+
const feature = FeatureFlag("shared")
1226+
rule := NewFeatureRule([]FeatureFlag{feature}, func(featureAsBool FeatureResolver) bool {
1227+
return featureAsBool(feature)
1228+
})
1229+
tool := mockTool("tool", "toolset1", true)
1230+
tool.FeatureRule = rule
1231+
resource := mockResource("resource", "toolset1", "test://{id}")
1232+
resource.FeatureRule = rule
1233+
prompt := mockPrompt("prompt", "toolset1")
1234+
prompt.FeatureRule = rule
1235+
1236+
calls := 0
1237+
checker := func(context.Context, FeatureFlag) (bool, error) {
1238+
calls++
1239+
return calls == 1, nil
1240+
}
1241+
inv := mustBuild(t, NewBuilder().
1242+
SetTools([]ServerTool{tool}).
1243+
SetResources([]ServerResourceTemplate{resource}).
1244+
SetPrompts([]ServerPrompt{prompt}).
1245+
WithToolsets([]string{"all"}).
1246+
WithFeatureChecker(checker))
1247+
1248+
server := mcp.NewServer(&mcp.Implementation{Name: "test-server", Version: "v0.0.1"}, nil)
1249+
inv.RegisterAll(context.Background(), server, nil)
1250+
clientTransport, serverTransport := mcp.NewInMemoryTransports()
1251+
serverSession, err := server.Connect(context.Background(), serverTransport, nil)
1252+
require.NoError(t, err)
1253+
t.Cleanup(func() { _ = serverSession.Close() })
1254+
client := mcp.NewClient(&mcp.Implementation{Name: "test-client", Version: "v0.0.1"}, nil)
1255+
clientSession, err := client.Connect(context.Background(), clientTransport, nil)
1256+
require.NoError(t, err)
1257+
t.Cleanup(func() { _ = clientSession.Close() })
1258+
1259+
tools, err := clientSession.ListTools(context.Background(), nil)
1260+
require.NoError(t, err)
1261+
require.Len(t, tools.Tools, 1)
1262+
resources, err := clientSession.ListResourceTemplates(context.Background(), nil)
1263+
require.NoError(t, err)
1264+
require.Len(t, resources.ResourceTemplates, 1)
1265+
prompts, err := clientSession.ListPrompts(context.Background(), nil)
1266+
require.NoError(t, err)
1267+
require.Len(t, prompts.Prompts, 1)
1268+
require.Equal(t, 1, calls)
1269+
}
1270+
12241271
func TestFeatureMetadataIsCachedAndNarrowed(t *testing.T) {
12251272
tools := []ServerTool{
12261273
mockToolWithFlags("tool_x", "toolset1", true, "x", ""),

0 commit comments

Comments
 (0)