Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
<!-- Autogenerated by weave; DO NOT EDIT -->
# MCP Go SDK

***BREAKING CHANGES***

The latest version contains breaking changes:

- Server.AddTools is replaced by Server.AddTool.

- NewServerTool is replaced by AddTool. AddTool takes a Tool rather than a name and description, so you can
set any field on the Tool that you want before associating it with a handler.

- Tool options have been removed. If you don't want AddTool to infer a JSON Schema for you, you can construct one
as a struct literal, or using any other code that suits you.

- AddPrompts, AddResources and AddResourceTemplates are similarly replaced by singular methods which pair the
feature with a handler. The ServerXXX types have been removed.

[![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
Expand Down Expand Up @@ -99,7 +114,7 @@ import (
)

type HiParams struct {
Name string `json:"name"`
Name string `json:"name", mcp:"the name of the person to greet"`
}

func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[HiParams]) (*mcp.CallToolResultFor[any], error) {
Expand All @@ -111,11 +126,8 @@ func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParam
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, mcp.Input(
mcp.Property("name", mcp.Description("the name of the person to greet")),
)),
)

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.NewStdioTransport()); err != nil {
log.Fatal(err)
Expand Down
139 changes: 50 additions & 89 deletions design/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,12 +372,11 @@ A server that can handle that client call would look like this:
```go
// Create a server with a single tool.
server := mcp.NewServer("greeter", "v1.0.0", nil)
server.AddTools(mcp.NewServerTool("greet", "say hi", SayHi))
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
// Run the server over stdin/stdout, until the client disconnects.
transport := mcp.NewStdioTransport()
session, err := server.Connect(ctx, transport)
...
return session.Wait()
if err := server.Run(context.Background(), mcp.NewStdioTransport()); err != nil {
log.Fatal(err)
}
```

For convenience, we provide `Server.Run` to handle the common case of running a session until the client disconnects:
Expand Down Expand Up @@ -603,14 +602,14 @@ type ClientOptions struct {

### Tools

A `Tool` is a logical MCP tool, generated from the MCP spec, and a `ServerTool` is a tool bound to a tool handler.
A `Tool` is a logical MCP tool, generated from the MCP spec.

A tool handler accepts `CallToolParams` and returns a `CallToolResult`. However, since we want to bind tools to Go input types, it is convenient in associated APIs to make `CallToolParams` generic, with a type parameter `TArgs` for the tool argument type. This allows tool APIs to manage the marshalling and unmarshalling of tool inputs for their caller. The bound `ServerTool` type expects a `json.RawMessage` for its tool arguments, but the `NewServerTool` constructor described below provides a mechanism to bind a typed handler.
A tool handler accepts `CallToolParams` and returns a `CallToolResult`. However, since we want to bind tools to Go input types, it is convenient in associated APIs to have a generic version of `CallToolParams`, with a type parameter `In` for the tool argument type, as well as a generic version of for `CallToolResult`. This allows tool APIs to manage the marshalling and unmarshalling of tool inputs for their caller.

```go
type CallToolParams[TArgs any] struct {
type CallToolParamsFor[In any] struct {
Meta Meta `json:"_meta,omitempty"`
Arguments TArgs `json:"arguments,omitempty"`
Arguments In `json:"arguments,omitempty"`
Name string `json:"name"`
}

Expand All @@ -621,23 +620,31 @@ type Tool struct {
Name string `json:"name"`
}

type ToolHandler[TArgs] func(context.Context, *ServerSession, *CallToolParams[TArgs]) (*CallToolResult, error)
type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error)
type ToolHandler = ToolHandlerFor[map[string]any, any]
```

type ServerTool struct {
Tool Tool
Handler ToolHandler[json.RawMessage]
}
Add tools to a server with the `AddTool` method or function. The function is generic and infers schemas from the handler
arguments:

```go
func (s *Server) AddTool(t *Tool, h ToolHandler)
func AddTool[In, Out any](s *Server, t *Tool, h ToolHandlerFor[In, Out])
```

Add tools to a server with `AddTools`:
```go
mcp.AddTool(server, &mcp.Tool{Name: "add", Description: "add numbers"}, addHandler)
mcp.AddTool(server, &mcp.Tool{Name: "subtract", Description: "subtract numbers"}, subHandler)
```

The `AddTool` method requires an input schema, and optionally an output one. It will not modify them.
The handler should accept a `CallToolParams` and return a `CallToolResult`.
```go
server.AddTools(
mcp.NewServerTool("add", "add numbers", addHandler),
mcp.NewServerTool("subtract, subtract numbers", subHandler))
t := &Tool{Name: ..., Description: ..., InputSchema: &jsonschema.Schema{...}}
server.AddTool(t, myHandler)
```

Remove them by name with `RemoveTools`:
Tools can be removed by name with `RemoveTools`:

```go
server.RemoveTools("add", "subtract")
Expand All @@ -650,53 +657,30 @@ A tool's input schema, expressed as a [JSON Schema](https://json-schema.org), pr

Both of these have their advantages and disadvantages. Reflection is nice, because it allows you to bind directly to a Go API, and means that the JSON schema of your API is compatible with your Go types by construction. It also means that concerns like parsing and validation can be handled automatically. However, it can become cumbersome to express the full breadth of JSON schema using Go types or struct tags, and sometimes you want to express things that aren’t naturally modeled by Go types, like unions. Explicit schemas are simple and readable, and give the caller full control over their tool definition, but involve significant boilerplate.

We have found that a hybrid model works well, where the _initial_ schema is derived using reflection, but any customization on top of that schema is applied using variadic options. We achieve this using a `NewServerTool` helper, which generates the schema from the input type, and wraps the handler to provide parsing and validation. The schema (and potentially other features) can be customized using ToolOptions.

```go
// NewServerTool creates a Tool using reflection on the given handler.
func NewServerTool[TArgs any](name, description string, handler ToolHandler[TArgs], opts …ToolOption) *ServerTool
We provide both ways. The `jsonschema.For[T]` function will infer a schema, and it is called by the `AddTool` generic function.
Users can also call it themselves, or build a schema directly as a struct literal. They can still use the `AddTool` function to
create a typed handler, since `AddTool` doesn't touch schemas that are already present.

type ToolOption interface { /* ... */ }
```

`NewServerTool` determines the input schema for a Tool from the `TArgs` type. Each struct field that would be marshaled by `encoding/json.Marshal` becomes a property of the schema. The property is required unless the field's `json` tag specifies "omitempty" or "omitzero" (new in Go 1.24). For example, given this struct:
If the tool's `InputSchema` is nil, it is inferred from the `In` type parameter. If the `OutputSchema` is nil, it is inferred from the `Out` type parameter (unless `Out` is `any`).

For example, given this handler:
```go
struct {
Name string `json:"name"`
Count int `json:"count,omitempty"`
Choices []string
Password []byte `json:"-"`
type AddParams struct {
X int `json:"x"`
Y int `json:"y"`
}
```

"name" and "Choices" are required, while "count" is optional.

As of this writing, the only `ToolOption` is `Input`, which allows customizing the input schema of the tool using schema options. These schema options are recursive, in the sense that they may also be applied to properties.

```go
func Input(...SchemaOption) ToolOption

type Property(name string, opts ...SchemaOption) SchemaOption
type Description(desc string) SchemaOption
// etc.
```

For example:

```go
NewServerTool(name, description, handler,
Input(Property("count", Description("size of the inventory"))))
func addHandler(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[AddParams]) (*mcp.CallToolResultFor[int], error) {
return &mcp.CallToolResultFor[int]{StructuredContent: params.Arguments.X + params.Arguments.Y}, nil
}
```

The most recent JSON Schema spec defines over 40 keywords. Providing them all as options would bloat the API despite the fact that most would be very rarely used. For less common keywords, use the `Schema` option to set the schema explicitly:

You can add it to a server like this:
```go
NewServerTool(name, description, handler,
Input(Property("Choices", Schema(&jsonschema.Schema{UniqueItems: true}))))
mcp.AddTool(server, &mcp.Tool{Name: "add", Description: "add numbers"}, addHandler)
```

Schemas are validated on the server before the tool handler is called.
The input schema will be inferred from `AddParams`, and the output schema from `int`.

Since all the fields of the Tool struct are exported, a Tool can also be created directly with assignment or a struct literal.

Expand All @@ -718,27 +702,20 @@ For registering tools, we provide only `AddTools`; mcp-go's `SetTools`, `AddTool

### Prompts

Use `NewServerPrompt` to create a prompt. As with tools, prompt argument schemas can be inferred from a struct, or obtained from options.

```go
func NewServerPrompt[TReq any](name, description string,
handler func(context.Context, *ServerSession, TReq) (*GetPromptResult, error),
opts ...PromptOption) *ServerPrompt
```

Use `AddPrompts` to add prompts to the server, and `RemovePrompts`
Use `AddPrompt` to add a prompt to the server, and `RemovePrompts`
to remove them by name.

```go
type codeReviewArgs struct {
Code string `json:"code"`
}

func codeReviewHandler(context.Context, *ServerSession, codeReviewArgs) {...}
func codeReviewHandler(context.Context, *ServerSession, *mcp.GetPromptParams) (*mcp.GetPromptResult, error) {...}

server.AddPrompts(
NewServerPrompt("code_review", "review code", codeReviewHandler,
Argument("code", Description("the code to review"))))
server.AddPrompt(
&mcp.Prompt{Name: "code_review", Description: "review code"},
codeReviewHandler,
)

server.RemovePrompts("code_review")
```
Expand All @@ -757,25 +734,11 @@ type ResourceHandler func(context.Context, *ServerSession, *ReadResourceParams)

The arguments include the `ServerSession` so the handler can observe the client's roots. The handler should return the resource contents in a `ReadResourceResult`, calling either `NewTextResourceContents` or `NewBlobResourceContents`. If the handler omits the URI or MIME type, the server will populate them from the resource.

The `ServerResource` and `ServerResourceTemplate` types hold the association between the resource and its handler:

```go
type ServerResource struct {
Resource Resource
Handler ResourceHandler
}

type ServerResourceTemplate struct {
Template ResourceTemplate
Handler ResourceHandler
}
```

To add a resource or resource template to a server, users call the `AddResources` and `AddResourceTemplates` methods with one or more `ServerResource`s or `ServerResourceTemplate`s. We also provide methods to remove them.
To add a resource or resource template to a server, users call the `AddResource` and `AddResourceTemplate` methods. We also provide methods to remove them.

```go
func (*Server) AddResources(...*ServerResource)
func (*Server) AddResourceTemplates(...*ServerResourceTemplate)
func (*Server) AddResource(*Resource, ResourceHandler)
func (*Server) AddResourceTemplate(*ResourceTemplate, ResourceHandler)

func (s *Server) RemoveResources(uris ...string)
func (s *Server) RemoveResourceTemplates(uriTemplates ...string)
Expand All @@ -796,9 +759,7 @@ Here is an example:

```go
// Safely read "/public/puppies.txt".
s.AddResources(&mcp.ServerResource{
Resource: mcp.Resource{URI: "file:///puppies.txt"},
Handler: s.FileReadResourceHandler("/public")})
s.AddResource(&mcp.Resource{URI: "file:///puppies.txt"}, s.FileReadResourceHandler("/public"))
```

Server sessions also support the spec methods `ListResources` and `ListResourceTemplates`, and the corresponding iterator methods `Resources` and `ResourceTemplates`.
Expand Down
24 changes: 8 additions & 16 deletions examples/hello/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
var httpAddr = flag.String("http", "", "if set, use streamable HTTP at this address, instead of stdin/stdout")

type HiArgs struct {
Name string `json:"name"`
Name string `json:"name" mcp:"the name to say hi to"`
}

func SayHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[HiArgs]) (*mcp.CallToolResultFor[struct{}], error) {
Expand All @@ -43,21 +43,13 @@ func main() {
flag.Parse()

server := mcp.NewServer("greeter", "v0.0.1", nil)
server.AddTools(mcp.NewServerTool("greet", "say hi", SayHi, mcp.Input(
mcp.Property("name", mcp.Description("the name to say hi to")),
)))
server.AddPrompts(&mcp.ServerPrompt{
Prompt: &mcp.Prompt{Name: "greet"},
Handler: PromptHi,
})
server.AddResources(&mcp.ServerResource{
Resource: &mcp.Resource{
Name: "info",
MIMEType: "text/plain",
URI: "embedded:info",
},
Handler: handleEmbeddedResource,
})
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
server.AddPrompt(&mcp.Prompt{Name: "greet"}, PromptHi)
server.AddResource(&mcp.Resource{
Name: "info",
MIMEType: "text/plain",
URI: "embedded:info",
}, handleEmbeddedResource)

if *httpAddr != "" {
handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server {
Expand Down
6 changes: 3 additions & 3 deletions examples/sse/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ func main() {
}

server1 := mcp.NewServer("greeter1", "v0.0.1", nil)
server1.AddTools(mcp.NewServerTool("greet1", "say hi", SayHi))
mcp.AddTool(server1, &mcp.Tool{Name: "greet1", Description: "say hi"}, SayHi)

server2 := mcp.NewServer("greeter2", "v0.0.1", nil)
server2.AddTools(mcp.NewServerTool("greet2", "say hello", SayHi))
mcp.AddTool(server2, &mcp.Tool{Name: "greet2", Description: "say hello"}, SayHi)

log.Printf("MCP servers serving at %s\n", *httpAddr)
log.Printf("MCP servers serving at %s", *httpAddr)
handler := mcp.NewSSEHandler(func(request *http.Request) *mcp.Server {
url := request.URL.Path
log.Printf("Handling request for URL %s\n", url)
Expand Down
15 changes: 15 additions & 0 deletions internal/readme/README.src.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# MCP Go SDK

***BREAKING CHANGES***

The latest version contains breaking changes:

- Server.AddTools is replaced by Server.AddTool.

- NewServerTool is replaced by AddTool. AddTool takes a Tool rather than a name and description, so you can
set any field on the Tool that you want before associating it with a handler.

- Tool options have been removed. If you don't want AddTool to infer a JSON Schema for you, you can construct one
as a struct literal, or using any other code that suits you.

- AddPrompts, AddResources and AddResourceTemplates are similarly replaced by singular methods which pair the
feature with a handler. The ServerXXX types have been removed.

[![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
Expand Down
9 changes: 3 additions & 6 deletions internal/readme/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

type HiParams struct {
Name string `json:"name"`
Name string `json:"name", mcp:"the name of the person to greet"`
}

func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[HiParams]) (*mcp.CallToolResultFor[any], error) {
Expand All @@ -25,11 +25,8 @@ func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParam
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, mcp.Input(
mcp.Property("name", mcp.Description("the name of the person to greet")),
)),
)

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.NewStdioTransport()); err != nil {
log.Fatal(err)
Expand Down
Loading
Loading