4.7 KiB
EpiphanyGraph
Small .NET tool for turning an Obsidian note subtree into an MSAGL layout. It still works as a CLI, and now it can also sit on stdio as an MCP server.
What It Does
- scans markdown notes under a vault folder
- resolves Obsidian wikilinks and regular markdown note links
- optionally starts from an entry note and keeps only reachable notes
- classifies notes into
source-treeandcontrol-flowgraph families - lays out each family as its own graph partition
- reports cross-links that still jump between those families
- runs Microsoft Automatic Graph Layout
- writes:
manifest.jsonsource-tree.svgsource-tree.msagl.svgsource-tree.rich.svgsource-tree.jsoncontrol-flow.svgcontrol-flow.msagl.svgcontrol-flow.rich.svgcontrol-flow.jsoncross-links.jsonwarnings.txt
CLI Usage
Run from source:
dotnet run --project . -- `
--vault-root "E:\Projects\Aetheria-Economy\Aetheria\Source Tree Map" `
--entry-note "Source Tree Map" `
--renderer rich `
--output-dir ".\out\source-tree-map"
After packing as a .NET tool:
epiphanygraph `
--vault-root "E:\Projects\Aetheria-Economy\Aetheria\Source Tree Map" `
--entry-note "Source Tree Map" `
--renderer rich `
--output-dir ".\out\source-tree-map"
MCP Usage
The MCP server exposes one tool:
generate_obsidian_graph_layout
Run From Source
Use this in an MCP client config when you want to launch the server straight from the repo:
{
"mcpServers": {
"epiphanygraph": {
"command": "dotnet",
"args": [
"run",
"--project",
"E:\\Projects\\EpiphanyGraph\\EpiphanyGraph.csproj",
"--",
"--mcp"
]
}
}
}
Pack As A .NET Tool
Build the package:
dotnet pack . -c Release -o .\nupkg
Install it from the local package output:
dotnet tool install --global --add-source .\nupkg EpiphanyGraph
Then point your MCP client at the installed command:
{
"mcpServers": {
"epiphanygraph": {
"command": "epiphanygraph",
"args": ["--mcp"]
}
}
}
MCP Tool Arguments
vaultRoot: path to the Obsidian vault folderentryNote: optional entry note name or relative note pathoutputDir: optional output directory, defaults to./outlayout:sugiyamaormdsrenderer:msaglorrichincludeUnreachable: keep notes that are not reachable from the entry note
MCP Tool Result
The tool writes manifest.json, primary partition SVGs, raw .msagl.svg variants, custom .rich.svg variants, partition JSON files, cross-links.json, and warnings.txt, then returns:
- output paths for each partition
- seed note IDs used for traversal
- total note and edge counts
- per-partition node and edge counts
- warnings
- the resolved entry note ID, if one was used
Graph Families
By default, notes whose basename contains Control Flow land in the control-flow partition, and everything else lands in source-tree.
You can override that with frontmatter on a note:
---
epiphany-graph-family: control-flow
---
or:
---
epiphany-graph-families:
- source-tree
- control-flow
---
Supported keys:
epiphany-graph-familyepiphany-graph-familiesgraph-familygraph-families
If your chosen entryNote is itself one of the explicit family roots, the generator also seeds traversal from any sibling family roots it can find.
Source Tree Mapwill also pull inControl Flow MapControl Flow Mapwill also pull inSource Tree Map
That keeps structurally separated note graphs from vanishing just because they no longer link to each other directly.
Layout Modes
sugiyama: layered, better for hierarchy-heavy note treesmds: force-ish spread, better when the graph is cross-linked chaos
mds is the closest MSAGL-native approximation to the Obsidian or Kamada-Kawai vibe. It is not a named Kamada-Kawai mode.
Renderer Modes
msagl: the stock MSAGL SVG, useful as the plain baselinerich: a custom SVG renderer that consumes MSAGL graph geometry and semantic metadata, then draws something with more visual hierarchy and less beige despair
Notes
- unresolved and ambiguous links are written to
warnings.txt - node IDs in the partition
.jsonfiles are relative note paths without.md - the current implementation estimates label sizes instead of using a platform text measurer, because this is a generator, not a cathedral
source-tree.svgandcontrol-flow.svgare the primary outputs for the selected renderer- raw MSAGL output is always preserved as
*.msagl.svg - the custom renderer currently emits
*.rich.svgfrom MSAGL geometry, not from post-processing the stock SVG