---
title: "How mdfy works"
url: https://memory.wiki/KRKz_MD-
updated: 2026-05-20T12:30:41.106Z
source: "mcp"
---
# How mdfy works

> Read this once and you can explain mdfy.app to anyone — a teammate,
> a journalist, an investor, the AI you're about to paste a URL into.
> Assumes nothing. Pairs with `OVERVIEW.md` (what we have shipped).

---

## The one idea

**A mdfy URL is an API for any AI.**

Paste `mdfy.app/<id>`, `mdfy.app/b/<id>`, or `mdfy.app/hub/<slug>`
into ChatGPT, Claude, Gemini, or Cursor — the LLM fetches it and gets
back clean markdown shaped for an AI to read. No app to install. No
format to learn. No JavaScript to render. Just markdown over HTTPS.

That's the entire product. Every other feature exists to make the
markdown those URLs return more useful.

---

## The four moving parts

| Part                 | What it is                                                          | Where it lives                       |
| -------------------- | ------------------------------------------------------------------- | ------------------------------------ |
| **The markdown**     | Source of truth. What the human types.                              | `documents.markdown`                 |
| **The AI graph**     | What an LLM extracted from a *bundle*: themes, insights, edges.     | `bundles.graph_data` (JSON)          |
| **The concept index**| User's lifelong ontology — concepts pooled across every bundle.     | `concept_index` + `concept_relations`|
| **The embeddings**   | Vectors. Power semantic recall ("which docs cover X?").             | `documents.embedding`, `bundles.embedding`, `document_chunks.embedding` |

First three end up in the markdown an LLM fetches. Embeddings never
go to the LLM directly — they back `mdfy_search` via MCP.

---

## What an LLM gets when it fetches a mdfy URL

Three URL shapes, each tuned for its purpose.

### `mdfy.app/<id>` — one document

```
---
title: "..."
url: https://mdfy.app/<id>
updated: 2026-05-20T...
---
# Doc title
…full markdown body…
```

No AI processing. Source markdown with a frontmatter wrapper. The
cheapest payload, and the target every bundle / hub digest links
into when an LLM wants the full body.

### `mdfy.app/b/<id>` — a bundle (default: **Compact**)

```
---
type: bundle
url: https://mdfy.app/b/<id>
analysis_stale: false
---
# Bundle title
> intent: "why I built this bundle"

## Summary                ← AI graph
## Themes                 ← AI graph
## Cross-document insights
## Concepts               ← this bundle's concept subgraph
## Concept relations
## Documents
- [Doc A](https://mdfy.app/<id>) — annotation
- [Doc B](https://mdfy.app/<id>) — annotation
```

The bundle digest is the **map** — what's in it, how the pieces
relate, what the prior LLM thought after reading the whole set, plus
a clickable index. The LLM follows inline doc links only when it
needs full bodies. Same context as concatenating every doc, ~95%
fewer tokens.

Append `?full=1` to inline every doc body. Expensive, occasionally
the right call.

### `mdfy.app/hub/<slug>` — a person's whole hub (default: **Compact**)

```
---
type: hub_digest
url: https://mdfy.app/hub/<slug>
concept_count: 40
---
# <name>'s knowledge — concept digest

## Concepts
### Concept Label
*tag • weight 14 • 4 docs*
> short description
- [Linked doc](https://mdfy.app/<id>)
…
_Related:_ depends on → **Other concept** · contradicts → **Another**
_In bundles:_ [Bundle A](https://mdfy.app/b/<id>) (3 docs) · …

## Concept relations
- **Concept X** part_of **Concept Y**
- **Concept Z** contradicts **Concept W**
…
```

A hub digest is the **shape of someone's thinking**: top-weighted
concepts, the typed edges between them, which bundle each concept
lives in. The LLM navigates "this person cares about X" → "X is most
discussed in bundle Y" → fetch bundle Y for the AI graph.

Useful extras: `?full=1` for a flat per-doc / per-bundle listing;
`?since=2026-04-01` for "what's new since".

---

## Two ways an LLM talks to mdfy

**Pull (URL fetch).** The LLM has a URL, it `GET`s, it reads. Works
in every AI tool that can hit a webpage — ChatGPT, Claude.ai web,
Gemini, Perplexity, Cursor, your terminal.

**Push + pull (MCP).** `mdfy-mcp` is a published npm package. Hosts
that speak MCP — Claude Desktop, Cursor, Cline, Windsurf — load it
once and get **26 tools** the LLM can call by itself: `mdfy_create`,
`mdfy_append`, `mdfy_search`, `mdfy_outline`, `mdfy_versions`,
`mdfy_publish`, `mdfy_diff`, `mdfy_render_preview`, … This is where
embeddings earn their keep — `mdfy_search` is vector similarity,
not keyword match.

So: URL payload is for pasting; MCP is for living inside.

---

## What happens when you create things

### Write a new doc
1. `PATCH /api/docs/<id>` with `action="auto-save"` writes the body
   (or `POST /api/docs` for a brand-new doc).
