Case study
This site: an agent-ready resume
Bilingual static site (Astro + Tailwind CSS) whose resume data is published as HTML, JSON-LD and resume.json, with on-device semantic search, WebMCP tools for AI agents and PDF/DOCX CVs generated at build time.
- Company
- Oscar Arenas
- Period
- 2025 – Present
- My role
- Design, engineering and content
The problem
Recruiters and their AI agents need resume data they can read, verify and query, not only a PDF.
Approach and architecture
One typed data module drives every surface (page, JSON-LD, resume.json, CV files, agent tools); search and tools run in the browser against the published data.
-
Data
- resume.ts and projects.ts: typed, bilingual content
- Zod schema check before every build (npm run validate)
-
Build time
- Passage embeddings precomputed with multilingual-e5-small (int8)
- Astro renders the EN/ES pages with JSON-LD, resume.json, llms.txt and social cards
- CV files: Puppeteer prints the PDF, html-to-docx writes the Word file
-
Hosting
- Firebase Hosting serves the static build; the site needs no application server to work
-
In the browser
- WebMCP tools and plain HTML forms work on the published data
- Semantic search in a Web Worker: Transformers.js embeds only the visitor’s query
Every step is a file or a script in this site’s repository.
Architecture
- Astro static site in English and Spanish; one typed data module (resume.ts) feeds the HTML, JSON-LD, resume.json, the PDF/DOCX generators and the agent tools.
- On-device semantic search: passage embeddings (multilingual-e5-small) are precomputed at build time and only the visitor’s query is embedded in the browser with Transformers.js on WebAssembly.
- 19 WebMCP tools: 12 imperative tools registered with document.modelContext (navigator.modelContext in older browser builds; also exposed as window.oscarResume) and 7 declarative <form toolname> tools that also work as plain HTML forms.
- CV pipeline: Puppeteer prints the /print/ route to PDF (a default version and two focus versions) and html-to-docx builds the Word file, in both languages, on every release; all of them, and the Markdown CV, are rendered from one CV model (scripts/lib/cv-model.ts).
- Quality gates: Vitest unit tests, Playwright end-to-end and axe accessibility tests, Lighthouse CI budgets and a CV regression check run in GitHub Actions.
Key decisions and trade-offs
- Static first: every agent capability runs in the browser against resume.json, so the site needs no server to work.
- Precompute passage vectors at build time and download the ~135 MB model only when the visitor turns semantic search on, so it never blocks rendering or uses data by default.
- JSON-LD is generated from the same data as the page, so search engines and agents see the same facts as people.
- A Zod schema validates the resume data before every build, so broken dates or missing translations fail the build instead of shipping.
- Built with AI-assisted coding and review (Claude Code), under strict TypeScript and that pre-build content check.
Outcome
19 agent tools, and CVs in 2 languages as PDF (3 focus versions), Word and Markdown, generated from the same data on every release.
Key facts
- 19 agent tools
- 2 languages, 10 CV files per release (6 PDF, 2 Word, 2 Markdown)
Restated from the resume, with no estimates.
My role
Design, engineering and content
Stack
- Astro
- TypeScript
- Tailwind CSS
- Transformers.js
- WebMCP
- JSON-LD
- Puppeteer
- Playwright
- Vitest
- GitHub Actions
- Lighthouse CI
- Firebase Hosting
Links
- Live site resume-oscarenas.web.app/