README file from
GithubGraph Context for Claude Code
Claude Code IDE integration for Obsidian — with your knowledge graph attached.
This plugin makes Obsidian show up in Claude Code's /ide picker (the same lock-file +
localhost WebSocket + MCP protocol that the VS Code and JetBrains extensions use), so Claude
Code automatically sees the note you're editing and the text you've selected.
On top of that base IDE integration, it adds the thing that makes Obsidian different from a
code editor: expanded graph context. When you select text, the plugin doesn't just send the
raw markdown with opaque [[links]] — it resolves and inlines the surrounding knowledge graph
and ships it in a single payload:
![[embeds]]→ the linked note's actual content, inlined[[wikilinks]]→ a short summary (frontmatter + first paragraph) of each target- the heading path of your cursor (e.g.
## Section > ### Subsection) - the note's frontmatter
- the note's backlinks
No extra MCP server, no multi-step tool calls — it arrives with the selection.
Why this is different
Every other Obsidian ↔ Claude Code integration (and every generic Obsidian MCP server) reaches
linked content the same way: Claude pulls it, link by link, in multiple round-trips. Claude
sees [[Some Note]] as opaque text, has to decide to resolve it, calls a resolve/read tool, gets
raw markdown back (with more [[links]] inside), and repeats — N links means N×2 tool calls.
Servers that run outside Obsidian even re-implement Obsidian's link resolution themselves, which
can diverge from how Obsidian actually resolves links.
This plugin instead does the resolution server-side, inside Obsidian, using Obsidian's own
metadataCache link resolver, expands the content once, and pushes it with the selection.
| Typical plugins / MCP servers | Graph Context for Claude Code | |
|---|---|---|
| Link content delivery | Claude pulls, N round-trips | Pushed once with the selection |
[[link]] content |
Raw / not expanded | Embeds inlined, links summarized |
| Link resolution | Claude-driven or re-implemented | Obsidian's own resolver |
| Heading path / backlinks | Separate tool calls (if any) | Included in the payload |
It is not that other tools can't get this context — it's that they make Claude fetch it, link by link, after the fact. Here it's automatic, resolved correctly, and zero round-trips.
Requirements
- Desktop Obsidian (the plugin is
isDesktopOnly— it runs a Node WebSocket server). - Claude Code installed in a terminal.
Installation
Manual
- Build (or download a release):
npm install && npm run build - Copy
main.jsandmanifest.jsoninto your vault at<vault>/.obsidian/plugins/graph-context-for-claude-code/ - In Obsidian: Settings → Community plugins, turn off Restricted Mode if needed, then enable Graph Context for Claude Code.
You should see a notice: Graph Context for Claude Code: ready on port <PORT>.
Usage
- Enable the plugin in your vault.
- In a separate terminal, run
claude, then type/ideand pick Obsidian. - Ask Claude about what you're looking at — it can read your active file and selection, with the surrounding graph context attached.
Select a few lines in a note that contains links/embeds, then ask e.g. "summarize what I've selected and how it connects to the linked notes" — Claude already has the expanded context.
Tools exposed (MCP)
Base IDE tools: getCurrentSelection, getLatestSelection, getWorkspaceFolders,
getOpenEditors, openFile, openDiff.
Graph-context tools:
getSelectionWithContext— current selection +EnrichedContext(heading path, frontmatter, expanded embeds, wikilink summaries, backlinks).getNoteExpanded{ path }— any vault note, expanded the same way (path-guarded to the vault). Loads the note on demand if it isn't already cached, so it works on notes you haven't opened.
The standard selection_changed notification is also enriched with a context field, so the
context follows your selection automatically.
Settings
- Fixed port — leave empty/
0for a random ephemeral port (recommended), or set a fixed port.
How it works
- On load, the plugin starts a WebSocket server bound to
127.0.0.1, writes a lock file to~/.config/claude/ide/<port>.lock(and~/.claude/ide/<port>.lock) containing the port, the vault path, and a per-session auth token, and cleans it up on unload/exit. - Claude Code discovers the lock file, connects, authenticates with the token, and speaks MCP over JSON-RPC 2.0.
- Selection tracking uses CodeMirror 6's update listener (reliable, unlike DOM
selectionchange). - The graph-context layer (
src/context/) is pure and Obsidian-free: it takes an injectedVaultPort(backed bymetadataCache/vault.cachedRead) so the link-expansion, summary, heading-path and size-capping logic is fully unit-tested.
Notes & limits
![[note#heading]]embeds expand to just that section;![[note#^block]]to just that block.- Expansion is depth-1 (a linked note's own links are not expanded further).
- Expanded context is size-capped (embeds 2000 chars, wikilink summaries 200, total 8000, backlinks
20) with explicit
truncatedflags — never silently dropped. - Duplicate links to the same target (same anchor) collapse to a single entry.
- Link targets are read from a bounded in-memory cache (LRU) populated on file-open/modify, plus a
1-hop prefetch (parallel) of the opened note's link/embed targets — so embeds expand without
opening each target first. On the push path, a target neither opened nor referenced by an opened
note this session may not be cached yet;
getNoteExpandedsidesteps this by loading on demand. selection_changedis debounced (~150 ms) so a drag-select doesn't flood the connection. Graph context is attached only to non-empty selections; a bare cursor move sends just the standard selection fields. Note-level context (frontmatter, links, backlinks) is memoized per note, so moving the cursor within a note only recomputes the heading path.- The pull tools (
getSelectionWithContext,getNoteExpanded) always return full context on demand, regardless of the above push-side optimizations.
Security
The WebSocket server binds to 127.0.0.1 only and rejects any connection whose
x-claude-code-ide-authorization header doesn't match the per-session token in the lock file.
File-opening / diff tools are guarded to the vault root.
Filesystem access
Automated review flags this plugin for using Node's fs module outside the Obsidian vault API.
This is required by the Claude Code IDE protocol and is the plugin's core function: Claude Code
discovers an IDE by reading a lock file that the IDE writes under the user's config directory
(~/.config/claude/ide/<port>.lock and ~/.claude/ide/<port>.lock). Those paths are outside the
vault, so the Obsidian vault API cannot reach them — fs is the only option, and it's the same
mechanism the official VS Code / JetBrains extensions use.
The fs usage is intentionally confined to lock files: every write/delete is guarded by
isLockFilePath(), which permits only paths of the form …/ide/<port>.lock. The plugin does not
read or write any other file on disk; all note access goes through the Obsidian vault API.
Disclaimer
This is an independent, unofficial integration. "Claude Code" is a product of Anthropic; this plugin is not affiliated with or endorsed by Anthropic.
License
MIT © 2026 senna-lang