Back to Blog
Development

Build a Custom MCP Server Using Node.js (Simple Guide)

December 18, 20255 min readBy Nikhil Tiwari

The Model Context Protocol (MCP) lets you expose functions (called tools) that AI clients can reliably call. Think of it like building a clean, typed API for AI.

This guide shows how to build a basic stdio-based MCP server using Node.js, step by step. This is a simple "hello world" kind of implementation—perfect for getting started with MCP.

What We're Building

A small MCP server that:

  • Runs on Node.js
  • Exposes a few tools
  • Validates inputs
  • Returns predictable JSON output

No magic. No over-engineering.

Requirements

  • Node.js 18+
  • npm
  • Basic JavaScript knowledge

Step 1: Create a New Project

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

Install dependencies:

npm install @modelcontextprotocol/sdk zod

Step 2: Create the MCP Server

Create a file:

touch index.js

Add this code:

import { McpServer } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
  name: "My MCP Server",
  version: "1.0.0",
});

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

That's your MCP server. It runs over stdio, which is perfect for local testing and IDE clients.

Note: Once your server starts, MCP clients automatically discover your tools. Learn more about how MCP Discovery & Initialization works.

Step 3: Add Your First Tool

Tools are just functions with validated inputs.

import { z } from "zod";

server.tool(
  "sayHello",
  {
    name: z.string(),
  },
  async ({ name }) => {
    return {
      message: `Hello ${name}!`,
    };
  }
);

That's it.

Important rules

  • Always validate inputs
  • Always return JSON
  • Keep tools predictable

Step 4: Add Another Tool (Example)

server.tool(
  "addNumbers",
  {
    a: z.number(),
    b: z.number(),
  },
  async ({ a, b }) => {
    return {
      result: a + b,
    };
  }
);

MCP tools should behave like pure functions.

Step 5: Handle Errors Cleanly

server.tool(
  "divide",
  {
    a: z.number(),
    b: z.number(),
  },
  async ({ a, b }) => {
    if (b === 0) {
      throw new Error("Cannot divide by zero");
    }

    return {
      result: a / b,
    };
  }
);

If something is invalid → fail fast and clearly.

Step 6: Run the Server

node index.js

Your MCP server is now running.

Step 7: Test It with an MCP Client

Before deploying or publishing, check that:

  • Tools are listed correctly
  • Inputs are validated
  • Output shape is consistent
  • Errors are clear

If it works locally, it will work in real AI clients.

Pro tip: Test your server using MCP Playground to ensure it works correctly before publishing.

Recommended Project Structure

my-mcp-server/
├── index.js
├── tools/
│   ├── math.js
│   └── greeting.js
├── package.json

(You can split tools later—start simple.)

Good Practices (Keep This Short)

✅ Do

  • Small, focused tools
  • Clear input schemas
  • Stable output format

❌ Avoid

  • Free-text responses
  • Hidden side effects
  • Changing tool behavior

What's Next?

Once your server works:

Final Thought

MCP servers are easiest when you think of them as:

Typed functions for AI, not chat responses

Keep them boring, strict, and predictable—and they'll scale beautifully.

Ready to test your MCP server? Test it now on MCP Playground →

NT

Nikhil Tiwari

15+ years of experience in product development, AI enthusiast, and passionate about building innovative solutions that bridge the gap between technology and real-world applications. Specializes in creating developer tools and platforms that make complex technologies accessible to everyone.