Codex 7eb9b987ab feat(mcp): harden remote assets and job execution
Gate source-file downloads and verify registry paths and content hashes. Stream multipart uploads with bounded limits, supervise OpenCode jobs with explicit result markers and timeouts, and preserve managed endpoint security settings. Document the trusted-client boundary and add focused regression coverage.

Validation: PYTHONPATH=<staged-tree> python -m unittest discover -s tests (182 tests)
2026-07-09 21:44:48 +00:00

obsidian-wiki

Ask DeepWiki PRs Welcome X

obisidan-wiki

A knowledge mgmt system inspired by gist published by Andrej Karpathy about maintaining a personal knowledge base with LLMs : the "LLM Wiki" pattern.

Instead of asking an LLM the same questions over over (or doing RAG every time), you compile knowledge once into interconnected markdown files and keep them current. In this case Obsidian is the viewer and the LLM is the maintainer.

We took that and built a framework around it. The whole thing is a set of markdown skill files that any AI coding agent (Claude Code, Cursor, Windsurf, Pi, whatever you use) can read and execute. You point it at your Obsidian vault and tell it what to do.

Quick Start

pip install obsidian-wiki
obsidian-wiki setup --vault /path/to/your/vault

obsidian-wiki setup writes the config to ~/.obsidian-wiki/config and installs every wiki skill into all your AI agents (Claude Code, Cursor, Codex, Gemini, Hermes, Pi, and more). Skills are symlinked to the installed package, so pip install -U obsidian-wiki upgrades them everywhere — just re-run obsidian-wiki setup to pick up new skills. Then open a project in your agent and say "set up my wiki".

obsidian-wiki list              # list the bundled skills
obsidian-wiki info              # show install paths, version, and config
obsidian-wiki file-index rebuild # rebuild fast source-file lookup index
obsidian-wiki file-index search "FH52K 图纸" -n 10
obsidian-wiki business schema --json # business facts catalog used by wiki-query
obsidian-wiki setup --project . # also drop project-local skills + AGENTS.md into the current repo
obsidian-wiki setup --copy      # copy skill files instead of symlinking

For operational questions such as "2026 年 6 月接了多少单", ask your agent with wiki-query. The wiki-query skill decides whether the question needs structured business facts, then uses the business template catalog or guarded read-only SQL before falling back to normal wiki retrieval. For hybrid questions about customers, products, salespeople, projects, delivery risk, anomalies, rankings, or quality impact, it runs a normal wiki context track and a business facts track, then fuses narrative context with structured metrics.

When working from a cloned repo, scripts/obsidian-wiki-ops.py is the single repo-local operations entrypoint. It wraps common obsidian-wiki commands, Kingdee business sync/templates, the business sync timer, daily update, and the managed MCP service:

scripts/obsidian-wiki-ops.py help
scripts/obsidian-wiki-ops.py kingdee sync --incremental
scripts/obsidian-wiki-ops.py timer install --vault /path/to/vault
scripts/obsidian-wiki-ops.py mcp start

OBSIDIAN_VAULT_PATH is just any directory where you want your wiki documents to live — a new empty folder or an existing Obsidian vault. Omit --vault to be prompted (or set it later in ~/.obsidian-wiki/config).

Install via Skills CLI

npx skills add Ar9av/obsidian-wiki

This installs all wiki skills into your current agent (Claude Code, Cursor, Codex, etc.). Then open your agent and say "set up my wiki".

Browse the full skill list at skills.sh/ar9av/obsidian-wiki.

Install via git clone

git clone https://github.com/Ar9av/obsidian-wiki.git
cd obsidian-wiki
bash setup.sh

setup.sh asks for your vault path, writes the config to ~/.obsidian-wiki/config, symlinks skills into all your agents, and installs wiki-update globally so you can use it from any project.

OBSIDIAN_VAULT_PATH is just any directory where you want your wiki documents to live. It can be a new empty folder or an existing Obsidian vault. Obsidian will read from it directly.

Open the project in your agent and say "set up my wiki". That's it.

Agent Compatibility

Works with any AI coding agent that can read files — Claude Code, Cursor, Windsurf, Pi, Codex, Gemini CLI, Kiro, and more. setup.sh handles skill discovery for each one automatically.

Supported agents and manual setup instructions
Agent Bootstrap Skills Directory Slash Commands
Claude Code CLAUDE.md .claude/skills/ + ~/.claude/skills/ ✅ /wiki-ingest, /wiki-status, etc.
Cursor .cursor/rules/obsidian-wiki.mdc .cursor/skills/ ✅ /wiki-ingest, /wiki-status, etc.
Windsurf .windsurf/rules/obsidian-wiki.md .windsurf/skills/ ✅ via Cascade
Codex (OpenAI) AGENTS.md ~/.codex/skills/ $wiki-ingest (Codex uses $)
Gemini CLI GEMINI.md ~/.gemini/skills/ ✅ /wiki-ingest, /wiki-query, etc.
Google Antigravity .agent/rules/ + .agent/workflows/ .agents/skills/ ✅ via workflows registry
Kiro IDE/CLI .kiro/steering/obsidian-wiki.md .kiro/skills/ + ~/.kiro/skills/ ✅ /wiki-ingest, /wiki-status, etc.
Hermes .hermes.md ~/.hermes/skills/ ✅ /wiki-history-ingest hermes, etc.
OpenClaw AGENTS.md ~/.openclaw/skills/ + ~/.agents/skills/ ✅ /wiki-ingest, /wiki-history-ingest openclaw, etc.
OpenCode AGENTS.md ~/.agents/skills/ ✅ /wiki-ingest, /wiki-query, etc.
Aider AGENTS.md ~/.agents/skills/ Describe intent in chat
Factory Droid AGENTS.md ~/.agents/skills/ ✅ /wiki-ingest, /wiki-query, etc.
Trae / Trae CN AGENTS.md ~/.trae/skills/ / ~/.trae-cn/skills/ ✅ via Agent tool
GitHub Copilot (VS Code) .github/copilot-instructions.md — Describe intent in chat
GitHub Copilot (CLI) — ~/.copilot/skills/ ✅ /wiki-ingest, /wiki-query, etc.
Kilocode AGENTS.md / CLAUDE.md .agents/skills/ + .claude/skills/ ✅ /wiki-ingest, /wiki-status, etc.
Pi AGENTS.md .pi/skills/ + ~/.pi/agent/skills/ ✅ /wiki-ingest, /wiki-history-ingest pi, etc.

