# Creating an MCP Server in Go and Serving it with Docker ## Introduction Today we'll look at how to create an MCP server in Go and serve it with Docker. Prerequisites: having read [Understanding the Model Context Protocol (MCP)](https://k33g.hashnode.dev/understanding-the-model-context-protocol-mcp) To write an MCP server, there are several official SDKs: - [TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) - [Python SDK](https://github.com/modelcontextprotocol/python-sdk) - [Kotlin SDK](https://github.com/modelcontextprotocol/kotlin-sdk) In recent years, I've developed an appetite for Go, so I looked around to see if there was a Go implementation. On this page [https://github.com/punkpeye/awesome-mcp-servers?tab=readme-ov-file#frameworks](https://github.com/punkpeye/awesome-mcp-servers?tab=readme-ov-file#frameworks), I found several, notably [https://github.com/mark3labs/mcp-go](https://github.com/mark3labs/mcp-go), by the creator of `mcphost` (which I discuss and use in the [previous blog post](https://k33g.hashnode.dev/understanding-the-model-context-protocol-mcp)). The advantage of the `mcp-go` project is that it's simple and also provides tools to develop an MCP client, which will be very useful for a future blog post. ## Creating an MCP Server I won't detail or implement all the possibilities of an MCP server here. I'll just implement the essentials: - Provide a list of tools for the LLM - Execute these tools when the LLM invokes them When I use an LLM, there's one thing I'd like to be able to do: give it a website link so it can find information there, make a summary for me, etc. So I created an MCP server that calls the `curl` utility with a URL parameter and returns its content. But let's look at the source code: **`go.mod`**: ```go module mcp-curl go 1.23.4 require github.com/mark3labs/mcp-go v0.8.2 require github.com/google/uuid v1.6.0 ``` **`main.go`**: ```go package main import ( "context" "fmt" "os/exec" "github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server" ) func main() { // Create MCP server s := server.NewMCPServer( "mcp-curl", "1.0.0", ) // Add a tool tool := mcp.NewTool("use_curl", mcp.WithDescription("fetch this webpage"), mcp.WithString("url", mcp.Required(), mcp.Description("url of the webpage to fetch"), ), ) // Add a tool handler s.AddTool(tool, curlHandler) fmt.Println("🚀 Server started") // Start the stdio server if err := server.ServeStdio(s); err != nil { fmt.Printf("😡 Server error: %v\n", err) } fmt.Println("👋 Server stopped") } func curlHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { url, ok := request.Params.Arguments["url"].(string) if !ok { return mcp.NewToolResultError("url must be a string"), nil } cmd := exec.Command("curl", "-s", url) output, err := cmd.Output() if err != nil { return mcp.NewToolResultError(err.Error()), nil } content := string(output) return mcp.NewToolResultText(content), nil } ``` ### Explanations It's really very simple. My server will provide only one tool. The code implements a server that exposes a web "fetching" tool based on `curl` using the Model Control Protocol. - First, I create an MCP server named `mcp-curl` version `1.0.0` that works with standard input/output (stdio) - Then I define a tool named `use_curl` that takes a required `url` parameter - Finally, I add the `handler` for this tool that executes the `curl` command with the `-s` option to retrieve the webpage content - Of course, I handle errors and return the webpage content as text ## Packaging the MCP Server with Docker To make my life easier, I'll use a `Dockerfile` to build a Docker image containing my MCP server. This way, I can more easily deploy it on different platforms or make it available to anyone who wants to use it. **`Dockerfile`**: ```Dockerfile FROM golang:1.23.4-alpine AS builder WORKDIR /app COPY go.mod . COPY main.go . RUN <