Library / SDK
ThinkInAIXYZ/go-mcp avatar
ThinkInAIXYZ/go-mcp

go-mcp is a Go implementation of the Model Context Protocol for servers and clients

Go-MCP is a powerful Go(Golang) version of the MCP SDK that implements the Model Context Protocol (MCP) to facilitate seamless communication between external systems and AI applications.

678 stars108 forksGoMIT

At a glance

What is it?
go-mcp is a Go SDK that implements the Model Context Protocol, with a three-layer design covering protocol, server and client, an SSE transport, an http.Handler for web frameworks and typed tool schemas. It is MIT licensed and still on a 0.2.x release line.
Who is it for?
Adopt go-mcp if you are writing a Go service that should expose tools to an AI client, or a Go program that should consume tools, and you want the protocol layer, the transport and the web framework integration to be one dependency rather than three.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 68 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the Model Context Protocol layer is for

go-mcp is a Go implementation of the Model Context Protocol, described as a Go version of the MCP SDK that implements the protocol to facilitate communication between external systems and AI applications. The reasoning in the README is that Go's strong typing and performance characteristics produce a concise and idiomatic API for integrating external systems into AI applications.

The audience is Go developers building both halves of the protocol. On one side there is a server, which publishes tools that a model can call. On the other there is a client, which connects to a server, lists the tools it offers and calls them. The repository carries both, as separate top-level packages, and the example directories make the split visible: an auth_tool example, a current_time_server example, a filesystem_client example, an everything example, an http_handler example, a middleware_example and a tool_list_filter example.

The feature list in the README is a design summary rather than a feature dump. It claims a complete implementation of the MCP specification so the tool integrates with compatible services, a three-layer architecture supporting bidirectional communication, a protocol-compliant http.Handler so MCP can live inside an existing web service, type safety from Go's own type system, simple deployment thanks to static compilation, and a design that leans on Go's concurrency to keep resource overhead low.

Note what is not in that list. There is no hosted offering, no vendor lock-in, and no claim of a simpler protocol than MCP itself. This is an SDK, and the value on offer is how much of the plumbing you do not have to write.

Protocol, server and client as three separate layers

The architecture is the part of this SDK that will shape your code most, and the README describes it as a clear three-layer architecture with bidirectional communication.

The protocol package is the bottom layer and it holds the wire types. Tools are declared through a constructor that takes a name, a description and a request struct, and the request struct carries field tags that describe the input schema, including a required marker and a description. That is where the JSON schema for the tool arguments comes from, so you declare a tool once and the schema follows from your Go type.

The server layer sits above it and owns registration and dispatch. You construct a server over a transport, register a tool with a handler, and run it. The client layer mirrors that: you construct a client over a transport, list tools, call them and close the connection.

The dependency list in go.mod is short and tells you what the implementation chose to lean on: uuid for identifiers, a concurrent map for shared state, gjson for reading JSON without unmarshalling into a struct, and uritemplate for URI template expansion. The uritemplate dependency is a good sign of protocol fidelity, since resource templates in MCP are addressed by URI template, and a hand-rolled expander would have been a source of subtle bugs.

Because the layers are separate, the transport is swappable. The examples in the README use SSE on both sides, but the transport package is an interface boundary rather than an implementation detail of the server.

Declaring a tool and reading its arguments

The server example in the README is a working tool definition, and it is worth reading closely because it shows how the type system and the protocol schema are kept in step.

The request type carries a field tag that describes the input schema, and the tag comment in the example notes that this is how the input schema is described. A tool is then created with the protocol constructor, passing a name, a human-readable description and the request type:

go
type TimeRequest struct {
	Timezone string `json:"timezone" description:"timezone" required:"true"` // Use field tag to describe input schema
}

The tool is registered against a handler, and the handler receives the raw arguments and unmarshals them into the request type:

