MCP Curl Server logo

MCP Curl Server

by ollama-tlms-golang

This project demonstrates how to create a Model Context Protocol (MCP) server in Go and package it with Docker. The server provides a tool to fetch webpage content using curl, enabling LLMs to access and analyze online information.

View on GitHub

Last updated: N/A

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)

To write an MCP server, there are several official SDKs:

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, I found several, notably https://github.com/mark3labs/mcp-go, by the creator of mcphost (which I discuss and use in the previous blog post).

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:

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:

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:

FROM golang:1.23.4-alpine AS builder
WORKDIR /app
COPY go.mod .
COPY main.go .

RUN <<EOF
go mod tidy 
go build
EOF

FROM curlimages/curl:8.6.0
WORKDIR /app
COPY --from=builder /app/mcp-curl .
ENTRYPOINT ["./mcp-curl"]

My Dockerfile has two parts:

  • The first part builds my MCP server in Go
  • The second part uses the curlimages/curl:8.6.0 image that contains curl and copies my mcp-curl server into the image (so mcp-curl is the executable that calls curl)

To build the Docker image, I run the following command:

docker build -t mcp-curl .

And now let's see how to use our new MCP server with mcphost:

Using the MCP Server with mcphost

First, we need to create a configuration file mcp.json for mcphost:

{
  "mcpServers": {
    "mcp-curl-with-docker" :{
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "mcp-curl"
      ]
    }
  }
}

Then we can use mcphost to use our MCP server and an LLM with Ollama, like this:

mcphost --config ./mcp.json --model ollama:qwen2.5-coder:14b

The MCP server is recognized by mcphost:

mcp

mcp

You can request the list of available tools with the /tools command:

mcp

mcp

Now you can request the content of a webpage and analyze its content (in my example I retrieve Go code from GitHub):

mcp

mcp

Wait a little bit:

mcp

mcp

And here's the webpage content:

mcp

mcp

Conclusion

You can see that with just a few lines, it becomes really easy to give "superpowers" to your LLMs. In a future blog post, I'll show you how to create a generative AI application with Ollama and an MCP client in Go to interact with our MCP server.