June 9, 2026

Agents Are More Like People Than Software

Part 1 of 2: why the interface patterns that work for deterministic software don't work for agents, and what to do instead.
4 min read
Profile photo of Adam Bird
Adam Bird
Co-Founder and CEO of Cronofy
Blog post Hero Image

I've spent 24 years building and commercialising web APIs. Recently, we shipped a commercial MCP server at Cronofy and started building agentic products on top of it. What follows is what I've learned so far about what changes when your API consumer can reason, and what doesn't change at all.

This is a two-part series. Part 1 covers the design shift: why the interface patterns that work for deterministic software don't work for agents, and what to do instead. Part 2 covers the operational reality of shipping a commercial MCP server, including the surprises, the trade-offs, and the decisions we're still making.

Continue to Part 2: Shipping a Commercial MCP Server →

The wrong instinct

A common approach to building an MCP Server that I've seen teams take is to wrap their existing REST API in this new protocol. Expose the endpoints as tools. Ship it.

This misses the opportunity that MCP represents. Not because the protocol is wrong, but because the interface was designed for a different type of consumer.

Typically, REST APIs are designed for systems of record. They expose composable primitives so the calling system can orchestrate transactions with fine-grained control. Allocate stock. Assign to a customer. Trigger delivery. The API provides building blocks. The client system owns the choreography.

That works when the caller is deterministic software executing a known sequence. It breaks when the caller is an agent.

Why granularity changes

Agents don't execute sequences. They interpret intent and decide what to do. The more steps you ask an agent to chain together, the more decision points you create. Each one is an opportunity for assumptions to creep in, for context to go stale, for the outcome to drift.

For example, you can expect a REST client to follow a predictable “buy bread, buy sausage, cook sausage, assemble” path. With an agent you probably need to limit its options to “I'd like a hotdog please”.

At Cronofy, we’ve built what is essentially a proto query language for calendar availability. Callers can specify complex rules on every request: timezone handling, buffer times, participant constraints, recurrence patterns. For system integrators who need that flexibility to tailor the availability search to their specific use case, it’s exactly right.

For an agent, it’s a liability. Not because the agent can’t call it. Because all of that expressiveness requires a significant amount of context, which in the vast majority of scheduling scenarios, it’s unnecessary.

In an interview scheduling flow, the rules aren’t being invented in the moment. The HR system knows which candidates are in play. The CRM knows which account exec, sales engineer, and product sponsor need to be on the call. The recruiting platform holds the job requisition. Those are systems of record. The rules are already there.

Agents are good at orchestrating primary infrastructure. Your CRM, your HR system, your recruiting platform. These are tools in which rules, compliance, regulation, and company culture are already encoded. Recreating that context on every API call is the wrong model.

So for the MCP server, we constrained the interface. The agent says “schedule a meeting with these three people.” Cronofy handles availability using the rules those people have already configured. The agent orchestrates. The systems of record hold the rules.

The sweet spot

This doesn’t mean you collapse everything into single monolithic tools.

We started simple by exposing separate read and update tools for calendar events rather than a single “reschedule” tool. The agent reliably figured out the multi-step flow. You ask it to move your one-to-one with Sarah from Tuesday to Wednesday, and it works out that it needs to find the event first, then update it.

But those tools are not the same as the REST primitives underneath. The REST API’s update endpoint lets you change anything about an event with fine-grained control. The MCP tool exposes a constrained version that operates in a specific way for scheduling workflows. Less noise. Fewer ways to go wrong.

The principle: constrain operations to the task domain, not to maximum flexibility. And my hunch is that anything beyond two composable steps starts to introduce failure modes you don’t want.

This is model-dependent. Current-generation models handle two-step composition well. Older or cheaper models may not. If you need to support less capable clients, you might wrap those steps into a single transaction tool. Design for the best available model, but know where the fallback is.

I don’t think there is a universal rule on how many tools to expose. It depends on your domain, your context, how much accuracy matters. You have to experiment. But the sweet spot is definitely somewhere between “expose all REST primitives” and “one tool per workflow.” And, probably closer to the latter than the former.

Error messages are prompts now

Errors have a very different role to play with MCP Servers.

In a REST API, error handling is diagnostic. You return an HTTP status code. 422 for validation failure. 403 for authorisation. 429 for rate limiting. The client has conditional logic that, if the engineer decided to implement it, does something based on the code.

With MCP, status codes are vague noise. The error message itself carries the decision logic.

If you were training someone to operate a piece of machinery, you wouldn’t say “error 123”. You’d tell them how to fix it. That’s the framing for MCP error messages. What can you tell the agent that allows it to resolve the issue itself, possibly without any human interaction?

REST error text is generally terse. Just enough for an engineer to understand the failure and build appropriate conditional logic. MCP error text is descriptive, instructional. It’s a prompt.

“This parameter needs to be a Workday job requisition ID, not a generic identifier.” “You’re not authorised to perform this operation in the current context.” “The requested time is outside the participant’s configured availability window.”

We write error messages completely differently for MCP than for REST.

Here’s why that matters beyond the obvious: in a REST integration, the client hardcodes its response to every error type. If your service improves and a previously failing operation starts succeeding, the client code doesn’t know. Perhaps validation constraints have been relaxed because of more capabilities in the service.

The problem is that the old error handling is baked in and would require a code change on the client to adapt it. With an agent, there’s no hardcoded decision tree. If the operation succeeds tomorrow where it failed today, the agent moves on. The error handling adapts without a code change on the client side.

This connects back to the documentation problem. REST error handling is another artefact that diverges from reality over time. Agents working with live error responses don’t have that problem.

Where the responsibility falls

People want agents to automate things. They want that automation to happen predictably.

There are moments where you want the agent to cope with different situations and make decisions. But when it’s decided what to do, you want a repeatable outcome.

This means the MCP server should take on as much responsibility as possible for the task it’s been given. Wrap the transaction. When the agent performs the operation, the same outcome happens every time. The automation works.

The agent’s job is to decide when to call a tool. Not how to execute the underlying operation.

The more your MCP server handles within each tool invocation, the more predictable the agent-driven workflow becomes. This is the opposite of REST thinking, where you deliberately push orchestration responsibility to the caller. With agents, you pull it back.

A note on language

Agents don’t want anything. They don’t have desires. They operate.

It’s really easy to slip into saying “the agent wants a simpler interface” or “the agent prefers tasks over primitives.” That’s sloppy. It leads to sloppy design decisions. Agents are software. They operate within the constraints you give them. The question is always: what constraints produce reliable, predictable, auditable behaviour?

That said, agents are more like people than software in one important respect: they respond to the same design thinking. Clear task descriptions. Instructional error messages. Contextual information scoped to what they need. If you design your MCP server the way you’d brief a capable new hire, you’re closer to right than if you design it the way you’d write an API reference.

Continue to Part 2: Shipping a Commercial MCP Server →