Custom integrations for each AI assistant create more code to maintain. Copying business logic into those integrations also lets calculations and permission checks drift from the rest of your app.
Expose useful operations through an MCP (Model Context Protocol) server, with a thin adapter that calls your existing application core.
Think of MCP as a "USB port" for AI applications: a common protocol through which compatible clients can discover and call your app's tools. You implement the server interface once rather than build a separate connector for every client. Client capabilities and configuration still vary.
MCP standardizes the connection. Your implementation still needs authentication, authorization and safe handling of tool calls. It does not automatically update deployed integrations or make them secure.
Implement and test an operation in a shared application service or existing API before exposing it as an MCP tool. Keep business calculations, input constraints and resource permission checks in that core. Add a CLI when people or CI also need the operation. A tested library is enough when nobody needs a terminal command.
The MCP adapter only translates between the protocol and that core:
Follow the MCP tools specification for protocol behaviour.
A Northwind warehouse item has 20 units on hand, 8 reserved and 4 quarantined, so 8 are available. Both a CLI command and an MCP tool report that number.
// Each adapter copies the stock rule, and the copies drift.int CliAvailable(Stock stock) => stock.OnHand - stock.Reserved; // 12 - forgot quarantineint McpAvailable(Stock stock) => stock.OnHand - stock.Reserved - stock.Quarantined; // 8
❌ Figure: Bad example - Each adapter has its own calculation, so the CLI reports 12 available while MCP reports 8
// Both adapters only delegate to the shared core.int CliAvailable(Caller caller, string itemId) => inventory.GetAvailable(caller, itemId); // 8int McpAvailable(Caller caller, string itemId) => inventory.GetAvailable(caller, itemId); // 8// InventoryService - the one place the rule and the access check live.public int GetAvailable(Caller caller, string itemId){var stock = stockStore.GetForCaller(caller, itemId); // denies warehouses the caller can't accessreturn stock.OnHand - stock.Reserved - stock.Quarantined; // 20 - 8 - 4 = 8}
✅ Figure: Good example - Both adapters call InventoryService, so a stock rule change is made and tested once