Skip to main content
Glama

Logseq AI

AI-powered interactions with Logseq through two interfaces:

  1. MCP Server - Enables external AI tools (Windsurf, Claude Desktop) to interact with your Logseq graph

  2. Lain - Embedded AI assistant plugin within the Logseq UI

"No matter where you go, everyone's connected." - Serial Experiments Lain

Prerequisites

  • Node.js 18+

  • Logseq 2.x with a DB graph. File graphs (Logseq 1.x) are not supported — they use the older :block/content / :block/marker schema. The tools detect the graph type and fail with a clear message rather than returning empty results.

  • HTTP API server enabled (Settings → Features → HTTP APIs server)

Keep the Logseq window visible while the MCP server is in use. When the window is minimized or on another virtual desktop, Chromium throttles the timers Logseq's request queue depends on, and write calls hang until it regains focus. See doc/logseq-api-notes.md.

Task model in 2.x

Logseq 2.x replaced text markers with properties, so the task tools use Logseq's native vocabulary:

Values

Status

Backlog, Todo, Doing, In Review, Done, Canceled

Priority

Low, Medium, High, Urgent

Legacy spellings are still accepted on input and normalized (TODOTodo, AHigh), so existing prompts keep working. Writing "TODO buy milk" as block text, however, no longer creates a task — use create_task.

Related MCP server: Logseq MCP Server

Setup

# Install dependencies
pnpm install

# Build all packages
pnpm build

With just installed, just install and just build do the same; see Development for the full list.

Quick Start

Using the Core Library

import { LogseqClient, LogseqOperations } from "@logseq-ai/core";

// Create client and operations
const client = new LogseqClient({
  baseUrl: "http://localhost:12315",
  token: "your-api-token" // optional
});
const ops = new LogseqOperations(client);

// Search for pages
const results = await ops.search("meeting notes");

// Get page content as text
const content = await ops.getPageContent("My Notes");

// Create a new block
const block = await ops.createBlock({
  pageName: "My Notes",
  content: "New idea from AI!"
});

// Create multiple blocks with hierarchy (more efficient for structured content)
const result = await ops.createBlocks("My Notes", [
  {
    content: "## Project Overview",
    children: [
      { content: "Goal: Build a new feature" },
      { content: "Timeline: Q1 2025" },
    ]
  },
  {
    content: "## Tasks",
    children: [
      { content: "Design the API" },
      { content: "Implement backend" },
      { content: "Write tests" },
    ]
  }
]);
console.log(`Created ${result.created} blocks`);

// Run a Datalog query to find tasks.
// DB graphs store block text in :block/title; :block/content and
// :block/marker no longer exist.
const tasks = await ops.query(`
  [:find (pull ?b [:block/uuid :block/title])
   :where
   [?b :block/tags ?t]
   [?t :db/ident :logseq.class/Task]]
`);

Task Management

import { LogseqClient, LogseqOperations } from "@logseq-ai/core";

const client = new LogseqClient();
const ops = new LogseqOperations(client);

// Get open tasks (Backlog, Todo, Doing, In Review)
const tasks = await ops.getTasks();

// Or filter to specific statuses
const inFlight = await ops.getTasks({ statuses: ["Doing", "In Review"] });

// Get tasks from a specific page
const projectTasks = await ops.getTasks({ pageName: "Project A" });

// Create a task. In a DB graph this is a block tagged with Logseq's Task
// class - do NOT prefix the content with "TODO", that is just literal text.
const task = await ops.createTask({
  pageName: "Project A",
  content: "Review pull request",
  priority: "High",       // Low | Medium | High | Urgent
  deadline: "2026-08-15"
});

// Move it through the workflow
await ops.markTask(task.uuid, "Doing");
await ops.markTask(task.uuid, "In Review");
await ops.markTask(task.uuid, "Done");

// Set/change priority
await ops.setTaskPriority(task.uuid, "Medium");

