Blog ·

One payload, three clients

If an agent paraphrases a check people already run in Slack, you get two answers for one command.

I shipped MCP in May 2026 for a boring reason. Azure NetApp Files analyzers, the health checks that engineering already ran, were in Slack. SmartBot Web already rendered them. Cursor, Claude Code, and Claude Desktop needed the same check, not a new one. So the analyzers went out as a JSON API and an MCP server. One payload. Slack, the web UI, and the IDE agent are clients.

The failure I watch for is the paraphrase.

Someone in one time zone runs the command and pastes the result into a thread. Someone in another asks an agent whether the region is healthy. If the tool result is that same payload, both people are looking at one object: which check failed, which location was skipped, which field was empty. If the tool returns a blob and the model writes a paragraph, the thread now has two answers. They split on the dull parts. A failed check becomes “mostly fine.” A skipped location disappears. A dry-run that deleted nothing becomes “cleaned up.”

SmartBot was used by 800+ employees across 30+ time zones. They did not share a standup. They shared commands. The command is the agreement. An agent does not get a second wording of it.

That is why the MCP server was the small piece. The work was a payload dull enough that the three clients cannot drift. Same fields. Same failures. Same explicit empty when a check did not run. The first thing the agent returns is that result. A sentence that is not in the payload is a new source of truth. I do not want one of those in an incident thread.

Dry-run is part of the command

The same rule applies when the tool changes something.

The SmartBot cleanup service deleted stale Azure test resources. People ran it in Slack. The web UI had the same path. Both had a dry-run. A tool exposed to an agent that deletes and skips the dry-run is a shorter command than the one humans have. That verb does not get a solo entry on the tool list. If a person has to look at a dry-run before a delete, the agent calls the dry-run and stops. The next step is a separate call, after a person has seen the list.

This is the part that gets lost when MCP is framed as “give the model tools.” A tool list that is a subset of the human command is a different product. The agent should be a client of the command as it already works, including the cautious half.

The routine is the tool

A large share of SmartBot volume was scheduled jobs, not someone typing in a channel. Region health, test-location analysis, and workflow triggers ran on a clock. Those routines are the system. The chat command is a way to run the same thing while a person watches.

An agent that can only call the chatty surface, or a cousin written for the demo, is answering a different system than the one that ran overnight. I want the agent on the routine’s payload. If the scheduled check and the agent check can disagree, the platform has two healths, and the one in the IDE will be the one a tired person trusts at the wrong hour.

When a new client shows up, four rules apply:

  • It consumes the payload. It does not invent a parallel report.
  • The first reply quotes the result. Explanation is a follow-up, and only if someone asks.
  • A mutating tool includes the dry-run the human command already has. It does not offer the delete alone.
  • If a scheduled job already runs the check, the agent calls that check. It does not get a private copy.

Cursor, Claude Code, and Claude Desktop were the clients that consumed that payload. A new client will want its own phrasing. The payload stays. If each client has to be re-taught what healthy means, there is no longer one platform.

All posts