Skip to content
Conduit

mcp server

cvolve

CVolve is an open-source, self-hostable CV builder by Future Site. You keep your whole career in one master profile, create as many CVs as you need from it, render them through templates to A4 PDF and

future-site-ai/cvolvecommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 26, 2026

CVolve (CV + Evolve)

License: MIT PHP 8.3 Symfony 7.4 PostgreSQL 16 Docker GitHub stars

CVolve is an open-source, self-hostable CV builder by Future Site Di Haidar Anbar. You keep your whole career in one master profile, create as many CVs as you need from it, render them through templates to A4 PDF and DOCX, and let an AI assistant draft, tailor and translate the content. The assistant only proposes changes; you approve each one.

  • 🗂 Master profile: structured forms, or import a Markdown file / an existing CV (PDF, DOCX) parsed by AI, with a review step before anything is saved.
  • 📄 Many CVs per user: pick which summary, jobs, entries and bullets to show; edit wording per CV without touching the master profile.
  • 🎨 Templates own the design: content and styling are separate. The chat changes words, never CSS, and never steps outside the template's schema.
  • 🤖 Propose → approve: every AI change arrives as a before/after diff with Apply / Discard. A version snapshot is taken before applying.
  • 🌍 Multilingual: language versions of one CV (EN, IT, FR, DE, ES, PT, NL, AR), with AI translation.
  • 🎯 Job tailoring & checks: paste a job ad to tailor the CV; the checker reports template limits, page count, date consistency, weak bullets and keyword coverage.
  • 🕓 Version history: automatic and manual snapshots, side-by-side diff, one-click restore.
  • 📦 Templates in many formats: native .cvolve packages, HTML with Mustache placeholders, Twig HTML, JSON Resume themes, HTML zips, and finished HTML CVs converted by AI. Build templates with the companion CVolve Template Maker.
  • 🔌 REST API + MCP server: connect Claude Desktop, Cursor or any MCP client; automate with personal API tokens.
  • 🔗 Public share links that you can switch on and off.

🎬 Videos

Watch the CVolve demo on YouTube

▶️ All CVolve videos on YouTube: demos, guides and updates.


Quick start (Docker)

git clone https://github.com/future-site-ai/cvolve.git && cd cvolve
cp .env.example .env.docker      # optional: set APP_SECRET, AI provider, SMTP...
docker compose up -d --build

Open http://localhost:8080. First start runs the migrations, installs the preset templates and creates:

AccountEmailPassword
Adminadmin@cvolve.localadmin1234
Demo user (profile + 2 CVs)demo@cvolve.localdemo1234

Change these before exposing the app (see CVOLVE_ADMIN_* and CVOLVE_SEED_DEMO below). Outgoing email (sign-up confirmation, password reset) is caught by Mailpit at http://localhost:8025.

ServicePurpose
appSymfony app (PHP 8.3 + Apache); runs migrations and seeding on start
messenger-workerBackground jobs: PDF/DOCX exports, AI import parsing, emails
databasePostgreSQL 16
gotenbergChromium that renders the HTML templates to PDF (only inline resources may load)
mailerMailpit (development SMTP catcher)

Useful commands (see make help):

make logs                              # follow app + worker logs
make admin EMAIL=you@example.com       # create or promote an administrator
docker compose exec app php bin/console app:templates:install   # reinstall presets

Demo data: fixtures/demo_profile.json is a fictional profile ("Alex Morgan") used to show what CVolve does. To give the demo user a photo, put a JPEG at assets/photos/demo.jpg (gitignored). Set CVOLVE_SEED_DEMO=0 for a deployment without the demo user, or replace that file with your own.

Configuration

Docker reads docker/defaults.env, then your overrides in .env.docker (copy of .env.example). When running PHP locally, use .env.local instead.

