Back to the blog

Building your first MCP server, step by step

Code6 min read · 27 September 2026

The GetPack team

MCP stands for Model Context Protocol: a standard way to give a model like Claude direct access to a service, a database, or any tool you’ve written yourself. If you’ve ever connected an existing MCP connector, you already know what it does from the user’s side. This time, we’re switching sides: writing your own MCP server, minimal but working, using only the official documentation and SDKs.

MCP, concretely: who talks to whom

The official architecture describes three roles. The MCP host is the application that uses the AI, for example Claude Code or Claude Desktop. The MCP client is the component the host creates to maintain a dedicated connection to a given server. The MCP server, the one you’re about to write, is the program that provides context or actions to whichever clients connect to it. A local server (the most common case when you’re starting out) communicates over the process’s standard input/output (the “stdio” transport); a remote server, hosted somewhere, communicates over HTTP instead.

The three building blocks your server can expose

The official documentation defines three core primitives on the server side:

  • Tools: executable functions the AI application can invoke to take action (call an API, query a database, manipulate a file).
  • Resources: data sources that provide context (a file’s contents, a database record, an API response).
  • Prompts: reusable prompt templates that structure an interaction with the model (a system prompt, few-shot examples).

For a first server, one single tool is plenty: it’s the simplest building block to understand and the most immediately useful.

Writing a minimal server in TypeScript

Create a project folder, then install the official TypeScript SDK along with the validation library it uses to describe a tool’s input:

npm install @modelcontextprotocol/server
npm install zod

Create a file (say, index.ts) with this content, taken from the SDK’s official repository:

import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const server = new McpServer({ name: 'greeting-server', version: '1.0.0' });

server.registerTool(
    'greet',
    {
        description: 'Greet someone by name',
        inputSchema: z.object({ name: z.string() })
    },
    async ({ name }) => ({
        content: [{ type: 'text', text: `Hello, ${name}!` }]
    })
);

async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
}

main();

This code does three things: it creates a server named greeting-server, it registers a greet tool whose expected input is validated by a Zod schema ({ name: string }), then it connects the server to the stdio transport, the one MCP hosts use to talk to a server running locally as a plain process.

Notice the description field on the tool itself. It plays the same role as a skill’s description: the connected AI application reads it to decide whether this particular tool is the right one to call for a given request, especially once your server exposes more than one. A vague description here causes the same problem as a vague skill description — the tool either never gets used, or gets used for the wrong request — so it’s worth being just as specific here as anywhere else.

If you’d rather use Python

The official Python SDK gets you to the same place with a decorator-based syntax. Install it with:

uv add "mcp[cli]"

(or pip install "mcp[cli]" if you’re not using uv). The minimal server fits in a few lines:

from mcp.server import MCPServer

mcp = MCPServer("Demo")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b

No schema to write by hand: Python’s type hints (a: int, b: int) are enough for the SDK to generate the tool’s description automatically.

Testing it with the MCP Inspector

Before connecting anything to Claude, test your server with the MCP Inspector, the official reference tool built for exactly this. It runs through npx, no install needed (it requires Node 22.19.0 or newer):

npx @modelcontextprotocol/inspector node path/to/index.js

This opens a local web interface where you can list the tools your server exposes, call greet with a name as a parameter, and see the response come straight back. It’s the step that saves you from discovering a schema mistake only after connecting the server to Claude. If you’d rather stay in the terminal, the Inspector also offers a CLI mode (--cli) and a TUI mode (--tui) that do the same checks without a browser.

For a Python server launched with mcp dev, the Inspector opens automatically:

uv run mcp dev server.py

Connecting it to Claude

Once the server checks out, you can register it in Claude Code with claude mcp add, keeping Claude Code’s own options (before the --) clearly separated from the command that launches your server (after the --):

claude mcp add --transport stdio greeting-server -- node path/to/index.js

Claude Code registers the server right away. You can confirm it shows up with claude mcp list, and remove it with claude mcp remove greeting-server if needed. From that point on, in a conversation, Claude can discover your greet tool and call it exactly like any other MCP tool.

Going further: a resource, and serving more than one client

A tool is only one of the three possible building blocks. The Python SDK exposes a resource in much the same way as a tool, with its own dedicated decorator:

@mcp.resource("config://app-name")
def get_app_name() -> str:
    """Application name, exposed read-only as a resource."""
    return "greeting-server"

The real difference between the two: a tool is meant to act (it can have side effects), while a resource is meant to be read, a bit like a GET request that changes nothing. If your server only needs to expose data that already exists, a resource is often the more honest thing to expose, rather than a tool.

Second thing worth knowing before you go further: a server running over the stdio transport, like the one in this article, only serves one client at a time — perfectly fine for local development or strictly personal use. The day you want the same server reachable by several people at once, the official documentation recommends switching to the Streamable HTTP transport, built exactly for that case: the server then runs continuously somewhere (often hosted), and each client opens its own HTTP connection to it, with no need for a dedicated local process per user.

