๐ŸงฉCustom Development

How to Connect OpenClaw to Obsidian

Intermediate1-2 hoursUpdated 2025-01-10

Connecting OpenClaw to your Obsidian vault transforms your personal knowledge base into an AI-accessible resource. OpenClaw can search your notes, answer questions using your knowledge, and even create new notes based on conversations. This guide covers read access, search, write-back, and bi-directional sync.

Why This Is Hard to Do Yourself

These are the common pitfalls that trip people up.

๐Ÿ“

Vault path and permissions

Obsidian vaults can be anywhere on the filesystem. OpenClaw needs read/write access without breaking Obsidian's file watching

๐Ÿ”

Markdown search and indexing

Simple grep won't work well. You need full-text search with frontmatter parsing, wikilink resolution, and tag support

โœ๏ธ

Write-back conflicts

If both OpenClaw and Obsidian modify files simultaneously, you can get merge conflicts or lost edits

๐Ÿ”—

Wikilink and backlink resolution

Obsidian's [[wikilink]] syntax and graph view require special handling to preserve connections

Step-by-Step Guide

Step 1

Configure Obsidian vault path

# Find your Obsidian vault path:
# On macOS: ~/Documents/ObsidianVault
# On Windows: C:\Users\YourName\Documents\ObsidianVault
# On Linux: ~/Documents/ObsidianVault

# Add to OpenClaw config:
cat > ~/.openclaw/config.json << 'EOF'
{
  "obsidian": {
    "enabled": true,
    "vault_path": "/Users/yourname/Documents/ObsidianVault",
    "watch_for_changes": true,
    "auto_index": true
  }
}
EOF

Warning: Ensure OpenClaw has read/write permissions to the vault directory. Use absolute paths, not relative paths or ~/ shortcuts in config files.

Step 2

Create OpenClaw Obsidian skill

# Create the skill:
cat > ~/.openclaw/skills/obsidian/skill.md << 'EOF'
---
name: obsidian
version: 1.0.0
description: Reads, searches, and writes to Obsidian vault
permissions:
  - filesystem:read
  - filesystem:write
triggers:
  - command: /obsidian
  - pattern: "search my notes|check my obsidian|what do I have about"
---

## Instructions

You have access to the user's Obsidian vault.

When asked to search notes:
1. Use full-text search across all .md files
2. Parse frontmatter for metadata
3. Follow [[wikilinks]] to related notes
4. Return relevant excerpts with file paths

When asked to create/update notes:
1. Use clean markdown formatting
2. Add appropriate frontmatter (tags, date, etc.)
3. Preserve existing wikilinks
4. Create new wikilinks where relevant
EOF
Step 3

Implement search across vault

// Create search helper:
// ~/.openclaw/skills/obsidian/scripts/search.js

import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';

export function searchVault(vaultPath, query) {
  const results = [];

  function searchDir(dir) {
    const files = fs.readdirSync(dir);

    for (const file of files) {
      const filePath = path.join(dir, file);
      const stat = fs.statSync(filePath);

      if (stat.isDirectory()) {
        searchDir(filePath);
      } else if (file.endsWith('.md')) {
        const content = fs.readFileSync(filePath, 'utf8');
        const { data: frontmatter, content: body } = matter(content);

        if (body.toLowerCase().includes(query.toLowerCase())) {
          results.push({
            path: filePath,
            title: frontmatter.title || file.replace('.md', ''),
            excerpt: getExcerpt(body, query),
            tags: frontmatter.tags || []
          });
        }
      }
    }
  }

  searchDir(vaultPath);
  return results;
}

function getExcerpt(content, query, contextLength = 150) {
  const index = content.toLowerCase().indexOf(query.toLowerCase());
  if (index === -1) return content.slice(0, contextLength);

  const start = Math.max(0, index - contextLength / 2);
  const end = Math.min(content.length, index + contextLength / 2);
  return '...' + content.slice(start, end) + '...';
}
Step 4

