Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/update-connect-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ dist/
/public/theme-bootstrap.js
/src/data/contracts.json
/src/data/conformance.json
/src/data/mcp-tools.json
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
83 changes: 83 additions & 0 deletions scripts/sync-mcp-tools.mjs
Original file line number Diff line number Diff line change
@@ -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";
}
63 changes: 62 additions & 1 deletion src/data/docs-sections.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -51,6 +52,66 @@ export const docsSections: Record<DocsSectionId, DocsSection> = {
}
]
},
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",
Expand Down
99 changes: 99 additions & 0 deletions src/pages/apps/editor/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
import DocsLayout from "../../../components/DocsLayout.astro";
---

<DocsLayout
section="editor"
title="Get started"
description="mdbase Editor is a browser editor for a whole mdbase collection: notes, frontmatter, links and types."
active="overview"
headings={[
{ href: "#what", label: "What it is" },
{ href: "#open", label: "Open a collection" },
{ href: "#layout", label: "Find your way around" },
{ href: "#status", label: "Connection status" },
{ href: "#switch", label: "Switch collections" },
{ href: "#connect", label: "The Connect workspace" }
]}
>
<h2 id="what">What it is</h2>
<p>
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.
</p>
<p>
It works with collections hosted by mdbase Connect and with collections
that stay on your computer and are served by the{" "}
<a href="/downloads/">Connector</a>.
</p>

<h2 id="open">Open a collection</h2>
<ol>
<li>Go to <a href="https://editor.mdbase.dev/">editor.mdbase.dev</a>.</li>
<li>
Sign in to mdbase Connect. If you do not have an account,{" "}
<a href="https://connect.mdbase.dev/signup">sign up</a> first.
</li>
<li>
Choose a collection. The list includes collections hosted by mdbase and
collections on your connected computers.
</li>
<li>
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.
</li>
</ol>
<p>
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.
</p>

<h2 id="layout">Find your way around</h2>
<table class="api-table">
<thead>
<tr><th>Area</th><th>What it holds</th></tr>
</thead>
<tbody>
<tr><td>Collection rail</td><td>Switches between All notes, Types and Settings, filters notes by folder, tag or type, shows the connection status and opens the Connect workspace.</td></tr>
<tr><td>Note list</td><td>The notes, and attached files, in the current filter. Sort by modified date, title or path from view options.</td></tr>
<tr><td>Editor</td><td>The note's Markdown body, with the title, properties and backlinks around it.</td></tr>
</tbody>
</table>
<p>
Press <kbd>Ctrl</kbd> <kbd>P</kbd> (<kbd>⌘</kbd> <kbd>P</kbd> on a Mac)
to open any note by name, and <kbd>?</kbd> to see the main shortcuts. On a
phone, each level of navigation is a separate screen.
</p>

<h2 id="status">Connection status</h2>
<p>
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,
<strong>Use this computer</strong> in the rail, or{" "}
<strong>Allow local access</strong> under Settings → Connection, lets the
browser reach the Connector directly.
</p>

<h2 id="switch">Switch collections</h2>
<p>
Open the collection switcher from the rail. It lists collections you
opened before in this browser, and <strong>Connect another collection</strong>{" "}
starts a new approval. <strong>Forget from this browser</strong> in
Settings removes a collection from the Editor without changing its files.
</p>

<h2 id="connect">The Connect workspace</h2>
<p>
<a href="https://editor.mdbase.dev/connect">editor.mdbase.dev/connect</a>{" "}
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.
</p>
</DocsLayout>
Loading
Loading