// Set scheduled date
await ops.setTaskScheduled({ uuid: task.uuid, date: "2026-08-10" });

// Remove deadline
await ops.setTaskDeadline({ uuid: task.uuid, date: null });

Task Analytics

// Get overdue tasks
const overdue = await ops.getOverdueTasks();
console.log(`You have ${overdue.length} overdue tasks`);

// Get tasks due in the next 7 days
const dueSoon = await ops.getTasksDueSoon({ days: 7 });

// Get task statistics
const stats = await ops.getTaskStats();
console.log(`Total: ${stats.total}, Overdue: ${stats.overdue}`);
console.log(`By status:`, stats.byStatus);

// Search tasks by keyword
const reviewTasks = await ops.searchTasks({ query: "review", statuses: ["Todo"] });

Journal Operations

// Get or create today's journal
const today = await ops.getToday();
console.log(`Today's journal: ${today.page.title}`);

// Quick capture to today's journal
await ops.appendToToday("Meeting notes: discussed Q1 roadmap");

// Get recent journal entries
const journals = await ops.getRecentJournals({ days: 7, includeContent: true });
journals.forEach(j => console.log(`${j.date}: ${j.content.substring(0, 50)}...`));
// Find pages related to a topic
const links = await ops.findRelatedPages("Project A");
console.log("Pages linking to Project A:", links.backlinks);
console.log("Pages Project A links to:", links.forwardLinks);

// Find blocks referencing a specific block
const backlinks = await ops.getBlockBacklinks("block-uuid-123");
console.log(`Found ${backlinks.backlinks.length} references`);

Error Handling

import { 
  LogseqOperations, 
  LogseqApiError, 
  LogseqNotFoundError,
  isLogseqError 
} from "@logseq-ai/core";

try {
  await ops.getPageContent("Nonexistent Page");
} catch (error) {
  if (error instanceof LogseqNotFoundError) {
    console.log(`Page not found: ${error.identifier}`);
  } else if (isLogseqError(error)) {
    console.log(`Logseq error: ${error.toDetailedString()}`);
  }
}

Packages

@logseq-ai/core

Shared library for Logseq API interactions. Used by both the MCP server and plugin.

Key exports:

  • LogseqClient - Low-level HTTP client for Logseq API

  • LogseqOperations - High-level operations with error handling

  • Error classes: LogseqError, LogseqApiError, LogseqConnectionError, LogseqNotFoundError, LogseqValidationError

  • Type definitions: Page, Block, SearchResult, etc.

@logseq-ai/mcp-server

MCP server that exposes Logseq operations as tools for AI clients.

Features:

  • 43 tools for comprehensive Logseq interaction

  • Input validation with clear error messages (powered by Zod)

  • Proper error handling for Logseq API errors

# Build
pnpm --filter @logseq-ai/mcp-server build

# Run
LOGSEQ_API_TOKEN=your-token pnpm --filter @logseq-ai/mcp-server start

# Test
pnpm --filter @logseq-ai/mcp-server test

Configure in Windsurf/Claude Desktop

Add to your MCP configuration:

{
  "mcpServers": {
    "logseq": {
      "command": "node",
      "args": ["/path/to/logseq-ai/packages/mcp-server/dist/index.js"],
      "env": {
        "LOGSEQ_API_URL": "http://localhost:12315",
        "LOGSEQ_API_TOKEN": "your-token"
      }
    }
  }
}

Variable

Default

Description

LOGSEQ_API_URL

http://localhost:12315

Base URL of the Logseq HTTP API server

LOGSEQ_API_TOKEN

(none)

Authorization token

LOGSEQ_MAX_RETRIES

2

Retries for transient failures; 0 disables them

The client retries transient failures with exponential backoff and jitter. Connection failures are retried for any call, since the request never reached Logseq. Timeouts — including the empty response Logseq returns when its window is backgrounded — are retried for reads only: a mutation may have been applied before the response was lost, so replaying it could duplicate a block or page. Logseq handler errors are never retried.

