Tools

Tools

Tools allow clients (like LLMs) to perform actions by executing functions on your server. Use server.AddTool for easy registration with automatic schema generation based on a Go struct for arguments.

package main

import (
	"github.com/localrivet/gomcp/protocol"
	"github.com/localrivet/gomcp/server"
	// ... other imports
)

// Define the arguments struct for your tool
type ReverseArgs struct {
	Input string `json:"input" description:"The string to reverse" required:"true"`
}

// Your tool handler function
func reverseStringHandler(args ReverseArgs) (protocol.Content, error) {
	runes := []rune(args.Input)
	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
		runes[i], runes[j] = runes[j], runes[i]
	}
	reversed := string(runes)
	return server.Text(reversed), nil // Helper for simple text responses
}

func registerTools(srv *server.Server) error {
	// Register the tool using the helper
	err := server.AddTool(
		srv,
		"reverse_string",
		"Reverses the input string.",
		reverseStringHandler, // Pass the handler function
	)
	return err
}

Alternatively, you can provide a protocol.ToolDefinition manually for more control.

Alternative: Using RegisterTool and schema.FromStruct

For more direct control over the tool definition and handler signature, you can use srv.RegisterTool and explicitly generate the schema using schema.FromStruct from the util/schema package.

This approach requires you to manually handle argument parsing within your handler function, which receives the raw protocol.ToolCall.

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/localrivet/gomcp/protocol"
	"github.com/localrivet/gomcp/server"
	"github.com/localrivet/gomcp/util/schema" // Import schema helper
	// ... other imports
)

// Define the arguments struct (still useful for schema generation)
type MultiplyArgs struct {
	X float64 `json:"x" description:"Multiplicand" required:"true"`
	Y float64 `json:"y" description:"Multiplier" required:"true"`
}

// Handler function for RegisterTool takes protocol.ToolCall
func multiplyHandler(call protocol.ToolCall) (protocol.ToolResult, error) {
	var args MultiplyArgs
	// Manually parse arguments
	if err := json.Unmarshal(call.Arguments, &args); err != nil {
		log.Printf("[multiplyHandler] Error parsing args: %v", err)
		// Return a structured error result
		return protocol.ToolResult{IsError: true, Content: []protocol.Content{server.Text(fmt.Sprintf("Invalid arguments: %v", err))}}, nil
	}

	result := args.X * args.Y
	log.Printf("[multiply tool] %f * %f -> %f", args.X, args.Y, result)

	// Return a successful result
	return protocol.ToolResult{Content: []protocol.Content{server.Text(fmt.Sprintf("%f", result))}}, nil
}


func registerToolsManually(srv *server.Server) {
	// Register the tool using RegisterTool
	srv.RegisterTool(
		protocol.Tool{ // Define the full Tool struct
			Name:        "multiply",
			Description: "Multiplies two numbers.",
			// Generate schema explicitly
			InputSchema: schema.FromStruct(MultiplyArgs{}),
			// OutputSchema can also be defined here if needed
		},
		multiplyHandler, // Pass the handler with the (ToolCall) signature
	)
	// Note: Error handling for RegisterTool might differ or be absent
	// depending on the implementation version or desired behavior.
	// Check server implementation details if needed.
}

tools/list Request

Clients can request a list of all registered tools.

  • Method: "tools/list"
  • Parameters: (None)
  • Result: protocol.ListToolsResult
type ListToolsResult struct {
	Tools []protocol.Tool `json:"tools"` // List of registered tools
}

This request allows clients to discover the tools available on the server, including their names, descriptions, and input schemas.

notifications/tools/list_changed Notification

Servers can send the notifications/tools/list_changed notification to inform clients that the list of available tools has changed and they should re-list if they need the updated list.

  • Method: "notifications/tools/list_changed"
  • Parameters: protocol.ToolsListChangedParams (currently empty)
type ToolsListChangedParams struct{} // Currently empty

This notification does not include the updated list itself, only signals that a change has occurred. Clients must send a tools/list request to get the new list.

3. Register the Tool with the Server

Finally, use the RegisterTool method on your server.Server instance before running the server. Pass the protocol.Tool definition and the corresponding handler function.

import (
	"context"
	"log"
	"os"
	"github.com/localrivet/gomcp/protocol"
	"github.com/localrivet/gomcp/server"
	"github.com/localrivet/gomcp/util/schema"
)

// Assume echoTool definition (protocol.Tool) and
// handleEchoTool (hooks.FinalToolHandler) are defined as above.

func main() {
	// Configure logger
	log.SetOutput(os.Stderr)
	log.SetFlags(log.Ltime | log.Lshortfile)

	// Create the server instance
	srv := server.NewServer("MyToolServer", server.ServerOptions{})

	// Register the echo tool and its handler
	err := srv.RegisterTool(echoTool, handleEchoTool)
	if err != nil {
		log.Fatalf("Failed to register tool '%s': %v", echoTool.Name, err)
	}
	log.Printf("Registered tool: %s", echoTool.Name)

	// Start the server using stdio (or another transport)
	log.Println("Starting server on stdio...")
	if err := server.ServeStdio(srv); err != nil {
		log.Fatalf("Server exited with error: %v", err)
	}
	log.Println("Server stopped.")
}

Now, when a client connects and sends a tools/call request for the “echo” tool with valid arguments, the handleEchoTool function will be executed, and its result will be sent back to the client. If the client sends a tools/list request, the echoTool definition will be included in the response.