Building an MCP Tool Server
This tutorial walks through building an MCP (Model Context Protocol) server with custom tools that AI assistants like Claude can discover and call.
Prerequisites
- Scriptling CLI installed (Installation)
- An MCP-compatible AI client (e.g., Claude Desktop)
What You’ll Build
A tool server that exposes three tools:
- time_now — returns the current time in a given timezone format
- data_summary — generates a summary of a list of numbers
- format_report — formats key-value data as a structured report
Step 1: Create the Tool Directory
mkdir -p my-tools
cd my-toolsStep 2: Define the time_now Tool
Create my-tools/time_now.toml:
description = "Get the current date and time"
keywords = ["time", "date", "now"]
[[parameters]]
name = "format"
type = "string"
description = "Time format string (e.g. '%Y-%m-%d' for date, '%H:%M:%S' for time)"
required = falseCreate my-tools/time_now.py:
import time
def call(args, kwargs):
fmt = kwargs.get("format", "%Y-%m-%d %H:%M:%S")
return time.strftime(fmt, time.localtime())Step 3: Define the data_summary Tool
Create my-tools/data_summary.toml:
description = "Calculate summary statistics for a list of numbers"
keywords = ["statistics", "stats", "average", "mean", "data"]
[[parameters]]
name = "numbers"
type = "array:number"
description = "List of numbers to summarize"
required = trueCreate my-tools/data_summary.py:
import statistics
def call(args, kwargs):
numbers = kwargs["numbers"]
if not numbers:
return {"error": "Empty list provided"}
result = {
"count": len(numbers),
"sum": sum(numbers),
"mean": statistics.mean(numbers),
"min": min(numbers),
"max": max(numbers),
}
if len(numbers) > 1:
result["stdev"] = statistics.stdev(numbers)
result["median"] = statistics.median(numbers)
return resultStep 4: Define the format_report Tool
Create my-tools/format_report.toml:
description = "Format a dictionary of data as a structured text report"
keywords = ["format", "report", "text", "display"]
[[parameters]]
name = "title"
type = "string"
description = "Report title"
required = true
[[parameters]]
name = "data"
type = "string"
description = "JSON string of key-value pairs to include in the report"
required = trueCreate my-tools/format_report.py:
import json
def call(args, kwargs):
title = kwargs["title"]
data = json.loads(kwargs["data"])
lines = []
lines.append("=" * 50)
lines.append(f" {title}")
lines.append("=" * 50)
lines.append("")
for key, value in data.items():
lines.append(f" {key}:")
lines.append(f" {value}")
lines.append("")
lines.append("-" * 50)
return "\n".join(lines)Step 5: Start the MCP Server
Start Scriptling as an MCP server with the tools directory:
scriptling --mcp-server ./my-toolsOr with stdio transport for Claude Desktop:
scriptling --mcp-server ./my-tools --mcp-transport stdioStep 6: Configure Claude Desktop
Add the tool server to your Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"scriptling-tools": {
"command": "scriptling",
"args": ["--mcp-server", "/absolute/path/to/my-tools", "--mcp-transport", "stdio"]
}
}
}Restart Claude Desktop. The AI will discover the tools automatically and can call them when needed.
Step 7: Test Tools from the CLI
Test individual tools directly from the command line:
# Test time_now
scriptling --call-tool my-tools/time_now '{"format": "%H:%M"}'
# Test data_summary
scriptling --call-tool my-tools/data_summary '{"numbers": [10, 20, 30, 40, 50]}'
# Test format_report
scriptling --call-tool my-tools/format_report '{"title": "Sales Report", "data": "{\"Q1\": \"$10,000\", \"Q2\": \"$15,000\"}"}'Step 8: Add Discoverable Tools
For tools that need to be explicitly registered via script (e.g., tools that depend on runtime state), set discoverable = false in the TOML file:
description = "Query the internal database"
discoverable = false
[[parameters]]
name = "query"
type = "string"
description = "SQL-like query string"
required = trueThen register them in your setup script:
import scriptling.mcp.tool as mcp
# Register the tool with a custom handler
mcp.register("db_query", "internal/db_query.py")Tool Directory Structure
The final directory structure:
my-tools/
time_now.toml
time_now.py
data_summary.toml
data_summary.py
format_report.toml
format_report.pyWhat You Learned
- Defining MCP tools with TOML metadata files
- Implementing tool logic in Scriptling scripts
- Starting Scriptling as an MCP server
- Configuring Claude Desktop to use custom tools
- Testing tools from the command line
- Discoverable vs manually-registered tools
See Also
- Writing MCP Tools - Complete MCP tool reference
- MCP Client Library - Connecting to MCP servers from scripts
- CLI MCP Server - CLI server mode documentation
- Statistics Library - Stats functions used in this tutorial