add logseq tool: query/modify Logseq DB graph via built-in MCP server

- data/tools/logseq: Python wrapper (same pattern as atlassian_cli)
- data/tools/logseq.meta: tool metadata with full prompt
- data/scripts/logseq-cli: shell bridge using mcp-to-command + mcp-remote
- justfile: install logseq tool + logseq-cli script

Commands: listPages, getPage, searchBlocks, listTags, listProperties, upsertNodes
Transport: streamable-http to http://127.0.0.1:12315/mcp (Logseq 2 DB built-in)
This commit is contained in:
Levi Neely 2026-07-31 15:01:20 +02:00
parent 9b087f23d1
commit c75dc6f6c7
4 changed files with 105 additions and 1 deletions

24
data/scripts/logseq-cli Executable file
View File

@ -0,0 +1,24 @@
#!/bin/sh
set -e
for cmd in mcp-to-command mcp-remote; do
command -v "$cmd" >/dev/null 2>&1 || { echo "Error: Missing required command: $cmd" >&2; exit 1; }
done
LOGSEQ_MCP_URL="${LOGSEQ_MCP_URL:-http://127.0.0.1:12315}"
have_timeout=
command -v timeout >/dev/null 2>&1 && have_timeout=1
if [ -n "$have_timeout" ]; then
exec timeout 30 mcp-to-command mcp-remote "$LOGSEQ_MCP_URL/mcp" --transport http-first -- "$@" || {
if [ $? -eq 124 ]; then
echo "Error: timeout exceeded, may want to retry" >&2
exit 124
else
exit $?
fi
}
fi
exec mcp-to-command mcp-remote "$LOGSEQ_MCP_URL/mcp" --transport http-first -- "$@"

61
data/tools/logseq Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
import json
import os
import subprocess
import sys
sys.path.insert(0, os.environ.get('OLLIE_TOOLS_PATH', os.path.dirname(os.path.abspath(__file__))))
from _lib.args import parse_args
RESERVED_KEYS = {"command"}
def to_flags(raw: dict) -> list[str]:
"""Convert all non-reserved keys to --key=value flags."""
flags = []
for key, value in raw.items():
if key in RESERVED_KEYS:
continue
if isinstance(value, str):
try:
parsed = json.loads(value)
if isinstance(parsed, dict):
for k, v in parsed.items():
flags.append(f"--{k}={v}")
continue
elif isinstance(parsed, list):
for item in parsed:
flags.append(f"--{key}={item}")
continue
except (json.JSONDecodeError, TypeError):
pass
flags.append(f"--{key}={value}")
elif isinstance(value, list):
for item in value:
flags.append(f"--{key}={item}")
elif isinstance(value, bool):
if value:
flags.append(f"--{key}")
elif isinstance(value, dict):
flags.append(f"--{key}={json.dumps(value)}")
else:
flags.append(f"--{key}={value}")
return flags
args = parse_args()
raw = args.raw()
command = args.require("command")
cmd = ["logseq-cli", command] + to_flags(raw)
result = subprocess.run(cmd, capture_output=True, text=True)
if result.stdout:
sys.stdout.write(result.stdout)
if result.stderr:
sys.stderr.write(result.stderr)
sys.exit(result.returncode)

17
data/tools/logseq.meta Normal file
View File

@ -0,0 +1,17 @@
{
"description": "Query and modify your Logseq knowledge graph via the built-in MCP server.",
"prompt": "## logseq\n\nQuery and modify your Logseq DB graph via the built-in MCP server.\n\n**Args**: `command` (required), plus any tool parameters as top-level keys.\n\n### Available commands\n\n```\nlogseq(command=\"listPages\")\nlogseq(command=\"listPages\", expand=true)\nlogseq(command=\"getPage\", pageName=\"My Page\")\nlogseq(command=\"searchBlocks\", searchTerm=\"keyword\")\nlogseq(command=\"listTags\")\nlogseq(command=\"listTags\", expand=true)\nlogseq(command=\"listProperties\")\nlogseq(command=\"listProperties\", expand=true)\nlogseq(command=\"upsertNodes\", operations=[...], dry-run=true)\n```\n\n### upsertNodes operations format\n\nEach operation is an object with:\n- `operation`: \"add\" or \"edit\"\n- `entityType`: \"block\", \"page\", \"tag\", or \"property\"\n- `id`: UUID string (required for edit), temp string for add (if referenced later)\n- `data`: object with fields to set:\n - `title`: page/tag/property name or block content\n - `page-id`: page UUID (required when adding a block)\n - `tags`: list of tag UUIDs\n\nExample: add a block to a page:\n```\nlogseq(command=\"upsertNodes\", operations=[{\"operation\":\"add\",\"entityType\":\"block\",\"id\":null,\"data\":{\"page-id\":\"<page-uuid>\",\"title\":\"New block text\"}}])\n```\n\n- Use `dry-run=true` to validate without committing.\n- Before creating a page/tag/property, check if it exists with `getPage`.\n- Every key except `command` becomes a `--key=value` flag.",
"args": {
"type": "object",
"required": ["command"],
"properties": {
"command": {
"type": "string",
"description": "Logseq MCP tool name (listPages, getPage, searchBlocks, listTags, listProperties, upsertNodes)"
}
},
"additionalProperties": true
},
"tier": "hot",
"readOnly": false
}

View File

@ -165,6 +165,7 @@ install-data:
install -m755 data/tools/route {{cfg}}/tools/route
install -m755 data/tools/subagent_generate {{cfg}}/tools/subagent_generate
install -m755 data/tools/subagent_spawn {{cfg}}/tools/subagent_spawn
install -m755 data/tools/logseq {{cfg}}/tools/logseq
install -m755 data/tools/system_logs_dmesg {{cfg}}/tools/system_logs_dmesg
install -m755 data/tools/system_logs_journald {{cfg}}/tools/system_logs_journald
# Tool metadata (.meta sidecar files)
@ -175,10 +176,11 @@ install-data:
install -m755 data/tools/_lib/args.sh {{cfg}}/tools/_lib/args.sh
# Help
install -m644 doc/help.md {{cfg}}/help.md
# Scripts (ollie-remount, ollie-watchdog)
# Scripts (ollie-remount, ollie-watchdog, logseq-cli)
mkdir -p {{bin}}
install -m755 data/scripts/ollie-remount {{bin}}/ollie-remount
install -m755 data/scripts/ollie-watchdog {{bin}}/ollie-watchdog
install -m755 data/scripts/logseq-cli {{bin}}/logseq-cli
install-autostart:
@echo === Installing autostart files ===