internal/mcp: meta and progress token

Meta is a struct that contains a map[string]any, and a ProgressToken
field.

Each Params and Result type has a Meta as a value field.

Meta marshals and unmarshals specially, so the ProgressToken field
becomes part of the map. The marshalling code is generalized in
anticipation of every Params and Result growing a map of additional
properties.

Change-Id: I6d2f34270d33501c96437f24f91ef3e51d20d955
Reviewed-on: https://go-review.googlesource.com/c/tools/+/674956
Reviewed-by: Sam Thanawalla <samthanawalla@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Jonathan Amsterdam 2025-05-21 07:11:50 -04:00 • committed by Gopher Robot
commit 7130f0f081
2 changed files with 548 additions and 115 deletions

View file

@ -616,14 +616,19 @@ func structPropertiesOf(t reflect.Type) propertyMap {
// jsonName returns the name for f as would be used by [json.Marshal].
// That is the name in the json struct tag, or the field name if there is no tag.
// If f is not exported or the tag name is "-", jsonName returns "", false.
// If f is not exported or the tag is "-", jsonName returns "", false.
func jsonName(f reflect.StructField) (string, bool) {
if !f.IsExported() {
return "", false
}
if tag, ok := f.Tag.Lookup("json"); ok {
if name, _, _ := strings.Cut(tag, ","); name != "" {
return name, name != "-"
name, _, found := strings.Cut(tag, ",")
// "-" means omit, but "-," means the name is "-"
if name == "-" && !found {
return "", false
}
if name != "" {
return name, true
}
}
return f.Name, true

View file

@ -10,6 +10,7 @@ import (
"net/url"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
@ -171,6 +172,26 @@ func TestStructInstance(t *testing.T) {
}
}
func TestJSONName(t *testing.T) {
type S struct {
A int
B int `json:","`
C int `json:"-"`
D int `json:"-,"`
E int `json:"echo"`
F int `json:"foxtrot,omitempty"`
g int `json:"golf"`
}
want := []string{"A", "B", "", "-", "echo", "foxtrot", ""}
tt := reflect.TypeFor[S]()
for i := range tt.NumField() {
got, _ := jsonName(tt.Field(i))
if got != want[i] {
t.Errorf("got %q, want %q", got, want[i])
}
}
}
// loadRemote loads a remote reference used in the test suite.
func loadRemote(uri *url.URL) (*Schema, error) {
// Anything with localhost:1234 refers to the remotes directory in the test suite repo.