Skip to main content
Glama
camiloluvino

Roam Research MCP Server

by camiloluvino

roam_search_block_refs

Find block references within pages or across your entire Roam Research graph to track connections and identify relationships between notes.

Instructions

Search for block references within a page or across the entire graph. Can search for references to a specific block or find all block references.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
block_uidNoOptional: UID of the block to find references to
page_title_uidNoOptional: Title or UID of the page to search in (UID is preferred for accuracy). If not provided, searches across all pages.

Implementation Reference

  • Core handler logic: Executes Datomic queries to find blocks referencing a specific block UID or containing any block references ((uid)) within a page or graph-wide. Resolves embedded references and formats results.
    async execute(): Promise<SearchResult> {
      const { block_uid, page_title_uid } = this.params;
    
      // Get target page UID if provided
      let targetPageUid: string | undefined;
      if (page_title_uid) {
        targetPageUid = await SearchUtils.findPageByTitleOrUid(this.graph, page_title_uid);
      }
    
      // Build query based on whether we're searching for references to a specific block
      // or all block references within a page/graph
      let queryStr: string;
      let queryParams: any[];
    
      if (block_uid) {
        // Search for references to a specific block
        if (targetPageUid) {
          queryStr = `[:find ?block-uid ?block-str
                      :in $ ?ref-uid ?page-uid
                      :where [?p :block/uid ?page-uid]
                             [?b :block/page ?p]
                             [?b :block/string ?block-str]
                             [?b :block/uid ?block-uid]
                             [(clojure.string/includes? ?block-str ?ref-uid)]]`;
          queryParams = [`((${block_uid}))`, targetPageUid];
        } else {
          queryStr = `[:find ?block-uid ?block-str ?page-title
                      :in $ ?ref-uid
                      :where [?b :block/string ?block-str]
                             [?b :block/uid ?block-uid]
                             [?b :block/page ?p]
                             [?p :node/title ?page-title]
                             [(clojure.string/includes? ?block-str ?ref-uid)]]`;
          queryParams = [`((${block_uid}))`];
        }
      } else {
        // Search for any block references
        if (targetPageUid) {
          queryStr = `[:find ?block-uid ?block-str
                      :in $ ?page-uid
                      :where [?p :block/uid ?page-uid]
                             [?b :block/page ?p]
                             [?b :block/string ?block-str]
                             [?b :block/uid ?block-uid]
                             [(re-find #"\\(\\([^)]+\\)\\)" ?block-str)]]`;
          queryParams = [targetPageUid];
        } else {
          queryStr = `[:find ?block-uid ?block-str ?page-title
                      :where [?b :block/string ?block-str]
                             [?b :block/uid ?block-uid]
                             [?b :block/page ?p]
                             [?p :node/title ?page-title]
                             [(re-find #"\\(\\([^)]+\\)\\)" ?block-str)]]`;
          queryParams = [];
        }
      }
    
      const rawResults = await q(this.graph, queryStr, queryParams) as [string, string, string?][];
      
      // Resolve block references in content
      const resolvedResults = await Promise.all(
        rawResults.map(async ([uid, content, pageTitle]) => {
          const resolvedContent = await resolveRefs(this.graph, content);
          return [uid, resolvedContent, pageTitle] as [string, string, string?];
        })
      );
      
      const searchDescription = block_uid 
        ? `referencing block ((${block_uid}))`
        : 'containing block references';
        
      return SearchUtils.formatSearchResults(resolvedResults, searchDescription, !targetPageUid);
    }
  • Defines the tool schema including name, description, and input schema for 'roam_search_block_refs'.
    [toolName(BASE_TOOL_NAMES.SEARCH_BLOCK_REFS)]: {
      name: toolName(BASE_TOOL_NAMES.SEARCH_BLOCK_REFS),
      description: 'Search for block references within a page or across the entire graph. Can search for references to a specific block or find all block references.',
      inputSchema: {
        type: 'object',
        properties: {
          block_uid: {
            type: 'string',
            description: 'Optional: UID of the block to find references to'
          },
          page_title_uid: {
            type: 'string',
            description: 'Optional: Title or UID of the page to search in (UID is preferred for accuracy). If not provided, searches across all pages.'
          }
        }
      }
    },
  • Registers the tool handler in the MCP server request handler switch statement, delegating to ToolHandlers.searchBlockRefs.
    case BASE_TOOL_NAMES.SEARCH_BLOCK_REFS: {
      const params = request.params.arguments as {
        block_uid?: string;
        page_title_uid?: string;
      };
      const result = await this.toolHandlers.searchBlockRefs(params);
      return {
        content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
      };
    }
  • Wrapper method in ToolHandlers class that delegates to SearchOperations.searchBlockRefs.
    async searchBlockRefs(params: { block_uid?: string; page_title_uid?: string }) {
      return this.searchOps.searchBlockRefs(params);
    }
  • Method in SearchOperations class that instantiates and executes BlockRefSearchHandlerImpl.
    async searchBlockRefs(params: BlockRefSearchParams): Promise<SearchHandlerResult> {
      const handler = new BlockRefSearchHandlerImpl(this.graph, params);
      return handler.execute();
    }

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/camiloluvino/roamMCP'

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