AI & Agents

How to Build an MCP Server in Go

Go is a strong fit for MCP servers thanks to its compiled performance, lightweight goroutines, and simple deployment as a single binary. This guide walks through the available Go SDKs, builds a working MCP server from scratch, and covers transport options, concurrency patterns, and production deployment.

Fast.io Editorial Team 9 min read
Abstract visualization of AI agent architecture and tool connections

What Is an MCP Server?

An MCP server is a process that exposes tools, resources, and prompts to AI models through the Model Context Protocol. It handles JSON-RPC 2.0 communication between an AI client (like Claude Desktop, Cursor, or a custom agent) and your application logic. The server registers capabilities, such as a "search files" tool or a "read database" resource, and the AI model calls them as needed during a conversation. The protocol standardizes this interaction so any MCP-compatible client can connect to any MCP server, regardless of language or framework. Go works well for this. Its compiled binaries start instantly, goroutines handle concurrent tool calls without thread overhead, and the type system catches protocol mismatches at compile time rather than at runtime.

What to check before scaling mcp server golang

Three options exist for building MCP servers in Go, each with different trade-offs.

Official Go SDK (modelcontextprotocol/go-sdk)

Maintained by the MCP project in collaboration with Google, this is the canonical implementation. It provides typed server scaffolding, automatic JSON schema generation from Go structs, and built-in support for stdio and command transports. The API design follows Go conventions closely. ``` go get github.com/modelcontextprotocol/go-sdk


Best for: Teams that want to stay close to the spec and prefer an officially maintained library.

**mcp-go (`mark3labs/mcp-go`)**

The most popular community SDK with less boilerplate. It supports four transports out of the box: stdio, Streamable HTTP, SSE, and in-process. The API is slightly more opinionated but gets you to a working server faster. ```
go get github.com/mark3labs/mcp-go

Best for: Developers who want quick setup and HTTP-based transports.

mcp-golang (metoro-io/mcp-golang)

An alternative community implementation with its own approach to tool registration and schema handling. It provides a solid framework but has a smaller community compared to the other two options. Best for: Projects that align with its specific API style. For this guide, we will use the official SDK for the primary examples and note mcp-go differences where they matter.

Dashboard showing AI-powered tool integration and server management

Building a Minimal MCP Server

Let's build a working MCP server that exposes a single tool. Start with a new Go module:

mkdir my-mcp-server && cd my-mcp-server
go mod init my-mcp-server
go get github.com/modelcontextprotocol/go-sdk

Create main.go:

package main

import (
    "context"
    "fmt"

"github.com/modelcontextprotocol/go-sdk/mcp"
)

type GreetInput struct {
    Name string `json:"name" jsonschema:"description=The name to greet"`
}

func main() {
    server := mcp.NewServer(
        &mcp.Implementation{
            Name:    "greeting-server",
            Version: "1.0.0",
        },
        nil,
    )

mcp.AddTool(server, &mcp.Tool{
        Name:        "greet",
        Description: "Returns a greeting for the given name",
    }, func(ctx context.Context, req *mcp.CallToolRequest, input GreetInput) (*mcp.CallToolResult, error) {
        return &mcp.CallToolResult{
            Content: []mcp.Content{
                mcp.TextContent(fmt.Sprintf("Hello, %s!", input.Name)),
            },
        }, nil
    })

if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
        fmt.Printf("Server error: %v
", err)
    }
}

Build and test it:

