MCP Explained: How AI Agents Connect to Your Apps
MCP Explained: How AI Agents Connect t…
MCP compresses AI-to-tool M×N integrations into M+N—but doesn't provide memory or security.
Starting from a real e-commerce support bot scenario, the article explains why MCP is still needed when REST APIs, GraphQL, and SDKs already exist. Direct integration falls into the M×N trap: every AI app writes a custom connector per tool, and costs explode when switching models, tool versions, or reusing across teams. Letting LLMs call REST directly doesn't work either—verbose responses waste tokens, pollute context, and 422-style errors confuse models. MCP adds a translation layer exposing high-level, schema-validated tools and moves tool definitions to the server via dynamic discovery. But the protocol itself provides no conversation memory, access control, or credential protection—those must be implemented explicitly at the host, server, and backend layers. The article ends by unpacking M, C, P, the Host/Client/Server architecture, the full path of a single request, and criteria for building your own server.
Why Do We Need MCP When We Already Have APIs?
Most engineers react the same way the first time they hear about MCP (Model Context Protocol): we already have REST APIs, GraphQL, and SDKs, and services have been talking to each other for years—so why invent a new protocol just because AI showed up?
You can't answer this question by memorizing definitions. Walk through an e-commerce customer service bot scenario and the answer emerges on its own. Suppose you're building an AI support agent for your company, and the user asks: "Where's my order 12345? Can I get a refund?" An LLM is fundamentally a language model steeped in its training data—it knows how to write a polite reply, but it knows nothing about your order database, payment gateway, or ticketing system. To make it actually do work, you have to connect those systems.
An API is like a restaurant's order counter: you send a defined request, and the system returns a defined response. Orders go through the ERP or Shopify, refunds through Stripe, tickets into Zendesk, shipping status from FedEx, and for urgent cases a Slack notification to pull in a human. The question isn't whether you can connect—it's how you connect, how many connections you need, and how you maintain them over time.
The Hidden Cost of Direct Integration: The M×N Problem
The traditional API approach is straightforward: write integration code in your app backend for each service. Read the Stripe docs, pull the SDK, write wrapper functions, then define a schema for the LLM's function calling; write a set of connection and query logic for Postgres; repeat the same process for Zendesk and Slack, each handling auth, parsing JSON, and writing tool definitions.
Three or four tools is tedious but manageable. The real trouble comes with scale and change:
- Switching models: Move from GPT to Claude, or to a local open-source model, and every vendor's tool calling format and SDK differs—migration means code changes plus testing.
- Switching tool versions: Zendesk adds a
check_warranty_statustool, or an endpoint gains a required parameter, and the old schema hardcoded in your app causes requests to fail. - Duplicate work across teams: Another team needs the same systems, so they either rewrite everything or awkwardly share a library.
Zoom out to the whole industry: M AI applications (Claude Desktop, Cursor, various in-house chatbots) × N tools and data sources (GitHub, Slack, Postgres, Stripe) produces the classic M×N integration dilemma. Every AI app developer writes a custom connector for every tool—code that's duplicated, fragile, and hard to maintain. What MCP solves is compressing this into M+N: define one shared integration boundary, and apps and tools each take responsibility for the protocol.
Why Not Just Let the LLM Call REST APIs Directly?
A fair pushback: REST APIs are already the standard—can't we just have the LLM call HTTP endpoints directly?
Technically yes, but the experience is terrible. Take fetching an order from Stripe or Shopify: real responses often have hundreds of fields—internal database UIDs, nested metadata, pagination cursors, deprecation flags, assorted timestamps, system-level configuration. All of it is noise to an LLM, with three practical consequences:
- Token consumption and latency: Verbose responses burn a lot of tokens, driving up cost and slowing responses.
- Context pollution: Irrelevant fields crowd the context window, making the model more prone to confusion and even hallucination. Think of the context window as the model's desk—relevant files help, clutter hurts.
- Unfriendly error semantics: REST is built for developers, returning HTTP status codes like 422 and cryptic error codes. A human engineer can look up the docs and understand, but at runtime an LLM needs a natural-language explanation—"what went wrong and how to fix it"—not a number.
MCP's approach is to add a translation layer in front of the API. The MCP server still calls Stripe's REST API or queries a SQL database internally, but it exposes high-level, AI-native tools—for example, "check order refund eligibility," an operation that combines order status and shipping info—with schema validation (order ID required, must be a string, reject malformed input outright). The protocol itself is not an AI brain and grants no magical permissions—the actual implementation and permissions still depend on the APIs, databases, and filesystems behind the server.
function calling is the prerequisite for understanding this section: it's a structured output capability offered by mainstream LLMs, where developers pass the available tools (name, purpose description, parameter schema) to the model along with the prompt. When needed, the model stops generating natural language and instead returns a call intent matching the schema, e.g. {"name": "get_order_status", "arguments": {"order_id": "12345"}}. The model doesn't execute any function itself—the host application makes the actual call, then feeds the result back to the model to continue reasoning. Precisely because function calling's inputs are textual tool definitions and JSON arguments, the verbosity of tool descriptions, the strictness of parameter schemas, and the readability of error messages all directly affect call accuracy—which is the root cause of why raw REST responses perform so poorly in LLM scenarios.
Tool Discovery and State Management: What MCP Changes
With direct integration, tool definitions are hardcoded in application code. MCP moves those definitions to the server side, solving the maintenance problem through dynamic discovery. A compatible client can request a list of capabilities from the server, getting each tool's exact name, natural-language description (telling the LLM what the tool does and when to call it), and precise input schema. When the server ships a new capability, refreshing the tool list makes it visible.
But that doesn't mean zero changes. Host approval policies and compatibility testing remain necessary. The USB analogy fits here: a shared interface lets devices connect, but each device's version and capabilities aren't automatically compatible.
Another common misconception is conflating "connection" with "memory." MCP connections and conversations are two different things—a live wire doesn't mean the chatbot remembers the last sentence. Real support scenarios are multi-step workflows: the user says their keyboard is broken, the AI looks up order 987, checks the return policy, the user asks for an exchange shipped to a new address, and the AI initiates the exchange, generates a return label, and updates the ticket. The order selected, the address confirmed, and the workflow progress all need to be explicitly stored by the application itself. According to the video's discussion of protocol evolution, the request model changed in the July 2026 revision, and the initialization handshake and session concepts in older tutorials may be outdated—always verify the protocol version when implementing. Some servers implement workflow storage and notifications, but MCP itself does not provide conversation memory automatically.
JSON-RPC is the skeleton of MCP's message format, and understanding it helps clarify why dynamic discovery works. It's a lightweight remote procedure call protocol: each request contains a jsonrpc version, a unique id, a method, and params, and the server returns a result or error with the same id, so multiple requests can run concurrently on one connection without mixing up responses. MCP builds on it to define specific methods for listing tools, calling tools, reading resources, and more, and uses an initialize phase to exchange protocol versions and capability declarations. Dynamic discovery is essentially the client sending a request to list tools, and the server returning the currently available tools and their schemas; because the methods are standard, any compatible client can talk to any compatible server without hardcoding code for each tool in advance.
Security Boundaries: Authentication, Authorization, and Credential Placement
When engineers realize they're about to hand a production database, a Stripe account, and a Slack workspace to an LLM, alarms go off immediately: will the model see the database password? Will Stripe's secret key get stuffed into a prompt and sent to the model vendor's servers?
The answer depends on the design. Credentials should never appear in model prompts; they belong in protected host or server environments. The host may hold authorization tokens, while backend services keep their own database credentials. MCP does not guarantee secrets never leak—sensitive data in tool results, logs, and configuration must be constrained and tested by developers.
When the LLM decides to look up an order, all it outputs is a structured intent: "call get_order_status with order_id 12345." The server is responsible for verifying the caller's permissions, using protected credentials, and explicitly stripping unauthorized fields. These are implementation-level choices, not magic built into the protocol.
A few concrete security practices:
- Distinguish authentication from authorization: Authentication answers "who are you," authorization answers "what can you do"—the boundaries differ.
- Prevent prompt injection: Returned document content should not be granted instruction-level authority. Permissions must be enforced at three layers—host, server, and backend; writing "don't issue refunds" in a prompt is not reliable access control.
- Backstop at the database layer: A correctly configured read-only database role blocks writes at the boundary. But audit the actual grants and exposed functions—don't just trust the "read-only" label.
- Route high-risk operations to humans: For example, refunds over $50 go to a human approval queue; amount and ownership checks happen server-side, not via the model's judgment. Before state-changing operations, the host can ask the user to confirm—but even after confirmation, the server must still run its own policy checks.
prompt injection is when an attacker hides instructions in data the model will read—order notes, ticket bodies, web page content, or documents returned by tools—inducing the model to deviate from its task and carry out the attacker's intent, such as sending data to an external address or calling high-privilege tools. What distinguishes it from traditional injection is the lack of a clear data/instruction separation: to the model, tool-returned text and the system prompt may occupy the same context. The defense focus isn't prompt wording but architecture—tag and sanitize the provenance of returned content, limit tool permissions to the minimum set, prohibit untrusted content from triggering side-effect operations, introduce human confirmation before high-risk actions, and assume the model may be persuaded, so the server independently validates every call.
Breaking Down the Three Letters of MCP
M (Model): the core LLM—Claude, GPT, Gemini, or Llama—good at reasoning and generation, but isolated from your private systems.
C (Context): the information that makes the model actually useful. MCP standardizes it into three primitives a server can provide: Tools (callable functions—the model proposes a call, the app approves it, then routes execution), Resources (read-only data sources like refund policy docs, customer profiles, logs, database schemas), and Prompts (reusable instruction templates, such as the structure for support escalation scripts; selecting a template doesn't automatically execute an entire workflow).
P (Protocol): the rules and message format for the two ends to communicate, like HTTP for the web or SMTP for email. MCP messages use a JSON-RPC style—think numbered envelopes, where the request ID matches the response and the method states what to do. Communication also requires a compatible protocol version, declared capabilities, and authorization.
The Three-Layer Architecture and the Full Path of a Single Request
Every MCP deployment consists of three layers:
- Host: the application the user faces—Claude Desktop, Cursor, Windsurf, VS Code + Copilot, or your in-house support system. It handles the conversation with the LLM, manages the UI, and orchestrates the whole application.
- Client: the protocol connector inside the host, which you typically never touch directly. The host spawns one client instance per connected server, forming a one-to-one relationship; it handles JSON-RPC communication, tool discovery, forwarding the host's tool execution requests to the server, and bringing responses back.
- Server: a lightweight program sitting in front of a specific data source—a GitHub MCP server, Postgres MCP server, Stripe MCP server—translating MCP requests into native API calls, SQL queries, or local file operations.
Walk through a refund check for order 550:
- The application sends the user's question to the LLM along with the discovered tool list and schemas.
- The LLM reads the question and executes nothing—it just returns an intent: it needs to call get_order_status and check_refund_status with order_id 550.
- The host runs its policy check and sends the tool call to the server via the corresponding MCP client.
- The server uses its securely stored credentials to query Postgres and call the Stripe API.
- The server returns a curated set of relevant facts—received at warehouse, refund processed. The filtering logic is implemented by the developer.
- The host hands the results to the model, which generates a response based on the facts. When it hits errors or uncertainty, it should handle them honestly rather than guessing.
On the transport side, local servers typically use stdio, running as a subprocess on the same machine as the host with no network port required—good for accessing local files, running local git commands, querying local SQLite, or letting an AI coding assistant work with the local dev environment. Remote servers commonly use streamable HTTP, suited to production web apps, centralized servers shared by dozens of engineers or many users, and cloud SaaS integrations. The HTTP+SSE approach in older tutorials differs from the current version—don't confuse them.
stdio and streamable HTTP are the two mainstream transports today; choosing between them depends on deployment shape rather than feature differences. stdio means standard input/output: the host launches the server as a subprocess, and the two exchange JSON-RPC messages line by line over pipes—no network port is listening by nature, and credential and file access stay local, making it a good fit for personal dev tools and local data sources, at the cost of not being shareable across machines. streamable HTTP is the standard approach for remote scenarios: the client POSTs requests to a single endpoint, the server can stream responses back incrementally, and session identifiers support state across requests—easy to place behind a reverse proxy, gateway, and unified auth, and suited to centralized servers shared by many users. The older HTTP+SSE approach used two separate endpoints for requests and event push respectively, and has been superseded in the new spec; when migrating, note that both the endpoints and the handshake have changed.
Should You Write Your Own Server?
In most cases, no need. That's the whole point of an open standard: many services—Postgres, Slack, GitHub, Stripe—already have official or community-maintained MCP servers. But no list is automatically trustworthy; before use, check the publisher, maintenance status, permission scope, and protocol compatibility, and understand what you're doing before running commands from a config.
Two scenarios justify building your own: first, proprietary internal microservices or internal APIs—a homegrown legacy inventory system, an internally deployed pipeline; second, when you need to combine multiple internal business rules and API calls into curated high-level tools with guardrails and data filtering. Official SDKs are available for Python, TypeScript, Java, and Go.
Back to the original question: traditional APIs aren't going away—they remain the backbone of software system communication. MCP just adds a shared boundary between apps and servers, reducing duplicated integration work; backend integration, testing, and security still have to be done. It's not a universal adapter—the host decides what to ask, and the server implements what's allowed. To recap in one line: discover capabilities, issue tool requests, enforce permissions, return useful results; as for memory, filtering, and security, those all depend on the implementation.
Related articles

NVIDIA OpenShell: Putting the Brakes on AI Agents
NVIDIA open-sources OpenShell, an Agent security platform using an OS-level sandbox and the Sentry network chip as dual defenses to cage AI Agents. This article breaks down the technical principles, adversarial test results, and real shortcomings of the Alpha stage.

NVIDIA OpenShell Tutorial: Free Sandbox Isolation for AI Agents, Protecting API Keys
NVIDIA OpenShell free open-source sandbox tutorial: a Rust-based Agent runtime that enforces file, syscall, and network policies via an out-of-sandbox supervisor, keeping API keys hidden from Agents. Includes WSL2 installation pitfalls and hands-on policy configuration.

NVIDIA Open-Sources OpenShell: Putting a Safety Cage Around AI Agents
NVIDIA open-sources OpenShell, a runtime security sandbox for AI agents with kernel-level isolation and formal verification of policy changes to prevent permission runaway.