diff --git a/README.md b/README.md index c4a9ac6..f545645 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,28 @@ + # MCP SDK prototype [![PkgGoDev](https://pkg.go.dev/badge/golang.org/x/tools)](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp) +# Contents + +1. [Installation](#installation) +1. [Quickstart](#quickstart) +1. [Design](#design) +1. [Testing](#testing) +1. [Code of Conduct](#code-of-conduct) +1. [License](#license) + The mcp package provides a software development kit (SDK) for writing clients and servers of the [model context protocol](https://modelcontextprotocol.io/introduction). It is unstable, and will change in breaking ways in the future. As of writing, it is a prototype to explore the design space of client/server transport and binding. -## Installation +# Installation The mcp package is currently internal and cannot be imported using `go get`. -## Quickstart +# Quickstart Here's an example that creates a client that talks to an MCP server running as a sidecar process: @@ -35,15 +45,20 @@ func main() { // Connect to a server over stdin/stdout transport := mcp.NewCommandTransport(exec.Command("myserver")) session, err := client.Connect(ctx, transport) - if err != nil { + if err != nil { log.Fatal(err) } - defer session.Close() + defer session.Close() // Call a tool on the server. - if content, err := session.CallTool(ctx, "greet", map[string]any{"name": "you"}, nil); err != nil { + if res, err := session.CallTool(ctx, "greet", map[string]any{"name": "you"}, nil); err != nil { log.Printf("CallTool failed: %v", err) } else { - log.Printf("CallTool returns: %v", content) + if res.IsError { + log.Print("tool failed") + } + for _, c := range res.Content { + log.Print(c.Text) + } } } ``` @@ -78,13 +93,13 @@ func main() { } ``` -## Design +# Design See [design.md](./design/design.md) for the SDK design. That document is canonical: given any divergence between the design doc and this prototype, the doc reflects the latest design. -## Testing +# Testing To test your client or server using stdio transport, you can use an in-memory transport. See [example](server_example_test.go). @@ -92,12 +107,12 @@ transport. See [example](server_example_test.go). To test your client or server using sse transport, you can use the [httptest](https://pkg.go.dev/net/http/httptest) package. See [example](sse_example_test.go). -## Code of Conduct +# Code of Conduct This project follows the [Go Community Code of Conduct](https://go.dev/conduct). If you encounter a conduct-related issue, please mail conduct@golang.org. -## License +# License Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the [LICENSE](../../LICENSE) file. diff --git a/internal/readme/Makefile b/internal/readme/Makefile new file mode 100644 index 0000000..5e48418 --- /dev/null +++ b/internal/readme/Makefile @@ -0,0 +1,19 @@ +# This makefile builds ../README.md from the files in this directory. + +OUTFILE=../../README.md + +$(OUTFILE): build README.src.md + go run golang.org/x/example/internal/cmd/weave@latest README.src.md > $(OUTFILE) + +# Compile all the code used in the README. +build: $(wildcard */*.go) + go build -o /tmp/mcp-readme/ ./... + +# Preview the README on GitHub. +# $HOME/markdown must be a github repo. +# Visit https://github.com/$HOME/markdown to see the result. +preview: $(OUTFILE) + cp $(OUTFILE) $$HOME/markdown/ + (cd $$HOME/markdown/ && git commit -m . README.md && git push) + +.PHONY: build preview diff --git a/internal/readme/README.src.md b/internal/readme/README.src.md new file mode 100644 index 0000000..998b720 --- /dev/null +++ b/internal/readme/README.src.md @@ -0,0 +1,57 @@ +# MCP SDK prototype + +[![PkgGoDev](https://pkg.go.dev/badge/golang.org/x/tools)](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp) + +# Contents + +%toc + +The mcp package provides a software development kit (SDK) for writing clients +and servers of the [model context +protocol](https://modelcontextprotocol.io/introduction). It is unstable, and +will change in breaking ways in the future. As of writing, it is a prototype to +explore the design space of client/server transport and binding. + +# Installation + +The mcp package is currently internal and cannot be imported using `go get`. + +# Quickstart + +Here's an example that creates a client that talks to an MCP server running +as a sidecar process: + +%include client/client.go - + +Here is an example of the corresponding server, connected over stdin/stdout: + +%include server/server.go - + +# Design + +See [design.md](./design/design.md) for the SDK design. That document is +canonical: given any divergence between the design doc and this prototype, the +doc reflects the latest design. + +# Testing + +To test your client or server using stdio transport, you can use an in-memory +transport. See [example](server_example_test.go). + +To test your client or server using sse transport, you can use the [httptest](https://pkg.go.dev/net/http/httptest) +package. See [example](sse_example_test.go). + +# Code of Conduct + +This project follows the [Go Community Code of Conduct](https://go.dev/conduct). +If you encounter a conduct-related issue, please mail conduct@golang.org. + +# License + +Unless otherwise noted, the Go source files are distributed under the BSD-style +license found in the [LICENSE](../../LICENSE) file. + +Upon a potential move to the +[modelcontextprotocol](https://github.com/modelcontextprotocol) organization, +the license will be updated to the MIT License, and the license header will +reflect the Go MCP SDK Authors. diff --git a/internal/readme/build.sh b/internal/readme/build.sh new file mode 100755 index 0000000..354c46c --- /dev/null +++ b/internal/readme/build.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Build README.md from the files in this directory. +# Must be invoked from the internal/cmp directory. + +cd internal/readme + +outfile=../../README.md + +# Compile all the code used in the README. +go build -o /tmp/mcp-readme/ ./... +# Combine the code with the text in README.src.md. +# TODO: when at Go 1.24, use a tool directive for weave. +go run golang.org/x/example/internal/cmd/weave@latest README.src.md > $outfile + +if [[ $1 = '-preview' ]]; then + # Preview the README on GitHub. + # $HOME/markdown must be a github repo. + # Visit https://github.com/$HOME/markdown to see the result. + cp $outfile $HOME/markdown/ + (cd $HOME/markdown/ && git commit -m . README.md && git push) +fi diff --git a/internal/readme/client/client.go b/internal/readme/client/client.go new file mode 100644 index 0000000..ffe7ec6 --- /dev/null +++ b/internal/readme/client/client.go @@ -0,0 +1,40 @@ +// 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" + "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-client", "v1.0.0", nil) + // Connect to a server over stdin/stdout + transport := mcp.NewCommandTransport(exec.Command("myserver")) + session, err := client.Connect(ctx, transport) + if err != nil { + log.Fatal(err) + } + defer session.Close() + // Call a tool on the server. + if res, err := session.CallTool(ctx, "greet", map[string]any{"name": "you"}, nil); err != nil { + log.Printf("CallTool failed: %v", err) + } else { + if res.IsError { + log.Print("tool failed") + } + for _, c := range res.Content { + log.Print(c.Text) + } + } +} + +//!- diff --git a/internal/readme/server/server.go b/internal/readme/server/server.go new file mode 100644 index 0000000..d63da8d --- /dev/null +++ b/internal/readme/server/server.go @@ -0,0 +1,32 @@ +// 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" + + "github.com/modelcontextprotocol/go-sdk/mcp" +) + +type HiParams struct { + Name string `json:"name"` +} + +func SayHi(ctx context.Context, cc *mcp.ServerSession, params *HiParams) ([]*mcp.Content, error) { + return []*mcp.Content{ + mcp.NewTextContent("Hi " + params.Name), + }, nil +} + +func main() { + // Create a server with a single tool. + server := mcp.NewServer("greeter", "v1.0.0", nil) + server.AddTools(mcp.NewTool("greet", "say hi", SayHi)) + // Run the server over stdin/stdout, until the client diconnects + _ = server.Run(context.Background(), mcp.NewStdIOTransport()) +} + +// !- diff --git a/mcp/mcp.go b/mcp/mcp.go index b2b71f1..e8ccd4d 100644 --- a/mcp/mcp.go +++ b/mcp/mcp.go @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. //go:generate go run generate.go +//go:generate ./internal/readme/build.sh // The mcp package provides an SDK for writing model context protocol clients // and servers.