From 2aa78aad5415ddaad3ccbdfbb80b7a600aba2e2d Mon Sep 17 00:00:00 2001
From: callumalpass
Date: Sun, 4 Oct 2026 12:28:45 +1100
Subject: [PATCH] Publish Editor and MCP guides, generate the MCP tool
reference, and describe account email
---
.github/workflows/ci.yml | 7 +
.github/workflows/deploy.yml | 7 +
.github/workflows/update-connect-release.yml | 7 +
.gitignore | 1 +
package.json | 3 +-
scripts/sync-mcp-tools.mjs | 83 +++++++++
src/data/docs-sections.ts | 63 ++++++-
src/pages/apps/editor/index.astro | 99 +++++++++++
src/pages/apps/editor/links/index.astro | 83 +++++++++
src/pages/apps/editor/notes/index.astro | 137 +++++++++++++++
src/pages/apps/editor/sharing/index.astro | 87 ++++++++++
src/pages/apps/editor/shortcuts/index.astro | 86 +++++++++
src/pages/apps/editor/types/index.astro | 123 +++++++++++++
src/pages/apps/index.astro | 5 +-
src/pages/apps/mcp/collections/index.astro | 115 ++++++++++++
src/pages/apps/mcp/data-handling/index.astro | 81 +++++++++
src/pages/apps/mcp/index.astro | 117 +++++++++++++
src/pages/apps/mcp/tools/index.astro | 82 +++++++++
.../apps/mcp/troubleshooting/index.astro | 121 +++++++++++++
.../apps/mcp/working-with-records/index.astro | 164 ++++++++++++++++++
src/pages/privacy/index.astro | 13 ++
21 files changed, 1480 insertions(+), 4 deletions(-)
create mode 100644 scripts/sync-mcp-tools.mjs
create mode 100644 src/pages/apps/editor/index.astro
create mode 100644 src/pages/apps/editor/links/index.astro
create mode 100644 src/pages/apps/editor/notes/index.astro
create mode 100644 src/pages/apps/editor/sharing/index.astro
create mode 100644 src/pages/apps/editor/shortcuts/index.astro
create mode 100644 src/pages/apps/editor/types/index.astro
create mode 100644 src/pages/apps/mcp/collections/index.astro
create mode 100644 src/pages/apps/mcp/data-handling/index.astro
create mode 100644 src/pages/apps/mcp/index.astro
create mode 100644 src/pages/apps/mcp/tools/index.astro
create mode 100644 src/pages/apps/mcp/troubleshooting/index.astro
create mode 100644 src/pages/apps/mcp/working-with-records/index.astro
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b4f64c3..d11ce5e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -86,6 +86,13 @@ jobs:
working-directory: .sources/mdbase-spec/site
run: npm ci && npm run build
+ - name: Build the MCP server for the tool reference
+ working-directory: .sources/mdbase-connect
+ run: |
+ pnpm_version="$(jq -r '.packageManager | sub("^pnpm@"; "")' package.json)"
+ npx --yes "pnpm@$pnpm_version" install --frozen-lockfile --filter "@mdbase/connect-mcp..."
+ npx --yes "pnpm@$pnpm_version" --filter "@mdbase/connect-mcp..." build
+
- name: Synchronize schemas, contracts, and conformance claims
run: pnpm sync:sources
env:
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 203084c..5e378ad 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -93,6 +93,13 @@ jobs:
working-directory: .sources/mdbase-spec/site
run: npm ci && npm run build
+ - name: Build the MCP server for the tool reference
+ working-directory: .sources/mdbase-connect
+ run: |
+ pnpm_version="$(jq -r '.packageManager | sub("^pnpm@"; "")' package.json)"
+ npx --yes "pnpm@$pnpm_version" install --frozen-lockfile --filter "@mdbase/connect-mcp..."
+ npx --yes "pnpm@$pnpm_version" --filter "@mdbase/connect-mcp..." build
+
- name: Synchronize schemas, contracts, and conformance claims
run: pnpm sync:sources
env:
diff --git a/.github/workflows/update-connect-release.yml b/.github/workflows/update-connect-release.yml
index fd7b7b2..11a8273 100644
--- a/.github/workflows/update-connect-release.yml
+++ b/.github/workflows/update-connect-release.yml
@@ -127,6 +127,13 @@ jobs:
working-directory: .sources/mdbase-spec/site
run: npm ci && npm run build
+ - name: Build the MCP server for the tool reference
+ working-directory: .sources/mdbase-connect
+ run: |
+ pnpm_version="$(jq -r '.packageManager | sub("^pnpm@"; "")' package.json)"
+ npx --yes "pnpm@$pnpm_version" install --frozen-lockfile --filter "@mdbase/connect-mcp..."
+ npx --yes "pnpm@$pnpm_version" --filter "@mdbase/connect-mcp..." build
+
- name: Synchronize schemas, contracts, and conformance claims
run: pnpm sync:sources
env:
diff --git a/.gitignore b/.gitignore
index 9472de1..6f518ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ dist/
/public/theme-bootstrap.js
/src/data/contracts.json
/src/data/conformance.json
+/src/data/mcp-tools.json
diff --git a/package.json b/package.json
index 3822541..c9ddb19 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
"deploy:dev": "node scripts/deploy-pages-dev.mjs",
"check": "astro check",
"preview": "astro preview",
- "sync:sources": "node scripts/sync-sources.mjs && node scripts/sync-contracts.mjs",
+ "sync:sources": "node scripts/sync-sources.mjs && node scripts/sync-contracts.mjs && node scripts/sync-mcp-tools.mjs",
+ "sync:mcp-tools": "node scripts/sync-mcp-tools.mjs",
"sync:contracts": "node scripts/sync-contracts.mjs",
"import:spec": "node scripts/import-spec.mjs",
"check:links": "node scripts/check-links.mjs",
diff --git a/scripts/sync-mcp-tools.mjs b/scripts/sync-mcp-tools.mjs
new file mode 100644
index 0000000..169fb3f
--- /dev/null
+++ b/scripts/sync-mcp-tools.mjs
@@ -0,0 +1,83 @@
+import { existsSync, statSync, writeFileSync } from "node:fs";
+import { dirname, join, resolve } from "node:path";
+import { fileURLToPath, pathToFileURL } from "node:url";
+
+// The MCP tool reference is read from the gateway's own tool registrations
+// through an in-memory MCP client, so the published names, descriptions,
+// annotations and input schemas are exactly what an MCP host receives.
+
+const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+const connectDir = resolve(process.env.MDBASE_CONNECT_DIR ?? join(root, "..", "mdbase-connect"));
+const mcpDir = join(connectDir, "services", "mcp");
+const built = join(mcpDir, "dist", "mcp.js");
+const source = join(mcpDir, "src", "mcp.ts");
+
+if (!existsSync(built)) {
+ throw new Error(`Missing ${built}. Run pnpm --filter @mdbase/connect-mcp build in mdbase-connect.`);
+}
+if (statSync(source).mtimeMs > statSync(built).mtimeMs) {
+ throw new Error(`${built} is older than ${source}. Rebuild @mdbase/connect-mcp first.`);
+}
+
+const sdk = join(mcpDir, "node_modules", "@modelcontextprotocol", "sdk", "dist", "esm");
+const { createMcpServer } = await import(pathToFileURL(built).href);
+const { Client } = await import(pathToFileURL(join(sdk, "client", "index.js")).href);
+const { InMemoryTransport } = await import(pathToFileURL(join(sdk, "inMemory.js")).href);
+
+async function listTools(scopes) {
+ // Listing tools never calls the gateway or OAuth service.
+ const server = createMcpServer({ connectionSetId: "reference", scopes }, {}, {});
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
+ const client = new Client({ name: "mdbase.dev-reference", version: "0" });
+ await server.connect(serverTransport);
+ await client.connect(clientTransport);
+ const { tools } = await client.listTools();
+ const version = client.getServerVersion()?.version;
+ await client.close();
+ return { tools, version };
+}
+
+const read = await listTools(["mdbase:read"]);
+const all = await listTools(["mdbase:read", "mdbase:write"]);
+const readNames = new Set(read.tools.map((tool) => tool.name));
+
+const tools = all.tools.map((tool) => ({
+ name: tool.name,
+ title: tool.title ?? tool.name,
+ description: tool.description ?? "",
+ access: readNames.has(tool.name) ? "read" : "write",
+ annotations: tool.annotations ?? {},
+ inputs: Object.entries(tool.inputSchema?.properties ?? {}).map(([name, schema]) => ({
+ name,
+ required: (tool.inputSchema.required ?? []).includes(name),
+ type: describeType(schema),
+ description: schema.description ?? ""
+ }))
+}));
+
+const output = {
+ generated_from: "mdbase-connect/services/mcp/src/mcp.ts",
+ server_version: all.version,
+ tools
+};
+writeFileSync(join(root, "src", "data", "mcp-tools.json"), `${JSON.stringify(output, null, 2)}\n`);
+console.log(`Wrote ${tools.length} MCP tools from mdbase MCP ${all.version}`);
+
+function describeType(schema) {
+ if (!schema || Object.keys(schema).length === 0) return "any";
+ if (schema.const !== undefined) return JSON.stringify(schema.const);
+ if (schema.enum) return schema.enum.map((value) => JSON.stringify(value)).join(" | ");
+ if (schema.format === "uuid") return "UUID";
+ if (schema.type === "array") return `${describeType(schema.items)}[]`;
+ if (schema.type === "integer") {
+ const bounds = [
+ schema.minimum !== undefined ? `≥ ${schema.minimum}` : undefined,
+ schema.maximum !== undefined && schema.maximum < Number.MAX_SAFE_INTEGER
+ ? `≤ ${schema.maximum}`
+ : undefined
+ ].filter(Boolean);
+ return bounds.length > 0 ? `integer (${bounds.join(", ")})` : "integer";
+ }
+ if (schema.type === "object") return "object";
+ return schema.type ?? "any";
+}
diff --git a/src/data/docs-sections.ts b/src/data/docs-sections.ts
index 432a38c..1347542 100644
--- a/src/data/docs-sections.ts
+++ b/src/data/docs-sections.ts
@@ -1,6 +1,7 @@
import release from "./connect-release.json";
+import mcpTools from "./mcp-tools.json";
-export type DocsSectionId = "sdk" | "reader" | "writer";
+export type DocsSectionId = "sdk" | "mcp" | "editor" | "reader" | "writer";
type DocLink = { href: string; label: string; key: string };
type DocsSection = {
@@ -51,6 +52,66 @@ export const docsSections: Record = {
}
]
},
+ mcp: {
+ label: "mdbase MCP",
+ navLabel: "mdbase MCP documentation",
+ headerCurrent: "apps",
+ sourcePath: "src/pages/apps/mcp",
+ meta: { label: "Server", value: mcpTools.server_version },
+ groups: [
+ {
+ label: "Start",
+ docs: [
+ { href: "/apps/mcp/", label: "Set up mdbase MCP", key: "overview" },
+ { href: "/apps/mcp/collections/", label: "Collections and access", key: "collections" }
+ ]
+ },
+ {
+ label: "Use",
+ docs: [
+ { href: "/apps/mcp/working-with-records/", label: "Working with records", key: "records" },
+ { href: "/apps/mcp/troubleshooting/", label: "Troubleshooting", key: "troubleshooting" }
+ ]
+ },
+ {
+ label: "Reference",
+ docs: [
+ { href: "/apps/mcp/tools/", label: "Tool reference", key: "tools" },
+ { href: "/apps/mcp/data-handling/", label: "Data handling", key: "data" }
+ ]
+ }
+ ]
+ },
+ editor: {
+ label: "mdbase Editor",
+ navLabel: "mdbase Editor documentation",
+ headerCurrent: "apps",
+ sourcePath: "src/pages/apps/editor",
+ meta: { label: "Web", value: "editor.mdbase.dev" },
+ groups: [
+ {
+ label: "Start",
+ docs: [
+ { href: "/apps/editor/", label: "Get started", key: "overview" }
+ ]
+ },
+ {
+ label: "Use",
+ docs: [
+ { href: "/apps/editor/notes/", label: "Writing notes", key: "notes" },
+ { href: "/apps/editor/links/", label: "Links, embeds and files", key: "links" },
+ { href: "/apps/editor/types/", label: "Types", key: "types" },
+ { href: "/apps/editor/sharing/", label: "Sharing a collection", key: "sharing" }
+ ]
+ },
+ {
+ label: "Reference",
+ docs: [
+ { href: "/apps/editor/shortcuts/", label: "Shortcuts and settings", key: "shortcuts" }
+ ]
+ }
+ ]
+ },
reader: {
label: "mdbase Reader",
navLabel: "mdbase Reader documentation",
diff --git a/src/pages/apps/editor/index.astro b/src/pages/apps/editor/index.astro
new file mode 100644
index 0000000..a053917
--- /dev/null
+++ b/src/pages/apps/editor/index.astro
@@ -0,0 +1,99 @@
+---
+import DocsLayout from "../../../components/DocsLayout.astro";
+---
+
+
+ What it is
+
+ mdbase Editor opens one mdbase collection and shows every record as a note.
+ It edits the Markdown files in place. Paths, frontmatter and type
+ definitions stay as ordinary files that other tools, such as a text editor,
+ Obsidian or an AI client, can keep using.
+
+
+ It works with collections hosted by mdbase Connect and with collections
+ that stay on your computer and are served by the{" "}
+ Connector.
+
+
+ Open a collection
+
+ - Go to editor.mdbase.dev.
+ -
+ Sign in to mdbase Connect. If you do not have an account,{" "}
+ sign up first.
+
+ -
+ Choose a collection. The list includes collections hosted by mdbase and
+ collections on your connected computers.
+
+ -
+ Approve the Editor's access to that one collection. Reading is
+ required. Creating, editing and deleting notes, managing types and
+ adding files are optional, and you can turn each one off. The Editor is
+ read-only for anything you do not approve.
+
+
+
+ The Editor opens mdbase 0.3 collections. For an older collection, upgrade a
+ copy with mdbase, check the copy, and then open it. Your original files can
+ stay untouched while you check the result.
+
+
+ Find your way around
+
+
+ | Area | What it holds |
+
+
+ | Collection rail | Switches between All notes, Types and Settings, filters notes by folder, tag or type, shows the connection status and opens the Connect workspace. |
+ | Note list | The notes, and attached files, in the current filter. Sort by modified date, title or path from view options. |
+ | Editor | The note's Markdown body, with the title, properties and backlinks around it. |
+
+
+
+ Press Ctrl P (⌘ P on a Mac)
+ to open any note by name, and ? to see the main shortcuts. On a
+ phone, each level of navigation is a separate screen.
+
+
+ Connection status
+
+ The rail shows whether the collection is connected or reconnecting. A
+ collection on your computer can only be read and saved while the Connector
+ on that computer is running and online. When you are on that computer,
+ Use this computer in the rail, or{" "}
+ Allow local access under Settings → Connection, lets the
+ browser reach the Connector directly.
+
+
+ Switch collections
+
+ Open the collection switcher from the rail. It lists collections you
+ opened before in this browser, and Connect another collection{" "}
+ starts a new approval. Forget from this browser in
+ Settings removes a collection from the Editor without changing its files.
+
+
+ The Connect workspace
+
+ editor.mdbase.dev/connect{" "}
+ is where you manage your mdbase Connect account: collections, hosted
+ storage, sharing, connected applications, computers, sign-in methods and
+ browser sessions. It uses your account session, not a collection grant, so
+ it can list your collections but cannot read their contents.
+
+
diff --git a/src/pages/apps/editor/links/index.astro b/src/pages/apps/editor/links/index.astro
new file mode 100644
index 0000000..2626207
--- /dev/null
+++ b/src/pages/apps/editor/links/index.astro
@@ -0,0 +1,83 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+import CodeBlock from "../../../../components/CodeBlock.astro";
+
+const links = `[[projects/alpha]]
+[[projects/alpha|Project Alpha]]
+[[projects/alpha#Goals]]
+[[projects/alpha#^decision-1]]`;
+
+const embeds = `![[meeting-notes#Decisions]]
+![[Attachments/diagram.png]]
+![[Attachments/contract.pdf]]`;
+---
+
+
+ Link to a note
+
+ Type [[ and start typing a title or path. The Editor suggests
+ notes and files from the collection and completes the link:
+
+
+
+ After a note name, type # to link to one of its headings, or{" "}
+ #^ to link to a block. Links are stored as ordinary Markdown
+ wikilinks, so other mdbase tools and Obsidian read them the same way.
+ Links that do not resolve to a note are marked as you write.
+
+
+ Link with @
+
+ Typing @ at the start of a line, after a space or after an opening bracket opens the same suggestions and inserts a{" "}
+ [[wikilink]]. To limit suggestions to one type, type{" "}
+ @/, choose the type, and continue typing. For example,{" "}
+ @/person/sam suggests only records of the{" "}
+ person type that match "sam".
+
+
+ Embed a note or file
+
+ Add ! before a link to show its content inside the current note.
+ You can embed a whole note, a section or block of it (#^id), or a file:
+
+
+
+ The embedded content is read from the other file each time, so it stays up
+ to date. The Editor tells you when an embed points to a missing note or
+ heading, matches more than one note, or would include itself.
+
+
+ Backlinks
+
+ Backlinks lists every note that links to the one you are
+ reading. The button in the note header shows how many there are.
+
+
+ Attach files
+
+ Choose Attach file… from the note's action menu. Each file
+ is uploaded to an Attachments folder next to the note, and an
+ embed or link to each file is added to the note. You can choose several
+ files at once. An existing file with the same name is never replaced. The
+ new file gets a numbered name instead. Images, PDFs, audio, video and text
+ files preview in the Editor. Other files show their name and path and are
+ stored as they are, for other tools to open.
+
+
+ If the Editor's approval does not include file access, the menu shows{" "}
+ Request attachment access instead, which asks you to
+ approve it in Connect.
+
+
diff --git a/src/pages/apps/editor/notes/index.astro b/src/pages/apps/editor/notes/index.astro
new file mode 100644
index 0000000..b138b27
--- /dev/null
+++ b/src/pages/apps/editor/notes/index.astro
@@ -0,0 +1,137 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+---
+
+
+ Create a note
+
+ Choose New note or press Ctrl Shift{" "}
+ N. A note is not written to the collection until you create it,
+ so you can fill in everything first:
+
+
+ -
+ Type. Pick one of the collection's types, or{" "}
+ General note for a note without a type.
+
+ -
+ Required properties. The fields the type requires are
+ shown up front. Nested objects and lists follow the type's schema.
+
+ -
+ Path. The Editor suggests where the file will live.
+ Edit it to choose another folder or file name. A new folder appears once
+ its first note is created.
+
+
+
+ Press Ctrl Enter to create the note.
+
+
+ Write
+
+ The body is plain Markdown. Changes save automatically. Type{" "}
+ / at the start of a line for Markdown commands such as
+ headings, lists, tasks, quotes, code blocks, tables and links.
+
+
+ Ctrl B and Ctrl I set bold and
+ italic, Ctrl Shift C sets inline code,{" "}
+ Ctrl K adds a link, and pasting a URL over
+ selected text turns it into a link. Tab and{" "}
+ Shift Tab indent and outdent list items.
+
+
+ Quiet Markdown, on by default, softens Markdown punctuation
+ away from the line you are editing and makes task checkboxes clickable.
+ Vim key bindings can be turned on in Settings. See{" "}
+ Shortcuts and settings.
+
+
+ Properties
+
+ Open Note properties to see and edit frontmatter. It has
+ three views:
+
+
+
+ | View | Shows |
+
+
+ | Fields | Structured inputs based on the note's types, including dates, links, lists and nested objects. |
+ | JSON | The frontmatter stored in the file, as JSON. |
+ | Source | The exact Markdown file, including YAML frontmatter and body. |
+
+
+
+ Find notes
+
+ -
+ Quick open (Ctrl P) searches
+ titles, paths, properties and text. Start with > to search
+ actions instead.
+
+ -
+ Folders, Tags and By type in the collection rail filter the note list.
+ Its view options sort by modified date, title or path.
+
+ -
+ Alt J and Alt K move to the
+ next and previous note. Alt ← and{" "}
+ Alt → go back and forward through notes you
+ opened.
+
+ - Ctrl F finds text within the open note.
+
+
+ Rename and move
+
+ Click the path in the note header to rename or move the Markdown file; the
+ new path must end in .md. If other notes link to it, the
+ Editor asks whether to Rename and update links, which
+ rewrites those links, or Rename only. Otherwise it renames
+ the file and updates links straight away.
+
+
+ Check a note
+
+ Check note, in the note's action menu, validates the note
+ against its types and reports any fields that do not fit. Links that do not
+ resolve are marked in the editor as you write.
+
+
+ Edits made elsewhere
+
+ Every save checks that the file has not changed since the Editor read it.
+ If another app, a text editor or another person changed the note, the
+ Editor does not overwrite it. It shows This note changed elsewhere{" "}
+ and lets you compare the title and body before choosing{" "}
+ Keep my edits or Use latest.
+
+
+ Edits that were not saved, for example because the browser closed, stay in
+ this browser for up to seven days. Reopening the note offers{" "}
+ Restore unsaved edits or Discard recovered edits.
+
+
+ Delete a note
+
+ Delete note is in the note's action menu. It asks for
+ confirmation, then removes the Markdown file from the collection.
+
+
diff --git a/src/pages/apps/editor/sharing/index.astro b/src/pages/apps/editor/sharing/index.astro
new file mode 100644
index 0000000..ce91234
--- /dev/null
+++ b/src/pages/apps/editor/sharing/index.astro
@@ -0,0 +1,87 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+---
+
+
+ Which collections can be shared
+
+ Sharing is for collections hosted by mdbase Connect. A collection that
+ stays on your computer is not shared this way. Sharing is managed in the
+ Connect workspace at{" "}
+ editor.mdbase.dev/connect.
+ Open the collection and find People & sharing. Only
+ the collection's owner sees it.
+
+
+ Roles
+
+
+ | Role | Can |
+
+
+ | Owner | Everything, including inviting and removing people. |
+ | Editor | Edit notes, manage types, rename the collection, and connect apps. |
+ | Viewer | Read the collection and connect read-only apps or synced folders. |
+
+
+
+ A viewer who opens the collection in mdbase Editor sees it read-only.
+
+
+ Invite someone
+
+ - Choose Invite person.
+ -
+ Under Invite using, choose Verified email{" "}
+ and enter their email address, or choose Sharing code{" "}
+ and enter the code they gave you.
+
+ - Choose Viewer or Editor, then Create invitation.
+ - Copy the invitation link and send it to them yourself.
+
+
+ Email invitations only work for someone who already has a Connect account
+ with that email address verified. If they signed up after you created the
+ invitation, create a new one.
+
+
+ Sharing codes
+
+ Use a sharing code when you cannot invite someone by email, for example if
+ they sign in with a different address. The person you are inviting creates
+ the code on the Account & sessions page in the Connect
+ workspace, under Sharing code → Generate code,
+ and sends it to you. A code can be used once. Invitations and sharing codes
+ expire after seven days.
+
+
+ Accept an invitation
+
+ Open the invitation link while signed in to the account it was created for.
+ Connect shows A collection was shared with you. Choose{" "}
+ Accept to add the collection to your collections, then
+ open it in the Editor or approve other apps to use it.
+
+
+ Change or remove access
+
+ In People & sharing, change a person's role from the
+ list, or choose Remove. Removing someone also revokes the
+ apps and synced folders they connected to the collection. Invitations that
+ have not been accepted are listed under Pending invitations,
+ where you can cancel them.
+
+
diff --git a/src/pages/apps/editor/shortcuts/index.astro b/src/pages/apps/editor/shortcuts/index.astro
new file mode 100644
index 0000000..99654e0
--- /dev/null
+++ b/src/pages/apps/editor/shortcuts/index.astro
@@ -0,0 +1,86 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+
+const shortcuts = [
+ ["Ctrl P", "Quick open"],
+ ["Ctrl K", "Quick open, outside the note text"],
+ [">", "Actions, typed at the start of quick open"],
+ ["Ctrl Shift N", "New note"],
+ ["Ctrl Enter", "Create the note being composed"],
+ ["Ctrl Shift L", "Show or hide the notes sidebar"],
+ ["↑ / ↓", "Move through the note list"],
+ ["Alt J / Alt K", "Next or previous note"],
+ ["Alt ← / Alt →", "Back or forward through opened notes"],
+ ["Ctrl F", "Find in note"],
+ ["Ctrl B / Ctrl I", "Bold or italic"],
+ ["Ctrl Shift C", "Inline code"],
+ ["Ctrl K", "Add a link, in the note text"],
+ ["Tab / Shift Tab", "Indent or outdent a list item"],
+ ["/", "Markdown commands, at the start of a line"],
+ ["[[", "Link to a note or file"],
+ ["@", "Link to a note, optionally filtered by type"],
+ ["?", "Show the shortcut guide"],
+ ["Esc", "Close quick open or the shortcut guide"]
+];
+
+const settings = [
+ ["Color theme", "Follow the system appearance, or keep a light or dark theme in this browser."],
+ ["Text size", "Compact, Comfortable or Large note text, without changing the rest of the interface."],
+ ["Wrap long lines", "Keep Markdown within the writing width. On by default."],
+ ["Quiet Markdown", "Soften punctuation away from the active line and make tasks checkable. On by default."],
+ ["Vim key bindings", "Use normal, insert, visual and command modes in the note editor. Off by default."]
+];
+---
+
+
+ Keyboard shortcuts
+
+ On a Mac, use ⌘ in place of Ctrl. Press{" "}
+ ? anywhere outside a text field to see the guide in the Editor.
+
+
+
+ | Keys | Action |
+
+
+ {shortcuts.map(([keys, action]) => (
+ | {keys} | {action} |
+ ))}
+
+
+
+ Editor settings
+
+ Open Settings from the collection rail. These preferences
+ are stored in this browser only.
+
+
+
+ | Setting | Effect |
+
+
+ {settings.map(([name, effect]) => (
+ | {name} | {effect} |
+ ))}
+
+
+
+ Collection settings
+
+ Settings also shows the open collection: its ID, the specification version
+ it uses, its types folder, the frontmatter keys that declare a type, its
+ validation level, and the operations the Editor is approved for.{" "}
+ Forget from this browser removes the collection from the
+ Editor without changing its files.
+
+
diff --git a/src/pages/apps/editor/types/index.astro b/src/pages/apps/editor/types/index.astro
new file mode 100644
index 0000000..9f647fd
--- /dev/null
+++ b/src/pages/apps/editor/types/index.astro
@@ -0,0 +1,123 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+---
+
+
+ What a type is
+
+ A type is a Markdown file in the collection's types folder. It names a kind
+ of record, such as a task or a person, and describes its frontmatter fields
+ with JSON Schema. The Editor uses types to build property forms, check
+ notes, and suggest links. Other apps use the same files. The format is
+ defined in the mdbase specification.
+
+
+ Open Types from the collection rail to see every type. Changing
+ types needs the collection's definition management permission, which the
+ Editor requests when you approve it.
+
+
+ Create a type
+ In the Types list, choose Add a type, then one of:
+
+ -
+ New type starts from an empty definition. Give the type
+ a unique name and add your own fields.
+
+ -
+ Ready-made types is a catalog. Each type comes
+ with an application contract, so apps built for that contract can use
+ your notes. Adding one creates a new, editable type. Existing files are
+ never overwritten. Installing one needs permission to install type packs;
+ without it, the catalog offers to request that access.
+
+
+
+ Fields
+
+ The visual editor covers the field shapes most collections need: text,
+ numbers, dates and times, choices, links, lists, nested objects, lists of
+ objects, and required fields. You can also set Read defaults, which are
+ used when a note leaves a property out. Source files stay unchanged.
+
+
+ Link rules say which fields point to other records, which types they may
+ point to, and whether the target must exist. Uniqueness rules require a
+ value to be unique within the type, across the collection, or among
+ records that match a path pattern.
+
+
+ Anything the visual editor cannot represent stays in the YAML source,
+ marked where it appears. Switch to YAML to edit the complete
+ definition directly.
+
+
+ Type membership
+
+ Type membership decides which notes belong to the type.
+ A note that names a type in one of the collection's type keys (type{" "}
+ or types by default) belongs to the types it names, and the
+ automatic rules below are skipped. Otherwise the note belongs when every
+ configured condition matches:
+
+
+
+ | Condition | Matches when |
+
+
+ | Path | Its path matches one of the path patterns. |
+ | Field presence | Its frontmatter contains all the listed fields. |
+ | Expression | Each extra rule on its fields is true. These rules are edited in YAML. |
+
+
+
+ Some collections turn explicit assignment off. Then every note is
+ classified by the other rules.
+
+
+ Collection behaviour
+
+ Collection behaviour controls how the type appears and
+ acts across tools: the field used as a record's name, description and
+ colour, an icon, and a path policy. The path policy tells tools where to put
+ new records of this type and how to name them. Changing it affects future
+ files only. Existing files are not moved.
+
+
+ Works with applications
+
+ An application contract describes the fields an app expects, such as a
+ task manager's due date and status. Works with applications{" "}
+ connects a type to an installed contract and maps each contract field to one
+ of your field names. The app reads your notes through that mapping, and your
+ notes keep their own field names. A mapping copies values as they are. It
+ does not convert them.
+
+
+ The Editor suggests contracts that might fit, based on field names and
+ shapes. Check that the meanings match before you accept a suggestion.
+
+
+ Review and save
+
+ A type change can affect every note of that type. Before saving, the Editor
+ shows the fields added, changed and removed, the YAML differences, and
+ which existing notes would need attention. You can open the list of
+ affected notes. The collection checks the complete definition when you
+ confirm.
+
+
diff --git a/src/pages/apps/index.astro b/src/pages/apps/index.astro
index 44df789..859afa3 100644
--- a/src/pages/apps/index.astro
+++ b/src/pages/apps/index.astro
@@ -32,6 +32,7 @@ const applications: CatalogueEntry[] = [
collections: "Hosted by Connect or local through Connector",
links: [
{ label: "Open Editor", href: "https://editor.mdbase.dev/" },
+ { label: "Documentation", href: "/apps/editor/" },
{ label: "Source", href: "https://github.com/mdbase-dev/mdbase-connect/tree/main/apps/editor" }
]
},
@@ -75,8 +76,8 @@ const applications: CatalogueEntry[] = [
runsOn: "OAuth-capable MCP clients",
collections: "Hosted by Connect or local through Connector",
links: [
- { label: "Setup and tools", href: "https://github.com/mdbase-dev/mdbase-connect/blob/main/docs/mcp-gateway.md" },
- { label: "Endpoint", href: "https://mcp.mdbase.dev/mcp" }
+ { label: "Set up", href: "/apps/mcp/" },
+ { label: "Tool reference", href: "/apps/mcp/tools/" }
]
}
];
diff --git a/src/pages/apps/mcp/collections/index.astro b/src/pages/apps/mcp/collections/index.astro
new file mode 100644
index 0000000..98bda08
--- /dev/null
+++ b/src/pages/apps/mcp/collections/index.astro
@@ -0,0 +1,115 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+---
+
+
+ One approval per collection
+
+ The client does not get general access to your account. Each collection is
+ approved on its own, with its own list of operations. There is no query or
+ write that spans collections, and no collection is used unless you approved
+ it.
+
+
+ Connection IDs
+
+ list_connections returns one entry for each approved
+ collection:
+
+
+
+ | Field | Meaning |
+
+
+ id | The connection ID. Every collection tool requires it as connection_id. |
+ collection_id | The collection's ID in Connect. |
+ display_name | The collection name shown in Connect. |
+ operations | The operations you approved, such as read, query or update. |
+ authority | local when the collection is on your computer, remote when it is hosted. |
+
+
+
+ Connection IDs are opaque. They are not collection paths and do not reveal
+ where the collection is stored.
+
+
+ Connect more collections
+
+ Ask the client to connect another collection. It calls{" "}
+ add_connection, which returns a link for you to open in a
+ browser. The link can be used once and expires after ten minutes.
+
+
+ The link opens the same Connect approval screen as the first setup. Choose
+ the collection and its operations, approve, then ask the client to list
+ collections again.
+
+
+ Reconnect or broaden access
+
+ If a collection's approval expires, or you want to allow operations you
+ previously left out, the client calls reconnect_collection{" "}
+ with that connection ID. The returned link opens Connect on that
+ collection only. You review and approve its operations, or decline.
+
+
+ Read and write access
+
+ Two separate approvals control writing. The first is the MCP sign-in scope
+ the client requests: mdbase:read, mdbase:write,
+ or both. A client that does not ask for a specific scope gets both. The
+ client only sees the write tools when the scope includes{" "}
+ mdbase:write.
+
+
+ The second is the set of operations you approve for each collection in
+ Connect. This one decides what can actually happen. If you approved a
+ collection for reading only, a write tool fails with{" "}
+ insufficient_collection_access. It never expands your
+ approval by itself. If you narrow access in Connect, calls may fail with a
+ different code until the MCP server's sign-in next refreshes, within an
+ hour.
+
+
+ Revoke access
+
+ Open the Applications{" "}
+ page in the Connect workspace. The MCP server is listed as{" "}
+ mdbase. You can narrow or revoke its access to each
+ collection there. The next tool call for a revoked collection fails, and
+ other collections keep working.
+
+
+ Removing the server from your MCP client stops that client from using it,
+ but does not remove the approvals held in Connect. Revoke them in Connect as
+ well if you no longer use them.
+
+
+ Collections on your computer
+
+ For a collection that stays on your computer, requests travel from the MCP
+ server through the Connect relay to the Connector. They are encrypted end
+ to end between the MCP server and your Connector. The Connector checks its
+ own copy of your approval before it opens the collection, so a grant you
+ revoked is refused locally too.
+
+
+ The Connector must be running and online. If it is not, tools return{" "}
+ connector_offline. See{" "}
+ Troubleshooting.
+
+
diff --git a/src/pages/apps/mcp/data-handling/index.astro b/src/pages/apps/mcp/data-handling/index.astro
new file mode 100644
index 0000000..5cbf193
--- /dev/null
+++ b/src/pages/apps/mcp/data-handling/index.astro
@@ -0,0 +1,81 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+---
+
+
+ Why it is a separate service
+
+ The Connect control plane, which handles accounts and approvals, does not
+ see collection content. Requests to a collection on your computer are
+ encrypted before they reach the Connect relay, and only your Connector
+ decrypts them. A hosted collection is stored by the mdbase hosted
+ provider, a separate service that can read it to perform operations; it is
+ encrypted at rest.
+
+
+ An AI client cannot perform that encryption itself, so the MCP server does
+ it on the client's behalf. That means the MCP server sees operation inputs
+ and results in memory while it handles a tool call, for local and hosted
+ collections alike. It runs as its own service, with its own database, so
+ that this access stays outside the control plane. It is not a
+ zero-knowledge service.
+
+
+ What is stored
+
+ - Client registrations (name and redirect URLs), hashes of access and refresh tokens, and short-lived sign-in state.
+ - For each approved collection: its ID, display name, approved operations and contract scope.
+ - The credentials the MCP server uses to reach Connect, encrypted.
+ - A key pair for each connection, with the private key encrypted, and a plain message counter for each key.
+
+
+ Expired sign-in records are not currently deleted automatically. The
+ server does not log individual requests. Unexpected errors are logged for
+ diagnosis.
+
+
+ What is not stored
+ The MCP server's database does not hold:
+
+ - Record contents, including frontmatter and bodies.
+ - Tool call results.
+ - Record paths within a collection.
+ - Filesystem paths on your computer. These are never sent to the MCP server or to Connect.
+
+
+ Your MCP client
+
+ Whatever a tool returns goes to your MCP client and, through it, to the AI
+ model you are using. That client's own data policies apply to it. Approve
+ only the collections and permissions you are comfortable sharing with that
+ client.
+
+
+ Your controls
+
+ - Each collection is approved separately, with its own permissions.
+ -
+ Revoking a collection on the{" "}
+ Applications{" "}
+ page stops the next tool call. For a local collection, the Connector also
+ refuses a revoked approval on its own.
+
+ -
+ See the privacy notice and{" "}
+ security page for how mdbase handles account
+ data in general.
+
+
+
diff --git a/src/pages/apps/mcp/index.astro b/src/pages/apps/mcp/index.astro
new file mode 100644
index 0000000..f32b565
--- /dev/null
+++ b/src/pages/apps/mcp/index.astro
@@ -0,0 +1,117 @@
+---
+import DocsLayout from "../../../components/DocsLayout.astro";
+import CodeBlock from "../../../components/CodeBlock.astro";
+
+const claudeCode = `claude mcp add --transport http mdbase https://mcp.mdbase.dev/mcp`;
+
+const firstPrompt = `List my mdbase collections, then describe the first one.`;
+---
+
+
+ What it does
+
+ mdbase MCP is a remote Model Context Protocol server. An MCP client, such
+ as Claude, uses it to read and change records in mdbase collections you
+ have approved. It works the same way whether a collection is hosted by
+ mdbase Connect or stays on your computer and is served by the Connector.
+
+
+ The client never receives your Connect account. You approve one collection
+ at a time, with the exact operations you want to allow, and you can revoke
+ that approval from Connect at any time.
+
+
+ Before you start
+
+ -
+ An mdbase Connect account. Sign up{" "}
+ if you do not have one.
+
+ -
+ At least one collection. A hosted collection works immediately. A
+ collection on your computer needs the Connector{" "}
+ running and online whenever the client uses it.
+
+ -
+ An MCP client that supports remote servers over Streamable HTTP with
+ OAuth sign-in, including dynamic client registration and PKCE.
+
+
+
+ Add the server to your client
+ Every client uses the same endpoint:
+
+
+ In Claude on the web or desktop, add it as a custom connector. In Claude
+ Code, add it from a terminal:
+
+
+
+ Other clients have their own screens for adding a remote MCP server. Give
+ them the endpoint above. There is no API key to paste in. The client
+ discovers the sign-in flow from the server.
+
+
+ Approve a collection
+ When the client first connects, it opens a browser window for sign-in.
+
+ - Sign in to mdbase Connect.
+ - Choose one collection.
+ -
+ Review the permissions. Read this collection is
+ required. Create records, Edit records,{" "}
+ Delete records and Manage definitions{" "}
+ are optional, and you can leave any of them out. They appear when the
+ client asked for write access, which it does by default.
+
+ - Approve. The browser returns you to your client.
+
+
+ Each approval covers one collection. To use more than one, ask the client
+ to connect another collection after setup. See{" "}
+ Connect more collections.
+
+
+ Check the connection
+ Ask your client something that needs the collection:
+
+
+ The client should call list_connections and then{" "}
+ describe_collection. list_connections shows
+ each collection's approved operations and whether it is local or hosted;
+ describe_collection returns its types, fields and contracts.
+
+
+ Next steps
+
+
diff --git a/src/pages/apps/mcp/tools/index.astro b/src/pages/apps/mcp/tools/index.astro
new file mode 100644
index 0000000..08d982f
--- /dev/null
+++ b/src/pages/apps/mcp/tools/index.astro
@@ -0,0 +1,82 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+import reference from "../../../../data/mcp-tools.json";
+
+// Generated by scripts/sync-mcp-tools.mjs from the gateway's registered tools.
+const groups = [
+ {
+ id: "read-tools",
+ label: "Always available",
+ note: "Offered to every client. Tools that act on a collection still need the matching operation approved for it.",
+ tools: reference.tools.filter((tool) => tool.access === "read")
+ },
+ {
+ id: "write-tools",
+ label: "Write tools",
+ note: "Offered only when the client's sign-in scope includes mdbase:write.",
+ tools: reference.tools.filter((tool) => tool.access === "write")
+ }
+];
+
+function behaviour(annotations: Record): string {
+ if (annotations.readOnlyHint) return "Read-only";
+ if (annotations.destructiveHint) return "Destructive";
+ if (annotations.openWorldHint) return "Returns a browser link";
+ return "Changes the collection";
+}
+---
+
+ ({ href: `#${group.id}`, label: group.label }))}
+>
+
+ This page is generated from the tool definitions in the MCP server, so the
+ names, descriptions and inputs match what your client receives. Inputs
+ marked optional can be left out.
+
+
+ {
+ groups.map((group) => (
+
+ {group.label}
+ {group.note}
+ {group.tools.map((tool) => (
+ <>
+
+ {tool.description}
+ {tool.title} · {behaviour(tool.annotations)}
+ {tool.inputs.length > 0 ? (
+
+
+ | Input | Type | Notes |
+
+
+ {tool.inputs.map((input) => (
+
+ {input.name}{input.required ? "" : " (optional)"} |
+ {input.type} |
+ {input.description} |
+
+ ))}
+
+
+ ) : (
+ No inputs.
+ )}
+ >
+ ))}
+
+ ))
+ }
+
+
+
diff --git a/src/pages/apps/mcp/troubleshooting/index.astro b/src/pages/apps/mcp/troubleshooting/index.astro
new file mode 100644
index 0000000..cbba566
--- /dev/null
+++ b/src/pages/apps/mcp/troubleshooting/index.astro
@@ -0,0 +1,121 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+---
+
+
+
+ Failed calls return an error with a code and a message. Most
+ clients show the message; ask the client for the code if it does not.
+ Some rejections, such as conflicts and validation problems, instead return
+ a result marked valid: false with diagnostics that carry the
+ code.
+
+
+ Error codes
+
+
+ | Code | What happened | What to do |
+
+
+
+ connector_offline |
+ The computer that holds this collection is not reachable. |
+ Start the Connector on that computer and check it is online, then retry. |
+
+
+ connector_busy |
+ The Connector is handling other work. |
+ Retry after a moment. |
+
+
+ connector_upgrade_required |
+ The Connector is too old for this request. |
+ Install the latest Connector from Downloads. |
+
+
+ insufficient_collection_access |
+ You did not approve this operation for the collection. |
+ Ask the client to reconnect the collection, then approve the extra permission. |
+
+
+ connection_expired |
+ The collection's approval is no longer valid. |
+ Ask the client to reconnect the collection. |
+
+
+ connection_not_found |
+ The connection ID does not belong to this client's approvals. |
+ Ask the client to list collections again and use a current ID. |
+
+
+ concurrent_modification |
+ The record changed after the client read it. Reported as a diagnostic. |
+ Let the client read the record again before changing it. |
+
+
+ invalid_query |
+ The query does not follow the mdbase query format. |
+ The message names the problem. The client should correct the query. |
+
+
+ encryption_binding_stale |
+ Access to a collection on your computer changed in Connect. |
+ Retry later, or reconnect the collection. |
+
+
+ generation_expired, query_cursor_expired |
+ The query cursor expired. |
+ Run the query again. |
+
+
+ mutation_request_conflict |
+ A mutation_id was reused for a different change. |
+ Use a new ID, or repeat exactly the original change. |
+
+
+
+
+ Sign-in does not finish
+
+ If you closed the browser window or declined the approval, start the
+ connection again from your client. Links from add_connection{" "}
+ and reconnect_collection can be used once and expire after
+ ten minutes, so ask the client for a new one if a link no longer works.
+
+
+ A collection is missing
+
+ The client only sees collections you approved for it. Ask it to connect
+ another collection, or check the{" "}
+ Applications{" "}
+ page in Connect to see which collections mdbase can use.
+
+
+ The client cannot write
+
+ If the client says it has no tool to create or change records, it signed
+ in with read-only scope. Remove the server from the client and add it
+ again. If the tools exist but fail with{" "}
+ insufficient_collection_access, reconnect the collection and
+ approve the write permissions. See{" "}
+ Read and write access.
+
+
+ Self-hosted servers
+
+ Running your own MCP server alongside a self-hosted Connect is covered in
+ the MCP gateway operator guide.
+
+
diff --git a/src/pages/apps/mcp/working-with-records/index.astro b/src/pages/apps/mcp/working-with-records/index.astro
new file mode 100644
index 0000000..f11aa32
--- /dev/null
+++ b/src/pages/apps/mcp/working-with-records/index.astro
@@ -0,0 +1,164 @@
+---
+import DocsLayout from "../../../../components/DocsLayout.astro";
+import CodeBlock from "../../../../components/CodeBlock.astro";
+
+const query = `{
+ "connection_id": "…",
+ "types": ["task"],
+ "where": "status != \\"done\\" && priority >= 3",
+ "order_by": [{ "field": "due", "direction": "asc" }],
+ "limit": 20
+}`;
+
+const paged = `{
+ "connection_id": "…",
+ "types": ["note"],
+ "pagination": "cursor",
+ "limit": 200
+}`;
+
+const update = `{
+ "connection_id": "…",
+ "path": "tasks/renew-passport.md",
+ "patch": { "status": "done" },
+ "if_revision": "…revision from read_record…"
+}`;
+
+const receipt = `{
+ "mutation_receipt": {
+ "request_id": "5b0d…",
+ "retry": "Reuse this request_id as mutation_id when retrying the exact mutation."
+ },
+ "outcome": { … }
+}`;
+---
+
+
+
+ You do not need to call these tools yourself. Your MCP client chooses them.
+ This page explains what they do, so you can tell the client what you want
+ and understand what it did.
+
+
+ Start with the collection
+
+ describe_collection returns the collection's types, their
+ fields and its contracts. A client that reads
+ it first can use your field names and types instead of guessing them from a
+ few files.
+
+
+ Query records
+
+ query_records uses the mdbase query format from the{" "}
+ specification. where is an expression
+ over record fields, types limits the query to declared types,
+ and order_by sorts the results.
+
+
+
+ Record bodies are left out unless include_body is{" "}
+ true. frontmatter_mode chooses between the
+ frontmatter stored in the file (persisted), the values after
+ type defaults and computed fields are applied (effective), or
+ both.
+
+
+ Large result sets
+
+ limit can be at most 1,000. For large result sets, set{" "}
+ pagination to "cursor". Pages may be smaller
+ than limit. Each page's result
+ includes a cursor for the next page, and every page reads from the same
+ snapshot of the collection.
+
+
+
+ A cursor that is no longer needed can be released with{" "}
+ release_query_cursor. Unused cursors expire after a short
+ idle period, from seconds to minutes, so continue promptly. To continue,
+ repeat the same query with the cursor.
+
+
+ Read one record
+
+ read_record takes a path relative to the collection root, such
+ as projects/alpha.md. The result includes the frontmatter,
+ body, the types the record belongs to, and its current{" "}
+ revision.
+
+
+ Create and change records
+
+ These tools are available when the client has write access and you approved
+ the matching operation for the collection.
+
+
+
+ | Tool | Use |
+
+
+ create_record | Create a record from a type, frontmatter and body. The path can be given or left to the collection's path rules. |
+ update_record | Change fields with patch and replace the body, or replace the whole Markdown source with document. A document cannot be combined with the other two. |
+ rename_record | Move a record. Links to it are updated unless update_refs is false or the collection turns this off. |
+ delete_record | Delete a record. With check_backlinks: true, the result lists records whose links to it are now broken. |
+
+
+
+ Changes are validated against every type the record belongs to. At the
+ default validation level, an invalid change is rejected with details and
+ nothing is written. A collection can lower its validation level in its
+ configuration.
+
+
+ Revisions prevent lost edits
+
+ Each record has a revision that changes whenever the file changes. When a
+ client passes the revision it read as if_revision, the change
+ is applied only if nobody else has changed the record since. That includes
+ you, in another app or a text editor.
+
+
+
+ If the record changed in the meantime, the change is not applied. The
+ result is marked valid: false with a{" "}
+ concurrent_modification diagnostic. The client should read the record again and decide what to do with the newer
+ content, rather than overwrite it.
+
+
+ Retrying safely
+
+ Every change returns a receipt containing a request ID:
+
+
+
+ If a call times out and the client cannot tell whether it happened, it can
+ repeat the same change with that ID as mutation_id. The
+ change is applied at most once. Reusing an ID for a different change fails
+ with mutation_request_conflict.
+
+
+ Type definitions
+
+ read_type returns a type definition by name or path.{" "}
+ create_type and update_type take the complete
+ type file as document. Updating a type always requires its
+ current revision. A type change can affect every record of that type, so
+ the Manage definitions permission is approved separately in Connect.
+
+
diff --git a/src/pages/privacy/index.astro b/src/pages/privacy/index.astro
index 92d701c..9f80568 100644
--- a/src/pages/privacy/index.astro
+++ b/src/pages/privacy/index.astro
@@ -53,6 +53,19 @@ import BaseLayout from "../../layouts/BaseLayout.astro";
The provider decrypts them to execute authorized mdbase operations and
acts as a trusted data processor.
+ Email
+
+ Connect uses your verified address for account email: verification and
+ other messages about your account that the service needs to send. Beta
+ accounts may also receive a short welcome and setup message.
+
+
+ Occasionally, we may email account holders about a significant change to
+ mdbase Connect or a new mdbase application. These announcements are
+ infrequent and include an unsubscribe link. We do not send regular
+ product updates or marketing email unless you opt in, and we do not sell
+ or share your address for others' marketing.
+
Application providers
Applications connected through mdbase receive data under the grants users