VariableDefaultMeaning
APP_SECRET–Set a random value in production (php -r 'echo bin2hex(random_bytes(16));')
DEFAULT_URIhttp://localhost:8080Public URL (links in emails and API answers)
DATABASE_URL, POSTGRES_*cvolve/cvolveDatabase credentials (change both together)
MAILER_DSN, MAILER_FROMMailpitSMTP for verification and reset emails
REQUIRE_EMAIL_VERIFICATION1Users must confirm their email before logging in
GOTENBERG_URLhttp://gotenberg:3000PDF renderer
STORAGE_DIRvar/storageExports, photos, template assets (a Docker volume)
CVOLVE_SEED_DEMO1On an empty database: 1 = admin + demo user, 0 = admin only
CVOLVE_ADMIN_EMAIL / _PASSWORDadmin@cvolve.local / admin1234First administrator
AI_PROVIDER …geminiSee below
CVOLVE_PORT, MAILPIT_PORT8080, 8025Host ports (shell environment)

Choosing an AI provider

The assistant is built on Neuron AI, configured exactly like Plate Plan:

AI_PROVIDER=anthropic          # gemini | openai | anthropic | ollama | mistral | deepseek | grok | openai_like | none
AI_MODEL=                      # optional; defaults: gemini-2.5-flash, gpt-4o-mini, claude-sonnet-5-5, llama3.1, ...
AI_API_KEY=sk-...              # or GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / MISTRAL_API_KEY ...
AI_BASE_URL=                   # ollama (default http://host.docker.internal:11434/api) or any OpenAI-compatible server

Without a provider, CVolve still works fully: forms, templates, exports, versions, sharing and CVolve-JSON import. The chat then falls back to a few plain commands (“add professional summary from my master info”, “add experience”, “check my CV”, “export pdf”). Markdown/PDF/DOCX import and the generative tools need a provider.

Using CVolve

  1. Master profile (/profile): fill in the tabs or Import… a Markdown file such as md/master_info.md, an old CV, or a CVolve profile JSON. Review what was found, then add it to or replace parts of your profile.
  2. New CV: choose a template, language, summary, jobs and sections. Content is copied from the profile.
  3. Editor: sections on the left (edit, reorder, hide entries/bullets, add from profile), a live A4 preview in the middle (red guides show page ends; PDF preview shows the exact output), the assistant on the right.
  4. Assistant: ask in plain language: “Suggest 3 summaries for a fintech backend role”, “Make my 3Tech bullets more quantified”, paste a job ad to tailor, “Translate to Italian”. Each change is a proposal with a diff.
  5. Export to PDF or DOCX, save named versions, or share a read-only public link.

Screenshots

Interactive CV Editor & AI Assistant

The core workspace: structured section tree on the left, live sandboxed A4 preview with real-time template styling in the center, and an AI assistant on the right that proposes changes as diffs with instant Apply / Discard controls.

Multi-CV Dashboard

Manage multiple tailored CV versions for specific positions, industries, or languages—all dynamically sourced from your master profile.

Master Career Profile

A single source of truth for your entire career: contact details, professional summaries, work experience, education, skills, and certifications, with import support for Markdown, PDF, and DOCX resumes.

Template Gallery

Browse and switch between native templates separating layout from content. Supports .cvolve packages, Mustache HTML, Twig, and JSON Resume themes.

Version History & Side-by-Side Diff

Automatic snapshots before every applied AI change, side-by-side unified diff comparison, and one-click rollback.

AI tools, REST API and MCP

One tool layer (src/Service/Tool/CvToolbox.php) serves the in-app chat, the MCP server and the REST API:

ReadPropose a change (user applies)AI-generated proposalsDirect actions
list_cvs, get_cv, get_master_profile, list_templates, get_template_schema, list_proposals, check_cvadd_section (incl. from_master), update_section, remove_section, reorder_sections, update_header, set_professional_summary, add_experience_from_master, select_items, deselect_items, switch_templategenerate_summary_from_master, suggest_summaries (3 proposals), rewrite_bullets, tailor_to_job_description, translate_cvcreate_cv, export_cv

