How to Build a Custom MCP Tool in Under 10 Min

Published: (March 9, 2026 at 05:01 PM EDT)
3 min read
Source: Dev.to

Source: Dev.to

Introduction

Your AI agent can browse the web, read files, and call APIs. But what if you need it to query your database or call an internal service?
That’s where custom MCP (Model Context Protocol) tools come in. MCP lets you expose any Python function as a tool that AI agents can discover and call. Below is a quick guide to building one in under 10 minutes.

Install FastMCP

FastMCP is the fastest way to create MCP‑compatible tools.

pip install fastmcp

Create Your Tool Module

Create a file called my_tools.py:

from fastmcp import FastMCP

mcp = FastMCP(name="MyTools")

# Sample data — swap this with your real database
USERS = {
    "u001": {"name": "Alice Chen", "role": "engineer", "team": "platform"},
    "u002": {"name": "Bob Park", "role": "designer", "team": "growth"},
    "u003": {"name": "Carol Silva", "role": "engineer", "team": "infra"},
}


@mcp.tool
def lookup_user(user_id: str) -> dict:
    """Look up an employee by their user ID. Returns name, role, and team."""
    if user_id not in USERS:
        return {"error": f"No user found with ID '{user_id}'"}
    return USERS[user_id]


@mcp.tool
def list_team_members(team: str) -> list[dict]:
    """List all employees on a given team."""
    members = [
        {"id": uid, **info}
        for uid, info in USERS.items()
        if info["team"] == team
    ]
    return members if members else [{"error": f"No members found for team '{team}'"}]


if __name__ == "__main__":
    mcp.run()

What the Code Does

  • FastMCP(name="MyTools") creates an MCP server; the name identifies your tool collection.
  • @mcp.tool decorates a function, registering it as a callable tool.
  • FastMCP automatically:
    • Uses the function name as the tool name.
    • Uses the docstring as the tool description (what the AI reads to decide when to call it).
    • Generates an input schema from type annotations.
    • Validates parameters before the function runs.
  • Return‑type annotations (-> dict, -> list[dict]) inform the schema that agents use to parse responses.

Run the Server

python my_tools.py

FastMCP launches a local MCP server using stdio transport by default. Any MCP‑compatible client (Claude Desktop, Cursor, or your own agent) can now discover and call lookup_user and list_team_members.

Connect from Claude Desktop

Add the following to your Claude Desktop config:

{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["my_tools.py"]
    }
  }
}

Extending the Example

Replace the in‑memory USERS dictionary with real data sources:

  • Database query – use sqlite3 or SQLAlchemy instead of the dict lookup.
  • API call – use httpx (or requests) to hit an internal service.
  • File search – read from local logs or configuration files.

The pattern stays the same: decorate a function, add type hints, write a clear docstring. FastMCP handles the rest.

Important Constraints

  • Function signatures cannot use *args or **kwargs; FastMCP needs explicit parameters to generate the JSON schema.
  • Every parameter must have a type annotation.

Adding More Tools

Simply add additional @mcp.tool functions to the same module. You can also:

  • Use Annotated types to provide parameter descriptions for better agent behavior.
  • Deploy the server as an HTTP endpoint:
mcp.run(transport="streamable-http")

Managed Workflows with Nebula

If you prefer not to manage infrastructure, Nebula can connect to MCP servers and let your agents call custom tools on a schedule.

Wrap‑Up

You now have a working MCP server with custom tools. From here you can:

  • Add more tools to the same server.
  • Enhance schemas with Annotated types.
  • Deploy as an HTTP service for broader accessibility.

This article is part of the AI Agent Quick Tips series. Follow for more bite‑sized agent tutorials.

0 views
Back to Blog

Related posts

Read more »