this site
a portfolio is the artifact a hiring manager scans for ten seconds before deciding to read. so the constraint isn't "build a portfolio" — it's "prove the taste in ten seconds, prove the engineering in the next ten."
- status
- shipped · in continuous edit
- stack
- next 16 · hono · neon · mastra · fly
- spec
- /reference
the problem
i interview every two years and i was tired of having to talk people through the same screenshots of the same projects. i wanted a site that did three things: be skim-readable in ten seconds for the hiring manager, be inspectable by an AI agent for the company that ships an AI agent for candidate review (this is now a real thing), and not look like every other developer portfolio generated in 2026.
the second constraint mattered more than i expected. the wave of AI-generated developer portfolios in 2025 collapsed the median into a recognizable pattern: hero with purple-to-blue gradient, "unleash" or "elevate" in the headline, a 4×3 icon wall of every technology touched, and a proficiency radar chart. the tell isn't any single element — it's the conjunction. so the design brief became a bans list before it became a feature list.
the approach
frontend: next 16 (turbopack), react 19, tailwind 4, shadcn/ui · api: hono on fly machines, oRPC, drizzle, neon serverless · ai: mastra, openrouter, ai sdk, langfuse · agent surfaces: model context protocol (mcp), /.well-known/agent-card.json (a2a), llms.txt · infra: fly machines, cloudflare r2, sentry, infisical
every layer is something an AI agent can read directly — the openapi spec via /reference, the prose via /llms.txt, the tool catalog via /mcp.
the case-study infrastructure you're reading came out of a single constraint: every project page should look like a careful editorial decision, not a template. that meant building a small library of components instead of one mega-template, then dogfooding it on the portfolio itself before using it on real apps. you're inside the dogfood right now.
scalar for /reference
- reads the same openapi.json hono already emits
- warm-machine-themed cleanly via css custom properties
- keyboard-navigable, no jquery
- agent-readable: scalar exposes the spec at the same origin
swagger ui
- 2008 chrome — the brand of "this is a backend"
- theming is a fight
- keyboard nav is incomplete
- the page weighs 2mb of jquery and css resets
picked scalar because it lets the openapi spec _be_ the documentation without dressing it up as enterprise software. the spec is the source; scalar is just a viewer.
a single warm-machine palette
- oklch tokens, monospace data, tinted neutrals — one direction
- identity registers in the first paint
- every component shares the same five border / text / surface tokens
vs-code-shell theme picker
- first iteration shipped a 16-theme chooser in the corner
- diluted the identity — the visitor was looking at the picker, not the work
- every theme was a compromise; none felt finished
killed the theme picker after a friend said “cool palette switcher.” that wasn't the takeaway i wanted.
the 2026-native primitives
three protocols ship behind well-known paths so an AI agent can introspect the site without scraping. each is a single file, generated from the same data model the human pages render. the long version is making the site readable to agents.
/llms.txt— markdown summary of the site for llm context windows; lists the case studies, the stack, the contact path. one route handler, under 200 lines./.well-known/agent-card.json— A2A descriptor: the agent's name, capabilities, contact endpoint. fixed schema, served as a static-ish JSON./mcp— model context protocol server hosted on the hono api. tools are eight read-only tools — projects, posts, experience, profile, tech stack, activity. zero write tools — there's nothing to mutate from outside. you can watch an agent call them at /mcp/try.
building an ai-chat portfolio that doesn't feel like ai slop
rebuilt the chat with the same warm-machine palette as the rest of the site, killed the gradient send button (rust accent only on the active state), wrote the empty-state copy as a real question ("what do you want to know about my work?" → not "ask me anything"), and lowered the avatar density. the bans list i ran against:
- no purple-to-blue gradient anywhere (banned at the css-token layer)
- no "elevate, seamless, unleash, next-gen" in any headline
- no "ask me anything" / "learn more" placeholder copy
- no avatar bubbles wider than 32px
- no emoji in the assistant's replies (configured at the system prompt)
- no skill radar charts, no proficiency bars, no tech-stack icon walls
- no centered hero with a glow effect
- no model name shown in chrome — the model is implementation, not branding
default vercel ai sdk chat ui — purple gradient send button, generic empty state, “ask me anything” placeholder.
the chat looked exactly like every other AI integration in 2025. the visitor stopped reading after the third familiar pattern. the chat was supposed to be a feature; it was reading as a stamp.
the same public /mcp endpoint, called by a live agent. real round-trips; no fixture.
/mcp/try runs a mastra agent against the public server and shows each json-rpc call with its latency, next to a terminal running the npx bokendell cli.
the result
continuous deploy on fly · sentry tracing on every route · scale-to-zero web and api machines · zero gradient text · zero skill radars · zero icon walls.