Your First MCP Server: File Operations

Your First MCP Server: File Operations

Building MCP Server

Introduction

Now we get hands-on. In this lesson you'll build your first custom MCP server from scratch — one that performs file operations. We'll use TypeScript and the official MCP SDK, following the same patterns used by production MCP servers.

Prerequisites

  • Node.js 18 or newer
  • npm or a package manager
  • A TypeScript build toolchain (tsx, ts-node, or compiled output)

Setting Up the Project

Create a new directory and initialize it:

mkdir my-mcp-server
cd my-mcp-server
npm init -y

Install the MCP SDK:

npm install @modelcontextprotocol/sdk
npm install --save-dev typescript @types/node tsx

The Core SDK

The package @modelcontextprotocol/sdk is the official TypeScript SDK for building MCP servers. It provides the base classes, types, and transport handling you need.

{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0"
  }
}

Building a Minimal Server

A basic MCP server must implement at least these methods:

  • list_tools — advertise what tools the server provides
  • call_tool — execute a requested tool
It may also implement list_resources and list_prompts for capabilities that expose data or canned prompts.

Here's a minimal file-operations server:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";

// Only allow operations inside this directory const ALLOWED_ROOT = path.resolve(process.argv[2] || ".");

const server = new McpServer({ name: "file-ops", version: "1.0.0", });

// list_tools is handled by the SDK from your registered tools

server.tool( "list_files", "List files in the allowed root directory", {}, async () => { const entries = await fs.readdir(ALLOWED_ROOT); return { content: [{ type: "text", text: entries.join("\n") }], }; } );

server.tool( "write_file", "Write text content to a file within the allowed root", { filename: z.string(), content: z.string() }, async ({ filename, content }) => { const target = path.resolve(ALLOWED_ROOT, filename); // Safety: ensure the target stays inside ALLOWED_ROOT if (!target.startsWith(ALLOWED_ROOT)) { throw new Error("Path outside of allowed root"); } await fs.writeFile(target, content, "utf8"); return { content: [{ type: "text", text: Wrote ${filename} }] }; } );

const transport = new StdioServerTransport(); await server.connect(transport);

Why the Root Restriction Matters

Notice how every file operation is checked against ALLOWED_ROOT. This is the single most important safety pattern in MCP development. By restricting operations to a specific project directory, you prevent the AI from reading or modifying files outside its intended scope.

ALLOWED_ROOT  (/project)
    |----- read/write allowed here
    |
    x----- /etc, /home, other paths are OFF LIMITS

Running Your Server

# Run with the project directory as the allowed root
npx tsx src/server.ts /path/to/project

Once running over stdio, an MCP client like Claude Code or Cursor can connect to it and start calling list_files and write_file.

Testing Your Server

The simplest test is to connect it to any MCP-compatible client and ask it to list files. You can also write a tiny script that starts the server and sends a JSON-RPC request, but using a real client is far more practical.

Summary

  • Use @modelcontextprotocol/sdk to build MCP servers in TypeScript
  • Implement list_tools and call_tool at minimum
  • Register tools with the SDK and define their input schemas
  • Restrict file operations to a scoped root directory for safety

Next Lesson

Let's take things further and build advanced servers combining Git integration and browser automation.

Quiz - Quiz - Your First MCP Server

1. When building an MCP server in TypeScript, which package provides the core SDK?

2. A basic MCP server must implement which of these methods?

3. What is the safest approach when your MCP server needs to write files?

Connecting MCP Servers: The Basics