โœฆTalki Academy
Tutorial18 min read

MCP for Beginners: Build Your First Server in 20 Minutes

Practical tutorial for developers discovering MCP. Build a filesystem MCP server that lets Claude read and write files in a secure folder. Installation, complete code, Claude Desktop connection, testing, troubleshooting. No prior MCP knowledge required.

By Talki AcademyยทPublished April 3, 2026

What is MCP in Plain Language?

MCP (Model Context Protocol) is an open protocol that allows Claude (and other AIs) to access tools and data you define. Imagine Claude being able to read your files, query your database, or send Slack messages โ€” all securely and under your control.

Without MCP, Claude is limited to its context: it can only respond based on what you type in the conversation. With MCP, you give it "superpowers": read a config file, execute a SQL query, search technical documentation.

๐ŸŽฏ What you'll build in this tutorial

An MCP server that exposes a /workspace folder to Claude. Claude will be able to list files, read their content, and create new files โ€” only within this secure folder. Perfect for generating code, writing documentation, or analyzing text files.

How it works: 3 key concepts

MCP relies on three roles:

  • Host โ€” the application that orchestrates everything (e.g., Claude Desktop)
  • Client โ€” the component in the Host that connects to an MCP server
  • Server โ€” your program that exposes capabilities (read files, query database, etc.)

Communication happens via JSON-RPC 2.0 over stdio (local process) or HTTP (remote server). The LLM never directly calls your APIs โ€” the MCP server acts as a secure intermediary.

Prerequisites

Before starting, make sure you have:

  • Node.js 18+ installed (node --version should show v18.0.0 or higher)
  • A code editor (VS Code, Cursor, or any editor)
  • Claude Desktop installed (download here)
  • Basic JavaScript/TypeScript knowledge (know what a function, variable, async/await are)
# Check that Node.js is installed node --version # โ†’ v20.11.0 (or higher) npm --version # โ†’ 10.2.4 (or higher) # If Node is not installed, download from https://nodejs.org/

Installing the MCP SDK

Step 1: Create the project

# Create a new folder for your MCP server mkdir mcp-filesystem-server cd mcp-filesystem-server # Initialize a Node.js project npm init -y # Install the official MCP SDK from Anthropic npm install @modelcontextprotocol/sdk # Install development dependencies npm install --save-dev typescript @types/node tsx # Verify installation npm list @modelcontextprotocol/sdk # โ†’ @modelcontextprotocol/sdk@1.0.4

Step 2: Configure TypeScript

# Create tsconfig.json cat > tsconfig.json << 'EOF' { "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules"] } EOF # Create the source folder mkdir src # Create the workspace folder (where Claude can read/write) mkdir workspace

Step 3: Add npm scripts

Open package.json and add these scripts:

