Skip to content

Conversation

@CaliLuke
Copy link

@CaliLuke CaliLuke commented Jan 5, 2026

Summary

Adds environment variable-based filtering to reduce MCP tool token usage by enabling only needed tool groups.

Usage

Inclusion mode - only register specified groups:

PLANE_TOOLS=projects,work_items,users

Exclusion mode - register all except specified groups:

PLANE_TOOLS=!cycles,!modules,!initiatives

Default (no env var): All tools registered (backwards compatible)

Available tool groups

  • projects (9 tools)
  • work_items (6 tools)
  • cycles (12 tools)
  • users (1 tool)
  • modules (11 tools)
  • initiatives (5 tools)
  • intake (5 tools)
  • work_item_properties (5 tools)

Use case

LLM-based MCP clients have limited context windows. Each tool definition consumes tokens (~600-1500 per tool). Users who don't need all features can exclude unused tool groups to free up context for actual conversation.

Example: Excluding cycles, modules, and initiatives reduces tool count from 55 to 27, saving ~18k tokens.


Note

Introduces environment-controlled tool registration to reduce loaded MCP tools.

  • New PLANE_TOOLS env var supports inclusion (projects,work_items,...), exclusion (!cycles,!modules,...), and default (all) modes
  • Refactors register_tools to register only enabled groups; iterates TOOL_GROUPS and applies selection logic
  • Adds immutable TOOL_GROUPS mapping for group→registration function
  • Emits warnings for unknown groups and unprefixed items in exclusion mode

Written by Cursor Bugbot for commit a1bfdc0. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Environment-driven tool registration via PLANE_TOOLS:
      • Leave unset to enable all tools (default)
      • Comma-separated names to enable specific tool groups
      • Prefix a name with "!" to exclude specific tool groups
      • Validation emits warnings for unknown or mixed entries
    • Tool group list made immutable for more stable registrations
  • Refactor

    • Simplified registration flow to drive enabled groups from configuration

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 5, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds an environment-driven tool registration system: a public immutable TOOL_GROUPS mapping and a refactored register_tools() that conditionally registers groups based on the PLANE_TOOLS environment variable (include, exclude with !, or default register-all behavior).

Changes

Cohort / File(s) Summary
Tool group registration
plane_mcp/tools/__init__.py
Added public TOOL_GROUPS (immutable MappingProxyType) with private _TOOL_GROUPS backing store; added imports (logging, os, Callable, MappingProxyType); refactored register_tools() to read PLANE_TOOLS and support default (all), inclusion (comma list), and exclusion (comma list with !) modes; validates unknown/malformed entries and logs warnings; iterates TOOL_GROUPS to invoke selected registration functions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Env as Environment (PLANE_TOOLS)
  participant Registrar as register_tools()
  participant Registry as TOOL_GROUPS (mapping)
  participant ToolFn as Tool registration functions

  Env->>Registrar: read PLANE_TOOLS
  Registrar->>Registry: evaluate entries (include / exclude / default)
  alt inclusion mode (explicit list)
    Registrar->>Registry: select included groups
  else exclusion mode (leading '!')
    Registrar->>Registry: remove excluded groups
  else default (unset/empty)
    Registrar->>Registry: select all groups
  end
  loop for each selected group
    Registrar->>ToolFn: call registration function
    ToolFn-->>Registrar: registration complete (or log warning)
  end
  Registrar-->>Env: finished
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I hopped through env and mapping bright,

Tools lined up in morning light,
Include, exclude, the choice in hand,
I register them across the land,
A tiny rabbit, tidy and spry.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding an environment variable (PLANE_TOOLS) to filter tool groups during registration, which aligns with the primary objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
plane_mcp/tools/__init__.py (2)

17-27: Consider making TOOL_GROUPS immutable.

Since TOOL_GROUPS is a new public export, consider making it immutable to prevent unintended modifications by external code. This ensures the tool registry remains stable.

🔎 Proposed fix using MappingProxyType
+from types import MappingProxyType
+
 from fastmcp import FastMCP

 from plane_mcp.tools.cycles import register_cycle_tools
 # Map of tool group names to their registration functions