Each agent has its own convention for discovering skills. setup.sh symlinks the canonical .skills/ directory into each agent's expected location. You write skills once, every agent can use them.

Manual setup (if you prefer setup.sh)

Claude Code

Skills are auto-discovered from .claude/skills/. Either run setup.sh or copy .skills/* to .claude/skills/. The CLAUDE.md file at the repo root is automatically loaded as project context.

cd /path/to/obsidian-wiki && claude "set up my wiki"
Cursor

Skills are auto-discovered from .cursor/skills/. The .cursor/rules/obsidian-wiki.mdc file provides always-on context. Either run setup.sh or copy .skills/* to .cursor/skills/. Then type /wiki-setup in the chat.

Windsurf

Cascade reads rules from .windsurf/rules/ and skills from .windsurf/skills/. Either run setup.sh or copy .skills/* to .windsurf/skills/. Then tell Cascade: "set up my wiki".

Codex

Reads AGENTS.md for project context. setup.sh installs skills globally to ~/.codex/skills/. Either run setup.sh or manually symlink .skills/* to ~/.codex/skills/.

cd /path/to/obsidian-wiki && codex "set up my wiki"
Gemini CLI

Reads GEMINI.md and discovers global skills from ~/.gemini/skills/. Either run setup.sh or manually symlink .skills/* to ~/.gemini/skills/.

cd /path/to/obsidian-wiki && gemini "set up my wiki"
Google Antigravity

Always-on via .agent/rules/ + .agent/workflows/. setup.sh ships both files and symlinks skills into .agents/skills/. The legacy ~/.gemini/antigravity/skills/ path is also wired.

Kiro IDE/CLI

Always-on via .kiro/steering/*.md with inclusion: always. setup.sh symlinks .skills/* into both .kiro/skills/ and ~/.kiro/skills/. Invoke with /wiki-ingest, /wiki-query, etc.

OpenCode / Aider / Factory Droid / Trae

All read AGENTS.md at the repo root. setup.sh symlinks skills into ~/.agents/skills/ (shared discovery path). Trae also gets ~/.trae/skills/ and ~/.trae-cn/skills/.

Hermes

Reads .hermes.md first, then falls back to AGENTS.md. Skills discovered from ~/.hermes/skills/. Run setup.sh or manually symlink .skills/* there.

cd /path/to/obsidian-wiki && hermes "set up my wiki"
# Mine Hermes history into the wiki:
/wiki-history-ingest hermes
OpenClaw

Reads AGENTS.md (priority 10). Discovers skills from ~/.openclaw/skills/ and ~/.agents/skills/. Skills auto-register as slash commands.

cd /path/to/obsidian-wiki && openclaw "set up my wiki"
# Mine OpenClaw history:
/wiki-history-ingest openclaw
GitHub Copilot

VS Code Chat: reads .github/copilot-instructions.md. Say "set up my wiki" in Copilot Chat.

CLI: discovers skills from ~/.copilot/skills/. Run setup.sh or manually symlink .skills/* there.

Pi

Reads AGENTS.md (walking up from cwd). Discovers skills from .pi/skills/, .agents/skills/, and ~/.pi/agent/skills/. Run setup.sh or manually symlink .skills/* to ~/.pi/agent/skills/.

cd /path/to/obsidian-wiki && pi "set up my wiki"
# Mine Pi session history:
/wiki-history-ingest pi

How it works

Every ingest runs through four stages:

1. Ingest — The agent reads your source material directly. It handles whatever you throw at it: markdown files, PDFs (with page ranges), JSONL conversation exports, plain text logs, chat exports, meeting transcripts, and images (screenshots, whiteboard photos, diagrams — vision-capable model required). No preprocessing step, no pipeline to run. The agent reads the file the same way it reads code.

2. Pull Information — From the raw source, the agent pulls out concepts, entities, claims, relationships, and open questions. A conversation about debugging a React hook yields a "stale closure" pattern. A research paper yields the key idea and its caveats. A work log yields decisions and their rationale. Noise gets dropped, signal gets kept. Each page also gets a 1–2 sentence summary: in its frontmatter at write time — later queries use this to preview pages without opening them.

3. Merge — New knowledge gets merged against what's already in the wiki. If a concept page exists, the agent updates it — merging new information, noting contradictions, strengthening cross-references. If it's genuinely new, a page gets created. Nothing is duplicated. Sources are tracked in frontmatter so every claim stays attributable.

4. Schema — The wiki schema isn't fixed upfront. It emerges from your sources and evolves as you add more. The agent maintains coherence: categories stay consistent, wikilinks point to real pages, the index reflects what's actually there. When you add a new domain (a new project, a new field of study), the schema expands to accommodate it without breaking what exists.

A .manifest.json tracks every source that's been ingested — path, timestamps, which wiki pages it produced. On the next ingest, the agent computes the delta and only processes what's new or changed.

Visualization

Through Global Graph View visualize every note and link within your entire vault.

  • Ribbon Icon: Click the "Open graph view" icon (looks like a connected network) on the left-side ribbon.
  • Command Palette: Press Ctrl + P (Windows/Linux) or Cmd + P (Mac), type "Open graph view", and press Enter.
obsidian-wiki

Color-coding the graph

Say "color my graph", "color code by tag", "color by category", or "highlight visibility in graph" and the graph-colorize skill rewrites <vault>/.obsidian/graph.json so Obsidian tints nodes by tag, folder, or visibility. It scans your actual vocabulary, picks a colorblind-friendly palette, backs up the existing graph.json first, and only touches the colorGroups field — your zoom, physics, and filter preferences stay intact. Reload Obsidian (Cmd/Ctrl+R) to see the change.

Modes: by-tag (default — top 10 tags), by-category (the seven vault folders), by-visibility (highlight visibility/pii and visibility/internal), combined (visibility + tags), or custom (user-supplied mapping).

What we added on top of Karpathy's pattern

  • Delta tracking. A manifest tracks every source file that's been ingested: path, timestamps, which wiki pages it produced. When you come back later, it computes the delta and only processes what's new or changed. You're not re-ingesting your entire document library every time.

  • Project-based organization. Knowledge gets filed under projects when it's project-specific, globally when it's not. Both are cross-referenced with wikilinks. If you're working on 10 different codebases, each one gets its own space in the vault.

  • Archive and rebuild. When the wiki drifts too far from your sources, you can archive the whole thing (timestamped snapshot, nothing lost) and rebuild from scratch. Or restore any previous archive.

  • Multi-agent ingest. Documents, PDFs, Claude Code history (~/.claude), Codex sessions (~/.codex/), Hermes memories and sessions (~/.hermes/), OpenClaw MEMORY.md and sessions (~/.openclaw/), Pi sessions (~/.pi/agent/sessions/), Windsurf data (~/.windsurf), ChatGPT exports, Slack logs, meeting transcripts, raw text. There are dedicated skills for Claude, Codex, Hermes, OpenClaw, and Pi history, plus a catch-all ingest skill for arbitrary text exports.

  • Cross-agent targeted search. /wiki-claude, /wiki-codex, /wiki-hermes, /wiki-openclaw, /wiki-copilot, /wiki-pi — query-driven ingest from a specific agent's raw history. Say /wiki-codex "rust ownership" while in Claude Code and it finds your Codex sessions about that topic, extracts the relevant conversation blobs, distills them into wiki pages, and returns a synthesized answer you can use immediately. Different from bulk ingest: this is topic-first, not session-first. Each agent has its own extraction strategy (Codex rollout events, Claude JSONL turns, OpenClaw's pre-synthesized MEMORY.md, Pi tree-structured JSONL sessions, etc.). Pair with /memory-bridge diff to see what each tool uniquely contributed to a topic.

  • Audit and lint. Find orphaned pages, broken wikilinks, stale content, contradictions, missing frontmatter. See a dashboard of what's been ingested vs what's pending.

  • Automated cross-linking. After ingesting new pages, the cross-linker scans the vault for unlinked mentions and weaves them into the knowledge graph with [[wikilinks]]. No more orphan pages.

  • Tag taxonomy. A controlled vocabulary of canonical tags stored in _meta/taxonomy.md, with a skill that audits and normalizes tags across your entire vault.

  • Provenance tracking. Every claim on a wiki page is tagged: extracted (default), ^[inferred] (LLM synthesis), or ^[ambiguous] (sources disagree). A provenance: block in the frontmatter summarizes the mix per page, and wiki-lint flags pages that drift into mostly speculation. You can always tell what your wiki actually knows from what it guessed.

  • Source archive and catalog. When WIKI_ARCHIVE_SOURCES=true, file-based ingest copies original sources into _sources/archive/ inside the vault and records them in _meta/source-registry.jsonl. When WIKI_SOURCE_CATALOG=true, it also maintains _sources/catalog/ pages organized by document type, product line, customer, competitor, standard, and market. Page citations can then point at vault-relative archived files instead of machine-local paths, while catalog pages provide clean business navigation without duplicating files.

  • SQLite file asset lookup. _meta/file-assets.sqlite indexes archived and staged source files for low-latency file search. Use obsidian-wiki file-index search "part/report/customer" when the answer should be an original drawing, 3D/CAD file, spreadsheet, PDF, or test report path instead of a synthesized wiki answer. The database lives inside the vault and can be rebuilt from _meta/source-registry.jsonl; rebuilds preserve structured registry fields such as part numbers, series, pitch, pin count, product lines, customers, standards, related pages, and catalog paths.

  • Multimodal sources. Screenshots, whiteboard photos, slide captures, and diagrams ingest the same way as text — the agent transcribes any visible text verbatim and tags interpreted content as inferred. Requires a vision-capable model.

  • Wiki insights. Beyond delta tracking, wiki-status can analyze the shape of your vault itself: top hubs, bridge pages (nodes whose removal would partition the graph), tag cluster cohesion scores, scored surprising connections, a graph delta since last run, and suggested questions the wiki structure is uniquely positioned to answer. Output goes to _insights.md.

  • Graph export. wiki-export turns the vault's wikilink graph into graph.json (queryable), graph.graphml (Gephi/yEd), cypher.txt (Neo4j), and a self-contained graph.html interactive browser visualization — no server required.

  • Tiered retrieval. wiki-query reads titles, tags, and page summaries first and only opens page bodies when the cheap pass can't answer. Say "quick answer" or "just scan" to force index-only mode. Keeps query cost roughly flat as your vault grows from 20 pages to 2000.

  • QMD semantic search (optional). QMD indexes your wiki and source documents for semantic search. When QMD_WIKI_COLLECTION is set in .env, wiki-query runs a lex+vec pass against the collection before falling back to Grep — enabling concept-level matches that exact-string search misses. When QMD_PAPERS_COLLECTION is set, wiki-ingest queries your indexed sources before writing a new page, surfacing related work, detecting contradictions, and deciding whether to create or merge. QMD can be used through MCP or the local CLI. Without QMD, both skills fall back to Grep/Glob and remain fully functional.

  • _raw/ staging directory. Drop rough notes, clipboard pastes, or quick captures into _raw/ inside your vault. The next wiki-ingest run promotes them to proper wiki pages and removes the originals. Configured via OBSIDIAN_RAW_DIR in .env (defaults to _raw).

By default, wiki-ingest and wiki-query use Grep/Glob for search — fully functional, no extra setup. If your vault grows large or you want concept-level matches across your sources, you can plug in QMD, either through MCP or by letting the agent call the local qmd CLI.

Setup:

  1. Install QMD. If you want MCP mode, also add it to your MCP config (see the QMD repo for instructions).
  2. Index your wiki and/or source documents:
    qmd index --name wiki /path/to/your/vault
    qmd index --name papers /path/to/your/sources
    
  3. Set the collection names and transport in your .env:
    QMD_WIKI_COLLECTION=wiki       # used by wiki-query
    QMD_PAPERS_COLLECTION=papers   # used by wiki-ingest (source discovery)
    QMD_TRANSPORT=mcp              # mcp | cli
    QMD_CLI_SEARCH_MODE=quality    # quality | balanced | fast
    

QMD_TRANSPORT=mcp preserves the original behavior and uses an agent-configured QMD MCP server. QMD_TRANSPORT=cli runs the local qmd command directly. CLI mode defaults to quality, which uses qmd query with reranking for the best relevance. If that is too slow on CPU, set QMD_CLI_SEARCH_MODE=balanced to use qmd query --no-rerank, or fast for a lighter semantic pass.

What changes with QMD enabled:

  • wiki-query runs a semantic pass (lex+vec) against your wiki collection before falling back to Grep. Finds conceptually related pages even when the exact terms don't match.
  • wiki-ingest queries your papers collection before writing a new page — surfaces related sources, spots contradictions, and decides whether to create a new page or merge into an existing one.

Both skills degrade gracefully: if QMD_WIKI_COLLECTION / QMD_PAPERS_COLLECTION are not set, they skip the QMD step silently and use Grep instead.

Optional: File Asset Lookup

When source archiving is enabled, the vault can also keep a SQLite locator for original files:

obsidian-wiki file-index init
obsidian-wiki file-index rebuild
obsidian-wiki file-index search "Mini FAKRA 高频测试" --doc-type test_report -n 10
obsidian-wiki file-index search "X4104 STEP" --source-type cad -n 10

The database is stored at <vault>/_meta/file-assets.sqlite. It is rebuilt from <vault>/_meta/source-registry.jsonl, so deleting the database is safe as long as the registry and _sources/archive/ remain. Registry metadata is treated as durable index input: bjd_part_numbers, competitor_part_numbers, series, pitch, pin_count, product_lines, customers, competitors, standards, business_categories, related_pages, and catalog_paths are preserved and merged with automatic extraction on every rebuild. To queue a new file for later ingest, copy it into the vault inbox and add a staged index row:

obsidian-wiki file-index stage ./drawing.step --project bjd --doc-type drawing

Staging is for upload/intake only. The normal ingest flow should still classify the file, archive it under _sources/archive/, update source registry/catalog pages, and rebuild the file index.

Optional: Business Fact Queries

Operational counts and amounts belong in a structured fact database, not in markdown pages. The business CLI stores Kingdee sales orders in <vault>/_meta/business-facts.sqlite. Normal users should ask through wiki-query; the skill decides when to use structured business facts, prefers templates, and only falls back to guarded read-only SQL when no template fits. For mixed business-and-context questions, wiki-query runs a hybrid path: one track gathers wiki context and citations, another gathers read-only business metrics, and the final answer keeps the two sources clearly separated.

# Sync from an exported or API-fetched Kingdee JSON payload.
scripts/obsidian-wiki-ops.py kingdee sync --input ./kingdee-orders.json

# Or create the private local config for the real Kingdee API/token flow.
scripts/obsidian-wiki-ops.py kingdee config
vim .runtime/kingdee.env
scripts/obsidian-wiki-ops.py kingdee auth-test

# Then fetch reviewed June orders from Kingdee.
scripts/obsidian-wiki-ops.py kingdee sync \
  --start-bizdate 2026-06-01 \
  --end-bizdate 2026-06-30 \
  --billstatus C

# Or fetch incrementally by Kingdee modify time.
scripts/obsidian-wiki-ops.py kingdee sync --incremental

# Inspect the schema/template catalog used by wiki-query.
scripts/obsidian-wiki-ops.py kingdee schema --json

# Run explicit templates for debugging or automation.
scripts/obsidian-wiki-ops.py kingdee template order_count --vault /path/to/vault --year 2026 --month 6 --json
scripts/obsidian-wiki-ops.py kingdee template sales_amount --vault /path/to/vault --year 2026 --month 6 --json
scripts/obsidian-wiki-ops.py kingdee template top_customers --vault /path/to/vault --year 2026 --month 6 --limit 10 --json
scripts/obsidian-wiki-ops.py kingdee template staff_order_list --vault /path/to/vault --start-date 2026-06-30 --end-date 2026-07-07 --staff 徐丽 --json

# Run guarded read-only SQL only when templates do not fit.
scripts/obsidian-wiki-ops.py kingdee sql "SELECT COUNT(*) AS order_count FROM orders"

Templates cover order counts, sales amounts, customer/product counts, quantity, averages, trends, rankings, breakdowns, order lists, customer/product/staff summaries, status breakdowns, and delivery-plan queries. Salesperson templates default to the main Kingdee salesperson and can include secondary/follow-up roles with --staff-role-scope all. If no template fits, business sql allows guarded read-only SQL against structured tables only; raw JSON and write statements are blocked. Detailed order rows stay in SQLite; the wiki remains for business context, customer/project knowledge, and cross-linked notes. Real Kingdee credentials live in .runtime/kingdee.env, and fetched tokens are cached in .runtime/kingdee-token.json; both files are local-only and ignored by git.

For daily unattended sync on Linux:

scripts/obsidian-wiki-ops.py timer install --vault /path/to/vault
scripts/obsidian-wiki-ops.py timer run-now --vault /path/to/vault
scripts/obsidian-wiki-ops.py timer status

The timer runs as a user-level systemd timer, defaults to 07:10 daily, and uses --incremental with a 48-hour lookback window.

Optional: Source File MCP Server

Agents that support MCP can ask for original engineering files through a local stdio server:

pip install "obsidian-wiki[mcp]"
obsidian-wiki file-index rebuild
obsidian-wiki-mcp

Configure your agent to run obsidian-wiki-mcp as a stdio MCP server. The server exposes:

  • search_file_assets — finds safe source files by product code, series, customer, doc type, or file type.
  • list_file_assets — browses safe source files with optional filters.
  • get_file_asset_metadata — returns one file's safe metadata and asset://source/sha256/... URI.
  • file_index_status — shows vault/index/profile status.
  • get_mcp_capabilities — tells remote agents which tool groups are enabled and which tool to use first.
  • asset://source/sha256/{digest} — binary resource for an allowed archived file.

By default the MCP layer is stricter than local CLI lookup: it only returns metadata for engineering files such as drawings, 3D/STEP models, specifications, product parameters, and test reports. Customer-shareable engineering deliverables, such as PDF customer drawings, are eligible for lookup when the active profile already allows technical engineering data. It blocks raw orders, prices, email correspondence, complaint evidence, claims, personal data, and files missing from _meta/source-registry.jsonl. Downloading the underlying asset://source/... bytes is a separate opt-in and defaults off for new managed installs; set OBSIDIAN_MCP_ALLOW_SOURCE_FILES=true (or pass --allow-source-files) only for trusted clients. Use OBSIDIAN_MCP_BOT_PROFILE, OBSIDIAN_MCP_MAX_FILE_BYTES, and OBSIDIAN_MCP_EXPOSE_PATHS to tune the remaining runtime behavior.

Optional: run the same MCP server over Streamable HTTP for a remote MCP client:

export OBSIDIAN_MCP_TOKEN='<long-random-token>'
obsidian-wiki-mcp --transport streamable-http --host 127.0.0.1 --port 8765 --path /mcp

Remote HTTP requests must include:

Authorization: Bearer <long-random-token>

Client configuration example:

{
  "mcpServers": {
    "wiki-file-assets": {
      "type": "streamable-http",
      "url": "http://127.0.0.1:8765/mcp",
      "headers": {
        "Authorization": "Bearer <long-random-token>"
      }
    }
  }
}

For long-running access from another machine, use the managed user service:

scripts/obsidian-wiki-ops.py mcp start

scripts/obsidian-wiki-ops.py mcp start creates .runtime/obsidian-wiki-mcp.env with 0600 permissions if it does not exist, writes ~/.config/systemd/user/obsidian-wiki-mcp.service, starts the service, and prints the remote URL, Authorization header, enabled tool groups, config file path, and client JSON to copy into another agent. The managed endpoint values (bind host/port/path, public URLs, and transport allowlists) live in that env file and are preserved across later starts and token rotations. The user service restarts on failure and sets MemoryMax=2G. When ingest is enabled, it also prints the /mcp-ingest/uploads file upload endpoint. It serves the configured OBSIDIAN_VAULT_PATH by default; pass --vault /path/to/vault to override. The service defaults to 0.0.0.0:8765/mcp because it is intended for remote clients, but it still requires the bearer token. Managed installs derive OBSIDIAN_MCP_ALLOWED_HOSTS and OBSIDIAN_MCP_ALLOWED_ORIGINS from their bind/public URLs; review them when using custom DNS, a tunnel, or a reverse proxy. Prefer SSH tunnel, Tailscale, WireGuard, or another trusted network boundary instead of directly exposing the port to the public internet.

Customize the first startup if needed:

scripts/obsidian-wiki-ops.py mcp start --vault /path/to/vault --host 0.0.0.0 --port 9876 --path /wiki-mcp

Tool groups are controlled by the same config file:

OBSIDIAN_MCP_ENABLED_TOOLS=file-assets

Edit that line directly, or use the service command:

scripts/obsidian-wiki-ops.py mcp tools list
scripts/obsidian-wiki-ops.py mcp tools enable ingest
scripts/obsidian-wiki-ops.py mcp tools disable ingest
scripts/obsidian-wiki-ops.py mcp tools enable query
scripts/obsidian-wiki-ops.py mcp tools disable query

To let a remote MCP client upload files and ask this machine's local OpenCode to run the normal wiki-ingest workflow, enable the ingest tool group and restart/start the same service:

scripts/obsidian-wiki-ops.py mcp start --enable-ingest

After ingest is enabled once, later scripts/obsidian-wiki-ops.py mcp start preserves the config file setting. Use scripts/obsidian-wiki-ops.py mcp start --disable-ingest or scripts/obsidian-wiki-ops.py mcp tools disable ingest to turn it off.

This adds write-capable MCP tools to the same wiki-file-assets server and bearer token. The server is tool-first: some MCP clients may show empty resources or prompts lists until a concrete asset://source/sha256/... URI is returned by get_file_asset_metadata, so use the tool list as the source of truth.

  • get_ingest_upload_instructions — returns the preferred upload methods, HTTP endpoint path, fields, limits, and curl example for remote agents.
  • ingest_text_note — accepts a short thought/note as text, wraps it as a temporary markdown upload, and optionally starts the normal fixed /wiki-ingest job.
  • create_ingest_upload_ticket — returns a short-lived, single-use upload token plus the upload URL for remote-agent HTTP uploads.
  • validate_upload — checks planned file names, sizes, sha256 values, count limits, and exact duplicate sha matches before upload.
  • preview_upload_metadata — previews completed uploads, inferred metadata, and exact duplicate warnings before starting ingest.
  • check_ingest_opencode_readiness — verifies the local OpenCode binary, required run flags, project ingest skills, and optionally runs a no-write smoke test that confirms wiki-ingest can be loaded.
  • start_ingest_job — queues a local opencode run --auto --format json job with a fixed /wiki-ingest prompt for the completed uploads.
  • get_ingest_job_status, get_ingest_job_result, cancel_ingest_job — inspect or cancel the local job.

The managed service config sets the OpenCode model server-side. By default ingest uses OBSIDIAN_MCP_INGEST_OPENCODE_MODEL=openai/gpt-5.5, OBSIDIAN_MCP_INGEST_OPENCODE_VARIANT=high, and an OBSIDIAN_MCP_INGEST_JOB_TIMEOUT_SECONDS=1800 deadline. It also writes OBSIDIAN_MCP_PUBLIC_BASE_URL from the detected remote address so ticket responses can include a complete upload URL; set it manually when the service is behind a tunnel, reverse proxy, or custom DNS name. If the upload endpoint is exposed through a different public URL than /mcp, set OBSIDIAN_MCP_INGEST_PUBLIC_UPLOAD_URL explicitly and it will override the derived upload URL in capabilities, upload instructions, tickets, and show-config. Change these values in .runtime/obsidian-wiki-mcp.env on the server if needed; remote MCP clients do not choose the model per call.

For short text thoughts, notes, findings, or pasted context, call ingest_text_note(text, title?, project?, doc_type_hint?, note?, start_job?). It uses doc_type_hint="thought" by default and returns the created upload plus the queued job when start_job=true.

For binary files and larger source documents, remote agents should use upload tickets by default: call create_ingest_upload_ticket(start_job=false), upload with the returned upload_url and X-Obsidian-Wiki-Upload-Token, call preview_upload_metadata(upload_ids), then call start_ingest_job(upload_ids) when the preview looks correct:

curl -H "X-Obsidian-Wiki-Upload-Token: <upload-token>" \
  -F "files=@drawing.pdf" \
  -F "project=bjd" \
  -F "doc_type_hint=drawing" \
  -F "start_job=false" \
  <upload_url>

The endpoint accepts repeated files fields plus optional project, doc_type_hint, note, and start_job fields. Multipart files are parsed as streams into spooled storage instead of buffering the whole request in memory, and OBSIDIAN_MCP_INGEST_MAX_FILE_BYTES is enforced while each file part is being parsed. OBSIDIAN_MCP_INGEST_MAX_FILES_PER_JOB limits the count. With OBSIDIAN_MCP_INGEST_MAX_REQUEST_BYTES=0, the route derives its total request-body cap from per-file size times maximum file count plus multipart overhead; a positive value adds a stricter total cap. The stricter upload-ticket limits still apply. Ticket-bound project, doc_type_hint, and note values cannot be replaced by form data, and a ticket created with start_job=false cannot be escalated to start a job by sending start_job=true in the form. Oversized requests return HTTP 413. The endpoint still accepts the same bearer token as /mcp for local/admin compatibility, but remote agents should prefer single-use upload tickets. Upload tickets are consumed by authorized POST attempts, including malformed multipart or oversized uploads, but not by accidental GET/wrong-method requests. When an upload-ticket POST fails after the ticket is consumed, the JSON error includes ticket_consumed: true and retry_action: "create_new_upload_ticket", so the remote agent should request a fresh ticket before retrying. The upload route enforces configured --allowed-host and --allowed-origin checks. Base64 file upload is intentionally not exposed as an MCP tool because large JSON arguments are fragile in many agent tool-call implementations. Duplicate detection is exact sha256 matching only; it does not replace human review for near-duplicate or renamed source files.

Run check_ingest_opencode_readiness before the first remote upload. Use run_smoke_test: true when you want OpenCode itself to prove it can load the project wiki-ingest skill; otherwise the static preflight is also run automatically before every start_ingest_job.

Uploads and text notes land under .runtime/mcp-ingest/uploads/, outside the Obsidian vault, and are treated only as temporary source staging. Job state and logs live under .runtime/mcp-ingest/jobs/. Remote upload and job responses expose upload/job ids, hashes, states, and bounded result text, but omit local runtime file paths; the internal job record still keeps paths so the fixed local OpenCode prompt can ingest only the uploaded files. Completion is accepted only from the final JSON text event containing MCP_INGEST_OK; MCP_INGEST_FAILED, a missing marker, a nonzero exit, timeout, or cancellation marks the job failed/cancelled. get_mcp_capabilities includes server_version, schema_version/capabilities_schema_version, tools_version, file-index freshness, ingest queue state, and known limitations so remote agents can detect stale client instructions. Its recommended_first_steps only names currently enabled tool groups. The MCP server refuses to enable ingest if OBSIDIAN_MCP_INGEST_RUNTIME_ROOT points inside the vault; let wiki-ingest decide how to archive sources into _sources/archive/, update registries, and rebuild the file index.

Ingest and query launch OpenCode with a fixed prompt and --auto, but the MCP service does not enforce a shell sandbox and prompt restrictions are not a security boundary. There is no direct arbitrary-prompt or arbitrary-shell MCP tool, yet a client able to influence vault/upload content must still be trusted. Keep these tool groups behind a trusted network boundary and rotate the token if it has been shared too broadly.

To let a remote MCP client ask synthesized questions against the compiled wiki, enable the query tool group:

scripts/obsidian-wiki-ops.py mcp start --enable-query

After query is enabled once, later scripts/obsidian-wiki-ops.py mcp start preserves it. Use scripts/obsidian-wiki-ops.py mcp start --disable-query or scripts/obsidian-wiki-ops.py mcp tools disable query to turn it off.

This adds read-oriented OpenCode-backed query tools to the same wiki-file-assets server and bearer token:

  • check_query_opencode_readiness — verifies the local OpenCode binary, required run flags, project wiki-query/llm-wiki skills, and optionally runs a no-write smoke test.
  • ask_wiki — preferred one-call query path for remote agents. It starts the local job, polls briefly, and returns either a structured answer or a job_id to continue polling.
  • start_wiki_query — queues a local opencode run --auto --format json job with a fixed /wiki-query prompt. Parameters: query, mode (normal or index_only), public_only (default false), and optional note.
  • get_wiki_query_status, get_wiki_query_result, cancel_wiki_query — inspect, retrieve, or cancel the local query job.

The managed service config sets query to OBSIDIAN_MCP_QUERY_OPENCODE_MODEL=openai/gpt-5.5, OBSIDIAN_MCP_QUERY_OPENCODE_VARIANT=minimal, and OBSIDIAN_MCP_QUERY_JOB_TIMEOUT_SECONDS=300 by default, prioritizing response speed over maximum reasoning depth. Tune the server env file if a different balance is needed.

public_only defaults to false, so the answer may use the same full vault visibility that a local operator sees. Set public_only=true when the remote answer must exclude visibility/internal and visibility/pii pages. Job state and logs live under .runtime/mcp-query/jobs/, outside the vault; remote query status/result responses expose bounded, path-redacted text and omit local log paths. The final JSON text event must contain MCP_QUERY_OK; MCP_QUERY_FAILED, a missing marker, a nonzero exit, timeout, or cancellation cannot be reported as a successful answer. Query results keep the legacy answer field and also include answer_markdown, citations, used_wiki, used_business_facts, data_freshness, warnings, and confidence so agents can parse the response without scraping raw logs. A normal wiki-query log entry is allowed. The generated prompt instructs the agent to avoid ingest/update/rebuild workflows, writes, sync/config/timer operations, and arbitrary shell execution; it only permits the obsidian-wiki business schema/template/sql read-only commands needed by the wiki-query business facts route, but these prompt restrictions do not replace the trusted-client boundary described above. Hybrid business + wiki questions may run a wiki context track and a business facts track, using parallel subagents only when the host supports them and otherwise running sequentially. When public_only=true, business facts remain disabled.

Clean old MCP runtime staging data with the default 30-day retention window:

scripts/obsidian-wiki-ops.py mcp cleanup
scripts/obsidian-wiki-ops.py mcp cleanup --max-age-days 14

Cleanup removes expired upload tickets and old completed/cancelled/failed jobs, logs, and upload staging directories that are not referenced by active jobs.

Rotate the token later with:

scripts/obsidian-wiki-ops.py mcp new-token

Useful service commands:

scripts/obsidian-wiki-ops.py mcp show-config
scripts/obsidian-wiki-ops.py mcp tools list
scripts/obsidian-wiki-ops.py mcp tools enable ingest
scripts/obsidian-wiki-ops.py mcp tools disable ingest
scripts/obsidian-wiki-ops.py mcp cleanup
scripts/obsidian-wiki-ops.py mcp stop
scripts/obsidian-wiki-ops.py mcp uninstall
scripts/obsidian-wiki-ops.py mcp status

_raw/ Staging Directory

_raw/ is a staging area inside your vault for unprocessed captures — rough notes, clipboard pastes, quick voice-memo transcripts. Drop files there and the next wiki-ingest run will promote them to proper wiki pages and remove the originals.

The fastest way to feed _raw/ during a live coding session is /wiki-quick-chat-capture — it scans the current conversation, extracts bugs and gotchas, and writes structured draft files in under 60 seconds with no subagents or manifest writes.

The directory is created automatically by wiki-setup. The path is configurable via OBSIDIAN_RAW_DIR in .env (defaults to _raw).


Skills

Everything lives in .skills/. Each skill is a markdown file the agent reads when triggered:

Skill What it does Slash Command
wiki-setup Initialize vault structure /wiki-setup
wiki-ingest Distill documents into wiki pages /wiki-ingest
wiki-history-ingest Unified history router (claude, codex, hermes, pi) `/wiki-history-ingest <claude
claude-history-ingest Mine your ~/.claude conversations and memories from Claude code and desktop /claude-history-ingest
codex-history-ingest Mine your ~/.codex sessions and rollout logs /codex-history-ingest
hermes-history-ingest Mine your ~/.hermes memories and sessions /hermes-history-ingest
openclaw-history-ingest Mine your ~/.openclaw MEMORY.md and sessions /openclaw-history-ingest
copilot-history-ingest Mine your ~/.copilot CLI session history /copilot-history-ingest
pi-history-ingest Mine your ~/.pi/agent/sessions JSONL history /pi-history-ingest
data-ingest Ingest any text — chat exports, logs, transcripts /data-ingest
ingest-url Fetch and ingest a URL directly into the wiki /ingest-url <url>
obsidian-wiki-ingest Project-scoped automation wrapper for wiki-ingest /obsidian-wiki-ingest
wiki-status Show what's ingested, what's pending, the delta /wiki-status
wiki-rebuild Archive, rebuild from scratch, or restore /wiki-rebuild
wiki-query Answer questions from the wiki /wiki-query
wiki-lint Find broken links, orphans, contradictions /wiki-lint
cross-linker Auto-discover and insert missing wikilinks /cross-linker
tag-taxonomy Enforce consistent tag vocabulary across pages /tag-taxonomy
llm-wiki The core pattern and architecture reference /llm-wiki
wiki-update Sync current project's knowledge into the vault /wiki-update
wiki-export Export vault graph to JSON, GraphML, Neo4j, HTML /wiki-export
wiki-capture Save the current conversation as a wiki note /wiki-capture
wiki-quick-chat-capture Zero-friction mid-session finding capture to _raw/ /wiki-quick-chat-capture
wiki-research Autonomous multi-round web research, self-filed /wiki-research [topic]
wiki-dashboard Create dynamic Obsidian Bases dashboard views /wiki-dashboard
wiki-synthesize Discover and fill synthesis gaps across concepts /wiki-synthesize
wiki-agent Query-driven ingest from a specific agent's history /wiki-claude [topic], /wiki-codex [topic], etc.
memory-bridge Browse and diff knowledge by which AI tool wrote it /memory-bridge
daily-update Daily maintenance cycle — freshness, index, hot cache /daily-update
impl-validator Validate an implementation against its stated goal /impl-validator
graph-colorize Color-code the Obsidian graph by tag/category/visibility /graph-colorize
skill-creator Create new skills /skill-creator

Note: Slash commands (/skill-name) work in Claude Code, Cursor, and Windsurf. In other agents, just describe what you want and the agent will find the right skill.

We handle the knowledge management workflow — ingest, query, lint, rebuild. For Obsidian format mastery, we recommend installing kepano/obsidian-skills alongside this framework. These are optional but improve the quality of wiki output:

Skill What it adds
obsidian-markdown Teaches the agent correct Obsidian-flavored syntax — wikilinks, callouts, embeds, properties
obsidian-bases Create and edit .base files (database-like views of notes)
json-canvas Create and edit .canvas files (visual mind maps, flowcharts)
obsidian-cli Interact with a running Obsidian instance via CLI (search, create, manage notes)
defuddle Extract clean markdown from web pages — less noise than raw fetch, saves tokens during ingest

Both projects use the same Agent Skills spec, so they coexist in the same .skills/ directory with no conflicts.

Install:

npx skills add kepano/obsidian-skills

After installing, your agent will automatically pick up the new skills alongside the existing wiki skills.

Project Structure

obsidian-wiki/
├── .skills/                          # ← Canonical skill definitions (source of truth)
│   ├── wiki-setup/SKILL.md
│   ├── wiki-ingest/SKILL.md
│   ├── wiki-history-ingest/SKILL.md
│   ├── claude-history-ingest/SKILL.md
│   ├── codex-history-ingest/SKILL.md
│   ├── hermes-history-ingest/SKILL.md
│   ├── openclaw-history-ingest/SKILL.md
│   ├── pi-history-ingest/SKILL.md
│   ├── data-ingest/SKILL.md
│   ├── wiki-status/SKILL.md
│   ├── wiki-rebuild/SKILL.md
│   ├── wiki-query/SKILL.md
│   ├── wiki-lint/SKILL.md
│   ├── cross-linker/SKILL.md
│   ├── tag-taxonomy/SKILL.md
│   ├── wiki-update/SKILL.md
│   ├── llm-wiki/SKILL.md
│   ├── wiki-export/SKILL.md
│   └── skill-creator/SKILL.md
│
├── CLAUDE.md                            # Bootstrap → Claude Code / Kilocode (→ AGENTS.md)
├── GEMINI.md                            # Bootstrap → Gemini CLI (→ AGENTS.md)
├── AGENTS.md                            # Bootstrap → Codex, OpenCode, Aider, Droid, Trae, Hermes, OpenClaw, Kilocode
├── .hermes.md                           # Bootstrap → Hermes (symlink → AGENTS.md)
├── .cursor/rules/obsidian-wiki.mdc      # Always-on → Cursor (alwaysApply: true)
├── .windsurf/rules/obsidian-wiki.md     # Always-on → Windsurf
├── .kiro/steering/obsidian-wiki.md      # Always-on → Kiro (inclusion: always)
├── .agent/rules/obsidian-wiki.md        # Always-on → Google Antigravity
├── .agent/workflows/obsidian-wiki.md    # Slash-command registry → Google Antigravity
├── .github/copilot-instructions.md      # Always-on → GitHub Copilot (VS Code Chat)
│
├── .claude/skills/   → symlinks to .skills/*  (created by setup.sh)
├── .cursor/skills/   → symlinks to .skills/*  (created by setup.sh)
├── .windsurf/skills/ → symlinks to .skills/*  (created by setup.sh)
├── .agents/skills/   → symlinks to .skills/*  (created by setup.sh)
├── .pi/skills/       → symlinks to .skills/*  (created by setup.sh)
├── .kiro/skills/     → symlinks to .skills/*  (created by setup.sh)
│
├── ~/.claude/skills/              → portable skills (wiki-update, wiki-query)
├── ~/.gemini/skills/              → global symlinks — Gemini CLI
├── ~/.gemini/antigravity/skills/  → global symlinks — Antigravity (legacy path)
├── ~/.codex/skills/               → global symlinks — Codex
├── ~/.hermes/skills/              → global symlinks — Hermes
├── ~/.openclaw/skills/            → global symlinks — OpenClaw (managed)
├── ~/.copilot/skills/             → global symlinks — GitHub Copilot CLI
├── ~/.trae/skills/                → global symlinks — Trae
├── ~/.trae-cn/skills/             → global symlinks — Trae CN
├── ~/.kiro/skills/                → global symlinks — Kiro CLI
├── ~/.pi/agent/skills/            → global symlinks — Pi
├── ~/.agents/skills/              → global symlinks — OpenCode, Aider, Droid, generic
│
├── setup.sh                          # One-command agent setup
├── .env.example                      # Configuration template
├── README.md                         # You are here
└── SETUP.md                          # Detailed setup guide

Using from other projects

The whole point is that your wiki should stay up to date as you work across different codebases. You don't want to come back to the obsidian-wiki repo every time. So setup.sh installs two global skills that work from any project: wiki-update and wiki-query.

When you run bash setup.sh, it does the following:

  1. Writes a config to ~/.obsidian-wiki/config with your vault path and the repo location. This is how the skills know where to read and write.
  2. Symlinks wiki-update and wiki-query into ~/.claude/skills/ so they're available everywhere in Claude Code.
  3. Symlinks all skills into every agent's global discovery path:
    • ~/.gemini/skills/ — Gemini CLI (canonical)
    • ~/.gemini/antigravity/skills/ — Google Antigravity (legacy)
    • ~/.codex/skills/ — Codex
    • ~/.hermes/skills/ — Hermes
    • ~/.openclaw/skills/ — OpenClaw (managed)
    • ~/.copilot/skills/ — GitHub Copilot CLI
    • ~/.trae/skills/ + ~/.trae-cn/skills/ — Trae / Trae CN
    • ~/.kiro/skills/ — Kiro CLI
    • ~/.pi/agent/skills/ — Pi
    • ~/.agents/skills/ — OpenCode, Aider, Factory Droid, and other AGENTS.md-aware agents

After that, you're in some project, say ~/projects/my-cool-app, working with Claude or Pi. Two commands:

# You're working on some project
cd ~/projects/my-cool-app
claude

# Write to the wiki: distill what you've learned
> /wiki-update

# Read from the wiki: pull context about anything you've captured before
> /wiki-query what do I know about rate limiting?

/wiki-update reads your project, figures out what's worth keeping, and distills it into your Obsidian vault. Architecture decisions, patterns you discovered, key concepts, trade-offs you evaluated. It doesn't copy code or dump file listings. It distills the stuff you'd forget in 3 months. Next time you run it from the same project, it checks what changed since last sync (via git log) and only processes the delta.

/wiki-query goes the other direction. You're working on something and you want to know what your wiki says about a topic. Maybe you solved a similar problem 2 months ago in a different project and the answer is already in your vault. The agent searches the wiki, reads the relevant pages, and gives you a synthesized answer with citations.

Both skills follow the same Karpathy pattern as everything else. If a concept page already exists in the vault, it merges into it. Everything gets cross-linked with [[wikilinks]], tracked in .manifest.json, and logged.

Contributing

This is early. The skills work but there's a lot of room to make them smarter — better cross-referencing, smarter deduplication, handling larger vaults, new ingest sources. If you've been thinking about this problem or have a workflow that could be a skill, PRs are welcome.

Adding a new skill

  1. Create a folder in .skills/your-skill-name/
  2. Add a SKILL.md with YAML frontmatter (name, description) and markdown instructions
  3. Run bash setup.sh to symlink into all agent directories
  4. Test with your agent by saying something that matches the description

See .skills/skill-creator/SKILL.md for the full guide on writing effective skills.

S
Description
Skill-based framework for building and maintaining an Obsidian knowledge base
Readme MIT
708 KiB
Languages
Python 92.8%
HTML 4.3%
Shell 2.9%