- Go 99.7%
- Shell 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
An issuer identifier written with a terminating slash, like `https://auth.example.com/`, makes authorization server metadata discovery miss every endpoint. `GetAuthServerMetadata` probes three URLs that no conformant server serves, finds nothing, and the caller falls back to the guessed 2025-03-26 endpoints, which are themselves malformed because they are built by appending to the same slash-terminated issuer. `authorizationServerMetadataURLs` in `auth/shared.go` decides whether the issuer has a path component with `baseURL.Path == ""` (line 47), and a trailing-slash issuer has `Path == "/"`, so it takes the path-insertion branch. That branch inserts `strings.TrimLeft(originalPath, "/")` (lines 59 and 62), which strips the leading slash but keeps the trailing one. For `https://auth.example.com/` the probes come out as `/.well-known/oauth-authorization-server/`, `/.well-known/openid-configuration/` and `//.well-known/openid-configuration`. For `https://auth.example.com/tenant1/` the two path-insertion probes come out with a trailing slash. Only the path-appending form was already right, because it uses `strings.Trim`. RFC 8414 section 3.1 settles it: "If the issuer identifier value contains a path component, any terminating "/" MUST be removed before inserting "/.well-known/" and the well-known URI suffix between the host component and the path component." So `https://auth.example.com/` has no path component and gets the two root forms, which is the list the MCP authorization spec gives for issuers without a path component. The change trims slashes off both ends of the issuer path once, uses that to pick between the root and path-insertion branches, and reuses it for all three URLs. An issuer without a terminating slash produces exactly the same list as before. `TestAuthorizationServerMetadataURLs` pins the URL list for all four issuer shapes. `TestGetAuthServerMetadataTrailingSlashIssuer` drives the whole lookup against the existing fake authorization server, serving metadata at the root well-known path. On unmodified main the first fails on both trailing-slash cases and the second fails with `GetAuthServerMetadata() error = Get "/.well-known/openid-configuration": redirect into loopback address "127.0.0.1"`, because `//.well-known/openid-configuration` makes `net/http.ServeMux` issue a canonicalizing redirect that the discovery client's `CheckRedirect` refuses. Both pass with the change. Verification on go1.26.4: `gofmt -l .` clean, `go vet ./...` clean, `staticcheck ./...` clean at v0.6.1 (the version the Test workflow pins), `go test ./auth/...` ok, `go test ./...` ok. I did not file an issue first. The fix is self-contained, confined to one unexported helper, and every issuer shape other than the slash-terminated ones is byte-identical to before. Co-authored-by: Guglielmo Colombo <guglielmoc@google.com> |
||
| .agents/skills/fix-bug | ||
| .devcontainer | ||
| .github | ||
| auth | ||
| conformance | ||
| design | ||
| docs | ||
| examples | ||
| internal | ||
| jsonrpc | ||
| mcp | ||
| oauthex | ||
| scripts | ||
| .gitignore | ||
| AGENTS.md | ||
| CONTRIBUTING.md | ||
| copyright_test.go | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| mkdocs.yaml | ||
| README.md | ||
| ROADMAP.md | ||
| SECURITY.md | ||
MCP Go SDK
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
github.com/modelcontextprotocol/go-sdk/mcppackage defines the primary APIs for constructing and using MCP clients and servers. - The
github.com/modelcontextprotocol/go-sdk/jsonrpcpackage is for users implementing their own transports. - The
github.com/modelcontextprotocol/go-sdk/authpackage provides some primitives for supporting OAuth. - The
github.com/modelcontextprotocol/go-sdk/oauthexpackage provides extensions to the OAuth protocol, such as ProtectedResourceMetadata.
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.