> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vespper.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Connect vespper-docx-mcp to your agent, SDK, or raw MCP client

Get an API key from the [developer dashboard](https://app.vespper.com/onboarding), then connect the hosted **`vespper-docx-mcp`** server.

<Note>
  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.
</Note>

## 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.

```bash theme={null}
export VESPPER_API_KEY=sk_live_YOUR_KEY
```

## 2. Connect MCP

|               |                                          |
| ------------- | ---------------------------------------- |
| **Endpoint**  | `https://mcp.vespper.com/mcp`            |
| **Transport** | Streamable HTTP                          |
| **Auth**      | `Authorization: Bearer sk_live_YOUR_KEY` |
| **Tools**     | `read_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

<Tabs>
  <Tab title="Agent SDK / Framework">
    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.

    ```python theme={null}
    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

    ```typescript theme={null}
    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

    ```typescript theme={null}
    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();
    ```
  </Tab>

  <Tab title="No framework">
    Own the loop yourself: connect MCP, call `read_document`, choose edits, call `edit_document`, then save the returned `base64`.

    ### Pure MCP client

    ```python theme={null}
    import asyncio
    import base64
    import os
    import re
    from pathlib import Path

    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()

                res = (
                    await session.call_tool("read_document", {"base64_data": docx_b64})
                ).structuredContent or {}
                last_p = re.findall(r"<p\b[^>]*>.*?</p>", res["html"], re.DOTALL)[-1]

                out = (
                    await session.call_tool(
                        "edit_document",
                        {
                            "base64_data": docx_b64,
                            "edits": [
                                {
                                    "old": last_p,
                                    "new": last_p + "<p>Hello from the MCP client.</p>",
                                }
                            ],
                            "author": "Vespper Agent",
                        },
                    )
                ).structuredContent or {}

        Path("sample-redlined.docx").write_bytes(base64.b64decode(out["base64"]))


    asyncio.run(main())
    ```

    ### MCP TypeScript SDK

    ```typescript theme={null}
    import { readFileSync, writeFileSync } from "node:fs";
    import { Client } from "@modelcontextprotocol/sdk/client/index.js";
    import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

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

    const client = new Client({
      name: "typescript-sdk-example",
      version: "0.1.0",
    });
    await client.connect(
      new StreamableHTTPClientTransport(
        new URL("https://mcp.vespper.com/mcp"),
        {
          requestInit: {
            headers: { Authorization: `Bearer ${process.env.VESPPER_API_KEY}` },
          },
        }
      )
    );

    const read = await client.callTool({
      name: "read_document",
      arguments: { base64_data: docx_b64 },
    });
    const html = (read.structuredContent as { html: string }).html;

    const last_p = html.match(/<p\b[^>]*>.*?<\/p>/gs)!.at(-1)!;
    const edits = [
      { old: last_p, new: last_p + "<p>Hello from the MCP client.</p>" },
    ];

    const out = await client.callTool({
      name: "edit_document",
      arguments: { base64_data: docx_b64, edits, author: "Vespper Agent" },
    });
    docx_b64 = (out.structuredContent as { base64: string }).base64;

    writeFileSync("sample-redlined.docx", Buffer.from(docx_b64, "base64"));
    await client.close();
    ```
  </Tab>

  <Tab title="Agent client">
    Turnkey clients discover tools from `tools/list` and call them for you.

    ### Cursor

    Cursor supports Streamable HTTP directly. Add this to `.cursor/mcp.json` in your project:

    ```json theme={null}
    {
      "mcpServers": {
        "vespper-docx-mcp": {
          "url": "https://mcp.vespper.com/mcp",
          "headers": {
            "Authorization": "Bearer sk_live_YOUR_KEY"
          }
        }
      }
    }
    ```

    Restart Cursor, open the folder containing `sample.docx`, then ask the agent to read and redline it.

    ### Cursor CLI

    Cursor CLI uses the same project `.cursor/mcp.json` as Cursor IDE:

    ```bash theme={null}
    cursor agent
    ```

    ### Codex CLI

    ```bash theme={null}
    export VESPPER_API_KEY=sk_live_YOUR_KEY
    codex mcp add vespper-docx --url https://mcp.vespper.com/mcp \
      --bearer-token-env-var VESPPER_API_KEY
    ```

    Or add this to `~/.codex/config.toml`:

    ```toml theme={null}
    [mcp_servers.vespper-docx]
    url = "https://mcp.vespper.com/mcp"
    bearer_token_env_var = "VESPPER_API_KEY"
    ```

    ### Gemini CLI

    ```bash theme={null}
    export VESPPER_API_KEY=sk_live_YOUR_KEY
    gemini mcp add --transport http vespper-docx https://mcp.vespper.com/mcp \
      --header "Authorization: Bearer $VESPPER_API_KEY"
    ```

    Or add this to `~/.gemini/settings.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "vespper-docx-mcp": {
          "httpUrl": "https://mcp.vespper.com/mcp",
          "headers": {
            "Authorization": "Bearer $VESPPER_API_KEY"
          }
        }
      }
    }
    ```

    ### Copilot CLI

    ```bash theme={null}
    export VESPPER_API_KEY=sk_live_YOUR_KEY
    copilot mcp add vespper-docx \
      --transport http --url https://mcp.vespper.com/mcp \
      --header "Authorization: Bearer $VESPPER_API_KEY" \
      --tools "*"
    ```

    Or edit `~/.copilot/mcp-config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "vespper-docx-mcp": {
          "type": "http",
          "url": "https://mcp.vespper.com/mcp",
          "headers": {
            "Authorization": "Bearer sk_live_YOUR_KEY"
          },
          "tools": ["*"]
        }
      }
    }
    ```

    ### OpenClaw

    ```bash theme={null}
    export VESPPER_API_KEY=sk_live_YOUR_KEY
    openclaw mcp add vespper-docx \
      --url https://mcp.vespper.com/mcp --transport streamable-http \
      --header "Authorization: Bearer $VESPPER_API_KEY"
    ```

    Tools surface as `vespper-docx__read_document` and `vespper-docx__edit_document`.

    ### Claude Desktop

    Claude Desktop speaks stdio, so use `mcp-remote` as a bridge for the hosted Streamable HTTP server. Put this in `claude_desktop_config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "vespper-docx-mcp": {
          "command": "npx",
          "args": [
            "-y",
            "mcp-remote",
            "https://mcp.vespper.com/mcp",
            "--header",
            "Authorization:${AUTH_HEADER}"
          ],
          "env": {
            "AUTH_HEADER": "Bearer sk_live_YOUR_KEY"
          }
        }
      }
    }
    ```

    Fully quit and restart Claude Desktop, then open a folder containing `sample.docx`.
  </Tab>
</Tabs>

## Tool workflow

```
read_document → edit_document → use returned base64 for the next call or save it locally
```

| Tool            | Arguments                                                    | Returns                                     |
| --------------- | ------------------------------------------------------------ | ------------------------------------------- |
| `read_document` | `base64_data` for hosted HTTP, or `path` for local stdio use | `html`, `approx_tokens`, `bytes`            |
| `edit_document` | `base64_data`, `edits: [{ old, new }]`, `author`             | `ok`, `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

<CardGroup cols={2}>
  <Card title="MCP server" icon="plug" href="/mcp">
    Endpoint, tools, and connection details.
  </Card>

  <Card title="Authentication" icon="key" href="/authentication">
    API key handling and security notes.
  </Card>
</CardGroup>
