### Server Setup, Tool Addition, and Connection Handling (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Illustrates the server-side setup in the MCP SDK. It shows how to create a server instance, add tools (like the 'greet' tool), and handle client connections using a standard I/O transport. The example also covers initiating the server's connection and ensuring it waits for client disconnections. ```go // Create a server with a single tool. server := mcp.NewServer("greeter", "v1.0.0", nil) server.AddTools(mcp.NewServerTool("greet", "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() ``` -------------------------------- ### Client Connection and Tool Call Example (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Demonstrates how to initialize an MCP client, establish a connection to a server using a command transport (e.g., over stdio), and subsequently call a tool on the server. This example illustrates the client-side workflow for interacting with server-side functionality. ```go 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 { ... } // Call a tool on the server. content, err := session.CallTool(ctx, "greet", map[string]any{"name": "you"}, nil) ... return session.Close() ``` -------------------------------- ### Example of Adding a File Resource (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md An example demonstrating how to add a file resource to the server using `FileResourceHandler`. This specific example safely reads `/public/puppies.txt` by setting the handler's root directory to `/public`. ```go // Safely read "/public/puppies.txt". s.AddResources(&mcp.ServerResource{ Resource: mcp.Resource{URI: "file:///puppies.txt"}, Handler: s.FileReadResourceHandler("/public")}) ``` -------------------------------- ### MCP Server Example in Go Source: https://github.com/findleyr/mcp/blob/main/internal/readme/README.src.md Illustrates the server-side component of an MCP communication, handling requests and responses over standard input/output. This code snippet requires the MCP Go SDK packages. ```go package main import ( "context" "log" "net/http" "github.com/modelcontextprotocol/go-sdk/mcp" ) // ExampleService implements the methods for the MCP server. type ExampleService struct {} // Process is an example method that takes integers and returns their sum. func (s *ExampleService) Process(ctx context.Context, params []int) (int, error) { sum := 0 for _, v := range params { sum += v } return sum, nil } func main() { ctx := context.Background() // Create a new MCP server instance sserver, err := mcp.NewServer(ctx, &ExampleService{}) if err != nil { log.Fatalf("failed to create MCP server: %v", err) } // Serve MCP requests over HTTP on localhost:8080 // The server automatically handles JSON-RPC over HTTP. http.Handle("/", server) log.Println("MCP server listening on :8080") if err := http.ListenAndServe(":8080", nil); err != nil { log.Fatalf("MCP server failed to start: %v", err) } } ``` -------------------------------- ### MCP Client Example in Go Source: https://github.com/findleyr/mcp/blob/main/internal/readme/README.src.md Demonstrates how an MCP client can communicate with an MCP server running in a sidecar process. This code snippet requires the MCP Go SDK packages. ```go package main import ( "context" "fmt" "log" "github.com/modelcontextprotocol/go-sdk/mcp" ) func main() { ctx := context.Background() // Create a new MCP client connecting to a local server client, err := mcp.NewClient(ctx, "unix:///tmp/mcp.sock") if err != nil { log.Fatalf("failed to create MCP client: %v", err) } defer client.Close() // Example: Send a request and receive a response request := &mcp.Request{ Method: "Example.Process", Params: []int{1, 2, 3}, } var response int if err := client.Call(ctx, request, &response); err != nil { log.Fatalf("MCP client call failed: %v", err) } fmt.Printf("Received response: %d\n", response) } ``` -------------------------------- ### Go: MCP Server Responding to Client Tool Calls Source: https://github.com/findleyr/mcp/blob/main/README.md Example of an MCP server component that communicates with a client over stdin/stdout. It defines a tool ('greet') and registers it with the server. The server then runs, listening for client requests. Dependencies include the go-sdk/mcp package. It takes no explicit inputs but relies on client communication for operation. ```go package main import ( "context" "log" "github.com/modelcontextprotocol/go-sdk/mcp" ) type HiParams struct { Name string `json:"name"` } func SayHi(ctx context.Context, cc *mcp.ServerSession, params *mcp.CallToolParamsFor[HiParams]) (*mcp.CallToolResultFor[any], error) { return &mcp.CallToolResultFor[any]{ Content: []*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.NewServerTool("greet", "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) } } ``` -------------------------------- ### Go: MCP Client Communicating with MCP Server Source: https://github.com/findleyr/mcp/blob/main/README.md Example of an MCP client connecting to an MCP server over stdin/stdout. It demonstrates creating a client, establishing a connection via a command transport, and calling a tool on the server. Dependencies include the go-sdk/mcp package. It takes no explicit inputs but relies on an external process for the server. ```go 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. params := &mcp.CallToolParams{ Name: "greet", Arguments: map[string]any{"name": "you"}, } res, err := session.CallTool(ctx, params) if err != nil { log.Fatalf("CallTool failed: %v", err) } if res.IsError { log.Fatal("tool failed") } for _, c := range res.Content { log.Print(c.Text) } } ``` -------------------------------- ### Add Server Resources in Go Source: https://context7.com/findleyr/mcp/llms.txt Demonstrates how to add static, dynamic, and binary resources to an MCP server using Go. It includes examples for JSON configuration, file system access via templates, and binary image data. Resources can also be added dynamically. ```go func setupServerResources(server *mcp.Server) { // Add a static resource server.AddResources(&mcp.ServerResource{ Resource: &mcp.Resource{ URI: "config://app.json", Name: "Application Configuration", Description: "Current application configuration", MIMEType: "application/json", }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { config := `{"version": "1.0", "features": ["search", "cache"]}` return &mcp.ReadResourceResult{ Contents: []*mcp.ResourceContents{ mcp.NewTextResourceContents( params.URI, "application/json", config, ), }, }, nil }, }) // Add resource template for dynamic resources server.AddResourceTemplates(&mcp.ServerResourceTemplate{ ResourceTemplate: &mcp.ResourceTemplate{ URITemplate: "file:///{path}", Name: "File System", Description: "Access files by path", MIMEType: "text/plain", }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { // Extract path from URI path := strings.TrimPrefix(params.URI, "file:///") // Read file (simplified) content := fmt.Sprintf("Content of file: %s", path) return &mcp.ReadResourceResult{ Contents: []*mcp.ResourceContents{ mcp.NewTextResourceContents( params.URI, "text/plain", content, ), }, }, nil }, }) // Add binary resource (image example) server.AddResources(&mcp.ServerResource{ Resource: &mcp.Resource{ URI: "image://logo.png", Name: "Company Logo", Description: "Company logo image", MIMEType: "image/png", Size: 1024, }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { // In real implementation, read actual image data imageData := []byte{0x89, 0x50, 0x4E, 0x47} // PNG header return &mcp.ReadResourceResult{ Contents: []*mcp.ResourceContents{ mcp.NewBlobResourceContents( params.URI, "image/png", imageData, ), }, }, nil }, }) // Dynamically add/remove resources go func() { // Example: add a resource after some event server.AddResources(&mcp.ServerResource{ Resource: &mcp.Resource{ URI: "dynamic://new-data", Name: "Dynamic Data", }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { return &mcp.ReadResourceResult{ Contents: []*mcp.ResourceContents{ mcp.NewTextResourceContents(params.URI, "text/plain", "Dynamic content"), }, }, nil }, }) }() } ``` -------------------------------- ### Go Iterator Methods for Paginated Listing Source: https://github.com/findleyr/mcp/blob/main/design/design.md Provides examples of iterator methods that simplify pagination for 'List' spec methods. These methods automatically traverse all pages and can start iteration from a provided cursor if Params are supplied. ```go func (*ClientSession) Tools(context.Context, *ListToolsParams) iter.Seq2[Tool, error] ``` ```go func (*ClientSession) Prompts(context.Context, *ListPromptsParams) iter.Seq2[Prompt, error] ``` ```go func (*ClientSession) Resources(context.Context, *ListResourceParams) iter.Seq2[Resource, error] ``` ```go func (*ClientSession) ResourceTemplates(context.Context, *ListResourceTemplatesParams) iter.Seq2[ResourceTemplate, error] ``` -------------------------------- ### Get Server Prompts using Go Source: https://context7.com/findleyr/mcp/llms.txt This Go function retrieves prompt templates from the server. It lists all available prompts and their arguments, then demonstrates how to get a specific prompt with its associated messages and content. It requires the 'context' package and the 'mcp' client session. ```go func useServerPrompts(ctx context.Context, session *mcp.ClientSession) error { // List available prompts promptsResult, err := session.ListPrompts(ctx, nil) if err != nil { return fmt.Errorf("failed to list prompts: %w", err) } fmt.Printf("Available prompts (%d):\n", len(promptsResult.Prompts)) for _, prompt := range promptsResult.Prompts { fmt.Printf(" - %s: %s\n", prompt.Name, prompt.Description) if len(prompt.Arguments) > 0 { fmt.Printf(" Arguments:\n") for _, arg := range prompt.Arguments { required := "" if arg.Required { required = " (required)" } fmt.Printf(" - %s: %s%s\n", arg.Name, arg.Description, required) } } } // Get a specific prompt with arguments promptParams := &mcp.GetPromptParams{ Name: "code-review", Arguments: map[string]string{ "language": "go", "file": "main.go", "context": "refactoring", }, } promptResult, err := session.GetPrompt(ctx, promptParams) if err != nil { return fmt.Errorf("failed to get prompt: %w", err) } fmt.Printf("\nPrompt: %s\n", promptResult.Description) fmt.Println("Messages:") for i, message := range promptResult.Messages { fmt.Printf(" Message %d [%s]:\n", i, message.Role) if message.Content != nil { fmt.Printf(" %s\n", message.Content.Text) } } return nil } ``` -------------------------------- ### Server-Side Logging Middleware Example in Go Source: https://github.com/findleyr/mcp/blob/main/design/design.md Demonstrates how to create and add a server-side middleware function in Go for logging incoming requests and outgoing responses. This middleware wraps the original method handler to log method names, parameters, results, and errors. ```go import ( "context" "log" "project/mcp" ) func withLogging(h mcp.MethodHandler[*mcp.ServerSession]) mcp.MethodHandler[*mcp.ServerSession]{ return func(ctx context.Context, s *mcp.ServerSession, method string, params any) (res any, err error) { log.Printf("request: %s %v", method, params) defer func() { log.Printf("response: %v, %v", res, err) }() return h(ctx, s , method, params) } } server.AddReceivingMiddleware(withLogging) ``` -------------------------------- ### Initialize and Run MCP Server with Tools in Go Source: https://context7.com/findleyr/mcp/llms.txt Initializes and runs an MCP server in Go. The `main` function sets up server options, creates a new server instance, and registers tools like 'search', 'advanced-search', and 'risky-op'. It demonstrates adding tools with automatic schema inference, custom schema definitions, and annotations, then runs the server over stdio. ```go func main() { serverOpts := &mcp.ServerOptions{ Instructions: "A search server that provides web search capabilities", PageSize: 100, } server := mcp.NewServer("search-server", "v1.0.0", serverOpts) // Add tool with automatic schema inference server.AddTools(mcp.NewServerTool( "search", "Search the web for information", SearchHandler, )) // Add tool with custom schema options server.AddTools(mcp.NewServerTool( "advanced-search", "Advanced search with filters", SearchHandler, mcp.Input( mcp.Property("query", mcp.Description("Search query string"), mcp.Required(true), ), mcp.Property("maxResults", mcp.Description("Maximum number of results to return"), mcp.Default(10), ), mcp.Property("filters", mcp.Description("Search filters"), ), ), )) // Add tool with annotations (hints for clients) server.AddTools(mcp.NewServerTool( "risky-op", "Perform a potentially risky operation", RiskyOperationHandler, mcp.Annotations(&mcp.ToolAnnotations{ DestructiveHint: true, IdempotentHint: false, }), )) // Run server over stdio ctx := context.Background() if err := server.Run(ctx, mcp.NewStdioTransport()); err != nil { log.Fatalf("Server error: %v", err) } } ``` -------------------------------- ### Iterator Methods Source: https://github.com/findleyr/mcp/blob/main/design/design.md Iterator methods provide a convenient way to handle pagination for `List` spec methods, automatically traversing all pages. They can also start iteration from a provided cursor if parameters are supplied. ```APIDOC ## Iterator Methods ### Description Iterator methods automatically handle pagination for `List` spec methods, providing a sequential iterator over all results. They can optionally begin iteration from a specified cursor. ### Method Signatures - **Tools**: `func (*ClientSession) Tools(context.Context, *ListToolsParams) iter.Seq2[Tool, error]` - **Prompts**: `func (*ClientSession) Prompts(context.Context, *ListPromptsParams) iter.Seq2[Prompt, error]` - **Resources**: `func (*ClientSession) Resources(context.Context, *ListResourceParams) iter.Seq2[Resource, error]` - **ResourceTemplates**: `func (*ClientSession) ResourceTemplates(context.Context, *ListResourceTemplatesParams) iter.Seq2[ResourceTemplate, error]` ### Parameters #### Path Parameters None #### Query Parameters - **ListToolsParams**, **ListPromptsParams**, **ListResourceParams**, **ListResourceTemplatesParams** (*Params) - Optional. Parameters for the list method, potentially including a cursor for starting iteration. #### Request Body None ### Request Example ```go // Example for iterating through tools: ctx := context.Background() params := &mcp.ListToolsParams{ /* optional: Cursor: "some-cursor" */ } for tool, err := range session.Tools(ctx, params) { if err != nil { // Handle error break } // Process tool } ``` ### Response #### Success Response (200) - **iter.Seq2[T, error]** - An iterator that yields items of type `T` and an error. ``` -------------------------------- ### Creating and Managing Server Prompts in Go Source: https://github.com/findleyr/mcp/blob/main/design/design.md Illustrates the creation and management of server-side prompts using `NewServerPrompt` and `AddPrompts`/`RemovePrompts`. It shows how to define a prompt with a name, description, handler function, and argument options, and how to register or unregister it from the server. ```go func NewServerPrompt[TReq any](name, description string, handler func(context.Context, *ServerSession, TReq) (*GetPromptResult, error), opts ...PromptOption) *ServerPrompt type codeReviewArgs struct { Code string `json:"code"` } func codeReviewHandler(context.Context, *ServerSession, codeReviewArgs) {...} server.AddPrompts( NewServerPrompt("code_review", "review code", codeReviewHandler, Argument("code", Description("the code to review")))) server.RemovePrompts("code_review") ``` -------------------------------- ### List and Call MCP Server Tools Source: https://context7.com/findleyr/mcp/llms.txt Shows how to query available tools from an MCP server, iterate through them with pagination, and call a specific tool with arguments. It also demonstrates how to process the results, handling text, image, and resource content types, and checking for tool execution errors. ```Go func callServerTools(ctx context.Context, session *mcp.ClientSession) error { // List all available tools toolsResult, err := session.ListTools(ctx, nil) if err != nil { return fmt.Errorf("failed to list tools: %w", err) } fmt.Printf("Available tools (%d):\n", len(toolsResult.Tools)) for _, tool := range toolsResult.Tools { fmt.Printf(" - %s: %s\n", tool.Name, tool.Description) } // Alternative: iterate with automatic pagination fmt.Println("\nIterating tools with pagination:") for tool, err := range session.Tools(ctx, nil) { if err != nil { return fmt.Errorf("iteration error: %w", err) } fmt.Printf(" Tool: %s\n", tool.Name) } // Call a specific tool with arguments callParams := &mcp.CallToolParams{ Name: "calculate", Arguments: map[string]any{ "operation": "add", "a": 42, "b": 58, }, } result, err := session.CallTool(ctx, callParams) if err != nil { return fmt.Errorf("tool call failed: %w", err) } if result.IsError { fmt.Printf("Tool returned error: %s\n", result.Content[0].Text) return nil } // Process tool results fmt.Println("Tool execution successful:") for i, content := range result.Content { switch content.Type { case "text": fmt.Printf(" Content %d (text): %s\n", i, content.Text) case "image": fmt.Printf(" Content %d (image): %s, %d bytes\n", i, content.MIMEType, len(content.Data)) case "resource": fmt.Printf(" Content %d (resource): %s\n", i, content.Resource.URI) } } return nil } ``` -------------------------------- ### Context Cancellation Example for Tool Calls in Go Source: https://github.com/findleyr/mcp/blob/main/design/design.md Illustrates how to cancel an ongoing tool call using context cancellation in Go. Cancelling the context sends a "notifications/cancelled" notification to the server and causes the client call to return immediately with ctx.Err(). ```go import ( "context" ) ctx, cancel := context.WithCancel(ctx) go session.CallTool(ctx, "slow", map[string]any{}, nil) cancel() ``` -------------------------------- ### Go CommandTransport for Stdio JSON-RPC Communication (Client) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Implements the client-side of the stdio transport for MCP. The `CommandTransport` starts a specified command and establishes communication with it over its standard input and output streams, utilizing newline-delimited JSON for message exchange. It requires a Go `exec.Command` to be provided during initialization. ```go import ( "context" "io" "os/exec" "github.com/sourcegraph/jsonrpc2" ) // A CommandTransport is a [Transport] that runs a command and communicates // with it over stdin/stdout, using newline-delimited JSON. type CommandTransport struct { /* unexported fields */ } // NewCommandTransport returns a [CommandTransport] that runs the given command // and communicates with it over stdin/stdout. func NewCommandTransport(cmd *exec.Command) *CommandTransport // Connect starts the command, and connects to it over stdin/stdout. func (*CommandTransport) Connect(ctx context.Context) (Connection, error) { // ... implementation details ... return nil, nil } ``` -------------------------------- ### SSE Server Transport for MCP Sessions (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Represents a logical SSE session established via a hanging GET request. It handles incoming POST requests for reading data and sends SSE 'message' events for writing. Callers are responsible for serving its HTTP requests. ```Go package main import ( "context" "net/http" ) // Mock Connection interface for demonstration type Connection interface { Write([]byte) error Read() ([]byte, error) Close() } // A SSEServerTransport is a logical SSE session created through a hanging GET // request. // // When connected, it returns the following [Connection] implementation: // - Writes are SSE 'message' events to the GET response. // - Reads are received from POSTs to the session endpoint, via // [SSEServerTransport.ServeHTTP]. // - Close terminates the hanging GET. type SSEServerTransport struct { /* ... */ } // NewSSEServerTransport creates a new SSE transport for the given messages // endpoint, and hanging GET response. // // Use [SSEServerTransport.Connect] to initiate the flow of messages. // // The transport is itself an [http.Handler]. It is the caller's responsibility // to ensure that the resulting transport serves HTTP requests on the given // session endpoint. // // Most callers should instead use an [SSEHandler], which transparently handles // the delegation to SSEServerTransports. func NewSSEServerTransport(endpoint string, w http.ResponseWriter) *SSEServerTransport { // Implementation omitted for brevity return &SSEServerTransport{} } // ServeHTTP handles POST requests to the transport endpoint. func (*SSEServerTransport) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Implementation omitted for brevity } // Connect sends the 'endpoint' event to the client. // See [SSEServerTransport] for more details on the [Connection] implementation. func (*SSEServerTransport) Connect(context.Context) (Connection, error) { // Implementation omitted for brevity return nil, nil } ``` -------------------------------- ### MCP Dynamic Resource Subscription and Updates (Go) Source: https://context7.com/findleyr/mcp/llms.txt This Go function, setupDynamicResources, implements dynamic resource subscription patterns for an MCP server. It allows servers to notify clients about resource changes, enabling reactive data synchronization. It includes handlers for adding resources, tracking subscribers, and a background goroutine to push updates. Dependencies include 'sync', 'context', 'encoding/json', 'fmt', and 'time'. ```Go func setupDynamicResources(server *mcp.Server) { // Track active resource subscriptions subscribers := make(map[string][]*mcp.ServerSession) var mu sync.Mutex // Add a resource that can be updated server.AddResources(&mcp.ServerResource{ Resource: &mcp.Resource{ URI: "live://metrics", Name: "System Metrics", Description: "Real-time system metrics", MIMEType: "application/json", }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { // Register subscriber mu.Lock() subscribers[params.URI] = append(subscribers[params.URI], ss) mu.Unlock() // Return current metrics metrics := map[string]any{ "cpu": 45.2, "memory": 68.5, "disk": 82.1, } data, _ := json.Marshal(metrics) return &mcp.ReadResourceResult{ Contents: []*mcp.ResourceContents{ mcp.NewTextResourceContents( params.URI, "application/json", string(data), ), }, }, nil }, }) // Background process that updates metrics and notifies clients go func() { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() for range ticker.C { mu.Lock() sessions := subscribers["live://metrics"] mu.Unlock() // Notify all subscribed sessions for _, session := range sessions { // Note: ResourceUpdated notification would be sent here // (requires client ResourceListChangedHandler) fmt.Printf("Resource updated, notifying %d subscribers\n", len(sessions)) } } }() // Handler for adding/removing resources dynamically server.AddTools(mcp.NewServerTool( "create-resource", "Dynamically create a new resource", func(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[map[string]any]) (*mcp.CallToolResultFor[any], error) { uri := params.Arguments["uri"].(string) name := params.Arguments["name"].(string) server.AddResources(&mcp.ServerResource{ Resource: &mcp.Resource{ URI: uri, Name: name, }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.ReadResourceParams) (*mcp.ReadResourceResult, error) { return &mcp.ReadResourceResult{ Contents: []*mcp.ResourceContents{ mcp.NewTextResourceContents(params.URI, "text/plain", "Dynamic content"), }, }, nil }, }) return &mcp.CallToolResultFor[any]{ Content: []*mcp.Content{ mcp.NewTextContent(fmt.Sprintf("Created resource: %s", uri)), }, }, nil }, )) } ``` -------------------------------- ### Create MCP Server Tool with Options in Go Source: https://github.com/findleyr/mcp/blob/main/design/design.md Illustrates the creation of a `ServerTool` using the `NewServerTool` constructor, which leverages reflection to infer the input schema from the handler's argument type. It shows how to apply `ToolOption`s, such as customizing the input schema properties. ```go // NewServerTool creates a Tool using reflection on the given handler. func NewServerTool[TArgs any](name, description string, handler ToolHandler[TArgs], opts …ToolOption) *ServerTool type ToolOption interface { /* ... */ } NewServerTool(name, description, handler, Input(Property("count", Description("size of the inventory")))) ``` -------------------------------- ### Initialize Go Workspace for Multi-Module Project Source: https://github.com/findleyr/mcp/blob/main/CONTRIBUTING.md This command sets up a Go workspace to manage multiple modules, useful for testing local SDK changes against a project that uses the SDK. It requires the path to your project and the SDK directory. ```sh go work init ./project ./go-sdk ``` -------------------------------- ### Client and Server Structures and Constructors (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Defines the core structures for `Client` and `Server`, along with their respective constructors (`NewClient`, `NewServer`) and options types. It also details the session structures (`ClientSession`, `ServerSession`) and their associated methods for managing connections and interactions. These are foundational for establishing communication within the MCP SDK. ```go type Client struct { /* ... */ } func NewClient(name, version string, opts *ClientOptions) *Client func (*Client) Connect(context.Context, Transport) (*ClientSession, error) func (*Client) Sessions() iter.Seq[*ClientSession] type ClientOptions struct { /* ... */ } // described below type ClientSession struct { /* ... */ } func (*ClientSession) Client() *Client func (*ClientSession) Close() error func (*ClientSession) Wait() error type Server struct { /* ... */ } func NewServer(name, version string, opts *ServerOptions) *Server func (*Server) Connect(context.Context, Transport) (*ServerSession, error) func (*Server) Sessions() iter.Seq[*ServerSession] type ServerOptions struct { /* ... */ } // described below type ServerSession struct { /* ... */ } func (*ServerSession) Server() *Server func (*ServerSession) Close() error func (*ServerSession) Wait() error ``` -------------------------------- ### Advanced Schema Features: OneOf (Union Types) Source: https://context7.com/findleyr/mcp/llms.txt This Go code snippet showcases advanced JSON schema features, specifically the `OneOf` keyword, which allows defining a schema that must match exactly one of the subschemas provided. This is used to model union types or polymorphic structures where an object can conform to one of several distinct schemas. The example defines a configuration object that can either be a database configuration or a network configuration. ```go import ( "github.com/modelcontextprotocol/go-sdk/jsonschema" ) // Advanced schema features func advancedSchemaFeatures() *jsonschema.Schema { return &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "config": { // OneOf for union types OneOf: []*jsonschema.Schema{ { Type: "object", Properties: map[string]*jsonschema.Schema{ "type": {Type: "string", Const: jsonschema.Ptr("database")}, "host": {Type: "string"}, "port": {Type: "integer"}, }, Required: []string{"type", "host"}, }, { Type: "object", Properties: map[string]*jsonschema.Schema{ "type": {Type: "string", Const: jsonschema.Ptr("network")}, "address": {Type: "string"}, }, Required: []string{"type", "address"}, }, }, }, }, } } ``` -------------------------------- ### Convenience Method for Server Session Handling (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Introduces the `Server.Run` convenience method, which simplifies the common server-side scenario of managing a session until the connected client disconnects. This method abstracts away the manual session waiting logic. ```go func (*Server) Run(context.Context, Transport) ``` -------------------------------- ### Read Server Resources using Go Source: https://context7.com/findleyr/mcp/llms.txt This Go function demonstrates how to interact with server resources. It lists available resources, reads the content of a specific resource (e.g., a configuration file), and lists resource templates. It requires the 'context' package and the 'mcp' client session. ```go func readServerResources(ctx context.Context, session *mcp.ClientSession) error { // List available resources resourcesResult, err := session.ListResources(ctx, nil) if err != nil { return fmt.Errorf("failed to list resources: %w", err) } fmt.Printf("Available resources (%d):\n", len(resourcesResult.Resources)) for _, resource := range resourcesResult.Resources { fmt.Printf(" - %s (%s): %s\n", resource.Name, resource.URI, resource.Description) if resource.MIMEType != "" { fmt.Printf(" MIME Type: %s\n", resource.MIMEType) } } // Read a specific resource readParams := &mcp.ReadResourceParams{ URI: "file:///data/config.json", } readResult, err := session.ReadResource(ctx, readParams) if err != nil { return fmt.Errorf("failed to read resource: %w", err) } fmt.Println("\nResource contents:") for i, content := range readResult.Contents { fmt.Printf(" Content %d:\n", i) fmt.Printf(" URI: %s\n", content.URI) fmt.Printf(" MIME Type: %s\n", content.MIMEType) if content.Text != "" { fmt.Printf(" Text: %s\n", content.Text) } else if len(content.Blob) > 0 { fmt.Printf(" Binary data: %d bytes\n", len(content.Blob)) } } // List resource templates (dynamic resources) templatesResult, err := session.ListResourceTemplates(ctx, nil) if err != nil { return fmt.Errorf("failed to list templates: %w", err) } fmt.Printf("\nResource templates (%d):\n", len(templatesResult.ResourceTemplates)) for _, template := range templatesResult.ResourceTemplates { fmt.Printf(" - %s: %s\n", template.URITemplate, template.Description) } return nil } ``` -------------------------------- ### Server Options for Subscription Handlers (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Configuration options for the server to handle client subscriptions and unsubscriptions. `SubscribeHandler` and `UnsubscribeHandler` are functions called when clients respectively subscribe or unsubscribe from resources. Both must be provided if subscriptions are supported. ```go type ServerOptions struct { ... // Function called when a client session subscribes to a resource. SubscribeHandler func(context.Context, *SubscribeParams) error // Function called when a client session unsubscribes from a resource. UnsubscribeHandler func(context.Context, *UnsubscribeParams) error } ``` -------------------------------- ### Create and Connect MCP Client with stdio Transport Source: https://context7.com/findleyr/mcp/llms.txt Demonstrates how to create an MCP client instance, configure optional handlers for logging and tool list changes, add client roots for resource access, and connect to an MCP server using the stdio command transport. The client manages connection lifecycle and protocol negotiation. ```Go package main import ( "context" "fmt" "log" "os/exec" "github.com/modelcontextprotocol/go-sdk/mcp" ) func main() { ctx := context.Background() // Create client with optional configuration clientOpts := &mcp.ClientOptions{ LoggingMessageHandler: func(ctx context.Context, cs *mcp.ClientSession, params *mcp.LoggingMessageParams) { fmt.Printf("[%s] %s: %v\n", params.Logger, params.Level, params.Data) }, ToolListChangedHandler: func(ctx context.Context, cs *mcp.ClientSession, params *mcp.ToolListChangedParams) { fmt.Println("Server tools have changed") }, } client := mcp.NewClient("my-ai-client", "v1.0.0", clientOpts) // Add client roots (directories/resources the server can access) client.AddRoots(&mcp.Root{ URI: "file:///home/user/project", Name: "Project Directory", }) // Connect to server via command transport (stdio) transport := mcp.NewCommandTransport(exec.Command("./my-mcp-server")) session, err := client.Connect(ctx, transport) if err != nil { log.Fatalf("Failed to connect: %v", err) } defer session.Close() fmt.Println("Connected to MCP server successfully") // Wait for session to complete (blocks until server disconnects) if err := session.Wait(); err != nil { log.Printf("Session ended with error: %v", err) } } ``` -------------------------------- ### File Resource Handler Creation (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Function to create a `ResourceHandler` that reads files from the local filesystem. It uses a specified directory as the root and includes protection against path traversal attacks. It ensures files are within the client's requested root set. ```go // FileResourceHandler returns a ResourceHandler that reads paths using dir as a root directory. // It protects against path traversal attacks. // It will not read any file that is not in the root set of the client requesting the resource. func (*Server) FileResourceHandler(dir string) ResourceHandler ``` -------------------------------- ### MCP Server Progress Reporting (Go) Source: https://context7.com/findleyr/mcp/llms.txt This Go code snippet demonstrates how to set up progress reporting for a client in the MCP framework. It defines a client option to format and display progress messages, including percentage and status text. It requires the 'fmt' package for printing. ```Go percentage := (params.Progress / params.Total) * 100 fmt.Printf("Progress: %.1f%% - %s\n", percentage, params.Message) }, }, return mcp.NewClient("progress-client", "v1.0.0", opts) } ``` -------------------------------- ### Server Requests Client Root Directory Listing Source: https://context7.com/findleyr/mcp/llms.txt This Go function demonstrates a server requesting a list of available root directories from the connected client. It uses the `ListRoots` method on the `ServerSession` object. The output includes the name and URI of each root found. ```go // Server requesting root directory listing func serverRequestsRoots(ctx context.Context, ss *mcp.ServerSession) error { result, err := ss.ListRoots(ctx, nil) if err != nil { return fmt.Errorf("failed to list roots: %w", err) } fmt.Printf("Client roots (%d):\n", len(result.Roots)) for _, root := range result.Roots { fmt.Printf(" - %s: %s\n", root.Name, root.URI) } return nil } ``` -------------------------------- ### Calling Tools with Typed Arguments in Go Source: https://github.com/findleyr/mcp/blob/main/design/design.md Demonstrates how to call tools with typed arguments using a generic helper function. This function simplifies the process of calling tools by abstracting away the raw JSON message handling, expecting typed arguments and returning a structured result. ```go func (cs *ClientSession) CallTool(context.Context, *CallToolParams[json.RawMessage]) (*CallToolResult, error) func CallTool[TArgs any](context.Context, *ClientSession, *CallToolParams[TArgs]) (*CallToolResult, error) ``` -------------------------------- ### Client Subscription and Unsubscription Methods (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Methods on the `ClientSession` type for managing resource change notifications. `Subscribe` registers a client for updates, and `Unsubscribe` removes the registration. Both methods require a `context.Context` and corresponding parameters. ```go func (*ClientSession) Subscribe(context.Context, *SubscribeParams) error func (*ClientSession) Unsubscribe(context.Context, *UnsubscribeParams) error ``` -------------------------------- ### Define and Add Server Prompts in Go Source: https://context7.com/findleyr/mcp/llms.txt This Go code defines and adds various prompt templates to an MCP server. It showcases how to create prompts with different argument requirements and handlers, enabling structured AI interactions. ```go package main import ( "context" "fmt" "mcp" ) type CodeReviewPromptArgs struct { Language string Code string Context string } func setupServerPrompts(server *mcp.Server) { // Simple prompt without arguments server.AddPrompts(&mcp.ServerPrompt{ Prompt: &mcp.Prompt{ Name: "greeting", Description: "A friendly greeting prompt", }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.GetPromptParams) (*mcp.GetPromptResult, error) { return &mcp.GetPromptResult{ Description: "Greet the user warmly", Messages: []*mcp.PromptMessage{ { Role: "system", Content: mcp.NewTextContent("You are a helpful assistant."), }, { Role: "user", Content: mcp.NewTextContent("Say hello!"), }, }, }, nil }, }) // Prompt with required arguments server.AddPrompts(&mcp.ServerPrompt{ Prompt: &mcp.Prompt{ Name: "code-review", Description: "Review code for quality and best practices", Arguments: []*mcp.PromptArgument{ { Name: "language", Description: "Programming language", Required: true, }, { Name: "code", Description: "Code to review", Required: true, }, { Name: "context", Description: "Additional context about the code", Required: false, }, }, }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.GetPromptParams) (*mcp.GetPromptResult, error) { language := params.Arguments["language"] code := params.Arguments["code"] context := params.Arguments["context"] systemMsg := fmt.Sprintf( "You are an expert %s code reviewer. Analyze code for bugs, performance, and style.", language, ) userMsg := fmt.Sprintf("Review this %s code:\n\n%s", language, code) if context != "" { userMsg += fmt.Sprintf("\n\nContext: %s", context) } return &mcp.GetPromptResult{ Description: fmt.Sprintf("Code review for %s", language), Messages: []*mcp.PromptMessage{ { Role: "system", Content: mcp.NewTextContent(systemMsg), }, { Role: "user", Content: mcp.NewTextContent(userMsg), }, }, }, nil }, }) // Prompt with embedded resources server.AddPrompts(&mcp.ServerPrompt{ Prompt: &mcp.Prompt{ Name: "analyze-file", Description: "Analyze a file with context", Arguments: []*mcp.PromptArgument{ {Name: "uri", Description: "File URI", Required: true}, }, }, Handler: func(ctx context.Context, ss *mcp.ServerSession, params *mcp.GetPromptParams) (*mcp.GetPromptResult, error) { uri := params.Arguments["uri"] return &mcp.GetPromptResult{ Description: "File analysis prompt", Messages: []*mcp.PromptMessage{ { Role: "user", Content: mcp.NewTextContent( fmt.Sprintf("Analyze the file at %s", uri), ), }, }, }, nil }, }) } ``` -------------------------------- ### Client Sampling Message Handler Configuration (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Illustrates the configuration of a CreateMessageHandler for client options, essential for clients supporting sampling. This handler is invoked by server sessions when they call the spec method CreateMessage to perform sampling operations. It defines the callback for processing sampling requests from the server. ```Go type ClientOptions struct { ... CreateMessageHandler func(context.Context, *ClientSession, *CreateMessageParams) (*CreateMessageResult, error) } ``` -------------------------------- ### MCP Client with Resource Change Handlers (Go) Source: https://context7.com/findleyr/mcp/llms.txt This Go function, clientWithResourceTracking, demonstrates how to configure an MCP client to react to changes in server resources, tools, and prompts. It defines callback handlers for ResourceListChangedHandler, ToolListChangedHandler, and PromptListChangedHandler, which are triggered when the server's available resources, tools, or prompts are updated. This enables reactive client behavior. ```Go // Client with resource change handler func clientWithResourceTracking() *mcp.Client { opts := &mcp.ClientOptions{ ResourceListChangedHandler: func(ctx context.Context, cs *mcp.ClientSession, params *mcp.ResourceListChangedParams) { fmt.Println("Server resources have changed - refetching list") }, ToolListChangedHandler: func(ctx context.Context, cs *mcp.ClientSession, params *mcp.ToolListChangedParams) { fmt.Println("Server tools have changed - refetching list") }, PromptListChangedHandler: func(ctx context.Context, cs *mcp.ClientSession, params *mcp.PromptListChangedParams) { fmt.Println("Server prompts have changed - refetching list") }, } return mcp.NewClient("reactive-client", "v1.0.0", opts) } ``` -------------------------------- ### MCP Server Options for Logging Source: https://github.com/findleyr/mcp/blob/main/design/design.md Defines server-side configuration options for handling logging notifications to clients. Includes options for logger name and the minimum interval between log messages to a single client. ```go package mcp type ServerOptions struct { // ... // The value for the "logger" field of the notification. LoggerName string // Log notifications to a single ClientSession will not be // sent more frequently than this duration. LoggingInterval time.Duration } ``` -------------------------------- ### Add and Remove Resources/Templates Methods (Go) Source: https://github.com/findleyr/mcp/blob/main/design/design.md Methods on the `Server` type to add or remove resources and resource templates. `AddResources` and `AddResourceTemplates` accept variadic arguments of their respective types. `RemoveResources` and `RemoveResourceTemplates` accept URIs or URI templates. ```go func (*Server) AddResources(...*ServerResource) func (*Server) AddResourceTemplates(...*ServerResourceTemplate) func (s *Server) RemoveResources(uris ...string) func (s *Server) RemoveResourceTemplates(uriTemplates ...string) ```