Skip to main content
Glama

workflowy_list_nodes

Retrieve child nodes from a WorkFlowy outline to navigate hierarchical task structures. This tool helps organize and access nested information within your outlines.

Instructions

DEPRECATED: Use workflowy_glimpse (GLIMPSE) instead

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
parent_idNo

Implementation Reference

  • Registration and deprecated handler stub for 'workflowy_list_nodes' tool. Raises ValueError with deprecation notice recommending workflowy_glimpse.
    @mcp.tool(name="workflowy_list_nodes", description="DEPRECATED: Use workflowy_glimpse (GLIMPSE) instead") async def list_nodes_base(parent_id: str | None = None) -> dict: """Deprecated - use GLIMPSE instead.""" raise ValueError("""⚠️ FUNCTION RENAMED The function 'workflowy_list_nodes' has been renamed to 'workflowy_list_nodes__WARNING__prefer_glimpse'. BUT MORE IMPORTANTLY: Use workflowy_glimpse (GLIMPSE command) instead! ✅ RECOMMENDED: workflowy_glimpse(node_id="...") Returns: {"root": {...}, "children": [...]} with complete tree structure. GLIMPSE is better: - Gets full nested tree (not just direct children) - Gets root node metadata - More efficient 📚 Build the GLIMPSE habit! """)
  • Full handler logic (under warning suffixed name). Validates secret_code, creates NodeListRequest, calls client.list_nodes, returns nodes list.
    @mcp.tool(name="workflowy_list_nodes__WARNING__prefer_glimpse", description="⚠️ WARNING: Prefer workflowy_glimpse (GLIMPSE) for reading trees. List WorkFlowy nodes (omit parent_id for root)") async def list_nodes( parent_id: str | None = None, secret_code: str | None = None, ) -> dict: """List WorkFlowy nodes. Args: parent_id: ID of parent node to list children for (omit or pass None to list root nodes - parameter won't be sent to API) secret_code: Authorization code from Dan (required for WARNING functions) Returns: Dictionary with 'nodes' list and 'total' count """ # 🔐 SECRET CODE VALIDATION is_valid, error = validate_secret_code(secret_code, "workflowy_list_nodes__WARNING__prefer_glimpse") if not is_valid: raise ValueError(error) client = get_client() request = NodeListRequest( # type: ignore[call-arg] parentId=parent_id, ) if _rate_limiter: await _rate_limiter.acquire() try: nodes, total = await client.list_nodes(request) if _rate_limiter: _rate_limiter.on_success() return { "nodes": [node.model_dump() for node in nodes], "total": total, "_warning": "⚠️ For reading multiple nodes or full trees, use workflowy_glimpse (GLIMPSE) instead for efficiency" } except Exception as e: if _rate_limiter and hasattr(e, "__class__") and e.__class__.__name__ == "RateLimitError": _rate_limiter.on_rate_limit(getattr(e, "retry_after", None)) raise
  • Pydantic input schema model NodeListRequest used by list_nodes handlers.
    class NodeListRequest(BaseModel): """Request parameters for listing nodes.""" parentId: str | None = Field(None, description="Parent node ID to list children for")
  • Core client-side implementation: HTTP GET /nodes with parent_id param, response parsing to WorkFlowyNode list, full retry/rate-limit handling.
    async def list_nodes(self, request: NodeListRequest, max_retries: int = 10) -> tuple[list[WorkFlowyNode], int]: """List nodes with optional filtering and exponential backoff retry. Args: request: Node list request max_retries: Maximum retry attempts (default 10) """ import asyncio logger = _ClientLogger() retry_count = 0 base_delay = 1.0 while retry_count < max_retries: # Force delay at START of each iteration (rate limit protection) await asyncio.sleep(API_RATE_LIMIT_DELAY) try: # exclude_none=True ensures parent_id is omitted entirely for root nodes # (API requires absence of parameter, not null value) # Build params manually to ensure snake_case (API expects parent_id not parentId) params = {} if request.parentId is not None: params['parent_id'] = request.parentId response = await self.client.get("/nodes", params=params) response_data: list[Any] | dict[str, Any] = await self._handle_response(response) # Assuming API returns an array of nodes directly # (Need to verify actual response structure) nodes: list[WorkFlowyNode] = [] if isinstance(response_data, dict): if "nodes" in response_data: nodes = [WorkFlowyNode(**node_data) for node_data in response_data["nodes"]] elif isinstance(response_data, list): nodes = [WorkFlowyNode(**node_data) for node_data in response_data] total = len(nodes) # API doesn't provide a total count return nodes, total except RateLimitError as e: retry_count += 1 retry_after = getattr(e, 'retry_after', None) or (base_delay * (2 ** retry_count)) logger.warning( f"Rate limited on list_nodes. Retry after {retry_after}s. " f"Attempt {retry_count}/{max_retries}" ) if retry_count < max_retries: await asyncio.sleep(retry_after) else: raise except NetworkError as e: retry_count += 1 logger.warning( f"Network error on list_nodes: {e}. Retry {retry_count}/{max_retries}" ) if retry_count < max_retries: await asyncio.sleep(base_delay * (2 ** retry_count)) else: raise except httpx.TimeoutException as err: retry_count += 1 logger.warning( f"Timeout error: {err}. Retry {retry_count}/{max_retries}" ) if retry_count < max_retries: await asyncio.sleep(base_delay * (2 ** retry_count)) else: raise TimeoutError("list_nodes") from err raise NetworkError("list_nodes failed after maximum retries")

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/daniel347x/workflowy-mcp-fixed'

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