go build -o greeting-server . ```

This produces a single binary. The SDK automatically generates the JSON schema from the `GreetInput` struct, so the AI client knows what parameters to send. No separate schema definition needed.

### Connecting to Claude Desktop

Add your server to Claude Desktop's configuration file (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "greeting": {
      "command": "/path/to/greeting-server"
    }
  }
}

Claude Desktop will start the binary and communicate over stdio.

Fast.io features

Give Your AI Agents Persistent Storage

Fast.io's MCP server gives your agent 251 pre-built tools for file management, RAG, and collaboration. 50GB free, no credit card.

Transport Options

MCP supports multiple transport mechanisms. Your choice depends on deployment context.

Stdio is the default for local integrations. The AI client spawns your server as a child process and communicates through stdin/stdout. It's the simplest option and works with Claude Desktop, Cursor, and VS Code out of the box.

Streamable HTTP is the right choice for remote or shared servers. The client connects over HTTP, and the server can stream responses back. With mcp-go, setup looks like this:

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

srv := server.NewStreamableHTTPServer(mcpServer)
srv.Start(":8080")

Server-Sent Events (SSE) provides another HTTP-based option, useful when you need one-way streaming from server to client. Both the official SDK and mcp-go support this.

In-Process transport connects a client and server within the same Go binary. This is useful for testing or when embedding MCP capabilities directly in a larger application.

Transport Use Case Network Required
Stdio Local desktop integrations No
Streamable HTTP Remote/shared servers Yes
SSE Event streaming scenarios Yes
In-Process Testing, embedded use No

For production deployments serving multiple clients, Streamable HTTP gives you the most flexibility. Fast.io's own MCP server uses Streamable HTTP and SSE transports, handling 251 tools across file storage, AI chat, and collaboration operations.

Adding Real Tools: File Operations Example

A greeting tool is nice for learning, but real MCP servers need to do real work. Here's a more practical example that exposes file listing and reading capabilities:

type ListFilesInput struct {
    Directory string `json:"directory" jsonschema:"description=Directory path to list"`
}

type ReadFileInput struct {
    Path string `json:"path" jsonschema:"description=File path to read"`
}

func addFileTools(server *mcp.Server) {
    mcp.AddTool(server, &mcp.Tool{
        Name:        "list_files",
        Description: "List files in a directory",
    }, func(ctx context.Context, req *mcp.CallToolRequest, input ListFilesInput) (*mcp.CallToolResult, error) {
        entries, err := os.ReadDir(input.Directory)
        if err != nil {
            return nil, fmt.Errorf("reading directory: %w", err)
        }

var names []string
        for _, e := range entries {
            names = append(names, e.Name())
        }

return &mcp.CallToolResult{
            Content: []mcp.Content{
                mcp.TextContent(strings.Join(names, "
")),
            },
        }, nil
    })

mcp.AddTool(server, &mcp.Tool{
        Name:        "read_file",
        Description: "Read the contents of a file",
    }, func(ctx context.Context, req *mcp.CallToolRequest, input ReadFileInput) (*mcp.CallToolResult, error) {
        data, err := os.ReadFile(input.Path)
        if err != nil {
            return nil, fmt.Errorf("reading file: %w", err)
        }

return &mcp.CallToolResult{
            Content: []mcp.Content{
                mcp.TextContent(string(data)),
            },
        }, nil
    })
}

Each tool gets its own typed input struct, its own handler function, and automatic schema generation. The AI model sees the tool descriptions and parameter schemas, then calls them with the right arguments. For production file operations, consider using an existing MCP server rather than building from scratch. Fast.io's MCP server provides 251 pre-built tools for file management, including uploads, downloads, search, versioning, and AI-powered document queries, all accessible through Streamable HTTP.

Visualization of concurrent AI processing and neural indexing

Concurrency Patterns for Go MCP Servers

Go's concurrency model is one of the main reasons to pick it over Python or Node.js for MCP servers. Here are the patterns that matter most.

Parallel Tool Execution

When an AI client sends multiple tool calls in a single request, you can execute them concurrently:

func handleBatchTools(ctx context.Context, calls []ToolCall) []ToolResult {
    results := make([]ToolResult, len(calls))
    var wg sync.WaitGroup

for i, call := range calls {
        wg.Add(1)
        go func(idx int, c ToolCall) {
            defer wg.Done()
            results[idx] = executeTool(ctx, c)
        }(i, call)
    }

wg.Wait()
    return results
}

Goroutines are cheap (a few KB of stack each), so spinning up one per tool call costs almost nothing compared to Python threads or Node.js worker threads.

Rate Limiting External APIs

If your tools call external APIs, use a semaphore pattern to limit concurrent requests:

var apiSem = make(chan struct{}, 10) // max 10 concurrent API calls

func callExternalAPI(ctx context.Context, url string) ([]byte, error) {
    apiSem <- struct{}{}
    defer func() { <-apiSem }()

req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
    resp, err := http.DefaultClient.Do(req)
    // handle response... }

Context Cancellation MCP clients can cancel in-flight requests. Always pass the context through to blocking operations:

func handler(ctx context.Context, req *mcp.CallToolRequest, input MyInput) (*mcp.CallToolResult, error) {
    select {
    case <-ctx.Done():
        return nil, ctx.Err()
    default:
    }

result, err := doExpensiveWork(ctx, input)
    if err != nil {
        return nil, err
    }

return &mcp.CallToolResult{
        Content: []mcp.Content{mcp.TextContent(result)},
    }, nil
}

These patterns are straightforward in Go but require external libraries (asyncio, threading) in Python. For high-throughput tool serving where multiple AI sessions call tools simultaneously, Go's concurrency gives you a real advantage.

Deploying Your Go MCP Server

Go's single-binary compilation simplifies deployment compared to Python (virtualenvs) or Node.js (node_modules).

Building for Production Cross-compile for your target platform:

GOOS=linux GOARCH=amd64 go build -o mcp-server-linux . GOOS=darwin GOARCH=arm64 go build -o mcp-server-mac . ```