2. An `extraction_jobs` row is enqueued (`kind=doc_ontology`, 30 min
   throttle per doc so autosaves don't burn LLM budget).
3. Fast path: the same request runs the extractor inline — an LLM
   pulls out concepts / entities / tags and upserts them into your
   `concept_index`.
4. If the serverless instance dies mid-extraction, a cron worker
   picks the pending row up within a minute. Nothing silently fails.
5. Embedding pipeline (`/api/embed/<id>`) embeds title + body for
   global search and chunks each section for finer recall. Skips
   re-embedding via `embedding_source_hash` when nothing changed.
6. The moment you publish (un-draft), `mdfy.app/<id>` is fetchable
   by any AI in the world. Your hub digest already reflects the new
   concepts.

### Create a new bundle
1. `POST /api/bundles` creates the row.
2. You add N docs — `bundle_documents` join rows record order + per-
   doc annotations ("why this belongs").
3. On open (or on Analyze click), `POST /api/bundles/<id>/graph`
   sends the first ~10 doc bodies (truncated to 2000 chars each)
   plus the bundle's *intent* string to an LLM. The LLM returns a
   structured JSON: themes, insights, concept nodes, typed edges,
   takeaways, doc summaries, open questions.
4. JSON lands in `bundles.graph_data`. From now on the bundle URL
   serves the rich Compact payload above.
5. Same call merges every concept into your `concept_index` —
   weights and `doc_ids` accumulate across bundles. That's how your
   hub digest grows.
6. A separate request embeds the bundle (title + description +
   member titles) for MCP search.

### Edit a doc that's in a bundle
The bundle's `graph_data` is now older than its source. The next
fetch of `mdfy.app/b/<id>` carries `analysis_stale: true` in the
frontmatter so the LLM knows the AI summary may not match the
current text. The owner sees a "stale" pill in the canvas and can
re-analyze.

### Collaborate live
1. Owner shares with `someone@example.com` as Editor — server adds
   to `allowed_editors`.
2. Editor opens `mdfy.app/<id>` — `/d/<id>` viewer detects
   `isEditor=true` and redirects them to the live editor.
3. Both browsers subscribe to `yjs-doc-<id>` (Yjs CRDT) and
   `doc-cursor:<id>` (presence). Edits flow as Y.Doc updates;
   carets flow as broadcast events.
4. `Y.applyUpdate` keeps the peer's clientId ids in your Y.Doc, so
   subsequent deltas anchor correctly — sync converges on every
   keystroke.

---

## Same graph, many readers

The hub digest, the bundle canvas, the constellation view at
`/galaxy`, MCP search results — they all read from the **same four
tables**: `concept_index`, `concept_relations`, `documents`,
`bundles` + `bundle_documents`.

The visualization is not a separate dataset. What you see in the
galaxy is what the LLM gets in the hub digest, just rendered for
eyes instead of for a context window. Extend one, you extend the
other.

The mental model: **every mdfy surface is a different reader of
the same underlying graph**. The graph is the product.

---

## Why this is different from a PDF, Notion, or Google Doc

| Property                                | PDF upload    | Notion / GDoc share | mdfy URL              |
| --------------------------------------- | ------------- | ------------------- | --------------------- |
| Any AI reads it with no setup           | ✓             | ✗ (auth / JS)       | ✓                     |
| Updates automatically as you edit       | ✗             | ✓                   | ✓                     |
| AI gets pre-built navigation + summary  | ✗             | ✗                   | ✓ (bundle / hub)      |
| Single URL composes many docs           | ✗             | ✗                   | ✓ (bundle, hub)       |
| Cross-AI portable                       | re-upload     | re-share            | one URL, every AI     |
| LLM can write back via standard tools   | ✗             | ✗                   | ✓ (MCP, 26 tools)     |

The thing AI companies cannot copy is the **delivery model**: a
universal URL that any LLM fetches into its context, carrying both
the source and the pre-built graph over it. That's the moat.

---

## Trust: permissions, gates, freshness

- `is_draft=true` — never served to public fetch. Owner-only.
- `allowed_emails: [...]` — viewers must be signed in as one of
  these.
- `allowed_editors: [...]` — editors can save + collaborate live
  (separate from view access).
- `edit_mode`: `owner` / `account` / `token` / `view` / `public`.
- `password_hash` — gates the raw endpoint with HTTP basic.
- `analysis_stale: true` in bundle frontmatter — LLM knows the
  graph reflects an older version of the docs.
- `embedding_source_hash` — embedding pipeline skips re-embedding
  unchanged content; you don't pay for no-op updates.
- **No expiry, ever.** Pricing policy: docs are permanent. Free
  during beta, future Pro is for auto-analysis features, never for
  keeping a URL alive.

Every protection enforces at the raw endpoint, so the same rules
apply whether the consumer is a browser, an MCP host, a curl from
a script, or an LLM following an inline link.

---

## Glossary

- **Document** — a markdown file. URL: `mdfy.app/<id>`.
- **Bundle** — a curated set of docs analyzed as a group. URL:
  `mdfy.app/b/<id>`. Has its own AI graph.
- **Hub** — your personal home: every doc, every bundle, your
  concept index, your galaxy. URL: `mdfy.app/hub/<slug>`.
- **Compact** — the default URL response shape. AI graph or concept
  map + links instead of full bodies. ~30× cheaper to paste than
  Full.
- **Full** — `?full=1`. Inlines every body. Expensive but complete.
- **Concept index** — the user-scoped ontology. Updated on doc save
  (`doc_ontology` jobs) and bundle analyze.
- **Galaxy** — the visual reader of your concept index + relations.
- **Canvas** — the visual reader of a single bundle's AI graph.
- **Intent / annotation** — short text attached to a bundle ("why
  I made this") or to a doc-in-bundle ("why this belongs"). Both
  feed the LLM analysis prompt and the rendered digest.
- **MCP** — Model Context Protocol. The standard hosts use to give
  LLMs structured tool access. mdfy-mcp ships 26 tools.

---

*Last updated 2026-05-20. If anything here drifts from the code,
the code wins — file an issue.*
