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
41 changes: 41 additions & 0 deletions .github/workflows/ci-mcp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI MCP

permissions:
contents: read

on:
pull_request:
paths:
- 'apps/mcp/**'
workflow_dispatch:

concurrency:
group: ci-mcp-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: pnpm

- uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build superdoc (dependency)
run: pnpm run build:superdoc

- name: Build
run: pnpm --prefix apps/mcp run build

- name: Test
run: pnpm --prefix apps/mcp run test
64 changes: 64 additions & 0 deletions .github/workflows/release-mcp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Auto-releases on push to main (@next channel)
# For stable (@latest): cherry-pick commits to stable branch, then manually dispatch this workflow
name: 📦 Release MCP

on:
push:
branches:
- main
paths:
- 'apps/mcp/**'
- '!**/*.md'
workflow_dispatch:

permissions:
contents: write
packages: write

concurrency:
group: release-mcp-${{ github.ref }}
cancel-in-progress: true

jobs:
release:
runs-on: ubuntu-24.04
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ steps.generate_token.outputs.token }}

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
cache: pnpm
registry-url: 'https://registry.npmjs.org'

- uses: oven-sh/setup-bun@v2

- name: Install dependencies
run: pnpm install

- name: Build superdoc (dependency)
run: pnpm run build:superdoc

- name: Build MCP server
run: pnpm --prefix apps/mcp run build

