cd /news/developer-tools/mcp-deep-dive-part-1-why-model-conte… · home topics developer-tools article
[ARTICLE · art-47869] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

MCP Deep Dive, Part 1: Why Model Context Protocol Kills Integration Glue Code for Good

A developer at Mattrx, a multi-tenant marketing-analytics SaaS built on .NET 9 and Azure, reports that adopting the Model Context Protocol (MCP) eliminated 14 point-to-point integrations by replacing them with 3 MCP servers, deleting approximately 9,000 lines of glue code. The shift from bespoke integrations to capability-based MCP tools reduced the integration model from N agents × M backends to N agents + M servers, with tool discovery, auth, and audit handled uniformly across the protocol.

read5 min views1 publishedJul 4, 2026

Your AI roadmap does not die from a bad model. It dies from integration glue code — the hand-written adapter that wires agent number four to backend number nine, times every agent and every backend you will ever build. Model Context Protocol (MCP) is the thing that stops that multiplication.

This is Part 1 of a 15-part deep dive. Every part uses the same running example: Mattrx, our multi-tenant marketing-analytics SaaS (.NET 9 / Azure), and every metric here is from that real system.

Dimension Before (bespoke glue) After (MCP)
Integration model N agents × M backends N agents + M servers
Mattrx integrations 14 point-to-point clients 3 MCP servers
Adding a capability New adapter on both sides Declare one MCP tool
Tool discovery Hardcoded per agent Discovered at runtime
Auth & audit Reinvented per integration One OAuth/Entra boundary
External AI access Unsafe / not possible Scoped, governed, audited

The one mental shift: stop building integrations and start publishing capabilities. An integration teaches one agent how to call one backend. A capability is a tool any agent can discover and call from its schema alone. MCP makes capabilities additive instead of multiplicative.

With N agents and M backends, you write up to N×M integrations, and each one re-implements auth, retries, error mapping, and logging in its own slightly-wrong way.

BEFORE — N agents x M backends = up to N*M bespoke integrations

Insights ---+--> Campaigns API (custom client)
            +--> Events API   (custom client)
            +--> KPI API      (custom client)
            +--> Reporting API (custom client)

Help -------+--> Campaigns API (a DIFFERENT custom client)
            +--> KPI API       (a DIFFERENT custom client)

External AI ....> (no safe path at all)

AFTER — N agents + M servers = N+M, one protocol

Insights ---+
Help -------+           +--> mattrx-analytics (campaigns, events, kpis)
External AI +--- MCP ---+--> mattrx-reports  (create_report, status)
(approved) -+           +--> mattrx-admin    (flags, exports; locked)

Before, every agent embedded a bespoke client for every backend:

// BEFORE: the agent is welded to four hand-written clients.
public sealed class InsightsAgent(
    CampaignsApiClient campaigns,   // bespoke HTTP client #1
    EventsApiClient events,         // bespoke HTTP client #2
    KpiApiClient kpis,              // bespoke HTTP client #3
    ReportingApiClient reporting)   // bespoke HTTP client #4
{
    // Each client has its own auth, retry policy, and error model.
    // The next agent we build re-implements a slice of all four.
}

After, each capability is declared once as an MCP tool:

// AFTER: a capability declared once on the mattrx-analytics MCP server.
[McpServerToolType]
public sealed class AnalyticsTools(ICampaignQueries campaigns, AiPrincipal principal)
{
    [McpServerTool(Name = "get_campaign_kpis")]
    [Description("Return the KPI time-series for a campaign in the caller's tenant.")]
    public async Task<CampaignKpis> GetCampaignKpis(
        [Description("Campaign id within the caller's tenant")] string campaignId,
        [Description("ISO-8601 range, e.g. 2026-06-01/2026-06-28")] string range,
        CancellationToken ct)
    {
        // Tenant comes from the authenticated principal — never from the arguments.
        return await campaigns.GetKpisAsync(principal.TenantId, campaignId, range, ct);
    }
}

The agent side collapses to one client that speaks MCP to every server:

var result = await mcp.CallToolAsync(
    "get_campaign_kpis",
    new { campaignId = "4821", range = "2026-06-01/2026-06-28" },
    ct);

Result: 14 integrations → 3 servers, ~9,000 lines of glue deleted, almost all deletions.

Before, the toolset was a constant the agent was compiled with — the list and reality drift. After, the server advertises its tools and the client discovers them at runtime:

// AFTER: the agent asks the server what it can do, every session.
var tools = await mcp.ListToolsAsync(ct);
// each tool: name, description, JSON Schema for args — enough for an LLM to
// decide when and how to call it, with zero hardcoding.

Discovery is the quiet superpower: ship a new tool on the server, and every agent can use it next session. Onboarding a capability went from ~3 days to ~2 hours.

Before, every bespoke client reinvented auth (one static key, one OAuth scope, one that trusted a tenant id passed as an argument — the bug we shipped). After, every tool call enters through one MCP boundary:

// AFTER: one boundary enforces auth, scope, tenant binding, and audit for ALL tools.
public sealed class GovernedToolFilter(AiPrincipal principal, IAuthorizationService authz, IAiAuditLog audit)
{
    public async Task<ToolResult> InvokeAsync(McpToolCall call, Func<Task<ToolResult>> next, CancellationToken ct)
    {
        var decision = await authz.AuthorizeAsync(principal, call.RequiredScope, ct);
        if (!decision.Allowed) return ToolResult.Denied(call.RequiredScope);

        var result = await next();                 // tenant already bound from the token
        await audit.RecordAsync(principal, call, result, ct);
        return result;
    }
}

One OAuth 2.1 / Entra ID boundary replaced N bespoke auth flows. Tool-call error rate fell from 6% to 0.8% — most of those errors were auth and contract mismatches that simply stopped existing.

Before, a partner wanting their AI assistant to pull your KPIs meant "build them yet another client" — so the answer was "no." After, an approved external assistant authenticates via Entra ID, gets a token scoped to its tenant and to campaigns:read

, and calls the exact same tools our internal agents do — discovered, scoped, and audited identically. A capability that simply did not exist under bespoke integration.

The protocol is small — three message types do almost all the work: initialize

, tools/list

, tools/call

, all JSON-RPC over the transport (Streamable HTTP + SSE in production, stdio in local dev). That small surface is the point: it's small enough that any client and any server can implement it, which is exactly what makes capabilities additive.

Integrations scale as N×M. Protocols scale as N+M. Every bespoke client you write is a multiplication you'll pay for again with the next agent. Every capability you publish as an MCP tool is an addition every future agent gets for free.

Originally published on PrepStack. Adopting MCP and want a second pair of eyes on where to draw your server boundaries? Reach me at randhir.jassal[at]gmail.com.

── more in #developer-tools 4 stories · sorted by recency
── more on @mattrx 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/mcp-deep-dive-part-1…] indexed:0 read:5min 2026-07-04 ·