How to Build a Custom MCP Tool in Under 10 Min
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.tooldecorates 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
sqlite3orSQLAlchemyinstead of the dict lookup. - API call – use
httpx(orrequests) 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
*argsor**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
Annotatedtypes 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
Annotatedtypes. - 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.