go-sdk/examples/hello/main.go
Jonathan Amsterdam 3da831b0c9
mcp: move to 2025-06-18 schema (#8)
* mcp: move to 2025-06-18 schema

Add types and fields for the latest MCP schema.

The main challenge here was dealing with the presence of _meta fields
on many types, like Tool, Prompt and Resource.

- Distinguish between the _meta field of a request's params, which
  includes a progress token, from the _meta field on other types,
  which is simply a map.

- Redefine GetMeta to return a pointer to the map. We wanted to avoid
  that, but I don't see a clean way to do so. An alternative would be
  to have GetMeta return a map, and SetMeta to set it.

- Provide GetProgressToken for requests only (not notifications or other
  types).

- Still to do: deal with the meta and annotation fields of ContentBlock,
  which are currently duplicated for Resource and ResourceContents.

* Meta and progress token redesign
2025-06-25 09:48:33 -04:00

98 lines
2.7 KiB
Go

// 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"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"os"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
var httpAddr = flag.String("http", "", "if set, use streamable HTTP at this address, instead of stdin/stdout")
type HiArgs struct {
Name string `json:"name"`
}
func SayHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[HiArgs]) (*mcp.CallToolResultFor[struct{}], error) {
return &mcp.CallToolResultFor[struct{}]{
Content: []*mcp.ContentBlock{
mcp.NewTextContent("Hi " + params.Name),
},
}, nil
}
// TODO(jba): it should be OK for args to be a pointer, but this fails in
// jsonschema. Needs investigation.
func PromptHi(ctx context.Context, ss *mcp.ServerSession, params *mcp.GetPromptParams) (*mcp.GetPromptResult, error) {
return &mcp.GetPromptResult{
Description: "Code review prompt",
Messages: []*mcp.PromptMessage{
{Role: "user", Content: mcp.NewTextContent("Say hi to " + params.Arguments["name"])},
},
}, nil
}
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,
})
if *httpAddr != "" {
handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server {
return server
}, nil)
log.Printf("MCP handler listening at %s", *httpAddr)
http.ListenAndServe(*httpAddr, handler)
} else {
t := mcp.NewLoggingTransport(mcp.NewStdioTransport(), os.Stderr)
if err := server.Run(context.Background(), t); err != nil {
log.Printf("Server failed: %v", err)
}
}
}
var embeddedResources = map[string]string{
"info": "This is the hello example server.",
}
func handleEmbeddedResource(_ context.Context, _ *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) {
u, err := url.Parse(params.URI)
if err != nil {
return nil, err
}
if u.Scheme != "embedded" {
return nil, fmt.Errorf("wrong scheme: %q", u.Scheme)
}
key := u.Opaque
text, ok := embeddedResources[key]
if !ok {
return nil, fmt.Errorf("no embedded resource named %q", key)
}
return &mcp.ReadResourceResult{
Contents: []*mcp.ResourceContents{mcp.NewTextResourceContents(params.URI, "text/plain", text)},
}, nil
}