MCP server
Expose DOCX review tools over the Model Context Protocol. Transport-agnostic McpServer, a stdio helper, and hosting patterns with your auth and storage.
McpServer exposes the agent tools over the Model Context Protocol. Use it when an MCP-compatible client should read, comment, or redline a DOCX through your host process.
import { McpServer } from '@docx-editor.dev/agents/mcp';
import { DocxReviewer, createReviewerBridge } from '@docx-editor.dev/agents';
const reviewer = await DocxReviewer.fromBuffer(buffer, 'MCP Reviewer');
const server = new McpServer(createReviewerBridge(reviewer), {
name: 'acme-docx-review',
version: '1.0.0',
});The server wraps any EditorBridge. In practice that is a reviewer bridge (file-backed, server-side); the same class would accept a live-editor bridge since the interface is identical.
Transport-agnostic handle()
The core is one synchronous method:
handle(message: JsonRpcMessage): JsonRpcResponse | null- Accepts one JSON-RPC message; returns the response to send back, or
nullfor notifications (which never get a reply). - It does not throw for protocol or tool failures. Protocol-level problems come back as JSON-RPC errors; tool failures come back as successful responses with
isError: truein the MCP content payload, per MCP convention. - Implements
initialize,tools/list,tools/call, andping. Protocol version:2025-06-18(overridable via options).
tools/list advertises every built-in tool with its JSON-Schema inputs. tools/call dispatches through the same executeToolCall the other integration shapes use. Note the headless caveats: against a reviewer bridge, read_selection and the page tools fail with descriptive errors, since there is no user and no rendered pages.
Because handle() is sync and transport-free, you can put it behind anything: HTTP, SSE, WebSocket, a queue consumer.
Hosting over HTTP
The host owns everything around the protocol: authentication, loading the right document for the caller, and persisting the result. The server only translates JSON-RPC into bridge calls.
import express from 'express';
import { McpServer } from '@docx-editor.dev/agents/mcp';
import { DocxReviewer, createReviewerBridge } from '@docx-editor.dev/agents';
const app = express();
app.use(express.json());
app.post('/mcp/:docId', requireAuth, async (req, res) => {
// You own storage: load the caller's document...
const buffer = await loadDocxForUser(req.user, req.params.docId);
const reviewer = await DocxReviewer.fromBuffer(buffer, req.user.name);
const server = new McpServer(createReviewerBridge(reviewer), {
name: 'acme-docx-review',
version: '1.0.0',
});
const reply = server.handle(req.body); // sync; errors are returned as JSON-RPC/MCP payloads
if (reply) res.json(reply);
else res.status(204).end(); // notification: no reply by spec
// ...and you own persistence: flush mutations back to storage.
await saveDocxForUser(req.user, req.params.docId, await reviewer.toBuffer());
});For high-traffic hosts, keep a reviewer per session instead of re-parsing per request; the server and bridge are plain objects with no global state.
Stdio transport
For local, single-document use there is a ready-made stdio loop (newline-delimited JSON-RPC on stdin/stdout):
import { readFile } from 'node:fs/promises';
import { runStdioServer } from '@docx-editor.dev/agents/mcp';
import { DocxReviewer, createReviewerBridge } from '@docx-editor.dev/agents';
const buffer = await readFile(process.argv[2]);
const reviewer = await DocxReviewer.fromBuffer(buffer, 'MCP Reviewer');
runStdioServer(createReviewerBridge(reviewer), { name: 'docx-review', version: '1.0.0' });runStdioServer returns a handle with the underlying server, a feed() method (used by tests), and close(). It defaults to process.stdin / process.stdout but accepts any duck-typed streams, caps the frame buffer at 1 MiB, and answers malformed frames with JSON-RPC parse errors instead of crashing.
Stdio is one document per config
A stdio MCP bin binds to one file at launch, and clients like Claude Desktop load their server list at startup. That does not fit a multi-document product. For anything beyond local experiments, host the server yourself over HTTP with your own auth and per-request document loading, as above.
When to use MCP
Pick MCP when the agent side already exists and supports MCP: a desktop assistant, an agent platform, or an internal orchestrator. The MCP client discovers and invokes tools through the protocol; it does not import this library.
Pick the other shapes when you control both ends:
- Building a product UI: the live editor bridge gives you streaming, in-document feedback that MCP cannot.
- Building a server pipeline you also own: calling DocxReviewer directly is simpler than adding JSON-RPC framing.
Next steps
- DocxReviewer reference, the bridge these servers wrap
- Tools reference, what
tools/listadvertises - AI redlining, the review semantics behind the tools
- API reference
DocxReviewer
DocxReviewer API reference: parse a DOCX buffer in Node, add comments and tracked changes, accept or reject, batch-apply an AI review, serialize back.
Bring your own agent
Wire the DOCX tools into Vercel AI SDK, OpenAI, Anthropic, LangChain, or any runtime that accepts OpenAI-style function schemas, with one shared executor.