How to Build a Claude MCP Server (From Scratch)

7 min read

How to Build a Claude MCP Server (From Scratch)

An MCP server is a small program that describes a set of tools — their names, parameters and descriptions — over the Model Context Protocol, so any MCP-capable client can discover and call them. Claude Desktop and Claude Code both speak it. Build one and Claude gains real abilities: querying your database, hitting your internal API, publishing a post. You do not write per-framework wrappers, because the protocol is the wrapper.

#Quick answer

  • What is it? A server that advertises tools, resources and prompts to an AI client over a standard protocol.
  • What does it give you? Tool use in Claude without custom glue per framework.
  • Local or remote? stdio for local processes, streamable HTTP for hosted servers.
  • Hardest part? Not the protocol — it is writing tool descriptions the model uses correctly.
  • Should you build one? Only if the tools are yours. If someone already ships one for that service, connect it.

#What MCP actually is

The Model Context Protocol is an open standard, originally from Anthropic, for connecting AI clients to tools. It defines three things a server can expose:

  • Tools — actions the model can call. schedule_post, run_query, create_ticket.
  • Resources — read-only context the model can pull in. A file, a config, a list of accounts.
  • Prompts — reusable templates the client can offer the user.

The value is not technical novelty. It is that you describe your tools once and every MCP client can use them. Before MCP you wrote a LangChain tool, then an OpenAI function schema, then something bespoke for the next framework. Now you write one server.

#Two transports, and which to pick

stdio — the client launches your server as a subprocess and talks over standard input/output. Simplest to build, runs on the user's machine, has access to their filesystem. Right for developer tooling, local scripts, anything touching local files.

Streamable HTTP — your server runs somewhere and clients connect over HTTPS with headers for auth. Right for anything multi-user or hosted, because you are not asking every user to run your code locally.

If you are exposing a SaaS API, you want HTTP. If you are exposing the user's own machine, you want stdio.

#Building one

The reference SDKs are @modelcontextprotocol/sdk for TypeScript and mcp for Python. Here is a minimal stdio server in TypeScript:

 1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
 2import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 3import { z } from "zod";
 4
 5const server = new McpServer({ name: "my-tools", version: "1.0.0" });
 6
 7server.tool(
 8  "get_order_status",
 9  "Look up the current status of a customer order by its ID. " +
10    "Returns status, carrier and estimated delivery date. " +
11    "Use when the user asks where an order is.",
12  { orderId: z.string().describe("The order ID, e.g. ORD-12345") },
13  async ({ orderId }) => {
14    const res = await fetch(`https://api.example.com/orders/${orderId}`, {
15      headers: { Authorization: `Bearer ${process.env.API_KEY}` },
16    });
17    const data = await res.json();
18    return { content: [{ type: "text", text: JSON.stringify(data) }] };
19  },
20);
21
22await server.connect(new StdioServerTransport());

Register it in Claude Desktop's claude_desktop_config.json:

 1{
 2    "mcpServers": {
 3        "my-tools": {
 4            "command": "node",
 5            "args": ["/absolute/path/to/server.js"],
 6            "env": { "API_KEY": "..." }
 7        }
 8    }
 9}

Restart the client. Ask Claude "what is the status of order ORD-12345?" and it should call the tool.

For a hosted server the shape is the same but the client config points at a URL:

 1{
 2    "mcpServers": {
 3        "schedulenchill": {
 4            "url": "https://schedulenchill.com/mcp",
 5            "headers": { "Authorization": "Bearer YOUR_API_KEY" }
 6        }
 7    }
 8}

#The part that actually decides whether it works

The protocol is easy. Tool design is where MCP servers succeed or fail, because the model reads your descriptions and nothing else.

Descriptions are prompts, not documentation. "Gets order status" tells the model almost nothing. "Look up the current status of a customer order by its ID. Returns status, carrier and estimated delivery date. Use when the user asks where an order is." tells it what the tool does, what comes back, and when to reach for it. That last clause prevents most wrong-tool calls.

Never let the model invent identifiers. If a tool takes an account ID, ship a second tool that lists valid accounts. Asked to post to "the company LinkedIn" with no list available, a model will confidently make an ID up. Our server has get_accounts for exactly this reason, and the guidance in every integration doc is to call it at the start of a run.

Make destructive actions hard to reach. A delete_everything tool with a friendly description will eventually get called. Prefer reversible operations, and where you cannot, add a confirmation parameter the model must set deliberately.

Return structured, compact data. Everything you return costs context. Dumping a 200-field JSON object when the model needed three fields degrades the whole conversation.

Give it read tools, not just write tools. An agent that can only act is worse than one that can look first. Ours has list_scheduled and get_post alongside schedule_post, so the agent can check what already exists before adding to it — which is what stops duplicate posts.

#Testing

Use the MCP Inspector (npx @modelcontextprotocol/inspector) to call tools directly without going through a model. It catches schema and transport bugs fast.

Then test with an actual model, because the failure modes are different. The inspector proves the tool works; only a model shows you that your description was ambiguous and it picks the wrong tool half the time.

#When not to build one

If the service already ships an official MCP server, connect it. A community wrapper around someone's API will drift out of date the moment that API changes, and you will be maintaining it.

Build your own when the tools are genuinely yours: your internal API, your database, your product. That is the case MCP is for.

Our own server is a worked example of the hosted pattern — ten tools, four resources, two prompts, maintained in the same codebase as the REST API so the two cannot drift apart. If you want to see the tool-design ideas above in a real server rather than a toy one, it is documented in full.

#Frequently Asked Questions

What is an MCP server? A program that exposes tools, resources and prompts to AI clients over the Model Context Protocol, so any MCP-capable client can discover and call them without custom integration code.

How do I add an MCP server to Claude? Add an entry to your client's MCP config — claude_desktop_config.json for Claude Desktop, .mcp.json for Claude Code — with either a command for a local stdio server or a url plus headers for a remote one. Restart the client to load it.

What language should I write an MCP server in? Whatever your tools already live in. Official SDKs exist for TypeScript and Python; those are the smoothest, but the protocol is transport-agnostic.

Should I use stdio or HTTP transport? stdio for local developer tooling that touches the user's machine. Streamable HTTP for anything hosted or multi-user, since you do not want every user running your process locally.

Does an MCP server work with clients other than Claude? Yes. MCP is an open standard. Any MCP-capable client can connect, and langchain-mcp-adapters converts MCP tools into LangChain tools.

How do I stop an agent calling the wrong tool? Write descriptions that say when to use the tool, not just what it does. Provide list/read tools so the model can discover valid inputs instead of guessing them, and keep destructive actions behind an explicit parameter.

How do I test an MCP server? Start with the MCP Inspector to verify tools and schemas directly, then test through a real model — ambiguous descriptions only show up when a model is choosing between tools.

#Where to go next

Zakir Hossen profile image

Zakir Hossen

Founder of Schedule & Chill. Bootstrapped entrepreneur and software engineer.

More posts from Zakir Hossen

Related Posts

by zakir

Can AI Agents Post on Social Media? Yes — Here's Exactly How

AI agents cannot reach X or LinkedIn on their own. They post through a social posting API or an MCP server that holds the OAuth tokens. Here is how both paths work, what they cost, and how to stop an agent publishing something you regret.

ai agentsmcpsocial media api+2 more
Read more
by zakir

Social Media API for AI Agents: MCP vs REST in 2026

AI agents can post to social media via REST API or via an MCP server. Here is when to use each, what the tradeoffs are, and what to look for in a social posting API built for agent use cases.

ai agentsmcpsocial media api+3 more
Read more