@vantageos/vantage-doc-forge-mcp
v0.2.0
Published
MCP server — doc-forge surface layer. Exposes 7 tools (list_templates, get_template, render_doc, upload_template, extract_template_schema, render_history, create_template_from_docx) to any MCP client (Claude Desktop/Code, ChatGPT, Cursor, Grok).
Maintainers
Readme
@vantageos/vantage-doc-forge-mcp
MCP server surface layer for VantageOS Document Forge. Exposes 7 document generation tools to any MCP client (Claude Desktop, Claude Code, ChatGPT, Cursor, Grok).
Installation / Quickstart
stdio — Claude Desktop / Claude Code (local dev)
Run directly from npm without a local checkout:
{
"mcpServers": {
"vantage-doc-forge": {
"command": "npx",
"args": ["-y", "@vantageos/vantage-doc-forge-mcp"],
"env": {
"CONVEX_URL": "https://your-deployment.convex.cloud",
"CONVEX_DOC_FORGE_SERVICE_SECRET": "your-service-secret"
}
}
}
}Place this in:
- Claude Desktop —
~/Library/Application Support/Claude/claude_desktop_config.json(macOS) or%APPDATA%\Claude\claude_desktop_config.json(Windows) - Claude Code —
.claude/settings.jsonat repo root
HTTP — Railway cloud (multi-client: ChatGPT / Cursor / Grok / Claude.ai connector)
Transport V1 decision: HTTP on Railway with Bearer auth is the cloud transport for multi-client production deploys. Set MCP_TRANSPORT=http in your Railway service and point clients to the /mcp endpoint.
{
"mcpServers": {
"vantage-doc-forge": {
"url": "https://your-railway-deploy.up.railway.app/mcp",
"headers": {
"Authorization": "Bearer your-admin-token"
}
}
}
}For Claude.ai custom connector or ChatGPT plugins, use the same url and Authorization header.
Architecture
Part of the trinité doc-forge:
@vantageos/convex-doc-forge— Convex component (data layer: templates, jobs, audit)vantage-doc-forge-renderer— Python FastAPI sidecar on Railway (compute: docxtpl + LibreOffice)@vantageos/vantage-doc-forge-mcp— MCP server surface (this package)
The MCP server wraps the Convex component via ConvexHttpClient. Rendering logic is delegated to the Convex action which calls the renderer sidecar.
Tools (7)
list_templates
List doc-forge templates available for a tenant. Use this whenever a user asks what templates are available or wants to browse the template catalog.
{
"tenant_id": "org_acme",
"team": "hr",
"type": "docxtpl"
}get_template
Fetch a single template by Convex document ID. Use this to retrieve full metadata (input_schema, renderer_kind, output_formats) before calling render_doc.
{
"template_id": "jd7abc123",
"tenant_id": "org_acme"
}render_doc
Render a document from a template and context data. Returns a signed URL (TTL 30 days) pointing to the rendered file. The context object must conform to the template's input_schema.
{
"template_id": "jd7abc123",
"tenant_id": "org_acme",
"context": {
"consultant_name": "Test User",
"client_name": "Test Corp",
"date": "2026-06-21",
"amount": 2500
},
"output_format": "pdf"
}Output formats: docx | pdf | pptx | doc
upload_template
Register a new template asset in the Convex component. Use this when a user provides an already-jinjatized .docx/.pptx template (with {{ variable }} placeholders already in place) and wants to register it. For raw Word files without Jinja2 placeholders, use create_template_from_docx instead.
{
"name": "hr-onboarding-contract-v1",
"team": "hr",
"tenant_id": "org_acme",
"renderer_kind": "docxtpl",
"asset_url": "https://storage.convex.cloud/signed/abc123",
"input_schema": {
"type": "object",
"properties": {
"employee_name": { "type": "string" },
"start_date": { "type": "string" }
},
"required": ["employee_name", "start_date"]
},
"output_formats": ["docx", "pdf"]
}extract_template_schema
Analyse an existing .docx or .pptx template to discover its {{ variable }} Jinja2/docxtpl declarations. Calls the renderer sidecar's /v1/extract-schema endpoint. Returns variables_found (list) and suggested_schema (JSON Schema object) ready to pass to upload_template.
Use this when a template already contains {{ }} placeholders and you need to extract its schema. To convert a raw Word file into a Jinja2 template in one call, use create_template_from_docx instead.
{
"template_url": "https://example.com/template.docx"
}Returns: { "variables_found": ["employee_name", "start_date"], "suggested_schema": { ... } }
render_history
List past render jobs for a tenant, ordered newest-first. Use this when a user asks for recent documents rendered, job status, or signed URLs of previously rendered files.
{
"tenant_id": "org_acme",
"since": "2026-06-01T00:00:00Z"
}create_template_from_docx
Use this whenever a user provides a raw (non-jinjatized) .docx Word file and wants to register it as a reusable template. This is the self-serve onboarding path: raw Word → registered template in one call.
Calls the renderer /v1/jinjatize to detect placeholders (e.g. [BRACKET] patterns), convert them to {{ variables }}, extract a JSON Schema, upload the jinjatized file to Convex, and register the template record. Returns template_id (ready for render_doc), variables_found, unresolved_conflicts, and placeholders_detected/placeholders_converted.
Difference from extract_template_schema: extract_template_schema reads {{ }} that already exist; create_template_from_docx creates them from a raw file.
{
"raw_docx_url": "https://example.com/raw-contract.docx",
"name": "hr-disc-restitution-v1",
"team": "hr",
"tenant_id": "org_acme",
"output_formats": ["pdf", "docx"],
"hint_pattern": "bracket",
"llm_naming": true
}Returns:
{
"template_id": "jd7abc123",
"variables_found": ["consultant_name", "client_name", "date"],
"unresolved_conflicts": [],
"placeholders_detected": 12,
"placeholders_converted": 12
}Configuration
Environment variables
| Variable | Required | Description |
|---|---|---|
| CONVEX_URL | Yes | Convex deployment URL — e.g. https://vibrant-ibex-858.convex.cloud |
| CONVEX_DOC_FORGE_SERVICE_SECRET | Yes | Shared service secret; must equal DOC_FORGE_WRITE_SECRET on the host backend. Fail-closed: server refuses Convex calls without it. |
| CONVEX_DOC_FORGE_SECRET | No | Clerk JWT or service key forwarded as Authorization header to Convex (reserved for V2 per-tenant JWT) |
| RENDERER_URL | No | Renderer sidecar URL. Default: https://doc-forge-renderer-production.up.railway.app (prod Railway). Set to http://localhost:8000 for local renderer dev. |
| RENDERER_SECRET | No | Bearer token for the renderer sidecar |
| MCP_TRANSPORT | No | Set to http for Railway HTTP transport; omit for stdio (default) |
| PORT | No | HTTP port when MCP_TRANSPORT=http (default: 3000) |
| MCP_BEARER_TOKEN | No | Admin Bearer token (full access — HTTP mode) |
| MCP_BEARER_TOKEN_READONLY | No | Read-only Bearer token (blocks write tools — HTTP mode) |
.env.local (local dev)
CONVEX_URL=https://your-deployment.convex.cloud
CONVEX_DOC_FORGE_SERVICE_SECRET=your-service-secret
RENDERER_URL=http://localhost:8000Auth model (HTTP mode)
Two roles:
- admin (
MCP_BEARER_TOKEN) — full access to all 7 tools - readonly (
MCP_BEARER_TOKEN_READONLY) — blocked fromrender_doc,upload_template,extract_template_schema,create_template_from_docx
If neither token is configured the server runs in open mode (suitable for local dev or trusted internal networks).
Tenant isolation: the Convex component asserts template ownership per tenant_id. In HTTP mode, a mismatch between the Bearer token's resolved org and the tenant_id argument returns HTTP 403.
Transport
- stdio — local dev with Claude Desktop/Code. Zero infra, zero auth required. Started via
npx @vantageos/vantage-doc-forge-mcpor thecommand/argsconfig above. - HTTP (Railway) — cloud V1 production transport for multi-client deploys (ChatGPT, Cursor, Grok, Claude.ai). Set
MCP_TRANSPORT=http. Auth via Bearer token.
Development
cd mcp-server
npm install
npm run build # tsc → dist/
npm test # vitest — 123 tests (5 files)
npm run typecheck # tsc --noEmitHealth check (HTTP mode): GET /health → { "status": "ok" } (unauthenticated).
Orchestrator: Hephaistos — VantageOS Team | 2026-06-21
