Skip to main content
Glama
import-markdown.ts3.42 kB
#!/usr/bin/env node import { initializeGraph } from '@roam-research/roam-api-sdk'; import { API_TOKEN, GRAPH_NAME } from '../config/environment.js'; import { PageOperations } from '../tools/operations/pages.js'; import { parseMarkdown } from '../markdown-utils.js'; interface MarkdownNode { content: string; level: number; heading_level?: number; children: MarkdownNode[]; } /** * Flatten nested MarkdownNode[] to flat array with absolute levels */ function flattenNodes( nodes: MarkdownNode[], baseLevel: number = 1 ): Array<{ text: string; level: number; heading?: number }> { const result: Array<{ text: string; level: number; heading?: number }> = []; for (const node of nodes) { result.push({ text: node.content, level: baseLevel, ...(node.heading_level && { heading: node.heading_level }) }); if (node.children.length > 0) { result.push(...flattenNodes(node.children, baseLevel + 1)); } } return result; } /** * Read all input from stdin */ async function readStdin(): Promise<string> { const chunks: Buffer[] = []; for await (const chunk of process.stdin) { chunks.push(chunk); } return Buffer.concat(chunks).toString('utf-8'); } /** * Show usage help */ function showUsage(): void { console.error('Usage: roam-import <page-title>'); console.error(''); console.error('Reads markdown from stdin and imports to Roam Research.'); console.error(''); console.error('Examples:'); console.error(' cat document.md | roam-import "Meeting Notes"'); console.error(' pbpaste | roam-import "Ideas"'); console.error(' echo "- Item 1\\n- Item 2" | roam-import "Quick Note"'); console.error(''); console.error('Environment variables required:'); console.error(' ROAM_API_TOKEN Your Roam Research API token'); console.error(' ROAM_GRAPH_NAME Your Roam graph name'); } async function main(): Promise<void> { // Parse CLI arguments const args = process.argv.slice(2); const pageTitle = args[0]; if (!pageTitle || pageTitle === '--help' || pageTitle === '-h') { showUsage(); process.exit(pageTitle ? 0 : 1); } // Check if stdin is a TTY (no input piped) if (process.stdin.isTTY) { console.error('Error: No input received. Pipe markdown content to this command.'); console.error(''); showUsage(); process.exit(1); } // Read markdown from stdin const markdownContent = await readStdin(); if (!markdownContent.trim()) { console.error('Error: Empty input received.'); process.exit(1); } // Initialize Roam graph const graph = initializeGraph({ token: API_TOKEN, graph: GRAPH_NAME }); // Parse markdown to nodes const nodes = parseMarkdown(markdownContent) as MarkdownNode[]; // Flatten nested structure to content blocks const contentBlocks = flattenNodes(nodes); if (contentBlocks.length === 0) { console.error('Error: No content blocks parsed from input.'); process.exit(1); } // Create page with content const pageOps = new PageOperations(graph); const result = await pageOps.createPage(pageTitle, contentBlocks); if (result.success) { console.log(`Created page '${pageTitle}' (uid: ${result.uid})`); } else { console.error(`Failed to create page '${pageTitle}'`); process.exit(1); } } main().catch((error: Error) => { console.error(`Error: ${error.message}`); process.exit(1); });

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/2b3pro/roam-research-mcp'

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