Building Your First MCP Server: From Zero to Claude Integration
Stop reading docs and start building. Here's a working MCP server in under an hour.
·12 min read
What We're Building
A minimal MCP server that exposes:
- Resources: A list of tasks from a JSON file
- Tools: Ability to add new tasks
By the end, you'll ask Claude "What tasks do I have?" and it will read your data.
Prerequisites
# Node.js 18+ required
node --version # v18.0.0 or higher
# Create project
mkdir my-mcp-server && cd my-mcp-server
npm init -yStep 1: Install Dependencies
npm install @modelcontextprotocol/sdk zod@modelcontextprotocol/sdk is the official MCP SDK. zod helps with schema validation.
Step 2: Create the Data Source
// tasks.json
{
"tasks": [
{ "id": 1, "title": "Review Q1 metrics", "status": "pending" },
{ "id": 2, "title": "Prepare board deck", "status": "in_progress" },
{ "id": 3, "title": "Schedule team offsite", "status": "completed" }
]
}Step 3: Build the Server
// server.js
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { readFileSync, writeFileSync } from "fs";
import { z } from "zod";
// Load tasks
const loadTasks = () => {
const data = readFileSync("./tasks.json", "utf-8");
return JSON.parse(data);
};
const saveTasks = (data) => {
writeFileSync("./tasks.json", JSON.stringify(data, null, 2));
};
// Create server
const server = new Server(
{ name: "task-manager", version: "1.0.0" },
{ capabilities: { resources: {}, tools: {} } }
);
// List available resources
server.setRequestHandler("resources/list", async () => ({
resources: [{
uri: "tasks://all",
name: "All Tasks",
description: "Complete list of tasks with status",
mimeType: "application/json"
}]
}));
// Read resource content
server.setRequestHandler("resources/read", async (request) => {
const uri = request.params.uri;
if (uri === "tasks://all") {
const data = loadTasks();
return {
contents: [{
uri,
mimeType: "application/json",
text: JSON.stringify(data.tasks, null, 2)
}]
};
}
throw new Error(`Unknown resource: ${uri}`);
});
// List available tools
server.setRequestHandler("tools/list", async () => ({
tools: [{
name: "add_task",
description: "Add a new task to the task list",
inputSchema: {
type: "object",
properties: {
title: { type: "string", description: "Task title" },
status: {
type: "string",
enum: ["pending", "in_progress", "completed"],
default: "pending"
}
},
required: ["title"]
}
}]
}));
// Execute tool
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "add_task") {
const data = loadTasks();
const newTask = {
id: Math.max(...data.tasks.map(t => t.id)) + 1,
title: args.title,
status: args.status || "pending"
};
data.tasks.push(newTask);
saveTasks(data);
return {
content: [{
type: "text",
text: `Added task: "${newTask.title}" with ID ${newTask.id}`
}]
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Task Manager MCP Server running");Step 4: Configure package.json
{
"name": "my-mcp-server",
"version": "1.0.0",
"type": "module",
"main": "server.js",
"scripts": {
"start": "node server.js"
}
}Step 5: Connect to Claude Desktop
Edit Claude Desktop's config file:
// macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
// Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"task-manager": {
"command": "node",
"args": ["/full/path/to/my-mcp-server/server.js"]
}
}
}Restart Claude Desktop.
Step 6: Test It
Open Claude Desktop and try:
- "What tasks do I have?"
- "Add a task: Review MCP documentation"
- "Show me my pending tasks"
Claude will use your MCP server to read and modify your tasks.
What Just Happened
User: "What tasks do I have?"
↓
Claude: Recognizes it needs task data
↓
Claude: Calls MCP resources/list
↓
Your Server: Returns available resources
↓
Claude: Calls MCP resources/read for "tasks://all"
↓
Your Server: Returns task JSON
↓
Claude: Formats response for user
↓
User: Sees formatted task listCommon Issues
Server Not Appearing
- Check the path in config is absolute
- Restart Claude Desktop completely
- Check server logs in Console (macOS) or Event Viewer (Windows)
Connection Errors
- Ensure
type: "module"is in package.json - Verify Node.js version is 18+
- Check file permissions on tasks.json
Next Steps
- Add more resources: Connect to a real database
- Add more tools: Update, delete, filter tasks
- Add authentication: Protect sensitive data
- Deploy it: Run as a service for team access
You just built a working MCP server. Your AI can now access your data. What will you connect next?
Skip the Setup
Xtended is MCP-ready out of the box. Connect your knowledge base to Claude in minutes, not hours.
Get Started Free