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
Endpoint https://mcp.vespper.com/mcpTransport Streamable HTTP Auth Authorization: Bearer sk_live_YOUR_KEYTools 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
Agent SDK / Framework
No framework
Agent client
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 ();
Own the loop yourself: connect MCP, call read_document, choose edits, call edit_document, then save the returned base64. Pure MCP client 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 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 ();
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: {
"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: Codex CLI 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: [ mcp_servers . vespper-docx ]
url = "https://mcp.vespper.com/mcp"
bearer_token_env_var = "VESPPER_API_KEY"
Gemini CLI 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: {
"mcpServers" : {
"vespper-docx-mcp" : {
"httpUrl" : "https://mcp.vespper.com/mcp" ,
"headers" : {
"Authorization" : "Bearer $VESPPER_API_KEY"
}
}
}
}
Copilot CLI 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: {
"mcpServers" : {
"vespper-docx-mcp" : {
"type" : "http" ,
"url" : "https://mcp.vespper.com/mcp" ,
"headers" : {
"Authorization" : "Bearer sk_live_YOUR_KEY"
},
"tools" : [ "*" ]
}
}
}
OpenClaw 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: {
"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.
read_document → edit_document → use returned base64 for the next call or save it locally
Tool Arguments Returns read_documentbase64_data for hosted HTTP, or path for local stdio usehtml, approx_tokens, bytesedit_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.