Claude Cursor Skill

obsidian-rest-api

Call the Obsidian Local REST API directly (over HTTP) for vault operations the mcp__obsidian__* tools do NOT expose — move/rename a note, overwrite a whole file atomically (PUT), act on the currently-open active file, run an Obsidian command, open a note in the UI, list all tags,

LLM Mart · 0 points · 3 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download davepoon-buildwithclaude-plugins_all-skills_skills_obsidian-rest-api-a6c484b.zip · 6 KB
Part of davepoon/buildwithclaude — 187 skills

Install

skills CLI npx skills add https://github.com/davepoon/buildwithclaude/tree/main/plugins/all-skills/skills/obsidian-rest-api
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install davepoon-buildwithclaude@llmmart
Git git clone https://github.com/davepoon/buildwithclaude.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole davepoon/buildwithclaude collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Obsidian Local REST API

The connected obsidian MCP server exposes only a subset of the Obsidian Local REST API (plugin obsidian-local-rest-api). This skill provides the full API surface plus an authenticated request wrapper, so a missing MCP method is called over HTTP instead of being worked around with hacks (e.g. delete+recreate to rename a note).

When to Use This Skill

Use the mcp__obsidian__* tools first for read, append, patch, delete, and search. Fall back to this skill only for operations that have no MCP tool:

  • Move / rename a note (preserves history, updates internal links)
  • Overwrite a whole file atomically (PUT) instead of delete+recreate
  • Act on the currently-open "active" file in the Obsidian UI
  • Run an Obsidian command from the command palette
  • Open / focus a note in the UI
  • List all vault tags with counts
  • Create/update/delete date-specific periodic notes

What This Skill Does

  1. Resolves the API host, port, and key from the connected obsidian MCP server config (~/.claude.json) or OBSIDIAN_* env vars — no hardcoded secrets.
  2. Handles the plugin's self-signed TLS certificate.
  3. Exposes every endpoint of the Local REST API (see references/api_reference.md), with the header enums (Operation, Target-Type, Target-Scope), the custom MOVE contract, and the search (JsonLogic/Dataview) formats.

How to Use

Call the wrapper scripts/olrapi.sh <METHOD> <path> [curl args...]:

S=scripts/olrapi.sh   # adjust to the skill's install path

# rename/move a note (the most common reason to reach for this skill)
"$S" MOVE "/vault/Path/To/Old Name.md" -H 'Destination: Path/To/New Name.md'

# move into a folder, keeping the filename (trailing slash on Destination)
"$S" MOVE "/vault/Inbox/todo.md" -H 'Destination: Archive/'

# atomically overwrite a whole note
"$S" PUT "/vault/Path/Note.md" -H 'Content-Type: text/markdown' --data-binary @/tmp/body.md

# read a note as structured JSON (frontmatter + tags + stat)
"$S" GET "/vault/Path/Note.md" -H 'Accept: application/vnd.olrapi.note+json'

# list tags, run a command, open a note in the UI
"$S" GET /tags/
"$S" POST "/commands/editor:toggle-bold/"
"$S" POST "/open/Path/Note.md?newLeaf=true"

The wrapper prints <<HTTP nnn>> after the body. Success: 200/204. On MOVE, 409 means the destination exists — add -H 'Allow-Overwrite: true' to force. For non-trivial calls, load references/api_reference.md.

Path & encoding rules

  • {filename} is vault-relative (no leading slash on the vault path).
  • Percent-encode non-ASCII in URL paths and in the MOVE Destination header (e.g. r%C3%A9sum%C3%A9.md). Destination rejects absolute (/…) paths.
  • Target a sub-part of a note with Target-Type (heading|block|frontmatter)
    • Target headers on GET/PATCH/POST.

Example

User: "Rename 3-Resources/Draft.md to 3-Resources/Final.md in my vault."

Output:

scripts/olrapi.sh MOVE "/vault/3-Resources/Draft.md" \
  -H 'Destination: 3-Resources/Final.md'
# <<HTTP 204>>  — moved, history preserved, internal links updated

