- Go 99.7%
- Shell 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
## Summary Add DNS rebinding protection that is automatically enabled when requests arrive via localhost (127.0.0.1, [::1]). This protects against malicious websites using DNS rebinding to interact with local MCP servers. (note: this was a claude assisted PR, mostly wanted to see how difficult passing this test would be for this SDK) ## Design Goal: Secure by Default The primary goal is to make it difficult to run a localhost server without these protections by mistake. There are other approaches that could provide secure defaults (e.g., a helper for `ListenAndServe` that does the localhost check), but using `http.LocalAddrContextKey` for runtime detection seemed like the most backwards compatible approach and least likely to be disabled by accident. With this implementation: - **No code changes required** - existing servers get protection automatically - **No opt-in needed** - protection activates based on the connection's local address - **Explicit opt-out** - users must deliberately set `DisableLocalhostProtection: true` ## Changes - Add `DisableLocalhostProtection` option to `StreamableHTTPOptions` - Add `isLocalhostAddr` and `isLocalhostHost` helper functions - Validate Host header at start of `ServeHTTP`, rejecting non-localhost Host headers with 403 Forbidden ## How it works The protection uses `http.LocalAddrContextKey` to detect the connection's local address at runtime. When a request arrives via localhost (127.0.0.1 or [::1]), the handler validates that the Host header also matches a localhost value. If not, the request is rejected with 403 Forbidden. This approach means: - Protection is enabled for requests arriving via localhost, regardless of whether the server listens on `127.0.0.1` or `0.0.0.0` - Requests arriving via non-localhost IPs (e.g., external network requests) are **not** affected ### Edge case: Reverse proxies If a reverse proxy (e.g., Envoy, nginx) runs on the same host and forwards requests to the MCP server via localhost while preserving the original Host header, those requests would be rejected. In this case, users should either: 1. Set `DisableLocalhostProtection: true` 2. Configure the proxy to rewrite the Host header to localhost ## Testing - Added unit tests for `isLocalhostAddr` and `isLocalhostHost` helper functions - Added integration tests for the full protection flow - Verified against the MCP conformance test suite: - `localhost-host-rebinding-rejected`: PASS - `localhost-host-valid-accepted`: PASS ## Related - Spec: https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices#local-mcp-server-compromise - TypeScript SDK implementation: `localhostHostValidation()` middleware - Conformance test: `dns-rebinding-protection` scenario --------- Co-authored-by: Maciek Kisiel <mkisiel@google.com> |
||
| .devcontainer | ||
| .github | ||
| auth | ||
| conformance | ||
| design | ||
| docs | ||
| examples | ||
| internal | ||
| jsonrpc | ||
| mcp | ||
| oauthex | ||
| scripts | ||
| .gitignore | ||
| CONTRIBUTING.md | ||
| copyright_test.go | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| 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.2.0+ | 2025-06-18 | 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 |
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.