go
func handleTimeRequest(ctx context.Context, req *protocol.CallToolRequest) (*protocol.CallToolResult, error) {
	var timeReq TimeRequest
	if err := protocol.VerifyAndUnmarshal(req.RawArguments, &timeReq); err != nil {
		return nil, err
	}

Two details are worth noting. The handler is a plain function with a context and an error return, so it composes with ordinary Go code, and the result is a struct with a slice of content, where each element is a typed content value such as a text content with a type and a text field.

The VerifyAndUnmarshal call is doing two things at once, validating the arguments against what the tool declared and then decoding them. That is the part of the design that prevents a malformed tool call from reaching your business logic as a zero-valued struct, and it is the reason the request type doubles as the schema definition.

SSE transport, and the client that talks to it

The transport layer is SSE. The client side creates a client transport pointing at an address, then hands it to the client constructor:

go
func main() {
	// Create SSE transport client
	transportClient, err := transport.NewSSEClientTransport("http://127.0.0.1:8080/sse")
	if err != nil {
		log.Fatalf("Failed to create transport client: %v", err)
	}

The client then lists the available tools and closes cleanly when it is done:

go
	// Initialize MCP client
	mcpClient, err := client.NewClient(transportClient)
	if err != nil {
		log.Fatalf("Failed to create MCP client: %v", err)
	}
	defer mcpClient.Close()

	// Get available tools
	tools, err := mcpClient.ListTools(context.Background())
	if err != nil {
		log.Fatalf("Failed to list tools: %v", err)
	}
	log.Printf("Available tools: %+v", tools)
}

On the server side the transport is created with a listen address rather than a URL, the server is built on top of it, tools are registered and the server is run:

go
	// Register time query tool
	tool, err := protocol.NewTool("current_time", "Get current time for specified timezone", TimeRequest{})
	if err != nil {
		log.Fatalf("Failed to create tool: %v", err)
		return
	}
	mcpServer.RegisterTool(tool, handleTimeRequest)

	// Start server
	if err = mcpServer.Run(); err != nil {
		log.Fatalf("Server failed to start: %v", err)
	}
}

The address in the client example is a loopback port, which is the right shape for a local test and the wrong shape for a container that another service has to reach. The server side takes a plain host and port string, so exposing it on a real interface is a configuration decision you make rather than a mode you switch on.

Mounting MCP inside an existing Gin service

The integration example is the one that decides whether this SDK is usable in a codebase you already have. The README shows a Gin application where the SSE transport and an http.Handler are created together, the server is started in a goroutine, and the two routes are wired into the router.

The function that does this is named for what it returns, a transport and a handler, and it takes the message endpoint path. The Gin routes then delegate to it directly, one GET handler serving the SSE stream and one POST handler serving the message endpoint at the path you chose.

This is the difference between an SDK and a framework. Rather than owning your HTTP router, go-mcp hands you two handlers and lets you mount them wherever you want. The README's feature list calls this out as an http.Handler that is MCP protocol compliant, allowing developers to integrate MCP into their service frameworks.

The lifecycle is worth noting too. The server runs in its own goroutine, and a deferred shutdown call takes a context, so a graceful stop is part of the example rather than something you have to invent. There is a separate shutdown method on the server for that.

The middleware example in the repository covers the other half of running inside an existing service. The server example shows a global middleware registration that is variadic, so multiple middleware functions can be attached, with the example naming logging, auth and metrics as placeholders.

Deployment shape, tooling and what the repository does not say

Because Go compiles to a static binary, the deployment story is simple, and the README makes simple deployment one of its key features on the grounds that it eliminates complex dependency management. There is no sidecar to run and no interpreter to install.

The repository shows a serious commit-tooling setup for a project of this size. A Makefile drives the development commands, and the targets cover formatting with gofumpt and goimports, linting through golangci-lint, tests and coverage through a shell helper in the hack directory, and changelog generation with git-chglog. There is a separate init-dev target that installs git-chglog, gofumpt and goimports, and an inspector target that launches the Model Context Protocol inspector through npx, which is the practical way to exercise a server by hand.

