Skip to main content
Get an API key from the developer dashboard, then connect the hosted vespper-docx-mcp server.
The MCP tool descriptions carry the editing rules: complete HTML-block anchors, lazy classes, batching, and Word tracked-change semantics. Use tools/list descriptions instead of copying those rules into your own prompt.

1. Get a key

Create a key in the developer dashboard. Keys start with sk_live_; keep them secret and use them only from server-side code, local tools, or agent config.
export VESPPER_API_KEY=sk_live_YOUR_KEY

2. Connect MCP

Endpointhttps://mcp.vespper.com/mcp
TransportStreamable HTTP
AuthAuthorization: Bearer sk_live_YOUR_KEY
Toolsread_document, edit_document
Put a local Word file named sample.docx beside the example script or in the folder opened by your agent, then try:
Read sample.docx, then add the word hello to the end of the document.

3. Pick your stack

SDKs and frameworks own the agent loop. The important part is to expose the MCP-discovered read_document and edit_document tools to the model and preserve their descriptions.

OpenAI Agents SDK

Save as main.py beside sample.docx, then run it with VESPPER_API_KEY set.
import asyncio
import base64
import os
from pathlib import Path

from agents import Agent, Runner, function_tool
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client


async def main():
    docx_b64 = base64.b64encode(Path("sample.docx").read_bytes()).decode()

    async with streamablehttp_client(
        "https://mcp.vespper.com/mcp",
        headers={"Authorization": f"Bearer {os.environ['VESPPER_API_KEY']}"},
    ) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            desc = {t.name: t.description for t in (await session.list_tools()).tools}

            @function_tool(description_override=desc["read_document"])
            async def read_document():
                return (
                    await session.call_tool(
                        "read_document", {"base64_data": docx_b64}
                    )
                ).structuredContent

            @function_tool(
                description_override=desc["edit_document"], strict_mode=False
            )
            async def edit_document(edits: list[dict[str, str]]):
                nonlocal docx_b64
                out = (
                    await session.call_tool(
                        "edit_document",
                        {
                            "base64_data": docx_b64,
                            "edits": edits,
                            "author": "Vespper Agent",
                        },
                    )
                ).structuredContent or {}
                docx_b64 = out["base64"]
                return out

            await Runner.run(
                Agent(
                    name="Example Agent",
                    tools=[read_document, edit_document],
                    model="gpt-5.5",
                ),
                "Add the word hello to the end of the document",
            )

    Path("sample-redlined.docx").write_bytes(base64.b64decode(docx_b64))


asyncio.run(main())

Mastra

import "dotenv/config";
import { readFileSync, writeFileSync } from "node:fs";
import { MCPClient } from "@mastra/mcp";
import { Agent } from "@mastra/core/agent";
import { createTool } from "@mastra/core/tools";
import { z } from "zod";

let docx_b64 = readFileSync("sample.docx").toString("base64");

const mcp = new MCPClient({
  servers: {
    vespperDocx: {
      url: new URL("https://mcp.vespper.com/mcp"),
      requestInit: {
        headers: { Authorization: `Bearer ${process.env.VESPPER_API_KEY}` },
      },
    },
  },
});
const tools: any = await mcp.listTools();

const read_document = createTool({
  id: "read_document",
  description: tools.vespperDocx_read_document.description,
  execute: async () =>
    tools.vespperDocx_read_document.execute({ base64_data: docx_b64 }),
});

const edit_document = createTool({
  id: "edit_document",
  description: tools.vespperDocx_edit_document.description,
  inputSchema: z.object({
    edits: z.array(z.object({ old: z.string(), new: z.string() })),
  }),
  execute: async ({ edits }) => {
    const out = await tools.vespperDocx_edit_document.execute({
      base64_data: docx_b64,
      edits,
      author: "Vespper Agent",
    });
    docx_b64 = out.base64;
    return out;
  },
});

const agent = new Agent({
  id: "example-agent",
  name: "Example Agent",
  instructions: "Use read_document then edit_document.",
  model: "openai/gpt-5.5",
  tools: { read_document, edit_document },
});

await agent.generate("Add the word hello to the end of the document");
writeFileSync("sample-redlined.docx", Buffer.from(docx_b64, "base64"));
await mcp.disconnect();

Vercel AI SDK

import "dotenv/config";
import { readFileSync, writeFileSync } from "node:fs";
import { createMCPClient } from "@ai-sdk/mcp";
import { generateText, stepCountIs, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

let docx_b64 = readFileSync("sample.docx").toString("base64");

const mcp = await createMCPClient({
  transport: {
    type: "http",
    url: "https://mcp.vespper.com/mcp",
    headers: { Authorization: `Bearer ${process.env.VESPPER_API_KEY}` },
  },
});
const mcpTools = (await mcp.tools()) as any;

const read_document = tool({
  description: mcpTools.read_document.description,
  inputSchema: z.object({}),
  execute: async () =>
    mcpTools.read_document.execute({ base64_data: docx_b64 }),
});

const edit_document = tool({
  description: mcpTools.edit_document.description,
  inputSchema: z.object({
    edits: z.array(z.object({ old: z.string(), new: z.string() })),
  }),
  execute: async ({ edits }) => {
    const out = await mcpTools.edit_document.execute({
      base64_data: docx_b64,
      edits,
      author: "Vespper Agent",
    });
    docx_b64 = out.base64;
    return out;
  },
});

await generateText({
  model: openai("gpt-5.5"),
  tools: { read_document, edit_document },
  stopWhen: stepCountIs(5),
  prompt: "Add the word hello to the end of the document",
});

writeFileSync("sample-redlined.docx", Buffer.from(docx_b64, "base64"));
await mcp.close();

Tool workflow

read_document → edit_document → use returned base64 for the next call or save it locally
ToolArgumentsReturns
read_documentbase64_data for hosted HTTP, or path for local stdio usehtml, approx_tokens, bytes
edit_documentbase64_data, edits: [{ old, new }], authorok, count, bytes, base64, message
There is no server-side document session. Pass the current document bytes on every call. After edit_document, continue with the returned base64 or decode it to save a .docx.

Editing rules

  • Call read_document first and copy old anchors from its exact HTML.
  • Prefer one complete block (<p>, <li>, heading, row) per edit.
  • Use class="..." in old and new to keep existing classes unless changing the class is the edit.
  • Batch independent edits in one edit_document call; they reconcile in parallel.
  • If an edit fails, nothing is applied. Fix the listed edit and resend the whole batch.
  • Successful edits remain visible as tracked changes until a human accepts or rejects them in Word.

Next steps

MCP server

Endpoint, tools, and connection details.

Authentication

API key handling and security notes.