{"slug": "turning-a-system-of-record-into-an-ai-agent-building-mcp-tools-on-azure", "title": "Turning a System of Record into an AI Agent: Building MCP Tools on Azure", "summary": "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.", "body_md": "*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.*\n\nWe 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.\n\nThe constraints shaped every decision:\n\nFive layers, one direction of flow:\n\n```\nAgent (Agent Foundry)\n      │  MCP (JSON-RPC over HTTP)\n      ▼\nMCP server (API Management)\n      │  REST operation per tool\n      ▼\nAPI gateway (API Management)\n      │  single POST, routed by body\n      ▼\nTool executor (Logic App)\n      │  OAuth token + REST calls\n      ▼\nSource system (REST API)\n```\n\nThe key design choice: **one backend endpoint, many logical tools.** The Logic App exposes a single HTTP trigger that accepts `{ \"tool\": \"records.list\", \"parameters\": { ... } }`\n\nand 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.\n\nStart from the *questions*, not the schema. A useful toolset usually falls into a few families:\n\nTwo implementation notes that matter a lot:\n\n**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.\n\n**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.\n\nThe Logic App is a dispatcher. One HTTP request trigger, then:\n\n`first(split(tool, '.'))`\n\n).`Switch`\n\non category, with a nested `Switch`\n\non the full tool id inside each — each leaf is one read-only REST call.A few hard-won details:\n\n`@`\n\n.`@`\n\nalias, remember that Logic Apps reads a bare `@`\n\nas an expression. Double it (`@@`\n\n) so it renders as a literal `@`\n\nat runtime.`{ \"error\": \"unknown tool\" }`\n\nobject; 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`\n\nop takes a record id; an aggregate takes nothing). Keep the path equal to the tool id so a gateway policy can recover it later.\n\nGenerate 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.\n\nGotcha: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.\n\nImport the OpenAPI to create the operations, then add one API-scoped inbound policy that:\n\n`validate-jwt`\n\nwith your audience),`{ tool, parameters }`\n\nbody the Logic App expects,\n\n```\n<set-variable name=\"toolId\" value=\"@(context.Request.OriginalUrl.Path.Split('/').Last())\" />\n<set-body><![CDATA[@{\n    var incoming = context.Request.Body?.As<JObject>(preserveContent: true) ?? new JObject();\n    if (incoming[\"tool\"] != null) { return incoming.ToString(); }   // already wrapped\n    var wrapped = new JObject();\n    wrapped[\"tool\"] = (string)context.Variables[\"toolId\"];\n    wrapped[\"parameters\"] = incoming;\n    return wrapped.ToString();\n}]]></set-body>\n```\n\n**Authenticating to the backend — drop the shared secret.** A Logic App request trigger defaults to a SAS signature (`sig`\n\n) baked into its URL. That's a shared secret you then have to store and rotate. The better pattern: give API Management a **managed identity**, add an OAuth authorization policy on the Logic App trigger (issuer + audience, optionally locking to APIM's `appid`\n\n), disable SAS on the trigger, and let APIM authenticate with its own identity:\n\n```\n<authentication-managed-identity resource=\"api://<APP_ID>\" />\n```\n\nNo `sig`\n\n, nothing to rotate, and you can restrict the trigger to exactly one identity.\n\nAPI Management can publish selected operations as an MCP server. Point its \"expose an API as an MCP server\" feature at your API, select the operations you want as tools, and it hands you a server URL (an `/mcp`\n\nendpoint). The operation `operationId`\n\ns become the tool names the agent sees, so keep them clean (`records_list`\n\n, `analytics_by_category`\n\n).\n\nTier caveat:MCP server export isn't available on the Consumption tier of API Management. If the MCP Servers blade is missing, that's why.\n\nIn the agent, add a **Model Context Protocol tool**:\n\n`aud`\n\nyour gateway's `validate-jwt`\n\nrequires. The audience is the single most common misconfiguration — get it right and the token check passes.If the gateway 401s and the token *looks* right, paste it into a JWT decoder and check `iss`\n\n/`aud`\n\nagainst the policy. Managed-identity tokens use the v1 issuer (`https://sts.windows.net/<tenant>/`\n\n); configuring the policy with the v2 issuer produces a mismatch.\n\nTools enforce read-only at the API layer, but the *behavior* — tone, safety, honesty — lives in the system prompt. Ours does three jobs:\n\nThat last point is worth underlining: the agent should *never* hand-craft queries or field names. It picks a tool and fills named inputs. If a question needs a field the tools don't expose, that's a capability gap to report, not something to invent.\n\nThe demo worked on the first real question. The next few turns taught us the operational lessons:\n\nThe result is an agent a non-technical user can talk to in plain language, backed by a boringly maintainable pipeline, with the secret-handling and injection surface handled deliberately rather than by accident.\n\n*— Engineering notes, generalized. Replace all <placeholders> with your own tenant and resource values.*", "url": "https://wpnews.pro/news/turning-a-system-of-record-into-an-ai-agent-building-mcp-tools-on-azure", "canonical_source": "https://dev.to/vignesh_athiappan_818c9e0/turning-a-system-of-record-into-an-ai-agent-building-mcp-tools-on-azure-291f", "published_at": "2026-07-17 06:57:13+00:00", "updated_at": "2026-07-17 07:00:59.257803+00:00", "lang": "en", "topics": ["artificial-intelligence", "ai-agents", "ai-tools", "ai-infrastructure", "developer-tools"], "entities": ["Azure", "Azure Logic Apps", "API Management", "Agent Foundry", "Model Context Protocol", "MCP"], "alternates": {"html": "https://wpnews.pro/news/turning-a-system-of-record-into-an-ai-agent-building-mcp-tools-on-azure", "markdown": "https://wpnews.pro/news/turning-a-system-of-record-into-an-ai-agent-building-mcp-tools-on-azure.md", "text": "https://wpnews.pro/news/turning-a-system-of-record-into-an-ai-agent-building-mcp-tools-on-azure.txt", "jsonld": "https://wpnews.pro/news/turning-a-system-of-record-into-an-ai-agent-building-mcp-tools-on-azure.jsonld"}}