Skip to content

feat: Add enabled tools flag and environment variable to filter avail… #354

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

Merged
merged 3 commits into from
May 1, 2025

Conversation

rlcurrall
Copy link
Contributor

@rlcurrall rlcurrall commented Apr 30, 2025

Add enabled tools flag and environment variable to filter available tools

Description

This PR adds the ability to filter which tools are available in the MCP Atlassian server through both a command-line flag and an environment variable. This enhancement provides users with fine-grained control over which tools are exposed by the server, allowing for better security and customization of the server's functionality.

Fixes: #259

Changes

  • Added new --enabled-tools command-line option to specify which tools to enable
  • Added ENABLED_TOOLS environment variable support for tool filtering
  • Implemented tool filtering logic in the server's list_tools() function
  • Added helper functions get_enabled_tools() and should_include_tool() for tool filtering
  • Updated logging to provide feedback about tool filtering configuration
  • Tools are filtered based on their names using a comma-separated list
  • All tools remain enabled by default if no filtering is specified

Testing

  • Unit tests added/updated
  • Integration tests passed
  • Manual checks performed:
    • Verified tool filtering works with command-line flag using MCP inspector
    • Verified tool filtering works with environment variable using MCP inspector
    • Confirmed default behavior (all tools enabled) when no filtering is specified using MCP inspector
    • Tested with various combinations of enabled tools using MCP inspector

Default behavior:
image

With filtering applied:
image

Checklist

  • Code follows project style guidelines (linting passes)
  • Tests added/updated for changes
  • All tests pass locally
  • Documentation updated with new command-line option and environment variable

@rlcurrall
Copy link
Contributor Author

This is definitely the naive approach to this problem. There are many other approaches that could be considered like "toolsets" where groups of tools are enabled/disabled which might look like:

uvx mcp-atlassian --toolsets issues,work_logs,boards,sprints

Alternatively, as mentioned in the linked issue, dynamic tool discovery, which may provide the best user experience but be more complex.

Copy link
Owner

@sooperset sooperset left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for implementing this much-needed feature! I've left a few suggestions in the review comments.

Looking at the bigger picture, we should probably plan a more comprehensive refactoring of server.py in the future, as it's growing quite large. However, this implementation is a great step forward and addresses the immediate need effectively.

The separation of tools into read/write categories and the filtering mechanism are well thought out. Once you address the unit test suggestions, I think this will be ready to merge.

Thanks again for your contribution!

@@ -21,12 +21,29 @@
logger = logging.getLogger("mcp-atlassian")


def get_enabled_tools() -> list[str] | None:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get_enabled_tools helper function is a good addition for centralizing the logic to read and parse the ENABLED_TOOLS environment variable.

Have unit tests been added to cover cases like:

  • ENABLED_TOOLS not set (returns None)
  • Empty string (ENABLED_TOOLS="")
  • String with only commas/whitespace
  • Correct parsing of comma-separated values with/without extra whitespace?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback! As part of the refactoring, I moved the get_enabled_tools function to utils/tools.py to better organize the code. I've also added comprehensive unit tests that cover all the scenarios you mentioned:

  • When ENABLED_TOOLS is not set (returns None)
  • Empty string (ENABLED_TOOLS="")
  • Strings with only commas and whitespace
  • Proper parsing of comma-separated values, including cases with extra whitespace

You can find all these test cases in the test suite. Let me know if you'd like to see any specific test scenarios!

return None


def should_include_tool(tool_name: str, enabled_tools: list[str] | None) -> bool:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The should_include_tool helper function clearly encapsulates the filtering logic.

Similarly, Would you consider adding unit tests verifying its behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Yes, I've actually included unit tests for should_include_tool in the same test suite. The tests verify:

  • When enabled_tools is None (includes all tools)
  • When a tool is in the enabled list
  • When a tool is not in the enabled list

These are alongside the get_enabled_tools tests in tests/unit/utils/test_tools.py.

# Provide context to the application
yield AppContext(confluence=confluence, jira=jira)
yield AppContext(confluence=confluence, jira=jira, enabled_tools=enabled_tools)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding enabled_tools to the AppContext is the correct way to pass this configuration through the application lifecycle. Good integration. 👍

@@ -207,987 +229,999 @@ async def list_tools() -> list[Tool]:
# Add Confluence tools if Confluence is configured
if ctx and ctx.confluence:
# Always add read operations
tools.extend(
[
confluence_read_tools = [
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactoring list_tools to define separate lists for read/write tools per service (confluence_read_tools, jira_write_tools, etc.) is a great improvement for clarity and maintainability. The subsequent filtering loops using should_include_tool are clean and correctly apply the filtering logic after checking the read_only flag.

+1, it's important to clearly document the exact tool names (e.g., confluence_search, jira_create_issue) available for the --enabled-tools flag or ENABLED_TOOLS environment variable in the README or other documentation (e.g. .env.example) for usability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Glad you like the refactoring. I've added documentation in the README that covers both the tool filtering and read/write access control. You'll find examples of specific tool names (like confluence_search and jira_get_issue) in the new "Tool Filtering and Access Control" section, along with examples of using both the --enabled-tools flag and ENABLED_TOOLS environment variable.

@rlcurrall rlcurrall force-pushed the feat/enabled-tools-flag branch from 84e14dc to 585f22a Compare May 1, 2025 13:11
@rlcurrall rlcurrall force-pushed the feat/enabled-tools-flag branch from 585f22a to d8f1c92 Compare May 1, 2025 13:24
rlcurrall added 2 commits May 1, 2025 09:25
- Add documentation for tool filtering using --enabled-tools flag and ENABLED_TOOLS env var

- Document read/write access control with READ_ONLY_MODE

- Update configuration examples and troubleshooting sections
@rlcurrall rlcurrall force-pushed the feat/enabled-tools-flag branch from d8f1c92 to 3190ccf Compare May 1, 2025 13:25
@rlcurrall
Copy link
Contributor Author

@sooperset I ended up having to rebase off main to address some merge conflicts in the server.py file. You may want to give it another once over to make sure everything still looks good.

@sooperset sooperset self-requested a review May 1, 2025 13:48
Copy link
Owner

@sooperset sooperset left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rlcurrall Thanks for the excellent implementation! The code is clean, well-tested, and nicely refactored. Moving the helper functions to utils/tools.py and organizing the tools by category makes the codebase more maintainable.

The documentation is clear and the unit tests are comprehensive. This gives us the flexibility we need for running multiple MCP servers while leaving room for future enhancements.

LGTM! Approving and merging.

@sooperset sooperset merged commit 89ef101 into sooperset:main May 1, 2025
5 checks passed
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.

Enhancement: Allow selective enabling of tools to support multiple MCP servers
2 participants