# What is MCP? Model Context Protocol explained

> What the Model Context Protocol is, how hosts, clients and servers fit together, and how to build and test a hello-world MCP server in TypeScript.

Source: https://devaiper.com/courses/mcp-in-depth/what-is-mcp
Published: 2026-10-08
Topics: MCP, TypeScript, Tutorial

**Short answer:** MCP is an open protocol that lets AI apps discover and call tools, read data and use prompts from any server the same way. In this part you build a one-tool server and see it work in the MCP Inspector.

Every AI app that wants to touch your files, your database or your tickets used to need its own custom integration. MCP is the attempt to replace that with one shared plug. If you want the comparison with regular APIs first, read [MCP vs API: what is the difference?](https://devaiper.com/blog/mcp-vs-api).

## The three roles

- **Host**: the AI app a person uses, for example a desktop chat app, a terminal agent or an IDE.
- **Client**: a connection manager inside the host. The host creates **one client per server**.
- **Server**: a small program that offers capabilities. It can run on your machine or remotely.

A server can offer **tools** (the model calls them), **resources** (the app reads them as context) and **prompts** (the user picks them). This part uses tools only.

## Set up

You need Node.js 20 or newer. Create a folder and install the SDK, zod for the input schema, and tsx to run TypeScript directly:

```bash
mkdir repopilot && cd repopilot
npm init -y
npm pkg set type=module
npm i @modelcontextprotocol/sdk zod
npm i -D tsx typescript
```

## Your first server

One tool, `add`. It is deliberately boring so the protocol is the only new thing:

```ts
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: "repopilot", version: "0.1.0" });

server.registerTool(
  "add",
  {
    description: "Add two numbers.",
    inputSchema: { a: z.number(), b: z.number() },
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }],
  }),
);

await server.connect(new StdioServerTransport());
```

Save it as `server.ts`. Three things worth noticing:

1. The **description** is for the model. It is the only thing telling it when to use the tool.
2. The **input schema** is zod. The SDK turns it into JSON Schema and validates every call for you.
3. The **result** is a list of content blocks, not a bare value.

## Run it in the Inspector

Do not wire a server into an AI app before you have seen it work alone. The MCP Inspector is a small web UI that acts as a client:

```bash
npx @modelcontextprotocol/inspector npx tsx server.ts
```

Open the URL it prints, connect, open the **Tools** tab, run `add` with `a = 2` and `b = 3`. You should get `5`.

> **The stdio rule.** With stdio, stdout *is* the protocol. A stray `console.log` breaks the connection in confusing ways. Log with `console.error`, which goes to stderr.

## Register it in an AI app

In Claude Code, one command registers a local stdio server:

```bash
claude mcp add repopilot -- npx tsx /absolute/path/to/server.ts
```

Other apps take the same command and arguments in their MCP settings file. Ask the app to add two numbers and watch it pick your tool.

## What you learned

- MCP has three roles: host, client and server.
- A tool is a name, a description for the model, an input schema and a handler.
- stdio servers must keep stdout clean.
- Test with the Inspector before connecting an app.

Next up: real tools for RepoPilot, starting with `git_log` and `git_diff`.

## FAQ

### What does MCP stand for?

Model Context Protocol.

### What is the difference between an MCP host, client and server?

The host is the AI app the user talks to. It creates one client per server connection. The server is the program that offers tools, resources and prompts.

### Why must an MCP stdio server never print to stdout?

With the stdio transport, stdout carries the protocol messages. Anything else written there corrupts the stream. Log to stderr with console.error instead.

