@@ -41,6 +41,7 @@ type Builder struct {
4141 featureChecker FeatureFlagChecker
4242 filters []ToolFilter // filters to apply to all tools
4343 generateInstructions bool
44+ insidersMode bool
4445}
4546
4647// NewBuilder creates a new Builder.
@@ -135,6 +136,15 @@ func (b *Builder) WithFilter(filter ToolFilter) *Builder {
135136 return b
136137}
137138
139+ // WithInsidersMode enables or disables insiders mode features.
140+ // When insiders mode is disabled (default), UI metadata is removed from tools
141+ // so clients won't attempt to load UI resources.
142+ // Returns self for chaining.
143+ func (b * Builder ) WithInsidersMode (enabled bool ) * Builder {
144+ b .insidersMode = enabled
145+ return b
146+ }
147+
138148// cleanTools trims whitespace and removes duplicates from tool names.
139149// Empty strings after trimming are excluded.
140150func cleanTools (tools []string ) []string {
@@ -162,8 +172,14 @@ func cleanTools(tools []string) []string {
162172// (i.e., they don't exist in the tool set and are not deprecated aliases).
163173// This ensures invalid tool configurations fail fast at build time.
164174func (b * Builder ) Build () (* Inventory , error ) {
175+ // When insiders mode is disabled, strip insiders-only features from tools
176+ tools := b .tools
177+ if ! b .insidersMode {
178+ tools = stripInsidersFeatures (b .tools )
179+ }
180+
165181 r := & Inventory {
166- tools : b . tools ,
182+ tools : tools ,
167183 resourceTemplates : b .resourceTemplates ,
168184 prompts : b .prompts ,
169185 deprecatedAliases : b .deprecatedAliases ,
@@ -176,9 +192,9 @@ func (b *Builder) Build() (*Inventory, error) {
176192 r .enabledToolsets , r .unrecognizedToolsets , r .toolsetIDs , r .toolsetIDSet , r .defaultToolsetIDs , r .toolsetDescriptions = b .processToolsets ()
177193
178194 // Build set of valid tool names for validation
179- validToolNames := make (map [string ]bool , len (b . tools ))
180- for i := range b . tools {
181- validToolNames [b . tools [i ].Tool .Name ] = true
195+ validToolNames := make (map [string ]bool , len (tools ))
196+ for i := range tools {
197+ validToolNames [tools [i ].Tool .Name ] = true
182198 }
183199
184200 // Process additional tools (clean, resolve aliases, and track unrecognized)
@@ -326,3 +342,67 @@ func (b *Builder) processToolsets() (map[ToolsetID]bool, []string, []ToolsetID,
326342 }
327343 return enabledToolsets , unrecognized , allToolsetIDs , validIDs , defaultToolsetIDList , descriptions
328344}
345+
346+ // insidersOnlyMetaKeys lists the Meta keys that are only available in insiders mode.
347+ // Add new experimental feature keys here to have them automatically stripped
348+ // when insiders mode is disabled.
349+ var insidersOnlyMetaKeys = []string {
350+ "ui" , // MCP Apps UI metadata
351+ }
352+
353+ // stripInsidersFeatures removes insiders-only features from tools.
354+ // This includes Meta keys listed in insidersOnlyMetaKeys.
355+ func stripInsidersFeatures (tools []ServerTool ) []ServerTool {
356+ result := make ([]ServerTool , len (tools ))
357+ for i , tool := range tools {
358+ if stripped := stripInsidersMetaFromTool (tool ); stripped != nil {
359+ result [i ] = * stripped
360+ } else {
361+ result [i ] = tool
362+ }
363+ }
364+ return result
365+ }
366+
367+ // stripInsidersMetaFromTool removes insiders-only Meta keys from a single tool.
368+ // Returns a modified copy if changes were made, nil otherwise.
369+ func stripInsidersMetaFromTool (tool ServerTool ) * ServerTool {
370+ if tool .Tool .Meta == nil {
371+ return nil
372+ }
373+
374+ // Check if any insiders-only keys exist
375+ hasInsidersKeys := false
376+ for _ , key := range insidersOnlyMetaKeys {
377+ if tool .Tool .Meta [key ] != nil {
378+ hasInsidersKeys = true
379+ break
380+ }
381+ }
382+ if ! hasInsidersKeys {
383+ return nil
384+ }
385+
386+ // Make a shallow copy and remove insiders-only keys
387+ toolCopy := tool
388+ newMeta := make (map [string ]any , len (tool .Tool .Meta ))
389+ for k , v := range tool .Tool .Meta {
390+ isInsidersKey := false
391+ for _ , insidersKey := range insidersOnlyMetaKeys {
392+ if k == insidersKey {
393+ isInsidersKey = true
394+ break
395+ }
396+ }
397+ if ! isInsidersKey {
398+ newMeta [k ] = v
399+ }
400+ }
401+
402+ if len (newMeta ) == 0 {
403+ toolCopy .Tool .Meta = nil
404+ } else {
405+ toolCopy .Tool .Meta = newMeta
406+ }
407+ return & toolCopy
408+ }
0 commit comments