diff --git a/data/scripts/logseq-cli b/data/scripts/logseq-cli new file mode 100755 index 0000000..a830d0c --- /dev/null +++ b/data/scripts/logseq-cli @@ -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 -- "$@" diff --git a/data/tools/logseq b/data/tools/logseq new file mode 100755 index 0000000..ccdf7f0 --- /dev/null +++ b/data/tools/logseq @@ -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) diff --git a/data/tools/logseq.meta b/data/tools/logseq.meta new file mode 100644 index 0000000..07f0dd6 --- /dev/null +++ b/data/tools/logseq.meta @@ -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\":\"\",\"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 +} diff --git a/justfile b/justfile index 5473cd1..bc3e624 100644 --- a/justfile +++ b/justfile @@ -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 ===