npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@transmitt0r/openclaw-plugin-trilium

v1.4.3

Published

OpenClaw plugin for TriliumNext Notes

Readme

openclaw-plugin-trilium

CI License: MIT

An OpenClaw plugin for TriliumNext Notes, talking to its ETAPI REST API.

It registers a set of agent tools over notes, the tree (branches/cloning), labels/relations (attributes), attachments, revisions, and journal/calendar notes -- covering the same search/read/write/organize workflows you'd otherwise do by hand in Trilium's own UI. Tools are split by resource (notes, tree, attributes, attachments, revisions, calendar), each bounded by default rather than pulling a note's full content into context unless a caller specifically reads it that way.

Install

openclaw plugins install clawhub:transmitt0r/openclaw-plugin-trilium

Or for local development, point OpenClaw at a built copy of this repo.

Configure

The plugin needs the base URL of your Trilium instance and an ETAPI token (Options → ETAPI → Create new ETAPI token in the Trilium UI):

{
  "plugins": {
    "entries": {
      "trilium": {
        "config": {
          "baseUrl": "https://trilium.example.com",
          "apiToken": "your-etapi-token"
        }
      }
    }
  }
}

apiToken also accepts a SecretRef instead of a plain string:

openclaw config set plugins.entries.trilium.config.apiToken \
  --ref-provider default --ref-source env --ref-id TRILIUM_TOKEN

(or --ref-source exec/file for a password manager CLI, vault, etc.)

Tools

| Tool | Description | | --- | --- | | trilium_search_notes | Search/filter notes using Trilium's own query language (free text, #label, ~relation, note.property filters, boolean logic). Never returns full content. search is hybrid lexical/attribute + semantic under the hood -- no separate param or tool for it. | | trilium_get_note | Fetch a note's metadata by id -- title, type, attributes, tree placement. By default resolves parent/child ids to {noteId, branchId, title, type} so tree navigation rarely needs a follow-up call (resolve_names, capped at 50 ids). Can also include the note's attachments/revisions in the same call. | | trilium_read_note_content | Read a note's content, bounded to a line range (capped at 500 lines/call, defaults to the first 200). text-type note content (CKEditor HTML) is converted to plain text by default -- pass raw_html: true for the original markup. | | trilium_create_note | Create a note and place it in the tree. Plain text content is auto-wrapped into HTML paragraphs for text notes unless it already looks like HTML. | | trilium_update_note | Update a note's title/type/mime and/or replace its full content in one call (content has no partial/append write in ETAPI). | | trilium_delete_note | Delete a note (and its subtree). Recoverable via trilium_undelete_note. | | trilium_undelete_note | Restore a previously deleted note. | | trilium_get_recent_changes | List recent note creations/modifications/deletions, newest first -- "what did I change today" style queries. | | trilium_place_note_in_tree | Clone a note into (or move/reorder/reprefix it within) a tree location. | | trilium_remove_note_from_location | Remove one tree placement of a note without necessarily deleting the note itself (if it has other placements). | | trilium_create_attribute | Add a label (key-value tag) or relation (typed link) to a note. | | trilium_update_attribute | Update an existing label's value or any attribute's position by id. | | trilium_delete_attribute | Remove a label or relation by id. | | trilium_create_attachment | Create an attachment owned by a note (or revision). | | trilium_get_attachment | Fetch an attachment's metadata, optionally including its (bounded) content. | | trilium_update_attachment | Update an attachment's metadata and/or replace its content. | | trilium_delete_attachment | Permanently delete an attachment (no undelete, unlike notes). | | trilium_create_revision | Snapshot a note's current title/content -- useful before a large rewrite. | | trilium_read_revision_content | Read a past revision's content (bounded, same as trilium_read_note_content). | | trilium_get_calendar_note | Get or create the day/week/month/year journal note or the inbox note for a given date. |

Semantic search

trilium_search_notes understands meaning, not just keywords -- searching "car insurance" also finds a note whose text only ever says "Kfz-Haftpflichtversicherung". This happens automatically inside the existing search param; there's no separate tool or mode to choose.

This needs an embedding provider configured, and by default sends data off your machine: text/code note content and your search terms are sent to whatever endpoint you configure to be embedded. Without semanticSearch.embedding fully configured, this stays off automatically -- trilium_search_notes silently falls back to Trilium's own lexical/attribute-only search, the same way it fails open if the embedding endpoint is unreachable, rate limits you, or errors for any other reason.

