Who is your product talking to? An AI agent's instruction to remove an old routing binding in FlurryPORT nearly caused a destructive action because the agent's vocabulary differed from the UI's labels, highlighting a design gap in AI-human handoffs. The author, who built the product, emphasizes that every surface now has two readers—human and AI—and that mismatched vocabularies can lead to costly errors, advocating for shared vocabulary keys to prevent drift. Who is your product talking to? Every surface you ship now has two readers. I only wrote for one of them. Last night one of my AI agents finished a cleanup in FlurryPORT https://flurryport.io and handed me one instruction: remove the old routing binding. Reasonable words. It got them from my own MCP implementation. I built the product it was talking about, and I still opened the wrong tab. The closest thing on my screen to the agent’s words was a trash can, and that trash can would have taken the whole transformation with it, cascade and all. The agent was right about the work. The instruction failed at the last inch. Its word for the thing was not the button’s word for the thing, and the nearest match to its words was destructive. I stopped with the cursor over the trash can. That is the only reason this is a story about design instead of an afternoon spent resetting entities that never needed to be disturbed. Having mostly been working as a code reviewer of carefully crafted development plans, I missed one crucial thing: the wording presented to an AI agent through my MCP implementation was different from the wording the UI presented to that agent’s user. In this case I was the user, in my own application, being prompted by an AI agent through an MCP protocol of my own design. It had not occurred to me that when the UI pass finished weeks ago, the naming convention the AI was producing for the MCP recipe would come out different. Which yields another rule to be aware of while designing software -- establish the vocabulary both audience members will be using, AI and human. Mobile-first taught a generation of us to design for the smallest screen first and let everything else follow. Agent-first sounds like the same move. That is the trap, because the agent is not a smaller human. It is a second reader and translator. Every surface you ship now has two readers: the human who clicks, and the agent that reads, plans, and relays instructions back to the human. They do not fail independently. The agent relays in its vocabulary, the human executes by nearest match, and the gap between those two vocabularies is where the product breaks. Not in the model. Not in the API. In the handoff from AI to human. What makes it expensive is that nothing about it looks like a bug. Both sides pass. The UI does exactly what its labels say. The MCP surface does exactly what its descriptions say. Nothing throws, nothing logs, no test goes red, because the defect only exists in the space between the two and no suite you own is looking there. So it does not surface in development. It surfaces in a user’s hands, or in my case in my own hands, with the cursor over a trash can. Which is the oldest argument in software, wearing new clothes. An hour spent deciding what things are called is cheaper than the afternoon spent undoing what somebody deleted because nobody decided. The handoff sounds abstract until you go looking for it, and then it turns out to have an address. In an MCP implementation, everything the agent reads fits in a short list: tool names and descriptions, parameter names and their allowed values, error strings, and the text of the result you hand back. That is the whole of it. Everything else is implementation the agent never sees. Now, I have a habit that irritates other developers and that I correct AI coding agents about constantly. Every string a human will ever see goes in a configuration file. Not inline, not in the component, in a resource file with a key, so that on the day somebody needs the product in German I can hand that file to a translator and get it back. It is discipline in service of a future that may never arrive, which is exactly why people push back on it. That discipline would have caught this. A key holds one word. If the button label and the tool description both resolve from the same key, they cannot drift, because there is only one of them to drift from. But I never extended it to prose an AI writes for another AI. What is the harm, I figured, in letting the thing that codes the thing pick the words that describe the thing. There is the harm. Agent-facing strings are human-visible strings, they just take one extra hop to get there. Tool descriptions, parameter names, error text, the body of a result: all of it eventually gets read out loud to a person by an agent, which makes it your product’s voice whether you wrote it or not. Same file. Same keys as the UI wherever it is the same word. And if you are starting from a codebase that has already drifted, the way back is dull. Tool descriptions in one column, UI labels in the other, read across. Every row where the words differ is a row where an agent will hand your user a phrase your product does not answer to. That is not a code review. It is a design review, and it belongs in the pass where you decide the button labels, not weeks after it. None of this is a new concern, and other people are naming it. Agent-first design is the phrase I keep seeing, and it is a fair one. It marks the moment a secondary audience became a primary one, the same way mobile-first did. What I am describing is narrower than a category. It is the method we use inside one, and around here we call it harbor-pilot design. When a ship enters a harbor it does not know, a pilot comes aboard. The pilot holds the local knowledge: the channels, the depths, the markers, what the words on the chart actually mean in this port. The captain keeps the bridge. The pilot navigates, the captain commands, and neither one pretends to be the other. That is the shape I want between an AI agent and the person it works for. The agent boards your product and navigates it fluently, because it has the local knowledge and I gave it to it on purpose. The calls only a captain makes stay on the bridge: what to publish, what to delete, what to spend. And the product’s whole job in between is to make the handoff lossless, which is where the last three sections have been heading. The pilot’s word for a thing has to be the bridge’s word for that thing. Otherwise the pilot says remove the old routing binding, the captain reaches for the nearest thing that looks like it, and the harbor gets a new wreck on the chart. The trash can has an ending, and it is not a postmortem. The finding got filed the same morning: agent-facing copy uses the UI’s words. By that evening the fix was public in the CLI release, and the tool now says exactly what the button says. Almost none of that was code. It was a decision I had not made yet, and once it was made, the rest was typing. Which is the part worth sitting on. The expensive thing was never the fix. It was the weeks the mismatch sat there, shipped, in front of every agent that read it, while I reviewed carefully crafted development plans that were all correct about the code and silent about the words. An hour of design during the UI pass would have cost an hour. Finding it the way I found it cost the fix, plus the weeks, plus a cursor hovering over a trash can. Three rules, then. None of them need a new tool, and all three are design decisions rather than code. The vocabulary gets decided once, and both audiences read from that decision. I have already spent a whole section on this one, so here is the version that fits on a sticky note. If the button says Routing, then the tool description, the error message, and the confirmation the agent reads back all say Routing. Every synonym left lying around is one more nearest-match gamble your user takes on your behalf, and some of those buttons are trash cans. Obligations go where the agent already reads. If your product needs the agent to tell the human something, put it in the body of the result the agent is working with. An instruction tucked into metadata is a note passed to somebody mid conversation. It might get read. Design for the version where it does not. Declare how your team works, in plain words, somewhere the agent reads on arrival. An undeclared team gets a default, and the default is the model’s generic idea of an office, applied confidently to your product, your cadence, and your risk tolerance. The fix is a paragraph, not a platform. Ours lives in the first message of a shared stream: who posts what, what counts as a decision, what is worth interrupting a human for. Underneath those three is one habit: design time, the cheap kind, spent before there is anything to undo. I have said the old line about an hour of design saving a day of development often enough to be bored by it, and I still had to learn it again from an agent that did nothing wrong. The trash can is still there in the UI, where it belongs. If the two-reader problem is sitting in your product today, FlurryPORT is the rail I run mine on. It captures webhooks byte for byte, replays them to localhost or staging with signatures intact, and the same MCP stdio server that started this story will guide your agent to do this too. flurryport.io/recipes has the walkthroughs, flurryport.dev/try takes about two minutes, and flurryport.io/pricing has the honest numbers.