Hub
Hub is where my projects start. I choose the decisions for a new build, from stack and design system to motion and depth of UX work, and it composes a setup contract agents can follow. The same data is open to agents through an MCP server with 13 tools. One pure function serves the app, the server and the tests.
- 2026
- MCP
- Agents
- Next.js
- Tests
- Self-hosted
In short
- My part
- Product, interface, architecture and review. Built with Claude Code.
- Stack
- Next.js 16, React 19, TypeScript, Tailwind v4, Base UI, Drizzle, PostgreSQL
- MCP server
- Streamable HTTP, stateless, bearer tokens, 13 tools
- Prompt library
- 60 blocks in 9 categories, 20 cross rules, 16 contract sections
- Tests
- 203 cases in 15 files, 51 of them on the compose engine
- Data
- 12 tables
- Hosting
- Docker, on my own server
What it does
A new project starts with the same questions. Which framework, which design system, how much motion, is there 3D, how deep does the UX work go, what is the accessibility bar, which browsers. In Hub each answer is a block, and there are 60 of them. I pick, and the composer writes a setup contract in Markdown: the stack, the install commands, the skills to load, what not to do, and the acceptance criteria.
Around the composer sit the things I kept reaching for while working: brand palettes in OKLCH with their contrast results, notes, tasks with due dates, the tasks assigned to me in ClickUp, and a usage panel built from my own Claude Code transcripts.
One engine, three callers
The composer is a pure function. It takes a selection and returns Markdown, and it touches neither the database nor the request nor the browser. That is why the web interface, the MCP server and the tests all call the same code and get the same output. The rules between blocks are data as well: 20 of them say which block implies or rules out another, and an integrity test checks every reference.
Blocks live in code, not in the database. They show up in a diff, and a slug that does not exist is a type error.
What the agents may not do
The MCP server has no delete tool, on purpose: a misunderstood sentence in a chat should not be able to lose data. Closing a task works the same way. If the words match more than one task, the tool closes none and returns the candidates.
ClickUp is read only. The code sends it nothing but GET requests, and tasks are not copied into Hub, so the two lists cannot drift apart. There are two tokens, one for Claude Code and one for the remote agent, so either can be replaced without cutting off the other. With no token set, nothing gets in.
What the agent is handed
A contract pasted into a chat is read once. So the composer can also produce a package: a CLAUDE.md, a settings file with no destructive commands allowed, a setup script and a copy file. CLAUDE.md is read in every session, which means the rules are still there on the second day.