MCP & AI TOOLS
MCP server explained
A small service that publishes a list of tools and answers calls to them with structured results. For business data, the right tools are search, filter and aggregate over indexed collections - not raw SQL.
- Tool call with the access key - no key, no connection
- The server turns it into an index request
- Records and facets come back from the index
- A structured result goes to the client, and on to that client's AI provider
What a server publishes
When a client connects, the first thing it asks for is the tool list. Each entry has a name, a description, and a schema for the arguments the tool accepts. The model reads all three, and the description is the one that decides whether it calls the right tool with the right arguments. "Search" tells it nothing. "Search a collection for records matching text and filters; returns matching records with counts per value for the requested fields" tells it when to use the tool, what to pass, and what it will get back.
A server for business data is therefore mostly writing: a clear statement of what each collection contains, what each field means, which values a filter takes, and what the result looks like. The code that runs the search is the easy part. The descriptions are what make a model use it well, and they are worth the same care as documentation for a human developer, because that is what they are.
One tool call, end to end
The lead visual is the sequence. The client sends a tool call across the boundary with its access key. The server checks the key, turns the call into a request against the indexed collections, and gets back records and facets. It shapes them into a structured result and returns it to the client, which hands it to the model. The model, in the client, renders what it was given: a table, a chart, a sentence. At no point did the model talk to the index, and at no point did the server see the conversation; each side sees only its half.
What to expose for business data
list_collectionsWhat data exists: orders, customers, products, invoices, with record counts and what each is for.describe_collectionThe fields of one collection and their types, so the model can filter, facet and sort on real names.searchRecords matching text and filters, with sorting and paging.facetCounts and totals per value, range or stat, on the filtered set - the aggregation tool.get_recordOne record by id, in full.run_sqlNot published. Free SQL is the attack surface and the load on production.update_recordNot published. An assistant answers questions; it does not change the books.
Five tools cover most questions. list_collections and describe_collection let the model orient itself - what exists, what fields it has - so it filters on real names rather than guesses. search returns records for text and filters. facet returns counts and totals per value, range or stat on the filtered set, which is the tool that answers "how many" and "how much" exactly, without the model adding anything up. get_record returns one record in full.
What not to expose is as important. A free SQL tool turns the server into a database login for whoever holds a key, and every question into a query on whatever the tool can reach; it is the attack surface and the load on production in one. Write tools turn an assistant that answers questions into one that changes the books, with a model deciding when. A business-data server answers questions about a copy of the data, and the tool list should say so by what it leaves out.
Transport
A local server runs as a process on one machine, started by the client, and suits a developer connecting an editor to a tool on their own laptop. A remote server is an HTTP endpoint and suits a team: one deployment, next to the data, that many clients connect to with their own keys. For business data remote is the natural choice. The data is already inside the company's infrastructure, so the server sits beside it; the server is updated in one place; and the keys and the log live in one place.
Access
Endpoint plus access key. The key is issued from the deployment that runs the server, not from the AI tool's vendor, so the people who own the data decide who connects. One key per team or per tool keeps the blast radius small and the log readable. Rotation on a schedule limits how long a leaked key is useful; revocation ends it immediately. No key, no connection, and nothing the server does not publish is reachable with one.
What crosses the boundary
Out: the result of each tool call - the records and counts the client asked for - which go to the client and on to that client's AI provider, where they are handled under your agreement with that provider. In: the tool calls themselves and the key. What stays inside: the index, the keys, the production database, and everything the question did not ask for. Only the requested results are sent to the client; the server does not send data anywhere on its own. Data flow map puts this flow beside the others.
Shapes on the wire
A tool definition is a name, a description and a JSON schema. This is the kind of entry a client sees in the tool list:
// one entry from tools/list { "name": "facet", "description": "Counts and totals per value of a field, over the records that match the filters. Use for 'how many' and 'how much' questions.", "inputSchema": { "type": "object", "properties": { "collection": { "type": "string", "enum": ["orders", "customers", "products", "invoices"] }, "field": { "type": "string" }, "filters": { "type": "object" } }, "required": ["collection", "field"] } }
A call names the tool and fills the arguments; the response carries structured content the client can render:
// tools/call { "name": "facet", "arguments": { "collection": "orders", "field": "region", "filters": { "period": "this_month" } } } // result { "field": "region", "buckets": [ { "value": "North", "count": 395 }, { "value": "West", "count": 247 }, { "value": "East", "count": 168 }, { "value": "South", "count": 142 } ] }
Two details make a server operable. The enum on collection is a guardrail: the schema itself refuses a collection that does not exist, before any code runs. And every call should be logged with the key that made it, the tool, the arguments and the time, so that "who asked what" has an answer. The result above - North 395 · West 247 · East 168 · South 142 - came from the index, not from the model, which is the whole point of giving the model a facet tool instead of a calculator.