Building Custom MCP Tools
What this article covers
- How to develop custom MCP tools for AI agents.
- Setting up an MCP server and registering tools.
- Building custom tools for specific tasks.
- Practical examples for different tool types.
- Best practices for design, security, and documentation.
Introduction: Custom MCP tools explained
Custom MCP tools are specialized integrations for your agents. They connect your agent to your systems, APIs, and data sources. Not generic off-the-shelf tools, but purpose-built solutions tailored to your needs.
This article is for developers building custom MCP tools. For foundational concepts, see MCP and MCP Permissions.
Why do I need custom MCP tools?
Imagine your agent needs to query your internal CRM. No standard tool exists for it. You build a custom MCP tool: “query_crm(customer_id) → customer data”. Now your agent can access your CRM, optimized for your infrastructure.
Custom MCP tools at a glance
An MCP tool is an interface between your agent and a system. You define the tool name, description, parameters, permissions, and implementation. The agent calls the tool via tool-calling.
The key idea: specialized integration for specific tasks.
Who should read this?
- Developers building custom tools for agents.
- Integration specialists connecting systems together.
- Agent builders needing specialized capabilities.
- DevOps engineers deploying custom tools.
Key concepts
- MCP - Model Context Protocol. Use when: integrating tools.
- MCP Permissions - Access control. Use when: enforcing security.
- Tool-Calling - Invoking tools. Use when: building agents.
- Ollama - Local model server. Use when: running local agents.
MCP tool structure
class CustomMCPTool:
"""Custom MCP tool class"""
def __init__(self):
self.name = "custom_tool"
self.description = "What this tool does"
self.permissions = {
"allowed_actions": ["read", "write"],
"rate_limit": 100
}
def get_tool_definition(self):
"""Tool definition for MCP"""
return {
"name": self.name,
"description": self.description,
"parameters": {
"type": "object",
"properties": {
"param1": {
"type": "string",
"description": "Parameter 1"
},
"param2": {
"type": "integer",
"description": "Parameter 2"
}
},
"required": ["param1"]
}
}
def execute(self, param1, param2=None):
"""Execute tool"""
# Check permissions
self.check_permission("execute")
# Tool logic
result = self.do_something(param1, param2)
return result
Practical example 1: CRM tool
class CRMTool:
"""MCP tool for CRM integration"""
def __init__(self, crm_api_url, api_key):
self.api_url = crm_api_url
self.api_key = api_key
self.name = "query_crm"
self.description = "Query CRM"
def get_tool_definition(self):
return {
"name": self.name,
"description": "Query customer data from CRM",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "Customer ID"
},
"fields": {
"type": "array",
"items": {"type": "string"},
"description": "Fields to return"
}
},
"required": ["customer_id"]
}
}
def query_crm(self, customer_id, fields=None):
"""Query CRM"""
# Check permissions
self.check_permission("read")
# API call
response = requests.get(
f"{self.api_url}/customers/{customer_id}",
headers={"Authorization": f"Bearer {self.api_key}"},
params={"fields": fields}
)
return response.json()
Practical example 2: Email tool
class EmailTool:
"""MCP tool for email delivery"""
def __init__(self, smtp_config):
self.smtp = smtp_config
self.name = "send_email"
self.description = "Send email"
def get_tool_definition(self):
return {
"name": self.name,
"description": "Send email",
"parameters": {
"type": "object",
"properties": {
"to": {"type": "string", "description": "Recipient"},
"subject": {"type": "string", "description": "Subject"},
"body": {"type": "string", "description": "Message body"}
},
"required": ["to", "subject", "body"]
}
}
def send_email(self, to, subject, body):
"""Send email"""
# Check permissions
self.check_permission("send")
# Check rate limit
self.check_rate_limit()
# Send email
msg = MIMEText(body)
msg["Subject"] = subject
msg["To"] = to
with smtplib.SMTP(self.smtp["host"], self.smtp["port"]) as server:
server.send_message(msg)
return {"status": "sent", "to": to}
Practical example 3: Web scraping tool
class WebScrapingTool:
"""MCP tool for web scraping"""
def __init__(self):
self.name = "scrape_web"
self.description = "Scrape web page"
self.allowed_domains = ["example.com", "docs.example.com"]
def get_tool_definition(self):
return {
"name": self.name,
"description": "Scrape web page and extract text",
"parameters": {
"type": "object",
"properties": {
"url": {"type": "string", "description": "URL"},
"selector": {"type": "string", "description": "CSS selector (optional)"}
},
"required": ["url"]
}
}
def scrape_web(self, url, selector=None):
"""Scrape web page"""
# Domain check
domain = urlparse(url).netloc
if domain not in self.allowed_domains:
raise PermissionError(f"Domain '{domain}' not allowed")
# Scraping
response = requests.get(url)
soup = BeautifulSoup(response.text, "html.parser")
if selector:
elements = soup.select(selector)
return [e.get_text() for e in elements]
else:
return soup.get_text()
MCP Server for Custom Tools
from mcp import Server, Tool
# Create MCP server
server = Server("custom-tools")
# Register tools
server.add_tool(Tool(
name="query_crm",
description="Query CRM",
parameters=crm_tool.get_tool_definition()["parameters"],
function=crm_tool.query_crm
))
server.add_tool(Tool(
name="send_email",
description="Send email",
parameters=email_tool.get_tool_definition()["parameters"],
function=email_tool.send_email
))
# Start server
server.run(port=8000)
Security Considerations
- Permissions: Each tool should validate permissions. See MCP Permissions.
- Input Validation: Validate all parameters (type, format, range).
- Rate Limiting: Set rate limits for APIs and external calls.
- Secrets: Store API keys as environment variables, not in code.
- Audit: Log all tool calls. See Audit Logging.
Common Pitfalls
- Overly Complex Tools: Each tool should have a single, clear purpose. Avoid catch-all tools.
- Poor Descriptions: The model decides which tool to use based on the description. Write precisely.
- Missing Error Handling: Tools should handle errors gracefully and return informative messages.
- No Documentation: Document your tools well (parameters, return values, errors).
- Too Many Parameters: More than 3-4 parameters confuses the model. Use objects for complex parameters.
Further Reading
- MCP - Model Context Protocol.
- MCP Permissions - Access control.
- MCP Filesystem - Filesystem MCP.
- MCP Security - Security.
- Tool Permissions - Permissions.
Key Takeaways:
- Custom MCP tools: integration layer for your specific needs.
- Tool structure: name, description, parameters, permissions, function.
- MCP server: register tools, connect agents.
- Use for CRM, email, web scraping, internal APIs.
- Permissions, input validation, and audit logging are essential.
FAQ
What is an MCP tool?
How do I build an MCP tool?
What kinds of tools can I build?
How do I set permissions?
How important is the tool description?
How do I secure my tools?
Custom tools or standard tools?
How do I test my tools?
Sources and Further Reading
- MCP - Model Context Protocol.
- MCP Specification - Specification.


