An MCP server that let a support agent stop reading and start acting
A B2B SaaS company had a support copilot that could summarize a ticket beautifully and do nothing about it. Every action still went through a human copying values between Zendesk, Salesforce, and an internal Postgres database. We designed and shipped an MCP server that gave the agent scoped, audited tools, and tier-1 resolution went from advisory to autonomous.
- Scope
- MCP server exposing 14 tools across 3 systems
- Timeline
- 5 weeks from spec to production
- Delivery
- Full source in the client's repo, deployed on their infrastructure
- Volume
- About 12,000 tickets a month
01The mess before
The agent could read everything and change nothing
The copilot had read access through a handful of one-off API scripts. It could tell an engineer which plan a customer was on and what had broken, then stop. Applying the credit, extending the trial, or reopening the linked opportunity was all manual, so the agent saved reading time and nothing else.
Every new capability meant another bespoke script
Each time the team wanted the agent to do one more thing, someone wrote another integration by hand, with its own auth handling and its own idea of error handling. Fourteen scripts in, nobody could say with confidence what the agent was allowed to touch.
Nobody would sign off on write access
The blocker was not capability, it was accountability. Security would not approve an agent with write access to billing and CRM until there was a way to scope what it could do, log what it did, and revoke it in one place. Loose scripts with shared API keys did not meet that bar.
02What we wired together
03How the build ran
-
Started from the transcripts, not the API docs
We read two weeks of real ticket transcripts and marked every point where a human had to act. That list became the tool surface. It was noticeably shorter than the list the team had assumed, and three of the tools they had planned turned out never to be needed.
-
Designed tools around intent, not endpoints
A thin wrapper over the REST API would have given the agent forty low-level calls to compose, which is exactly how agents go wrong. Instead each tool matched a decision a support engineer actually makes, like extend_trial or apply_service_credit, with the multi-system orchestration hidden behind it.
-
Put authorization in the server, not the prompt
Every tool declares who may call it, against which accounts, and up to what value. A credit above the threshold does not get refused by the model, it gets refused by the server and returned as a structured error telling the agent to escalate.
-
Shipped read-only first
The server went to production with the read tools live and the write tools behind a flag. That gave two weeks of real traffic to check that the agent picked the right tool before anything it did was irreversible.
-
Turned on writes one tool at a time
Write tools were enabled in order of blast radius, starting with ticket comments and ending with billing credits. Each one ran shadowed for a week, where the agent proposed the call and a human approved it, before it went autonomous.
04Under the hood
Tool design is where agents actually fail
Most misuse we saw in testing was not the model being reckless, it was the model being handed ambiguous tools. Two tools with overlapping descriptions get picked at random. A tool with six optional parameters gets called with the wrong three. Merging overlapping tools, making the required parameters genuinely required, and writing descriptions that say when not to use the tool cut incorrect tool selection from roughly 9 percent to under 2 percent, with no change to the model.
Idempotency keys, because agents retry
An agent that gets a timeout will try again, and a support credit applied twice is a real financial event. Every write tool takes an idempotency key derived from the ticket id and the action, held in Redis. A repeated call returns the original result instead of performing the action again, which turned the retry behaviour from a liability into a non-event.
Structured errors so the agent can recover
A tool that fails with a raw stack trace tells the model nothing useful. Every failure returns a typed error with a reason and a suggested next step, so an over-threshold credit comes back as needs_human_approval rather than a 403. The agent then escalates correctly instead of retrying the same call until it gives up.
One audit log, which is what unblocked security
Because every action passes through the server, there is one place that records who invoked what, against which record, with which arguments and result. That single log is what moved the security review from a blocker to an approval, and it is the reason write access was granted at all.
05Before and after
| Metric | Before | After |
|---|---|---|
| Tier-1 tickets resolved without a human | 0 percent | About 62 percent |
| Median time to resolve a tier-1 ticket | About 5 hours | About 11 minutes |
| Integration surfaces to maintain | 14 bespoke scripts | 1 MCP server |
| Incorrect tool selection in evaluation | About 9 percent | Under 2 percent |
The copilot stopped being a reading aid and became part of the support process. Tier-1 work clears itself, engineers spend their time on the tickets that actually need judgment, and every action the agent takes is scoped, logged, and revocable from one place. When the team wants a new capability, it is one tool definition, not another script nobody will maintain.
Have a build like this one?
Book a scoping call →