Model Context Protocol (MCP) servers allow LLMs (MCP hosts/clients) to access prompts, resources, and tools in a standard way, allowing you to build agents and complex workflows on top of LLMs.
SDKs make building and integrating MCP clients and servers easy. While there isn’t an official SDK for Go yet, the community-built mark3labs/mcp-go has been gaining a lot of popularity among Go developers—including myself.
I used this SDK today to make a real-world MCP for a real project, and it has been pretty neat so far. This article is a quick walkthrough of how I set up an MCP server using the MCP Go SDK for DiceDB, an in-memory key-value store like Redis.
Install the mcp-go Module
To use the mcp-go
module, run:
go get github.com/mark3labs/mcp-go
We’ll also use the dicedb-go
module to talk to the database:
go get github.com/dicedb/dicedb-go
Create a New MCP Server
The entire server fits into a single main.go
file, thanks to the abstractions provided by the SDK. Let’s break down the important bits, starting with setting up the server:
main.go 1package main
2
3// Import ALL required modules
4import (
5 "context"
6 "fmt"
7 "net"
8 "strconv"
9 "strings"
10
11 "github.com/dicedb/dicedb-go"
12 "github.com/dicedb/dicedb-go/wire"
13 "github.com/mark3labs/mcp-go/mcp"
14 "github.com/mark3labs/mcp-go/server"
15)
16
17func main() {
18 // Create a new MCP server
19 s := server.NewMCPServer(
20 "DiceDB MCP", // Name of the server
21 "0.1.0", // Version
22 // Set listChanged to false as this example
23 // server does not emit notifications
24 // when the list of available tool changes
25 // https://modelcontextprotocol.io/specification/2024-11-05/server/tools#capabilities
26 server.WithToolCapabilities(false),
27 )
Here’s what’s happening here:
- 4-15: We import the MCP and DiceDB modules and some helpers.
- 19-27: We create a new MCP server instance:
Define a New Tool
Now, let’s add a tool. This one just pings the DiceDB server to check if it’s reachable:
main.go29 pingTool := mcp.NewTool("ping", // Tool name
30 // Short description of what the tool does
31 mcp.WithDescription("Ping the DiceDB server to check connectivity"),
32 // Add a string property to the tool schema
33 // to accept the URL of the DiceDB server
34 mcp.WithString("url",
35 // Description of the property
36 mcp.Description("The URL of the DiceDB server in format 'host:port'"),
37 // Default value that compliant clients
38 // can use if the value isn't explicitly set
39 mcp.DefaultString("localhost:7379"),
40 ),
41 )
Under the hood, these definitions get translated to a JSON schema following the JSON-RPC 2.0 specification. MCP clients use this schema to discover and call the tool.
The "ping"
tool has the following properties:
- 29:
mcp.NewTool("ping", ...
defines a tool named"ping"
that can be invoked by MCP clients. - 31:
mcp.WithDescription
adds a description property to the tool schema that helps both humans and AI (MCP host/client) decide which tool to use. - 34:
mcp.WithString
adds a string property to the tool schema to obtain the URL of the DiceDB server (from the MCP host/client). - 39:
mcp.DefaultString
isn’t defined in the MCP specification, but compliant clients can use this to set a default value if none is provided.
Next, we wire in the actual logic.
Create a Handler Function
Now that we’ve defined the tool let’s add what happens when it’s invoked. In our case, we want to ping the DiceDB server to check if it’s reachable:
main.go43 s.AddTool(pingTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
44 // Extract the URL argument from the client request
45 url := request.Params.Arguments["url"].(string)
46
47 // Parse host and port from URL
48 parts := strings.Split(url, ":")
49 host := parts[0]
50 port := 7379 // Default to 7379 if no port is provided
51
52 if len(parts) > 1 {
53 if p, err := strconv.Atoi(parts[1]); err == nil {
54 port = p
55 }
56 }
57
58 // Create a new DiceDB client
59 client, err := dicedb.NewClient(host, port)
60 if err != nil {
61 return mcp.NewToolResultText(fmt.Sprintf("Error connecting to DiceDB: %v", err)), nil
62 }
63
64 // Send the PING command to DiceDB
65 resp := client.Fire(&wire.Command{Cmd: "PING"})
66
67 // Return the result to the MCP client
68 return mcp.NewToolResultText(fmt.Sprintf("Response from DiceDB: %v", resp)), nil
69 })
Here, we define a handler function that runs when the MCP client calls the tool. This is what it does:
- 45: Extracts the URL from the client request.
- 48-56: Splits the string to separate host and port. If a port isn’t specified, we fall back to DiceDB’s default (
7379
). - 59-62: Initializes a new DiceDB client using the
dicedb-go
SDK. - 65: Sends a
PING
command to DiceDB. - 68: Returns the formatted response back to the MCP client.
See how the SDK provides neat wrappers like NewToolResultText
to structure responses as required by the MCP specification.
Start the Server
All that’s left is to start the server:
main.go71 if err := server.ServeStdio(s); err != nil {
72 fmt.Printf("Error starting server: %v\n", err)
73 }
74}
This uses the stdio transport, meaning our MCP server communicates using standard input and output streams. This is the simplest way to run an MCP server locally.
The MCP specification also allows SSE or Server-Sent Events transport, which enables server-to-client streaming and uses HTTP POST for client-to-server messages. You can also create custom transports for specific needs.
Build/Install the Server
Before using the server, build the binary:
go build -o ./dist/dicedb-mcp
Or install it globally (adds it to $GOBIN
):
go install
This gives you either a local or a global runnable binary that can be executed by MCP clients and hosts.
Use with MCP Hosts/Clients
To use this MCP server with host applications like Claude Desktop or Cursor, add it to their configuration file (claude_desktop_config.json
or mcp.json
):
mcp.json{
"mcpServers": {
"dicedb-mcp": {
"command": "path/to/dicedb-mcp"
}
}
}
Now you can prompt the LLM like:
Can you check if DiceDB is running at localhost:7379?
The LLM will detect the ping
tool we registered, ask to run it, and show the result returned by the MCP server.
Note: Make sure you have DiceDB running. You can also apply this pattern to wrap other tools into MCP servers instead of DiceDB, as used in this example.
Learn More
We’ve built a minimal example to demonstrate how the MCP Go SDK works. It is functional but intentionally simple.
You can check out a more robust DiceDB MCP implementation (with additional tools and features) on GitHub.
To go further, I recommend these resources:
Thank you for reading "Building a Model Context Protocol (MCP) Server in Go."
Subscribe via email or RSS feed to be the first to receive my content.
If you liked this post, check out my featured posts or learn more about me.