Building Your First MCP Server in Go

go dev.to

Building Your First MCP Server in Go

MCP (Model Context Protocol) is the standard that lets LLMs connect to the outside world. By default, your AI assistant is isolated — it can't read your database, call an API, or touch your filesystem. When you build an MCP server, you give it that access. Add a Postgres MCP server to your agent, and it can query your PostgreSQL database by making a tool call.

In this guide we'll build a hello world MCP server in Go from scratch, to understand the concept.


Project Setup

mkdir hello-mcp-server && cd hello-mcp-server
go mod init github.com/yourname/hello-mcp-server
go get github.com/mark3labs/mcp-go
Enter fullscreen mode Exit fullscreen mode

We use mcp-go — the most popular Go library for MCP servers.


Create the Server

s := server.NewMCPServer(
    "Demo 🚀",
    "1.0.0",
    server.WithToolCapabilities(false),
)
Enter fullscreen mode Exit fullscreen mode

Creates the MCP server with a name and version. WithToolCapabilities(false) tells the client the tool list is fixed at startup — no dynamic registration. No HTTP, no ports.


Define a Tool

helloTool := mcp.NewTool("hello_world",
    mcp.WithDescription("Say hello to someone"),
    mcp.WithString("name",
        mcp.Required(),
        mcp.Description("Name of the person to greet"),
    ),
)
Enter fullscreen mode Exit fullscreen mode

The description is what the AI reads to decide when to call this tool — write it clearly. The parameter is typed and required, with its own description so the AI knows what to pass.


Write the Handler

func helloHandler(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    name, err := req.RequireString("name")
    if err != nil {
        return mcp.NewToolResultError(err.Error()), nil
    }
    return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}
Enter fullscreen mode Exit fullscreen mode

RequireString reads the parameter from the request. NewToolResultError returns a soft error the AI can read and react to — the server keeps running. NewToolResultText returns the result back to the AI.


Register and Serve

s.AddTool(helloTool, helloHandler)

if err := server.ServeStdio(s); err != nil {
    fmt.Printf("Server error: %v\n", err)
}
Enter fullscreen mode Exit fullscreen mode

AddTool wires the tool to its handler. ServeStdio starts the server and blocks — Claude Code and Cursor spawn your binary as a subprocess and talk to it over stdin/stdout.


Full Code

package main

import (
    "context"
    "fmt"

    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
)

func main() {
    s := server.NewMCPServer(
        "Demo 🚀",
        "1.0.0",
        server.WithToolCapabilities(false),
    )

    helloTool := mcp.NewTool("hello_world",
        mcp.WithDescription("Say hello to someone"),
        mcp.WithString("name",
            mcp.Required(),
            mcp.Description("Name of the person to greet"),
        ),
    )

    s.AddTool(helloTool, helloHandler)

    if err := server.ServeStdio(s); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

func helloHandler(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    name, err := req.RequireString("name")
    if err != nil {
        return mcp.NewToolResultError(err.Error()), nil
    }
    return mcp.NewToolResultText(fmt.Sprintf("Hello, %s!", name)), nil
}
Enter fullscreen mode Exit fullscreen mode

Build

go build -o hello-mcp-server .
Enter fullscreen mode Exit fullscreen mode

A single self-contained binary. No runtime, no dependencies — just a path to hand to the AI client.


Connect to Claude Code

claude mcp add --scope user hello-mcp-server -- /Users/absolute/path/to/hello-mcp-server
Enter fullscreen mode Exit fullscreen mode

--scope user makes it available across all your projects. Use --scope project to share with your team via .mcp.json in git.

claude mcp list
Enter fullscreen mode Exit fullscreen mode

This command should show the hello-mcp-server in the list of mcp server of Claude code

Try it

Once connected, just type in Claude Code:

use hello_world with name John
Enter fullscreen mode Exit fullscreen mode

Connect to Cursor

Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json in your project root:

{"mcpServers":{"hello-mcp-server":{"command":"/absolute/path/to/hello-mcp-server","args":[]}}}
Enter fullscreen mode Exit fullscreen mode

Restart Cursor. Your tool will appear in the MCP tools list.


That's It

Step What
NewMCPServer Create the server
NewTool Define a tool — name, description, params
AddTool Wire the tool to its handler
ServeStdio Start serving over stdin/stdout
go build Compile to a single binary

Add more tools by repeating the define → register → handle pattern for each one.

Source: dev.to

arrow_back Back to Tutorials