roam_fetch_block_with_children
Retrieve a Roam Research block with its hierarchical child structure to specified depth, returning nested UID, text, order, and children data for knowledge graph analysis.
Instructions
Fetch a block by its UID along with its hierarchical children down to a specified depth. Returns a nested object structure containing the block's UID, text, order, and an array of its children.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| block_uid | Yes | The UID of the block to fetch. | |
| depth | No | Optional: The number of levels deep to fetch children. Defaults to 4. |
Implementation Reference
- Implements the core logic to fetch a Roam block by UID and its children recursively up to a specified depth using Datomic queries on the Roam graph.async fetchBlockWithChildren(block_uid_raw: string, depth: number = 4): Promise<RoamBlock | null> { if (!block_uid_raw) { throw new McpError(ErrorCode.InvalidRequest, 'block_uid is required.'); } const block_uid = block_uid_raw.replace(/^\(\((.*)\)\)$/, '$1'); const fetchChildren = async (parentUids: string[], currentDepth: number): Promise<Record<string, RoamBlock[]>> => { if (currentDepth >= depth || parentUids.length === 0) { return {}; } const childrenQuery = `[:find ?parentUid ?childUid ?childString ?childOrder ?childHeading :in $ [?parentUid ...] :where [?parent :block/uid ?parentUid] [?parent :block/children ?child] [?child :block/uid ?childUid] [?child :block/string ?childString] [?child :block/order ?childOrder] [(get-else $ ?child :block/heading 0) ?childHeading]]`; const childrenResults = await q(this.graph, childrenQuery, [parentUids]) as [string, string, string, number, number | null][]; const childrenByParent: Record<string, RoamBlock[]> = {}; const allChildUids: string[] = []; for (const [parentUid, childUid, childString, childOrder, childHeading] of childrenResults) { if (!childrenByParent[parentUid]) { childrenByParent[parentUid] = []; } childrenByParent[parentUid].push({ uid: childUid, string: childString, order: childOrder, heading: childHeading || undefined, children: [], }); allChildUids.push(childUid); } const grandChildren = await fetchChildren(allChildUids, currentDepth + 1); for (const parentUid in childrenByParent) { for (const child of childrenByParent[parentUid]) { child.children = grandChildren[child.uid] || []; } childrenByParent[parentUid].sort((a, b) => a.order - b.order); } return childrenByParent; }; try { const rootBlockQuery = `[:find ?string ?order ?heading :in $ ?blockUid :where [?b :block/uid ?blockUid] [?b :block/string ?string] [?b :block/order ?order] [(get-else $ ?b :block/heading 0) ?heading]]`; const rootBlockResult = await q(this.graph, rootBlockQuery, [block_uid]) as [string, number, number | null] | null; if (!rootBlockResult) { return null; } const [rootString, rootOrder, rootHeading] = rootBlockResult; const childrenMap = await fetchChildren([block_uid], 0); return { uid: block_uid, string: rootString, order: rootOrder, heading: rootHeading || undefined, children: childrenMap[block_uid] || [], }; } catch (error) { throw new McpError( ErrorCode.InternalError, `Failed to fetch block with children: ${error instanceof Error ? error.message : String(error)}` ); } }
- src/tools/schemas.ts:556-575 (schema)Defines the input schema, description, and name for the 'roam_fetch_block_with_children' tool.[toolName(BASE_TOOL_NAMES.FETCH_BLOCK_WITH_CHILDREN)]: { name: toolName(BASE_TOOL_NAMES.FETCH_BLOCK_WITH_CHILDREN), description: 'Fetch a block by its UID along with its hierarchical children down to a specified depth. Returns a nested object structure containing the block\'s UID, text, order, and an array of its children.', inputSchema: { type: 'object', properties: { block_uid: { type: 'string', description: 'The UID of the block to fetch.' }, depth: { type: 'integer', description: 'Optional: The number of levels deep to fetch children. Defaults to 4.', minimum: 0, maximum: 10 } }, required: ['block_uid'] }, },
- src/server/roam-server.ts:340-349 (registration)Registers the tool by handling the CallToolRequest in the MCP server, dispatching to ToolHandlers.fetchBlockWithChildren based on the base tool name.case BASE_TOOL_NAMES.FETCH_BLOCK_WITH_CHILDREN: { const { block_uid, depth } = request.params.arguments as { block_uid: string; depth?: number; }; const result = await this.toolHandlers.fetchBlockWithChildren(block_uid, depth); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; }