-TOOL_GROUPS: dict[str, Callable[[FastMCP], None]] = {
+_TOOL_GROUPS: dict[str, Callable[[FastMCP], None]] = {
     "projects": register_project_tools,
     "work_items": register_work_item_tools,
     "cycles": register_cycle_tools,
     "users": register_user_tools,
     "modules": register_module_tools,
     "initiatives": register_initiative_tools,
     "intake": register_intake_tools,
     "work_item_properties": register_work_item_property_tools,
 }
+
+TOOL_GROUPS = MappingProxyType(_TOOL_GROUPS)

52-58: Consider validating group names to catch typos.

Invalid group names in PLANE_TOOLS are silently ignored, which could lead to confusion if users misspell a group name. Consider adding validation to warn users about unrecognized groups.

🔎 Proposed enhancement with validation
 def register_tools(mcp: FastMCP) -> None:
     """Register tools with the MCP server based on PLANE_TOOLS configuration.
 
     The PLANE_TOOLS environment variable controls which tool groups are registered:
     - Not set or empty: Register all tools (default)
     - Comma-separated list: Register only specified groups
       Example: PLANE_TOOLS=projects,work_items,users
     - Exclusion mode (prefix with !): Register all except specified groups
       Example: PLANE_TOOLS=!cycles,!modules,!initiatives
 
     Available tool groups: projects, work_items, cycles, users, modules,
     initiatives, intake, work_item_properties
     """
     tools_config = os.getenv("PLANE_TOOLS", "").strip()
 
     if not tools_config:
         # Default: register all tools
         enabled_groups = set(TOOL_GROUPS.keys())
     elif tools_config.startswith("!"):
         # Exclusion mode: start with all, remove specified groups
         excluded = {t.strip().lstrip("!") for t in tools_config.split(",")}
+        # Validate excluded group names
+        invalid = excluded - set(TOOL_GROUPS.keys()) - {""}
+        if invalid:
+            import logging
+            logging.warning(f"Unknown tool groups in PLANE_TOOLS will be ignored: {', '.join(invalid)}")
         enabled_groups = set(TOOL_GROUPS.keys()) - excluded
     else:
         # Inclusion mode: only register specified groups
         enabled_groups = {t.strip() for t in tools_config.split(",")}
+        # Validate included group names
+        invalid = enabled_groups - set(TOOL_GROUPS.keys()) - {""}
+        if invalid:
+            import logging
+            logging.warning(f"Unknown tool groups in PLANE_TOOLS will be ignored: {', '.join(invalid)}")
 
     for name, register_fn in TOOL_GROUPS.items():
         if name in enabled_groups:
             register_fn(mcp)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 754faa9 and 9b19501.

📒 Files selected for processing (1)
  • plane_mcp/tools/__init__.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
plane_mcp/tools/__init__.py (2)

3-4: LGTM!

The imports are correctly added to support environment variable access and type hints for the new filtering logic.


30-58: Well-structured implementation!

The environment variable parsing logic correctly handles all documented use cases:

  • Default mode (empty) registers all tools
  • Inclusion mode registers only specified groups
  • Exclusion mode registers all except specified groups

The logic properly handles whitespace, trailing commas, and empty strings. The docstring provides clear examples for users.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@CaliLuke CaliLuke force-pushed the tool-group-filtering branch from 9b19501 to 12eafa4 Compare January 5, 2026 21:45
Allows users to reduce context usage by enabling only needed tool groups.

Usage:
- PLANE_TOOLS=projects,work_items,users (inclusion mode)
- PLANE_TOOLS=!cycles,!modules,!initiatives (exclusion mode)

Available groups: projects, work_items, cycles, users, modules,
initiatives, intake, work_item_properties

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@CaliLuke CaliLuke force-pushed the tool-group-filtering branch from 12eafa4 to a1bfdc0 Compare January 5, 2026 21:48
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
plane_mcp/tools/__init__.py (2)

19-30: LGTM!

The pattern of using a private _TOOL_GROUPS dict with a public immutable TOOL_GROUPS proxy is excellent for preventing accidental modifications. The mapping is clean and well-structured.

Optional: Add explicit type annotation for clarity

Adding an explicit type annotation would improve code documentation:

 }
-TOOL_GROUPS = MappingProxyType(_TOOL_GROUPS)
+TOOL_GROUPS: MappingProxyType[str, Callable[[FastMCP], None]] = MappingProxyType(_TOOL_GROUPS)

60-63: Consider clarifying the warning message.

The warning message "Unprefixed items in exclusion mode will be ignored" could be misinterpreted. Users might think these groups won't be registered at all, when actually they will be registered (because they're not excluded).

Suggested improvement for clarity
             logging.warning(
-                f"Unprefixed items in exclusion mode will be ignored: {unprefixed_str}. "
+                f"Unprefixed items in exclusion mode will not be excluded (groups will still be registered): {unprefixed_str}. "
                 f"Did you mean: {suggested}?"
             )
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12eafa4 and a1bfdc0.

📒 Files selected for processing (1)
  • plane_mcp/tools/__init__.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (3)
plane_mcp/tools/__init__.py (3)

3-6: LGTM!

The new imports are appropriate and necessary for the environment-based configuration and immutable tool groups mapping.


34-45: LGTM!

The docstring is comprehensive and provides clear examples for each mode. The listing of available tool groups is particularly helpful for users.


71-83: LGTM!

The inclusion mode parsing and registration loop are correctly implemented. Empty strings from malformed input (trailing/double commas) are silently ignored, which is reasonable behavior. The validation warnings will help users catch typos in group names.

# Exclusion mode: start with all, remove specified groups
# Only items prefixed with ! are excluded
items = [t.strip() for t in tools_config.split(",")]
excluded = {t[1:] for t in items if t.startswith("!")}
Copy link

Choose a reason for hiding this comment

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

Whitespace after ! prefix not stripped in exclusion

In exclusion mode, when extracting group names with t[1:], whitespace after the ! prefix is preserved. If a user writes PLANE_TOOLS="! cycles" (with a space after !), the excluded set contains " cycles" (with leading space), which doesn't match "cycles" in TOOL_GROUPS. This causes the exclusion to silently fail—all groups remain enabled. A warning is logged about unknown groups, but the behavior is unexpected compared to inclusion mode which properly strips each item.

Fix in Cursor Fix in Web

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