Turning a System of Record into an AI Agent: Building MCP Tools on Azure A developer built an AI agent that provides read-only access to an enterprise system of record using the Model Context Protocol (MCP) on Azure. The architecture uses Azure Logic Apps, API Management, and Agent Foundry to expose discrete tools that an AI model can call in response to natural language queries. Key design choices include a single backend endpoint that routes to multiple logical tools and generating tool schemas programmatically to prevent drift. A practical, end-to-end walkthrough of taking a read-only slice of an enterprise source system and exposing it to an AI agent as a set of Model Context Protocol MCP tools — using Azure Logic Apps, API Management, and Agent Foundry. All identifiers below are placeholders; swap in your own. We wanted a simple outcome: a user asks a business question in plain language and gets a straight answer — no dashboards, no field names, no training on the underlying system. That means an AI agent with safe, read-only access to a backend system of record, exposed as discrete tools the model can call. The constraints shaped every decision: Five layers, one direction of flow: Agent Agent Foundry │ MCP JSON-RPC over HTTP ▼ MCP server API Management │ REST operation per tool ▼ API gateway API Management │ single POST, routed by body ▼ Tool executor Logic App │ OAuth token + REST calls ▼ Source system REST API The key design choice: one backend endpoint, many logical tools. The Logic App exposes a single HTTP trigger that accepts { "tool": "records.list", "parameters": { ... } } and routes internally. API Management then fans that single endpoint out into many named operations, and its MCP feature turns those operations into agent tools. This keeps the backend trivial to maintain while the agent still sees a rich, typed tool catalog. Start from the questions , not the schema. A useful toolset usually falls into a few families: Two implementation notes that matter a lot: Prefer aggregates over "pull a list and count." If the source system can compute a sum or a group-by server-side, a "total by category" answer is one summary row, not hundreds of records the model has to tally. Push the work down to the system wherever you can. Prototype against the real API first. A quick collection of calls against the source system's REST endpoint with OAuth client-credentials lets you nail the field names, filters, and relationships before you touch orchestration. Field-name mismatches are the most common silent failure — verify against real responses. The Logic App is a dispatcher. One HTTP request trigger, then: first split tool, '.' . Switch on category, with a nested Switch on the full tool id inside each — each leaf is one read-only REST call.A few hard-won details: @ . @ alias, remember that Logic Apps reads a bare @ as an expression. Double it @@ so it renders as a literal @ at runtime. { "error": "unknown tool" } object; each matched case overwrites it. Unknown tools then return cleanly instead of hanging.The agent needs a tool schema per operation. Generate an OpenAPI document with one operation per tool , where each operation's request body is only that tool's parameters a get op takes a record id; an aggregate takes nothing . Keep the path equal to the tool id so a gateway policy can recover it later. Generate this programmatically from the same source of truth as the Logic App so the two can't drift — derive each operation's parameters from the inputs the Logic App actually reads. Gotcha:API Management's inline "OpenAPI specification" editor parses the document asSwagger 2.0. Paste an OpenAPI 3.0 doc and you get"The Swagger version specified is unknown."Either import via theAdd API → OpenAPIsurface which accepts 3.0 , or hand it a Swagger 2.0 document. Same operations either way. Import the OpenAPI to create the operations, then add one API-scoped inbound policy that: validate-jwt with your audience , { tool, parameters } body the Logic App expects,