A write that times out is reported as uncertain, not failed. Logseq cannot cancel a handler that is already running, so a timed-out write can commit seconds or minutes later — retrying it blindly is how one appended block becomes two. Where a cheap read-back exists, the error says what is actually there ("a page titled X now exists…", "2 blocks with this exact text are already on Y…"). A tree write that fails partway names the blocks that already landed, so the fix is to repair the page rather than write it again.

Available Tools

Page & Block Operations

Tool

Description

search_logseq

Search for pages and blocks

get_page

Get page content as plain text (includeUuids adds each block's uuid for editing)

get_block

Get one block and its children by uuid, with uuids attached

find_blocks

Find blocks by text, page, and/or tag - structured, escaped, bin-filtered

get_pages

Get multiple pages in a batch (more efficient)

get_page_with_context

Get page with backlinks and forward links

list_pages

List all pages in the graph

create_page

Create a new page (optionally with blocks)

delete_page

Delete a page (moves it to the recycle bin)

rename_page

Rename a page; references follow automatically

list_deleted_pages

List pages in the recycle bin

restore_page

Restore a page from the recycle bin

create_block

Create a single block

create_blocks

Create multiple blocks with hierarchy, on a page or under a block

update_block

Update a block's content

delete_block

Delete a block

query_logseq

Run Datalog queries

update_page_properties

Update properties on an existing page

Graph Discovery

Tool

Description

get_current_graph

Get current graph info

get_graph_stats

Get graph statistics (pages by type, orphans, etc.)

find_missing_pages

Find referenced pages that don't exist

find_orphan_pages

Find pages with no incoming links

find_pages_by_properties

Find pages by property values

find_related_pages

Find backlinks and forward links

get_block_backlinks

Find blocks referencing a block

Journal Operations

Tool

Description

get_today

Get today's journal page

append_to_today

Add content to today's journal

get_recent_journals

Get recent journal entries

Task Management

Tool

Description

get_tasks

Get open tasks (Backlog/Todo/Doing/In Review)

create_task

Create a new task

mark_task

Change task status

mark_tasks

Change multiple tasks' status (batch)

search_tasks

Search tasks by keyword

get_overdue_tasks

Get tasks past deadline

get_tasks_due_soon

Get tasks due within N days

get_task_stats

Get task statistics

set_task_priority

Set task priority (Low/Medium/High/Urgent)

set_task_deadline

Set task deadline

set_task_scheduled

Set task scheduled date

Example: Creating Structured Content

When creating pages with multiple sections, use create_blocks for efficiency:

User: Create a page about Python with sections for Overview, Features, and Links

AI uses:
1. create_page({ pageName: "Python", tags: ["Technology"], properties: { Topic: "Programming" } })
2. create_blocks({
     pageName: "Python",
     blocks: [
       {
         content: "## Overview",
         children: [
           { content: "Python is a high-level programming language." }
         ]
       },
       {
         content: "## Features",
         children: [
           { content: "Dynamic typing" },
           { content: "Garbage collection" }
         ]
       },
       {
         content: "## Links",
         children: [
           { content: "[Official Site](https://python.org)" }
         ]
       }
     ]
   })

To extend a section later, pass parentBlockUuid instead of pageName — the tree lands under that block rather than at the top level of the page. Both create_block and create_blocks take exactly one of the two; passing both is an error rather than a silently ignored argument:

create_blocks({
  parentBlockUuid: "<uuid of the '## Features' block>",
  blocks: [{ content: "Large standard library" }]
})

Block Content Rules

Every tool that writes block text (create_block, create_blocks, create_page, update_block, append_to_today, create_task, and template variable values) validates it first. Logseq accepts unsupported markup and stores it verbatim, so these mistakes succeed silently and corrupt the graph quietly; the MCP server rejects them with a message naming the fix instead:

  • One logical unit per block. No markdown list markers (- , 1. ), no second heading, no heading mixed with body text — Logseq is an outliner and renders one bullet per block. Use children for structure.

  • No file-graph task markup. A TODO prefix makes an ordinary block, not a task; [#A] creates a tag literally named A]. Use create_task, mark_task and set_task_priority.

  • No key:: value text. DB graphs store properties as structured fields — use the properties argument or update_page_properties.

  • No [[Page|label]] (a Roam convention) and no unbalanced [[. Write [[Page]], or [label]([[Page]]) for different link text.

Fenced code blocks are exempt — inside them the markup is meant literally. Multi-line prose is fine; only list and heading structure has to be split across blocks.

Page References

Write references as [[Page Title]]. A DB graph stores them by uuid, so before writing, each title is resolved to a page — created once if it does not exist yet — and rewritten to [[uuid]]. Letting Logseq auto-create reference targets per insert is what produced duplicate pages for the same title.

Resolving up front also lets a whole block tree go in as a single request, so a timeout cannot leave a half-built page behind. If a title cannot be resolved, that tree falls back to one insert per block, which resolves references itself at the cost of atomicity.

Reads go the other way: get_page and friends render [[uuid]] back as [[Page Title]], resolving every reference on the page in one query. A uuid whose page cannot be named is left as-is rather than dropped.

Editing What You Read

Every edit tool addresses blocks by uuid, so reads can hand you the uuids directly: pass includeUuids: true to get_page and each line comes back as - text ((uuid)), ready for update_block, delete_block, or create_blocks({ parentBlockUuid }). get_block re-reads a single subtree by uuid (uuids always included), and find_blocks locates blocks by text, page, and/or tag without hand-writing Datalog - values are escaped and recycle-bin content filtered automatically.

The Recycle Bin

delete_page never erases: Logseq stamps the page deleted-at and hides it, and the title cannot be reused while the page sits in the bin. list_deleted_pages shows what is in there, restore_page brings a page back (verified by re-reading, since Logseq reports success even for property writes it discards), and rename_page frees a title without deleting anything. Rename and restore both refuse conflicting titles - live or recycled - rather than creating duplicates.

logseq-lain

Lain - AI assistant plugin for Logseq.

# Build
pnpm --filter logseq-lain build

# Development (watch mode)
pnpm dev:plugin

Install in Logseq

  1. Enable Developer Mode in Logseq (Settings → Advanced → Developer mode)

  2. Go to Plugins → Load unpacked plugin

  3. Select the packages/logseq-plugin directory

  4. Use /lain ask, /lain summarize, /lain expand slash commands

Architecture

See doc/architecture.md for detailed architecture documentation.

Development

Common tasks are wrapped in a justfile. Run just to see them all.

just check          # format-check, lint, typecheck, test — everything CI enforces
just test           # run all test suites (489 tests)
just typecheck      # tsc across all three packages
just lint           # eslint
just format-check   # prettier, without writing
just coverage       # tests with a coverage report
just fix            # apply formatting and fixable lint rules, then re-check
just build          # compile every package

Each recipe is a thin wrapper over the equivalent pnpm script, so pnpm test, pnpm typecheck and friends still work on their own.

just check orders its steps cheapest-failure-first, so a formatting slip surfaces before the test suite runs.

Test layout

Package

Tests

Covers

core

256

Client transport, DB idents, operations, tasks, templates, errors

mcp-server

233

Tool registry wiring, Zod validation, handler dispatch and output

The mcp-server tests run against a stubbed LogseqOperations, so they need no running Logseq. The tool-registry tests assert that every advertised tool resolves to a handler — a tool without one is otherwise invisible until a client calls it and gets Unknown tool.

License

MIT

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.

  • Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.

  • Connect AI assistants to your GitHub-hosted Obsidian vault to seamlessly access, search, and analy…

View all MCP Connectors

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/HarrisonTotty/logseq-ai'

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