Build Your Own MCP Server for Claude Code
A working MCP server in Node.js. Tool definitions, handlers, REST and database patterns, and the config Claude Code needs to load it.
Problem: The public MCP servers don't talk to the systems you actually use. Your internal API, the company Postgres, a custom workflow nobody else touches. For Claude Code to reach those, you have to write the server yourself.
Quick Win: Five minutes of Node.js gets Claude talking to any REST API:
// my-api-server.js - Connect Claude to your API
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const server = new Server({ name: "my-api-server", version: "1.0.0" });
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "fetch_user_data",
description: "Get user information from our internal API",
inputSchema: {
type: "object",
properties: {
userId: { type: "string", description: "User ID to fetch" },
},
required: ["userId"],
},
},
],
}));
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "fetch_user_data") {
const response = await fetch(
`https://api.yourcompany.com/users/${args.userId}`,
{
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
},
);
return {
content: [{ type: "text", text: JSON.stringify(await response.json()) }],
};
}
});
server.connect(process.stdio);Save the file as my-api-server.js. Run node my-api-server.js to test it. That's a working integration.
What an MCP Server Actually Is
An MCP server is a Node.js process that hands Claude Code a list of callable tools. It runs on its own, separate from the editor, and gives Claude a wire into anything you can reach from Node: APIs, databases, internal services.
Every server ships four things:
- Tool definitions: the functions Claude is allowed to call
- Tool handlers: the code that runs when Claude calls one
- Error handling: useful messages when the call fails
- Authentication: a safe way to reach the systems behind it
Patterns That Cover Most Cases
Talking to a REST API
Point Claude at any HTTP endpoint with a small connector:
// Generic API connector pattern
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const server = new Server({ name: "api-connector", version: "1.0.0" });
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "api_get",
description: "GET request to any endpoint",
inputSchema: {
type: "object",
properties: {
endpoint: { type: "string", description: "API endpoint path" },
params: { type: "object", description: "Query parameters" },
},
required: ["endpoint"],
},
},
],
}));
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "api_get") {
const url = new URL(`${process.env.API_BASE_URL}${args.endpoint}`);
if (args.params) {
Object.entries(args.params).forEach(([key, value]) =>
url.searchParams.append(key, value),
);
}
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
return {
content: [
{ type: "text", text: JSON.stringify(await response.json(), null, 2) },
],
};
}
});
server.connect(process.stdio);The same shape handles Stripe, Shopify, your internal dashboard, anything that speaks HTTP.
Talking to a Database
Swap the fetch call for a database client:
// Database connector for PostgreSQL, MySQL, SQLite
const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
const { Client } = require("pg");
const server = new Server({ name: "database-connector", version: "1.0.0" });
const client = new Client({ connectionString: process.env.DATABASE_URL });
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "query_database") {
const result = await client.query(request.params.arguments.query);
return {
content: [{ type: "text", text: JSON.stringify(result.rows, null, 2) }],
};
}
});Claude can now run SQL and help out with database work.
Setup and Testing
Start the project:
mkdir my-mcp-server && cd my-mcp-server
npm init -y && npm install @modelcontextprotocol/sdkThen point Claude Code at the server from its MCP config file. Where that file sits depends on how you run Claude:
- Claude Code CLI:
~/.claude.json(user-level) or.mcp.json(project-level) - Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json(macOS) or%APPDATA%\Claude\claude_desktop_config.json(Windows)
{
"mcpServers": {
"my-custom-server": {
"command": "node",
"args": ["/path/to/your/server.js"],
"env": { "API_TOKEN": "your-token" }
}
}
}Restart Claude Code after you save. The new server loads on startup.
When Things Break
Server not found: the config path or JSON is wrong. Double-check the file location. Use an absolute path for command.
Tool timeout: long-running calls hang the tool. Wrap them in your own timeout so they fail cleanly.
Authentication failed: the environment variables in the config aren't reaching the server. Verify they're spelled right and present.
Things Worth Doing Up Front
- Every external call goes in a try-catch so errors surface as text, not crashes
- Tokens live in environment variables, never in the source
- Rate-limited APIs need their own throttle so Claude can't burn through your quota
console.logoutput shows up in Claude's logs, so use it freely for debugging
Next Steps
Pick one thing to wire up and build from there:
- Start with one API: the one you touch every day
- Copy the REST pattern: the connector above is the template
- Confirm it loaded: ask Claude "What MCP tools are available?" and your new tool should appear
- Write good tool descriptions: MCP Tool Search uses them to decide when to load your server
- Read further: the MCP basics guide and the popular MCP servers list cover what to build next
A custom MCP server turns Claude Code into a client for your own stack. One server a week and the list of things Claude can reach quietly gets longer.
Stop configuring. Start building.
Social Media Automation with Claude Code
Automate social posts with Claude Code. Generate platform-specific content, build API integrations, and schedule output with code you own.
Claude Code VSCode Extension
Anthropic's VS Code extension puts Claude Code inside the editor sidebar as a Spark-icon panel, with inline diffs, plan mode, subagents, and MCP support.