Inspiration
I'm a cybersecurity diploma student, and CTFs are where a lot of that learning actually happens. The problem isn't knowledge but it's that under time pressure, in the middle of a competition, you blank on which tool solves the thing in front of you. You know base64 when you see it decoded, you just forget CyberChef exists when you're staring at a locker combination at 2am with a clock running.
Before writing any code, I actually read the judging panel for this challenge. I learnt that Alex Nahas built the WebMCP spec itself, Sarah Drasner's public focus at Chrome is security/trust boundaries in the AI-web ecosystem, Justin Rushing works on agent safety at OpenAI. That information made me realise that generic "AI shopping assistant" demos which is what most of the reference implementations from the sponsors already show, was going to read as homework and not show up as effort. The panel skews toward people who built the standard, not people who market on top of it. So the idea had to be something where the tool design itself was the interesting part, not just another UI with a registerTool() call bolted on.
What it does
Hintel is a workspace for CTF players, built around sessions with one CTF event, one session, its own progress and history. Inside a session, an AI agent (running in ChatGPT's in-app browser, or Chrome with WebMCP enabled) has real tools it can call: suggest_tool_category routes a piece of evidence to the right technique, get_hint returns a nudge for whatever tier you've unlocked and nothing more, log_attempt unlocks the next tier. It never hands you the flag as it is enforced by the tool's own logic, not by a prompt asking it nicely. Every call it makes shows up live in an Agent Activity panel and a raw transcript underneath, so nothing happens off-screen. Sessions themselves are agent-callable too: list_sessions, create_session, switch_session which an agent can genuinely just start a session for you.
How we built it
The first draft was deliberately built in plain HTML/CSS/JS since WebMCP itself is just a browser API (document.modelContext.registerTool), and I wanted the very first version to prove the mechanism worked before touching a framework. Once the core loop worked (challenge → evidence → tool call → gated hint), I rebuilt it on Next.js 16 with a static export, so it stays deployable to any host the challenge allows the sponsors, Netlify, Vercel, Cloudflare, and isn't locked to one.
Design went through an actual process, not vibes: I pulled real reference patterns from Linear's activity-feed rows and Vercel's own login-page layout, generated a palette from a proper OKLCH seed rather than eyeballing hex codes, and caught myself about to ship a "colored side-stripe on cards" pattern that's a known lazy-accent tell before it went out.
Challenges we ran into
The first challenge was not building a chatbot by accident. The single biggest risk to this whole project was building something that looks WebMCP-native but is secretly just a chat window with extra steps. I kept coming back to one test: does the agent call a real, scoped tool with a real return value, or is it just talking? Every feature, including sessions, got redesigned around that question before I let myself build it.
The second challenge is that React + registerTool don't naturally get along. registerTool's execute callback gets registered once; React state closes over whatever it was when that callback was created. The fix was a ref that mirrors the latest state on every render, so the tool always reads current data even though it was registered once on mount. I hit the same problem again, worse, when I added multi-session support: switching sessions has to reload state into that same hook without the autosave effect immediately re-persisting the old session's data under the new session's storage key. That one required an actual guard flag and a few minutes of tracing render order to convince myself it was correct.
Finally, the last challenge was that onboarding that doesn't just repeat itself. My first onboarding pass was a 4-step modal that popped up regardless of context. Once sessions existed, I realized the dashboard's empty state, "no sessions yet", was the real first-run moment, not something to onboard past with a wall of text. The fix was giving up the modal as the primary teaching moment and replacing it with a populated sample session you can open with one click and see the whole thing already working.
Accomplishments that we're proud of
- 10 real, working tools, not a demo of 2. Seven scoped to a session (list_challenges, select_challenge, suggest_tool_category, get_hint, log_attempt, open_reference, submit_flag) and three global (list_sessions, create_session, switch_session). An agent can manage the whole workspace, not just one narrow action.
- Caught two real bugs before they shipped, not after. The stale-closure trap in registerTool and the session-switch autosave race were both things I found by actually testing the flow end to end, not things a judge would have found for me.
- Migrated frameworks mid-build without losing a working demo. Went from vanilla HTML/JS to Next.js 16 with a static export, and the core WebMCP loop (challenge → evidence → tool call → gated hint) worked identically on both sides of that rewrite, verified in-browser both times, not assumed.
- Did the research most hackathon entries skip. Read the actual judging panel's backgrounds before picking an idea, instead of guessing what "AI + web" should look like. That's the reason this isn't another storefront demo.
- Redesigned onboarding around a real UX principle, not a checklist. Empty states are onboarding opportunities which I only actually believed that once I ripped out my first 4-step modal and replaced it with a sample session that shows the product working before you type anything.
What we learned
WebMCP's actual constraint is a feature, not a limitation: tools run in the page's own execution context, so the UI and the agent are provably looking at the same state. There's no separate backend to get out of sync. That's a stronger trust story than anything you can build with a traditional chat-based assistant, and it only shows up if you design for it deliberately instead of defaulting to "add a chat box."
What's next for Hintel
- Let players (or an agent) add custom challenges instead of the three hardcoded ones. A create_challenge tool would make Hintel useful for an actual upcoming CTF, not just the seeded examples.
- Session sharing. A teammate working the same CTF should be able to open your session (or the agent should be able to hand off a session), instead of everything being locked to one browser's localStorage.
- Deeper tool routing. Right now suggest_tool_category is keyword-matching; it could reason over an actual file/content sample instead of just a text description.
- A real deployed URL on Netlify or Vercel so judges can test the live agent flow directly, not just read the repo.
Log in or sign up for Devpost to join the conversation.