Build Custom MCP Servers: Complete Beginner Guide 2025

The official MCP servers cover common tools - Slack, GitHub, PostgreSQL. But what if you need Claude Code to access your company's internal API, proprietary database, or custom business system?

You build a custom MCP server.

This sounds intimidating. It's not. MCP servers are simple programs that translate between Claude and your tools. With basic programming knowledge (or Claude's help), you can build custom servers in an afternoon.

## What You're Building

An MCP server is a program that:

1. Accepts requests from Claude ("Get customer data for ID 12345")
2. Translates to your system's format (SQL query, API call, etc.)
3. Fetches the data
4. Returns results to Claude in a standard format

That's it. No complex AI integration, no ML training. Just a data adapter.

## MCP Server Anatomy

Every MCP server has three components:

**1. Server Declaration:**
Tells Claude what your server can do.

```typescript
const server = {
name: "company-crm",
version: "1.0.0",
description: "Access customer data from Company CRM"
};
```

**2. Tools (Functions):**
Defines what actions Claude can take.

```typescript
const tools = [
{
name: "get_customer",
description: "Fetch customer details by ID",
input_schema: {
type: "object",
properties: {
customer_id: { type: "string" }
}
}
},
{
name: "search_customers",
description: "Search customers by name or email",
input_schema: {
type: "object",
properties: {
query: { type: "string" }
}
}
}
];
```

**3. Tool Implementations:**
Code that actually executes when Claude calls a tool.

```typescript
async function executeGetCustomer(customer_id) {
// Call your CRM API
const response = await fetch(`https://crm.company.com/api/customers/${customer_id}`, {
headers: { 'Authorization': `Bearer ${process.env.CRM_API_KEY}` }
});
const data = await response.json();
return data;
}
```

## Building Your First MCP Server

Let's build a real example: an MCP server for a company's internal wiki API.

### Step 1: Set Up Project

```bash
mkdir mcp-server-companywiki
cd mcp-server-companywiki
npm init -y
npm install @anthropic-ai/mcp-sdk
```

This creates a new MCP server project and installs the official SDK.

### Step 2: Create Server File

Create `index.ts`:

```typescript
import { MCPServer } from '@anthropic-ai/mcp-sdk';

const server = new MCPServer({
name: 'company-wiki',
version: '1.0.0',
description: 'Search and access company wiki articles'
});

// Define what actions Claude can take
server.addTool({
name: 'search_wiki',
description: 'Search wiki articles by keyword',
input_schema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query'
}
},
required: ['query']
},
handler: async ({ query }) => {
// Call your wiki's search API
const response = await fetch(
`https://wiki.company.com/api/search?q=${encodeURIComponent(query)}`,
{
headers: {
'Authorization': `Bearer ${process.env.WIKI_API_TOKEN}`
}
}
);

const results = await response.json();

// Return formatted results
return {
content: [
{
type: 'text',
text: JSON.stringify(results, null, 2)
}
]
};
}
});

server.addTool({
name: 'get_article',
description: 'Get full content of a wiki article by ID',
input_schema: {
type: 'object',
properties: {
article_id: {
type: 'string',
description: 'Wiki article ID'
}
},
required: ['article_id']
},
handler: async ({ article_id }) => {
const response = await fetch(
`https://wiki.company.com/api/articles/${article_id}`,
{
headers: {
'Authorization': `Bearer ${process.env.WIKI_API_TOKEN}`
}
}
);

const article = await response.json();

return {
content: [
{
type: 'text',
text: `# ${article.title}\n\n${article.content}`
}
]
};
}
});

// Start the server
server.start();
```

### Step 3: Build and Test

```bash
npm run build
node dist/index.js
```

The server should start without errors.

### Step 4: Configure in Claude Code

Add to your `mcp_settings.json`:

```json
{
"mcpServers": {
"company-wiki": {
"command": "node",
"args": ["/path/to/mcp-server-companywiki/dist/index.js"],
"env": {
"WIKI_API_TOKEN": "your-api-token-here"
}
}
}
}
```

### Step 5: Test in Claude Code

Restart Claude Code and ask:

"Search the company wiki for articles about expense reimbursement."

Claude calls your MCP server, which searches the wiki and returns results.

## Common Patterns

### Database Access

MCP server that queries internal database:

```typescript
import { Client } from 'pg';

