RWA MCP Server
A production MCP server splitting typed, audited tool access into separate customer and authoring surfaces.
Why there is no measurement
Measured internally at RWA — not mine to publish
No measurement
Why not
This ran in production and its effect was measured internally, but those numbers are RWA's rather than mine and are not mine to publish. I would rather show the gap than publish a figure I cannot let anyone check, or quietly leave the project off the site because the result is inconvenient to evidence.
Problem
AI assistants were being wired to company systems one integration at a time. Every new tool meant another bespoke client, another auth path and another place for a model to be handed more access than the task needed. The cost was not novelty — it was that nobody could answer, in one place, what an assistant could actually reach, or whether a customer-facing assistant and an internal one were reaching through the same door.
Approach
A single MCP server as the boundary, split into two entry points from one tool registry: a customer-facing surface carrying only read/query tools, and a separate authoring surface carrying the write and review tools, so the write path cannot be attached to the customer build by a registration mistake. Tools are declared with typed schemas so a model's call is validated before it reaches a system rather than after, access is scoped per tool and fails closed if unconfigured, and calls are logged so "what did it do" has an answer that does not depend on the model's own account of itself.
Components
-
Tool registry
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLEThe list of what an assistant can reach. Capability is added by declaring a tool rather than by writing another client with its own auth story.
-
Customer / authoring split
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLEOne registry, two entry points: a customer server with only read/query tools, an authoring server for writes — so the write path cannot reach the customer build by mistake.
-
Typed schema validation
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLEValidates a model's tool-call arguments at the boundary, so a malformed call fails locally as a validation error instead of reaching the downstream system and failing there.
-
Per-tool scoping
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLEScope is a property of the tool rather than of the caller, so an agent cannot reach past its allowed surface regardless of what the prompt says.
-
Fail-closed access gate
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLEAn unconfigured scope does not default to open. If the allowlist for a given caller is unset, every tool behind it refuses rather than falling back to unrestricted.
-
Metadata-first query gate
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLEA downstream source's schema must be read through the server before a query tool against it will run, so a call uses real field names instead of guessed ones.
-
Guarded query execution
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLEQuery and export tools carry their own guardrails — response size caps and output validation — rather than trusting the caller to ask for something reasonable.
-
Demo sites as a proving ground
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLENew capabilities are exercised on demo sites first — a staging ground ahead of the real customer sites they'll eventually reach.
-
Call audit log
Evidence: MEASURED INTERNALLY — NOT PUBLISHABLERecords what was called so that "what did it do" has an answer that does not depend on the model's own account of itself.
Why a server rather than more integrations
The failure mode with assistant tooling is not that any single integration is hard. It is that the tenth one is written the same way as the first, and by then no one person can enumerate what the assistants can reach. Access ends up described by whichever engineer last touched a client.
Putting one MCP server in front of the systems moves that question to a place where it can be answered. The set of tools is a list. The scope on each tool is a property of the tool, not of the caller. Adding capability is an edit to a declaration rather than a new code path with its own auth story.
Two entry points, one registry
The server is not one process with a flag. There is a customer-facing entry point and a separate authoring entry point, and the tools that write or review anything live only behind the second one. Both entry points register through the same function, which is the point — a write tool cannot end up on the customer build through a call site someone forgot to remove, because there is only one place tools get attached and the customer path never calls into the half of it that holds write tools.
The typed boundary
Tool schemas are the part that earns its place. A model producing arguments for a tool call is producing structured output under exactly the conditions that make structured output unreliable — long context, ambiguous instructions, a plausible-looking wrong answer available.
Validating at the boundary means a malformed call fails as a validation error with a message the model can act on, instead of reaching the downstream system and failing there as something harder to attribute. It converts a class of silent misbehaviour into a loud, local one.
What is deliberately absent here
No client names, no internal metrics, no architecture diagram of an RWA system. The employer boundary in this site’s content rules is not a formality: this is a live production system belonging to my employer, and the interesting details are exactly the ones that are not mine to give away.