Skip to content

feat: add enabled parameter support for tool registration #712

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

sargonpiraev
Copy link

@sargonpiraev sargonpiraev commented Jun 30, 2025

🚀 Add Dynamic Tool Enabling Support

📋 Summary

This PR introduces the ability to conditionally enable/disable tools during registration via an optional enabled parameter in the registerTool configuration. This addresses the need for dynamic tool management based on environment variables, feature flags, permissions, or other runtime conditions.

🎯 Motivation

Why is this change needed?

Many MCP server implementations require conditional tool availability based on:

  • Environment-specific features (debug tools only in development)
  • Permission-based access control (admin tools for authorized users)
  • Feature flags (experimental tools behind feature toggles)
  • Configuration-driven setup (enabling only specific tool categories)
  • Security requirements (disabling potentially dangerous operations)

Currently, developers must implement workarounds like wrapper functions or conditional registration logic, making code less clean and maintainable.

✨ What's Changed

Core Implementation

  • Added enabled?: boolean parameter to registerTool config object
  • Updated _createRegisteredTool to accept enabled state with default true
  • Maintained 100% backwards compatibility - existing code continues to work unchanged
  • Follows existing patterns - uses the established config object approach

New Capabilities

// Environment-based enabling
server.registerTool("debug-tool", {
  description: "Development debugging utilities",
  enabled: process.env.NODE_ENV === "development"
}, handler);

// Permission-based enabling
server.registerTool("admin-command", {
  description: "Administrative operations", 
  enabled: user.hasRole("admin")
}, handler);

// Pattern-based enabling with multimatch
import multimatch from 'multimatch';
const isEnabled = (name: string) => multimatch([name], ENABLED_PATTERNS).length > 0;

server.registerTool("file-operations", {
  enabled: isEnabled("file-operations")
}, handler);

🔧 Technical Details

Minimal Implementation

  • Only 2 methods modified: _createRegisteredTool and registerTool
  • Zero breaking changes - all existing tools default to enabled: true
  • Follows existing tool lifecycle patterns (enable/disable methods remain unchanged)
  • Consistent with existing resource and prompt enabled functionality

When enabled: false:

  • Tool doesn't appear in tools/list responses
  • Tool calls return "tool not found" errors
  • Dynamic enabling/disabling still works via .enable()/.disable() methods

📚 Documentation & Examples

Comprehensive Documentation Added:

  • Basic usage examples (environment variables, feature flags)
  • Advanced pattern-based enabling with minimatch library
  • Clear migration path for existing code
  • Security considerations and best practices

Real-world Use Cases:

# Enable all tools
ENABLED_TOOLS="*"

# Enable only file and user read operations  
ENABLED_TOOLS="file-*,user-get*"

# Enable only debug tools in development
ENABLED_TOOLS="debug-*"

# Disable all tools (testing/security)
ENABLED_TOOLS=""

Advanced Pattern Example:

import { minimatch } from 'minimatch';

const ENABLED_TOOL_PATTERNS = process.env.ENABLED_TOOLS?.split(',') || ['*'];

function isToolEnabled(toolName: string): boolean {
  return ENABLED_TOOL_PATTERNS.some(pattern => minimatch(toolName, pattern));
}

// Register tools with pattern-based enabling
server.registerTool("file-read", {
  description: "Read file contents",
  enabled: isToolEnabled("file-read")  // Enabled if matches pattern
}, handler);

server.registerTool("admin-delete-user", {
  description: "Delete user account", 
  enabled: isToolEnabled("admin-delete-user")  // e.g., ENABLED_TOOLS="file-*,user-*"
}, handler);

✅ Testing

  • Added comprehensive test coverage for enabled/disabled states
  • Verified backwards compatibility - existing tests pass unchanged
  • Tested dynamic state changes via enable/disable methods
  • Validated tool listing behavior with mixed enabled states

🔄 Migration Path

Existing Code: No changes required

server.registerTool("my-tool", { description: "..." }, handler);
// ✅ Still works - defaults to enabled: true

New Code: Opt-in enabled control

server.registerTool("my-tool", { 
  description: "...",
  enabled: shouldEnableTool() 
}, handler);
// ✅ New capability available when needed

🎯 Impact

This change enables:

  • Cleaner architecture - no more wrapper functions or conditional registration
  • Better security - easy disabling of sensitive operations
  • Flexible deployment - same codebase, different tool availability per environment
  • Improved UX - users only see tools they can actually use
  • Future extensibility - foundation for more advanced tool management features

📝 Checklist

  • Implementation follows existing code patterns
  • Backwards compatibility maintained
  • Documentation updated with examples
  • Test coverage added
  • No breaking changes
  • Security considerations documented
  • Performance impact: minimal (single boolean check)

Type: Enhancement
Breaking Change: No
Backwards Compatible: Yes

This enhancement provides immediate value while maintaining the stability and simplicity that makes MCP TypeScript SDK great to work with.

- Add optional enabled parameter to registerTool config
- Update _createRegisteredTool to accept enabled state with default true
- Maintain backwards compatibility - existing tools remain enabled by default
- Add comprehensive documentation with examples
- Include pattern-based tool enabling example using minimatch
- Add test coverage for enabled/disabled tool states

This allows conditional tool registration based on environment variables,
feature flags, permissions, or any runtime conditions.
- Replace minimatch with multimatch for cleaner pattern matching
- Simplify isEnabled helper function using multimatch directly
- Update all documentation examples for consistency
- multimatch is more suitable for this use case than minimatch
- Reduce README section to essential examples only
- Streamline PR description, focus on key benefits
- Remove verbose examples, keep core use cases
- Address potential concerns about feature size vs documentation
- Deleted the PR_DESCRIPTION.md file which contained detailed information about the dynamic tool enabling support feature.
- This change reflects the completion of the feature and the transition to more concise documentation.
Extends the enabled parameter functionality to all registerable objects:

- Add ResourceConfig and PromptConfig types with optional enabled field
- Update registerResource() and registerPrompt() to support enabled parameter
- Modify internal _create methods to accept enabled parameter with default true
- Fix resource template listing and reading to respect enabled state
- Add comprehensive tests for enabled/disabled states and dynamic toggling
- Update README.md with Resource and Prompt Enabled State documentation

Resources, resource templates, and prompts now support conditional enabling
during registration for environment-based, permission-based, and feature
flag scenarios, maintaining consistency with existing tool functionality.
User requested to keep README.md unchanged and focus only on code implementation.
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.

1 participant