server.addTool({
name: 'query_sales_data',
description: 'Run SQL query against sales database',
input_schema: {
type: 'object',
properties: {
query: { type: 'string' }
},
required: ['query']
},
handler: async ({ query }) => {
const client = new Client({
connectionString: process.env.DATABASE_URL
});

await client.connect();
const result = await client.query(query);
await client.end();

return {
content: [{
type: 'text',
text: JSON.stringify(result.rows, null, 2)
}]
};
}
});
```

### REST API Integration

MCP server for proprietary REST API:

```typescript
server.addTool({
name: 'get_inventory',
description: 'Get current inventory levels',
input_schema: {
type: 'object',
properties: {
sku: {
type: 'string',
description: 'Product SKU (optional)'
}
}
},
handler: async ({ sku }) => {
const url = sku
? `https://api.company.com/inventory/${sku}`
: 'https://api.company.com/inventory';

const response = await fetch(url, {
headers: {
'X-API-Key': process.env.INVENTORY_API_KEY
}
});

const data = await response.json();

return {
content: [{
type: 'text',
text: JSON.stringify(data, null, 2)
}]
};
}
});
```

### File System Access

MCP server for reading company files:

```typescript
import { readFile } from 'fs/promises';
import { join } from 'path';

server.addTool({
name: 'read_company_file',
description: 'Read files from company shared drive',
input_schema: {
type: 'object',
properties: {
path: {
type: 'string',
description: 'File path relative to shared drive'
}
},
required: ['path']
},
handler: async ({ path }) => {
const fullPath = join(process.env.SHARED_DRIVE_PATH, path);
const content = await readFile(fullPath, 'utf-8');

return {
content: [{
type: 'text',
text: content
}]
};
}
});
```

## Using Claude to Build MCP Servers

Here's the secret: use Claude to help build your MCP servers.

**The workflow:**

1. Find API documentation for the system you want to connect
2. Upload docs to Claude and ask:

"Build an MCP server that lets Claude Code access this API. I need tools for: searching articles, getting article details, and listing recent changes."

3. Claude generates the complete MCP server code
4. You review, test, and refine
5. Deploy and configure in Claude Code

You don't need to be a strong programmer. Claude writes the code. You just need to understand your system's API.

## Testing MCP Servers

Before adding to Claude Code, test your server:

```bash
# Start server
node dist/index.js

# In another terminal, test with MCP client
npx @anthropic-ai/mcp-client \
--server "node dist/index.js" \
--tool search_wiki \
--input '{"query": "test"}'
```

This calls your server's `search_wiki` tool directly. Verify it returns expected results.

## Security Considerations

**API credentials:**
Store in environment variables, never hardcode. Use `.env` files locally, secure secret management in production.

**Input validation:**
Validate all inputs before passing to your systems. Don't trust Claude to always send valid data.

**Rate limiting:**
If your system has rate limits, implement throttling in your MCP server to avoid overwhelming APIs.

**Read-only by default:**
Start with read-only tools. Only add write operations (creating, updating, deleting) after thorough testing.

**Audit logging:**
Log all MCP server actions. Know what Claude accessed and when.

## Publishing Your MCP Server

Once working, consider publishing for others:

1. Add README with setup instructions
2. Publish to npm: `npm publish`
3. Add to MCP community registry
4. Share on GitHub

Many companies build internal MCP servers and keep them private. Publishing is optional.

## Common Pitfalls

**Overly complex tools:**
Keep each tool focused on one action. Don't build a single "do everything" tool.

**Poor error handling:**
Handle API failures gracefully. Return meaningful error messages to Claude.

**Insufficient descriptions:**
Tool descriptions tell Claude when to use them. Be specific: "Search wiki articles by keyword" not "Search."

**Synchronous long operations:**
If operations take >30 seconds, return status and provide a separate tool to check results.

**Ignoring authentication:**
Most internal systems require auth. Don't skip it during development.

## Real-World Custom MCP Servers

**Consulting firm:**
- MCP server for project management system
- Access client information, project status, deliverables
- Used by entire consulting team in Claude Code

**E-commerce company:**
- MCP server for inventory and order management system
- Ops team queries inventory, checks order status, analyzes trends
- Reduced time to answer customer questions by 60%

**Healthcare organization:**
- MCP server for patient scheduling system (HIPAA-compliant deployment)
- Check availability, find appointment details, analyze scheduling patterns
- Improved scheduling team efficiency

## Time Investment

Building your first custom MCP server:
- Planning and API research: 1-2 hours
- Initial implementation: 2-4 hours
- Testing and refinement: 1-2 hours
- Documentation: 30 minutes

**Total: 4-8 hours for first server**

Subsequent servers are faster (1-3 hours) once you understand the pattern.

## Quick Takeaway

Custom MCP servers connect Claude Code to proprietary systems and internal tools. Building one requires: defining tools (what Claude can do), implementing handlers (code that calls your APIs), and configuring in Claude Code.

Use the official MCP SDK (@anthropic-ai/mcp-sdk) for structure and standards. Common patterns: database queries, REST API calls, file system access, and internal tool integrations.

Leverage Claude itself to generate MCP server code - upload API documentation and ask Claude to build the server. You review and refine, but don't need to write everything from scratch.

First server takes 4-8 hours. After that, adding MCP servers for new systems is routine. If your team has internal tools that would be useful in Claude Code, custom MCP servers are worth the investment.