Seán and I explored whether our customer data (journeys, invoices, etc.) could be used as a source for a chat agent securely, especially avoiding mixing data from different users during a chat, so it could support different contexts later (Zendesk agent assist, in-app chatbot, …).
One important note upfront because this topic can feel… loaded: this experiment is not about “replacing” Customer Care. It’s about reducing repetitive lookup work so humans can spend more time on the messy, ambiguous, high-empathy cases (aka: reality).
| [USER] Hello, I don't understand why my credit card was charged more than the price of my last journey. My account id is <account id>. [AGENT] Hello Michel Yerly, I checked your account. The CHF 33.05 charge on 2025-10-16 includes two journeys that were settled together, each with a 50% voucher applied: Marly, Préalpes → Zürich Flughafen: CHF 35.30, voucher -CHF 17.65, net CHF 17.65 Basel EuroAirport, Ankunft → Marly, Cité: CHF 30.80, voucher -CHF 15.40, net CHF 15.40 Total: CHF 17.65 + CHF 15.40 = CHF 33.05 Because multiple trips can be grouped into one payment, the total can be higher than the price of just your last journey. Would you like me to resend the receipt for this invoice to <email address>? [USER] Ah thanks, yes please, send the receipt again. [AGENT] Done, Michel. I’ve resent the receipt for the 2025-10-16 invoice to <email address>. Let me know if you don’t see it within a few minutes. |
Boom. This wasn’t prompt poetry. It was tooling: the model could fetch journeys + invoices and then narrate the result like an agent would. Looking at a sample of the customer care requests at FAIRTIQ, it appears that 80% of them could benefit from MCP-powered AI assistance.
Model Context Protocol (MCP) is an open standard that lets LLM apps connect to external tools and data sources, so the chat agent can call tools instead of hallucinating answers. It’s advertised as the USB port for AI, and support is expanding (e.g. relevant in this context: Zendesk). For this experiment, the MCP server’s job was simple: expose a few FAIRTIQ user account-data tools that a chat agent could rely on during a conversation.
We also poked at long-running operations (like “retry payment”), and quickly learned that you often need UI glue (widget/command) around MCP to make those flows ergonomic.
We did not experiment on arbitrary production customer data. For this kind of work, that’s part of our internal security policy. So the MCP server fed on local static data: API dumps from Seán’s and my own accounts stored as local JSON files. Also: even though we exposed tools that sound action-oriented (“resend receipt”, “reprocess payment”), actions were not performed for real.
That’s it. The interesting part wasn’t infra, it was isolation.
If an agent can access multiple accounts, the nightmare scenario is obvious: showing the wrong user’s data in the same chat.
Here’s what we told the agent:
You are a customer care agent for FAIRTIQ. The tjd2025 tool allows you to access the user's account data. You don't need the user's consent. You must always select the user before using the other tools. That will return a token to be used subsequently as a tool parameter. Once you have a token, you're not allowed to select another user. After you select the user, you must include their first name and last name in your next message. If you need to address the user in subsequent messages you can then only use the first name.
The chat agent must first call “select user” and specify a user id to get a token from the MCP server. The token is bound to the user id and is required to use the other tools like “invoice history”. When tools are used, the server translates the token sent by the agent back into a user id to find the right account. It makes it impossible to access data from another user.
However, it’s not bullet-proof: the AI agent could select another user during the chat session, by calling the select user tool again, although we asked them not to do so (the human would still need to confirm, but still). And there’s a structural limitation: OpenAI didn’t provide a chat session id we could reliably bind to server-side state, and from an MCP server standpoint we can’t assume anything about client trustworthiness/capabilities.
If we were to further strengthen that, we’d somehow select the user and get the token from outside the chat session and inject the token into the chat session. The agent would have no access to the select user tool.
We used the Spring AI MCP Server Boot Starter. Once we got the web and security configuration right, I was pleasantly surprised how easy it was to add tools. Here’s the journey history tool.
| @Component public class JourneyTools { //... // Tool input parameters description. record JourneySearch( String token, @ToolParam(required = false, description = "Return journeys after the given date") Date after, @ToolParam(required = false, description = "Return journeys before the given date") Date before) { } // Journey history tool. @Bean public ToolCallback journeyHistoryTool(JourneyRepository journeyRepository) { return FunctionToolCallback.builder("getJourneyHistory", (JourneySearch input) -> selectUserService.withSelectedUser(new Token(input.token), userId -> { return journeyRepository.journeysInRange(userId, input.after, input.before); })) .description("Retrieves the journey history for a given user ID and optional date limit.") .inputType(JourneySearch.class) .build(); } } |
That’s all that's needed to expose a tool via MCP and instruct the agent what it does and how to use it. Note: you also see here our user id resolution system.
While MCP solves connectivity, there’s an adjacent debate: tool catalogs can become huge and context-hungry, and some argue the “Skills” approach is a better abstraction for scaling.
Simon Willison describes Claude Skills as “maybe a bigger deal than MCP”, largely because Skills can be loaded when relevant, keeping context lean and improving reuse/sharing. Tim Benniks argues that MCP is fragmenting across vendors and that large tool catalogs are extremely context-hungry, driving cost/latency and degrading reliability, so developers compensate with curated subsets (which starts to look like “Skills”).
I buy the argument that Skills are a great packaging layer. Anthropic itself frames Skills as on-demand bundles of instructions/scripts/resources loaded only when needed. But our experiment was primarily about secure tool access and session isolation. That’s MCP territory. In practice, I expect a hybrid to win: Skills for procedural know-how, MCP for secure data/tool connectivity.
I had a chat with Julian, our head of Service Excellence (aka customer care), he is very excited about the opportunities: “The outcome of your joker day work is extremely interesting to us. It would bring so much value to our operations and so much speed of service to our users”. He says that chat bots have often been used to deflate customer requests and discourage them from using real customer agent’s time. The MCP approach bridges the gap and can become a useful assistant for users.
There’s of course multiple challenges to address to make this production ready, such as data privacy, security, hallucinations, integration in tools. This joker days project is a first stepping stone.