Tips

  • Regenerate the reference against the live plugin if it was updated: scripts/olrapi.sh GET /openapi.yaml. GET / shows the plugin version.
  • Prefer PUT over delete+recreate for whole-file overwrites — it is atomic and keeps the file's identity.
  • The API serves HTTPS on port 27124 (self-signed → curl -k) and HTTP on 27123.
Files (buildwithclaude)
  • references
    • api_reference.md 7.7 KB
      # Obsidian Local REST API — full endpoint reference
      
      Plugin: **Local REST API with MCP** (`obsidian-local-rest-api`), spec `openapi 3.2.0`, plugin v4.1.x.
      16 paths. This file is the authoritative surface to fall back on when an MCP tool is missing.
      
      ## Connection & auth
      
      - Transport: HTTPS on port **27124** (self-signed cert → `curl -k`). Plain HTTP is served on **27123**.
      - Auth: `Authorization: Bearer <API_KEY>` on every request (except `/` and `/obsidian-local-rest-api.crt`).
      - Credentials live in the connected MCP server env (`~/.claude.json` → obsidian server → `OBSIDIAN_API_KEY` / `OBSIDIAN_HOST` / `OBSIDIAN_PORT`). `scripts/olrapi.sh` resolves them automatically.
      - Servers template: `https://{host}:{port}`.
      
      ## Common headers (content-targeting)
      
      Many read/patch endpoints accept these headers to target a sub-part of a note instead of the whole file:
      
      - `Target-Type`: `heading` | `block` | `frontmatter`
      - `Target`: the section to target (heading text, block id, or frontmatter key). Required when `Target-Type` set.
      - `Target-Delimiter`: nested-heading delimiter, default `::` (e.g. `Heading 1::Subheading`).
      - `Target-Scope` (PATCH only): `content` | `marker` | `markerAndContent`.
      
      PATCH-specific control headers:
      
      - `Operation` (required): `append` | `prepend` | `replace`
      - `Create-Target-If-Missing`: `true` | `false`
      - `Reject-If-Content-Preexists`: `true` | `false`
      - `Trim-Target-Whitespace`: `true` | `false`
      
      Body content types: `text/markdown` for content; `application/json` for structured PATCH (e.g. frontmatter values); PUT accepts `*/*` (arbitrary file bytes) or `text/markdown`.
      
      ---
      
      ## Endpoints
      
      ### Server / meta
      | Method | Path | Purpose | Auth |
      |---|---|---|---|
      | GET | `/` | Server status + plugin/obsidian versions + cert info | no |
      | GET | `/openapi.yaml` | This OpenAPI spec | yes |
      | GET | `/obsidian-local-rest-api.crt` | The API's TLS certificate (for pinning/trust) | no |
      
      ### Active file (the note currently open in the Obsidian UI)
      | Method | Path | Purpose |
      |---|---|---|
      | GET | `/active/` | Read active file content (supports Target-* headers) |
      | PUT | `/active/` | Replace active file content (body `*/*` or `text/markdown`) |
      | POST | `/active/` | Append to active file (body `text/markdown`, supports targeting) |
      | PATCH | `/active/` | Partial update (Operation + Target-* required) |
      | DELETE | `/active/` | Delete the active file |
      
      ### Vault files
      | Method | Path | Purpose |
      |---|---|---|
      | GET | `/vault/` | List files in vault root |
      | GET | `/vault/{pathToDirectory}/` | List files in a directory (trailing slash) |
      | GET | `/vault/{filename}` | Read a file. Default returns raw markdown; send `Accept: application/vnd.olrapi.note+json` for NoteJson (content+frontmatter+tags+stat+path). Target-* headers supported. |
      | PUT | `/vault/{filename}` | **Create OR overwrite** a file (body `*/*` or `text/markdown`). The clean way to replace a whole note. |
      | POST | `/vault/{filename}` | Append to a new/existing file (body `text/markdown`) |
      | PATCH | `/vault/{filename}` | Partial update (Operation + Target-* required) — insert under a heading, set a frontmatter key, replace a block |
      | DELETE | `/vault/{filename}` | Delete a file |
      | **MOVE** | `/vault/{filename}` | **Rename/move** a file (see below). Custom HTTP method. |
      
      #### MOVE (rename / move) — the headline REST-only capability
      Custom HTTP method `MOVE` on `/vault/{filename}`. Preserves file history and updates internal links.
      
      - `Destination` header (**required**): new vault-relative path. `..` allowed if result stays in vault; absolute `/`-paths rejected. Trailing slash keeps source filename (e.g. `archive/` moves `notes/todo.md` → `archive/todo.md`). Percent-encode non-ASCII (`r%C3%A9sum%C3%A9.md`).
      - `Allow-Overwrite` header: `true` | `false` (default `false` → `409` if destination exists).
      - Responses: `204` success (with `Content-Location` header = new path), `400` bad/missing Destination or path escapes vault, `404` source not found, `409` destination exists.
      
      ```bash
      scripts/olrapi.sh MOVE "/vault/3-Resources/Investigate/Thread-Keeper/Old.md" \
        -H 'Destination: 3-Resources/Investigate/Thread-Keeper/New.md'
      ```
      
      ### Periodic notes (`{period}` = `daily` | `weekly` | `monthly` | `quarterly` | `yearly`)
      | Method | Path | Purpose |
      |---|---|---|
      | GET/PUT/POST/PATCH/DELETE | `/periodic/{period}/` | CRUD on the CURRENT periodic note for that period |
      | GET/PUT/POST/PATCH/DELETE | `/periodic/{period}/{year}/{month}/{day}/` | CRUD on the periodic note for a SPECIFIC date |
      
      Same body/targeting semantics as vault endpoints.
      
      ### Search
      | Method | Path | Purpose |
      |---|---|---|
      | POST | `/search/simple/` | Text search. Query params: `query` (required), `contextLength` (optional). |
      | POST | `/search/` | Advanced search. Body content type selects the engine: `application/vnd.olrapi.jsonlogic+json` (JsonLogic, supports `glob`/`regexp` ops over `frontmatter.*`, `tags`, `content`) — also Dataview DQL via its own content type. Returns matching filenames + results. |
      
      JsonLogic example (notes whose frontmatter.url matches):
      ```json
      { "or": [
        {"===": [{"var": "frontmatter.url"}, "https://x/"]},
        {"glob": [{"var": "frontmatter.url-glob"}, "https://x/*"]}
      ]}
      ```
      
      ### Tags
      | Method | Path | Purpose |
      |---|---|---|
      | GET | `/tags/` | List all tags in the vault with counts. |
      
      ### Commands (Obsidian command palette)
      | Method | Path | Purpose |
      |---|---|---|
      | GET | `/commands/` | List available command IDs + names. |
      | POST | `/commands/{commandId}/` | Execute a command by id (e.g. `editor:toggle-bold`, plugin commands). |
      
      ### Open in UI
      | Method | Path | Purpose |
      |---|---|---|
      | POST | `/open/{filename}` | Open a file in the Obsidian UI. Query `newLeaf=true` opens in a new pane. |
      
      ### MCP passthrough (usually ignore — we already have MCP tools)
      | Method | Path | Purpose |
      |---|---|---|
      | POST | `/mcp/` | JSON-RPC 2.0 to the plugin's own MCP server. |
      | GET | `/mcp/` | SSE stream for an MCP session (`Mcp-Session-Id` header). |
      
      ---
      
      ## MCP-tool ↔ REST coverage (when to fall back)
      
      Already covered by `mcp__obsidian__*` tools (prefer these):
      `list_files_in_vault` → GET /vault/ · `list_files_in_dir` → GET /vault/{dir}/ · `get_file_contents` → GET /vault/{file} · `batch_get_file_contents` → N× GET · `append_content` → POST /vault/{file} · `patch_content` → PATCH /vault/{file} · `delete_file` → DELETE /vault/{file} · `simple_search` → POST /search/simple/ · `complex_search` → POST /search/ · `get_periodic_note`/`get_recent_periodic_notes` → GET /periodic/... · `get_recent_changes` (plugin helper).
      
      **REST-only (no MCP tool → use `olrapi.sh`):**
      - **MOVE `/vault/{filename}`** — rename/move a note (history + link updates). No MCP equivalent.
      - **PUT `/vault/{filename}`** — atomic whole-file create/overwrite. MCP only appends/patches; overwriting via MCP needs delete+recreate. Use PUT instead.
      - **Active-file ops** (`/active/` GET/PUT/POST/PATCH/DELETE) — act on the note open in the UI.
      - **`/commands/` + POST `/commands/{id}/`** — run Obsidian commands.
      - **POST `/open/{filename}`** — open/focus a note in the UI.
      - **GET `/tags/`** — vault-wide tag list with counts.
      - **Periodic PUT/POST/PATCH/DELETE** and **specific-date periodic** endpoints.
      
      ## Response notes
      - NoteJson (with `Accept: application/vnd.olrapi.note+json`): `{ path, content, frontmatter, tags, stat:{ctime,mtime,size} }`.
      - Errors: `{ "errorCode": <int>, "message": <str> }` (schema `Error`).
      - Success without body: `204`. Bad targeting/headers: `400`. Missing file: `404`. Method not allowed on target: `405`. MOVE destination exists: `409`.
      
      ## Source of truth
      Regenerate against the live instance anytime: `scripts/olrapi.sh GET /openapi.yaml`. The plugin version and any `apiExtensions` show in `GET /`.
      
  • scripts
    • olrapi.sh 1.9 KB
      #!/usr/bin/env bash
      # olrapi.sh — authenticated wrapper around the Obsidian Local REST API.
      # Resolves host/port/API-key from the connected obsidian MCP server config
      # (~/.claude.json), so no secrets are hardcoded.
      #
      # Usage:
      #   olrapi.sh <METHOD> <path> [extra curl args...]
      #   olrapi.sh GET /tags/
      #   olrapi.sh GET /vault/Note.md
      #   olrapi.sh PUT /vault/New.md --data-binary @file.md -H 'Content-Type: text/markdown'
      #   olrapi.sh MOVE "/vault/old/Note.md" -H 'Destination: archive/Note.md'
      #
      # Prints the HTTP status to stderr and the body to stdout.
      set -euo pipefail
      
      CFG="${OLRAPI_CONFIG:-$HOME/.claude.json}"
      
      read -r HOST PORT KEY < <(python3 - "$CFG" <<'PY'
      import json, sys
      cfg = sys.argv[1]
      def walk(o):
          if isinstance(o, dict):
              if 'OBSIDIAN_API_KEY' in o:
                  yield o
              for v in o.values():
                  yield from walk(v)
          elif isinstance(o, list):
              for v in o:
                  yield from walk(v)
      try:
          d = json.load(open(cfg))
      except Exception:
          d = {}
      env = next(walk(d), {})
      host = (env.get('OBSIDIAN_HOST') or 'http://127.0.0.1').replace('https://','').replace('http://','')
      port = env.get('OBSIDIAN_PORT') or '27123'
      key  = env.get('OBSIDIAN_API_KEY') or ''
      # env vars override config (useful on other machines)
      import os
      host = os.environ.get('OBSIDIAN_HOST', host).replace('https://','').replace('http://','')
      port = os.environ.get('OBSIDIAN_PORT', port)
      key  = os.environ.get('OBSIDIAN_API_KEY', key)
      print(host, port, key)
      PY
      )
      
      if [[ -z "${KEY:-}" ]]; then
        echo "olrapi: could not resolve OBSIDIAN_API_KEY (checked $CFG and env)" >&2
        exit 2
      fi
      
      METHOD="$1"; PATH_="$2"; shift 2
      
      # 27124 is the TLS port for the Local REST API; -k because it uses a self-signed cert.
      BASE="https://${HOST}:27124"
      [[ "$PORT" == "27123" || "$PORT" == "27124" ]] || BASE="https://${HOST}:${PORT}"
      
      curl -sk -X "$METHOD" "${BASE}${PATH_}" \
        -H "Authorization: Bearer ${KEY}" \
        -w '\n<<HTTP %{http_code}>>\n' \
        "$@"
      
  • SKILL.md 4 KB
    ---
    name: obsidian-rest-api
    category: development-code
    license: MIT
    description: Call the Obsidian Local REST API directly (over HTTP) for vault operations the mcp__obsidian__* tools do NOT expose — move/rename a note, overwrite a whole file atomically (PUT), act on the currently-open active file, run an Obsidian command, open a note in the UI, list all tags, or do date-specific periodic-note CRUD. Prefer the mcp__obsidian__* tools for plain read/append/patch/delete/search; fall back to this skill only when the required method is missing from MCP.
    ---
    
    # Obsidian Local REST API
    
    The connected `obsidian` MCP server exposes only a subset of the Obsidian
    [Local REST API](https://coddingtonbear.github.io/obsidian-local-rest-api/)
    (plugin `obsidian-local-rest-api`). This skill provides the full API surface plus
    an authenticated request wrapper, so a missing MCP method is called over HTTP
    instead of being worked around with hacks (e.g. delete+recreate to rename a note).
    
    ## When to Use This Skill
    
    Use the `mcp__obsidian__*` tools first for read, append, patch, delete, and search.
    Fall back to this skill only for operations that have **no MCP tool**:
    
    - Move / rename a note (preserves history, updates internal links)
    - Overwrite a whole file atomically (PUT) instead of delete+recreate
    - Act on the currently-open "active" file in the Obsidian UI
    - Run an Obsidian command from the command palette
    - Open / focus a note in the UI
    - List all vault tags with counts
    - Create/update/delete date-specific periodic notes
    
    ## What This Skill Does
    
    1. Resolves the API host, port, and key from the connected obsidian MCP server
       config (`~/.claude.json`) or `OBSIDIAN_*` env vars — no hardcoded secrets.
    2. Handles the plugin's self-signed TLS certificate.
    3. Exposes every endpoint of the Local REST API (see `references/api_reference.md`),
       with the header enums (Operation, Target-Type, Target-Scope), the custom
       `MOVE` contract, and the search (JsonLogic/Dataview) formats.
    
    ## How to Use
    
    Call the wrapper `scripts/olrapi.sh <METHOD> <path> [curl args...]`:
    
    ```bash
    S=scripts/olrapi.sh   # adjust to the skill's install path
    
    # rename/move a note (the most common reason to reach for this skill)
    "$S" MOVE "/vault/Path/To/Old Name.md" -H 'Destination: Path/To/New Name.md'
    
    # move into a folder, keeping the filename (trailing slash on Destination)
    "$S" MOVE "/vault/Inbox/todo.md" -H 'Destination: Archive/'
    
    # atomically overwrite a whole note
    "$S" PUT "/vault/Path/Note.md" -H 'Content-Type: text/markdown' --data-binary @/tmp/body.md
    
    # read a note as structured JSON (frontmatter + tags + stat)
    "$S" GET "/vault/Path/Note.md" -H 'Accept: application/vnd.olrapi.note+json'
    
    # list tags, run a command, open a note in the UI
    "$S" GET /tags/
    "$S" POST "/commands/editor:toggle-bold/"
    "$S" POST "/open/Path/Note.md?newLeaf=true"
    ```
    
    The wrapper prints `<<HTTP nnn>>` after the body. Success: `200`/`204`.
    On `MOVE`, `409` means the destination exists — add `-H 'Allow-Overwrite: true'` to force.
    For non-trivial calls, load `references/api_reference.md`.
    
    ### Path & encoding rules
    
    - `{filename}` is vault-relative (no leading slash on the vault path).
    - Percent-encode non-ASCII in URL paths and in the `MOVE` `Destination` header
      (e.g. `r%C3%A9sum%C3%A9.md`). `Destination` rejects absolute (`/…`) paths.
    - Target a sub-part of a note with `Target-Type` (`heading`|`block`|`frontmatter`)
      + `Target` headers on GET/PATCH/POST.
    
    ## Example
    
    **User**: "Rename `3-Resources/Draft.md` to `3-Resources/Final.md` in my vault."
    
    **Output**:
    ```bash
    scripts/olrapi.sh MOVE "/vault/3-Resources/Draft.md" \
      -H 'Destination: 3-Resources/Final.md'
    # <<HTTP 204>>  — moved, history preserved, internal links updated
    ```
    
    ## Tips
    
    - Regenerate the reference against the live plugin if it was updated:
      `scripts/olrapi.sh GET /openapi.yaml`. `GET /` shows the plugin version.
    - Prefer `PUT` over delete+recreate for whole-file overwrites — it is atomic and
      keeps the file's identity.
    - The API serves HTTPS on port 27124 (self-signed → `curl -k`) and HTTP on 27123.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related