Skip to content

[WIP] Add enabled function and WithFilter to registry#1619

Closed
Copilot wants to merge 1 commit intomainfrom
copilot/add-enabled-function-withfilter
Closed

[WIP] Add enabled function and WithFilter to registry#1619
Copilot wants to merge 1 commit intomainfrom
copilot/add-enabled-function-withfilter

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 16, 2025

Thanks for assigning this issue to me. I'm starting to work on it and will keep this PR's description up to date as I form a plan and make progress.

Original prompt

This section details on the original issue you should resolve

<issue_title>Add Enabled function and WithFilter to registry for flexible tool filtering</issue_title>
<issue_description>## Summary

Add two generic mechanisms to the registry package for flexible tool filtering without coupling to specific domain concepts:

  1. Enabled function on ServerTool - Allows tools to self-filter based on request context
  2. WithFilter method on Builder - Allows cross-cutting filters to be applied to all tools

Motivation

Consumers of the registry (like github-mcp-server-remote) need to filter tools based on various conditions:

  • User permissions and policies
  • Request context (e.g., which client is calling)
  • Feature flags with complex logic (AND/OR combinations)
  • Runtime availability of dependencies

Currently, consumers must do this filtering externally before passing tools to the registry, which leads to complex orchestration code. By adding generic filter hooks, consumers can encapsulate their domain-specific logic while the registry remains agnostic.

Implementation

1. Add Enabled field to ServerTool struct in pkg/registry/tool.go:

type ServerTool struct {
    Tool              mcp.Tool
    Toolset           ToolsetMetadata
    HandlerFunc       HandlerFunc
    FeatureFlagEnable string
    FeatureFlagDisable string
    
    // Enabled is an optional function called at build/filter time to determine
    // if this tool should be available. If nil, the tool is considered enabled
    // (subject to FeatureFlagEnable/FeatureFlagDisable checks).
    // The context carries request-scoped information for the consumer to use.
    // Returns (enabled, error). On error, the tool should be treated as disabled.
    Enabled func(ctx context.Context) (bool, error)
}

2. Add ToolFilter type and WithFilter method to Builder in pkg/registry/registry.go:

// ToolFilter is a function that determines if a tool should be included.
// Returns true if the tool should be included, false to exclude it.
type ToolFilter func(ctx context.Context, tool *ServerTool) (bool, error)

// WithFilter adds a filter function that will be applied to all tools.
// Multiple filters can be added and are evaluated in order.
// If any filter returns false or an error, the tool is excluded.
func (b *Builder) WithFilter(filter ToolFilter) *Builder {
    b.filters = append(b.filters, filter)
    return b
}

3. Update isToolEnabled in the builder to check these in order:

  1. Check tool's own Enabled function (if set)
  2. Check FeatureFlagEnable (existing behavior)
  3. Check FeatureFlagDisable (existing behavior)
  4. Apply builder filters (new)

4. Add a context-aware method to get filtered tools:

// FilteredTools returns tools filtered by the Enabled function and builder filters.
// This should be called per-request with the request context.
func (r *Registry) FilteredTools(ctx context.Context) ([]ServerTool, error)

Example Usage

// Tool with self-filtering logic
tool := registry.ServerTool{
    Tool:    myTool,
    Toolset: myToolset,
    HandlerFunc: myHandler,
    Enabled: func(ctx context.Context) (bool, error) {
        // Consumer's domain-specific logic here
        user := mypackage.UserFromContext(ctx)
        return user != nil && user.HasPermission("use_tool"), nil
    },
}

// Builder with cross-cutting filter
reg := registry.NewBuilder().
    SetTools(tools).
    WithFilter(func(ctx context.Context, tool *registry.ServerTool) (bool, error) {
        // Cross-cutting concern like scope filtering
        return !shouldHideForCurrentAuth(ctx, tool), nil
    }).
    Build()

// Get filtered tools for a request
enabledTools, err := reg.FilteredTools(ctx)

Testing

Add tests for:

  • Enabled function returning true/false/error
  • WithFilter with single and multiple filters
  • Interaction between Enabled, feature flags, and filters
  • FilteredTools method with various filter combinations

Backwards Compatibility

  • All new fields are optional with nil/empty defaults
  • Existing code continues to work unchanged
  • FeatureFlagEnable/FeatureFlagDisable remain fully functional</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI requested a review from SamMorrowDrums December 16, 2025 10:03
@SamMorrowDrums SamMorrowDrums deleted the copilot/add-enabled-function-withfilter branch December 16, 2025 10:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Enabled function and WithFilter to registry for flexible tool filtering

2 participants