Tools & MCP
Understanding function calling and the Model Context Protocol.
What are Tools?
Section titled “What are Tools?”Tools (also called “function calling”) allow LLMs to execute code, query databases, call APIs, and interact with external systems.
Why Tools?
Section titled “Why Tools?”Extend capabilities: LLMs can do more than generate text
Real-time data: Access current information
Take actions: Update databases, send emails, etc.
Accuracy: Use code for math, not LLM reasoning
How Tools Work
Section titled “How Tools Work”- Define tools: Describe available functions
- LLM decides: When to use which tool
- Execute: Run the tool and get results
- LLM responds: Incorporate tool results in response
Example Flow
Section titled “Example Flow”User: "What's the weather in Paris?" ↓LLM: "I should use the weather tool" ↓Tool Call: get_weather(city="Paris") ↓Tool Result: {"temp": 18, "condition": "cloudy"} ↓LLM: "It's 18°C and cloudy in Paris"Tools in PromptKit
Section titled “Tools in PromptKit”Define Tools
Section titled “Define Tools”import "github.com/AltairaLabs/PromptKit/runtime/tools"
// Define toolweatherTool := &tools.ToolDef{ Name: "get_weather", Description: "Get current weather for a city", Parameters: json.RawMessage(`{ "type": "object", "properties": { "city": { "type": "string", "description": "City name" } }, "required": ["city"] }`),}Implement Executor
Section titled “Implement Executor”type WeatherExecutor struct{}
func (e *WeatherExecutor) Execute(ctx context.Context, call *types.ToolCall) (string, error) { var params struct { City string `json:"city"` } json.Unmarshal(call.Arguments, ¶ms)
// Call weather API weather := getWeather(params.City)
return fmt.Sprintf(`{"temp": %d, "condition": "%s"}`, weather.Temp, weather.Condition), nil}
func (e *WeatherExecutor) Name() string { return "get_weather"}Use in Pipeline
Section titled “Use in Pipeline”// Create registryregistry := tools.NewToolRegistry()registry.RegisterTool(weatherTool, &WeatherExecutor{})
// Add to pipelinepipe := pipeline.NewPipeline( middleware.ToolMiddleware(registry), middleware.ProviderMiddleware(provider, nil, nil, nil),)
// Executeresult, _ := pipe.Execute(ctx, "user", "What's the weather in Paris?")// LLM will use the tool automaticallyModel Context Protocol (MCP)
Section titled “Model Context Protocol (MCP)”MCP is a standard for connecting LLMs to external data sources and tools.
What is MCP?
Section titled “What is MCP?”MCP provides:
- Standard interface: Connect any tool to any LLM
- Tool discovery: LLMs learn available tools
- Secure execution: Sandboxed tool execution
- Composability: Combine multiple tool servers
MCP Architecture
Section titled “MCP Architecture”LLM Application ↓ MCP Client ↓ MCP Server(s) ↓External Systems (filesystem, databases, APIs)MCP in PromptKit
Section titled “MCP in PromptKit”Start MCP Server
Section titled “Start MCP Server”# Filesystem servernpx @modelcontextprotocol/server-filesystem ~/documents
# Memory servernpx @modelcontextprotocol/server-memoryConnect in Code
Section titled “Connect in Code”import "github.com/AltairaLabs/PromptKit/runtime/mcp"
// Connect to MCP servermcpClient, err := mcp.NewStdioClient("npx", []string{ "@modelcontextprotocol/server-filesystem", "/path/to/files",})if err != nil { log.Fatal(err)}defer mcpClient.Close()
// Create MCP executorexecutor := mcp.NewMCPExecutor(mcpClient)
// Register toolsregistry := tools.NewToolRegistry()mcpTools, _ := mcpClient.ListTools()for _, tool := range mcpTools { registry.RegisterTool(tool, executor)}
// Use in pipelinepipe := pipeline.NewPipeline( middleware.ToolMiddleware(registry), middleware.ProviderMiddleware(provider, nil, nil, nil),)Common Tools
Section titled “Common Tools”File Operations
Section titled “File Operations”fileTools := []tools.ToolDef{ { Name: "read_file", Description: "Read contents of a file", }, { Name: "write_file", Description: "Write contents to a file", }, { Name: "list_directory", Description: "List files in a directory", },}Database Queries
Section titled “Database Queries”dbTool := &tools.ToolDef{ Name: "query_database", Description: "Execute SQL query", Parameters: json.RawMessage(`{ "type": "object", "properties": { "query": {"type": "string"} } }`),}API Calls
Section titled “API Calls”apiTool := &tools.ToolDef{ Name: "fetch_url", Description: "Fetch data from URL", Parameters: json.RawMessage(`{ "type": "object", "properties": { "url": {"type": "string"} } }`),}Calculations
Section titled “Calculations”calcTool := &tools.ToolDef{ Name: "calculate", Description: "Perform mathematical calculation", Parameters: json.RawMessage(`{ "type": "object", "properties": { "expression": {"type": "string"} } }`),}Tool Design Best Practices
Section titled “Tool Design Best Practices”Clear Descriptions
Section titled “Clear Descriptions”Bad:
Description: "Gets stuff"Good:
Description: "Get current weather for a specified city. Returns temperature in Celsius and current conditions."Detailed Parameters
Section titled “Detailed Parameters”Parameters: json.RawMessage(`{ "type": "object", "properties": { "city": { "type": "string", "description": "City name (e.g., 'Paris', 'New York')" }, "units": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature units", "default": "celsius" } }, "required": ["city"]}`)Error Handling
Section titled “Error Handling”func (e *WeatherExecutor) Execute(ctx context.Context, call *types.ToolCall) (string, error) { // Validate parameters if params.City == "" { return "", errors.New("city is required") }
// Handle API errors weather, err := api.GetWeather(params.City) if err != nil { return "", fmt.Errorf("failed to get weather: %w", err) }
// Return structured result return json.Marshal(weather)}Security
Section titled “Security”✅ Validate inputs:
if !isValidCity(params.City) { return "", errors.New("invalid city name")}✅ Limit access:
// Only allow reading specific directoriesallowedPaths := []string{"/data", "/docs"}✅ Timeout operations:
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)defer cancel()❌ Don’t expose sensitive data:
// Bad: Returns API keysreturn fmt.Sprintf("API_KEY=%s", apiKey)
// Good: Returns only needed datareturn fmt.Sprintf("weather=%s", weather)Testing Tools
Section titled “Testing Tools”Unit Tests
Section titled “Unit Tests”func TestWeatherTool(t *testing.T) { executor := &WeatherExecutor{}
call := &types.ToolCall{ Name: "get_weather", Arguments: json.RawMessage(`{"city": "Paris"}`), }
result, err := executor.Execute(context.Background(), call) assert.NoError(t, err) assert.Contains(t, result, "temp")}Integration Tests
Section titled “Integration Tests”# arena.yamltests: - name: Weather Tool Test prompt: "What's the weather in Paris?" assertions: - type: tool_call tool_name: get_weather - type: contains value: "Paris"Monitoring Tools
Section titled “Monitoring Tools”Track Usage
Section titled “Track Usage”type ToolMetrics struct { CallCount map[string]int ErrorCount map[string]int AvgLatency map[string]time.Duration}
func RecordToolCall(toolName string, duration time.Duration, err error) { metrics.CallCount[toolName]++ if err != nil { metrics.ErrorCount[toolName]++ } metrics.AvgLatency[toolName] = updateAverage(duration)}Log Tool Calls
Section titled “Log Tool Calls”logger.Info("tool executed", zap.String("tool", call.Name), zap.Duration("duration", duration), zap.Bool("success", err == nil),)Common Patterns
Section titled “Common Patterns”Tool Chaining
Section titled “Tool Chaining”LLM uses multiple tools:
User: "Analyze the sales data" ↓Tool 1: read_file("sales.csv") ↓Tool 2: calculate("sum(column)") ↓Tool 3: create_chart(data) ↓Response: "Here's your sales analysis [chart]"Conditional Tools
Section titled “Conditional Tools”if userTier == "premium" { registry.RegisterTool(advancedAnalyticsTool, executor)}Cached Tools
Section titled “Cached Tools”type CachedExecutor struct { inner Executor cache map[string]string}
func (e *CachedExecutor) Execute(ctx context.Context, call *types.ToolCall) (string, error) { key := getCacheKey(call) if cached, ok := e.cache[key]; ok { return cached, nil }
result, err := e.inner.Execute(ctx, call) if err == nil { e.cache[key] = result } return result, err}Summary
Section titled “Summary”Tools & MCP provide:
✅ Extended capabilities - LLMs can do more
✅ Real-time data - Access current information
✅ Actions - Interact with external systems
✅ Standardization - MCP provides common interface
✅ Composability - Combine multiple tools
Related Documentation
Section titled “Related Documentation”- Integrate Tools - Implementation guide
- MCP Integration Tutorial - Step-by-step guide
- Tools Reference - API documentation
- MCP Architecture - Technical details