PTENES
MODULE 3.4

🔢 Calculator Server

Build a server with 4 math operations—your first multi-tool server with input validation.

6
Topics
35
Minutes
Intermediate
Level
Practical
Type
1

🎯 Planning the Tools

Before writing any code, let's plan our calculator server. The idea is to create four basic math tools: add, subtract, multiply e divide. Each will receive two floating-point numbers (float) as parameters and return the result of the operation. This will be your first server with multiple tools—a significant step toward building more complex MCP servers.

💎 Core Concept

Our server will have 4 tools, all following the same signature pattern:

add(a: float, b: float) → float

subtract(a: float, b: float) → float

multiply(a: float, b: float) → float

divide(a: float, b: float) → float # with validation that b != 0

All receive two floats and return a float. The exception is division, which needs to check whether the divisor is zero before performing the operation. This consistent pattern makes implementation and use by the AI model easier.

💡 Practical Tip

Planning the tools before coding is a essential best practice. Define the name, parameters (with types), and return value for each tool. This ensures consistency, makes the automatic documentation generated by FastMCP easier, and helps the AI model understand when and how to use each tool. Think of tools as an API—the more predictable, the better.

2

➕ Addition and Subtraction

Let's start with the simplest operations: addition and subtraction. These are straightforward tools that don't need special validation. They serve as a foundation for understanding the pattern that will be repeated in the other operations. With FastMCP, each tool is just a decorated Python function—it's that simple.

💎 Core Concept

# server_calculadora.py

from mcp.server.fastmcp import FastMCP


mcp = FastMCP("Calculator")


@mcp.tool()

def add(a: float, b: float) -> float:

"""Adds two numbers."""

return a + b


@mcp.tool()

def subtract(a: float, b: float) -> float:

"""Subtracts b from a."""

return a - b

Notice how the type hints (a: float, b: float) are essential. FastMCP uses these annotations to automatically generate the JSON schema that the MCP client sends to the model. Without type hints, the model doesn't know what kind of data to send.

💡 Practical Tip

Always write docstrings in your tools. The docstring becomes the tool description in the MCP schema—it’s what the model reads to decide which tool to use. A clear docstring like "Adds two numbers" is much better than having no docstring at all.

📊 Data and Research

FastMCP automatically converts Python type hints into JSON Schema. A parameter float becomes {"type": "number"} in the schema. A str becomes {"type": "string"}. This automatic conversion eliminates the need to write schemas manually—just annotate the Python functions correctly and FastMCP takes care of the rest.

3

✖️ Multiplication

The multiplication tool follows exactly the same pattern as the previous tools. This is intentional—consistency across tools makes life easier for both the developer and the model. This is also a good opportunity to understand how FastMCP automatically generates the schema from your Python function.

💎 Core Concept

@mcp.tool()

def multiply(a: float, b: float) -> float:

"""Multiplies two numbers."""

return a * b

When FastMCP registers this tool, it automatically generates a schema like: {"name": "multiplicar", "description": "Multiplica dois números.", "inputSchema": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]}}. All of this comes from a simple Python function with type hints and a docstring.

❌ Avoid
  • •Functions without type hints (the schema is empty)
  • •Functions without docstrings (no description for the model)
  • •Generic names like “calculate” or “func1”
  • •Mixing business logic with formatting
✅ Do
  • •Type hints for all parameters and the return value
  • •Clear, descriptive docstrings
  • •Specific names: add, subtract, multiply
  • •One responsibility per tool
4

➗ Division with Validation

Division differs from the other operations because it has a dangerous special case: the division by zero. If the user asks to divide any number by zero and the tool simply executes a / b, Python will raise a ZeroDivisionError and the server may crash. We need to validate the input before running the operation.

💎 Core Concept

@mcp.tool()

def divide(a: float, b: float) -> str:

"""Divides a by b. Returns an error if b is zero."""

if b == 0:

return "Error: Division by zero is not allowed."

