Model Context Protocol: MCP Server Foundations

9 min readYaseen Khatib · MERN + AI Architect

You wired Claude to your orders database with a hand-rolled tool. Then you needed the same database in your support bot, your internal CLI, and a teammate's agent — so you copy-pasted the integration four times, and now a schema change means four edits and three forgotten ones. Tool use (Lesson 4) solved how the model calls a function; it said nothing about how that capability is packaged, discovered, and reused. The Model Context Protocol is that missing layer.

Core Architectural Concepts & Trade-offs

MCP is an open protocol that standardizes how an AI client talks to an external capability — think of it as a USB-C port for tools. A server exposes three kinds of primitive: tools (functions the model can invoke), resources (read-only context the model can pull in by URI), and prompts (reusable templated workflows). A client — Claude Code, the desktop app, your own agent — connects, calls list_tools to discover what's available, and invokes by name. The integration is written once, behind a stable contract, and every client speaks the same wire format.

The decoupling is the whole point. Without MCP, every agent embeds bespoke glue for every system, and N agents × M systems is an N×M maintenance matrix. With MCP, each system ships one server and each agent speaks one protocol, collapsing N×M into N+M. Your orders server is authored, tested, and versioned in one place; a schema change is one edit, and every connected client picks it up through discovery rather than a redeploy.

Transport is a deliberate choice with security weight. stdio runs the server as a local subprocess — lowest latency, no network surface, ideal for local dev tooling and filesystem access. Streamable HTTP runs it as a remote service for shared, multi-user, or cloud deployments, at the cost of needing real auth and network hardening. Local-first by default; reach for HTTP when a capability genuinely must be shared across machines.

The trade-offs mirror tool use, amplified. Every server's tool schemas load into the context window, so connecting a dozen chatty servers silently inflates token cost on every turn — the motivation for the aggregation gateway in Lesson 13. And an MCP server is an execution and data-egress boundary: it runs with whatever credentials you hand it and can read whatever you scope it to. Treat each server as a trust boundary — least-privilege credentials, explicit allowlists, audited tools — not a convenient backdoor.

A Working MCP Server

A complete stdio server exposing one tool and one resource. Claude discovers both at connect time and calls them on demand.

orders-server.ts
// One server, authored once. Every MCP client discovers it the same way.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { z } from "zod"

const server = new McpServer({ name: "orders", version: "1.0.0" })

// TOOL — a function the model can invoke. The description drives selection.
server.tool(
  "lookup_order",
  "Fetch a single order by its ID. Use for status, totals, or line items.",
  { orderId: z.string() },
  async ({ orderId }) => {
    const order = await db.orders.find(orderId)   // parameterized, scoped creds
    return { content: [{ type: "text", text: JSON.stringify(order) }] }
  },
)

// RESOURCE — read-only context the model can pull in by URI.
server.resource("schema", "schema://orders", async (uri) => ({
  contents: [{ uri: uri.href, text: ORDERS_DDL }],
}))

// stdio: a local subprocess. No network surface, lowest latency.
await server.connect(new StdioServerTransport())

A client registers it with a few lines of config — command, args, and scoped environment. The same server now serves Claude Code, the desktop app, and any custom agent without a single line of re-integration.

.mcp.json — client config
{
  "mcpServers": {
    "orders": {
      "command": "node",
      "args": ["./dist/orders-server.js"],
      "env": { "DATABASE_URL": "postgres://localhost/shop?sslmode=require" }
    }
  }
}

One Protocol, Many Clients

MCP clientsClaude CodeDesktop appCustom agentMCPdiscover · invokeservers → systemsPostgresFilesystemHTTP API
N clients × M systems becomes N + M. Each integration is authored once behind a stable contract.

With a capability layer in place, the next lever is the model's own reasoning effort: adaptive extended thinking — latency vs. compute. Or browse the full roadmap.

Looking to architect a similar system?

Let's ship it at AI-speed.

Start a conversation →