The dependency footprint is genuinely small. go.mod pins uuid, a concurrent map, gjson and uritemplate, and requires Go 1.18 or higher, so the SDK compiles on a fairly old toolchain.

The gaps are worth naming. The README does not document transport options beyond SSE, so if you need stdio or another transport you will be reading the transport package rather than the documentation. It also does not describe authentication, session handling, rate limiting or how tool calls are authorised, which are exactly the questions a team should ask before exposing a tool server to a network. The docs directory and the example set are the places to look, and the fact that a whole example is named auth_tool suggests the topic is addressed in code rather than in prose.

Where this SDK is the wrong choice

The clearest wrong-tool case is a server you intend to expose beyond a trusted network. An MCP server hands a language model the ability to call functions on your behalf, so the interesting engineering question is not how to register a tool but how to decide which calls are allowed. The README does not document an authorisation model, so building that layer is on you, and the auth_tool example is the starting point rather than a policy you can switch on.

The second case is a deployment that is not Go. The SDK is a Go library, so a team whose agent services are written in TypeScript or Python is better served by an implementation in that language, since the protocol is the shared contract and the SDK is only one side of it. Nothing in go-mcp makes cross-language use easier; the interop happens at the wire format.

The third case is a project that wants the newest part of the protocol. The README claims a full implementation of the MCP specification, but the release history shows a 0.2.x line: v0.2.29 dated 2026-06-29, v0.2.28 dated 2026-06-15 and v0.2.27 dated 2026-05-15. A pre-1.0 version line means you should expect the API to move.

On the positive side, the license is MIT, which keeps the obligations light for commercial use as long as the notice is retained. The repository is not archived and the last push was on 2026-08-16, so the code is being changed, though the gap between that push and the newest tag is something to weigh when you pick a version.

Editorial conclusion

Adopt go-mcp if you are writing a Go service that should expose tools to an AI client, or a Go program that should consume tools, and you want the protocol layer, the transport and the web framework integration to be one dependency rather than three. Do not adopt it if you need a production-ready server without review, because the newest tagged release is v0.2.29 from 2026-06-29 and the last push was on 2026-08-16, so the version line is pre-1.0 with a licence and behaviour that can still change. Verify first that the transport you need is the SSE one the README documents, that the role you run your server under is not required to hold more privileges than the tools themselves need, and that your Go toolchain is 1.18 or higher, which is the stated minimum.

Frequently asked questions

How do I install go-mcp and what Go version do I need?

Run go get github.com/ThinkInAIXYZ/go-mcp. The README states that Go 1.18 or higher is required, and the go.mod file declares the same minimum with the module path github.com/ThinkInAIXYZ/go-mcp.

How do I define a tool and describe its input schema in go-mcp?

You declare a request struct with field tags that describe the schema, including json, description and required tags, then pass that struct to protocol.NewTool along with a name and a description, and register the tool on the server with RegisterTool. The handler receives the raw arguments and uses protocol.VerifyAndUnmarshal to validate and decode them.

Can I use go-mcp with an existing web framework such as Gin?

Yes. The README shows that transport.NewSSEServerTransportAndHandler returns a transport and an MCP compliant http.Handler, which you mount on your own routes, with one GET route for the SSE stream and one POST route for the message endpoint. The server itself can be run in a goroutine with a deferred Shutdown.

Does go-mcp support middleware on the server?

Yes. The server example shows an optional global middleware registration through a variadic Use call, so multiple middleware functions can be attached, with logging, auth and metrics given as example placeholders. The repository also contains a middleware_example directory.

What licence is go-mcp released under and how mature is it?

The project is MIT licensed, with the LICENSE file at the repository root. The recent tags are v0.2.29, v0.2.28 and v0.2.27, so the version line is still pre-1.0, and the last push was on 2026-08-16.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. ThinkInAIXYZ/go-mcp on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/thinkinaixyz-go-mcp.svg)](https://hysenlabs.com/projects/thinkinaixyz-go-mcp)

Was this useful?