- name: Release
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
LINEAR_TOKEN: ${{ secrets.LINEAR_TOKEN }}
working-directory: apps/mcp
run: pnpx semantic-release
6 changes: 5 additions & 1 deletion apps/docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@
"document-engine/overview",
"document-api/overview",
"document-engine/sdks",
"document-engine/cli"
"document-engine/cli",
{
"page": "document-engine/mcp",
"tag": "NEW"
}
]
},
{
Expand Down
193 changes: 193 additions & 0 deletions apps/docs/document-engine/mcp.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
title: MCP Server
sidebarTitle: MCP Server
description: Give AI agents direct access to .docx files through the Model Context Protocol
keywords: "mcp, model context protocol, ai agents, claude, cursor, windsurf, document automation, llm docx, track changes, comments"
---

The SuperDoc MCP server lets AI agents open, read, edit, and save `.docx` files. It exposes the same operations as the [Document API](/document-api/overview) through the [Model Context Protocol](https://modelcontextprotocol.io) — the open standard for connecting AI tools to agents.

<Info>
The MCP server is in <strong>alpha</strong>. Tools and output formats may change.
</Info>

## Setup

Install once. Your MCP client spawns the server automatically on each conversation.

<Tabs>
<Tab title="Claude Code">
```bash
claude mcp add superdoc -- npx @superdoc-dev/mcp
```
</Tab>
<Tab title="Claude Desktop">
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
"mcpServers": {
"superdoc": {
"command": "npx",
"args": ["@superdoc-dev/mcp"]
}
}
}
```
</Tab>
<Tab title="Cursor">
Add to `~/.cursor/mcp.json`:

```json
{
"mcpServers": {
"superdoc": {
"command": "npx",
"args": ["@superdoc-dev/mcp"]
}
}
}
```
</Tab>
<Tab title="Windsurf">
Add to `~/.codeium/windsurf/mcp_config.json`:

```json
{
"mcpServers": {
"superdoc": {
"command": "npx",
"args": ["@superdoc-dev/mcp"]
}
}
}
```
</Tab>
</Tabs>

## Workflow

Every interaction follows the same pattern: open, read or edit, save, close.

```
superdoc_open → superdoc_find / superdoc_get_text → edit tools → superdoc_save → superdoc_close
```

1. `superdoc_open` loads a `.docx` file and returns a `session_id`
2. `superdoc_find` locates content and returns target addresses
3. Edit tools (`superdoc_insert`, `superdoc_replace`, `superdoc_delete`, `superdoc_format`) use those addresses
4. `superdoc_save` writes changes to disk
5. `superdoc_close` releases the session

## Tools

23 tools in eight groups. All tools take a `session_id` from `superdoc_open`.

### Lifecycle

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_open` | `path` | Open a `.docx` file. Returns `session_id` and file path |
| `superdoc_save` | `session_id`, `out?` | Save to the original path, or to `out` if specified |
| `superdoc_close` | `session_id` | Close the session. Unsaved changes are lost |

### Query

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_find` | `session_id`, `type?`, `pattern?`, `limit?`, `offset?` | Search by node type, text pattern, or both. Returns matches with addresses |
| `superdoc_get_node` | `session_id`, `address` | Get details about a specific node by its address |
| `superdoc_info` | `session_id` | Document metadata: structure summary, node counts, capabilities |
| `superdoc_get_text` | `session_id` | Full plain-text content of the document |

### Mutation

All mutation tools accept `suggest?` — set to `true` to make the edit a tracked change instead of a direct edit.

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_insert` | `session_id`, `text`, `target`, `suggest?` | Insert text at a target position |
| `superdoc_replace` | `session_id`, `text`, `target`, `suggest?` | Replace content at a target range |
| `superdoc_delete` | `session_id`, `target`, `suggest?` | Delete content at a target range |

### Format

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_format` | `session_id`, `style`, `target`, `suggest?` | Toggle formatting on a text range. Styles: `bold`, `italic`, `underline`, `strikethrough` |

### Create

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_create` | `session_id`, `type`, `text?`, `level?`, `at?`, `suggest?` | Create a block element. Types: `paragraph`, `heading` (headings require `level` 1-6) |

### Track changes

Review and resolve tracked changes (suggestions) in the document.

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_list_changes` | `session_id`, `type?`, `limit?`, `offset?` | List tracked changes with type, author, date, and excerpt |
| `superdoc_accept_change` | `session_id`, `id` | Accept a single change, applying it to the document |
| `superdoc_reject_change` | `session_id`, `id` | Reject a single change, reverting it |
| `superdoc_accept_all_changes` | `session_id` | Accept all tracked changes at once |
| `superdoc_reject_all_changes` | `session_id` | Reject all tracked changes at once |

### Comments

Add and manage comments anchored to text ranges.

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_add_comment` | `session_id`, `text`, `target` | Add a comment anchored to a text range |
| `superdoc_list_comments` | `session_id`, `include_resolved?` | List all comments with author, status, and anchored text |
| `superdoc_reply_comment` | `session_id`, `comment_id`, `text` | Reply to an existing comment thread |
| `superdoc_resolve_comment` | `session_id`, `comment_id` | Mark a comment as resolved |

### Lists

| Tool | Input | Description |
| --- | --- | --- |
| `superdoc_insert_list` | `session_id`, `target`, `position`, `text?` | Insert a list item before or after an existing one |
| `superdoc_list_set_type` | `session_id`, `target`, `kind` | Change a list between `ordered` (numbered) and `bullet` |

## Suggesting mode

Set `suggest=true` on any mutation, format, or create tool to make edits appear as tracked changes. The document stays unchanged until someone accepts the suggestions — in Word, in SuperDoc's browser editor, or programmatically via the track changes tools.

## How it works

The MCP server runs as a local subprocess, communicating over stdio. It manages document sessions in memory — each `superdoc_open` creates an Editor instance, and all subsequent operations run against that in-memory state until you `superdoc_save`.

```
AI Agent (Claude, Cursor, Windsurf)
│ MCP protocol (stdio)
@superdoc-dev/mcp
│ Document API
SuperDoc Editor (in-memory)
│ export
.docx file on disk
```

Your documents never leave your machine. The server runs locally, reads files from disk, and writes back to disk.

## Debugging

Test the server directly with the MCP Inspector:

```bash
npx @modelcontextprotocol/inspector -- npx @superdoc-dev/mcp
```

This opens a browser UI where you can call each tool manually and inspect the raw JSON-RPC messages.

## Related

- [CLI](/document-engine/cli) — edit documents from the terminal
- [SDKs](/document-engine/sdks) — typed Node.js and Python wrappers
- [Document API](/document-api/overview) — the in-browser API that defines the operation set
- [AI Agents](/getting-started/ai-agents) — headless mode for server-side AI workflows
13 changes: 8 additions & 5 deletions apps/docs/document-engine/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,31 @@ keywords: "document engine, document api, sdk, cli, headless docx, document auto
Document Engine is in <strong>alpha</strong> and subject to breaking changes while the contract and adapters continue to evolve.
</Info>

Document Engine is the programmatic surface of SuperDoc. It gives you three ways to read and edit `.docx` files without a visible editor:
Document Engine is the programmatic surface of SuperDoc. It gives you four ways to read and edit `.docx` files without a visible editor:

| Surface | Use case | Runtime |
| --- | --- | --- |
| [Document API](/document-api/overview) | In-browser editing via `editor.doc.*` methods | Browser (ProseMirror) |
| [SDKs](/document-engine/sdks) | Node.js and Python wrappers for backend automation | Node / Python |
| [CLI](/document-engine/cli) | Terminal commands for scripting and CI pipelines | Any shell |
| [MCP Server](/document-engine/mcp) | AI agent access via the Model Context Protocol | Local subprocess |

All three surfaces share the same operation set. An operation like `find` is available as `editor.doc.find()` in the browser, `superdoc.doc.find()` in the SDK, and `superdoc find` in the CLI.
All four surfaces share the same operation set. An operation like `find` is available as `editor.doc.find()` in the browser, `superdoc.doc.find()` in the SDK, `superdoc find` in the CLI, and `superdoc_find` in MCP.

## How it works

The Document API defines the canonical operations. The CLI wraps them in a stdio-based process. The SDKs manage the CLI process and expose typed methods for each operation.
The Document API defines the canonical operations. The CLI and MCP server wrap them for different consumers. The SDKs manage the CLI process and expose typed methods.

```
Document API (source of truth)
→ CLI (stdio process)
→ Node SDK / Python SDK (typed wrappers)
→ CLI (terminal interface)
→ MCP Server (AI agent interface)
→ Node SDK / Python SDK (typed wrappers over CLI)
```

## Where to start

- **Building a web editor?** Start with the [Document API](/document-api/overview).
- **Automating documents from a backend?** Start with the [SDKs](/document-engine/sdks).
- **Scripting from the terminal or CI?** Start with the [CLI](/document-engine/cli).
- **Connecting AI agents (Claude, Cursor, Windsurf)?** Start with the [MCP Server](/document-engine/mcp).
45 changes: 45 additions & 0 deletions apps/mcp/.releaserc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* eslint-env node */
const branch = process.env.GITHUB_REF_NAME || process.env.CI_COMMIT_BRANCH;

const config = {
branches: [
{ name: 'stable', channel: 'latest' },
{ name: 'main', prerelease: 'next', channel: 'next' },
],
tagFormat: 'mcp-v${version}',
plugins: [
'semantic-release-commit-filter',
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
['@semantic-release/npm'],
],
};

const isPrerelease = config.branches.some((b) => typeof b === 'object' && b.name === branch && b.prerelease);

if (!isPrerelease) {
config.plugins.push([
'@semantic-release/git',
{
assets: ['package.json'],
message: 'chore(mcp): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
},
]);
}

// Linear integration - labels issues with version on release
config.plugins.push(['semantic-release-linear-app', {
teamKeys: ['SD'],
addComment: true,
packageName: 'mcp',
commentTemplate: 'shipped in {package} {releaseLink} {channel}'
}]);

config.plugins.push([
'@semantic-release/github',
{
successComment: ':tada: This ${issue.pull_request ? "PR" : "issue"} is included in **@superdoc-dev/mcp** v${nextRelease.version}\n\nThe release is available on [GitHub release](${releases.find(release => release.pluginName === "@semantic-release/github").url})',
}
]);

module.exports = config;
Loading
Loading