Change tools validate against the template's schema.json (supported sections, maxLength, maxItems, maxBullets) and return errors the AI can correct. Generative tools use the AI provider configured in CVolve.

Personal API tokens

Create one under Account → API tokens (shown once, stored hashed, optional expiry). Send it as Authorization: Bearer cvk_....

REST API (/api/v1)

Interactive docs: /api/doc (OpenAPI JSON at /api/doc.json).

TOKEN=cvk_...
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/v1/cvs
curl -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
     -X POST http://localhost:8080/api/v1/tools/set_professional_summary \
     -d '{"cv_id": 1, "text": "Backend engineer with **8+ years**..."}'
curl -H "Authorization: Bearer $TOKEN" -X POST http://localhost:8080/api/v1/proposals/1/apply

Endpoints: GET /me · GET|PUT /profile · GET|POST /cvs · GET|DELETE /cvs/{id} · POST /cvs/{id}/exports · GET /exports/{id} · GET /exports/{id}/download · GET /proposals?cv_id= · GET /proposals/{id} · POST /proposals/{id}/apply|discard · GET /templates · POST /templates/import · GET /templates/{id}/export · GET /tools · POST /tools/{name}.

MCP server (/mcp)

A JSON-RPC 2.0 MCP endpoint (streamable HTTP, JSON responses; protocol versions 2025-06-18, 2025-03-26, 2024-11-05) exposing the same tools, acting as the token's owner.

Claude Desktop (claude_desktop_config.json), through the mcp-remote bridge:

{
  "mcpServers": {
    "cvolve": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8080/mcp", "--header", "Authorization: Bearer cvk_..."]
    }
  }
}

Cursor (.cursor/mcp.json):

{ "mcpServers": { "cvolve": { "url": "http://localhost:8080/mcp", "headers": { "Authorization": "Bearer cvk_..." } } } }

Changes made through MCP appear as proposals in the CV editor (or apply them with the REST API).

Template formats CVolve imports

Every format is converted into a sandboxed .cvolve package and validated before installation (Templates → Import, or POST /api/v1/templates/import). Missing manifest.json and schema.json are inferred.

FormatWhat to uploadNotes
.cvolvethe native zip (below)used as is
CVolve HTMLone .html/.mustache file with Mustache placeholderseasiest for designers, see below
Twig HTML.html/.twig using the Twig variables belowno manifest needed
JSON Resume themethe theme's zip (npm package) with a Handlebars or Mustache template (resume.hbs, resume.template) + CSSrendered against the JSON Resume schema; the theme's JavaScript is ignored, but common computed fields (workBool, capitalName, startDateYear…) are provided. Themes that render with JSX/Pug/React are not supported
HTML zipindex.html + CSS files (+ fonts/) in any HTML flavour abovelinked local stylesheets and <style> blocks are merged
Finished HTML CVa static CV with no placeholdersconverted by the configured AI provider; without one, use the Template Maker

During import, <script>, event handlers, remote stylesheets/fonts/images and @import are removed (with warnings).

CVolve HTML (Mustache placeholders)

