Skip to content

datarobot_genai.drmcputils.category_tree

category_tree

The tool-category tree behind GET /static/categories/.

Builds the two-level taxonomy (parents → leaf children, plus standalone leaves and the marker-resolved buckets) with per-node live tool counts scoped to the tools this server actually exposes, so a filter panel can render Data connectors (18) and a picker can show the tools inside each leaf.

Each node is {value, label, count, dynamic, appliesTo, toolNames, children}. value is the dr_* string a tool item reports in its categories and the gallery's ?category= filter accepts, so a selection round-trips against GET /static/tools/ without translation. label comes from TOOL_CATEGORY_LABELS — one map, used by every consumer.

appliesTo names the server types a category is meaningful for: the static taxonomy applies to ["global", "user"]; the marker-resolved buckets (dr_user_tools, dr_dynamic_tools) apply to ["user"] only, since global-mcp serves built-in tools exclusively.

Counts come from the server's real catalog (see resolve_catalog: the caller's session headers must not narrow it), classified by build_tool_gallery_items — the very function behind GET /static/tools/, so a node's contents and what ?category= returns are the same computation rather than two that agree by inspection. Never from the static LEAF_CATEGORY_TOOLS map alone, which would over-count tools a given server does not register. Proxied user-MCP tools carry no category and are not bucketed anywhere.

This module has no route of its own: it used to be routes/tool_categories.py serving a separate GET /toolCategories/, which meant two endpoints answered "what categories exist" with different shapes and different completeness. The tree IS the category endpoint now; only the builder lives here.

build_category_tree

build_category_tree(tools: Sequence[Any]) -> tuple[list[dict[str, Any]], int]

Build the category tree for tools.

Returns the top-level nodes and the number of DISTINCT tools that landed in at least one category — which is smaller than len(tools) whenever a server exposes uncategorized tools (proxied user-MCP tools, foremost).

Source code in datarobot_genai/drmcputils/category_tree.py
def build_category_tree(tools: Sequence[Any]) -> tuple[list[dict[str, Any]], int]:
    """Build the category tree for *tools*.

    Returns the top-level nodes and the number of DISTINCT tools that landed in at
    least one category — which is smaller than ``len(tools)`` whenever a server
    exposes uncategorized tools (proxied user-MCP tools, foremost).
    """
    by_category = _category_to_tools(tools)
    nodes = [_category_node(name, by_category) for name in ordered_top_level()]
    mapped: set[str] = set()
    for names in by_category.values():
        mapped |= names
    return nodes, len(mapped)

ordered_top_level

ordered_top_level() -> list[str]

Top-level categories, ordered alphabetically by display label.

Derived from the taxonomy (not hard-coded) so a new category flows through to the filter panel by existing. Alphabetical (case-insensitive, value as the tie-breaker) so the response order matches how a picker lists them.

Source code in datarobot_genai/drmcputils/category_tree.py
def ordered_top_level() -> list[str]:
    """Top-level categories, ordered alphabetically by display label.

    Derived from the taxonomy (not hard-coded) so a new category flows through to
    the filter panel by existing. Alphabetical (case-insensitive, value as the
    tie-breaker) so the response order matches how a picker lists them.
    """
    tops = [c.value for c in MCPToolCategory if c.value in _PARENTS or c.value not in _CHILDREN]
    return sorted(tops, key=_label_sort_key)