### Docker Deployment

For remote servers using HTTP transport, a minimal Dockerfile works:

```dockerfile
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . . RUN CGO_ENABLED=0 go build -o /mcp-server . FROM alpine:3.19
COPY --from=builder /mcp-server /mcp-server
EXPOSE 8080
CMD ["/mcp-server"]

The final image is typically under 20MB since you only ship the compiled binary.

Running as a Systemd Service

For persistent server deployments on Linux:

[Unit]
Description=MCP Server
After=network.target

[Service]
ExecStart=/usr/local/bin/mcp-server
Restart=always
Environment=MCP_PORT=8080

[Install]
WantedBy=multi-user.target

Connecting to Existing MCP Servers

You don't always need to build your own server. If your agent needs file storage, document querying, or collaboration features, connecting to an existing MCP server saves weeks of development. Fast.io provides a production MCP server at mcp.fast.io with 251 tools covering file operations, workspace management, AI-powered RAG, and branded sharing. It works with any MCP client and offers a free agent tier with 50GB storage, no credit card required. Your Go agent can connect to it as a client while also running its own MCP server for custom tools.

Frequently Asked Questions

How do I build an MCP server in Go?

Install the official Go SDK with `go get github.com/modelcontextprotocol/go-sdk`, create a server with `mcp.NewServer()`, register tools using `mcp.AddTool()` with typed input structs, and run it with a transport like `mcp.StdioTransport{}`. Build the binary with `go build` and point your MCP client at it.

Is there a Go SDK for MCP?

Yes. The official SDK is at `github.com/modelcontextprotocol/go-sdk`, maintained by the MCP project in collaboration with Google. It provides typed tool handlers, automatic JSON schema generation from Go structs, and supports stdio and command transports. The community-maintained `mark3labs/mcp-go` adds HTTP and SSE transports.

What are the best Go MCP libraries?

The top three options are the official SDK (`modelcontextprotocol/go-sdk`) for spec compliance, mcp-go (`mark3labs/mcp-go`) for HTTP transports and quick setup, and mcp-golang (`metoro-io/mcp-golang`) as an alternative community option. Most developers choose between the official SDK and mcp-go depending on whether they need stdio-only or HTTP-based transports.

How does Go compare to Python for MCP servers?

Go MCP servers compile to a single binary with no runtime dependencies, start in milliseconds, and handle concurrent tool calls natively through goroutines. Python servers require a virtual environment, start slower, and face GIL limitations for CPU-bound tool handlers. Go is the better choice for high-throughput or latency-sensitive MCP deployments. Python wins on ecosystem breadth and prototyping speed.

Can I use Streamable HTTP transport with a Go MCP server?

Yes. The mcp-go library (`mark3labs/mcp-go`) supports Streamable HTTP natively with `server.NewStreamableHTTPServer()`. This lets remote clients connect to your server over HTTP, which is necessary for shared or cloud-deployed MCP servers. The official SDK currently focuses on stdio and command transports.

Related Resources

Fast.io features

Give Your AI Agents Persistent Storage

Fast.io's MCP server gives your agent 251 pre-built tools for file management, RAG, and collaboration. 50GB free, no credit card.