Skip to content
BotServBotServ
MCPCustom ToolsMCP ServerTool DevelopmentIntegration

Develop Custom MCP Tools

Build custom MCP tools for AI agents. Custom tools, MCP servers, integration and practical examples.

S

schutzgeist

5 min read
Develop Custom MCP Tools

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

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?

An interface between an agent and a system: name, description, parameters, permissions, and function. The agent invokes it via tool calling to handle specific tasks.

How do I build an MCP tool?

1. Define a tool class (name, description, parameters). 2. Implement the function. 3. Add permissions. 4. Register it with the MCP server.

What kinds of tools can I build?

Nearly anything: CRM integration, email, web scraping, databases, APIs, filesystem, system commands. Whatever your agent needs.

How do I set permissions?

In each tool: call check_permission() before execution. Define allowed actions, rate limits, and domain restrictions. See MCP Permissions.

How important is the tool description?

Very important. The model chooses which tool to use based on its description. Write precise, clear descriptions for good tool selection.

How do I secure my tools?

Use input validation, permissions, rate limiting, environment variables for secrets, and audit logs. Validate all parameters and log all actions.

Custom tools or standard tools?

Standard tools for generic tasks (files, web, databases). Custom tools for system-specific workflows (CRM, internal APIs). Use both together.

How do I test my tools?

Write unit tests for tool logic, integration tests for MCP connections, and agent tests for tool selection. Test with real-world scenarios.

Sources and Further Reading

Back to Blog
Share:

Related Posts