# AI Model Context Protocol (MCP)

Canonical: https://gaboesquivel.com/blog/2025-05-model-context-protocol

**Published:** 2025-06-08
**Summary:** What the Model Context Protocol standardizes about connecting models to tools and data, and how I structure MCP-style tools, composition, and memory in TypeScript.
**Categories:** Engineering, Artificial Intelligence, Backend, Fullstack
**Technologies:** NodeJS, TypeScript, JavaScript, AI SDK, OpenAI

Anthropic proposed the Model Context Protocol (MCP) in 2024 as a standard for connecting [AI models](https://gaboesquivel.com/ai) to external data, tools, and services, and it has become the interface most agent tooling now targets. MCP defines how a model reaches tools and data through a consistent, modular structure, and this post covers what that means in practice and how I implement it with [TypeScript](https://gaboesquivel.com/tech/typescript) and current AI tooling.

## What MCP standardizes

MCP is a specification for how AI models interact with external APIs, databases, and tools. Because it defines context, tools, and workflows explicitly, it gives agentic systems a structure beyond prompting, and it lets developers:

* Declare the tools and capabilities a model can use.
* Share memory and execution logs across calls.
* Keep model logic separate from business logic.

![MCP host with model and client connecting to servers that expose tools and resources](https://gaboesquivel.com/images/diagrams/2025-05-model-context-protocol.svg)

MCP standardizes how a host application connects to servers that expose tools, resources, and prompts.

## Why a protocol is needed now

Applications moved from one-shot prompts to multi-step reasoning systems, and LLMs gained function calling, streaming, and memory integration along the way. Tooling like LangChain.js, Mastra, and the Vercel AI SDK now supports MCP-style architecture in TypeScript, and MCP gives that complexity a standard interface that improves reuse, observability, and control.

## Implementing MCP in TypeScript

TypeScript's static types, module system, and growing set of AI libraries make it a good fit for systems that follow the MCP specification. The examples below cover three properties I want in that code: modular tools, composition at runtime, and persistent state.

### Modular tools as isolated units of capability

```tsx
// tools/search.ts
export const search = async (query: string) => {
  const res = await fetch(`/api/search?q=${query}`);
  return res.json();
};

// memory/vector.ts
export const storeEmbedding = async (embedding: number[]) => {
  await db.insert({ embedding });
};
```

### Composing modules into an agent

```tsx
import { Agent } from 'mastra';
import { search } from './tools/search';
import { storeEmbedding } from './memory/vector';

const agent = new Agent({ tools: [search], memory: { store: storeEmbedding } });
await agent.run("Find latest AI news and store key terms");
```

### Persisting state across sessions

```tsx
const memory = new VectorMemory({ db: pgvector });

await memory.save({ userId: '123', embedding: await getEmbedding("AI regulation") });
const context = await memory.query({ userId: '123' });
```

Persistent memory lets an agent carry context from one session to the next, which is what long-running plans and consistent behavior depend on.

## Where this structure pays off

The same pattern supports several kinds of product:

* Agents that remember user preferences.
* Workflow automation with persistent state.
* Multi-agent systems coordinating through shared memory.
* AI-powered dashboards whose logic can be updated module by module.

## Open design questions

| Challenge     | Considerations                                   |
| ------------- | ------------------------------------------------ |
| Interop       | Standardizing interfaces between modules         |
| State storage | Choosing the right DB for persistence            |
| Composability | Ensuring modules can be reused cleanly           |
| Type safety   | Using TypeScript to enforce contracts between components |

I use MCP to keep model input and output behind a contract, so modules stay replaceable, types stay honest, and the rest of the app never needs to know which model is on the other side.