Configure write-back with conflict detection

// Create write helper:
// ~/.openclaw/skills/obsidian/scripts/write.js

import fs from 'fs';
import matter from 'gray-matter';

export function createOrUpdateNote(vaultPath, fileName, content, frontmatter = {}) {
  const filePath = `${vaultPath}/${fileName}`;

  // Check if file exists
  if (fs.existsSync(filePath)) {
    // Read existing content
    const existing = fs.readFileSync(filePath, 'utf8');
    const { data: existingFrontmatter } = matter(existing);

    // Merge frontmatter
    const mergedFrontmatter = {
      ...existingFrontmatter,
      ...frontmatter,
      updated: new Date().toISOString()
    };

    // Write with merged frontmatter
    const output = matter.stringify(content, mergedFrontmatter);
    fs.writeFileSync(filePath, output);

    return { created: false, path: filePath };
  } else {
    // Create new note
    frontmatter.created = new Date().toISOString();
    const output = matter.stringify(content, frontmatter);
    fs.writeFileSync(filePath, output);

    return { created: true, path: filePath };
  }
}

Warning: Writing to the vault while Obsidian is open can cause file conflicts. Always check modification times and consider implementing a lock file mechanism.

Step 5

Implement wikilink resolution

// Add wikilink support to search:
// ~/.openclaw/skills/obsidian/scripts/wikilinks.js

export function extractWikilinks(content) {
  const wikilinkRegex = /\[\[([^\]]+)\]\]/g;
  const links = [];
  let match;

  while ((match = wikilinkRegex.exec(content)) !== null) {
    const [fullMatch, linkText] = match;
    const [target, alias] = linkText.split('|');
    links.push({
      target: target.trim(),
      alias: alias?.trim() || target.trim(),
      raw: fullMatch
    });
  }

  return links;
}

export function resolveWikilink(vaultPath, wikilink) {
  // Wikilinks can be:
  // - [[Note Name]]
  // - [[Note Name|Alias]]
  // - [[Folder/Note Name]]

  const possiblePaths = [
    `${vaultPath}/${wikilink}.md`,
    `${vaultPath}/${wikilink}`,
  ];

  for (const path of possiblePaths) {
    if (fs.existsSync(path)) {
      return path;
    }
  }

  return null;
}
Step 6

Set up bi-directional sync

// Watch for changes in Obsidian:
// ~/.openclaw/skills/obsidian/scripts/watch.js

import fs from 'fs';
import path from 'path';

export function watchVault(vaultPath, onChange) {
  const watcher = fs.watch(vaultPath, { recursive: true }, (eventType, filename) => {
    if (!filename || !filename.endsWith('.md')) return;

    const filePath = path.join(vaultPath, filename);

    if (eventType === 'change') {
      onChange({
        type: 'modified',
        path: filePath,
        filename
      });
    } else if (eventType === 'rename') {
      // Could be create or delete
      const exists = fs.existsSync(filePath);
      onChange({
        type: exists ? 'created' : 'deleted',
        path: filePath,
        filename
      });
    }
  });

  return watcher;
}

// In skill.md, add instructions to re-index on change
Step 7

Test the integration

# Restart OpenClaw:
npm restart

# Test search:
/obsidian search "project ideas"

# Test note creation:
/obsidian create note "Meeting Notes 2025-02-01" with content "Discussion about X"

# Test wikilink resolution:
/obsidian show me notes linked to [[Project Alpha]]

# Verify in Obsidian:
# 1. Open Obsidian
# 2. Check if new notes appear
# 3. Verify frontmatter is preserved
# 4. Test graph view shows connections

Obsidian Integration That Respects Your Knowledge Graph

Obsidian's power comes from its interconnected notes. A naive integration breaks that. We build Obsidian integrations that preserve wikilinks, respect your structure, and feel native.

Get matched with a specialist who can help.

Sign Up for Expert Help โ†’

Frequently Asked Questions