How to Build an MCP Server

Create an MCP server from scratch that gives AI agents new capabilities. By the end of this guide, you'll have a working server with your own custom tools.

This guide covers the fundamentals. If you want a production-ready boilerplate with five tool patterns, a publishing pipeline, and a monetization guide, check out the Ship Your MCP Server starter kit.

1 Set up your project

Create a new directory and initialize it:

mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod

Open package.json and add "type": "module" so you can use ES module imports. Also add a bin field so the server can be run as a CLI command:

{
  "name": "my-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "bin": { "my-mcp": "./index.js" },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "zod": "^3.22.0"
  }
}

2 Create the server

Create index.js with the shebang line (required for npx), imports, and server setup:

#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

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

3 Add your first tool

Tools are the core of an MCP server. Each tool has a name, description, input schema (using Zod), and a handler function. Here's a simple example — a tool that checks if a website is up:

server.tool(
  "check_website",
  "Check if a website is responding and return the status code",
  { url: z.string().describe("URL to check") },
  async ({ url }) => {
    try {
      const res = await fetch(url, { method: "HEAD", signal: AbortSignal.timeout(10000) });
      return { content: [{ type: "text", text: `Status: ${res.status} ${res.statusText}` }] };
    } catch (e) {
      return { content: [{ type: "text", text: `Error: ${e.message}` }] };
    }
  }
);

The pattern is always the same: name, description, schema, handler. The description matters — it's what the AI agent reads to decide when to use your tool. Make it specific.

4 Connect the transport

Add this at the bottom of index.js to start the server using stdio (the standard transport for local MCP servers):

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

Make the file executable:

chmod +x index.js

5 Test it

Add the server to your AI tool's config and restart:

{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["/absolute/path/to/my-mcp-server/index.js"]
    }
  }
}

Ask your agent: "Check if google.com is up." It should call your check_website tool and return a real status code.

Where to go from here

This is the minimal version. A production MCP server typically includes:

Multiple tool categories — network tools, data tools, API wrappers, computation tools. Each focused on things the AI agent can't do natively.

Error handling — timeouts, rate limits, input validation, graceful failures. Your tools need to be as reliable as any API your code depends on.

Structured output — return data in formats the agent can reason about. Tables, JSON, clear labels. Not raw text dumps.

Publishing — npm publish makes it installable via npx for anyone. The MCP Registry and awesome-mcp-servers GitHub repo drive discovery.

Monetization — MCPize lets you sell access with an 85% revenue share. Freemium works: give away core tools, charge for premium ones. Industry benchmarks show 6-8% conversion from free to paid.

Ship Your MCP Server — Starter Kit

Production-ready boilerplate with five tool patterns (network, API, database, file system, scraper), a complete publishing pipeline, a monetization playbook, and our full 24-tool server as a reference. Go from idea to published, revenue-generating MCP server in an afternoon.

Get the kit — $79

Want to see a real MCP server? Our open-source server has 16 free tools you can read, fork, and learn from.

View on GitHub →