Model Context Protocol (MCP) is revolutionizing how AI assistants interact with external tools and services. In this guide, we’ll build a simple calculator server that demonstrates the core concepts of MCP by creating a tool that Claude (or other MCP clients) can use to perform mathematical calculations.
What is MCP?
The Model Context Protocol (MCP) is an open standard that enables AI assistants to securely connect to data sources and tools. Instead of hardcoding integrations, MCP allows you to create servers that expose tools, resources, and prompts that AI assistants can dynamically discover and use.
Key Components
- MCP Server: Exposes tools and resources (like our calculator)
- MCP Client: AI assistants like Claude Desktop that consume MCP services
- Tools: Functions that the AI can call (like our
calculate
function) - JSON-RPC: The underlying communication protocol
Building the Calculator Server
Let’s start by examining our MCP calculator server implementation:
package main
import (
"context"
"fmt"
"log"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
// Create MCP server
s := server.NewMCPServer("calculator-server", "0.1.0")
// Add calculator tool
s.AddTool(mcp.NewTool(
"calculate",
mcp.WithDescription("Perform basic mathematical operations like add, subtract, multiply, and divide on two numbers"),
mcp.WithString("expression",
mcp.Description("A mathematical expression to evaluate (e.g., '2 + 3', '10 * 5', '15 / 3')"),
mcp.Required(),
),
), handleCalculate)
// Serve using stdio
if err := server.ServeStdio(s); err != nil {
log.Fatalf("Server error: %v", err)
}
}
Tool Registration
The heart of our MCP server is tool registration. We define:
- Tool name:
calculate
- Description: What the tool does
- Parameters: Input schema (expression string)
- Handler: The function that processes requests
Understanding the MCP Protocol Flow
1. Tool Name Resolution
When you configure an MCP server in Claude Desktop, it gets a namespace:
local__calculator__calculate
│ │ │
│ │ └── Function: "calculate"
│ └── Category: "calculator" (from config)
└── Namespace: "local"
The server uses simple tool names such as “calculate”, but the client adds namespace prefixes for organization.
2. Client-Server Communication
The communication follows this JSON-RPC flow:
Tool Discovery
First, the client asks for available tools:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}
Server responds with:
{
"id": 2,
"jsonrpc": "2.0",
"result": {
"tools": [
{
"name": "calculate",
"description": "Perform basic mathematical operations like add, subtract, multiply, and divide on two numbers",
"inputSchema": {
"properties": {
"expression": {
"description": "A mathematical expression to evaluate (e.g., '2 + 3', '10 * 5', '15 / 3')",
"type": "string"
}
},
"required": ["expression"],
"type": "object"
}
}
]
}
}
Tool Execution
When Claude wants to calculate “3 + 3”, it sends:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "calculate",
"arguments": {
"expression": "3 + 3"
}
}
}
Our server processes this and responds:
{
"id": 3,
"jsonrpc": "2.0",
"result": {
"content": [
{
"text": "6",
"type": "text"
}
]
}
}
Testing the MCP Server
I’ve created a test client to help debug MCP servers. Here’s how the interaction looks:
go run -tags=client client.go /home/yina/go/bin/mcp-calculator-server
Test Results
Starting MCP server: /home/yina/go/bin/mcp-calculator-server
============================================================
MCP Client - Choose an action:
1. Initialize connection
2. List available tools
3. Calculate 3 + 3
4. Calculate custom expression
5. Send custom JSON-RPC message
6. Exit
============================================================
Configuration and Setup
Claude Desktop Configuration
Add this to the Claude Desktop configuration:
{
"mcpServers": {
"calculator": {
"command": "/path/to/mcp-calculator-server"
}
}
}
The key "calculator"
becomes the category in the namespaced tool name.
Installation
go install github.com/yinebebt/mcp-calculator-server
Key Insights
1. Naming Convention
- Server side: Use simple, descriptive names (
calculate
) - Client side: Automatically adds namespacing (
local__calculator__calculate
) - Configuration: The category comes from the MCP client config
2. Protocol Structure
- Initialization: Establish connection and capabilities
- Discovery: List available tools
- Execution: Call tools with parameters and return structured results
3. Error Handling
Proper error responses follow the JSON-RPC spec:
{
"error": {
"code": -32602,
"message": "tool 'invalid-tool' not found: tool not found"
},
"id": 3,
"jsonrpc": "2.0"
}
Real-World Usage
Once configured, you can ask Claude:
- “Can you calculate 25 * 4?”
- “What’s 15 divided by 3?”
- “Help me with this math: (10 + 5) * 2”
Claude will automatically use the calculator server to provide accurate results.
Conclusion
Building an MCP server is straightforward once you understand the protocol flow. The key principles are:
- Simple tool registration with clear descriptions
- Proper JSON-RPC handling for communication
- Structured responses that clients can process
- Good error handling for robust operation
This calculator example demonstrates the fundamentals, but MCP can power much more complex integrations - from database queries to API calls to file system operations.
The future of AI assistance lies in this kind of modular, protocol-driven architecture where AI assistants can dynamically discover and use the tools they need to help users accomplish their goals.
Source code available at: github.com/yinebebt/mcp-calculator-server