mcp: add docs for tier 1 assessment (#1195)
Closes open points in [comment](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/3220#issuecomment-5373089321)
This commit is contained in:
parent
6f0a645c20
commit
b8c53fedd6
1 changed files with 1461 additions and 5 deletions
|
|
@ -6,6 +6,7 @@ package mcp_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
|
|
@ -65,6 +66,61 @@ func Example_roots() {
|
|||
|
||||
// !-roots
|
||||
|
||||
// !+rootslistchanged
|
||||
|
||||
func Example_rootsListChanged() {
|
||||
ctx := context.Background()
|
||||
|
||||
changed := make(chan struct{}, 2)
|
||||
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, &mcp.ServerOptions{
|
||||
RootsListChangedHandler: func(context.Context, *mcp.RootsListChangedRequest) {
|
||||
changed <- struct{}{}
|
||||
},
|
||||
})
|
||||
|
||||
c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
|
||||
c.AddRoots(&mcp.Root{URI: "file:///project"})
|
||||
|
||||
t1, t2 := mcp.NewInMemoryTransports()
|
||||
ss, err := s.Connect(ctx, t1, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer ss.Close()
|
||||
|
||||
// ListRoots is a server-initiated request, so this session negotiates a
|
||||
// protocol version that still allows one.
|
||||
cs, err := c.Connect(ctx, t2, &mcp.ClientSessionOptions{ProtocolVersion: "2025-11-25"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cs.Close()
|
||||
|
||||
// Roots added after the client connects notify every connected server.
|
||||
c.AddRoots(&mcp.Root{URI: "file:///scratch"})
|
||||
<-changed
|
||||
|
||||
// The notification says only that the list changed, so read it back.
|
||||
res, err := ss.ListRoots(ctx, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, root := range res.Roots {
|
||||
fmt.Println(root.URI)
|
||||
}
|
||||
|
||||
c.RemoveRoots("file:///scratch")
|
||||
<-changed
|
||||
fmt.Println("roots changed again")
|
||||
|
||||
// Output:
|
||||
// file:///project
|
||||
// file:///scratch
|
||||
// roots changed again
|
||||
}
|
||||
|
||||
// !-rootslistchanged
|
||||
|
||||
// !+sampling
|
||||
|
||||
func Example_sampling() {
|
||||
|
|
@ -167,3 +223,117 @@ func Example_elicitation() {
|
|||
}
|
||||
|
||||
// !-elicitation
|
||||
|
||||
// !+elicitationschema
|
||||
|
||||
func Example_elicitationSchema() {
|
||||
ctx := context.Background()
|
||||
ct, st := mcp.NewInMemoryTransports()
|
||||
|
||||
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
|
||||
mcp.AddTool(s, &mcp.Tool{Name: "export_report"}, func(_ context.Context, req *mcp.CallToolRequest, _ struct{}) (*mcp.CallToolResult, any, error) {
|
||||
if len(req.Params.InputResponses) == 0 {
|
||||
return &mcp.CallToolResult{
|
||||
InputRequests: mcp.InputRequestMap{"format": &mcp.ElicitParams{
|
||||
Message: "Export quarterly-sales as which format?",
|
||||
RequestedSchema: &jsonschema.Schema{
|
||||
Type: "object",
|
||||
Properties: map[string]*jsonschema.Schema{
|
||||
"format": {
|
||||
Type: "string",
|
||||
Title: "Format",
|
||||
Enum: []any{"pdf", "csv"},
|
||||
Default: json.RawMessage(`"pdf"`),
|
||||
Extra: map[string]any{"enumNames": []any{"PDF document", "CSV spreadsheet"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}, nil, nil
|
||||
}
|
||||
res := req.Params.InputResponses["format"].(*mcp.ElicitResult)
|
||||
return &mcp.CallToolResult{
|
||||
Content: []mcp.Content{&mcp.TextContent{Text: "Exported as " + res.Content["format"].(string)}},
|
||||
}, nil, nil
|
||||
})
|
||||
if _, err := s.Connect(ctx, st, nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// The user accepts without filling anything in.
|
||||
c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, &mcp.ClientOptions{
|
||||
ElicitationHandler: func(context.Context, *mcp.ElicitRequest) (*mcp.ElicitResult, error) {
|
||||
return &mcp.ElicitResult{Action: "accept", Content: map[string]any{}}, nil
|
||||
},
|
||||
})
|
||||
cs, err := c.Connect(ctx, ct, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cs.Close()
|
||||
|
||||
res, err := cs.CallTool(ctx, &mcp.CallToolParams{Name: "export_report"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(res.Content[0].(*mcp.TextContent).Text)
|
||||
// Output: Exported as pdf
|
||||
}
|
||||
|
||||
// !-elicitationschema
|
||||
|
||||
// !+elicitationcomplete
|
||||
|
||||
func Example_elicitationComplete() {
|
||||
ctx := context.Background()
|
||||
ct, st := mcp.NewInMemoryTransports()
|
||||
|
||||
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
|
||||
ss, err := s.Connect(ctx, st, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer ss.Close()
|
||||
|
||||
done := make(chan struct{})
|
||||
c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, &mcp.ClientOptions{
|
||||
Capabilities: &mcp.ClientCapabilities{
|
||||
Elicitation: &mcp.ElicitationCapabilities{URL: &mcp.URLElicitationCapabilities{}},
|
||||
},
|
||||
ElicitationHandler: func(_ context.Context, req *mcp.ElicitRequest) (*mcp.ElicitResult, error) {
|
||||
fmt.Println("opening", req.Params.URL)
|
||||
return &mcp.ElicitResult{Action: "accept"}, nil
|
||||
},
|
||||
ElicitationCompleteHandler: func(_ context.Context, req *mcp.ElicitationCompleteNotificationRequest) {
|
||||
fmt.Println("flow finished:", req.Params.ElicitationID)
|
||||
close(done)
|
||||
},
|
||||
})
|
||||
cs, err := c.Connect(ctx, ct, &mcp.ClientSessionOptions{ProtocolVersion: "2025-11-25"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer cs.Close()
|
||||
|
||||
const elicitationID = "connect-calendar-1"
|
||||
if _, err := ss.Elicit(ctx, &mcp.ElicitParams{
|
||||
Message: "Grant calendar access",
|
||||
URL: "https://calendar.example.com/consent?state=" + elicitationID,
|
||||
ElicitationID: elicitationID,
|
||||
}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// The hosted page redirects back to the server, whose callback endpoint
|
||||
// signals that the user is done.
|
||||
if err := ss.NotifyElicitationComplete(ctx, &mcp.ElicitationCompleteParams{ElicitationID: elicitationID}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
|
||||
// Output:
|
||||
// opening https://calendar.example.com/consent?state=connect-calendar-1
|
||||
// flow finished: connect-calendar-1
|
||||
}
|
||||
|
||||
// !-elicitationcomplete
|
||||
|
|
|
|||
Loading…
Reference in a new issue