return f"{a / b}"

Instead of letting the exception bubble up, we return a friendly error message as a string. The model receives this message and can inform the user naturally: "You can't divide by zero." This is much better than an incomprehensible Python traceback.

🚨 Alert

Never let a tool crash! An unhandled exception can bring down the entire MCP server, disconnecting the client. Always validate inputs, use try/except when needed, and return clear error messages. The model knows how to handle error messages—it doesn't know how to handle a server that has stopped responding.

❌ Avoid
  • •Let exceptions propagate without handling them
  • •Return Python tracebacks as the response
  • •Ignore edge cases (zero, None, etc.)
  • •Trust that the model always sends valid data
✅ Do
  • •Validate inputs before processing
  • •Return user-friendly error messages
  • •Use try/except for unexpected errors
  • •Intentionally test with invalid inputs
5

🧪 Testing All Tools

With all 4 tools implemented, it's time to configure the server in Claude Desktop and test each operation. The goal is to verify that Claude correctly identifies which tool to use for each question and that all operations return correct results—including handling division by zero.

💎 Core Concept

Add the server to claude_desktop_config.json and restart Claude Desktop. Then, don’t forget to add the startup block to the end of the file:

if __name__ == "__main__":

mcp.run()

Now test the tools with natural-language questions. Claude will automatically choose the right tool for each operation.

📋 Recommended Testing Sequence
Test 1 — Addition

"What's 15 + 27?" — Claude should use the tool somar and return 42.

Test 2 — Subtraction

"What's 100 minus 37?" — Claude should use subtrair and return 63.

Test 3 — Multiplication

"Multiply 8 by 7." — Claude should use multiplicar and return 56.

Test 4 — Normal division

"Divide 100 by 4." — Claude should use dividir and return 25.

Test 5 — Division by zero

"Divide 100 by 0." — Claude should receive the error message and explain that division by zero is not possible.

💡 Practical Tip

Also test with decimal numbers ("How much is 3.14 times 2?") and with negative numbers ("Subtract 50 from 30"). Check whether Claude can chain operations: "Add 10 and 20, then multiply by 3" — it should call the tools in the correct order.

6

🔧 Improvements and Refactoring

Now that the server works, we can think about improvements. Rounding results, explicitly supporting integers and floats, and adding new operations such as exponentiation. These improvements turn a demo server into something more robust and professional.

💎 Core Concept

Some practical improvements for the calculator server:

  • 1. Rounding: Use round(resultado, 10) to avoid floating-point issues like 0.1 + 0.2 = 0.30000000000000004.
  • 2. Union types: Use a: int | float to explicitly accept both integers and floats.
  • 3. New tool: Add potenciar(base: float, expoente: float) as an exercise — it follows the same pattern as the others.
💡 Practical Tip

As an exercise, implement the tool potenciar(base: float, expoente: float). It should return base ** expoente. Think: does it need special validation? What happens with 0 ** 0? And with negative exponents? Practice thinking in "what could go wrong?" — this mindset will serve you well with every MCP server you build.

❌ Avoid
  • •Leave floating-point issues unhandled
  • •Create a single "calculate" tool that does everything
  • •Add unnecessary complexity
  • •Forgetting to test the improvements
✅ Do
  • •Round floating-point results
  • •Keep tools separated by operation
  • •Add new tools incrementally
  • •Test each improvement in isolation

📝 Module Summary

  • ✓ We planned 4 tools with consistent signatures: add, subtract, multiply, and divide.
  • ✓ We implemented addition and subtraction as simple functions with type hints and docstrings.
  • ✓ We learned how FastMCP automatically generates JSON schemas from Python type hints.
  • ✓ We added division-by-zero validation, returning an error message instead of crashing.
  • ✓ We tested all the tools in Claude Desktop with different types of input.
  • ✓ We explored improvements such as rounding, union types, and new operations.

Next Module: 3.5 — Weather Lookup Server