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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,8 @@ apps/backend/storage/
Dockerfile*
docker-compose*.yml
.dockerignore

# ---------- Non-runtime workspaces ----------
# The MCP server is a local development companion; nothing in the image depends
# on it, and excluding it keeps its edits out of the build context cache key.
apps/mcp
8 changes: 8 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"wordyme": {
"command": "pnpm",
"args": ["--filter", "@repo/mcp", "--silent", "start"]
}
}
}
116 changes: 116 additions & 0 deletions MCP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Connect Claude to your wiki

WordyMe ships an [MCP](https://modelcontextprotocol.io) server. Point Claude Code or
Claude Desktop at your wiki and Claude can search it, read any page, write new notes and
revise existing ones — in plain Markdown, as your own account, with every change recorded
as a restorable revision.

```text
You: Read my DOCKERHUB document and create a note called "MCP Test"
with a summary table and a mermaid diagram of the release flow.

Claude: Created "MCP Test" in Color Workspace — open it in WordyMe:
the table renders, the diagram draws, Revisions History says "via Claude".
```

## Why this one is different

**It speaks the editor's own language.** Claude writes Markdown; WordyMe's editor converts
it with the exact transformers it uses when _you_ type Markdown. Headings, lists, tables,
code blocks and even Mermaid diagram fences become real rich-text nodes — not an
approximation. Reading goes the other way through the same transformers, so Claude sees
your pages as faithful Markdown.

**It is you, not a second user.** WordyMe is deliberately single-user. The server signs in
with your credentials and acts on your behalf — the same delegation model every
connected app uses — so there is no "AI account" to manage and no sharing model to bolt
on.

**Nothing is ever lost.** Every content write — creating a page or revising one — is saved
as a new revision named **"via Claude"**. Earlier revisions stay in Revisions History and
can be restored with one click. Moving a page only changes where it sits in the tree. The
server never deletes anything.

**Local by design.** It runs on your machine, next to your clone of the repository.
Credentials live in a git-ignored file you control. Nothing about the WordyMe app or its
Docker image changes.

## What Claude can do

| Tool | What it does |
| ------------------ | -------------------------------------------------------------- |
| `list_spaces` | List your Spaces |
| `list_documents` | List the documents and folders of one Space |
| `search_documents` | Full-text search across the wiki, with snippets |
| `read_document` | Return a page as Markdown |
| `create_note` | Create a note from Markdown, optionally inside a folder |
| `update_document` | Replace a page body with new Markdown, saved as a new revision |
| `move_document` | Move a page or folder into a folder, or back to the Space root |

Things people ask once it is connected:

- _"Search my wiki for everything about backups and summarise it."_
- _"Read my meeting notes from this week and draft the follow-up email."_
- _"Turn this rough list into a properly structured page with a table."_
- _"Add a Mermaid diagram of the deployment flow to the Docker page."_

## Setup in three steps

Requires a running WordyMe and a clone of this repository.

1. **Credentials** — copy the example and fill in your login:

```bash
cp apps/mcp/.env.example apps/mcp/.env
```

`WORDYME_URL` is where the API answers: `http://localhost:3000` for `pnpm dev`,
`http://localhost:8080` for Docker, or your instance URL.

2. **Claude Code** (desktop app or CLI) — nothing to install. The repository carries a
project-level `.mcp.json`; open the repository in Claude Code and approve the
`wordyme` server when prompted.

3. **Claude Desktop** — add to `claude_desktop_config.json`:

```json
{
"mcpServers": {
"wordyme": {
"command": "pnpm",
"args": ["--dir", "/path/to/WordyMe", "--filter", "@repo/mcp", "--silent", "start"]
}
}
}
```

Then ask Claude to list your Spaces.

## How it works

- **Conversion** runs a headless copy of the WordyMe editor in Node — the same node list
and the same Markdown transformers the browser uses — so output is what the editor
itself would have produced.
- **Authentication** uses the backend's existing Better Auth bearer-token support: one
sign-in at first use, an expired session re-signs transparently.
- **Writes** go through the normal document and revision APIs; MCP-created notes are
structurally identical to notes created in the editor.

The implementation lives in [`apps/mcp`](apps/mcp/README.md).

## Honest limits

- Fidelity is bounded by Markdown. Standard constructs and the editor's fenced
extensions (` ```mermaid `) round-trip; exotic nodes such as sketches, scores and
stickies degrade to plain text when read.
- Nested list items need 4-space indentation.
- Claude acts with your full account; the server is meant for your own machine.

## What's next

- **Built into the image.** An opt-in `/mcp` endpoint inside the WordyMe container, so
self-hosters connect Claude with a URL and no local setup.
- **Proper OAuth.** WordyMe as the identity provider — Claude signs in through WordyMe's
own consent screen, with revocable tokens and read-only scopes.
- **claude.ai.** With the two above, publicly reachable instances connect from the web app
as well.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ This project uses [Turborepo](https://turborepo.com) for managing a monorepo wit
- User profiles with avatar and cover images
- Editor settings and preferences
- Real-time updates via WebSocket
- MCP server for Claude Code and Claude Desktop — see [MCP.md](MCP.md)

### Performance

Expand Down Expand Up @@ -185,6 +186,13 @@ frontend URL. The API reference is at <http://localhost:8080/api/docs>.
Already running an older version? Ports and origins changed; see
[Upgrading](DOCKER.md#upgrading-from-a-version-before-the-all-in-one-image).

## Connect Claude (MCP)

WordyMe™ ships an MCP server, so Claude Code and Claude Desktop can search your wiki, read
any page and write notes — in Markdown, as your own account, with every content change
saved as a restorable revision. See [MCP.md](MCP.md) for the three-step setup and what it
can do.

## Development Workflow

### Prerequisites
Expand Down
6 changes: 6 additions & 0 deletions apps/mcp/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copy to .env (git-ignored). Variables already set in the environment take precedence.
# The origin the WordyMe API answers on: http://localhost:3000 for `pnpm dev`,
# http://localhost:8080 for Docker, or your instance URL.
WORDYME_URL=http://localhost:3000
WORDYME_EMAIL=you@example.com
WORDYME_PASSWORD=your-password
74 changes: 74 additions & 0 deletions apps/mcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# @repo/mcp

A local MCP (Model Context Protocol) server that exposes a WordyMe instance to AI
clients such as Claude Code and Claude Desktop. Markdown in, Markdown out — content is
converted with the editor's own transformers, so what the AI writes is exactly what the
editor would produce if you typed the same Markdown yourself.

The server acts **as your account** (delegation — WordyMe stays single-user). Every content
change it makes is saved as a new revision named **"via Claude"**, restorable from
Revisions History; moving a document only changes its place in the tree.

## Tools

| Tool | What it does |
| ------------------ | ------------------------------------------------------------- |
| `list_spaces` | List the Spaces in the wiki |
| `list_documents` | List one Space's documents and folders |
| `search_documents` | Full-text search with snippets |
| `read_document` | Return a document as Markdown |
| `create_note` | Create a note from Markdown |
| `update_document` | Replace a document body with new Markdown (new revision) |
| `move_document` | Move a document or folder into a folder, or to the Space root |

## Setup

Requires a running WordyMe instance and its owner credentials. `WORDYME_URL` is the
origin the API answers on:

| How WordyMe runs | `WORDYME_URL` |
| ----------------------------- | ---------------------------------------------- |
| `pnpm dev` (backend directly) | `http://localhost:3000` |
| Docker / self-hosted | `http://localhost:8080` (or your instance URL) |

### 1. Credentials

```bash
cp apps/mcp/.env.example apps/mcp/.env
```

Fill in `apps/mcp/.env`. It is git-ignored and read by the server at startup; variables
already present in the environment take precedence over the file.

### 2. Claude Code (desktop app or CLI)

Nothing to install: the repository ships a project-level `.mcp.json` that starts the
server with `pnpm --filter @repo/mcp --silent start`. Open the repository in Claude Code
and approve the project MCP server when prompted — new sessions then have the `wordyme`
tools. Check status with `/mcp` inside a session.

### Claude Desktop

`claude_desktop_config.json`:

```json
{
"mcpServers": {
"wordyme": {
"command": "pnpm",
"args": ["--dir", "/path/to/WordyMe", "--filter", "@repo/mcp", "--silent", "start"]
}
}
}
```

Credentials never live in MCP client configuration or in this repository — only in
`apps/mcp/.env` (or the process environment).

## Notes

- `pnpm smoke` runs the Markdown round-trip self-test (no WordyMe instance needed).
- Content fidelity is bounded by Markdown: standard constructs plus the editor's fenced
extensions (```mermaid diagrams) round-trip; exotic nodes (sketches, scores, stickies)
degrade to plain text. Nested list items need 4-space indentation.
- The server never deletes anything and every write is a new revision.
3 changes: 3 additions & 0 deletions apps/mcp/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { config } from '@repo/eslint-config/base';

export default config;
36 changes: 36 additions & 0 deletions apps/mcp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@repo/mcp",
"version": "0.0.0",
"private": true,
"type": "module",
"engines": {
"node": ">=22.9.0"
},
"scripts": {
"build": "esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --banner:js=\"import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);\" --outfile=dist/index.js --loader:.css=empty --loader:.svg=empty --loader:.png=empty --loader:.jpg=empty --loader:.woff=empty --loader:.woff2=empty --log-level=warning",
"start": "pnpm build && node --env-file-if-exists=.env dist/index.js",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"smoke": "esbuild scripts/smoke-markdown.ts --bundle --platform=node --target=node20 --format=esm --banner:js=\"import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);\" --outfile=dist/smoke.js --loader:.css=empty --loader:.svg=empty --loader:.png=empty --loader:.jpg=empty --loader:.woff=empty --loader:.woff2=empty --log-level=warning && node dist/smoke.js",
"lint": "eslint .",
"check-types": "tsc --noEmit"
},
"dependencies": {
"@happy-dom/global-registrator": "^20.11.2",
"@lexical/markdown": "^0.41.0",
"@modelcontextprotocol/sdk": "^1.30.0",
"@repo/backend": "workspace:*",
"@repo/editor": "workspace:*",
"@repo/lib": "workspace:*",
"@repo/sdk": "workspace:*",
"axios": "^1.19.0",
"lexical": "^0.41.0",
"zod": "^3.25.76"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/node": "^26.2.0",
"esbuild": "^0.25.12",
"eslint": "^10.8.0",
"typescript": "^6.0.3"
}
}
49 changes: 49 additions & 0 deletions apps/mcp/scripts/smoke-markdown.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* SPDX-FileCopyrightText: 2026 TeamCoderz Ltd <legal@teamcoderz.org>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { fromMarkdown, toMarkdown } from '../src/markdown.js';

const sample = `# Smoke Test

Some **bold** and *italic* text with a [link](https://wordy.me).

## A list

- one
- two
- nested

## A table

| Col A | Col B |
| ----- | ----- |
| 1 | 2 |

## Code

\`\`\`ts
const x: number = 1;
\`\`\`

\`\`\`mermaid
graph TD; A-->B;
\`\`\`
`;

const state = fromMarkdown(sample);
const roundTripped = toMarkdown(state);

const rootTypes = state.root.children.map((node) => node.type);
if (rootTypes[0] !== 'page-setup' || rootTypes[1] !== 'page') {
throw new Error(`Expected the editor's page scaffold, got root children: ${rootTypes}`);
}
if (!roundTripped.includes('## A table') || !roundTripped.includes('```mermaid')) {
throw new Error('Round-trip lost block structure');
}

console.log('--- root structure ---');
console.log(rootTypes.join(' > '));
console.log('--- markdown round-trip ---');
console.log(roundTripped);
Loading
Loading