Get Started

Get Started

Prerequisites

Before you begin, ensure you have the following installed:

  • Go: You need a recent version of Go installed on your system. Go version 1.18 or later is recommended as it includes support for generics, which are used in some parts of the gomcp library. You can download and install Go from the official Go website: https://golang.org/dl/
  • A Go Project: You should have an existing Go project or create a new one. If you’re creating a new project, initialize a Go module:
    go mod init your_module_name

Installing GoMCP

To add the gomcp library to your Go project, open your terminal or command prompt, navigate to your project’s root directory, and run the following command:

go get github.com/localrivet/gomcp

This command will download the gomcp library and its dependencies and add them to your project’s go.mod file.

Importing the Library

Once installed, you can import the necessary packages in your Go code to start building MCP servers or clients:

import (
	"github.com/localrivet/gomcp/client"   // For building MCP clients
	"github.com/localrivet/gomcp/server"   // For building MCP servers
	"github.com/localrivet/gomcp/protocol" // For MCP message types and structures
	// You may also need to import specific transport packages, e.g.:
	// "github.com/localrivet/gomcp/transport/stdio"
	// "github.com/localrivet/gomcp/transport/sse"
	// "github.com/localrivet/gomcp/transport/websocket"
)

Quickstart

This guide provides a quick introduction to building a minimal GoMCP server and client that communicate using the standard input/output (stdio) transport. This will give you a basic understanding of how to get started with the library.

Your First MCP Server

Let’s create a simple MCP server that registers a basic “echo” tool. This tool will simply return the input text it receives.

Create a new file (e.g., server/main.go) and add the following code:

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/localrivet/gomcp/protocol"
	"github.com/localrivet/gomcp/server"
	"github.com/localrivet/gomcp/util/schema" // Helper for argument schema
)

// Define the arguments the echo tool expects
type EchoArgs struct {
	Input string `json:"input" description:"Text to echo back"`
}

// Define the handler function for the echo tool
func handleEchoTool(ctx context.Context, progressToken *protocol.ProgressToken, arguments any) (content []protocol.Content, isError bool) {
	// Use the schema helper to parse and validate arguments
	args, errContent, isErr := schema.HandleArgs[EchoArgs](arguments)
	if isErr {
		// Return the error content generated by HandleArgs
		return errContent, true
	}

	// Log the tool execution (optional)
	log.Printf("Echo tool received input: %s", args.Input)

	// Prepare the result content
	resultContent := []protocol.Content{
		protocol.TextContent{
			Type: "text",
			Text: "Echo: " + args.Input,
		},
	}

	// Return the result and indicate no error
	return resultContent, false
}

func main() {
	// Configure logger (optional, defaults to stderr)
	log.SetOutput(os.Stderr)
	log.SetFlags(log.Ltime | log.Lshortfile)
	log.Println("Starting Minimal MCP Server...")

	// Create the core server instance
	// Provide a name and default options
	srv := server.NewServer("MyMinimalEchoServer", server.ServerOptions{})

	// Define the metadata for the echo tool
	echoTool := protocol.Tool{
		Name:        "echo",
		Description: "Echoes back the input text.",
		InputSchema: schema.FromStruct(EchoArgs{}), // Generate schema from the struct
	}

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

	// Start the server using the built-in stdio handler.
	// This blocks until the server exits (e.g., EOF on stdin or error).
	log.Println("Server setup complete. Listening on stdio...")
	if err := server.ServeStdio(srv); err != nil {
		log.Fatalf("Server exited with error: %v", err)
	}

	log.Println("Server shutdown complete.")
}

Your First MCP Client

Now, let’s create a simple MCP client that connects to our server via stdio, lists the available tools, and calls the “echo” tool.

Create a new file (e.g., client/main.go) and add the following code:

package main

import (
	"context"
	"log"
	"os"
	"time"

	"github.com/localrivet/gomcp/client"
	"github.com/localrivet/gomcp/protocol"
)

func main() {
	// Configure logger (optional)
	log.SetOutput(os.Stderr)
	log.SetFlags(log.Ltime | log.Lshortfile)
	log.Println("Starting Simple Stdio MCP Client...")

	// Create a client instance for the stdio transport
	// NewStdioClient handles the underlying transport setup
	clt, err := client.NewStdioClient("MyMinimalEchoClient", client.ClientOptions{})
	if err != nil {
		log.Fatalf("Failed to create stdio client: %v", err)
	}
	// Ensure the client connection is closed when main exits
	defer clt.Close()

	// Use a context for the connection and requests
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Connect to the server and perform the initialization handshake
	log.Println("Connecting to server via stdio...")
	err = clt.Connect(ctx)
	if err != nil {
		log.Fatalf("Failed to connect and initialize with server: %v", err)
	}
	log.Printf("Connected to server: %s (Version: %s)", clt.ServerInfo().Name, clt.ServerInfo().Version)

	// --- Client is now ready to make requests ---

	// Example: List available tools from the server
	log.Println("Listing available tools...")
	toolsResult, err := clt.ListTools(ctx, protocol.ListToolsRequestParams{})
	if err != nil {
		log.Printf("Error listing tools: %v", err)
	} else {
		log.Printf("Available tools (%d):", len(toolsResult.Tools))
		for _, tool := range toolsResult.Tools {
			log.Printf("- %s: %s", tool.Name, tool.Description)
		}
	}

	// Example: Call the "echo" tool
	log.Println("Calling 'echo' tool...")
	callToolParams := protocol.CallToolParams{
		Name: "echo",
		Arguments: map[string]interface{}{
			"input": "Hello from the client!",
		},
	}
	callToolResult, err := clt.CallTool(ctx, callToolParams)
	if err != nil {
		log.Printf("Error calling tool 'echo': %v", err)
	} else {
		log.Println("Tool 'echo' returned content:")
		for _, content := range callToolResult.Content {
			// Assuming TextContent for this example
			if textContent, ok := content.(protocol.TextContent); ok {
				log.Printf("- %s: %s", textContent.Type, textContent.Text)
			} else {
				log.Printf("- Received content of type: %s", content.GetContentType())
			}
		}
		if callToolResult.IsError != nil && *callToolResult.IsError {
			log.Println("Tool execution reported an error.")
		}
	}

	log.Println("Client operations finished.")
}

Running the Example

To run this minimal server and client and see them communicate:

  1. Save the server code as server/main.go and the client code as client/main.go within your Go project.
  2. Navigate to your project’s root directory in your terminal.
  3. Run go mod tidy to ensure dependencies are fetched.
  4. Pipe the output of the client directly into the input of the server:
    go run client/main.go | go run server/main.go
    You should see output from both the client and the server demonstrating the MCP handshake, tool listing, and the echo tool call.

Next Steps

You have successfully built and run a minimal GoMCP server and client. To learn more, explore the detailed guides:

  • Servers: Learn how to build more complex MCP servers with various capabilities.
  • Clients: Understand how to build clients that interact with servers using different transports and features.