Originally published at claudeguide.io/claude-mcp-custom-server-build
Build a Custom MCP Server for Claude Code: Complete Guide (2026)
A custom MCP (Model Context Protocol) server lets Claude Code call your own tools, APIs, and data sources as naturally as built-in commands — a minimal working server takes under 60 minutes to build. To build one: scaffold a Node.js/TypeScript project, implement the Server class from @modelcontextprotocol/sdk, define tools with JSON Schema, and register the server in Claude Code's settings. This guide walks through every step with production-ready patterns.
What Is MCP and Why Build a Custom Server?
MCP is an open standard that defines how AI models communicate with external tools. Claude Code ships with built-in MCP servers (filesystem, browser, memory), but custom servers let you:
- Connect to internal APIs (JIRA, Confluence, Salesforce)
- Query private databases
- Wrap CLI tools as Claude-callable functions
- Build domain-specific toolkits for your team
Benchmark: teams using custom MCP servers report 40–60% reduction in manual copy-paste steps per development session, based on internal surveys at early MCP adopters.
Prerequisites
- Node.js 20+ and npm/bun
- Claude Code installed and running
- Basic TypeScript knowledge
Project Scaffold
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
npx tsc --init
Update tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"strict": true
}
}
Add to package.json:
{
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
}
}
Minimal MCP Server
Create src/index.ts:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListToolsRequestSchema,
CallToolRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const server = new Server(
{ name: "my-custom-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () =
---
## Register the Server in Claude Code
Add to `~/.claude/settings.json` (or project `.claude/settings.json`):
json
{
"mcpServers": {
"my-custom-server": {
"command": "node",
"args": ["/absolute/path/to/my-mcp-server/dist/index.js"],
"env": {
"MY_API_KEY": "your-key-here"
}
}
}
}
For development with hot reload:
json
{
"mcpServers": {
"my-custom-server": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/my-mcp-server/src/index.ts"]
}
}
}
Restart Claude Code after updating settings. Verify the server is loaded with `/mcp` in Claude Code's command palette.
## Multi-Tool Server Pattern
Real servers expose multiple tools. Structure them cleanly:
typescript
// tools/definitions.ts
export const TOOLS = [
{
name: "create_jira_ticket",
description: "\"Create a JIRA issue\","
inputSchema: {
type: "object",
properties: {
summary: { type: "string" },
description: "{ type: \"string\" },"
priority: { type: "string", enum: ["Low", "Medium", "High", "Critical"] },
},
required: ["summary"],
},
},
{
name: "search_confluence",
description: "\"Search Confluence wiki pages\","
inputSchema: {
type: "object",
properties: {
query: { type: "string" },
space: { type: "string", description: "\"Space key (optional)\" },"
},
required: ["query"],
},
},
];
// tools/handlers.ts
export async function handleTool(name: string, args: unknown) {
switch (name) {
case "create_jira_ticket":
return createJiraTicket(args);
case "search_confluence":
return searchConfluence(args);
default:
throw new Error(Unknown tool: ${name});
}
}
## Error Handling Best Practices
MCP tool errors should be returned as structured content, not thrown exceptions (which crash the server):
typescript
server.setRequestHandler(CallToolRequestSchema, async (request) =
Frequently Asked Questions
What is MCP and how does it relate to Claude Code?
MCP (Model Context Protocol) is an open standard for connecting AI models to external tools and data. Claude Code has a built-in MCP client; by building a custom MCP server, you expose any function or API as a tool Claude can call during its agentic loop.
Do I need TypeScript to build an MCP server?
No. The MCP SDK is available for Python (mcp package) and TypeScript/JavaScript (@modelcontextprotocol/sdk). Python is popular for data and ML tooling; TypeScript is preferred for Node.js ecosystem integrations.
How do I pass environment variables to a custom MCP server?
In Claude Code's settings.json, add an "env" object inside the server config block. Claude Code will set those variables in the server process's environment before starting it.
Can multiple people use the same MCP server?
Yes. Deploy it as an HTTP SSE server (using SSEServerTransport) behind your internal network or VPN. Each Claude Code user points to the URL in their settings. Authentication is handled via bearer tokens in request headers.
How many tools can one MCP server expose?
There is no hard limit, but keep it practical. Claude includes all tool definitions in its context window, so 20–30 focused tools is a good upper bound. Group related tools into separate servers if the list grows large.
How do I debug a broken MCP server?
Run npx @modelcontextprotocol/inspector node dist/index.js to open the MCP inspector UI. It shows the exact JSON sent and received. Also check Claude Code's MCP logs: open the command palette → /mcp → select your server → view logs.
Is there a way to share MCP servers with my team?
Yes. Host the server as an HTTP service and share the URL, or commit the server repo and have teammates run it locally. For Claude Code, each user registers the server in their own settings.json. A shared project .claude/settings.json can automate this.
Related Guides
- Claude Code Complete Guide — Full Claude Code reference including MCP setup
- Claude Agent SDK Guide — Agentic patterns and tool definitions
- Claude API Cost Monitoring Guide — Monitor spend from MCP tool calls
Top comments (0)