Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
### PR Tips

Typically, PRs should consist of a single commit, and so should generally follow
the [rules for Go commit messages](https://go.dev/wiki/CommitMessage), with the following
changes and additions:

- Markdown is allowed.

- For a pervasive change, use "all" in the title instead of a package name.

- The PR description should provide context (why this change?) and describe the changes
at a high level. Changes that are obvious from the diffs don't need to be mentioned.
38 changes: 38 additions & 0 deletions .github/workflows/readme-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: README Check
on:
workflow_dispatch:
pull_request:
paths:
- 'internal/readme/**'
- 'README.md'

permissions:
contents: read

jobs:
readme-check:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v5
- name: Check out code
uses: actions/checkout@v4
- name: Check README is up-to-date
run: |
cd internal/readme
make
if [ -n "$(git status --porcelain)" ]; then
echo "ERROR: README.md is not up-to-date!"
echo ""
echo "The README.md file differs from what would be generated by running 'make' in internal/readme/."
echo "Please update internal/readme/README.src.md instead of README.md directly,"
echo "then run 'make' in the internal/readme/ directory to regenerate README.md."
echo ""
echo "Changes:"
git status --porcelain
echo ""
echo "Diff:"
git diff
exit 1
fi
echo "README.md is up-to-date"
19 changes: 18 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,25 @@ on:

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v5
- name: Check out code
uses: actions/checkout@v4
- name: Check formatting
run: |
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "The following files are not properly formatted:"
echo "$unformatted"
exit 1
fi
echo "All Go files are properly formatted"

test:
runs-on: ubuntu-latest
strategy:
Expand Down
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ This process is similar to the [Go proposal
process](https://github.com/golang/proposal), but is necessarily lighter weight
to accommodate the greater rate of change expected for the SDK.

### Design discussion

For open ended design discussion (anything that doesn't fall into the issue
categories above), use [GitHub
Discussions](https://github.com/modelcontextprotocol/go-sdk/discussions).
Ideally, each discussion should be focused on one aspect of the design. For
example: Tool Binding and Session APIs would be two separate discussions.
When discussions reach a consensus, they should be promoted into proposals.

## Contributing code

The project uses GitHub pull requests (PRs) to review changes.
Expand Down Expand Up @@ -96,6 +105,19 @@ copyright header following the format below:
// license that can be found in the LICENSE file.
```

### Updating the README

The top-level `README.md` file is generated from `internal/readme/README.src.md`
and should not be edited directly. To update the README:

1. Make your changes to `internal/readme/README.src.md`
2. Run `make` in the `internal/readme/` directory to regenerate `README.md`
3. Commit both files together

The CI system will automatically check that the README is up-to-date by running
`make` and verifying no changes result. If you see a CI failure about the
README being out of sync, follow the steps above to regenerate it.

## Code of conduct

This project follows the [Go Community Code of Conduct](https://go.dev/conduct).
Expand Down
36 changes: 20 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
<!-- Autogenerated by weave; DO NOT EDIT -->
# MCP Go SDK

<!-- TODO: update pkgsite links here to point to the modelcontextprotocol
module, once it exists. -->

[![PkgGoDev](https://pkg.go.dev/badge/golang.org/x/tools)](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/modelcontextprotocol/go-sdk)](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk)

This repository contains an unreleased implementation of the official Go
software development kit (SDK) for the Model Context Protocol (MCP).
Expand All @@ -15,6 +12,18 @@ proposals, but don't use it in real projects. See the issue tracker for known
issues and missing features. We aim to release a stable version of the SDK in
August, 2025.

## Design

The design doc for this SDK is at [design.md](./design/design.md), which was
initially reviewed at
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).

Further design discussion should occur in
[issues](https://github.com/modelcontextprotocol/go-sdk/issues) (for concrete
proposals) or
[discussions](https://github.com/modelcontextprotocol/go-sdk/discussions) for
open-ended discussion. See CONTRIBUTING.md for details.

## Package documentation

The SDK consists of two importable packages:
Expand Down Expand Up @@ -71,7 +80,7 @@ func main() {
log.Fatal("tool failed")
}
for _, c := range res.Content {
log.Print(c.Text)
log.Print(c.(*mcp.TextContent).Text)
}
}
```
Expand All @@ -95,14 +104,18 @@ type HiParams struct {

func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[HiParams]) (*mcp.CallToolResultFor[any], error) {
return &mcp.CallToolResultFor[any]{
Content: []*mcp.ContentBlock{mcp.NewTextContent("Hi " + params.Name)},
Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + params.Arguments.Name}},
}, nil
}

func main() {
// Create a server with a single tool.
server := mcp.NewServer("greeter", "v1.0.0", nil)
server.AddTools(mcp.NewServerTool("greet", "say hi", SayHi))
server.AddTools(
mcp.NewServerTool("greet", "say hi", SayHi, mcp.Input(
mcp.Property("name", mcp.Description("the name of the person to greet")),
)),
)
// Run the server over stdin/stdout, until the client disconnects
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
log.Fatal(err)
Expand All @@ -112,15 +125,6 @@ func main() {

The `examples/` directory contains more example clients and servers.

## Design

The design doc for this SDK is at [design.md](./design/design.md), which was
initially reviewed at
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).

Further design discussion should occur in GitHub issues. See CONTRIBUTING.md
for details.

## Acknowledgements

Several existing Go MCP SDKs inspired the development and design of this
Expand Down
33 changes: 30 additions & 3 deletions design/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ server.AddReceivingMiddleware(withLogging)

**Differences from mcp-go**: Version 0.26.0 of mcp-go defines 24 server hooks. Each hook consists of a field in the `Hooks` struct, a `Hooks.Add` method, and a type for the hook function. These are rarely used. The most common is `OnError`, which occurs fewer than ten times in open-source code.

#### Rate Limiting

Rate limiting can be configured using middleware. Please see [examples/rate-limiting](<https://github.com/modelcontextprotocol/go-sdk/tree/main/examples/rate-limiting>] for an example on how to implement this.

### Errors

With the exception of tool handler errors, protocol errors are handled transparently as Go errors: errors in server-side feature handlers are propagated as errors from calls from the `ClientSession`, and vice-versa.
Expand Down Expand Up @@ -850,7 +854,30 @@ type ClientOptions struct {

### Completion

Clients call the spec method `Complete` to request completions. Servers automatically handle these requests based on their collections of prompts and resources.
Clients call the spec method `Complete` to request completions. If a server installs a `CompletionHandler`, it will be called when the client sends a completion request.

```go
// A CompletionHandler handles a call to completion/complete.
type CompletionHandler func(context.Context, *ServerSession, *CompleteParams) (*CompleteResult, error)

type ServerOptions struct {
...
// If non-nil, called when a client sends a completion request.
CompletionHandler CompletionHandler
}
```

#### Security Considerations

Implementors of the CompletionHandler MUST adhere to the following security guidelines:

- **Validate all completion inputs**: The CompleteRequest received by the handler may contain arbitrary data from the client. Before processing, thoroughly validate all fields.

- **Implement appropriate rate limiting**: Completion requests can be high-frequency, especially in interactive user experiences. Without rate limiting, a malicious client could potentially overload the server, leading to denial-of-service (DoS) attacks. Consider applying rate limits per client session, IP address, or API key, depending on your deployment model. This can be implemented within the CompletionHandler itself or via middleware (see [Middleware](#middleware)) that wraps the handler.

- **Control access to sensitive suggestions**: Completion suggestions should only expose information that the requesting client is authorized to access. If your completion logic draws from sensitive data sources (e.g., internal file paths, user data, restricted API endpoints), ensure that the CompletionHandler performs proper authorization checks before generating or returning suggestions. This might involve checking the ServerSession context for authentication details or consulting an external authorization system.

- **Prevent completion-based information disclosure**: Be mindful that even seemingly innocuous completion suggestions can inadvertently reveal sensitive information. For example, suggesting internal system paths or confidential identifiers could be an attack vector. Ensure that the generated CompleteResult contains only appropriate and non-sensitive information for the given client and context. Avoid revealing internal data structures or error messages that could aid an attacker.

**Differences from mcp-go**: the client API is similar. mcp-go has not yet defined its server-side behavior.

Expand Down Expand Up @@ -929,7 +956,7 @@ In addition to the `List` methods, the SDK provides an iterator method for each

# Governance and Community

While the sections above propose an initial implementation of the Go SDK, MCP is evolving rapidly. SDKs need to keep pace, by implementing changes to the spec, fixing bugs, and accomodating new and emerging use-cases. This section proposes how the SDK project can be managed so that it can change safely and transparently.
While the sections above propose an initial implementation of the Go SDK, MCP is evolving rapidly. SDKs need to keep pace, by implementing changes to the spec, fixing bugs, and accommodating new and emerging use-cases. This section proposes how the SDK project can be managed so that it can change safely and transparently.

Initially, the Go SDK repository will be administered by the Go team and Anthropic, and they will be the Approvers (the set of people able to merge PRs to the SDK). The policies here are also intended to satisfy necessary constraints of the Go team's participation in the project.

Expand Down Expand Up @@ -957,7 +984,7 @@ A proposal is an issue that proposes a new API for the SDK, or a change to the s

Proposals that are straightforward and uncontroversial may be approved based on GitHub discussion. However, proposals that are deemed to be sufficiently unclear or complicated will be deferred to a regular steering meeting (see below).

This process is similar to the [Go proposal process](https://github.com/golang/proposal), but is necessarily lighter weight to accomodate the greater rate of change expected for the SDK.
This process is similar to the [Go proposal process](https://github.com/golang/proposal), but is necessarily lighter weight to accommodate the greater rate of change expected for the SDK.

### Steering meetings

Expand Down
2 changes: 1 addition & 1 deletion examples/hello/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type HiArgs struct {
func SayHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[HiArgs]) (*mcp.CallToolResultFor[struct{}], error) {
return &mcp.CallToolResultFor[struct{}]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + params.Name},
&mcp.TextContent{Text: "Hi " + params.Arguments.Name},
},
}, nil
}
Expand Down
8 changes: 8 additions & 0 deletions examples/rate-limiting/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/modelcontextprotocol/go-sdk/examples/rate-limiting

go 1.25

require (
github.com/modelcontextprotocol/go-sdk v0.0.0-20250625185707-09181c2c2e89
golang.org/x/time v0.12.0
)
8 changes: 8 additions & 0 deletions examples/rate-limiting/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/modelcontextprotocol/go-sdk v0.0.0-20250625185707-09181c2c2e89 h1:kUGBYP25FTv3ZRBhLT4iQvtx4FDl7hPkWe3isYrMxyo=
github.com/modelcontextprotocol/go-sdk v0.0.0-20250625185707-09181c2c2e89/go.mod h1:DcXfbr7yl7e35oMpzHfKw2nUYRjhIGS2uou/6tdsTB0=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
54 changes: 54 additions & 0 deletions examples/rate-limiting/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package main

import (
"context"
"errors"
"time"

"github.com/modelcontextprotocol/go-sdk/mcp"
"golang.org/x/time/rate"
)

// GlobalRateLimiterMiddleware creates a middleware that applies a global rate limit.
// Every request attempting to pass through will try to acquire a token.
// If a token cannot be acquired immediately, the request will be rejected.
func GlobalRateLimiterMiddleware[S mcp.Session](limiter *rate.Limiter) mcp.Middleware[S] {
return func(next mcp.MethodHandler[S]) mcp.MethodHandler[S] {
return func(ctx context.Context, session S, method string, params mcp.Params) (mcp.Result, error) {
if !limiter.Allow() {
return nil, errors.New("JSON RPC overloaded")
}
return next(ctx, session, method, params)
}
}
}

// PerMethodRateLimiterMiddleware creates a middleware that applies rate limiting
// on a per-method basis.
// Methods not specified in limiters will not be rate limited by this middleware.
func PerMethodRateLimiterMiddleware[S mcp.Session](limiters map[string]*rate.Limiter) mcp.Middleware[S] {
return func(next mcp.MethodHandler[S]) mcp.MethodHandler[S] {
return func(ctx context.Context, session S, method string, params mcp.Params) (mcp.Result, error) {
if limiter, ok := limiters[method]; ok {
if !limiter.Allow() {
return nil, errors.New("JSON RPC overloaded")
}
}
return next(ctx, session, method, params)
}
}
}

func main() {
server := mcp.NewServer("greeter1", "v0.0.1", nil)
server.AddReceivingMiddleware(GlobalRateLimiterMiddleware[*mcp.ServerSession](rate.NewLimiter(rate.Every(time.Second/5), 10)))
server.AddReceivingMiddleware(PerMethodRateLimiterMiddleware[*mcp.ServerSession](map[string]*rate.Limiter{
"callTool": rate.NewLimiter(rate.Every(time.Second), 5), // once a second with a burst up to 5
"listTools": rate.NewLimiter(rate.Every(time.Minute), 20), // once a minute with a burst up to 20
}))
// Run Server logic.
}
2 changes: 1 addition & 1 deletion examples/sse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type SayHiParams struct {
func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[SayHiParams]) (*mcp.CallToolResultFor[any], error) {
return &mcp.CallToolResultFor[any]{
Content: []mcp.Content{
&mcp.TextContent{Text: "Hi " + params.Name},
&mcp.TextContent{Text: "Hi " + params.Arguments.Name},
},
}, nil
}
Expand Down
2 changes: 1 addition & 1 deletion internal/jsonrpc2/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var (
// ErrUnknown should be used for all non coded errors.
ErrUnknown = NewError(-32001, "JSON RPC unknown error")
// ErrServerClosing is returned for calls that arrive while the server is closing.
ErrServerClosing = NewError(-32002, "JSON RPC server is closing")
ErrServerClosing = NewError(-32004, "JSON RPC server is closing")
// ErrClientClosing is a dummy error returned for calls initiated while the client is closing.
ErrClientClosing = NewError(-32003, "JSON RPC client is closing")
)
Expand Down
26 changes: 13 additions & 13 deletions internal/readme/README.src.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# MCP Go SDK

<!-- TODO: update pkgsite links here to point to the modelcontextprotocol
module, once it exists. -->

[![PkgGoDev](https://pkg.go.dev/badge/golang.org/x/tools)](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/modelcontextprotocol/go-sdk)](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk)

This repository contains an unreleased implementation of the official Go
software development kit (SDK) for the Model Context Protocol (MCP).
Expand All @@ -14,6 +11,18 @@ proposals, but don't use it in real projects. See the issue tracker for known
issues and missing features. We aim to release a stable version of the SDK in
August, 2025.

## Design

The design doc for this SDK is at [design.md](./design/design.md), which was
initially reviewed at
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).

Further design discussion should occur in
[issues](https://github.com/modelcontextprotocol/go-sdk/issues) (for concrete
proposals) or
[discussions](https://github.com/modelcontextprotocol/go-sdk/discussions) for
open-ended discussion. See CONTRIBUTING.md for details.

## Package documentation

The SDK consists of two importable packages:
Expand Down Expand Up @@ -41,15 +50,6 @@ with its client over stdin/stdout:

The `examples/` directory contains more example clients and servers.

## Design

The design doc for this SDK is at [design.md](./design/design.md), which was
initially reviewed at
[modelcontextprotocol/discussions/364](https://github.com/orgs/modelcontextprotocol/discussions/364).

Further design discussion should occur in GitHub issues. See CONTRIBUTING.md
for details.

## Acknowledgements

Several existing Go MCP SDKs inspired the development and design of this
Expand Down
Loading
Loading