ShuiSpace

Inspiration

Imagine you're trying to change the layout of your room. The choices you have are to either use a design tool with a steep learning curve, do the change in reality or click photos and send them to an LLM. The LLM might return back a new arrangement, but it has never entered your room. It doesn't know the dimensions and actual space available.

I wanted to see what it looks like when the tool and the agent are the same thing, when the agent isn't describing my room from the outside, but working on it with me, using the geometry the app already has.

What it does

ShuiSpace is a room designer with nineteen actions: place something, move it, swap it, pin it, measure a walkway, check the budget, check whether a piece gets through the door, ask where something should go.

I can use all nineteen by clicking and dragging. My agent can call the same nineteen directly, because the page publishes them as WebMCP tools. They reach the same functions, hit the same collision checks, and produce the same undo entries.

Things follow from that:

The proposal waits for me. propose_layout stages its placements as translucent ghosts on the plan, shows what the change would do — "3 items · clears 1 issue · $312.00 left of the budget" — and the tool call does not return until I click Accept or Reject. If I reject it, my agent receives that as a decision it can act on, not as a timeout. This is the whole reason the project is a WebMCP project: a server-side MCP can compute a layout, but it cannot wait for me to look at it.

Refusals carry a way out. An agent told "no" gives up. An agent told which item is in the way moves that item. Every failure returns a machine-readable reason, a human-readable message, and where relevant the id of the thing that blocked it.

There's also an activity feed showing both hands in one list, in the order things happened, so you can see the collaboration rather than take my word for it.

How I built it

React 19, TypeScript, Zustand and Vite, deployed as static assets on Cloudflare Workers. Three views of the same plan — an SVG floor plan, an SVG isometric, and a three.js 3D view that's lazy-loaded — all behind one small adapter interface so the tools don't know or care which one you're looking at.

Two rules did most of the architectural work:

Tools and UI call the same store actions. This is the one principle the whole thing rests on, and it's what makes the WebMCP surface trustworthy rather than a parallel API that drifts.

Derived data is recomputed, never stored. Violations, budget, free space and stacking heights are all selectors. That's what lets the app answer a hypothetical — "what would this proposal do?" — using exactly the machinery it uses to draw the real thing.

Tools register through document.modelContext, and the hook feature-detects so the app degrades to an ordinary room designer in browsers without WebMCP. There's also a built-in tool console that drives the identical registry by hand, which meant I could develop the tool surface before I had any agent that could see it.

What I learned

That the interesting part of an agent-facing API isn't the happy path; it's what a refusal contains, and whether a human can stay in the loop without the loop breaking. .

What's next

Non-rectangular rooms, a larger catalogue, and saved plans.

Built With

Share this project:

Updates

Submission history