If this MCP server is part of graded coursework (a lab, an end-of-module project), check your school’s AI usage rules first: getting help from Claude to write or debug your code isn’t necessarily treated the same way from one course to another.

Key takeaways

  • MCP connects a host (like Claude Code), a client, and a server you write yourself.
  • Three possible primitives on the server side: tools (actions), resources (data), prompts (interaction templates). One tool is enough to start.
  • Official TypeScript SDK: npm install @modelcontextprotocol/server, an McpServer instance, a registerTool call, a stdio transport.
  • Official Python SDK: uv add "mcp[cli]", an MCPServer instance, a tool declared with @mcp.tool().
  • Always test before connecting anything: npx @modelcontextprotocol/inspector node your-server.js.
  • Connecting to Claude Code: claude mcp add --transport stdio <name> -- <command>, then claude mcp list to confirm.

Sources

  1. Architecture overview — Model Context Protocol · accessed 27 September 2026
  2. MCP Inspector — Model Context Protocol · accessed 27 September 2026
  3. typescript-sdk (README) — GitHub modelcontextprotocol · accessed 27 September 2026
  4. MCP Python SDK — Get started · accessed 27 September 2026
  5. python-sdk (README) — GitHub modelcontextprotocol · accessed 27 September 2026
  6. Connect Claude Code to tools via MCP — Claude Code Docs · accessed 27 September 2026

Read the next article

Code27 September 2026

Deploying your first site for free: Vercel, Netlify, Cloudflare Pages, or GitHub Pages

Explainer27 September 2026

AI detectors: are Turnitin, GPTZero and Compilatio reliable?

Code27 September 2026

Writing your own Claude skill: structure, SKILL.md, and a description that triggers

Career27 September 2026

France's national student-entrepreneur status (SNEE) and the PEPITE network, explained

Analysis27 September 2026

France in the AI race: Mistral, energy and talent

AI news27 September 2026

French Tech and AI: the French startups to know in 2026

Code27 September 2026

Git without fear: commit, branch, remote explained, then the commands that save you

Explainer27 September 2026

Chinese AI: DeepSeek, Qwen, Kimi… why Europe is wary

Health27 September 2026

AI in medical school: study for PASS, LAS and the EDN safely

Tools27 September 2026

Free AI for students: every offer and discount (2026)

Career27 September 2026

AI on an internship or apprenticeship: what’s allowed, what isn’t

Code27 September 2026

From IDE to ADE: coding with AI agents in 2026

Code27 September 2026

Reading an error without panicking: the anatomy of a stack trace (Python and JavaScript)

Tools27 September 2026

Best AI for students in 2026: the honest comparison

Tools27 September 2026

New AI models in 2026: which one should you study with?

Tools27 September 2026

French AI tools you’ve never heard of: Noota, Moshi, Vibe…

Analysis27 September 2026

Why AI is so expensive (and American AI even more so)

Code27 September 2026

How to prompt an AI coding tool well: the method that changes everything

Code27 September 2026

Securing a vibe-coded app: 7 mistakes to fix before you publish

Code27 September 2026

Slopsquatting: when AI recommends packages that don't exist

Weekly brief27 September 2026

AI news roundup: the week of September 21–27, 2026

Code27 September 2026

Vibe coding: what it actually means (and how not to mess it up)

AI news27 September 2026

Why Yann LeCun wants AMI: AI beyond LLMs

Tutorial26 September 2026

Connect an MCP connector to Claude without writing a line of code

Degrees26 September 2026

Choosing your program with real MonMaster and InserSup data

Method26 September 2026

APA 7, ISO 690, Vancouver: how to cite your sources properly

Explainer26 September 2026

ChatGPT's 'hidden codes' on TikTok: fact vs. fiction

Health26 September 2026

Medicine: revise for the EDN with France's public drug database

Career26 September 2026

Internship pay and apprentice wages in 2026: the rules

Explainer26 September 2026

AI and academic integrity: what universities actually say

Tutorial26 September 2026

Installing a skill in Claude in 2 minutes

Thesis26 September 2026

Thesis: building your research question and outline with AI

Method26 September 2026

Building your exam study schedule with AI, the right way

Method26 September 2026

Revising with AI, honestly: active recall, Feynman, quizzes

Career26 September 2026

Choosing your apprenticeship with real employment data

Code27 September 2026

Learning to code in the age of AI: what you still need to know how to do yourself

Code27 September 2026

The one-page spec to write before you prompt an AI to code

Thesis27 September 2026

How to cite ChatGPT or Claude in a thesis: APA, MLA, ISO 690

Code27 September 2026

Claude Code for beginners: install, first launch, CLAUDE.md

AI news27 September 2026

Claude Opus 5.5: what really changes (and how to use it to study)

Analysis27 September 2026

The AI race: why some people are scared and others aren't

Join GetPack

Already have an account? Sign in