cf-rendercv is an HTTP API + MCP server for generating resume PDFs using the rendercv CLI.
rendercv is a CLI tool and is not readily portable to run inside Cloudflare workerd. To work around this, the repo uses Cloudflare Containers to run a Docker container that has the rendercv CLI available. A Node.js server wraps the CLI and exposes an HTTP endpoint; the Cloudflare Worker proxies requests to it.
The apps are:
./apps/http- Cloudflare Worker (Hono)
- MCPAgent (MCP tool/agent wiring)
- hosts an MCP server that registers the
rendercvtool, plus a prompt and a JSON schema resource (see./apps/http/src/mcp/rendercv.mcp.ts) - handles MCP tool calls by routing them to the container-backed PDF generator
- Cloudflare Container (
docker run) (starts/manages the Docker container lifecycle)
./apps/rendercv-app(lives inside the Docker container)- Node.js HTTP server
- Hono + Swagger routes
- Executes
rendercvto generate the returnedapplication/pdf
-
Cloudflare Worker (
./apps/http)- Boots a Docker container when needed.
- Proxies incoming HTTP traffic to the Node.js app running inside the container.
-
Node.js API (
./apps/rendercv-app)- Endpoint:
POST /api/v1/generate - Request Body: RenderCV configuration provided as JSON or yaml (a JSON equivalent of the
rendercvYAML file). - Response:
Content-Type: application/pdf- Body is the generated PDF.
- Endpoint:
This Worker supports using RenderCV in two ways:
- HTTP API:
POST /api/v1/generate(RenderCV configuration as JSON) returns a generated PDF. - MCP tool: the Worker registers an MCP tool named
rendercvthat accepts{ content, format }and returns a generated PDF URL (or base64 whenformat: "base64").
The Worker also registers:
- a prompt named
rendercv - a resource at
rendercv://schema-and-promptcontaining the RenderCV JSON schema
See ./apps/rendercv-app/README.md for detailed API docs and examples.
At a high level, you will:
docker- node >= 20
- pnpm >= 10.30.3
The HTTP/MCP server uses Google OAuth for authentication.
To run locally, you must set up a Google OAuth client application in the Google Cloud console: https://console.cloud.google.com/auth/clients/create
After creating the app, configure the resulting GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET for the worker.
-
Install dependencies at the repo root:
pnpm install
-
Start the cloudflare worker and api locally from the repo root:
pnpm run dev:http
-
Start the Node.js API locally from the repo root:
pnpm run dev:api
-
Send a
POSTrequest tohttp://localhost:<port>/api/v1/generatewith your RenderCV JSON payload and save theapplication/pdfresponse.
You must have rendercv installed on your local machine for PDF generation to work. See the official RenderCV “Get Started” guide for installation instructions: Get Started - RenderCV.
To develop or deploy the Cloudflare Worker in ./apps/http, refer to that app’s own configuration and scripts (e.g., wrangler.toml, package.json) for the precise commands.
- Unit tests live next to source under
apps/**/src/**/__tests__/(see.cursor/rules/unit-testing.mdc). - Worker integration tests live in the repo-root
./integration/directory. They use Cloudflare’s Workers Vitest integration (@cloudflare/vitest-pool-workers): tests run insideworkerdagainst the sameapps/http/wrangler.jsoncas local dev, and call the Worker viaexports.default.fetch()fromcloudflare:workers(see Cloudflare Test APIs).
From the repo root:
pnpm run test:integrationWatch mode:
pnpm run test:integration:watchThese tests do not replace container-backed PDF smoke checks; they validate routing and request handling in the Worker isolate without requiring wrangler dev in a separate terminal. The Vitest pool does not emulate Cloudflare Containers—paths that need a live container (for example a successful POST /api/v1/generate with a full RenderCV document) still require pnpm run dev:http / Docker or a deployed environment.
Use the @modelcontextprotocol/inspector tool to debug the MCP server.
npx @modelcontextprotocol/inspector@latestSequence Diagram (HTTP)
Rendering a resume via HTTP request
sequenceDiagram
participant C as HTTP Client
participant W as Cloudflare Worker (Hono)
box Blue Durable Objects
participant D as RendercvDo (MCPAgent durable object)
participant K as DockerRendercvApp (Container)
end
box Purple Container
participant A as rendercv-app (Node.js + Hono)
participant R as rendercv CLI
end
C->>W: POST /api/v1/generate (RenderCV JSON)
W->>D: stub.fetch(request)
D->>K: callContainerService(path, method, body)
K->>A: HTTP POST /api/v1/generate
A->>R: rendercv (generate PDF)
R-->>A: PDF binary
A-->>K: application/pdf response
K-->>D: proxy response
D-->>W: proxy response
W-->>C: application/pdf
Sequence Diagram (MCP)
MCP is the Model Context Protocol, a protocol for building agents that can interact with other agents and tools.
sequenceDiagram
participant M as MCP Client (agent)
participant R2 as R2 Bucket
box Blue Durable Objects
participant D as RendercvDo (MCPAgent durable object)
participant T as MCP tool: rendercv
participant K as DockerRendercvApp (Container)
end
box Purple Container
participant A as rendercv-app (Node.js + Hono)
participant R as rendercv CLI
end
M->>D: MCP request to RendercvDo (/mcp)
D->>T: invoke tool rendercv (content, format)
T->>K: callContainerService('/api/v1/generate', body)
K->>A: HTTP POST /api/v1/generate
A->>R: rendercv (generate PDF)
R-->>A: PDF binary
A-->>K: application/pdf
rect rgba(33, 66, 99, 0.12)
K-->>T: proxy response (PDF)
T-->>R2: upload to R2 bucket
end
T-->>D: tool result (PDF URL)
D-->>M: MCP response (PDF URL)
Sequence Diagram (MCP Discovery)
Discovery is the process of the MCP client (agent) discovering the tools, resources, and prompts available on the MCP server.
sequenceDiagram
participant M as MCP Client (agent)
participant D as RendercvDo (MCPAgent durable object)
participant G as GitHub: rendercv/schema.json
M->>D: MCP initialize
M->>D: tools/list
D-->>M: tools include rendercv (input schema: RenderCV JSON)
M->>D: resources/list
D-->>M: resources include rendercv://schema-and-prompt
M->>D: resources/read(rendercv://schema-and-prompt)
D->>G: fetch schema.json
G-->>D: schema.json (raw JSON)
D->>D: parse JSON + JSON.stringify(schema, 2)
D-->>M: resource text (application/json) containing the RenderCV schema
M->>D: prompts/list
D-->>M: prompts include rendercv
M->>D: prompts/get('rendercv')
D-->>M: prompt text with examples/instructions to build content (see rendercv://schema-and-prompt for the full schema)
Note over M,D: Using the tool list + prompt examples + the schema resource, <br/>the LLM knows how to build a valid `content` payload, then calls the MCP tool when the user asks to generate a resume.
M->>D: tool call rendercv { content, format: "url" }