<style> h1 { color: #0f766e; } </style>
<h1>{{header.name}}</h1>
<p>{{header.headline}} · {{header.email}}</p>
{{#regions.main}}
  <section>
    <h2>{{title}}</h2>
    {{#is_summary}}<p>{{{text}}}</p>{{/is_summary}}
    {{#items}}
      <h3>{{title}}{{name}}{{degree}} <small>{{period}}</small></h3>
      <ul>{{#bullets}}<li>{{{.}}}</li>{{/bullets}}</ul>
    {{/items}}
  </section>
{{/regions.main}}

{{x}} prints escaped text, {{{x}}} prints formatted text (**bold**, *italic*, never raw HTML), {{#x}}…{{/x}} loops over a list or shows the block when x is set, {{^x}}…{{/x}} shows it when x is empty, and {{.}} is the current list value. Names are looked up from the innermost block outwards. Without a schema.json, regions are detected from regions.<name> and a column called sidebar/aside/left/right receives skills, languages, certifications and custom sections.

The .cvolve template format

A template is a zip file with the extension .cvolve:

my-template.cvolve
├── manifest.json        required
├── schema.json          required
├── template.html.twig   required  sandboxed Twig, renders the <body> content
├── style.css            required  print CSS
├── thumbnail.png        optional  gallery image (794×1123 recommended)
├── fonts/*.woff2|woff|ttf|otf     optional bundled fonts
└── README.md, LICENSE.txt         optional

manifest.json

{
  "format": "cvolve-template",
  "formatVersion": 1,
  "name": "My Template",
  "slug": "my-template",
  "version": "1.0.0",
  "author": "You",
  "licence": "MIT",
  "description": "One sentence for the gallery.",
  "page": "A4",
  "maxPages": 2,
  "fonts": [{"family": "Inter", "file": "fonts/inter-400.woff2", "weight": "400", "style": "normal"}],
  "docx": {"accent": "#0984E3", "font": "Calibri"}
}

schema.json declares the layout regions and which section types the template supports, with optional limits that both the editor and the AI respect:

{
  "regions": ["sidebar", "main"],
  "sections": {
    "summary":    {"region": "main", "maxLength": 900},
    "experience": {"region": "main", "maxItems": 6, "maxBullets": 8},
    "skills":     {"region": "sidebar"}
  }
}

Section types: summary, experience, projects, education, skills, languages, certifications, publications, custom.

Template variables (plain arrays only; text is autoescaped):

VariableContent
headername, headline, email, phone, location, website, linkedin, github, nationality, dateOfBirth, workAuthorization, drivingLicence, extra, photo (data URI or null)
regions.<name>visible sections for that region, in order
sectionsall visible sections, in order
sectionid, type, title, region, text (summary), items (visible entries; bullets is a list of visible strings)
labelstranslated fixed words: contact, personal, present, nationality, born, work_authorization, driving_licence, tech, grade
cvtitle, locale, dir (ltr/rtl)
by_type.<type>the first visible section of a type
section extrashas_items, is_<type> (e.g. is_experience); every item also has period
resumethe CV in JSON Resume shape (resume.basics.name, resume.work…)

Entry fields: experience title, organization, location, start, end, description, tech, bullets · projects name, role, start, end, url, tech, description, bullets · education degree, institution, location, start, end, grade, description · skills group, skills · languages name, level, note · certifications name, issuer, date, url · publications title, authors, venue, date, url · custom title, subtitle, date, description.

Allowed Twig: tags if for set apply macro import from with verbatim; filters abs batch capitalize column default e escape filter first format join keys last length lower map merge nl2br number_format replace reverse round slice sort split striptags title trim upper url_encode plus CVolve's md (renders **bold** / *italic* safely), period (start – end), pretty_url, initials, as_list; functions range max min cycle; tests defined empty null none odd even iterable same as divisible by. Macros can be called through _self. No raw, include, source, object access or method calls.

Validation on import rejects: scripts, event handlers, javascript: URLs, <iframe|object|embed|form|link|meta|style|base> in the template; @import, remote url() (only fonts/… and data:image/… are allowed, CSS escapes are decoded first) in the CSS; unknown files (e.g. .js); unsafe zip paths (zip-slip); oversized packages; and anything the sandbox refuses during a trial render with sample data. Previews are served with a strict Content-Security-Policy inside a sandboxed iframe, and Gotenberg only loads inline resources.

Print tips: design for @page { size: A4; margin: 0 } and pad your columns; use box-decoration-break: clone so padding repeats on every page, a root linear-gradient background for full-height sidebars, and break-after: avoid on headings. Look at packages/template-kit/resources/presets/ for three complete examples. make pack-templates writes the presets as .cvolve files to start from.

DOCX export

PDF is a pixel-accurate Chromium rendering of the template. DOCX is generated separately with PHPWord from the same content, because Word cannot reproduce arbitrary CSS layouts. It is an editable single-column document using the template's docx.accent colour and docx.font, with all regions included (main first). Expect the same content and emphasis, not the same visual layout.

Architecture

src/
├── Entity/                User, ApiToken, Template, Cv, CvVariant (language), CvSection (JSONB content),
│   └── Profile/           CvVersion, AiProposal, ChatConversation/Message, ExportJob, ImportJob
│                          MasterProfile + Experience/Project/Bullet, Education, Skill..., CustomSection
├── Service/
│   ├── Cv/                SectionTypes (content shapes), CvBuilder (profile → CV), CvRenderer,
│   │                      ProposalManager (propose/apply/discard + diffs), VersionManager, CvChecker
│   ├── Template/          TemplateInstaller (import any format via the kit, export .cvolve)
│   ├── Tool/CvToolbox     every AI/MCP/API tool, defined once
│   ├── Ai/                ProviderFactory + CallbackTool (from Plate Plan), ChatAgent, AiTextService, Prompts
│   ├── Mcp/McpServer      JSON-RPC 2.0 MCP server
│   ├── Export/            GotenbergClient (PDF), DocxExporter (PHPWord), ExportService
│   └── Import/            TextExtractor (MD/PDF/DOCX), ProfileImporter
├── Controller/            web UI, Admin/, Api/ (REST v1 + MCP)
└── Security/              API token auth, ownership voters, user checker
packages/template-kit/     shared template engine (Composer package, also used by the Template Maker):
                           sandbox, package reader/writer, DocumentRenderer, sample CVs, presets,
                           importers (Mustache HTML, Handlebars/JSON Resume, HTML zip, AI conversion)
fixtures/demo_profile.json demo master profile (CVolve profile JSON)

The UI is server-rendered Twig with Symfony UX (Stimulus + Turbo) and AssetMapper, so there is no Node build step. Ownership is enforced by voters on every controller and by the toolbox for API, MCP and chat calls.

Development

Run PHP on your machine against containerised services:

composer install
make local-deps                      # PostgreSQL :5432, Gotenberg :3000, Mailpit :8025
cat > .env.local <<'ENV'
APP_SECRET=change_me_to_a_random_32_char_hex
DATABASE_URL="postgresql://cvolve:cvolve@127.0.0.1:5432/cvolve?serverVersion=16&charset=utf8"
GOTENBERG_URL=http://127.0.0.1:3000
MAILER_DSN=smtp://127.0.0.1:1025
AI_PROVIDER=none
ENV
php bin/console doctrine:migrations:migrate -n
php bin/console app:seed-demo        # or: php bin/console doctrine:fixtures:load
symfony serve                        # or: php -S 127.0.0.1:8000 -t public
make worker                          # in another terminal, for exports and imports

Or run everything in Docker with live code: make dev.

Quality checks (also in GitHub Actions):

make test      # PHPUnit on SQLite: sandbox, package validation, proposals, exports (Gotenberg mocked), API/MCP auth, chat agent
make stan      # PHPStan level 5

🤝 Contributing

Contributions are welcome! Bug reports, ideas, docs and code all help. Read CONTRIBUTING.md to get started, and look for issues labelled good first issue.

📄 License

CVolve is open source under the MIT License, © Future Site Di Haidar Anbar. You are free to use, modify, and distribute it, including commercially, as long as the copyright and license notice are kept. Bundled fonts are under the SIL Open Font License 1.1 (see each template's LICENSE.txt).

🙌 Using This Project?

If you use CVolve or build on its source code, please credit the project by linking back to this repository:

Based on [CVolve](https://github.com/future-site-ai/cvolve) by Future Site Di Haidar Anbar.

⭐ Support the Project

If you find CVolve useful, please kindly:

Thank you for your support! 💚

↑↓ navigate openesc close