{ "name": "mcp-filesystem-server", "version": "1.0.0", "type": "module", "scripts": { "build": "tsc", "dev": "tsx src/index.ts", "start": "node dist/index.js" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.4" }, "devDependencies": { "typescript": "^5.3.3", "@types/node": "^20.11.0", "tsx": "^4.7.0" } }

Create the Filesystem MCP Server

Create the file src/index.ts with the following code. Each section is commented to explain what's happening.

// src/index.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import * as fs from "fs/promises"; import * as path from "path"; // Secure folder where Claude can read/write const WORKSPACE_DIR = path.resolve(process.cwd(), "workspace"); // Create the MCP server const server = new Server( { name: "filesystem-server", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // === TOOL 1: List files in the workspace === server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "list_files", description: "List all files in the workspace folder", inputSchema: { type: "object", properties: {}, }, }, { name: "read_file", description: "Read the content of a file in the workspace", inputSchema: { type: "object", properties: { filename: { type: "string", description: "Name of the file to read (e.g., notes.txt)", }, }, required: ["filename"], }, }, { name: "write_file", description: "Write content to a file in the workspace", inputSchema: { type: "object", properties: { filename: { type: "string", description: "Name of the file to create or overwrite", }, content: { type: "string", description: "Content to write to the file", }, }, required: ["filename", "content"], }, }, ], }; }); // === Handle tool calls === server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "list_files": { const files = await fs.readdir(WORKSPACE_DIR); return { content: [ { type: "text", text: files.length > 0 ? `Files in workspace: ${files.map((f) => `- ${f}`).join("\n")}` : "The workspace folder is empty.", }, ], }; } case "read_file": { const filename = args.filename as string; const filepath = path.join(WORKSPACE_DIR, filename); // Security: prevent access to files outside workspace if (!filepath.startsWith(WORKSPACE_DIR)) { throw new Error("Access denied outside workspace"); } const content = await fs.readFile(filepath, "utf-8"); return { content: [ { type: "text", text: `Content of ${filename}: ${content}`, }, ], }; } case "write_file": { const filename = args.filename as string; const content = args.content as string; const filepath = path.join(WORKSPACE_DIR, filename); // Security: prevent access to files outside workspace if (!filepath.startsWith(WORKSPACE_DIR)) { throw new Error("Access denied outside workspace"); } await fs.writeFile(filepath, content, "utf-8"); return { content: [ { type: "text", text: `โœ… File ${filename} created successfully (${content.length} characters)`, }, ], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error: any) { return { content: [ { type: "text", text: `โŒ Error: ${error.message}`, }, ], isError: true, }; } }); // === Start the server === async function main() { // Ensure workspace folder exists await fs.mkdir(WORKSPACE_DIR, { recursive: true }); const transport = new StdioServerTransport(); await server.connect(transport); console.error("Filesystem MCP server started โœ…"); console.error(`Workspace: ${WORKSPACE_DIR}`); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

Build the server

# Compile TypeScript to JavaScript npm run build # Verify that dist/index.js was created ls dist/ # โ†’ index.js # Test in dev mode (without compiling) npm run dev # โ†’ Filesystem MCP server started โœ… # โ†’ Workspace: /Users/you/mcp-filesystem-server/workspace # Ctrl+C to stop

Connect the Server to Claude Desktop

Now that your MCP server is ready, you need to declare it in Claude Desktop's configuration.

Locate the config file

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json

Add your MCP server

{ "mcpServers": { "filesystem": { "command": "node", "args": [ "/absolute/path/to/mcp-filesystem-server/dist/index.js" ] } } }
โš ๏ธ Important: Use an ABSOLUTE path

Replace /absolute/path/to/mcp-filesystem-server/dist/index.js with the full path on your machine. Example macOS: /Users/alice/mcp-filesystem-server/dist/index.js

Restart Claude Desktop

  • macOS: Cmd+Q to quit completely, then relaunch
  • Windows/Linux: Close all windows and relaunch

After restarting, open Claude Desktop. You should see a tool icon (๐Ÿ”จ) at the bottom of the conversation window, indicating that MCP servers are connected.

Testing the Server with Real Conversations

Test 1: List files

In Claude Desktop, type:

You:
Can you list the files in my workspace?

Claude should automatically call the list_files tool and respond:

Claude:
The workspace folder is empty.

Test 2: Create a file

You:
Create a file hello.txt containing "Hello from MCP!"

Claude calls write_file and responds:

Claude:
โœ… File hello.txt created successfully (16 characters)

Test 3: Read the created file

You:
Read the content of hello.txt
Claude:
Content of hello.txt:

Hello from MCP!

Test 4: Real use case โ€” Generate code

You:
Write a file fibonacci.py containing a recursive function to calculate the nth Fibonacci number, with unit tests.

Claude will generate the Python code and write it to workspace/fibonacci.py. You can then verify the file on your machine:

# Check that the file exists ls workspace/ # โ†’ fibonacci.py hello.txt # Read the content cat workspace/fibonacci.py # Expected output: # def fibonacci(n): # if n <= 1: # return n # return fibonacci(n-1) + fibonacci(n-2) # # def test_fibonacci(): # assert fibonacci(0) == 0 # assert fibonacci(1) == 1 # assert fibonacci(10) == 55 # print("โœ… All tests pass") # # if __name__ == "__main__": # test_fibonacci()

Troubleshooting: Common Issues

SymptomCauseSolution
Server doesn't appear in Claude DesktopRelative path in config or Claude not restartedUse an absolute path. Quit Claude completely (Cmd+Q) and relaunch
Error "Cannot find module"The dist/index.js file doesn't existRun npm run build to compile TypeScript
Claude says "I don't have access to that tool"Server crashed or didn't startCheck logs: View โ†’ Developer โ†’ Developer Console in Claude Desktop
Error "Access denied outside workspace"Attempted read/write outside secure folderNormal โ€” security working as intended. Claude can only access workspace folder
Server starts then stops immediatelyError in code or missing dependenciesRun npm run dev in a terminal to see the exact error

Debug with logs

// Add debug logs in src/index.ts server.setRequestHandler(CallToolRequestSchema, async (request) => { console.error(`[DEBUG] Tool called: ${request.params.name}`); console.error(`[DEBUG] Args: ${JSON.stringify(request.params.arguments)}`); // ... your existing code }); # Rebuild and test npm run build # Logs will appear in Claude Desktop's Developer Console # (View โ†’ Developer โ†’ Developer Console)

Next Steps

Congratulations! You've created your first MCP server. Here's how to go further:

1. Add more tools

Examples of useful tools to add:

  • search_file โ€” search for a keyword in all workspace files
  • delete_file โ€” delete a file
  • create_folder โ€” create subfolders in the workspace
  • run_command โ€” execute shell commands (caution: security risk!)

2. Connect a database

Create an MCP server that lets Claude query PostgreSQL, MongoDB, or SQLite. Example: a query_db tool that executes SQL queries and returns results.

3. Expose an external API

Create an MCP server that calls third-party APIs (GitHub, Slack, Notion). Example: a create_github_issue tool that opens a ticket on GitHub directly from Claude.

4. Deploy to production

To share your MCP server with your team, deploy it on a remote server and configure HTTP/SSE transport instead of stdio. Consult the official MCP documentation for details.

5. Explore the MCP registry

There are over 2,000 open-source MCP servers ready to use (GitHub, Slack, PostgreSQL, Brave Search, etc.). Check the official MCP registry for inspiration.

6. Get in-depth training

This tutorial covers the basics (Resources, Tools). To master MCP in production (Prompts, Sampling, error handling, security), check our Claude API for Developers training. We cover the Claude API thoroughly, then introduce MCP with hands-on server creation exercises. 3-day training, available worldwide.

Frequently Asked Questions

Can I create an MCP server without TypeScript experience?

Yes, with basic JavaScript knowledge. This tutorial is designed for beginners and explains every line of code. If you know what a function and a variable are, you can follow along. Python is also supported via the official SDK, but TypeScript is recommended for getting started (better documentation, more examples).

Do I need to pay to use Claude Desktop with my MCP server?

No for local testing. Claude Desktop is free for personal use. You can use your MCP server with the free Claude plan (limited to ~45 messages/day). For intensive or professional use, Claude Pro (USD 20/month) or Claude API are required. The MCP server itself costs nothing.

Can my MCP server work with tools other than Claude Desktop?

Yes! MCP is an open standard. Your server works with all MCP clients: VS Code (MCP extension), Cursor, Windsurf, JetBrains IDEs, Zed, and many no-code tools. Once created, your server is reusable everywhere.

What if my MCP server doesn't show up in Claude Desktop?

Check 3 things: (1) The path in claude_desktop_config.json is absolute, not relative. (2) The index.js file exists in dist/ (run `npm run build`). (3) Fully restart Claude Desktop (Cmd+Q on Mac, not just close the window). See the Troubleshooting section for more diagnostics.

How long to master MCP after this tutorial?

This tutorial: 20-30 minutes to create your first server. Mastering MCP at production level: 2-3 days of practice. This guide covers fundamentals (Resources, Tools), but not advanced concepts (Prompts, Sampling, SSE remote servers). To go further, see our Claude API training which includes a complete MCP module.

Master MCP and Claude API in 3 Days

Hands-on training for developers. Available worldwide.

View Claude API TrainingContact Us