---
title: "this site"
description: "the portfolio site, built on the case-study infrastructure it documents. ships the 2026-native primitives — llms.txt, agent-card, mcp — without the AI-slop visual signatures that usually come with them."
canonical: "https://bokendell.com/projects/portfolio"
last-updated: 2026-08-25
---

# this site

the portfolio site, built on the case-study infrastructure it documents. ships the 2026-native primitives — llms.txt, agent-card, mcp — without the AI-slop visual signatures that usually come with them.

## Metadata
- Canonical: https://bokendell.com/projects/portfolio
- Markdown: https://bokendell.com/projects/portfolio.md
- Lifecycle: shipping
- Tags: next, hono, mcp, a2a, design-system
- Updated: 2026-04-20

## Source
```mdx
<CaseStudyHero
	title={meta.title}
	thesis={meta.thesis}
	metric={meta.heroMetric}
	statusRows={[
		{ label: "status", value: "shipped · in continuous edit" },
		{ label: "stack", value: "next 16 · hono · neon · mastra · fly" },
		{ label: "repo", value: "private monorepo" },
	]}
	media={
		<div
			style={{
				width: "100%",
				height: "100%",
				display: "flex",
				alignItems: "center",
				justifyContent: "center",
				fontFamily: "var(--font-data)",
				fontSize: 12,
				color: "var(--dim)",
				letterSpacing: "0.04em",
			}}
		>
			[ 8s loop · /reference → /llms.txt → /mcp ]
		</div>
	}
/>

## 01 — 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.

## 02 — approach

<TechStackLine
	groups={[
		{ label: "frontend", items: ["next 16 (turbopack)", "react 19", "tailwind 4", "shadcn/ui"] },
		{ label: "api", items: ["hono on fly machines", "oRPC", "drizzle", "neon serverless"] },
		{ label: "ai", items: ["mastra", "openrouter", "ai sdk", "langfuse"] },
		{ label: "agent surfaces", items: ["model context protocol (mcp)", "/.well-known/agent-card.json (a2a)", "llms.txt"] },
		{ label: "infra", items: ["fly machines", "cloudflare r2", "sentry", "infisical"] },
	]}
	whyOneLiner="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.

<DecisionPair
	chosen={{
		name: "scalar for /reference",
		bullets: [
			"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",
		],
	}}
	rejected={{
		name: "swagger ui",
		bullets: [
			"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",
		],
	}}
	narrative="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."
/>

<DecisionPair
	chosen={{
		name: "a single warm-machine palette",
		bullets: [
			"oklch tokens, monospace data, tinted neutrals — one direction",
			"identity registers in the first paint",
			"every component shares the same five border / text / surface tokens",
		],
	}}
	rejected={{
		name: "vs-code-shell theme picker",
		bullets: [
			"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",
		],
	}}
	narrative="killed the theme picker after a friend said "cool palette switcher." that wasn't the takeaway i wanted."
/>

<Mermaid
	caption="agent-readable surface area"
	chart={`flowchart LR
		visitor[human visitor] --> ssg[next 16 ssg]
		ssg --> openapi[/openapi.json/]
		ssg --> reference[/reference]
		ssg --> llms[/llms.txt]
		ssg --> agent[/.well-known/agent-card.json]
		ssg --> mcp[/mcp]
		openapi --> hono[hono api · 47 endpoints]
		mcp --> hono
		classDef hot fill:#ca653c26,stroke:#ca653c,color:#eae3e1;
		class mcp,agent hot
	`}
/>

### 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.

- `/llms.txt` — markdown summary of the site for llm context windows; lists the case studies, the
	stack, the contact path. one route handler, ~140 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
	read-only catalog queries (`list_projects`, `get_project_by_slug`, `list_skills`). zero write
	tools — there's nothing to mutate from outside.

<HardProblem
	headline="building an ai-chat portfolio that doesn't feel like ai slop"
	triedFirst="default vercel ai sdk chat ui — purple gradient send button, generic empty state, "ask me anything" placeholder."
	whyFailed="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."
>
	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
</HardProblem>

<HardProblem
	headline="the playground demo had to feel like a real tool, not a marketing widget"
	triedFirst="embedded the zod playground inside a card with a "try it" pill and a play-button."
	whyFailed="it read as an interactive product demo, the genre. the playground is a real thing i use to debug schemas; the case study should signal that, not dress it up."
>
	stripped the chrome. the playground sits in the page as a flat text editor with the same monospace
	font as the rest of the page, no card frame, no animated label. the "try it" affordance is one
	line of rust mono above it. the value is in the thing itself.
</HardProblem>

<TryIt caption="this is the same /reference page the openapi tools serve. live data; no fixture.">
	<div
		style={{
			padding: "var(--sp-7)",
			textAlign: "center",
			color: "var(--faint)",
			fontFamily: "var(--font-data)",
			fontSize: 12,
		}}
	>
		[ inline /playground zod widget — pending Track 3; visit{" "}
		<a href="/reference" style={{ color: "var(--rust)" }}>
			/reference
		</a>{" "}
		for the live tool catalog ]
	</div>
</TryIt>

## 03 — result

<MetricStrip
	primary={{ value: "47", label: "openapi endpoints" }}
	secondary={[
		{ value: "18", label: "api packages" },
		{ value: "3", label: "agent-readable surfaces", delta: "llms · a2a · mcp" },
		{ value: "1", label: "design direction across every page" },
	]}
	context="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."
/>
```

_Generated from live portfolio data and MDX source. Canonical site: https://bokendell.com_
