Skip to main content
Glama
deepwa7er
by deepwa7er

search_tree

Search the entire passive skill tree for nodes matching a keyword, including unallocated nodes. Filter by class to focus on reachable nodes near specific starting locations.

Instructions

Search the full passive skill tree for nodes matching a keyword.

Unlike search_passives, this searches ALL nodes in the tree — not just those allocated in the loaded build. Use this to discover nodes you haven't taken yet.

Each result includes:

  • allocated: whether the node is already taken in the current build

  • ascendancy: the ascendancy class that unlocks this node, or "" for generic nodes

  • distance_from_build: minimum passive points needed to reach this node from the current build (null if no build is loaded or the node is unreachable)

Use classes to restrict results to one or more class regions (Voronoi partition). This eliminates nodes from the opposite side of the tree that are never reachable in practice. Valid class names: Druid, Huntress, Mercenary, Monk, Sorceress, Warrior.

A build does not need to be loaded. Tree data must be present.

Examples: search_tree("life") → all life notables across the full tree search_tree("lightning", classes=["Monk"]) → lightning nodes near the Monk start search_tree("life", classes=["Monk", "Sorceress"]) → life nodes in two regions search_tree("Invoker") → all Invoker ascendancy nodes

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
include_small_nodesNo
classesNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The search_tree tool handler function decorated with @mcp.tool(). It searches the full passive skill tree for nodes matching a keyword (case-insensitive), with optional filtering by class region (Voronoi partition) and whether to include small nodes. Returns nodes with id, name, type, stats, ascendancy, allocation status, and distance from the current build.
    @mcp.tool()
    def search_tree(
        query: str,
        include_small_nodes: bool = False,
        classes: list[str] | None = None,
    ) -> list[dict]:
        """
        Search the full passive skill tree for nodes matching a keyword.
    
        Unlike search_passives, this searches ALL nodes in the tree — not just those
        allocated in the loaded build. Use this to discover nodes you haven't taken yet.
    
        Each result includes:
          - allocated: whether the node is already taken in the current build
          - ascendancy: the ascendancy class that unlocks this node, or "" for generic nodes
          - distance_from_build: minimum passive points needed to reach this node from the
            current build (null if no build is loaded or the node is unreachable)
    
        Use classes to restrict results to one or more class regions (Voronoi partition).
        This eliminates nodes from the opposite side of the tree that are never reachable
        in practice. Valid class names: Druid, Huntress, Mercenary, Monk, Sorceress, Warrior.
    
        A build does not need to be loaded. Tree data must be present.
    
        Examples:
          search_tree("life")                         → all life notables across the full tree
          search_tree("lightning", classes=["Monk"])  → lightning nodes near the Monk start
          search_tree("life", classes=["Monk", "Sorceress"])  → life nodes in two regions
          search_tree("Invoker")                      → all Invoker ascendancy nodes
        """
        if _tree is None:
            return [{"error": "Tree data not loaded — passive tree search unavailable."}]
    
        allocated_ids: set[int] = set(_build.allocated_node_ids) if _build is not None else set()
    
        distance_map = _build_distance_map(allocated_ids) if allocated_ids else {}
    
        matches = _tree.search(query, classes=classes)
    
        if not include_small_nodes:
            matches = [n for n in matches if n.is_keystone or n.is_notable or n.is_mastery]
    
        return [
            {
                "id": n.id,
                "name": n.name,
                "type": _node_type(n),
                "stats": n.stats,
                "ascendancy": n.ascendancy_name,
                "allocated": n.id in allocated_ids,
                "distance_from_build": distance_map.get(n.id),
            }
            for n in matches
        ]
  • The search_tree tool is registered via the @mcp.tool() decorator on line 205. The MCP server is instantiated on line 20 as FastMCP('poe2-mcp'), and the decorator pattern registers the function as a tool automatically.
    """
    PoE2 MCP server.
    
    Tools:
      load_build      — decode a PoB export code or pobb.in URL
      get_stats       — computed character stats (life, mana, damage, resists, …)
      get_passives    — allocated passive nodes (with names/stats if tree data is loaded)
      get_items       — equipped items and their mods
      get_skills      — skill socket groups and gem links
      search_passives — find allocated passives matching a keyword
      search_tree     — find any node in the full passive tree (allocated or not)
    """
    
    from mcp.server.fastmcp import FastMCP
  • Exports the PassiveTree class and TreeNode dataclass which define the schema used by search_tree. TreeNode includes id, name, stats, is_keystone, is_notable, is_mastery, ascendancy_name, and neighbors fields.
    from .loader import PassiveTree, TreeNode, load_tree, load_default_tree
    
    __all__ = ["PassiveTree", "TreeNode", "load_tree", "load_default_tree"]
  • The PassiveTree.search() method is the core search helper called by the search_tree handler. It performs a case-insensitive search across all node names and stats, with optional class region filtering using Voronoi partition data.
    def search(self, query: str, classes: list[str] | None = None) -> list[TreeNode]:
        """Return all nodes whose name or stats contain the query string (case-insensitive).
    
        If classes is provided, only nodes belonging to those Voronoi regions are returned.
        """
        q = query.lower()
        results = [
            node for node in self._nodes.values()
            if q in node.name.lower() or any(q in s.lower() for s in node.stats)
        ]
        if classes:
            class_set = set(classes)
            results = [n for n in results if self._voronoi.get(n.id, "") in class_set]
        return results
  • The TreeNode dataclass defines the structure of tree nodes returned by search_tree queries.
    @dataclass
    class TreeNode:
        id: int
        name: str
        stats: list[str] = field(default_factory=list)
        is_keystone: bool = False
        is_notable: bool = False
        is_mastery: bool = False
        ascendancy_name: str = ""
        neighbors: list[int] = field(default_factory=list)
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Describes the output fields (allocated, ascendancy, distance_from_build) and their behaviors, notes that a build does not need to be loaded, and explains the effect of the classes parameter. Provides sufficient transparency for a search tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections: purpose, contrast, return fields, usage guidance, examples. Every sentence adds value, and the length is appropriate for the complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Fully covers purpose, usage, parameters (with one minor gap), prerequisites, and examples. The presence of an output schema reduces the need to describe return format. Complete enough for correct agent invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The query and classes parameters are well described, but the include_small_nodes parameter is not mentioned in the description despite having no schema description coverage. This omission reduces completeness.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it searches the full passive skill tree for nodes matching a keyword, and distinguishes from the sibling 'search_passives' tool by noting this searches ALL nodes, not just allocated ones in the current build.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly contrasts with search_passives, explains when to use this tool (discover nodes not yet taken), provides guidance on using the classes parameter to restrict results, and lists valid class names and examples.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/deepwa7er/poe2-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server