Building Custom MCP Servers & Tools
Create Your Own AI-Accessible Integrations
Learn to build production-grade MCP servers from scratch. Design tools that LLMs can understand and use effectively, handle errors gracefully, implement security, and deploy servers that your entire team can use.
Why Build Custom MCP Servers?
When Pre-Built Servers Are Not Enough
The Need for Custom Servers:
Pre-built MCP servers cover popular services like GitHub, Slack, and databases. But every company has unique internal systems that no generic server covers. Your internal CRM, custom deployment pipeline, proprietary data warehouse, internal ticketing system - these need custom MCP servers.
Building a custom MCP server means your AI can interact with systems that are completely unique to your organization. It is like building a custom app for your business instead of using off-the-shelf software.
Real-World Analogy - Flipkart Seller Dashboard:
Flipkart provides standard tools for all sellers (listing, orders, payments). But a large seller like a clothing brand might build a custom integration that connects Flipkart to their internal inventory system, automatically updates stock levels, and generates custom reports. That custom integration is like a custom MCP server - purpose-built for their specific workflow.
When to Build vs. Use Pre-Built:
| Build Custom | Use Pre-Built |
|---|---|
| Internal APIs and databases | GitHub, Slack, Google Drive |
| Proprietary business logic | Standard file operations |
| Custom data transformations | Web search, standard DB queries |
| Company-specific workflows | Standard code management |
Note: Building MCP servers is straightforward with official SDKs (TypeScript, Python, Kotlin, C#). Most developers can build a simple server in under an hour.
MCP Server Architecture & SDK Setup
Understanding What Goes Into an MCP Server
Server Components:
Every MCP server has four main parts:
- Transport Handler: Manages how messages flow (stdio or HTTP). The SDK handles this for you.
- Tool Definitions: Describe what tools are available, their parameters (JSON Schema), and what they do.
- Tool Handlers: The actual code that runs when a tool is called. Your business logic lives here.
- Error Handling: How the server responds when things go wrong. Critical for good LLM experience.
Available SDKs:
- TypeScript SDK (@modelcontextprotocol/sdk): Most popular, great for Node.js developers. Full support for all MCP features.
- Python SDK (mcp): Excellent for data science and ML teams. Async-first design.
- Kotlin SDK: For JVM-based applications and Android.
- C# SDK: For .NET applications and enterprise systems.
Project Structure (TypeScript):
my-mcp-server/
package.json // Dependencies: @modelcontextprotocol/sdk
src/
index.ts // Server setup and transport
tools/
search-users.ts // Tool: search users in CRM
create-ticket.ts // Tool: create support ticket
get-metrics.ts // Tool: fetch dashboard metrics
utils/
api-client.ts // Internal API connection
validators.ts // Input validation helpersNote: Start with the TypeScript or Python SDK - they have the best documentation and community support. You can always port to other languages later.
Designing Great Tool Definitions
The Art of Writing Tools That LLMs Actually Use Well
Tool Descriptions Are Everything:
The LLM reads your tool descriptions to decide when to use each tool and what parameters to pass. A bad description means the LLM will misuse or ignore your tool. Think of it like writing documentation for a very smart but very literal intern.
Good vs Bad Tool Definitions:
Bad Definition:
Name: "get_data"
Description: "Gets data from the system"
Params: { id: string }Too vague. What data? Which system? What kind of ID?
Good Definition:
Name: "search_customers"
Description: "Search customers in the CRM by name, email,
or phone number. Returns up to 10 matching customers
with their name, email, subscription plan, and
last active date. Use this when the user asks about
a specific customer or wants to look someone up."
Params: {
query: string (required) - "Search term: name, email, or phone"
status: enum ["active", "inactive", "all"] - "Filter by status. Default: all"
}Tool Design Principles:
- One Tool, One Job: search_customers, get_customer_details, update_customer - NOT manage_customers. Granular tools are easier for the LLM to use correctly.
- Descriptive Names: Use verb_noun format. search_logs, create_ticket, get_report. The name alone should hint at what it does.
- Typed Parameters: Use JSON Schema with types, enums, required fields, and descriptions for every parameter.
- Return Format: Return structured data, not raw dumps. Include only relevant fields. The LLM will present it to the user.
- Usage Hints: Tell the LLM WHEN to use this tool. "Use this when the user asks about order status" helps the LLM pick the right tool.
Note: Spend 80% of your design time on tool descriptions and 20% on implementation. Great descriptions make the difference between a tool the LLM uses perfectly vs. one it constantly misuses.
Error Handling, Validation & Security
Making Your Server Production-Ready
Input Validation - Trust Nothing:
The LLM generates the tool inputs. It can pass wrong types, missing fields, injection attempts, or completely unexpected values. Validate every single input as if it came from an untrusted user on the internet.
- Check required fields exist
- Validate types (string, number, enum values)
- Sanitize strings to prevent injection
- Enforce reasonable limits (max length, ranges)
Error Response Best Practices:
When something goes wrong, return errors that help the LLM recover:
// BAD: Cryptic error
{ "error": "404" }
// GOOD: Helpful error that guides the LLM
{
"error": "Customer with ID cust_123 was not found.
The customer may have been deleted.
Try searching by email or name instead
using the search_customers tool."
}Security Checklist:
- Authentication: Verify the caller is authorized. For stdio, the OS process model handles this. For HTTP, implement proper auth (API keys, OAuth).
- Rate Limiting: Prevent the AI from making 1000 API calls in a loop. Set reasonable per-minute limits.
- Audit Logging: Log every tool call with timestamp, caller, parameters, and result. Essential for debugging and compliance.
- Timeouts: Set timeouts for external API calls. A hanging request should fail after a reasonable time, not block forever.
- Data Filtering: Never return sensitive data (passwords, API keys, PII) in tool results. Filter it out server-side.
Note: LLM-generated inputs are untrusted. A prompt injection attack could make the LLM pass malicious parameters to your tools. Always validate and sanitize.
Building a Complete MCP Server - Step by Step
Walkthrough: Employee Directory MCP Server
Scenario:
Your company has an internal employee directory API. You want AI to help managers look up team members, check org charts, and find experts. Let us build an MCP server for this.
Step 1: Define Your Tools
Tool 1: search_employees
Description: Search for employees by name, department,
or skill. Returns matching employees with name, title,
department, and email. Max 20 results.
Params: { query: string, department?: string }
Tool 2: get_employee_profile
Description: Get detailed profile of a specific employee
including their team, manager, skills, location,
and recent projects.
Params: { employee_id: string }
Tool 3: get_org_chart
Description: Get the organizational chart showing
direct reports for a given manager.
Params: { manager_id: string }
Tool 4: find_experts
Description: Find employees with expertise in a
specific technology or domain. Useful when user
needs help finding the right person for a task.
Params: { skill: string, location?: string }Step 2: Implement Handlers
// Pseudocode for search_employees handler
function handle_search_employees(params):
// 1. Validate inputs
if params.query is empty: return error("Query is required")
if params.query.length > 100: return error("Query too long")
// 2. Call internal API
results = call_employee_api("/search", params)
// 3. Filter sensitive data
remove salary, SSN, personal phone from results
// 4. Format response
return formatted_results with name, title, dept, emailStep 3: Configure and Test
// Add to Claude Desktop config
{
"mcpServers": {
"employee-directory": {
"command": "node",
"args": ["/path/to/your/server/dist/index.js"],
"env": {
"EMPLOYEE_API_URL": "https://internal-api.company.com",
"API_KEY": "your-secret-key"
}
}
}
}
// Test conversation:
User: "Who are the React experts in Bangalore?"
AI calls: find_experts({skill: "React", location: "Bangalore"})
AI: "Found 5 React experts in Bangalore: Priya (Staff Eng),
Rahul (Senior Eng), ..."Note: Always test your MCP server with a variety of queries, including edge cases and unusual inputs, before deploying. LLMs will find every edge case you missed.
Interview Questions
Q: When should you build a custom MCP server vs using a pre-built one?
Build custom when you have internal systems unique to your organization - internal CRMs, proprietary databases, custom deployment pipelines, internal APIs. Use pre-built for standard services like GitHub, Slack, file systems, and popular databases. The rule: if a public server exists and meets your needs, use it. If your use case involves company-specific logic or data, build custom.
Q: What makes a good MCP tool description?
A good description is specific, complete, and includes usage hints. It should explain: what the tool does, what parameters it accepts (with types and examples), what it returns, and when to use it. Avoid vague descriptions like "gets data". Instead: "Search customers by name or email. Returns up to 10 results with name, plan, and status. Use when user asks about a specific customer." The LLM uses descriptions to decide when and how to use tools.
Q: Why should LLM-generated tool inputs be treated as untrusted?
LLMs can generate unexpected, malformed, or malicious inputs. A prompt injection could trick the LLM into passing SQL injection strings, path traversal attempts, or excessively large values. Treat all inputs like untrusted user input: validate types, check required fields, sanitize strings, enforce limits. Never directly pass LLM-generated strings to database queries or shell commands.
Q: What is the recommended approach for MCP tool granularity?
Follow the one tool, one job principle. Instead of a single "manage_users" tool that does search/create/update/delete, create four separate tools: search_users, create_user, update_user, delete_user. Granular tools are easier for the LLM to understand and use correctly. The LLM can precisely pick the right tool for each step. Complex multi-purpose tools lead to confusion and misuse.
Q: How should MCP server errors be structured?
Error messages should help the LLM recover, not just report failure. Include: what went wrong, why it might have happened, and what the LLM can try instead. Example: "Customer ID cust_123 not found. The customer may have been deleted. Try using search_customers to find them by name or email." This is much better than a generic "Error 404" because the LLM can take corrective action.
Frequently Asked Questions
What is Building Custom MCP Servers & Tools?
Learn to build production-grade MCP servers from scratch. Design tools that LLMs can understand and use effectively, handle errors gracefully, implement security, and deploy servers that your entire team can use.
How does Building Custom MCP Servers & Tools work?
When Pre-Built Servers Are Not Enough The Need for Custom Servers: Pre-built MCP servers cover popular services like GitHub, Slack, and databases. But every company has unique internal systems that no generic server covers.
Related topics
Practice this on DevInterviewMaster
Read the full Building Custom MCP Servers & Tools breakdown with interactive demos, quizzes, and Hinglish notes.
800+ system-design, LLD, coding, and design-pattern topics. Unlock everything with Pro (₹499, one-time) or Ultimate (₹999, one-time) — lifetime access, no subscription.