Building your first MCP server.

If you want Claude to interact with your APIs, databases, or internal tools, MCP is the answer. This is the practical guide we wished existed when we built our first one.

What you're building.

An MCP server is a small service that runs alongside your application. Claude connects to it using the Model Context Protocol, which tells it what tools are available, what arguments those tools take, and what they return. Claude calls your tools the same way it would use any function — with proper types, error handling, and no API keys floating in the prompt.

The result: Claude can query your database, call your APIs, read your CMS content, or trigger workflows — with you controlling exactly what it can and cannot touch.

Prerequisites.

Step 1 — Install the SDK.

npm install @modelcontextprotocol/sdk

The MCP SDK handles the transport layer, JSON-RPC protocol, and connection management. You write the tools; the SDK handles the communication.

Step 2 — Define a tool.

A tool has a name, description, input schema, and handler. Here is a minimal example that lets Claude query a user by ID:

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-api-server", version: "1.0.0" });

server.tool(
  "get_user",
  "Retrieve a user by their ID",
  { userId: z.string().describe("The user's unique identifier") },
  async ({ userId }) => {
    const user = await db.users.findOne({ id: userId });
    if (!user) return { content: [{ type: "text", text: "User not found" }] };
    return { content: [{ type: "text", text: JSON.stringify(user) }] };
  }
);

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

Step 3 — Handle errors properly.

Claude needs to understand when something went wrong. Do not throw raw errors — return structured error responses:

server.tool("get_order", "Get an order by ID", { orderId: z.string() }, async ({ orderId }) => {
  try {
    const order = await db.orders.findOne({ id: orderId });
    if (!order) {
      return { content: [{ type: "text", text: `Order ${orderId} not found` }], isError: true };
    }
    return { content: [{ type: "text", text: JSON.stringify(order) }] };
  } catch (err) {
    return { content: [{ type: "text", text: `Database error: ${err.message}` }], isError: true };
  }
});

Step 4 — Connect Claude to your server.

In Claude Desktop or any MCP-compatible client, add your server to the configuration:

{
  "mcpServers": {
    "my-api": {
      "command": "node",
      "args": ["/path/to/your/server.js"]
    }
  }
}

Claude will now have access to all tools your server defines. It will call them when they are relevant to the task at hand.

What to do next.

A basic server is easy. A production server is where the real work starts: authentication (never put API keys in the Claude config — use environment variables and validate them server-side), rate limiting (Claude can and will hammer your tools), logging (you want to know what Claude is calling and why), and tool scope (fewer, well-named tools outperform many ambiguous ones).

"The biggest mistake we see: defining too many tools with overlapping purposes. Claude gets confused about which to call. Keep tools focused, names specific, descriptions clear."

If you are building something complex — multi-system integrations, custom auth flows, tools that need to persist state — we build these for teams at 404 Technologies. See the service page.

Common questions

What is MCP and why should I build a server?

MCP (Model Context Protocol) is Anthropic's open standard for connecting Claude to external tools and data. Instead of stuffing API responses into prompts, you define a server that Claude can call like a function — with typed inputs, typed outputs, and real auth. If you want Claude to interact with your infrastructure, this is how.

How long does it take to build a basic MCP server?

A focused single-tool MCP server can be built in a few hours. A production-grade server with auth, error handling, and multiple tools typically takes 1-3 days depending on the complexity of the underlying APIs.

What language should I use to build an MCP server?

TypeScript is the most common choice because the official MCP SDK has full TypeScript support and most Claude tooling is JS-native. Python is also supported and is a good choice if your existing infrastructure is Python-first.