Powered by @transmitt0r/mycelium-embed: any OpenAI-compatible /v1/embeddings endpoint works -- OpenAI, OpenRouter, Ollama, vLLM, LM Studio, and so on. baseUrl, apiKey, model, and dimensions are all required (models vary by endpoint, so there's no universal default to fall back to):

{
  "plugins": {
    "entries": {
      "trilium": {
        "config": {
          "semanticSearch": {
            "embedding": {
              "baseUrl": "https://openrouter.ai/api/v1",
              "apiKey": "your-api-key",
              "model": "text-embedding-3-small",
              "dimensions": 1536
            }
          }
        }
      }
    }
  }
}

Or run entirely on-CPU with zero API calls, opt in explicitly with "provider": "local" (baseUrl/apiKey aren't needed there -- model/dimensions default to a small bundled ONNX model). This is never chosen automatically: an earlier in-process local-inference attempt got OOM-killed in production on a memory-constrained host, so treat it as something to enable deliberately on a box with enough headroom, not a drop-in default.

semanticSearch.embedding.apiKey accepts a SecretRef too, same as apiToken above. Once configured, the index builds up in the background; to turn it back off or move where its local index file lives:

{
  "semanticSearch": {
    "enabled": false,
    "indexPath": "/custom/path/semantic-index.db"
  }
}

Only text and code notes are indexed -- file/image/canvas/etc. notes don't have free-form textual content to embed meaningfully. Trilium's ETAPI has no pagination on its search endpoint (only a flat limit), so each sync pass pages through up to 200 changed notes by re-querying with an advancing utcDateModified cursor; a pass runs on plugin startup and every 15 minutes after. A vault with more than a couple hundred existing text/code notes backfills gradually over several passes (partial semantic coverage in the meantime -- lexical/attribute search still works normally throughout) rather than all at once, the same bound @transmitt0r/mycelium-index applies to every source it syncs.

Standalone MCP server

These tools also run outside OpenClaw entirely, as an ordinary MCP server (stdio or Streamable HTTP), via @transmitt0r/mycelium-mcp. Useful for any MCP client -- Claude Desktop, Claude Code, etc. -- not just OpenClaw.

Configuration is env vars instead of openclaw.json:

| Env var | Required | Notes | | --- | --- | --- | | TRILIUM_BASE_URL | yes | Same as baseUrl above | | TRILIUM_API_TOKEN | yes | Same as apiToken above -- always a plain string here, no SecretRef support (that's an OpenClaw config-system concept) | | TRILIUM_BASE_URL_FILE / TRILIUM_API_TOKEN_FILE | no | Docker-secret variants: path to a file whose trimmed contents are used instead of the plain env var above (the _FILE convention used by the postgres image and friends). Takes precedence when set | | TRILIUM_SEMANTIC_SEARCH_ENABLED | no | Set to true/false to enable/disable (defaults on, same fail-open behavior as the plugin). Only the exact strings "true"/"false" are accepted; any other value aborts startup | | TRILIUM_SEMANTIC_INDEX_PATH | no | Defaults under ~/.mycelium/trilium/ | | TRILIUM_EMBEDDING_PROVIDER | no | openai-compatible (default) or local -- see the Semantic search section above | | TRILIUM_EMBEDDING_BASE_URL / TRILIUM_EMBEDDING_API_KEY / TRILIUM_EMBEDDING_MODEL / TRILIUM_EMBEDDING_DIMENSIONS | see above | Required together for the openai-compatible provider, same as semanticSearch.embedding.* above | | TRILIUM_READ_ONLY | no | Set to exactly true to register only the read tools (search, get/read note, recent changes, attachment metadata, revision content) -- the write tools (create/update/delete note, attributes, attachments, revisions, calendar-journal materialization) are never registered at all, so they can't be listed or called. Good defense-in-depth whenever the server is exposed over HTTP -- but it trims the tool list only; it is not a substitute for authenticating the HTTP transport. Any unrecognized non-empty value fails startup rather than silently shipping a writable server | | MCP_TRANSPORT | no | stdio (default) or http. Only stdio/http (or unset/empty) are accepted; any other value aborts startup instead of silently falling back | | MCP_PORT | no | Only used when MCP_TRANSPORT=http; default 3000; the Streamable HTTP path is fixed at the MCP standard /mcp. MCP_PORT must be a decimal integer (1–65535) | | MCP_HOST | no | Only used when MCP_TRANSPORT=http; default 127.0.0.1 (loopback-only). Set to 0.0.0.0 to expose the server on all interfaces (e.g. a bridged Docker network) — always an explicit choice, never the default, and it requires MCP_ALLOWED_HOSTS (see below) or startup fails. The app provides no authentication; any non-loopback exposure MUST sit behind an authenticated reverse proxy (e.g. Caddy Basic auth) and ideally run with TRILIUM_READ_ONLY=true | | MCP_ALLOWED_HOSTS | no | Only used when MCP_TRANSPORT=http; comma-separated list of bare hostnames/IPs the server will accept in the Host header (DNS-rebinding protection). Setting it replaces the loopback-only default, and a non-loopback bind (MCP_HOST=0.0.0.0) requires it. List the hostname clients will send: the public FQDN behind a reverse proxy (MCP_ALLOWED_HOSTS=mcp.example.com), or localhost/127.0.0.1 for a host-published port (-p 127.0.0.1:3000:3000). Not access control — the Host header is client-controlled; the real boundary for exposure is an authenticated reverse proxy |

pnpm run build
TRILIUM_BASE_URL=https://trilium.example.com TRILIUM_API_TOKEN=your-etapi-token \
  pnpm run start:mcp

There's no npm bin entry for this yet -- node dist/mcp-server.js (or pnpm run start:mcp) is the current way to run it outside a container. A Dockerfile is available too (build from the monorepo root, not this directory -- see the Dockerfile's own header comment for the exact command); it isn't published to any registry, so build it locally.

Skills

The plugin bundles one skill (skills/trilium) that OpenClaw picks up automatically once it's installed: on-demand note search and retrieval, plus lightweight organization (labels, tree placement). Everyone's own note-taking conventions differ enough that anything more opinionated (auto-tagging rules, a fixed daily-journal template, etc.) is left for you to write in your own workspace against these tools, tuned to how you actually use Trilium.

Development

See CONTRIBUTING.md for dev setup, regenerating API types, commit conventions, and how releases work.