Mirror of https://github.com/modelcontextprotocol/go-sdk — The official Go SDK for Model Context Protocol servers and clients. Maintained in collaboration with Google.
  • Go 99.7%
  • Shell 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
José M. Requena Plens 826e653c27
mcp: refuse a call on a legacy session before initialize (#1273)
Closes #1271.

`Server.handle` refuses a call made before `initialize` on a legacy
session, and that refusal lives in the `default` branch of its method
switch, so any method with a `case` of its own skips it. Three are
served on a session that never handshook: `logging/setLevel`,
`resources/subscribe` and `resources/unsubscribe`.

`resources/subscribe` is the one that reaches state rather than merely
answering: it starts a watcher and registers a subscriber, so a client
that subscribes before `initialize` is delivered
`notifications/resources/updated` for the lifetime of a session the
server never agreed to.

The check now runs after the switch, for every call carrying no
new-protocol `_meta`, with `initialize` and `ping` exempt. That is the
same condition the `default` branch already applied and the same error
it produced; the change is that the three methods above stop skipping
it.

`ping` stays allowed because the lifecycle page exempts it.
`server/discover` is deliberately left alone: a legacy request there
already receives method-not-found, which tells the client more than an
initialization error would. The two notification cases stay outside,
since the gate covers calls and a notification carries no refusal.

The SEP-2575 path is untouched. A session without an `initialize`
handshake is exactly what that proposal is for, so the exemption is the
request's own `_meta.protocolVersion` rather than anything about the
session, and a new-protocol client is served as it is today.

Tests: `TestServerHandle_LegacyCallBeforeInitialize` drives each
affected method on a fresh session and asserts the refusal, that
`resources/subscribe` runs no handler and leaves no subscriber, and that
`initialize` and `ping` are served.
`TestServerHandle_NewProtocolCallWithoutInitialize` asserts a
new-protocol session with no handshake still gets `tools/list`, and that
the three methods keep their existing method-not-found rather than the
initialization refusal. Without the change, the setLevel, subscribe and
unsubscribe rows fail.

`go test ./mcp/ -count=1` is green, `gofmt -l` is empty and `go vet
./mcp/` is clean. This does not depend on #1266 and rebases onto it
cleanly, verified with the suite green on the combination.

One decision worth your view: the refusal is a plain error, which on the
wire carries code 0, exactly as the `default` branch produced before
this change. The specification prescribes no code for it. If you would
rather it were `-32600` or `-32603`, say so and I will change it here.

---------

Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
2026-09-17 10:36:23 +02:00
.agents/skills/fix-bug chore: configure a simple AGENTS.md file and a skill for fixing GitHu… (#810) 2026-02-19 15:22:12 +01:00
.devcontainer feat(devcontainer): Adds Dev Container configuration (#154) 2025-07-30 12:54:41 -04:00
.github mcp: update conformance tests (#1231) 2026-09-03 11:27:42 +02:00
auth auth: strip a terminating slash from the issuer before building metadata URLs (#1245) 2026-09-07 09:31:45 +00:00
conformance mcp: update conformance tests (#1231) 2026-09-03 11:27:42 +02:00
design feat: multi-round-trip request implementation (SEP-2322) (#950) 2026-05-29 09:29:34 +02:00
docs mcp: preserve structured content number precision (#1239) 2026-09-16 09:22:16 +02:00
examples mcp: add HasParams so middleware can tell whether params are present (#1269) 2026-09-14 18:45:48 +02:00
internal mcp: preserve structured content number precision (#1239) 2026-09-16 09:22:16 +02:00
jsonrpc all: expose JSON-RPC error codes, and support URLElicitationRequired errors (#655) 2025-12-01 11:29:17 -05:00
mcp mcp: refuse a call on a legacy session before initialize (#1273) 2026-09-17 10:36:23 +02:00
oauthex oauthex: oauth discovery checks (#1220) 2026-09-02 21:07:19 +02:00
scripts ci: fix nightly runs (#1086) 2026-07-14 16:24:47 +02:00
.gitignore .gitignore: add 2025-08-11 08:33:50 -04:00
AGENTS.md chore: configure a simple AGENTS.md file and a skill for fixing GitHu… (#810) 2026-02-19 15:22:12 +01:00
CONTRIBUTING.md mcp: add docs for tier 1 assessment (#1195) 2026-08-24 12:00:59 +02:00
copyright_test.go mcp: update JSON marshaling to not HTML-escape messages (#769) 2026-01-30 17:13:30 +01:00
go.mod feat: multi-round-trip request implementation (SEP-2322) (#950) 2026-05-29 09:29:34 +02:00
go.sum feat: multi-round-trip request implementation (SEP-2322) (#950) 2026-05-29 09:29:34 +02:00
LICENSE chore: update licensing to Apache 2.0 for new contributions (#750) 2026-01-28 09:17:41 +01:00
mkdocs.yaml Add documentation backed by mkdocs (#883) 2026-04-09 14:25:43 +02:00
README.md mcp: add documentation for new protocol version (#1019) 2026-06-23 17:07:38 +02:00
ROADMAP.md chore: add ROADMAP.md (#788) 2026-02-13 10:39:53 +01:00
SECURITY.md chore: update SECURITY.md to use GitHub Security Advisories (#809) 2026-02-18 22:24:53 +01:00

MCP Go SDK

Open in GitHub Codespaces

PkgGoDev OpenSSF Scorecard

This repository contains an implementation of the official Go software development kit (SDK) for the Model Context Protocol (MCP).

Package / Feature documentation

The SDK consists of several importable packages:

The SDK endeavors to implement the full MCP spec. The docs/ directory contains feature documentation, mapping the MCP spec to the packages above.

Version Compatibility

The following table shows which versions of the Go SDK support which versions of the MCP specification:

SDK Version Latest MCP Spec All Supported MCP Specs
v1.7.0+ 2026-07-28 2026-07-28, 2025-11-25*, 2025-06-18, 2025-03-26, 2024-11-05
v1.4.0 - v1.6.1 2025-11-25* 2025-11-25*, 2025-06-18, 2025-03-26, 2024-11-05
v1.2.0 - v1.3.1 2025-11-25** 2025-11-25**, 2025-06-18, 2025-03-26, 2024-11-05
v1.0.0 - v1.1.0 2025-06-18 2025-06-18, 2025-03-26, 2024-11-05

* Client side OAuth has experimental support.

** Partial support for 2025-11-25 (client side OAuth and Sampling with tools not available).

The roots, sampling, and logging features are deprecated as of protocol version 2026-07-28 by SEP-2577. The SDK continues to support them for compatibility during the deprecation window (at least twelve months). See the individual feature documentation for migration guidance.

New releases of the SDK target only supported versions of Go. See https://go.dev/doc/devel/release#policy for more information.

Getting started

To get started creating an MCP server, create an mcp.Server instance, add features to it, and then run it over an mcp.Transport. For example, this server adds a single simple tool, and then connects it to clients over stdin/stdout:

package main

import (
	"context"
	"log"

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

type Input struct {
	Name string `json:"name" jsonschema:"the name of the person to greet"`
}

type Output struct {
	Greeting string `json:"greeting" jsonschema:"the greeting to tell to the user"`
}

func SayHi(ctx context.Context, req *mcp.CallToolRequest, input Input) (
	*mcp.CallToolResult,
	Output,
	error,
) {
	return nil, Output{Greeting: "Hi " + input.Name}, nil
}

func main() {
	// Create a server with a single tool.
	server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
	mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
	// Run the server over stdin/stdout, until the client disconnects.
	if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
		log.Fatal(err)
	}
}

To communicate with that server, create an mcp.Client and connect it to the corresponding server, by running the server command and communicating over its stdin/stdout:

package main

import (
	"context"
	"log"
	"os/exec"

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

func main() {
	ctx := context.Background()

	// Create a new client, with no features.
	client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)

	// Connect to a server over stdin/stdout.
	transport := &mcp.CommandTransport{Command: exec.Command("myserver")}
	session, err := client.Connect(ctx, transport, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer session.Close()

	// Call a tool on the server.
	params := &mcp.CallToolParams{
		Name:      "greet",
		Arguments: map[string]any{"name": "you"},
	}
	res, err := session.CallTool(ctx, params)
	if err != nil {
		log.Fatalf("CallTool failed: %v", err)
	}
	if res.IsError {
		log.Fatal("tool failed")
	}
	for _, c := range res.Content {
		log.Print(c.(*mcp.TextContent).Text)
	}
}

The examples/ directory contains more example clients and servers.

Contributing

We welcome contributions to the SDK! Please see CONTRIBUTING.md for details of how to contribute.

Acknowledgements / Alternatives

Several third party Go MCP SDKs inspired the development and design of this official SDK, and continue to be viable alternatives, notably mcp-go, originally authored by Ed Zynda. We are grateful to Ed as well as the other contributors to mcp-go, and to authors and contributors of other SDKs such as mcp-golang and go-mcp. Thanks to their work, there is a thriving ecosystem of Go MCP clients and servers.

License

This project is licensed under Apache 2.0 for new contributions, with existing code under MIT - see the LICENSE file for details.