Data Governance Assistant over MCP
An LLM agent I added to a production governance web app at HCA Healthcare. It answers questions through grounded SQL over a secured MCP tool service and controls the dashboard from plain language.
What it is
A data governance web app at HCA Healthcare already had its backend and frontend. I added the assistant inside it: the agent, its tools, the MCP service those tools run in, the grounded SQL retrieval, most of the frontend control the agent uses, and the security around all of it.
What the assistant does
It answers natural language questions through grounded SQL retrieval rather than free generation, keeps multi-turn session memory so follow-up questions work as expected, routes questions by intent, and can set dashboard filters directly from a plain-language request.
Grounding matters because in a governance context a confident wrong answer is worse than no answer.
The MCP service is the boundary
The agent does not touch the database. Its tools run in a separate MCP service on Cloud Run, which the backend calls over authenticated HTTP. The service exposes a small allowlisted set of read-only tools: reference data and schema metadata, filter-field metadata, read-only queries, filter suggestions for the dashboard, and a model-card action. Unknown tool names are denied.
Two layers of identity
The backend presents a bearer token, compared in constant time. That proves the call came from the backend, but not which user it is acting for.
For that, the backend signs a short-lived JWT assertion carrying the user’s validated identity. The service checks the signature, issuer, audience, and expiry, and that the tenant, groups, and roles are consistent. A valid assertion still does not grant access on its own. A deny-by-default policy decides per tool whether that user may call it, and explicit deny rules win over allow rules.
Read-only SQL
Only SELECT and WITH statements are accepted, one statement at a time. Write and administrative keywords are blocked, system catalogs are blocked, access is limited to the approved schema, results are capped, and queries run in read-only transactions with a statement timeout. Raw database errors are not passed back to the model.
The model can ask for a query, but it cannot grant itself permissions. SQL validation and database privileges are separate layers, and either one alone stops a write.
Fail closed
If authorization is disabled, the policy file is missing, or the default action is allow, the service refuses to start instead of coming up looking healthy but unprotected.
CI enforces the same posture. It rejects deploy scripts that allow unauthenticated access, checks the service template for the secure defaults, and runs the security regression tests on every pull request.