---
name: build-mcp-server
description: Builds a Model Context Protocol (MCP) server that gives AI agents like Claude, Cursor or Codex tools for your API, database or files - a few well-named tools with typed inputs, clear errors, stdio or HTTP transport, and a test with the MCP Inspector. Use when the user wants to build an MCP server, expose their app or API to an AI agent, or add custom tools to Claude or Cursor.
license: CC-BY-4.0
metadata:
  category: backend
  tags: "mcp,agents,api,typescript,nodejs"
  author: johnny-bravo
  version: "1.0"
  updated: "2026-10-31"
  status: published
  aliases: "mcp-builder,mcp-server,create-mcp-server"
---

# Build an MCP server

An MCP server is a small program that offers "tools" to an AI agent. The agent reads each tool's name and description, then calls it with arguments. Good servers have few tools, clear descriptions and helpful errors.

## When to use
- You want Claude, Cursor or another agent to use your API, database or files.
- You are wrapping an existing service so agents can search or act on it.
- An MCP server you built works, but agents call the wrong tool or misuse it.

## Steps
1. **List the jobs, not the endpoints.** Write the 3-8 tasks an agent should do, such as "search orders" or "refund an order". One tool per task beats one tool per endpoint.
2. **Pick a transport.** Use stdio for a tool that runs on the user's machine. Use Streamable HTTP for a hosted server many people share.
3. **Start the project** (TypeScript shown):
   ```bash
   npm init -y && npm install @modelcontextprotocol/sdk zod
   ```
   In Python, use the official `mcp` package and its `FastMCP` helper instead.
4. **Register each tool** with a verb-first name, a description that says when to use it, and typed inputs:
   ```typescript
   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: "orders", version: "1.0.0" });
   server.registerTool("search_orders", {
     title: "Search orders",
     description: "Find orders by customer email or order id. Use before refund_order.",
     inputSchema: { query: z.string().describe("Email or order id"), limit: z.number().int().max(50).default(10) },
   }, async ({ query, limit }) => {
     const rows = await findOrders(query, limit);
     return { content: [{ type: "text", text: JSON.stringify(rows) }] };
   });
   await server.connect(new StdioServerTransport());
   ```
5. **Return small, useful results.** Send the fields an agent needs, not whole records. Paginate with a `limit` and a cursor.
6. **Make errors teach.** Return `isError: true` with a message that says what to do next, like "Order not found. Call search_orders first."
7. **Test with the MCP Inspector:** `npx @modelcontextprotocol/inspector node build/index.js`. Call every tool with good and bad inputs.
8. **Connect it to an agent.** Add it to the client's MCP config (for example `.cursor/mcp.json`), then try 5 real requests and watch which tool it picks.

## Gotchas
- Vague descriptions make agents pick the wrong tool. Say when to use each tool and how it relates to the others.
- With stdio, anything printed to stdout breaks the protocol. Log to stderr.
- Huge outputs waste the agent's context. Trim and paginate.
- Mark tools that change data (`readOnlyHint: false`, `destructiveHint: true`) so clients can ask the user first.

## Safety
- Read API keys and tokens from environment variables. Never hard-code a secret or return it in a tool result.
- Treat tool inputs as untrusted. Validate them, and never pass them straight into SQL or shell commands.
- A hosted HTTP server needs authentication (OAuth or an API key header) and rate limits. Check the `Origin` header too.
- Data your tools return can contain prompt injection. Label external content as data in the result.

## Works with other stacks
Works with any language that has an MCP SDK, including TypeScript, Python, Go, C# and Java. If you use a framework such as Cloudflare Workers or FastAPI, the tool design rules are the same; only the transport setup changes.

## Done when
- Every tool works in the MCP Inspector with valid input and returns a helpful error for bad input.
- An agent picks the right tool for 5 real requests without being told its name.

## Example prompt
"Build an MCP server so Claude can search my Supabase orders table and look up a customer."
