@nesso-how/mcp 0.1.0-alpha.38 → 0.1.0-alpha.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,7 +23,7 @@ Add to your MCP client (Cursor, Claude Desktop, etc.):
23
23
 
24
24
  Tools: `get_relation_types`, `get_nesso_docs`, `validate_graph`, `build_graph`.
25
25
 
26
- Full guide: [MCP integration](https://nesso.how/docs/guides/mcp-integration/).
26
+ Full guide: [MCP](https://nesso.how/docs/guides/mcp/).
27
27
 
28
28
  ## License
29
29
 
@@ -1,52 +1,76 @@
1
1
  {
2
2
  "pages": [
3
+ {
4
+ "slug": "faq",
5
+ "title": "FAQ",
6
+ "description": "Common conceptual questions about privacy, the web/desktop split, and the AI mentor.",
7
+ "markdown": "## Is my data private?\n\nGraph content (concepts, definitions, examples, notes, relation structure) and review progress (FSRS stability, ratings, due dates) are stored locally, in IndexedDB on the web app or as plain JSON files on disk for the desktop app, and never leave your device. Mentor chat history lives only in memory for the current panel session, and AI endpoint API keys are stored locally and sent only to the endpoint you configured.\n\nThe only things that can leave your device:\n\n- **AI mentor prompts and chat history**, if you enable the mentor with a remote endpoint. A local Ollama endpoint sends nothing off your machine.\n- **Opt-in telemetry**, off by default.\n- **The desktop app's version check** against GitHub Releases, a plain request that carries no graph or usage data.\n\n## What changes between the web app and the desktop app?\n\n| Area | Web | Desktop |\n| --------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Graph storage | IndexedDB only | Plain JSON files on disk (authoritative); IndexedDB mirrors the active project |\n| Projects | Single implicit workspace, no switching | Multiple named project folders: create, open, switch, remove |\n| File import/export | File System Access API or browser download | Native save/open dialogs |\n| Native app menu | None | A real File/Edit/View/Window/Help menu bar |\n| Auto-update | Not applicable | Checks GitHub Releases on launch, can install and relaunch |\n| External file changes | Not applicable | A file watcher detects edits made outside the app (see [Troubleshooting](../troubleshooting/#this-graph-was-changed-on-disk-while-you-were-editing)) |\n\nEverything else, the canvas, the Inspector, Review, and the AI mentor, works the same on both.\n\n## Why does the AI mentor need an API key?\n\nIt doesn't, if you run a model locally. The default setup points at [Ollama](https://ollama.com/) on `http://localhost:11434/v1`, which needs no key. An API key is only required when you point the mentor at a hosted provider that authenticates requests (most OpenAI-compatible APIs); Nesso sends it as a bearer token to that endpoint only, never anywhere else. See [AI mentor](../guides/ai-mentor/#connecting-a-model).\n\n## Where do I report a bug or request a feature?\n\nOpen an issue on [GitHub](https://github.com/nesso-how/nesso/issues). The code is MIT-licensed and contributions are welcome."
8
+ },
3
9
  {
4
10
  "slug": "guides/ai-mentor",
5
11
  "title": "AI mentor (Socrates)",
6
12
  "description": "How the Socratic mentor works, connecting a model, persona, and graph-aware context.",
7
- "markdown": "The Socratic mentor is **experimental** and **off by default**. Enable it under **Settings → AI** with the **Mentor** toggle (marked _Experimental_). While off, **Socrates** is hidden from the status bar.\n\nWhen enabled, click **Socrates** in the **status bar** (bottom-left) to start a dialogue. The mentor reads your current graph and selection, and replies with **questions rather than explanations**. The goal is to surface what you understand and where the gaps are.\n\n## How it works\n\nEvery send rebuilds a system prompt from the live store: a snapshot of up to ~60 concept nodes, sorted weakest-first via **`nodeStrength()`** ([`context.ts`](https://github.com/nesso-how/nesso/blob/main/src/llm/context.ts)): **FSRS stability** dominates ordering, **Again/Hard** nudge weaker items up, overdue is only a slight tie-break. Each node line lists stability (`s=` days), days since last review, last FSRS rating, and `DUE` when the scheduler says so, plus typed edges (~2× the node allowance), current selection when any, and focal-neighbour context when a node is selected (`Focus:` / `Related:` lines). The conversation history stays in the mentor card and is reset when you switch graphs or click **New chat**.\n\nChat history is **not persisted**. It lives only for the current panel session.\n\n## Connecting a model\n\nConfigure any OpenAI-compatible `chat/completions` endpoint under **Settings → AI**: base URL, model, and an optional API key. Endpoint fields appear only while the mentor toggle is on.\n\nThe default targets a local [Ollama](https://ollama.com/) instance (`http://localhost:11434/v1`, model `gemma3:4b`). Install Ollama, pull a model, and the mentor works with nothing leaving your machine. Any hosted OpenAI-compatible endpoint works too; set the API key it expects.\n\nUntil a reachable endpoint is configured, the chat input stays disabled and the mentor shows a short setup hint.\n\n### Reaching local Ollama from the hosted app\n\nIf you use the hosted web app over HTTPS, requests to `http://localhost:11434` are allowed (localhost is exempt from mixed-content blocking), but Ollama still rejects the cross-origin request unless you allow the app's origin: start it with `OLLAMA_ORIGINS=https://app.nesso.how` (or run the desktop build, where this does not apply).\n\n## The Socratic persona\n\nThe system prompt (`getMentorBase` in [`MentorPanel.tsx`](https://github.com/nesso-how/nesso/blob/main/src/components/mentor/MentorPanel.tsx)) shapes Socrates:\n\n- One short question per turn by default; explain only enough to frame the question.\n- Replies are soft-capped at ~200 words (hard cap via output tokens).\n- No graph edits proposed in dialogue. Socrates probes; the user edits.\n- No emojis, flattery, JSON, or pseudo-graph markup. Sparse `*asterisks*` on key terms.\n- Replies in the active UI language (English or Italian). Snapshot tokens stay **English-shaped** (`s=…d`, `…d since review`, etc.), with the same spelling in the legend for every locale.\n\nIf you want a more permissive coach, fork the persona. It is plain text in the component and easy to swap.\n\n## Opening message\n\nWhen the panel opens, the mentor sends itself a short synthetic **user** turn so its first message reflects what's selected:\n\n- **A concept node selected:** opens on that concept and one of its relations.\n- **An edge selected (no node):** opens on the typed relation between its endpoints.\n- **Nothing selected:** opens on a weak spot in the graph (low stability plus weak **last reviews** (Again/Hard or a long gap); **DUE** is extra scheduler context).\n\nClick **New chat** in the header to reset history and request a fresh opener.\n\n## Context size\n\nLarge graphs are summarised, not truncated abruptly. The weakest-reviewed nodes appear first (`nodeStrength`), so the verbatim slice emphasises instability and risky last ratings; tail nodes are omitted with a short count only. Edges have a ~2x allowance over node count. These limits live in [`MentorPanel.tsx`](https://github.com/nesso-how/nesso/blob/main/src/components/mentor/MentorPanel.tsx) as `MAX_SNAPSHOT_NODES` and `MAX_SNAPSHOT_EDGES`.\n\n## Privacy\n\n- **Local Ollama:** prompts and responses go only to your own machine; nothing leaves the device.\n- **Hosted endpoint:** the system prompt (graph snapshot) and chat history are sent to whichever endpoint you configured, each turn. The usual provider-side logging applies.\n\nYour graph itself always stays on your device regardless of the endpoint."
13
+ "markdown": "The Socratic mentor is **experimental** and **off by default**. Enable it under **Settings → AI** with the **Mentor** toggle. While off, **Socrates** is hidden from the status bar.\n\nWhen enabled, click **Socrates** in the **status bar** (bottom-left) to start a dialogue. The mentor reads your current graph and selection, and replies with **questions rather than explanations**. The goal is to surface what you understand and where the gaps are.\n\n:::note\nFor good results, use at least a 7–8B model. `qwen3:14b` is a strong default; `llama3.3:70b` for higher quality if your hardware allows. For hosted endpoints, model names change fast, so pick any capable instruction-following model from your preferred provider.\n:::\n\n## How it works\n\nEvery send rebuilds a system prompt from the live store: a snapshot of up to ~60 concept nodes, sorted weakest-first via **`nodeStrength()`** ([`context.ts`](https://github.com/nesso-how/nesso/blob/main/src/llm/context.ts)): **FSRS stability** dominates ordering, **Again/Hard** nudge weaker items up, overdue is only a slight tie-break. Each node line lists stability (`s=` days), days since last review, last FSRS rating, and `DUE` when the scheduler says so, plus typed edges (~2× the node allowance), current selection when any, and focal-neighbour context when a node is selected (`Focus:` / `Related:` lines). The conversation history stays in the mentor card and is reset when you switch graphs or click **New chat**.\n\nChat history is **not persisted**. It lives only for the current panel session.\n\n## Connecting a model\n\nConfigure any OpenAI-compatible `chat/completions` endpoint under **Settings → AI**: base URL, model, and an optional API key. Endpoint fields appear only while the mentor toggle is on.\n\nThe default targets a local [Ollama](https://ollama.com/) instance (`http://localhost:11434/v1`, model `gemma3:4b`). Install Ollama, pull a model, and the mentor works with nothing leaving your machine. Any hosted OpenAI-compatible endpoint works too. Set the API key it expects.\n\nUntil a reachable endpoint is configured, the chat input stays disabled and the mentor shows a short setup hint. If the mentor stops responding once a turn fails, see [Troubleshooting](../../troubleshooting/#mentor-not-responding).\n\n### Reaching local Ollama from the hosted app\n\nIf you use the hosted web app over HTTPS, requests to `http://localhost:11434` are allowed (localhost is exempt from mixed-content blocking), but Ollama still rejects the cross-origin request unless you allow the app's origin: start it with `OLLAMA_ORIGINS=https://app.nesso.how` (or run the desktop build, where this does not apply).\n\n## The Socratic persona\n\nThe system prompt (`getMentorBase` in [`MentorPanel.tsx`](https://github.com/nesso-how/nesso/blob/main/src/components/mentor/MentorPanel.tsx)) shapes Socrates:\n\n- One short question per turn by default, explaining only enough to frame it.\n- Replies are soft-capped at ~200 words (hard cap via output tokens).\n- No graph edits proposed in dialogue. Socrates probes; the user edits.\n- No emojis, flattery, JSON, or pseudo-graph markup. Sparse `*asterisks*` on key terms.\n- Replies in the active UI language (English or Italian). Snapshot tokens stay **English-shaped** (`s=…d`, `…d since review`, etc.), with the same spelling in the legend for every locale.\n\nIf you want a more permissive coach, fork the persona. It is plain text in the component and easy to swap.\n\n## Opening message\n\nWhen the panel opens, the mentor sends itself a short synthetic **user** turn so its first message reflects what's selected:\n\n- **A concept node selected:** opens on that concept and one of its relations.\n- **An edge selected (no node):** opens on the typed relation between its endpoints.\n- **Nothing selected:** opens on a weak spot in the graph (low stability plus weak **last reviews** (Again/Hard or a long gap); **DUE** is extra scheduler context).\n\nClick **New chat** in the header to reset history and request a fresh opener.\n\n## Context size\n\nLarge graphs are summarised, not truncated abruptly. The weakest-reviewed nodes appear first (`nodeStrength`), so the verbatim slice emphasises instability and risky last ratings; tail nodes are omitted with a short count only. Edges have a ~2x allowance over node count. These limits live in [`MentorPanel.tsx`](https://github.com/nesso-how/nesso/blob/main/src/components/mentor/MentorPanel.tsx) as `MAX_SNAPSHOT_NODES` and `MAX_SNAPSHOT_EDGES`."
8
14
  },
9
15
  {
10
- "slug": "guides/concepts-and-inspector",
11
- "title": "Concepts & Inspector",
12
- "description": "Adding concepts, drawing typed relations, and using the Inspector to enrich nodes with definitions, examples, notes, and images.",
13
- "markdown": "The canvas is the centre of Nesso. **Concepts** are nodes; **typed relations** are edges. The **Inspector** is the right-hand panel where you enrich whatever you've got selected.\n\n## Adding concepts\n\n- **Double-click** empty canvas to add a concept at the pointer.\n- **`N`** adds a concept at the viewport centre.\n- **Right-click** empty canvas and choose **Add concept here** to add one at the cursor.\n- New concepts open in edit mode. Type the label and press `Enter` to commit, `Esc` to cancel.\n- **Double-click** a concept to rename it inline.\n\nAn empty graph shows a centered **\"Your first concept\"** hint; the double-click still works through it.\n\nConcepts you add are stored locally in IndexedDB. Switch graphs from the sidebar; create new graphs from the **Graphs** list.\n\n## Drawing relations\n\nDrag from a node's right edge (`out` handle) to another node's left edge (`in` handle). On release, a **relation picker** opens, grouped by category. Pick the relation type and the edge is created.\n\n- Drag-to-self is ignored, so you can't accidentally create self-loops.\n- The connection line previews with the same quadratic geometry the final edge uses.\n- Edge type can be changed any time from the Inspector when an edge is selected.\n\nSee the [relation types reference](../../reference/relation-types/) for the full list, semantic meaning, and type properties. Per-type line style and glyph come from `@nesso-how/vocab-learning`; edge encoding density is under [Display options](#display-options-sidebar) below.\n\n## Selecting and editing\n\n- **Click** a node or edge to select it. The Inspector reflects the selection.\n- **Hold `⌘` / `Ctrl` and click** to toggle additional items into the selection.\n- **Drag on empty canvas** to marquee-select multiple items.\n- **`⌘A` / `Ctrl+A`** selects every concept and relation in the graph.\n- **Right-click** a concept, relation, or empty canvas for a context menu of the relevant actions (copy/cut/duplicate/delete a concept; flip / delete a relation; paste / add concept / center·fit on the canvas). To change a relation's type, select the edge and pick a new type in the Inspector.\n- **`Del`** or **`Backspace`** deletes the selection (one relation, one concept, or every concept in a marquee). Edges attached to a deleted concept go with it. Delete is also on the right-click menu and the Inspector's action toolbar.\n- **`⌘C` / `Ctrl+C`** copies the selection. Copying concepts also copies relations between them; copying a relation includes its two endpoints. **`⌘X` / `Ctrl+X`** cuts: it copies the selection and removes it in one step. **`⌘V` / `Ctrl+V`** pastes the clipboard with a small offset (right-click **Paste** drops it at the cursor instead). **`⌘D` / `Ctrl+D`** duplicates the selection in place without touching the clipboard. These also live on the right-click menu and the Inspector toolbar.\n- **Arrow keys** nudge a selected concept; **Shift + arrows** move it in larger steps.\n- **`⌘Z` / `Ctrl+Z`** undoes structural edits; **`⌘⇧Z` / `Ctrl+Shift+Z`** redoes. History has 50 steps and resets when you switch or import a graph.\n\n## The Inspector\n\nThe Inspector docks on the **right**, full height between the top bar and the status bar. Its header has a **collapse** control that shrinks it to a slim **rail** (keeping the selection plus a vertical action toolbar) and a **close** control; a docked bottom **action toolbar** offers copy / cut / duplicate / delete for a concept, or flip / delete for a relation.\n\nWhen a concept is selected it shows, top to bottom:\n\n- **Image + title:** the title edits inline (`Enter` commits, `Esc` reverts); the image button opens Commons search (see below).\n- **Memory** _(collapsible):_ the FSRS schedule, read-only when due, stability (in days), last self-rating, review count (with lapses), and time since the last review.\n- **Definition**, **Examples**, **Notes** see below.\n- **Relations** _(collapsible):_ outgoing and incoming edges, each connected concept shown with the relation glyph in a chip and the type on the right (incoming dimmed). Click a row to jump to that concept; change a relation's type by selecting the edge.\n\n### Notes fields\n\nThree free-text fields that travel with the concept and feed both the AI mentor and [Review](./review-mode/):\n\n- **Definition:** a one-sentence-ish explanation in your own words.\n- **Examples:** one per line. Press `Shift+Enter` or use the **Add** button to add a new line; press `Backspace` in an empty example to remove that line (unless it's the only one).\n- **Notes:** anything else: caveats, sources, mnemonics.\n\nIn Review, definition and examples appear when you **Reveal** a card so you can check your recall. The AI mentor uses the same fields in its graph snapshot and focal-neighbour context when a concept is selected.\n\n### Concept image\n\nPress the picture icon to open the **Wikimedia Commons search**. The query auto-fills from the concept title and runs immediately; pick any result to attach a 200-px thumbnail to the concept. The image shows in the Inspector, on reveal in Review mode, and is included as context for the AI mentor.\n\nThe image link and Commons description URL are persisted with the graph, so attribution is preserved on export.\n\n## Display options (sidebar)\n\n**Sidebar → Display** controls how the **active graph** is rendered: heatmap overlay, edge encoding density, curve style, and auto flip. Choices are saved **with the graph** in IndexedDB (and included in JSON export). New graphs start from the app defaults until you change them.\n\nWhen **Display → Curve** is set to **Arc**, **Auto flip** (on by default) bends relations toward the side that avoids overlapping nodes, flipping when the target is above the source on the right, or below on the left, and updates live while you drag concepts. **Flip curve** in the Inspector is **Off | Auto | On** while auto flip is on: **Auto** follows layout, **Off** / **On** pin a manual bend on that edge. With auto flip off for that graph, the control is **Off | On** only.\n\n## When an edge is selected\n\nThe Inspector shows the relation as a chip with its category colour and a dropdown of every relation type. Picking a new type updates the edge in place; the graph keeps its endpoints and identity.\n\n## Status bar and search\n\n- The **status bar** along the bottom shows the concept and relation counts. Its right side carries undo / redo, zoom out / in, and center·fit. When the mentor is enabled in **Settings → AI**, the **Socrates** entry appears on the left (see [AI mentor](./ai-mentor/)).\n- **`⌘K` / `Ctrl+K`** opens a fuzzy search palette over concept titles. `Enter` selects and recenters the viewport; `Esc` closes.\n\n## Edge encoding density\n\nEdges carry three visual channels: colour (category), line style, and glyph. Crank this down for large or printed graphs from **Sidebar → Display → Edges**:\n\n- **Full:** colour + style + glyph (default).\n- **Category:** colour only.\n- **Minimal:** plain line, no encoding.\n\nSymmetric relations (similarity, opposition) never render an arrowhead regardless of encoding."
16
+ "slug": "guides/building-a-graph",
17
+ "title": "Building a graph",
18
+ "description": "How to add concepts, draw typed relations, and use the Inspector to enrich nodes with definitions, examples, notes, and images.",
19
+ "markdown": "The canvas is the centre of Nesso. **Concepts** are nodes and **typed relations** are edges. The **Inspector** is the right-hand panel where you enrich whatever you've got selected.\n\n## Adding concepts\n\n- **Double-click** empty canvas to add a concept at the pointer.\n- **`N`** adds a concept at the viewport centre.\n- **Right-click** empty canvas and choose **Add concept here** to add one at the cursor.\n- New concepts open in edit mode. Type the label and press `Enter` to commit, `Esc` to cancel.\n- **Double-click** a concept to rename it inline.\n\nAn empty graph shows a centered **\"Your first concept\"** hint, but the double-click still works through it.\n\n## Drawing relations\n\nDrag from a node's right edge (`out` handle) to another node's left edge (`in` handle). On release, a **relation picker** opens, grouped by category. Pick the relation type and the edge is created.\n\n- The connection line previews with the same quadratic geometry the final edge uses.\n- Edge type can be changed any time from the Inspector when an edge is selected.\n\nSee the [relation types reference](../../reference/relation-types/) for the full list, semantic meaning, and type properties. Per-type line style and glyph come from `@nesso-how/vocab-learning`. Edge encoding density is under [Display options](#display-options-sidebar) below.\n\n## Selecting and editing\n\n- **Click** a node or edge to select it. The Inspector reflects the selection.\n- **Hold `⌘` / `Ctrl` and click** to toggle additional items into the selection.\n- **Drag on empty canvas** to marquee-select multiple items.\n- **`⌘A` / `Ctrl+A`** selects every concept and relation in the graph.\n- **Right-click** a concept, relation, or empty canvas for a context menu of the relevant actions (copy/cut/duplicate/delete a concept; flip / delete a relation; paste / add concept / center·fit on the canvas). To change a relation's type, select the edge and pick a new type in the Inspector.\n- **`Del`** or **`Backspace`** deletes the selection (one relation, one concept, or every concept in a marquee). Edges attached to a deleted concept go with it. Delete is also on the right-click menu and the Inspector's action toolbar.\n- **`⌘C` / `Ctrl+C`** copies the selection. Copying concepts also copies relations between them; copying a relation includes its two endpoints. **`⌘X` / `Ctrl+X`** cuts: it copies the selection and removes it in one step. **`⌘V` / `Ctrl+V`** pastes the clipboard with a small offset (right-click **Paste** drops it at the cursor instead). **`⌘D` / `Ctrl+D`** duplicates the selection in place without touching the clipboard. These also live on the right-click menu and the Inspector toolbar.\n- **Arrow keys** nudge a selected concept, and **Shift + arrows** move it in larger steps.\n- **`⌘Z` / `Ctrl+Z`** undoes structural edits; **`⌘⇧Z` / `Ctrl+Shift+Z`** redoes. History has 50 steps and resets when you switch or import a graph.\n\n## The Inspector\n\nThe Inspector docks on the **right**, full height between the top bar and the status bar. Its header has a **collapse** control that shrinks it to a slim **rail** (keeping the selection plus a vertical action toolbar) and a **close** control. A docked bottom **action toolbar** offers copy / cut / duplicate / delete for a concept, or flip / delete for a relation.\n\nWhen a concept is selected it shows, top to bottom:\n\n- **Image + title:** the title edits inline (`Enter` commits, `Esc` reverts). The image button opens Commons search (see below).\n- **Memory** _(collapsible):_ the FSRS schedule, read-only: when due, stability (in days), last self-rating, review count (with lapses), and time since the last review.\n- **Definition**, **Examples**, **Notes**: see below.\n- **Relations** _(collapsible):_ outgoing and incoming edges, each connected concept shown with the relation glyph in a chip and the type on the right (incoming dimmed). Click a row to jump to that concept. Change a relation's type by selecting the edge.\n\nWhen an edge is selected, the Inspector shows the relation as a chip with its category colour and a dropdown of every relation type. Picking a new type updates the edge in place. The graph keeps its endpoints and identity.\n\n### Notes fields\n\nThree free-text fields that travel with the concept and feed both the AI mentor and [Review](../../guides/review-mode/):\n\n- **Definition:** a one-sentence-ish explanation in your own words.\n- **Examples:** one per line. Press `Shift+Enter` or use the **Add** button to add a new line; press `Backspace` in an empty example to remove that line (unless it's the only one).\n- **Notes:** anything else: caveats, sources, mnemonics.\n\nIn Review, definition and examples appear when you **Reveal** a card so you can check your recall. The AI mentor uses the same fields in its graph snapshot and focal-neighbour context when a concept is selected.\n\n### Concept image\n\nPress the picture icon to open the **Wikimedia Commons search**. The query auto-fills from the concept title and runs immediately. Pick any result to attach a 200-px thumbnail to the concept. The image shows in the Inspector, on reveal in Review mode, and is included as context for the AI mentor.\n\nThe image link and Commons description URL are persisted with the graph, so attribution is preserved on export.\n\n## Display options (sidebar)\n\n**Sidebar → Display** controls how the **active graph** is rendered: heatmap overlay, edge encoding density, curve style, and auto flip. Choices are saved **with the graph** in IndexedDB (and included in JSON export). New graphs start from the app defaults until you change them.\n\nWhen **Display → Curve** is set to **Arc**, **Auto flip** (on by default) bends relations toward the side that avoids overlapping nodes, flipping when the target is above the source on the right, or below on the left, and updates live while you drag concepts. **Flip curve** in the Inspector is **Off | Auto | On** while auto flip is on: **Auto** follows layout, **Off** / **On** pin a manual bend on that edge. With auto flip off for that graph, the control is **Off | On** only."
14
20
  },
15
21
  {
16
22
  "slug": "guides/embedding-graphs",
17
23
  "title": "Embedding graphs",
18
24
  "description": "Render Nesso knowledge graphs in your own React app with @nesso-how/graph.",
19
- "markdown": "The [`@nesso-how/graph`](https://www.npmjs.com/package/@nesso-how/graph) package is an embeddable React component for rendering Nesso knowledge graphs: the same visual style (categories, glyphs, edge curves) used by the app, available for docs, blog posts, or any React surface.\n\n## Install\n\n```bash\nnpm install @nesso-how/graph @xyflow/react react react-dom\n```\n\n`@xyflow/react`, `react`, and `react-dom` are peer dependencies. Install versions matching the ranges declared in the package's `peerDependencies`.\n\n## Basic usage\n\n```tsx\nimport { NessoGraph } from '@nesso-how/graph'\nimport '@xyflow/react/dist/style.css'\n;<NessoGraph nodes={nodes} edges={edges} style={{ width: '100%', height: 400 }} />\n```\n\nBy default the graph renders read-only (no drag, connect, or selection) using the same category colors, glyphs, and edge encoding as the app. Pass a full `NessoGraphDocument` via `graph` instead of `nodes`/`edges` if you have one (e.g. JSON from `deserialize` in `@nesso-how/vocab-learning`). FSRS review fields are not in the file — embeds show an empty heatmap unless you merge review state yourself.\n\n## Display options\n\n`display` controls how edges and categories are drawn:\n\n```tsx\n<NessoGraph\n nodes={nodes}\n edges={edges}\n display={{ edgeEncoding: 'full', curveStyle: 'straight', showHeatmap: false }}\n/>\n```\n\n`palette` selects one of the category color palettes available in the app's settings. Embeds use hex colors from `PALETTES` (`categoryColorMode: 'palette'`, the default). Host apps that set `--cat-*` CSS variables (like Nesso itself) pass `categoryColorMode=\"css\"`.\n\nOptional `getRelationLabel` localizes edge labels; `isItemSelected` syncs selection with an external store.\n\n## Interactivity\n\n`nodesDraggable`, `nodesConnectable`, and `elementsSelectable` are all `false`/read-only by default, so turn on what you need. How you provide nodes determines who owns their state:\n\n- **`nodes`/`edges`** (_controlled_): you own the state, so also pass `onNodesChange`/`onEdgesChange`/`onConnect` to apply the resulting changes (e.g. when positions live in your own store, like the main app does).\n- **`defaultNodes`/`defaultEdges`** (_uncontrolled_): React Flow seeds its state once from these and manages drag, connect, and selection internally, with nothing else to wire up. The right choice for a self-contained embed.\n\n```tsx\n<NessoGraph\n defaultNodes={nodes}\n defaultEdges={edges}\n nodesDraggable\n style={{ width: '100%', height: 400 }}\n/>\n```\n\nDecorative embeds in a scrollable page (e.g. a landing hero) should turn off wheel zoom and drag pan so the page keeps scrolling over the canvas:\n\n```tsx\n<NessoGraph\n graph={doc}\n panOnDrag={false}\n zoomOnScroll={false}\n style={{ width: '100%', height: 400 }}\n/>\n```\n\n`NessoGraph` sets React Flow's `preventScrolling` from those flags: when neither `zoomOnScroll` nor `panOnScroll` consumes the wheel, page scroll is not blocked. Override with `reactFlowProps.preventScrolling` if you need different behaviour.\n\n## Escape hatch\n\nAny [`ReactFlow`](https://reactflow.dev/api-reference/react-flow) prop not listed above can be passed through `reactFlowProps`, for example to hide the attribution badge or disable double-click-to-zoom:\n\n```tsx\n<NessoGraph\n nodes={nodes}\n edges={edges}\n reactFlowProps={{ proOptions: { hideAttribution: true }, zoomOnDoubleClick: false }}\n/>\n```"
25
+ "markdown": "The [`@nesso-how/graph`](https://www.npmjs.com/package/@nesso-how/graph) package is an embeddable React component for rendering Nesso knowledge graphs: the same visual style (categories, glyphs, edge curves) used by the app, available for docs, blog posts, or any React surface.\n\n## Install\n\n```bash\nnpm install @nesso-how/graph @xyflow/react react react-dom\n```\n\n`@xyflow/react`, `react`, and `react-dom` are peer dependencies. Install versions matching the ranges declared in the package's `peerDependencies`.\n\n## Basic usage\n\nPass a `NessoGraphDocument` via `graph`, the same JSON you export from the app or build with the MCP server:\n\n<!-- prettier-ignore -->\n```tsx\nimport { NessoGraph } from '@nesso-how/graph';\nimport '@xyflow/react/dist/style.css';\n\n<NessoGraph graph={doc} style={{ width: '100%', height: 400 }} />\n```\n\nBy default the graph renders read-only (no drag, connect, or selection) using the same category colors, glyphs, and edge encoding as the app.\n\n## Display options\n\n`display` controls how edges and categories are drawn:\n\n```tsx\n<NessoGraph\n graph={doc}\n display={{ edgeEncoding: 'full', curveStyle: 'straight', showHeatmap: false }}\n/>\n```\n\nFSRS review fields are not in the file, so embeds show an empty heatmap unless you merge review state yourself.\n\n## Interactivity\n\n`nodesDraggable`, `nodesConnectable`, and `elementsSelectable` are all `false`/read-only by default, so turn on what you need.\n\nDecorative embeds in a scrollable page (e.g. a landing hero) should turn off wheel zoom and drag pan so the page keeps scrolling over the canvas:\n\n```tsx\n<NessoGraph\n graph={doc}\n panOnDrag={false}\n zoomOnScroll={false}\n style={{ width: '100%', height: 400 }}\n/>\n```\n\n`NessoGraph` sets React Flow's `preventScrolling` from those flags: when neither `zoomOnScroll` nor `panOnScroll` consumes the wheel, page scroll is not blocked. Override with `reactFlowProps.preventScrolling` if you need different behaviour.\n\nFor fully controlled maps where you manage node positions in your own state, pass `nodes`/`edges` with `onNodesChange`/`onEdgesChange` instead of `graph`:\n\n<!-- prettier-ignore -->\n```tsx\n<NessoGraph\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n/>\n```\n\n## Escape hatch\n\nAny [`ReactFlow`](https://reactflow.dev/api-reference/react-flow) prop not listed above can be passed through `reactFlowProps`, for example to hide the attribution badge or disable double-click-to-zoom:\n\n```tsx\n<NessoGraph\n graph={doc}\n reactFlowProps={{ proOptions: { hideAttribution: true }, zoomOnDoubleClick: false }}\n/>\n```"
20
26
  },
21
27
  {
22
28
  "slug": "guides/getting-started",
23
29
  "title": "Getting started",
24
30
  "description": "How to run Nesso locally or on the web.",
25
- "markdown": "Nesso is available as a hosted web app, a macOS desktop app, and as open-source code you can run locally. Graphs are stored in your browser or on your machine (local-first, no account). Optional telemetry and desktop update checks may contact external services; using the AI mentor with a remote endpoint sends prompts to that provider.\n\n## Web app\n\nOpen [app.nesso.how](https://app.nesso.how) in your browser. The app works offline after the first load and runs in any modern browser.\n\n## First run\n\nOn a fresh install, Nesso opens with an empty **Tutorial** graph and walks you through the essentials:\n\n1. **Welcome**: a short overview of typed knowledge graphs and spaced repetition.\n2. **Guided tour**: coachmarks on the real UI that walk you through adding and naming concepts, adding a definition in the inspector, connecting two ideas with a typed relation, then opening **Review**.\n3. **Telemetry** (optional): a one-time banner in the top-right asks whether to share anonymous usage events. Declining is remembered; you can change the choice anytime under **Settings → Privacy**.\n\nYou can skip the welcome screen or the tour at any step. Skipping removes the Tutorial graph and opens a demo seed map instead. Completing the tour keeps your Tutorial graph and also opens a demo seed map.\n\nDemo seed graphs are no longer loaded automatically on first launch; you build your first graph during the tour.\n\n## Desktop app (macOS)\n\nA pre-built alpha installer is published on [GitHub Releases](https://github.com/nesso-how/nesso/releases). Download the universal `.dmg` it runs on both Apple silicon and Intel Macs — and open it.\n\n:::caution\nThe app is not signed with an Apple developer certificate. macOS will block it on first launch. After installing, run this command in the terminal to remove the quarantine flag:\n\n```sh\nxattr -cr /Applications/Nesso.app\n```\n\nThen open the app normally.\n:::\n\nThe desktop app **updates itself**: on launch it checks GitHub Releases and, when a newer build is available, offers to install it and relaunch. Auto-updates begin once you are on a build that ships the updater (`v0.1.0-alpha.25` or later), so that first version still needs a one-time manual download.\n\n## Run from source\n\nRequires [Node.js](https://nodejs.org/) and [pnpm](https://pnpm.io/).\n\n```sh\ngit clone https://github.com/nesso-how/nesso.git\ncd nesso\npnpm install\npnpm dev\n```\n\nFor a desktop build, [Rust](https://rustup.rs/) is required as well:\n\n```sh\npnpm build:desktop\n```\n\n## Picking an AI backend\n\nSee [AI mentor (Socrates)](./ai-mentor/) for setup, persona, context, and privacy. Quick path with local Ollama:\n\nRun [Ollama](https://ollama.com/) locally, then pull a small instruction-tuned model:\n\n```sh\nollama pull gemma3:4b\n```\n\nIn **Settings → AI**, turn on **Mentor**, then set:\n\n- Base URL: `http://localhost:11434/v1`\n- Model: `gemma3:4b` (or `llama3.2:3b`, `qwen3:8b`; presets are shown in Settings)\n- API key: leave empty\n\nSettings auto-probes the endpoint and offers a **Pull** button if the model is missing. Prompts and responses stay on your machine.\n\nWhen using the hosted web app over HTTPS, allow its origin in Ollama so the browser request is not blocked by CORS: start Ollama with `OLLAMA_ORIGINS=https://app.nesso.how`. (Requests to `localhost` are exempt from mixed-content blocking, so only CORS needs configuring.)\n\n:::caution\nAPI keys are stored client-side in `localStorage`. Do not self-host the web app publicly with secrets baked in.\n:::\n\n## Keyboard shortcuts\n\n| Shortcut | Action |\n| ---------------------- | ------------------------------ |\n| `?` | Show shortcuts dialog |\n| `⌘,` / `Ctrl+,` | Settings |\n| `⌘K` / `Ctrl+K` | Search concepts |\n| `N` | Add concept at viewport centre |\n| `R` | Open review mode |\n| `⌘Z` / `Ctrl+Z` | Undo |\n| `⌘⇧Z` / `Ctrl+Shift+Z` | Redo |\n| `Del` / `Backspace` | Delete selection |\n| `⌘A` / `Ctrl+A` | Select all |\n| `⌘X` / `Ctrl+X` | Cut selection |\n| `⌘C` / `Ctrl+C` | Copy selection |\n| `⌘V` / `Ctrl+V` | Paste |\n| `↑` `↓` `←` `→` | Nudge selected concept |\n| `Shift` + arrows | Nudge selected concept (large) |\n| `Esc` | Close dialog |\n\nHold `⌘` / `Ctrl` to add to a selection; drag on empty canvas to marquee-select."
31
+ "markdown": "Nesso is available as a hosted web app, a macOS desktop app, and as open-source code you can run locally. All three share the same graph model. The desktop app adds project folders and a file-system-backed workspace. See [FAQ](../../faq/#what-changes-between-the-web-app-and-the-desktop-app) for the full comparison.\n\n## Tutorial\n\nThe simplest way to get started is the web app at [app.nesso.how](https://app.nesso.how), no install required. The first time you open Nesso, it starts with an empty **Tutorial** graph and walks you through the essentials:\n\n1. **Welcome**: a short overview of typed knowledge graphs and spaced repetition.\n2. **Guided tour**: coachmarks on the real UI that walk you through adding and naming concepts, adding a definition in the inspector, connecting two ideas with a typed relation, opening **Review**, then deleting a node and a graph.\n3. **Telemetry** (optional): a one-time banner in the top-right asks whether to share anonymous usage events.\n\nYou can skip the welcome screen or the tour at any step. To restart it later, open the **About** dialog from the menu bar.\n\n## Desktop app (macOS)\n\nA pre-built alpha installer is published on [GitHub Releases](https://github.com/nesso-how/nesso/releases). Download the universal `.dmg` (it runs on both Apple silicon and Intel Macs), drag **Nesso** to **Applications**, and open it.\n\nThe desktop app **updates itself**: on launch it checks GitHub Releases and, when a newer build is available, offers to install it and relaunch.\n\n## Run from source\n\nRequires [Node.js](https://nodejs.org/) and [pnpm](https://pnpm.io/).\n\n```sh\ngit clone https://github.com/nesso-how/nesso.git\ncd nesso\npnpm install\npnpm dev\n```\n\nFor a desktop build, [Rust](https://rustup.rs/) is required as well:\n\n```sh\npnpm build:desktop\n```"
32
+ },
33
+ {
34
+ "slug": "guides/graph-management",
35
+ "title": "Graph management",
36
+ "description": "Working with multiple graphs, exporting and importing JSON, and desktop project folders.",
37
+ "markdown": "A **graph** is one map: its concepts, relations, and display settings. You can keep several graphs and switch between them, export or import any of them as JSON, and, on desktop, group graphs into project folders.\n\n## Multiple graphs\n\nThe **Sidebar** lists every graph in the current project. Click a graph to switch to it: the canvas, Inspector, and Review queue all follow.\n\n- **Rename**: double-click a graph's row in the list, type the new name, `Enter` to commit.\n- **Delete**: hover a row and click the trash icon. You're asked to confirm. Deleting also removes its file from the project folder on desktop. The last remaining graph can't be deleted, so the delete action is hidden when only one graph is left.\n- **New graph**: the **New** button above the list creates an empty graph and switches to it immediately.\n\nSwitching graphs flushes any pending autosave on the one you're leaving, so edits made just before a switch aren't lost.\n\n## Export and import (JSON)\n\nOpen the **⋯** menu in the top bar (tooltip: \"Export · Import · Shortcuts\") for:\n\n- **Export graph**: saves the active graph as a `.json` file named after the graph.\n- **Export graph (.png)**: renders the canvas at 1920×1200 and saves it as an image, with React Flow's selection handles excluded from the render.\n- **Import graph**: opens a file picker for a `.json` file and adds it as a new graph.\n\nExport behaves differently depending on platform:\n\n- **Desktop**: a native save dialog lets you pick the destination.\n- **Web**: if your browser supports the File System Access API, you get a save dialog and can overwrite the same file on repeat exports. Otherwise it falls back to a normal browser download.\n\nImporting a file that isn't a valid Nesso graph shows an error toast and imports nothing. A successful import creates a new graph (de-duplicating its name against your existing graphs) and switches to it.\n\nExported JSON carries graph **content** only: concepts, relations, and display settings. Personal review progress (FSRS data) is never included, so sharing or re-importing a file never carries someone else's recall history.\n\n## Desktop projects\n\nOn the desktop app, graphs live inside **project folders**: plain directories on disk holding one JSON file per graph plus a small manifest. The **Projects** section in the sidebar (desktop only) lists every folder you've opened and lets you switch between them.\n\n- **New project**: the **+** menu's \"New project\" opens a native save dialog. Name and place a new folder. It's created empty and becomes the active project.\n- **Open project**: the same menu's \"Open project…\" opens a native folder picker for an existing project folder elsewhere on disk.\n- **Switch project**: click any project in the list. The current graph is saved first, then the target folder's graphs load.\n- **Remove project**: hover a project and click the trash icon, then confirm. This only removes it from Nesso's list. Its files stay on disk untouched. You can't remove the last project in the list.\n- **Reveal in Finder**: hover a project and click the folder icon to reveal it in the system file manager.\n\nIf a project folder is moved, renamed, or deleted outside the app, it stays in the list marked as missing (greyed out, with a \"not found\" label) rather than disappearing. Switching away from a missing project happens automatically. Switching back works again once the folder reappears at the same path. Only the trash icon removes it from the list for good.\n\n:::caution\nNative folder pickers and the system menu bar are desktop-only Tauri APIs. None of this section applies to the web app, which has a single implicit workspace backed by IndexedDB. See [FAQ](../../faq/#what-changes-between-the-web-app-and-the-desktop-app) for the full web/desktop comparison.\n:::"
26
38
  },
27
39
  {
28
- "slug": "guides/mcp-integration",
40
+ "slug": "guides/mcp",
29
41
  "title": "MCP",
30
42
  "description": "Connect Nesso to Claude, Cursor, or any MCP-compatible AI client.",
31
- "markdown": "The `@nesso-how/mcp` package is a [Model Context Protocol](https://modelcontextprotocol.io/) server that gives AI clients access to Nesso documentation, the full relation-type vocabulary, and tools to **build** and **validate** graph documents. Once connected, models can answer questions about how Nesso works, produce importable graph JSON, and check files before writing them back to disk.\n\n## Setup\n\n### Claude Desktop\n\nOpen `claude_desktop_config.json`. On macOS it lives at `~/Library/Application Support/Claude/claude_desktop_config.json`; on Windows at `%APPDATA%\\Claude\\claude_desktop_config.json`.\n\nAdd a `nesso` entry under `mcpServers`:\n\n```json\n{\n \"mcpServers\": {\n \"nesso\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@nesso-how/mcp\"]\n }\n }\n}\n```\n\nSave the file and restart Claude Desktop. The Nesso tools will be available to the model automatically.\n\n### Cursor\n\nOpen **Settings -> MCP** and add a new server with command `npx` and args `[\"-y\", \"@nesso-how/mcp\"]`.\n\n### Other MCP clients\n\nAny client that speaks the stdio MCP transport works. Run `npx -y @nesso-how/mcp` as the server command. No other configuration is required.\n\n## What it can do\n\nOnce connected, you can ask your AI client things like:\n\n- \"What relation types does Nesso support?\" (uses `get_relation_types`)\n- \"Show me the Nesso getting started guide\" (uses `get_nesso_docs`)\n- \"Build a graph about photosynthesis with causes and enables relations\" (uses `build_graph`)\n- \"Validate this graph JSON before I save it\" (uses `validate_graph`)\n\nThe MCP server is **stateless** and does not read or write files. Your client uses its own filesystem tools to read `.json` graph files, passes the contents inline to `validate_graph` or `build_graph`, and writes the result back. Nesso picks up external edits through its normal workspace sync.\n\n### Agent workflow for graph files\n\nA typical end-to-end flow when the client has filesystem access:\n\n1. **Read** the target graph `.json` from the project folder (client filesystem tool).\n2. **Validate** with `validate_graph` fix any `errors` before saving; review `warnings` (e.g. missing `vocabulary` or relation `type`).\n3. **Build or extend** with `build_graph` when creating a new graph from structured concepts and relations the tool assigns ids, vocabulary metadata, valid relation types, and layout positions.\n4. **Re-validate** the output with `validate_graph` if the client edited the JSON by hand.\n5. **Write** the JSON back to disk (client filesystem tool). Open or sync the graph in Nesso.\n\nExample prompt:\n\n> Read `graphs/biology.json` in this workspace, validate it with Nesso MCP, then use `build_graph` to add concepts \"Chloroplast\" and \"Glucose\" linked by `produces`, merge the result, re-validate, and write the file back.\n\nYou can also build graphs directly in [app.nesso.how](https://app.nesso.how) or import JSON via **Graph menu → Import JSON**.\n\n## Tools reference\n\n### `get_nesso_docs`\n\nFetches documentation pages from this site. Call it without a `slug` to get a table of contents, or with a slug (e.g. `\"guides/getting-started\"`) to get the full page content.\n\n### `get_relation_types`\n\nReturns the complete list of relation types grouped by category id (`taxonomic`, `structural`, … from `RELATION_CATEGORIES` in `@nesso-how/vocab-learning`), with line style, symmetry, and type properties (transitive, inverse, strength, polarity, cardinality). Use this whenever you need valid type names for graph JSON or explanations for the learner.\n\n### `validate_graph`\n\n**Input:** `{ \"graph\": \"<json string>\" }` — a Nesso graph **document** (`concepts[]`, `relations[]`), not runtime React Flow `nodes`/`edges`.\n\n**Output:** `{ \"valid\": boolean, \"errors\": [{ \"path\", \"message\" }], \"warnings\": [{ \"path\", \"message\" }] }`\n\nRuns envelope and vocabulary validation plus structural checks the deserializer does not cover today: duplicate ids, dangling relation endpoints, unknown relation types. Warnings flag missing `vocabulary` or relation `type` (the app falls back to `causes` at render time).\n\n`valid` is `true` only when `errors` is empty.\n\n### `build_graph`\n\n**Input:**\n\n```json\n{\n \"name\": \"Photosynthesis\",\n \"concepts\": [\n \"Sunlight\",\n {\n \"text\": \"Chloroplast\",\n \"elaboration\": { \"definition\": \"...\", \"examples\": \"...\", \"notes\": \"\" }\n }\n ],\n \"relations\": [{ \"from\": \"Sunlight\", \"to\": \"Chloroplast\", \"relation\": \"enables\" }]\n}\n```\n\n- `concepts` — label strings or objects with optional `id`, `text`, and `elaboration`.\n- `relations` — `from` / `to` match concept `id` or label (must be unambiguous); `relation` must be a valid type id from `get_relation_types`.\n\n**Output:** Pretty-printed graph document JSON ready to write to disk (includes `vocabulary`, generated ids, and dagre layout positions). FSRS review fields and React Flow edge shape (`type: \"nesso\"`, handles) are applied by the app on import, not stored in the file."
43
+ "markdown": "The `@nesso-how/mcp` package is a [Model Context Protocol](https://modelcontextprotocol.io/) server that gives AI clients access to Nesso documentation, the full relation-type vocabulary, and tools to **build** and **validate** graph documents. Once connected, models can answer questions about how Nesso works, produce importable graph JSON, and check files before writing them back to disk.\n\n## Setup\n\n### Claude Desktop\n\nOpen `claude_desktop_config.json`. On macOS it lives at `~/Library/Application Support/Claude/claude_desktop_config.json`; on Windows at `%APPDATA%\\Claude\\claude_desktop_config.json`.\n\nAdd a `nesso` entry under `mcpServers`:\n\n```json\n{\n \"mcpServers\": {\n \"nesso\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@nesso-how/mcp\"]\n }\n }\n}\n```\n\nSave the file and restart Claude Desktop. The Nesso tools will be available to the model automatically.\n\n### Cursor\n\nOpen **Settings -> MCP** and add a new server with command `npx` and args `[\"-y\", \"@nesso-how/mcp\"]`.\n\n### Other MCP clients\n\nAny client that speaks the stdio MCP transport works. Run `npx -y @nesso-how/mcp` as the server command. No other configuration is required.\n\n## What it can do\n\nOnce connected, you can ask your AI client things like:\n\n- \"What relation types does Nesso support?\" (uses `get_relation_types`)\n- \"Show me the Nesso getting started guide\" (uses `get_nesso_docs`)\n- \"Build a graph about photosynthesis with causes and enables relations\" (uses `build_graph`)\n- \"Validate this graph JSON before I save it\" (uses `validate_graph`)\n\nThe MCP server is **stateless** and does not read or write files. Your client uses its own filesystem tools to read `.json` graph files, passes the contents inline to `validate_graph` or `build_graph`, and writes the result back. Nesso picks up external edits through its normal workspace sync.\n\n### Agent workflow for graph files\n\nA typical end-to-end flow when the client has filesystem access:\n\n1. **Read** the target graph `.json` from the project folder (client filesystem tool).\n2. **Validate** with `validate_graph`. Fix any `errors` before saving. Review `warnings` (e.g. missing `vocabulary` or relation `type`).\n3. **Build or extend** with `build_graph` when creating a new graph from structured concepts and relations: the tool assigns ids, vocabulary metadata, valid relation types, and layout positions.\n4. **Re-validate** the output with `validate_graph` if the client edited the JSON by hand.\n5. **Write** the JSON back to disk (client filesystem tool). Open or sync the graph in Nesso.\n\nExample prompt:\n\n> Read `graphs/biology.json` in this workspace, validate it with Nesso MCP, then use `build_graph` to add concepts \"Chloroplast\" and \"Glucose\" linked by `produces`, merge the result, re-validate, and write the file back.\n\nSee [MCP tools](../../reference/mcp-tools/) for the full tool reference."
32
44
  },
33
45
  {
34
46
  "slug": "guides/review-mode",
35
47
  "title": "Review mode",
36
48
  "description": "Spaced-repetition review of your concepts with FSRS, how it queues cards and reschedules them from your self-ratings.",
37
- "markdown": "Press `R` (or open the **Review** pill in the top bar) to start a focused study session. Review surfaces concepts whose **FSRS** state says they are due, prompts you to recall what you know, and reschedules them based on how you self-rate.\n\nNesso uses [FSRS](https://github.com/open-spaced-repetition/ts-fsrs), a modern open-source successor to the SM-2 / Anki algorithm.\n\n## What gets reviewed\n\nEach concept node carries its own FSRS state: `stability`, `difficulty`, `due`, `lastReview`, `lastRating`, and friends. A node is **due** when `due <= now`. New, unrated concepts default to `due = 0`, which means they show up immediately the first time you open Review.\n\nThe session queue is built fresh every time you open the overlay: all due nodes, sorted by urgency, in random order within the same due bucket.\n\n## Flow\n\nFor each due concept, Review:\n\n1. **Shows the concept title** and a short recall prompt. Try to remember its definition, examples, and typed relations before revealing.\n2. **Waits for you to think**, then click **Reveal** (or press `Space`).\n3. **Reveals** the concept's typed relations, examples, and image (the same panel you see in the Inspector).\n4. **You rate** how it felt: `Again`, `Hard`, `Good`, `Easy`. Each button shows the **predicted next interval** under it (e.g. `< 1d`, `4d`, `2mo`).\n\nFSRS then updates `stability` and `difficulty`, schedules the next `due` date, and Review advances to the next card. Done with the queue, the overlay closes; you're caught up.\n\n## Tuning FSRS\n\nUnder **Settings -> Learning -> Review**:\n\n| Setting | What it does | Range |\n| -------------------- | ---------------------------------------------------------------------------------------------------- | ----------- |\n| **Target retention** | Probability of correctly recalling a concept at its next review. Higher means more frequent reviews. | 70% to 97% |\n| **Max interval** | Longest interval FSRS can schedule, in days. Caps how far into the future a card can be pushed. | 1 to 36,500 |\n\nThe defaults (90% retention, 100-year cap) match the FSRS reference defaults. Lower the retention if you're comfortable forgetting more in exchange for fewer reviews; raise the max interval if you want long-term cards to keep stretching out.\n\nReview is on by default. The **Review mode** toggle at the top of **Settings -> Learning** turns it off entirely: the **Review** pill and the `R` shortcut disappear, and the FSRS settings above hide while it is off.\n\n## Keyboard shortcuts\n\n| Key | Action |\n| ----------------- | ------------ |\n| `Space` / `Enter` | Reveal |\n| `1` | Again |\n| `2` | Hard |\n| `3` | Good |\n| `4` | Easy |\n| `Esc` | Close review |\n\nNumeric keys only fire after the answer is revealed.\n\n## Tips\n\n- Add a **definition** and **examples** in the Inspector's Notes tab. They appear on reveal and give you something concrete to check your recall against.\n- Pair concepts with **at least one typed edge** before reviewing them. Relations are part of what you reveal and what makes the graph worth remembering.\n- The session count in the top bar reflects the original queue size. Cards rated `Again` that come back later increase the count past 100%; that is expected."
49
+ "markdown": "Press `R` (or open the **Review** pill in the top bar) to start a focused study session. Review surfaces concepts that are due, prompts you to recall what you know, and reschedules them based on how you self-rate.\n\nNesso uses [FSRS](https://github.com/open-spaced-repetition/ts-fsrs), a modern open-source successor to the SM-2 / Anki algorithm.\n\n## What gets reviewed\n\nEach concept node carries its own FSRS state: `stability`, `difficulty`, `due`, `lastReview`, `lastRating`, and friends. A node is **due** when `due <= now`. New, unrated concepts default to `due = 0`, which means they show up immediately the first time you open Review.\n\nThe session queue is built fresh every time you open the overlay: all due nodes, sorted by urgency, in random order within the same due bucket.\n\n## Flow\n\nFor each due concept, Review:\n\n1. **Shows the concept title** and a short recall prompt. Try to remember its definition, examples, and typed relations before revealing.\n2. **Waits for you to think**, then click **Reveal** (or press `Space`).\n3. **Reveals** the concept's typed relations, examples, and image (the same panel you see in the Inspector).\n4. **You rate** how it felt: `Again`, `Hard`, `Good`, `Easy`. Each button shows the **predicted next interval** under it (e.g. `< 1d`, `4d`, `2mo`).\n\nFSRS then updates `stability` and `difficulty`, schedules the next `due` date, and Review advances to the next card. Done with the queue, the overlay closes. You're caught up.\n\n## Tuning FSRS\n\nUnder **Settings -> Learning -> Review**:\n\n| Setting | What it does | Range |\n| -------------------- | ---------------------------------------------------------------------------------------------------- | ----------- |\n| **Target retention** | Probability of correctly recalling a concept at its next review. Higher means more frequent reviews. | 70% to 97% |\n| **Max interval** | Longest interval FSRS can schedule, in days. Caps how far into the future a card can be pushed. | 1 to 36,500 |\n\nThe defaults (90% retention, 100-year cap) match the FSRS reference defaults. Lower the retention if you're comfortable forgetting more in exchange for fewer reviews. Raise the max interval if you want long-term cards to keep stretching out.\n\nReview is on by default. The **Review mode** toggle at the top of **Settings -> Learning** turns it off entirely: the **Review** pill and the `R` shortcut disappear, and the FSRS settings above hide while it is off.\n\n## Tips\n\n- Add a **definition** and **examples** in the Inspector's Notes tab. They appear on reveal and give you something concrete to check your recall against.\n- Pair concepts with **at least one typed edge** before reviewing them. Relations are part of what you reveal and what makes the graph worth remembering.\n- The session count in the top bar reflects the original queue size. Cards rated `Again` that come back later increase the count past 100%. That is expected."
38
50
  },
39
51
  {
40
52
  "slug": "introduction",
41
53
  "title": "Introduction",
42
54
  "description": "What Nesso is, why it exists, and the principles it is built on.",
43
- "markdown": ":::caution\nThis is an early-stage project. Some features are rough, some are not yet built, and this documentation is just getting started too.\n:::\n\nNesso is an open-source app for building typed, AI-assisted knowledge graphs for active learning. It is built on a specific claim about how understanding works and a specific critique of how most current tools approach it.\n\n## The problem with passive learning tools\n\nPassive learning is not a new problem. AI has made it the default and amplified it at scale: You hand over a source and receive a summary, ask a question and receive an answer, or describe what you want to learn and receive a pre-built map. This is convenient, and pedagogically counterproductive. Decades of cognitive science converge on the same conclusion: deep understanding is not received; it is constructed. When the work of deciding how concepts relate is offloaded to a system, the process that produces comprehension is bypassed.\n\nAlongside this, most learning platforms treat user data as a resource. In the context of learning, this data is uniquely sensitive: it reveals not just what someone has read, but how they reason, where they struggle, and how their understanding evolves over time. Capturing it passively, and often opaquely, is at odds with the interests of the people the tools claim to serve.\n\nFinally, most platforms are proprietary silos: closed standards, locked formats, no way to inspect or extend them. Educators, developers, and learners themselves have no meaningful recourse when a platform makes choices they disagree with or stops serving their needs.\n\n## What Nesso does instead\n\nNesso inverts the flow. The learner constructs their own knowledge structure: a typed concept graph that reflects how _they_ understand, not just what they have consumed. The act of deciding which relation holds between two concepts (does X _cause_ Y, or merely _enable_ it? is A an _instance of_ B, or a _subtype of_ it?) is where elaborative processing happens. The decision is the learning.\n\nAlgorithms work on what the learner has built, not on a generic curriculum. Spaced repetition is driven by graph structure: concepts with low stability or untested connections surface before well-reinforced ones. The review queue is always a function of the learner's own map.\n\nAn optional AI mentor, Socrates, can probe what you have built. It asks questions calibrated to your current graph rather than delivering answers. See [AI mentor (Socrates)](./guides/ai-mentor/) for setup and behaviour.\n\n## Principles\n\n**Constructivist by design.** Every feature is oriented around the learner doing cognitive work: drawing edges, labelling relations, writing definitions in their own words, self-rating recall. The system does not do this work for them.\n\n**Private by architecture.** In the web app, graphs are stored locally in your browser. In the desktop app, they are also saved as plain JSON files on your machine. Your graph content and definitions stay on your device. Mentor chat is session-only in the app; if you enable the mentor and use a remote AI endpoint, prompts are sent to that provider each turn. Optional telemetry (anonymous crash reports and aggregated usage events) is off by default and opt-in from **Settings Privacy**; it never includes graph content, chat, or keys. Privacy is an implementation detail, not a policy promise.\n\n**Open by default.** The code is MIT-licensed. Data formats are documented and importable/exportable as plain JSON. The MCP server makes the graph vocabulary available to any compatible client. Technical work done here is intended to be useful beyond this application.\n\n**Provider-agnostic AI.** The mentor talks to any OpenAI-compatible `chat/completions` endpoint. You choose whether to run a model locally or connect a remote provider; no vendor is privileged by the architecture.\n\n## What Nesso is not\n\nNesso is not a note-taking app. It does not replace a text editor, a spaced-repetition deck manager, or a general-purpose LLM interface. It is specifically a tool for the phase of learning where understanding a domain means deciding how its concepts relate to each other, and testing whether you can hold that structure under questioning.\n\nIt is also not a finished product. The codebase is publicly available for inspection and contribution.\n\n---\n\nThe remainder of this documentation covers how to use the app and how to integrate with it programmatically. If you want to start immediately, [Getting started](./guides/getting-started/) has everything you need."
55
+ "markdown": ":::caution\nThis is an early-stage project. Some features are rough, some are not yet built.\n:::\n\nNesso is an open-source app for building typed knowledge graphs for active learning. It is built on a specific claim about how understanding works and a specific critique of how most current tools approach it.\n\n## The problem with passive learning tools\n\nPassive learning is not a new problem, but AI has made it the default and amplified it at scale: you hand over a source and receive a summary, ask a question and receive an answer, or describe what you want to learn and receive a pre-built map. This is convenient, and pedagogically counterproductive. Decades of cognitive science converge on the same conclusion: deep understanding is not received; it is constructed. When the work of deciding how concepts relate is offloaded to a system, the process that produces comprehension is bypassed.\n\nAlongside this, most learning platforms treat user data as a resource. In the context of learning, this data is uniquely sensitive: it reveals not just what someone has read, but how they reason, where they struggle, and how their understanding evolves over time. Capturing it passively, and often opaquely, is at odds with the interests of the people the tools claim to serve.\n\nFinally, most platforms are proprietary silos: closed standards, locked formats, no way to inspect or extend them. Educators, developers, and learners themselves have no meaningful recourse when a platform makes choices they disagree with or stops serving their needs.\n\n## What Nesso does instead\n\nNesso inverts the flow. The learner constructs their own knowledge structure: a typed concept graph that reflects how _they_ understand, not just what they have consumed. The act of deciding which relation holds between two concepts (does X _cause_ Y, or merely _enable_ it? is A an _instance of_ B, or a _subtype of_ it?) is where elaborative processing happens. The decision is the learning.\n\nAlgorithms work on what the learner has built, not on a generic curriculum. Spaced repetition is driven by graph structure: concepts with low stability or untested connections surface before well-reinforced ones. The review queue is always a function of the learner's own map.\n\nAn optional AI mentor, Socrates, can probe what you have built. It asks questions calibrated to your current graph rather than delivering answers. See [AI mentor (Socrates)](../guides/ai-mentor/) for setup and behaviour.\n\n## Principles\n\n**Constructivist by design.** Every feature is oriented around the learner doing cognitive work: drawing edges, labelling relations, writing definitions in their own words, self-rating recall. The system does not do this work for them.\n\n**Open by default.** The code is MIT-licensed. The graph schema is documented, vocabulary-agnostic, and importable/exportable as plain JSON. The Nesso Learning Vocabulary it ships with is a declared, replaceable package. The MCP server makes the graph vocabulary available to any compatible client. Technical work done here is and will be intended to be useful beyond this application.\n\n**Private by architecture.** In the web app, graphs are stored locally in your browser. In the desktop app, they are also saved as plain JSON files on your machine. Your graph content and definitions stay on your device. Mentor chat is session-only in the app. If you enable the mentor and use a remote AI endpoint, prompts are sent to that provider each turn. Optional telemetry (anonymous crash reports and aggregated usage events) is off by default and opt-in from settings. It never includes graph content, chat, or keys.\n\n## What Nesso is not\n\nNesso is not a note-taking app. It does not replace a text editor, a spaced-repetition deck manager, or a general-purpose LLM interface. It is specifically a tool for the phase of learning where understanding a domain means deciding how its concepts relate to each other, and testing whether you can hold that structure under questioning.\n\nIt is also not a finished product. The codebase is publicly available for inspection and contribution.\n\n---\n\nThe remainder of this documentation covers how to use the app and how to integrate with it programmatically. If you want to start immediately, [Getting started](../guides/getting-started/) has everything you need."
56
+ },
57
+ {
58
+ "slug": "reference/mcp-tools",
59
+ "title": "MCP tools",
60
+ "description": "Reference for the four tools exposed by the @nesso-how/mcp server.",
61
+ "markdown": "The `@nesso-how/mcp` server exposes four tools. See [MCP](../../guides/mcp/) for setup and workflow.\n\n### `get_nesso_docs`\n\nFetches documentation pages from this site. Call it without a `slug` to get a table of contents, or with a slug (e.g. `\"guides/getting-started\"`) to get the full page content.\n\n### `get_relation_types`\n\nReturns the complete list of relation types grouped by category id (`taxonomic`, `structural`, and so on from `RELATION_CATEGORIES` in `@nesso-how/vocab-learning`), with line style, symmetry, and type properties (transitive, inverse, strength, polarity, cardinality). Use this whenever you need valid type names for graph JSON or explanations for the learner.\n\n### `validate_graph`\n\n**Input:** `{ \"graph\": \"<json string>\" }`: a Nesso graph document (`concepts[]`, `relations[]`), not runtime React Flow `nodes`/`edges`.\n\n**Output:** `{ \"valid\": boolean, \"errors\": [{ \"path\", \"message\" }], \"warnings\": [{ \"path\", \"message\" }] }`\n\nRuns envelope and vocabulary validation plus structural checks the deserializer does not cover today: duplicate ids, dangling relation endpoints, unknown relation types. Warnings flag missing `vocabulary` or relation `type` (the app falls back to `causes` at render time).\n\n`valid` is `true` only when `errors` is empty.\n\n### `build_graph`\n\n**Input:**\n\n```json\n{\n \"name\": \"Photosynthesis\",\n \"concepts\": [\n \"Sunlight\",\n {\n \"text\": \"Chloroplast\",\n \"elaboration\": { \"definition\": \"...\", \"examples\": \"...\", \"notes\": \"\" }\n }\n ],\n \"relations\": [{ \"from\": \"Sunlight\", \"to\": \"Chloroplast\", \"relation\": \"enables\" }]\n}\n```\n\n- `concepts`: label strings or objects with optional `id`, `text`, and `elaboration`.\n- `relations`: `from` / `to` match concept `id` or label (must be unambiguous); `relation` must be a valid type id from `get_relation_types`.\n\n**Output:** Pretty-printed graph document JSON ready to write to disk (includes `vocabulary`, generated ids, and dagre layout positions). FSRS review fields and React Flow edge shape (`type: \"nesso\"`, handles) are applied by the app on import, not stored in the file."
44
62
  },
45
63
  {
46
64
  "slug": "reference/relation-types",
47
65
  "title": "Relation types",
48
66
  "description": "The 52 semantic relation types across 8 categories in Nesso.",
49
- "markdown": "In Nesso, every edge carries a **semantic type**: a named relation describing how two concepts are connected, with type properties reserved for graph-analysis algorithms (future work, no algorithm currently consumes them). The vocabulary is fixed at 52 types across 8 categories, drawn from prior work in knowledge representation, lexical semantics, temporal logic, and signed-network theory.\n\n:::note[About this vocabulary]\nThese 52 relation types are one slice of `@nesso-how/vocab-learning`, which also defines node parameters (FSRS), display settings, and category palettes. Graph JSON files declare their vocabulary via `vocabulary.id` and `vocabulary.version`; the envelope format (`@nesso-how/schema`) is vocabulary-agnostic. The types and their property values are a considered first cut — both will evolve as real graphs accumulate and the analysis algorithms that consume them get built out.\n:::\n\n## Properties\n\nEach relation type declares the properties below. They define the contract that graph-analysis algorithms will consume; closing the enum guarantees every type comes with them, where a user-defined type would arrive without and stay analytically opaque.\n\n- **Transitive (T)**: `Y` (strict), `N` (none), or `weak` (transitivity with decay; algorithms may discount per step).\n- **Inverse (I)**: the canonical inverse type in the set; `self` for symmetric relations. Asymmetric relations declare it explicitly so traversal is first-class in both directions. The explicit-inverse design follows knowledge-graph embedding work <a id=\"cite-1\" href=\"#ref-1\">[1]</a>, which lists symmetry, antisymmetry, inversion, and composition as the four properties a good relation set should support.\n- **Strength (S)**: per-type semantic weight in `0..1`. Encodes how \"tight\" the relation is in general (e.g. `defines` 0.90 vs `similar-to` 0.40), not how sure the user is about a specific edge. The idea that different relation types carry different semantic distance comes from lexical-taxonomy weighting schemes <a id=\"cite-2\" href=\"#ref-2\">[2]</a>, <a id=\"cite-3\" href=\"#ref-3\">[3]</a>.\n- **Polarity (P)**: `+1` positive effect, `-1` antagonistic, `0` neutral/structural. From signed-network theory <a id=\"cite-4\" href=\"#ref-4\">[4]</a>: with polarity, the graph becomes a signed network where balance and cycle-sign analyses can apply.\n- **Cardinality (C)**: expected mapping pattern, always declared. One of `1-1`, `1-N`, `N-1`, `N-N` (no a-priori constraint). Setting this consistently lets algorithms flag structural anomalies (e.g. two competing `defines` edges into the same term).\n\n## Categories\n\nEach category answers a specific question about the relation. Grouping the types by question makes the vocabulary easier to navigate when authoring a graph, and lets graph-analysis algorithms compare and aggregate at category level instead of only per individual type.\n\n### Taxonomic\n\n_What kind of thing is it?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | -------------- | --- | ---- | --- | --- |\n| `subtype-of` | subtype of | `has-subtype` | Y | 0.90 | 0 | N-1 |\n| `has-subtype` | has subtype | `subtype-of` | Y | 0.90 | 0 | 1-N |\n| `instance-of` | instance of | `has-instance` | N | 0.95 | 0 | N-1 |\n| `has-instance` | has instance | `instance-of` | N | 0.95 | 0 | 1-N |\n\nTaxonomic relations answer the simplest question you can ask about a concept: what kind of thing is it? Nesso splits the answer into two layers that look alike at a glance but behave very differently under reasoning. The class-vs-instance distinction mirrors OWL/RDFS <a id=\"cite-5\" href=\"#ref-5\">[5]</a>: `subtype-of` corresponds to `rdfs:subClassOf` (one class refines another, as a sparrow refines bird), while `instance-of` corresponds to `rdf:type` (an individual belongs to a class, as Tweety belongs to sparrow). Inheritance flows freely through the subtype chain, but instances are leaves: Tweety is a sparrow and through that a bird, yet Tweety is not itself \"a kind of\" anything.\n\n### Structural\n\n_What is it made of or composed from?_\n\n| Type | Label | I | T | S | P | C |\n| ---------- | -------- | ---------- | ---- | ---- | --- | --- |\n| `part-of` | part of | `contains` | Y | 0.85 | 0 | N-1 |\n| `contains` | contains | `part-of` | Y | 0.85 | 0 | 1-N |\n| `made-of` | made of | `composes` | weak | 0.75 | 0 | N-N |\n| `composes` | composes | `made-of` | weak | 0.75 | 0 | N-N |\n\nStructural relations describe how a thing decomposes into its parts. The category covers two patterns. `part-of` and its inverse `contains` capture discrete structural decomposition: an engine is part of a car, a paragraph is part of a chapter, and transitivity flows cleanly through the chain. `made-of` and `composes` capture material or substantive composition instead: water is made of hydrogen and oxygen, a chair is made of wood. Transitivity there is only weak, because what something is made of doesn't always propagate in a meaningful way (a chair made of wood, made of cellulose, made of carbon, made of atoms dilutes the relationship as the chain grows).\n\n### Causal\n\n_What does it do or prevent?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | -------------- | ---- | ---- | --- | --- |\n| `causes` | causes | `caused-by` | N | 0.85 | +1 | N-N |\n| `caused-by` | caused by | `causes` | N | 0.85 | +1 | N-N |\n| `produces` | produces | `produced-by` | N | 0.70 | +1 | N-N |\n| `produced-by` | produced by | `produces` | N | 0.70 | +1 | N-N |\n| `enables` | enables | `enabled-by` | weak | 0.60 | +1 | N-N |\n| `enabled-by` | enabled by | `enables` | weak | 0.60 | +1 | N-N |\n| `prevents` | prevents | `prevented-by` | N | 0.85 | −1 | N-N |\n| `prevented-by` | prevented by | `prevents` | N | 0.85 | −1 | N-N |\n| `triggers` | triggers | `triggered-by` | N | 0.70 | +1 | N-N |\n| `triggered-by` | triggered by | `triggers` | N | 0.70 | +1 | N-N |\n| `inhibits` | inhibits | `inhibited-by` | N | 0.55 | −1 | N-N |\n| `inhibited-by` | inhibited by | `inhibits` | N | 0.55 | −1 | N-N |\n| `disables` | disables | `disabled-by` | weak | 0.60 | −1 | N-N |\n| `disabled-by` | disabled by | `disables` | weak | 0.60 | −1 | N-N |\n| `consumes` | consumes | `consumed-by` | N | 0.65 | −1 | N-N |\n| `consumed-by` | consumed by | `consumes` | N | 0.65 | −1 | N-N |\n| `delays` | delays | `delayed-by` | weak | 0.55 | −1 | N-N |\n| `delayed-by` | delayed by | `delays` | weak | 0.55 | −1 | N-N |\n\nCausal is the largest category in Nesso, because causation in the real world doesn't come in a single flavor. On the positive side, `causes` describes direct generation of an outcome, `triggers` describes the initiation of something that then plays out on its own (a spark triggers an explosion), and `enables` describes making something possible without forcing it. The negative side mirrors this: `prevents` is total blockage, `inhibits` is partial reduction, and `disables` is switching off a capacity or function. Intensity and mechanism live at the type level rather than as per-edge weights, so choosing between `inhibits` and `prevents` is a semantic decision about what is actually happening, not about how confident the author is.\n\n`consumes` and `delays` round out the category. `consumes` captures resource destruction, which is causal rather than dependency-flavored: it is distinct from `uses`, where the resource survives the interaction. `delays` carries negative polarity because slowing or postponing an outcome hinders it, even though nothing is destroyed.\n\n### Dependency\n\n_What does it need or serve?_\n\n| Type | Label | I | T | S | P | C |\n| ------------- | ----------- | ------------- | ---- | ---- | --- | --- |\n| `requires` | requires | `required-by` | Y | 0.85 | 0 | N-N |\n| `required-by` | required by | `requires` | Y | 0.85 | 0 | N-N |\n| `uses` | uses | `used-by` | weak | 0.50 | 0 | N-N |\n| `used-by` | used by | `uses` | weak | 0.50 | 0 | N-N |\n| `used-for` | used for | `purpose-of` | N | 0.55 | +1 | N-N |\n| `purpose-of` | purpose of | `used-for` | N | 0.55 | +1 | N-N |\n\nDependency relations capture what a concept _needs_ rather than what causes it. A car requires an engine to function, but the engine doesn't cause the car, and that difference shows up in how the graph traverses these edges. `requires` and `required-by` are the hard form, where the dependency is essential and transitivity is strict: if A requires B and B requires C, A also requires C. `uses` and `used-by` are softer, capturing a working relationship that doesn't necessarily imply the user can't survive without it, so transitivity decays through the chain. `used-for` and `purpose-of` are teleological: they point at the goal or function a thing serves (a hammer is used for driving nails). Cardinality there stays open at N-N, since one tool can serve many purposes and many tools can share a single purpose.\n\n### Temporal\n\n_When or where does it happen?_\n\n| Type | Label | I | T | S | P | C |\n| ---------------- | -------------- | ---------------- | --- | ---- | --- | --- |\n| `precedes` | precedes | `follows` | Y | 0.50 | 0 | N-N |\n| `follows` | follows | `precedes` | Y | 0.50 | 0 | N-N |\n| `occurs-in` | occurs in | `has-occurrence` | Y | 0.40 | 0 | N-1 |\n| `has-occurrence` | has occurrence | `occurs-in` | Y | 0.40 | 0 | 1-N |\n| `during` | during | `spans` | Y | 0.55 | 0 | N-1 |\n| `spans` | spans | `during` | Y | 0.55 | 0 | 1-N |\n| `overlaps-with` | overlaps with | self (symmetric) | N | 0.45 | 0 | N-N |\n| `derives-from` | derives from | `gives-rise-to` | Y | 0.70 | 0 | N-1 |\n| `gives-rise-to` | gives rise to | `derives-from` | Y | 0.70 | 0 | 1-N |\n\nTemporal relations describe when things happen relative to each other and how an event sits inside a larger period. Allen's interval algebra <a id=\"cite-6\" href=\"#ref-6\">[6]</a> inspires the containment pair: `during` and `spans` model intervals nested inside other intervals (the medieval period spans roughly a thousand years, and Charlemagne's reign was during it). `occurs-in` and its inverse work at a different scale, pinning a quasi-point event to the period it falls inside (the moon landing occurs in 1969). `precedes` and `follows` cover plain sequence without nesting, and `overlaps-with` is the symmetric case for two intervals that share a stretch of time without one containing the other.\n\n`derives-from` and `gives-rise-to` are not just about chronology, they capture genealogical descent: a transformative continuity where something becomes something else over time. Languages, species, and ideas all have lineages of this kind. The relation is close to `caused-by` but distinct, because causation is direct influence without requiring the cause to _become_ the effect. It is also distinct from taxonomic `subtype-of`, which is a snapshot of class membership rather than a historical claim. Italian derives from Latin, but Italian is not a subtype of Latin in the modern sense.\n\n### Opposition\n\n_What does it contrast with?_\n\n| Type | Label | I | T | S | P | C |\n| ---------------- | -------------- | ---------------- | --- | ---- | --- | --- |\n| `contrasts-with` | contrasts with | self (symmetric) | N | 0.50 | −1 | N-N |\n| `opposite-of` | opposite of | self (symmetric) | N | 0.80 | −1 | 1-1 |\n\nOpposition is the category for concepts that stand against each other. The two types differ in strength. `contrasts-with` is the weaker form, where two concepts highlight each other by sitting at different points on some dimension (warm contrasts with cool, North contrasts with South). `opposite-of` is the canonical, often binary opposite (alive is the opposite of dead, true is the opposite of false), and its cardinality is 1-1 because a canonical opposite is unique. Both are symmetric: if A is the opposite of B, then B is the opposite of A by definition.\n\n### Similarity\n\n_What is it like?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | ---------------- | ---- | ---- | --- | --- |\n| `similar-to` | similar to | self (symmetric) | weak | 0.40 | +1 | N-N |\n| `analogous-to` | analogous to | self (symmetric) | N | 0.30 | +1 | N-N |\n\nThe similarity category includes two related but distinct relations. `similar-to` is the looser one, where two concepts share enough properties to be grouped together (lions are similar to tigers). `analogous-to` is more structural: the two concepts aren't necessarily alike in their properties, but their roles or relationships mirror each other (an electron orbiting an atom is analogous to a planet orbiting the sun). Both are symmetric, and both carry positive polarity because finding similarity or analogy is usually a constructive move in reasoning.\n\n### Epistemic\n\n_How do we know?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | ---------------- | ---- | ---- | --- | --- |\n| `supports` | supports | `supported-by` | weak | 0.70 | +1 | N-N |\n| `supported-by` | supported by | `supports` | weak | 0.70 | +1 | N-N |\n| `contradicts` | contradicts | self (symmetric) | N | 0.75 | −1 | N-N |\n| `explains` | explains | `explained-by` | weak | 0.80 | 0 | N-N |\n| `explained-by` | explained by | `explains` | weak | 0.80 | 0 | N-N |\n| `defines` | defines | `defined-by` | N | 0.90 | 0 | 1-1 |\n| `defined-by` | defined by | `defines` | N | 0.90 | 0 | 1-1 |\n\nThe epistemic category is where Nesso models reasoning about claims rather than facts about the world. `supports` and `supported-by` connect a piece of evidence to the claim it bolsters; `explains` and `explained-by` connect an explanans (the explanatory account) to its explanandum (what is being explained). Both pairs are asymmetric, because evidence points to a claim and an explanation is not equivalent to what it explains.\n\n`defines` is the most rigid relation in this category. It goes from the defining expression (the _definiens_) to the term being defined (the _definiendum_): in \"F = ma defines force\", the equation is the definiens and `force` is the definiendum. Cardinality 1-1 enforces a single canonical definition per term, so two competing `defines` edges into the same concept signal a real ambiguity worth resolving.\n\n`contradicts` is the only symmetric relation in the category, because logical incompatibility goes both ways: if A contradicts B, then B equally contradicts A. This distinguishes it from `supports` and `explains`, which always point in a particular direction.\n\n## References\n\n1. <a id=\"ref-1\"></a>Sun, Z., Deng, Z.-H., Nie, J.-Y., and Tang, J. [_RotatE: Knowledge Graph Embedding by Relational Rotation in Complex Space_](https://arxiv.org/abs/1902.10197). ICLR, 2019. [↑](#cite-1)\n2. <a id=\"ref-2\"></a>Sussna, M. [_Word sense disambiguation for free-text indexing using a massive semantic network_](https://doi.org/10.1145/170088.170106). CIKM '93, 1993. [↑](#cite-2)\n3. <a id=\"ref-3\"></a>Jiang, J. J. and Conrath, D. W. [_Semantic similarity based on corpus statistics and lexical taxonomy_](https://arxiv.org/abs/cmp-lg/9709008). ROCLING X, 1997. [↑](#cite-3)\n4. <a id=\"ref-4\"></a>Cartwright, D. and Harary, F. [_Structural balance: A generalization of Heider's theory_](https://doi.org/10.1037/h0046049). Psychological Review, 63(5):277–293, 1956. [↑](#cite-4)\n5. <a id=\"ref-5\"></a>W3C. [_OWL 2 Web Ontology Language Primer (Second Edition)_](https://www.w3.org/TR/owl2-primer/). 2012. [↑](#cite-5)\n6. <a id=\"ref-6\"></a>Allen, J. F. [_Maintaining knowledge about temporal intervals_](https://doi.org/10.1145/182.358434). Communications of the ACM, 26(11):832–843, 1983. [↑](#cite-6)"
67
+ "markdown": "In Nesso, every edge carries a **semantic type**: a named relation describing how two concepts are connected, with type properties reserved for graph-analysis algorithms (future work, no algorithm currently consumes them). The vocabulary is fixed at 52 types across 8 categories, drawn from prior work in knowledge representation, lexical semantics, temporal logic, and signed-network theory.\n\n:::note[About this vocabulary]\nThese 52 relation types are one slice of `@nesso-how/vocab-learning`, which also defines node parameters (FSRS), display settings, and category palettes. Graph JSON files declare their vocabulary via `vocabulary.id` and `vocabulary.version`; the envelope schema (`@nesso-how/schema`) is vocabulary-agnostic. The types and their property values are a considered first cut, and both will evolve as real graphs accumulate and the analysis algorithms that consume them get built out.\n:::\n\n## Properties\n\nEach relation type declares the properties below. They define the contract that graph-analysis algorithms will consume; closing the enum guarantees every type comes with them, where a user-defined type would arrive without and stay analytically opaque.\n\n- **Transitive (T)**: `Y` (strict), `N` (none), or `weak` (transitivity with decay; algorithms may discount per step).\n- **Inverse (I)**: the canonical inverse type in the set; `self` for symmetric relations. Asymmetric relations declare it explicitly so traversal is first-class in both directions. The explicit-inverse design follows knowledge-graph embedding work <a id=\"cite-1\" href=\"#ref-1\">[1]</a>, which lists symmetry, antisymmetry, inversion, and composition as the four properties a good relation set should support.\n- **Strength (S)**: per-type semantic weight in `0..1`. Encodes how \"tight\" the relation is in general (e.g. `defines` 0.90 vs `similar-to` 0.40), not how sure the user is about a specific edge. The idea that different relation types carry different semantic distance comes from lexical-taxonomy weighting schemes <a id=\"cite-2\" href=\"#ref-2\">[2]</a>, <a id=\"cite-3\" href=\"#ref-3\">[3]</a>.\n- **Polarity (P)**: `+1` positive effect, `-1` antagonistic, `0` neutral/structural. From signed-network theory <a id=\"cite-4\" href=\"#ref-4\">[4]</a>: with polarity, the graph becomes a signed network where balance and cycle-sign analyses can apply.\n- **Cardinality (C)**: expected mapping pattern, always declared. One of `1-1`, `1-N`, `N-1`, `N-N` (no a-priori constraint). Setting this consistently lets algorithms flag structural anomalies (e.g. two competing `defines` edges into the same term).\n\n## Categories\n\nEach category answers a specific question about the relation. Grouping the types by question makes the vocabulary easier to navigate when authoring a graph, and lets graph-analysis algorithms compare and aggregate at category level instead of only per individual type.\n\n### Taxonomic\n\n_What kind of thing is it?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | -------------- | --- | ---- | --- | --- |\n| `subtype-of` | subtype of | `has-subtype` | Y | 0.90 | 0 | N-1 |\n| `has-subtype` | has subtype | `subtype-of` | Y | 0.90 | 0 | 1-N |\n| `instance-of` | instance of | `has-instance` | N | 0.95 | 0 | N-1 |\n| `has-instance` | has instance | `instance-of` | N | 0.95 | 0 | 1-N |\n\nTaxonomic relations answer the simplest question you can ask about a concept: what kind of thing is it? Nesso splits the answer into two layers that look alike at a glance but behave very differently under reasoning. The class-vs-instance distinction mirrors OWL/RDFS <a id=\"cite-5\" href=\"#ref-5\">[5]</a>: `subtype-of` corresponds to `rdfs:subClassOf` (one class refines another, as a sparrow refines bird), while `instance-of` corresponds to `rdf:type` (an individual belongs to a class, as Tweety belongs to sparrow). Inheritance flows freely through the subtype chain, but instances are leaves: Tweety is a sparrow and through that a bird, yet Tweety is not itself \"a kind of\" anything.\n\n### Structural\n\n_What is it made of or composed from?_\n\n| Type | Label | I | T | S | P | C |\n| ---------- | -------- | ---------- | ---- | ---- | --- | --- |\n| `part-of` | part of | `contains` | Y | 0.85 | 0 | N-1 |\n| `contains` | contains | `part-of` | Y | 0.85 | 0 | 1-N |\n| `made-of` | made of | `composes` | weak | 0.75 | 0 | N-N |\n| `composes` | composes | `made-of` | weak | 0.75 | 0 | N-N |\n\nStructural relations describe how a thing decomposes into its parts. The category covers two patterns. `part-of` and its inverse `contains` capture discrete structural decomposition: an engine is part of a car, a paragraph is part of a chapter, and transitivity flows cleanly through the chain. `made-of` and `composes` capture material or substantive composition instead: water is made of hydrogen and oxygen, a chair is made of wood. Transitivity there is only weak, because what something is made of doesn't always propagate in a meaningful way (a chair made of wood, made of cellulose, made of carbon, made of atoms dilutes the relationship as the chain grows).\n\n### Causal\n\n_What does it do or prevent?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | -------------- | ---- | ---- | --- | --- |\n| `causes` | causes | `caused-by` | N | 0.85 | +1 | N-N |\n| `caused-by` | caused by | `causes` | N | 0.85 | +1 | N-N |\n| `produces` | produces | `produced-by` | N | 0.70 | +1 | N-N |\n| `produced-by` | produced by | `produces` | N | 0.70 | +1 | N-N |\n| `enables` | enables | `enabled-by` | weak | 0.60 | +1 | N-N |\n| `enabled-by` | enabled by | `enables` | weak | 0.60 | +1 | N-N |\n| `prevents` | prevents | `prevented-by` | N | 0.85 | −1 | N-N |\n| `prevented-by` | prevented by | `prevents` | N | 0.85 | −1 | N-N |\n| `triggers` | triggers | `triggered-by` | N | 0.70 | +1 | N-N |\n| `triggered-by` | triggered by | `triggers` | N | 0.70 | +1 | N-N |\n| `inhibits` | inhibits | `inhibited-by` | N | 0.55 | −1 | N-N |\n| `inhibited-by` | inhibited by | `inhibits` | N | 0.55 | −1 | N-N |\n| `disables` | disables | `disabled-by` | weak | 0.60 | −1 | N-N |\n| `disabled-by` | disabled by | `disables` | weak | 0.60 | −1 | N-N |\n| `consumes` | consumes | `consumed-by` | N | 0.65 | −1 | N-N |\n| `consumed-by` | consumed by | `consumes` | N | 0.65 | −1 | N-N |\n| `delays` | delays | `delayed-by` | weak | 0.55 | −1 | N-N |\n| `delayed-by` | delayed by | `delays` | weak | 0.55 | −1 | N-N |\n\nCausal is the largest category in Nesso, because causation in the real world doesn't come in a single flavor. On the positive side, `causes` describes direct generation of an outcome, `triggers` describes the initiation of something that then plays out on its own (a spark triggers an explosion), and `enables` describes making something possible without forcing it. The negative side mirrors this: `prevents` is total blockage, `inhibits` is partial reduction, and `disables` is switching off a capacity or function. Intensity and mechanism live at the type level rather than as per-edge weights, so choosing between `inhibits` and `prevents` is a semantic decision about what is actually happening, not about how confident the author is.\n\n`consumes` and `delays` round out the category. `consumes` captures resource destruction, which is causal rather than dependency-flavored: it is distinct from `uses`, where the resource survives the interaction. `delays` carries negative polarity because slowing or postponing an outcome hinders it, even though nothing is destroyed.\n\n### Dependency\n\n_What does it need or serve?_\n\n| Type | Label | I | T | S | P | C |\n| ------------- | ----------- | ------------- | ---- | ---- | --- | --- |\n| `requires` | requires | `required-by` | Y | 0.85 | 0 | N-N |\n| `required-by` | required by | `requires` | Y | 0.85 | 0 | N-N |\n| `uses` | uses | `used-by` | weak | 0.50 | 0 | N-N |\n| `used-by` | used by | `uses` | weak | 0.50 | 0 | N-N |\n| `used-for` | used for | `purpose-of` | N | 0.55 | +1 | N-N |\n| `purpose-of` | purpose of | `used-for` | N | 0.55 | +1 | N-N |\n\nDependency relations capture what a concept _needs_ rather than what causes it. A car requires an engine to function, but the engine doesn't cause the car, and that difference shows up in how the graph traverses these edges. `requires` and `required-by` are the hard form, where the dependency is essential and transitivity is strict: if A requires B and B requires C, A also requires C. `uses` and `used-by` are softer, capturing a working relationship that doesn't necessarily imply the user can't survive without it, so transitivity decays through the chain. `used-for` and `purpose-of` are teleological: they point at the goal or function a thing serves (a hammer is used for driving nails). Cardinality there stays open at N-N, since one tool can serve many purposes and many tools can share a single purpose.\n\n### Temporal\n\n_When or where does it happen?_\n\n| Type | Label | I | T | S | P | C |\n| ---------------- | -------------- | ---------------- | --- | ---- | --- | --- |\n| `precedes` | precedes | `follows` | Y | 0.50 | 0 | N-N |\n| `follows` | follows | `precedes` | Y | 0.50 | 0 | N-N |\n| `occurs-in` | occurs in | `has-occurrence` | Y | 0.40 | 0 | N-1 |\n| `has-occurrence` | has occurrence | `occurs-in` | Y | 0.40 | 0 | 1-N |\n| `during` | during | `spans` | Y | 0.55 | 0 | N-1 |\n| `spans` | spans | `during` | Y | 0.55 | 0 | 1-N |\n| `overlaps-with` | overlaps with | self (symmetric) | N | 0.45 | 0 | N-N |\n| `derives-from` | derives from | `gives-rise-to` | Y | 0.70 | 0 | N-1 |\n| `gives-rise-to` | gives rise to | `derives-from` | Y | 0.70 | 0 | 1-N |\n\nTemporal relations describe when things happen relative to each other and how an event sits inside a larger period. Allen's interval algebra <a id=\"cite-6\" href=\"#ref-6\">[6]</a> inspires the containment pair: `during` and `spans` model intervals nested inside other intervals (the medieval period spans roughly a thousand years, and Charlemagne's reign was during it). `occurs-in` and its inverse work at a different scale, pinning a quasi-point event to the period it falls inside (the moon landing occurs in 1969). `precedes` and `follows` cover plain sequence without nesting, and `overlaps-with` is the symmetric case for two intervals that share a stretch of time without one containing the other.\n\n`derives-from` and `gives-rise-to` are not just about chronology, they capture genealogical descent: a transformative continuity where something becomes something else over time. Languages, species, and ideas all have lineages of this kind. The relation is close to `caused-by` but distinct, because causation is direct influence without requiring the cause to _become_ the effect. It is also distinct from taxonomic `subtype-of`, which is a snapshot of class membership rather than a historical claim. Italian derives from Latin, but Italian is not a subtype of Latin in the modern sense.\n\n### Opposition\n\n_What does it contrast with?_\n\n| Type | Label | I | T | S | P | C |\n| ---------------- | -------------- | ---------------- | --- | ---- | --- | --- |\n| `contrasts-with` | contrasts with | self (symmetric) | N | 0.50 | −1 | N-N |\n| `opposite-of` | opposite of | self (symmetric) | N | 0.80 | −1 | 1-1 |\n\nOpposition is the category for concepts that stand against each other. The two types differ in strength. `contrasts-with` is the weaker form, where two concepts highlight each other by sitting at different points on some dimension (warm contrasts with cool, North contrasts with South). `opposite-of` is the canonical, often binary opposite (alive is the opposite of dead, true is the opposite of false), and its cardinality is 1-1 because a canonical opposite is unique. Both are symmetric: if A is the opposite of B, then B is the opposite of A by definition.\n\n### Similarity\n\n_What is it like?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | ---------------- | ---- | ---- | --- | --- |\n| `similar-to` | similar to | self (symmetric) | weak | 0.40 | +1 | N-N |\n| `analogous-to` | analogous to | self (symmetric) | N | 0.30 | +1 | N-N |\n\nThe similarity category includes two related but distinct relations. `similar-to` is the looser one, where two concepts share enough properties to be grouped together (lions are similar to tigers). `analogous-to` is more structural: the two concepts aren't necessarily alike in their properties, but their roles or relationships mirror each other (an electron orbiting an atom is analogous to a planet orbiting the sun). Both are symmetric, and both carry positive polarity because finding similarity or analogy is usually a constructive move in reasoning.\n\n### Epistemic\n\n_How do we know?_\n\n| Type | Label | I | T | S | P | C |\n| -------------- | ------------ | ---------------- | ---- | ---- | --- | --- |\n| `supports` | supports | `supported-by` | weak | 0.70 | +1 | N-N |\n| `supported-by` | supported by | `supports` | weak | 0.70 | +1 | N-N |\n| `contradicts` | contradicts | self (symmetric) | N | 0.75 | −1 | N-N |\n| `explains` | explains | `explained-by` | weak | 0.80 | 0 | N-N |\n| `explained-by` | explained by | `explains` | weak | 0.80 | 0 | N-N |\n| `defines` | defines | `defined-by` | N | 0.90 | 0 | 1-1 |\n| `defined-by` | defined by | `defines` | N | 0.90 | 0 | 1-1 |\n\nThe epistemic category is where Nesso models reasoning about claims rather than facts about the world. `supports` and `supported-by` connect a piece of evidence to the claim it bolsters; `explains` and `explained-by` connect an explanans (the explanatory account) to its explanandum (what is being explained). Both pairs are asymmetric, because evidence points to a claim and an explanation is not equivalent to what it explains.\n\n`defines` is the most rigid relation in this category. It goes from the defining expression (the _definiens_) to the term being defined (the _definiendum_): in \"F = ma defines force\", the equation is the definiens and `force` is the definiendum. Cardinality 1-1 enforces a single canonical definition per term, so two competing `defines` edges into the same concept signal a real ambiguity worth resolving.\n\n`contradicts` is the only symmetric relation in the category, because logical incompatibility goes both ways: if A contradicts B, then B equally contradicts A. This distinguishes it from `supports` and `explains`, which always point in a particular direction.\n\n## References\n\n1. <a id=\"ref-1\"></a>Sun, Z., Deng, Z.-H., Nie, J.-Y., and Tang, J. [_RotatE: Knowledge Graph Embedding by Relational Rotation in Complex Space_](https://arxiv.org/abs/1902.10197). ICLR, 2019. [↑](#cite-1)\n2. <a id=\"ref-2\"></a>Sussna, M. [_Word sense disambiguation for free-text indexing using a massive semantic network_](https://doi.org/10.1145/170088.170106). CIKM '93, 1993. [↑](#cite-2)\n3. <a id=\"ref-3\"></a>Jiang, J. J. and Conrath, D. W. [_Semantic similarity based on corpus statistics and lexical taxonomy_](https://arxiv.org/abs/cmp-lg/9709008). ROCLING X, 1997. [↑](#cite-3)\n4. <a id=\"ref-4\"></a>Cartwright, D. and Harary, F. [_Structural balance: A generalization of Heider's theory_](https://doi.org/10.1037/h0046049). Psychological Review, 63(5):277–293, 1956. [↑](#cite-4)\n5. <a id=\"ref-5\"></a>W3C. [_OWL 2 Web Ontology Language Primer (Second Edition)_](https://www.w3.org/TR/owl2-primer/). 2012. [↑](#cite-5)\n6. <a id=\"ref-6\"></a>Allen, J. F. [_Maintaining knowledge about temporal intervals_](https://doi.org/10.1145/182.358434). Communications of the ACM, 26(11):832–843, 1983. [↑](#cite-6)"
68
+ },
69
+ {
70
+ "slug": "troubleshooting",
71
+ "title": "Troubleshooting",
72
+ "description": "What to do when a banner, the mentor, or an update check doesn't behave as expected.",
73
+ "markdown": "## \"This graph was changed on disk while you were editing\"\n\n**When it appears**: desktop only. The file watcher noticed the active graph's JSON file changed outside the app (an external editor, a sync tool, another Nesso window) while you had unsaved edits in memory.\n\nThe banner offers two actions:\n\n- **Keep my changes**: writes your in-memory version to disk, overwriting the external change.\n- **Reload from disk**: discards your in-memory edits and loads the file as it now is on disk. Undo/redo history is cleared in the process.\n\nThere's no automatic merge: pick whichever side of the conflict you want to keep.\n\n## Auto-update problems (desktop)\n\nThe desktop app checks GitHub Releases once on launch. If checking fails (offline, GitHub unreachable), it stays silent: no banner, no error, just no update for now.\n\nOnce an update is found, the banner can show:\n\n- **Version `X` is available**: click **Install & restart** to download and apply it.\n- **Installing…**: download/install in progress; the button is disabled and there is no way to cancel.\n- **Update failed. Try again?**: the download or install step failed (e.g. lost connectivity mid-download). Click **Retry**.\n- **Update installed. Restart Nesso to finish**: the update was applied to disk, but the app couldn't relaunch itself automatically. Quit and reopen Nesso manually.\n\nYou can dismiss the available/error states for the rest of the session with the banner's close button; the install-in-progress state has no dismiss, since the install can't be cancelled once started.\n\n## Mentor not responding\n\nIf a mentor message fails, the reply bubble shows one of two messages, depending on what went wrong:\n\n- **Can't reach the AI endpoint. Check Settings (`⌘,`) → AI. For local Ollama, run `ollama serve`.** A network-level failure: the endpoint is unreachable, the wrong port, or (on the hosted web app) a CORS rejection from a local Ollama instance. Confirm the base URL in **Settings → AI**, that Ollama (or your server) is actually running, and, if you're on the hosted web app talking to local Ollama, that you started it with `OLLAMA_ORIGINS` set to the app's origin (see [AI mentor](../guides/ai-mentor/#reaching-local-ollama-from-the-hosted-app)).\n- **_Hmm._ My voice failed me. Try again, slowly.** The endpoint was reachable but returned an error or a response Nesso couldn't parse (a model name that doesn't exist, a malformed response, a server-side error). Check the model name and that the endpoint actually speaks the OpenAI-compatible `chat/completions` format.\n\nClosing the mentor panel, switching graphs, or clicking **New chat** aborts any in-flight request without showing an error.\n\n## \"No AI endpoint configured\"\n\nThis isn't a failure: it's the mentor's resting state until you've set up an endpoint. It appears whenever **Mentor** is enabled in **Settings → AI** but the base URL or model field is empty. The input stays disabled until both are filled in. See [AI mentor](../guides/ai-mentor/#connecting-a-model) for setup."
50
74
  }
51
75
  ]
52
76
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nesso-how/mcp",
3
- "version": "0.1.0-alpha.38",
3
+ "version": "0.1.0-alpha.39",
4
4
  "description": "MCP server exposing Nesso knowledge graph tools to LLM clients",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,7 +23,7 @@
23
23
  "dagre": "^0.8.5",
24
24
  "zod": "^4.4.3",
25
25
  "@modelcontextprotocol/server": "^2.0.0-alpha.2",
26
- "@nesso-how/vocab-learning": "0.1.0-alpha.38"
26
+ "@nesso-how/vocab-learning": "0.1.0-alpha.39"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/dagre": "^0.7.53",