01 AI tooling · Sehat Kahani
Production database as an LLM tool surface
A Model Context Protocol server that exposes a live Azure MySQL database as a set of LLM-callable tools, so the platform can be inspected and managed in natural language instead of hand-written SQL.
The problem
Sehat Kahani runs a multi-tenant telemedicine platform. The operational questions that come up daily — how many consultations went through a given clinic, which records look inconsistent after a schema change, what a particular tenant's configuration actually is — all live in the production database, and all of them required someone who could write SQL against a schema they already understood.
That makes the database a bottleneck shaped like a person. Every operational question queues behind whoever is fluent in the schema.
The interesting problem was not "let an LLM write SQL." It was: what is the right tool surface to hand a model, such that the useful questions become easy and the dangerous ones become structurally hard?
Why MCP rather than a chatbot with database credentials
The Model Context Protocol standardises how an LLM client discovers and calls external capabilities. Instead of embedding database access inside one application's prompt logic, the capability is published once as a server, and any MCP-speaking client can use it.
Two properties made that the right shape here:
- The boundary is explicit. The model never holds a connection string. It calls named tools with typed arguments, and the server decides what those tools are permitted to do.
- The capability outlives the client. The same server works from a desktop LLM client, an IDE, or the platform's own chat interface — no rewrite per surface.
What I built
A Python MCP server sitting in front of the platform's Azure MySQL instance, publishing schema inspection and query execution as discrete tools, plus a Chainlit chat interface as the human-facing front end.
The design principle throughout: make the model do discovery before it does execution. A model that has to list tables and describe a table before it can query it produces far better queries than one handed a raw SQL escape hatch and a hopeful prompt.
Design decisions worth defending
- Schema introspection as a first-class tool, not context stuffed into a system prompt. Schemas drift; a tool call reads the current truth, a pasted schema goes stale the day it's written.
- Narrow tools over one wide one. A single
run_sqltool is easy to build and hard to reason about. Separating listing, describing and querying gives the model an obvious path and gives the server obvious places to enforce limits. - Chainlit for the human surface. Operational users needed a chat window, not an IDE. Chainlit gave a usable interface without building a frontend from scratch — the point of the project was the tool boundary, not the chrome.
What I'd change
Two things, honestly. The first build read its database credentials from configuration that was closer to the code than it should have been — that belongs in a managed secret store, and moving it there is the first change I'd make on any redeploy. The second is read-path enforcement: the strongest version of this server treats read-only as a property of the connection, not a convention the tool layer maintains.
I'd also add per-tool result caps and query timeouts at the server rather than relying on the model to ask for small results. Models are cooperative, not reliable.
Why this generalises
This is the same pattern behind most useful AI infrastructure right now: the model is not the product, the tool boundary is. Deciding what a model can call, what it must discover first, and what it can never reach is a systems design problem, and it looks a lot more like API design than prompt engineering.
I've since contributed to an open-source MCP server for Pakistan Stock Exchange data, which sharpened the same instinct against a codebase I did not write.