go-sdk/examples/server/custom-method/main.go
Guglielmo Colombo 65ece59643
mcp: Allow registration of custom JSON-RPC methods (#956)
## Description

This PR introduces support for custom (non-standard) JSON-RPC methods in
the MCP Go SDK.
This feature enables servers to handle arbitrary JSON-RPC methods and
clients to invoke them, all while maintaining full type safety and
integrating seamlessly with the SDK's existing middleware and session
management.

- Server-Side Registration: Added AddReceivingCustomMethod to allow
servers to register handlers for custom methods. Incoming requests are
automatically unmarshaled into user-defined parameter structs.
- Client-Side Calling: Added AddSendingCustomMethod which returns a
strongly-typed caller function that clients can use to invoke custom
methods and receive strongly-typed results.
- Helper Structs: Provided ParamsBase and ResultBase for users to embed
in their custom structs. This allows them to easily satisfy the SDK's
internal Params and Result interfaces.

Fixes #954
2026-06-25 23:13:17 +02:00

110 lines
3.3 KiB
Go

// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by the license
// that can be found in the LICENSE file.
// The custom-method example demonstrates registering and calling a custom
// JSON-RPC method that is not part of the standard MCP spec.
//
// The server registers a "latin/translate" method that translates simple
// English phrases into Latin. A client connects over an in-memory transport,
// calls the custom method, and prints the result.
package main
import (
"context"
"fmt"
"log"
"strings"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type TranslateParams struct {
mcp.ParamsBase
Text string `json:"text"`
}
type TranslateResult struct {
mcp.ResultBase
Latin string `json:"latin"`
}
var translations = map[string]string{
"hello": "salve",
"goodbye": "vale",
"thank you": "gratias tibi ago",
"how are you": "quid agis",
"good morning": "bonum mane",
"good night": "bonam noctem",
"friend": "amicus",
"water": "aqua",
"love": "amor",
"war": "bellum",
"peace": "pax",
"truth": "veritas",
"light": "lux",
"time": "tempus",
"life": "vita",
"death": "mors",
"star": "stella",
"earth": "terra",
"sea": "mare",
"the die is cast": "alea iacta est",
"i came i saw i conquered": "veni vidi vici",
"seize the day": "carpe diem",
}
func main() {
ctx := context.Background()
server := mcp.NewServer(&mcp.Implementation{Name: "latin-server", Version: "v1.0.0"}, nil)
if err := mcp.AddReceivingCustomMethod(server, "latin/translate",
func(ctx context.Context, ss *mcp.ServerSession, params *TranslateParams) (*TranslateResult, error) {
key := strings.ToLower(strings.TrimSpace(params.Text))
latin, ok := translations[key]
if !ok {
latin = fmt.Sprintf("[unknown: %q — try: %s]", params.Text, knownPhrases())
}
return &TranslateResult{Latin: latin}, nil
}); err != nil {
log.Fatal(err)
}
ct, st := mcp.NewInMemoryTransports()
ss, err := server.Connect(ctx, st, nil)
if err != nil {
log.Fatal(err)
}
defer ss.Close()
client := mcp.NewClient(&mcp.Implementation{Name: "latin-client", Version: "v1.0.0"}, nil)
if err := mcp.AddSendingCustomMethod[*TranslateParams, *TranslateResult](client, "latin/translate"); err != nil {
log.Fatal(err)
}
cs, err := client.Connect(ctx, ct, nil)
if err != nil {
log.Fatal(err)
}
defer cs.Close()
phrases := []string{"Hello", "Seize the day", "Peace", "Truth", "I came I saw I conquered"}
for _, phrase := range phrases {
result, err := mcp.CallCustomMethod[*TranslateParams, *TranslateResult](
ctx, cs, "latin/translate", &TranslateParams{Text: phrase})
if err != nil {
log.Fatalf("translate %q: %v", phrase, err)
}
fmt.Printf("%-35s → %s\n", phrase, result.Latin)
}
}
func knownPhrases() string {
phrases := make([]string, 0, len(translations))
for k := range translations {
phrases = append(phrases, fmt.Sprintf("%q", k))
}
return strings.Join(phrases, ", ")
}